forked from upstream-mirrors/OpenTracks
Compare commits
182 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e0b84df8f6 | |||
| a431850793 | |||
| 5c17c8ffcf | |||
| 0691524dfa | |||
| 1c825b052e | |||
| eb8d28dfc1 | |||
| 9f1ea4d745 | |||
| dbd64a8075 | |||
| 75a88ca0e1 | |||
| 2af19b00e5 | |||
| 11db0d1a4f | |||
| 6a4adee2f2 | |||
| d04b420c56 | |||
| 0122539d0f | |||
| 0b8c2a0ef6 | |||
| cef363562c | |||
| e681531186 | |||
| 65c4b85bc4 | |||
| d7022a07a2 | |||
| 81b090893b | |||
| 3dca9304c4 | |||
| 7fc6492e13 | |||
| 80d968dd0f | |||
| f336a1da12 | |||
| 1657bf5ec1 | |||
| d046ca1cd8 | |||
| 6c852091ce | |||
| 4906d3f942 | |||
| 0395394e26 | |||
| ebcc7dc46e | |||
| 1e4246e73a | |||
| 3ca8381504 | |||
| b92457cb23 | |||
| 4baec1e672 | |||
| c702c5a4a0 | |||
| 24c2c5fc1b | |||
| 8797b033a3 | |||
| b2c4cdc4aa | |||
| ae37ec9c33 | |||
| ccc18739a4 | |||
| f48c4806b5 | |||
| 3aadf6d5d8 | |||
| 2f2679c775 | |||
| 442e23b106 | |||
| 65249f11b2 | |||
| 34b5c2ab18 | |||
| 93919183be | |||
| 1915641d78 | |||
| c2c7cad3eb | |||
| e4957e45af | |||
| 77bce9a5eb | |||
| 6538f0d587 | |||
| 5b0e7e5065 | |||
| d6cf8ba90e | |||
| 29568d0e3f | |||
| a571c4c93b | |||
| 073fed6c84 | |||
| 6847e1f899 | |||
| 427048ac11 | |||
| 240ea47c43 | |||
| 068d56a850 | |||
| 76d02da973 | |||
| 4664895ae3 | |||
| 5235c8cf83 | |||
| 4f040ac79f | |||
| dcad42bf4d | |||
| 2eea93a4a3 | |||
| 55e419475a | |||
| 9225d90e45 | |||
| 32dc6fef82 | |||
| 96fb65b6b0 | |||
| 936bbf9264 | |||
| 241a52ee7c | |||
| c66e285bf5 | |||
| 8c1ddc077d | |||
| 61c18af639 | |||
| b83df3ac4b | |||
| 53f1c4b7f3 | |||
| 6290bdbab0 | |||
| bd0d85cd37 | |||
| ba08417236 | |||
| 63281a3ca8 | |||
| fae0cf8454 | |||
| efddb31391 | |||
| 3a8e547e10 | |||
| fe09d18197 | |||
| 93bcf8d494 | |||
| 819f454bd8 | |||
| 7d58014b15 | |||
| 9dac95569c | |||
| 96c98ad275 | |||
| 7dcc0ec8bc | |||
| 09ecc03be5 | |||
| d7a5ffb938 | |||
| 0605dbb0ab | |||
| bf03de6be7 | |||
| d65ce361f7 | |||
| 3f600f6be7 | |||
| 9aab3c216c | |||
| 7935185812 | |||
| 4c1b1be96c | |||
| 5762830655 | |||
| 6d61206782 | |||
| b9dedef292 | |||
| f3caecd049 | |||
| e16780277f | |||
| 13484685a3 | |||
| 7b745d9837 | |||
| 52742064f6 | |||
| 2edf5582aa | |||
| 808eb92bf2 | |||
| c0b84c3d72 | |||
| e275d3e5c0 | |||
| b4ebad2c3c | |||
| 1ab76e1317 | |||
| 3d1104dd0b | |||
| 2df45d31de | |||
| 5436981e0d | |||
| fd2b1bec7d | |||
| bb1eaa5b56 | |||
| dae8eaa97a | |||
| 565b1ce94d | |||
| 543d5e18bf | |||
| 01368df6b1 | |||
| c666d55211 | |||
| 8cc4fd1d4f | |||
| c241a35861 | |||
| 674d231b7a | |||
| bebee59eb2 | |||
| 24f843b0db | |||
| f0259d13dd | |||
| 9dff965a52 | |||
| b4e6d10fbe | |||
| 1a9d88277f | |||
| 4ea956ca0e | |||
| 8060c2e1e3 | |||
| 8c96b65e59 | |||
| a407e94d3c | |||
| 310c790324 | |||
| 12b3d31f5c | |||
| d43dca61c1 | |||
| 20c14a03b2 | |||
| a6601f4eea | |||
| 220c7b1443 | |||
| 33bb63976d | |||
| 8db8f68d69 | |||
| 99b417217e | |||
| ac80386b8a | |||
| b96441a964 | |||
| 207f9563df | |||
| 7b84bf448b | |||
| 41a62fd412 | |||
| 1087dbf387 | |||
| e16d1d53c6 | |||
| 00c9ba6595 | |||
| 7c1995c67f | |||
| d2e0ae8b11 | |||
| f0aa49537d | |||
| 14ace294f3 | |||
| b53ef455a6 | |||
| 3dc6c6607d | |||
| dd91e05fb6 | |||
| 19fe5275b9 | |||
| 1aeb9ca332 | |||
| 520c240a19 | |||
| 48aaca13d0 | |||
| 88a31f4f39 | |||
| 5ce9e6606b | |||
| 4ea70b15e7 | |||
| d3cf2957cc | |||
| dbac595754 | |||
| d70e7b8788 | |||
| f6835852b6 | |||
| d5864726f6 | |||
| 3cc3271726 | |||
| 8aef9c8d28 | |||
| bcca00d110 | |||
| aa929161b4 | |||
| 70cb9cbf51 | |||
| fef3b97661 | |||
| 945f366195 | |||
| 8c0a851d87 |
@@ -13,7 +13,7 @@ jobs:
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'adopt'
|
||||
java-version: '11'
|
||||
java-version: '17'
|
||||
|
||||
- name: Assemble APK
|
||||
shell: bash
|
||||
|
||||
@@ -16,6 +16,6 @@ jobs:
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '11'
|
||||
java-version: '17'
|
||||
|
||||
- run: ./gradlew lint
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
_OpenTracks_ is a sport tracking application that completely respects your privacy.
|
||||
|
||||
[](https://github.com/humanetech-community/awesome-humane-tech)
|
||||
[](https://delightful.club/delightful-humane-design/#health)
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
@@ -40,8 +40,10 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<a href="https://weblate.bubu1.eu/engage/OpenTracks/">
|
||||
<img src="https://weblate.bubu1.eu/widgets/OpenTracks/-/opentracks/horizontal-auto.svg" alt="Translation status" />
|
||||
Translations are hosted on <a href="https://hosted.weblate.org/engage/opentracks/">
|
||||
hosted.weblate.org</a>.
|
||||
<a href="https://hosted.weblate.org/engage/opentracks/">
|
||||
<img src="https://hosted.weblate.org/widgets/opentracks/-/horizontal-auto.svg" alt="Translation status" />
|
||||
</a>
|
||||
|
||||
## Screenshots
|
||||
@@ -150,7 +152,7 @@ The developer documentation is in [README_API.md](README_API.md).
|
||||
## Project history
|
||||
|
||||
_OpenTracks_ is based upon Google _My Tracks app_ ([code](https://code.google.com/archive/p/mytracks/)).
|
||||
Initially, _My Tracks_ was initially released by Google in 2010 as [open-source software](http://google-latlong.blogspot.fr/2010/05/code-for-my-tracks-is-now-yours.html).
|
||||
_My Tracks_ was initially released by Google in 2010 as [open-source software](http://google-latlong.blogspot.fr/2010/05/code-for-my-tracks-is-now-yours.html).
|
||||
In 2016, [Google decided to discontinue](https://support.google.com/maps/answer/6333516) _My Tracks_ and stopped distributing it via the Google Play store in April 2016.
|
||||
Then [Plonk42](https://github.com/plonk42) conducted some maintenance work until 2016, so _My Tracks_ could still be used (based upon version _Google's MyTracks_ version 2.0.6).
|
||||
Plonk42's version is available [here](https://github.com/Plonk42/mytracks).
|
||||
|
||||
+5
-3
@@ -17,10 +17,10 @@ Application id: de.dennisguse.opentracks
|
||||
It if it properly tagged F-Droid takes care of building the app and publishing.
|
||||
Usually, it takes two days before the update is published (build, signed, and published).
|
||||
|
||||
The recent build activity can be found [here](https://f-droid.org/wiki/index.php?title=Special:RecentChanges&days=7&from=&hidebots=0&hideanons=1&hideliu=1&limit=500)
|
||||
The recent build activity can be found [here](https://monitor.f-droid.org/builds/build)
|
||||
|
||||
## Google Play Store
|
||||
Application id: de.dennisguse.opentracks.playstore
|
||||
Application id: `de.dennisguse.opentracks.playstore`
|
||||
|
||||
Requirement: `gem install fastlane`
|
||||
|
||||
@@ -35,4 +35,6 @@ Supported languages: https://support.google.com/googleplay/android-developer/tab
|
||||
|
||||
## Translations (localization)
|
||||
|
||||
Translations of the OpenTracks are handled in Weblate: [https://weblate.bubu1.eu/projects/OpenTracks/](https://weblate.bubu1.eu/projects/OpenTracks/).
|
||||
Translations of the OpenTracks are handled in
|
||||
Weblate: [https://hosted.weblate.org/projects/opentracks/](https://hosted.weblate.org/projects/opentracks)
|
||||
.
|
||||
+27
-20
@@ -6,7 +6,7 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.3.1'
|
||||
classpath 'com.android.tools.build:gradle:8.1.0-beta04'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,25 +47,30 @@ android {
|
||||
compileOptions {
|
||||
coreLibraryDesugaringEnabled true
|
||||
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
compileSdk 33
|
||||
compileSdk 34
|
||||
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
buildConfig true
|
||||
}
|
||||
|
||||
androidResources {
|
||||
generateLocaleConfig = true
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "de.dennisguse.opentracks"
|
||||
versionCode 5080
|
||||
versionName "v4.1.2"
|
||||
versionCode 5262
|
||||
versionName "v4.5.0"
|
||||
|
||||
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
||||
|
||||
minSdk 21
|
||||
targetSdk 33
|
||||
targetSdk 34
|
||||
|
||||
testInstrumentationRunner "de.dennisguse.opentracks.TestRunner"
|
||||
testInstrumentationRunnerArguments clearPackageData: 'true'
|
||||
@@ -106,11 +111,11 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
applicationVariants.all { variant ->
|
||||
applicationVariants.configureEach { variant ->
|
||||
variant.resValue "string", "applicationId", variant.applicationId
|
||||
|
||||
if (variant.buildType.name == 'nightly') {
|
||||
variant.outputs.all {
|
||||
variant.outputs.configureEach {
|
||||
setVersionCodeOverride(getVersionCode())
|
||||
setVersionNameOverride(getVersionName())
|
||||
outputFileName = "${applicationId}_${variant.versionCode}.apk"
|
||||
@@ -120,21 +125,23 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.2.2'
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:1.6.0-rc01'
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'androidx.preference:preference:1.2.0'
|
||||
implementation 'androidx.documentfile:documentfile:1.0.1'
|
||||
implementation 'com.google.android.material:material:1.7.0'
|
||||
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
||||
implementation 'com.google.android.material:material:1.9.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
implementation 'androidx.core:core-splashscreen:1.0.0'
|
||||
implementation 'androidx.core:core-splashscreen:1.0.1'
|
||||
implementation 'androidx.mediarouter:mediarouter:1.4.0'
|
||||
|
||||
androidTestImplementation 'androidx.test:core:1.4.0'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||
androidTestImplementation 'androidx.test:rules:1.4.0'
|
||||
androidTestImplementation 'androidx.test:runner:1.4.0'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
androidTestImplementation 'org.mockito:mockito-android:4.8.1'
|
||||
androidTestImplementation 'androidx.test:core:1.5.0'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||
androidTestImplementation 'androidx.test:rules:1.5.0'
|
||||
androidTestImplementation 'androidx.test:runner:1.5.2'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||
androidTestImplementation 'org.mockito:mockito-android:5.4.0'
|
||||
|
||||
androidTestUtil 'androidx.test:orchestrator:1.4.1'
|
||||
androidTestUtil 'androidx.test:orchestrator:1.4.2'
|
||||
}
|
||||
|
||||
@@ -7,5 +7,5 @@ Changes:
|
||||
Bugfix:
|
||||
- Speech announcements use MAY_DUCK (again)
|
||||
- ChartView shows markers (again)
|
||||
- Fixed landscape layout of TrackListActivity
|
||||
- Fixed landscape recordingLayout of TrackListActivity
|
||||
- Request legacy file API behavior (necessary for Android 10)
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.2.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Show heartrate zone information
|
||||
|
||||
Developer:
|
||||
- Migrated to hosted.weblate.org
|
||||
- Recording UI uses Views directly
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.2.1: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Speed sensor: acceptable wheel diameter is 100mm-4000mm
|
||||
|
||||
Bugfix:
|
||||
- Reconnect to a sensor if connection is lost
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
v4.2.2: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- Track icon wrongly shown in dialog
|
||||
- Textcolor for heartrate zone 1 was ignoring theme
|
||||
- Not all activityTypes were shown in dropdownlist
|
||||
- Crash due to color not found
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
v4.3.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Allow backups (incl. encrypted to the Google)
|
||||
- Android 13: allow app-specific language settings
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.3.1: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Setting to skip voice announcements on device speaker
|
||||
|
||||
Bugfix:
|
||||
- GPS disabled warning was falsely shown
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
v4.3.2: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- KML header was misformed (#1522)
|
||||
|
||||
Developer:
|
||||
- Use Java 17 (multi-line text, switch)
|
||||
- API33: locale config is generated
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
v4.3.3: OpenTracks
|
||||
|
||||
Bugfixes:
|
||||
- Hidden fields were shown (introduced in v4.2.0)
|
||||
- Speed was shown instead of pace (introduced in v4.2.0)
|
||||
@@ -0,0 +1,4 @@
|
||||
v4.3.4: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- Android 13: regional locales could not be selected (introduced in v4.3.0)
|
||||
@@ -0,0 +1,6 @@
|
||||
v4.3.5: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- Sensor parsing had a overflow one off bug (#1559)
|
||||
- Insert marker: don't crash if no camera app is installed (#1561)
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
v4.4.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Export all tracks as one file
|
||||
@@ -0,0 +1,14 @@
|
||||
v4.5.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Material3
|
||||
- App shortcut for aggregated statistics
|
||||
- New Unit: miles with meters
|
||||
|
||||
Bugfix:
|
||||
- GPS disabled warnings shown repeatedly (#1429)
|
||||
|
||||
Developer:
|
||||
- Use Java Records
|
||||
- Target API34
|
||||
- Refactor sensor handling
|
||||
@@ -2,13 +2,13 @@ v3.19.0: OpenTracks
|
||||
|
||||
Wijzigingen:
|
||||
- Ondersteuning voor Running Speed en Cadence (RCS) sensoren
|
||||
- Bij het delen van een track wordt een afbeelding gemaakt
|
||||
- Bij delen van een track: maakt een deel-afbeelding
|
||||
- Niet trillen bij starten van opnemen
|
||||
- Bij opnemen: UI toont recentst ontvangen hoogte, snelheid en richting (ook al is deze niet opgeslagen)
|
||||
- Bij opnemen: UI toont meest recente hoogte, snelheid en richting (zelfs als niet opgeslagen)
|
||||
|
||||
Bugfix:
|
||||
- Berekening van totale tijd mag geen pauze-tijd bevatten (vereist opnieuw importeren)
|
||||
- (opnieuw) Gebruik aangepaste overzichten indien aanwezig
|
||||
- Berekening totale tijd mag geen pauze-tijd bevatten (vereist opnieuw importeren)
|
||||
- (opnieuw) Gebruik aanwezige aangepaste overzichten
|
||||
|
||||
Ontwikkelaar:
|
||||
- Upgrade naar SDK31 en AGP7.0.0 (vereist JDK11)
|
||||
|
||||
@@ -5,4 +5,4 @@ Wijzigingen:
|
||||
- Gesproken meldingen van hartslag
|
||||
|
||||
Bugfix:
|
||||
- Crash due to race condition
|
||||
- Vastloper door race condities
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
一個尊重您的隱私的運動蹤跡紀錄器。
|
||||
|
||||
運動與戶外活動賦予幸福、技能與自信。
|
||||
|
||||
由保持追蹤您的訓練來重視您的健康。
|
||||
紀錄您的跑步或健行,並且在您騎自行車時給您一台具有大螢幕的自行車電腦。
|
||||
用照片標記您一路上有趣的地點。
|
||||
保持紀錄非常詳細的統計資料以供分析。
|
||||
僅分享資料給您想要分享的其他人。
|
||||
|
||||
* 語音播報。
|
||||
* 支援藍芽 LE 感測器:心律、速度與距離 (騎自行車)、踏頻 (騎自行車)、功率計 (騎自行車)。
|
||||
* 海拔上升與下降:通過氣壓感測器。
|
||||
* EGM2008 海拔顯示 (平均海平面以上);匯出為 WGS84。
|
||||
* 匯出蹤跡資料為 KMZ (包含照片)、KML、GPX。
|
||||
* 沒有網路存取或額外的權限需求。
|
||||
* 深色與淺色主題,取決於系統設定。
|
||||
* 沒有廣告。
|
||||
|
||||
此為 自由軟體 / 免費軟體 / 開放原始碼
|
||||
這意味您可以使用、學習、修改與分享此專案的原始碼。
|
||||
授權條款:Apache 2.0
|
||||
@@ -0,0 +1 @@
|
||||
一個尊重您的隱私的運動蹤跡紀錄器。
|
||||
@@ -0,0 +1 @@
|
||||
OpenTracks
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
android.useAndroidX=true
|
||||
org.gradle.jvmargs=-Xmx1024m -XX:MaxPermSize=256m
|
||||
org.gradle.jvmargs=-Xmx1024m
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+2
-1
@@ -1,5 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-all.zip
|
||||
networkTimeout=10000
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@@ -80,10 +80,10 @@ do
|
||||
esac
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
@@ -143,12 +143,16 @@ fi
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@@ -205,6 +209,12 @@ set -- \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
|
||||
Vendored
+9
-6
@@ -14,7 +14,7 @@
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@@ -25,7 +25,8 @@
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
@@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
@@ -11,6 +11,7 @@ import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.anything;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.childAtPosition;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
||||
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||
@@ -30,7 +31,7 @@ public class EspressoDeleteTrackTest {
|
||||
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule mGrantPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
public GrantPermissionRule mGrantPermissionRule = TestUtil.createGrantPermissionRule();
|
||||
|
||||
@Test
|
||||
public void espressoDeleteTrackTest() {
|
||||
@@ -51,7 +52,15 @@ public class EspressoDeleteTrackTest {
|
||||
onData(anything()).inAdapterView(withId(R.id.track_list)).atPosition(0).perform(longClick());
|
||||
|
||||
// open menu and delete selected track
|
||||
onView(allOf(withContentDescription("More options"), isDisplayed()))
|
||||
//TODO openActionBarOverflowOrOptionsMenu(); doesn't work
|
||||
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());
|
||||
|
||||
@@ -15,6 +15,8 @@ import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
||||
|
||||
import android.Manifest;
|
||||
|
||||
import androidx.test.espresso.ViewInteraction;
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
@@ -38,7 +40,7 @@ public class EspressoEditTrackRecordingTest {
|
||||
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule mGrantPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
public GrantPermissionRule mGrantPermissionRule = TestUtil.createGrantPermissionRule();
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
|
||||
@@ -13,6 +13,8 @@ import static org.hamcrest.Matchers.anything;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.selectTabAtIndex;
|
||||
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
||||
|
||||
import android.Manifest;
|
||||
|
||||
import androidx.test.espresso.ViewInteraction;
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
@@ -31,8 +33,7 @@ public class EspressoUITest {
|
||||
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule mGrantPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
|
||||
public GrantPermissionRule mGrantPermissionRule = TestUtil.createGrantPermissionRule();
|
||||
@LargeTest
|
||||
@Test
|
||||
public void record_stop_resume_stop_finish() {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
import android.Manifest;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
|
||||
import de.dennisguse.opentracks.test.BuildConfig;
|
||||
|
||||
public class TestUtil {
|
||||
|
||||
public static GrantPermissionRule createGrantPermissionRule() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
return GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.BLUETOOTH_CONNECT, Manifest.permission.POST_NOTIFICATIONS);
|
||||
}
|
||||
return GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.BLUETOOTH_CONNECT);
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ public class ChartPointTest {
|
||||
statistics.setTotalTime(Duration.ofSeconds(1000));
|
||||
|
||||
// when
|
||||
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), false, UnitSystem.IMPERIAL);
|
||||
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), false, UnitSystem.IMPERIAL_FEET);
|
||||
|
||||
// then
|
||||
assertEquals(1000000, (long) point.getTimeOrDistance());
|
||||
|
||||
@@ -96,16 +96,7 @@ public class TestDataUtil {
|
||||
return new TrackData(track, trackPoints, markers);
|
||||
}
|
||||
|
||||
public static class TrackData {
|
||||
public final Track track;
|
||||
public final List<TrackPoint> trackPoints;
|
||||
public final List<Marker> markers;
|
||||
|
||||
public TrackData(Track track, List<TrackPoint> trackPoints, List<Marker> markers) {
|
||||
this.track = track;
|
||||
this.trackPoints = trackPoints;
|
||||
this.markers = markers;
|
||||
}
|
||||
public record TrackData(Track track, List<TrackPoint> trackPoints, List<Marker> markers) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
+2
-2
@@ -424,7 +424,7 @@ public class CustomContentProviderUtilsTest {
|
||||
Marker.Id markerId = new Marker.Id(System.currentTimeMillis());
|
||||
marker.setId(markerId);
|
||||
ContentValues contentValues = contentProviderUtils.createContentValues(marker);
|
||||
assertEquals(markerId.getId(), contentValues.get(MarkerColumns._ID));
|
||||
assertEquals(markerId.id(), contentValues.get(MarkerColumns._ID));
|
||||
assertEquals((int) (TestDataUtil.INITIAL_LONGITUDE * 1000000), contentValues.get(MarkerColumns.LONGITUDE));
|
||||
assertEquals(TEST_DESC, contentValues.get(MarkerColumns.DESCRIPTION));
|
||||
}
|
||||
@@ -458,7 +458,7 @@ public class CustomContentProviderUtilsTest {
|
||||
when(cursorMock.getLong(columnIndex++)).thenReturn(trackId);
|
||||
|
||||
Marker marker = contentProviderUtils.createMarker(cursorMock);
|
||||
assertEquals(id, marker.getId().getId());
|
||||
assertEquals(id, marker.getId().id());
|
||||
assertEquals(name, marker.getName());
|
||||
assertEquals(trackId, marker.getTrackId().getId());
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ public class TrackSelectionTest extends TestCase {
|
||||
@Test
|
||||
public void testFilterBuildSelection_onlyOneCategory() {
|
||||
// given
|
||||
TrackSelection filter = new TrackSelection().addCategory("running");
|
||||
TrackSelection filter = new TrackSelection().addActivityType("running");
|
||||
|
||||
// when
|
||||
SelectionData selection = filter.buildSelection();
|
||||
@@ -82,10 +82,10 @@ public class TrackSelectionTest extends TestCase {
|
||||
public void testFilterBuildSelection_severalCategories() {
|
||||
// given
|
||||
TrackSelection filter = new TrackSelection()
|
||||
.addCategory("running")
|
||||
.addCategory("road biking")
|
||||
.addCategory("mountain biking")
|
||||
.addCategory("trail walking");
|
||||
.addActivityType("running")
|
||||
.addActivityType("road biking")
|
||||
.addActivityType("mountain biking")
|
||||
.addActivityType("trail walking");
|
||||
|
||||
// when
|
||||
SelectionData selection = filter.buildSelection();
|
||||
@@ -136,8 +136,8 @@ public class TrackSelectionTest extends TestCase {
|
||||
.addTrackId(trackId1)
|
||||
.addTrackId(trackId2)
|
||||
.addTrackId(trackId3)
|
||||
.addCategory("running")
|
||||
.addCategory("road biking");
|
||||
.addActivityType("running")
|
||||
.addActivityType("road biking");
|
||||
|
||||
// when
|
||||
SelectionData selection = filter.buildSelection();
|
||||
@@ -188,8 +188,8 @@ public class TrackSelectionTest extends TestCase {
|
||||
long oneDay = 24 * 60 * 60 * 1000;
|
||||
|
||||
TrackSelection filter = new TrackSelection()
|
||||
.addCategory("running")
|
||||
.addCategory("road biking")
|
||||
.addActivityType("running")
|
||||
.addActivityType("road biking")
|
||||
.addDateRange(instant, instant.plusMillis(oneDay));
|
||||
|
||||
// when
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ public class DistanceFormatterTest {
|
||||
public void testFormatDistance_imperial() {
|
||||
DistanceFormatter formatter = DistanceFormatter.Builder()
|
||||
.setDecimalCount(2)
|
||||
.setUnit(UnitSystem.IMPERIAL)
|
||||
.setUnit(UnitSystem.IMPERIAL_FEET)
|
||||
.build(context);
|
||||
|
||||
// A large number in imperial
|
||||
|
||||
@@ -36,7 +36,7 @@ public class SpeedFormatterTest {
|
||||
public void testGetSpeedParts_pace_imperial() {
|
||||
SpeedFormatter formatter = SpeedFormatter.Builder()
|
||||
.setDecimalCount(2)
|
||||
.setUnit(UnitSystem.IMPERIAL)
|
||||
.setUnit(UnitSystem.IMPERIAL_FEET)
|
||||
.setReportSpeedOrPace(false)
|
||||
.build(context);
|
||||
|
||||
@@ -72,7 +72,7 @@ public class SpeedFormatterTest {
|
||||
public void testGetSpeedParts_speed_imperial() {
|
||||
SpeedFormatter formatter = SpeedFormatter.Builder()
|
||||
.setDecimalCount(2)
|
||||
.setUnit(UnitSystem.IMPERIAL)
|
||||
.setUnit(UnitSystem.IMPERIAL_FEET)
|
||||
.setReportSpeedOrPace(true)
|
||||
.build(context);
|
||||
|
||||
|
||||
+12
-10
@@ -27,21 +27,23 @@ public class KmlTrackExporterTest {
|
||||
*/
|
||||
@Test
|
||||
public void writeCloseSegment_only_write_sensordata_if_present() {
|
||||
String expected = "<when>1970-01-01T00:00:00Z</when>\n" +
|
||||
"<coord/>\n" +
|
||||
"<when>1970-01-01T01:00:00+01:00</when>\n" +
|
||||
"<coord/>\n" +
|
||||
"<ExtendedData>\n" +
|
||||
"<SchemaData schemaUrl=\"#schema\">\n" +
|
||||
"</SchemaData>\n" +
|
||||
"</ExtendedData>\n" +
|
||||
"</Track>\n";
|
||||
String expected = """
|
||||
<when>1970-01-01T00:00:00Z</when>
|
||||
<coord/>
|
||||
<when>1970-01-01T01:00:00+01:00</when>
|
||||
<coord/>
|
||||
<ExtendedData>
|
||||
<SchemaData schemaUrl="#schema">
|
||||
</SchemaData>
|
||||
</ExtendedData>
|
||||
</Track>
|
||||
""";
|
||||
|
||||
// given
|
||||
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0));
|
||||
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
KMLTrackExporter kmlTrackWriter = (KMLTrackExporter) TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context);
|
||||
KMLTrackExporter kmlTrackWriter = (KMLTrackExporter) TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context, null);
|
||||
kmlTrackWriter.prepare(outputStream);
|
||||
|
||||
kmlTrackWriter.writeTrackPoint(ZoneOffset.UTC, trackPoint);
|
||||
|
||||
+38
-44
@@ -12,7 +12,6 @@ import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
@@ -58,7 +57,6 @@ 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.AltitudeSumManager;
|
||||
import de.dennisguse.opentracks.sensors.BluetoothRemoteSensorManager;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadence;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingDistanceSpeed;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower;
|
||||
@@ -98,7 +96,7 @@ public class ExportImportTest {
|
||||
private final ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
|
||||
|
||||
private static final String TRACK_ICON = "the track icon";
|
||||
private static final String TRACK_CATEGORY = "the category";
|
||||
private static final String TRACK_ACTIVITY_TYPE = "the activity type";
|
||||
private static final String TRACK_DESCRIPTION = "the description";
|
||||
|
||||
private File tmpFile;
|
||||
@@ -166,7 +164,7 @@ public class ExportImportTest {
|
||||
service.insertMarker("Marker 2", "Marker 2 category", "Marker 2 desc", null);
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:02:18Z");
|
||||
trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, null, trackPointCreator));
|
||||
trackPointCreator.getSensorManager().sensorDataSet = new SensorDataSet();
|
||||
service.endCurrentTrack();
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:03:20Z");
|
||||
@@ -179,12 +177,11 @@ public class ExportImportTest {
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:03:23Z", 3, 16.001, 10, 27, 15, 10, 0);
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:03:24Z");
|
||||
trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, null, trackPointCreator));
|
||||
service.endCurrentTrack();
|
||||
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
track.setIcon(TRACK_ICON);
|
||||
track.setCategory(TRACK_CATEGORY);
|
||||
track.setActivityType(TRACK_ACTIVITY_TYPE);
|
||||
track.setDescription(TRACK_DESCRIPTION);
|
||||
contentProviderUtils.updateTrack(track);
|
||||
|
||||
@@ -204,11 +201,11 @@ public class ExportImportTest {
|
||||
// given
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
TrackExporter trackExporter = TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context);
|
||||
TrackExporter trackExporter = TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context, contentProviderUtils);
|
||||
|
||||
// when
|
||||
// 1. export
|
||||
trackExporter.writeTrack(track, context.getContentResolver().openOutputStream(tmpFileUri));
|
||||
trackExporter.writeTrack(List.of(track), context.getContentResolver().openOutputStream(tmpFileUri));
|
||||
contentProviderUtils.deleteTrack(context, trackId);
|
||||
|
||||
// 2. import
|
||||
@@ -219,7 +216,7 @@ public class ExportImportTest {
|
||||
// 1. track
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(importedTrack);
|
||||
assertEquals(track.getCategory(), importedTrack.getCategory());
|
||||
assertEquals(track.getActivityType(), importedTrack.getActivityType());
|
||||
assertEquals(track.getDescription(), importedTrack.getDescription());
|
||||
assertEquals(track.getName(), importedTrack.getName());
|
||||
assertEquals(track.getIcon(), importedTrack.getIcon());
|
||||
@@ -287,10 +284,10 @@ public class ExportImportTest {
|
||||
editor.commit();
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context);
|
||||
TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context, contentProviderUtils);
|
||||
|
||||
// when
|
||||
trackExporter.writeTrack(track, context.getContentResolver().openOutputStream(tmpFileUri));
|
||||
trackExporter.writeTrack(List.of(track), context.getContentResolver().openOutputStream(tmpFileUri));
|
||||
|
||||
// 2. import
|
||||
InputStream inputStream = context.getContentResolver().openInputStream(tmpFileUri);
|
||||
@@ -310,11 +307,11 @@ public class ExportImportTest {
|
||||
// given
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context);
|
||||
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context, contentProviderUtils);
|
||||
|
||||
// when
|
||||
// 1. export
|
||||
trackExporter.writeTrack(track, context.getContentResolver().openOutputStream(tmpFileUri));
|
||||
trackExporter.writeTrack(List.of(track), context.getContentResolver().openOutputStream(tmpFileUri));
|
||||
contentProviderUtils.deleteTrack(context, trackId);
|
||||
|
||||
// 2. import
|
||||
@@ -326,7 +323,7 @@ public class ExportImportTest {
|
||||
// 1. track
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(importedTrack);
|
||||
assertEquals(track.getCategory(), importedTrack.getCategory());
|
||||
assertEquals(track.getActivityType(), importedTrack.getActivityType());
|
||||
assertEquals(track.getDescription(), importedTrack.getDescription());
|
||||
assertEquals(track.getName(), importedTrack.getName());
|
||||
|
||||
@@ -428,11 +425,11 @@ public class ExportImportTest {
|
||||
editor.commit();
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context);
|
||||
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context, contentProviderUtils);
|
||||
|
||||
// when
|
||||
// 1. export
|
||||
trackExporter.writeTrack(track, context.getContentResolver().openOutputStream(tmpFileUri));
|
||||
trackExporter.writeTrack(List.of(track), context.getContentResolver().openOutputStream(tmpFileUri));
|
||||
|
||||
// 2. import
|
||||
InputStream inputStream = context.getContentResolver().openInputStream(tmpFileUri);
|
||||
@@ -453,11 +450,11 @@ public class ExportImportTest {
|
||||
// given
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
|
||||
TrackExporter trackExporter = TrackFileFormat.CSV.createTrackExporter(context);
|
||||
TrackExporter trackExporter = TrackFileFormat.CSV.createTrackExporter(context, contentProviderUtils);
|
||||
|
||||
// when
|
||||
// 1. export
|
||||
trackExporter.writeTrack(track, context.getContentResolver().openOutputStream(tmpFileUri));
|
||||
trackExporter.writeTrack(List.of(track), context.getContentResolver().openOutputStream(tmpFileUri));
|
||||
contentProviderUtils.deleteTrack(context, trackId);
|
||||
|
||||
// then
|
||||
@@ -495,37 +492,34 @@ public class ExportImportTest {
|
||||
}
|
||||
|
||||
private void mockBLESensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power) {
|
||||
trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, null, trackPointCreator) {
|
||||
@Override
|
||||
public SensorDataSet fill(@NonNull TrackPoint trackPoint) {
|
||||
SensorDataSet sensorDataSet = new SensorDataSet();
|
||||
sensorDataSet.set(new SensorDataCyclingPower("power", "power", Power.of(power)));
|
||||
sensorDataSet.set(new SensorDataHeartRate("heartRate", "heartRate", HeartRate.of(heartRate)));
|
||||
|
||||
SensorDataCyclingCadence cyclingCadence = Mockito.mock(SensorDataCyclingCadence.class);
|
||||
Mockito.when(cyclingCadence.hasValue()).thenReturn(true);
|
||||
Mockito.when(cyclingCadence.getValue()).thenReturn(Cadence.of(cadence));
|
||||
sensorDataSet.set(cyclingCadence);
|
||||
SensorDataSet sensorDataSet = new SensorDataSet();
|
||||
sensorDataSet.set(new SensorDataCyclingPower("power", "power", Power.of(power)));
|
||||
sensorDataSet.set(new SensorDataHeartRate("heartRate", "heartRate", HeartRate.of(heartRate)));
|
||||
|
||||
if (distance != null && speed != null) {
|
||||
SensorDataCyclingDistanceSpeed.Data distanceSpeedData = Mockito.mock(SensorDataCyclingDistanceSpeed.Data.class);
|
||||
Mockito.when(distanceSpeedData.getDistanceOverall()).thenReturn(distance);
|
||||
Mockito.when(distanceSpeedData.getSpeed()).thenReturn(Speed.of(speed));
|
||||
SensorDataCyclingDistanceSpeed distanceSpeed = Mockito.mock(SensorDataCyclingDistanceSpeed.class);
|
||||
Mockito.when(distanceSpeed.hasValue()).thenReturn(true);
|
||||
Mockito.when(distanceSpeed.getValue()).thenReturn(distanceSpeedData);
|
||||
sensorDataSet.set(distanceSpeed);
|
||||
}
|
||||
SensorDataCyclingCadence cyclingCadence = Mockito.mock(SensorDataCyclingCadence.class);
|
||||
Mockito.when(cyclingCadence.hasValue()).thenReturn(true);
|
||||
Mockito.when(cyclingCadence.getValue()).thenReturn(Cadence.of(cadence));
|
||||
sensorDataSet.set(cyclingCadence);
|
||||
|
||||
sensorDataSet.fillTrackPoint(trackPoint);
|
||||
return sensorDataSet;
|
||||
}
|
||||
});
|
||||
trackPointCreator.onChange(new SensorDataSet());
|
||||
if (distance != null && speed != null) {
|
||||
SensorDataCyclingDistanceSpeed.Data distanceSpeedData = Mockito.mock(SensorDataCyclingDistanceSpeed.Data.class);
|
||||
Mockito.when(distanceSpeedData.getDistanceOverall()).thenReturn(distance);
|
||||
Mockito.when(distanceSpeedData.getSpeed()).thenReturn(Speed.of(speed));
|
||||
SensorDataCyclingDistanceSpeed distanceSpeed = Mockito.mock(SensorDataCyclingDistanceSpeed.class);
|
||||
Mockito.when(distanceSpeed.hasValue()).thenReturn(true);
|
||||
Mockito.when(distanceSpeed.getValue()).thenReturn(distanceSpeedData);
|
||||
sensorDataSet.set(distanceSpeed);
|
||||
}
|
||||
|
||||
trackPointCreator.getSensorManager().sensorDataSet = sensorDataSet;
|
||||
trackPointCreator.onChange(sensorDataSet);
|
||||
|
||||
//TODO Might require: getSensorManager().sensorDataSet = new SensorDataSet()
|
||||
}
|
||||
|
||||
private void mockAltitudeChange(TrackPointCreator trackPointCreator, float altitudeGain) {
|
||||
AltitudeSumManager altitudeSumManager = trackPointCreator.getAltitudeSumManager();
|
||||
AltitudeSumManager altitudeSumManager = trackPointCreator.getSensorManager().getAltitudeSumManager();
|
||||
altitudeSumManager.setAltitudeGain_m(altitudeGain);
|
||||
altitudeSumManager.setAltitudeLoss_m(altitudeGain);
|
||||
}
|
||||
@@ -544,6 +538,6 @@ public class ExportImportTest {
|
||||
mockAltitudeChange(trackPointCreator, altitudeGain);
|
||||
|
||||
trackPointCreator.setClock(time);
|
||||
trackPointCreator.getGpsHandler().onLocationChanged(location);
|
||||
trackPointCreator.onChange(location);
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -79,7 +79,7 @@ public class GPXTrackImporterTest {
|
||||
// 2. track
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(importedTrack);
|
||||
assertEquals("the category", importedTrack.getCategory());
|
||||
assertEquals("the category", importedTrack.getActivityType());
|
||||
assertEquals("the description", importedTrack.getDescription());
|
||||
assertEquals("2021-01-07 22:51", importedTrack.getName());
|
||||
assertEquals("UNKNOWN", importedTrack.getIcon());
|
||||
@@ -133,7 +133,7 @@ public class GPXTrackImporterTest {
|
||||
// 2. track
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(importedTrack);
|
||||
assertEquals("", importedTrack.getCategory());
|
||||
assertEquals("", importedTrack.getActivityType());
|
||||
assertEquals("", importedTrack.getDescription());
|
||||
assertEquals("20210907_213924.gpx", importedTrack.getName());
|
||||
assertEquals("", importedTrack.getIcon());
|
||||
@@ -181,7 +181,7 @@ public class GPXTrackImporterTest {
|
||||
// 2. track
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(importedTrack);
|
||||
assertEquals("", importedTrack.getCategory());
|
||||
assertEquals("", importedTrack.getActivityType());
|
||||
assertEquals("", importedTrack.getDescription());
|
||||
assertEquals("20210907_213924.gpx", importedTrack.getName());
|
||||
assertEquals("", importedTrack.getIcon());
|
||||
@@ -231,8 +231,8 @@ public class GPXTrackImporterTest {
|
||||
importTrackId = importer.importFile(inputStream).get(0);
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
|
||||
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context);
|
||||
trackExporter.writeTrack(importedTrack, outputStream);
|
||||
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context, contentProviderUtils);
|
||||
trackExporter.writeTrack(List.of(importedTrack), outputStream);
|
||||
|
||||
// then
|
||||
String expected = new BufferedReader(
|
||||
|
||||
+3
-3
@@ -70,7 +70,7 @@ public class KMLTrackImporterTest {
|
||||
// 1. track
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(importedTrack);
|
||||
assertEquals("", importedTrack.getCategory());
|
||||
assertEquals("", importedTrack.getActivityType());
|
||||
assertEquals("", importedTrack.getDescription());
|
||||
assertEquals("", importedTrack.getName());
|
||||
assertEquals("", importedTrack.getIcon());
|
||||
@@ -119,7 +119,7 @@ public class KMLTrackImporterTest {
|
||||
// 1. track
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(importedTrack);
|
||||
assertEquals("", importedTrack.getCategory());
|
||||
assertEquals("", importedTrack.getActivityType());
|
||||
assertEquals("", importedTrack.getDescription());
|
||||
assertEquals("", importedTrack.getName());
|
||||
assertEquals("", importedTrack.getIcon());
|
||||
@@ -159,7 +159,7 @@ public class KMLTrackImporterTest {
|
||||
// 1. track
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(importedTrack);
|
||||
assertEquals("unknown", importedTrack.getCategory());
|
||||
assertEquals("unknown", importedTrack.getActivityType());
|
||||
assertEquals("Test Track", importedTrack.getDescription());
|
||||
assertEquals("2020-11-28 18:06", importedTrack.getName());
|
||||
assertEquals("UNKNOWN", importedTrack.getIcon());
|
||||
|
||||
@@ -1,47 +1,55 @@
|
||||
package de.dennisguse.opentracks.sensors;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.AtmosphericPressure;
|
||||
import de.dennisguse.opentracks.sensors.driver.BarometerInternal;
|
||||
|
||||
/**
|
||||
* NOTE: Test data is completely artificial.
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class AltitudeSumManagerTest {
|
||||
|
||||
private final AltitudeSumManager subject = new AltitudeSumManager();
|
||||
|
||||
private static void addSensorValue(AltitudeSumManager altitudeSumManager, float[] values) {
|
||||
for (float f : values) {
|
||||
altitudeSumManager.onSensorValueChanged(f);
|
||||
altitudeSumManager.onSensorValueChanged(AtmosphericPressure.ofHPA(f));
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
subject.reset();
|
||||
}
|
||||
@Mock
|
||||
BarometerInternal driver;
|
||||
|
||||
@Test
|
||||
public void getAltitudeGainLoss_downhill() {
|
||||
// given
|
||||
subject.setConnected(true);
|
||||
AltitudeSumManager subject = new AltitudeSumManager(driver);
|
||||
|
||||
when(driver.isConnected()).thenReturn(true);
|
||||
|
||||
// then
|
||||
addSensorValue(subject, new float[]{1015f, 1015.01f, 1015.02f, 1015.03f, 1015.04f, 1015.05f, 1015.06f, 1015.07f, 1015.08f, 1015.09f, 1015.10f, 1015.11f, 1015.12f, 1015.13f, 1015.14f, 1015.15f});
|
||||
addSensorValue(subject, new float[]{1015f, 1015.01f, 1015.02f, 1015.03f, 1015.04f, 1015.05f, 1015.06f, 1015.07f, 1015.08f, 1015.09f, 1015.10f, 1015.11f, 1015.12f, 1015.13f, 1018f, 1018.1f, 1018.1f, 1018.1f, 1018.1f});
|
||||
|
||||
// then
|
||||
Assert.assertEquals(0f, subject.getAltitudeGain_m(), 0.01);
|
||||
Assert.assertEquals(48.0, subject.getAltitudeLoss_m(), 0.01);
|
||||
Assert.assertEquals(15f, subject.getAltitudeLoss_m(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sensorUnavailable() {
|
||||
// given
|
||||
subject.setConnected(false);
|
||||
AltitudeSumManager subject = new AltitudeSumManager(driver);
|
||||
|
||||
when(driver.isConnected()).thenReturn(true);
|
||||
|
||||
// then
|
||||
subject.onSensorValueChanged(999f);
|
||||
subject.onSensorValueChanged(AtmosphericPressure.ofHPA(999f));
|
||||
|
||||
// then
|
||||
Assert.assertNull(subject.getAltitudeGain_m());
|
||||
|
||||
@@ -7,8 +7,10 @@ import android.bluetooth.BluetoothGattCharacteristic;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.AtmosphericPressure;
|
||||
import de.dennisguse.opentracks.data.models.Cadence;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDistanceSpeed;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower;
|
||||
@@ -19,32 +21,45 @@ public class BluetoothUtilsTest {
|
||||
@Test
|
||||
public void parseHeartRate_uint8() {
|
||||
// given
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.HEARTRATE.getServiceUUID(), 0, 0);
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.HEARTRATE.serviceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{0x02, 0x3C});
|
||||
|
||||
// when
|
||||
int heartRate = BluetoothUtils.parseHeartRate(characteristic);
|
||||
HeartRate heartRate = BluetoothUtils.parseHeartRate(characteristic);
|
||||
|
||||
// then
|
||||
assertEquals(60, heartRate);
|
||||
assertEquals(HeartRate.of(60), heartRate);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseHeartRate_uint16() {
|
||||
// given
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.HEARTRATE.getServiceUUID(), 0, 0);
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.HEARTRATE.serviceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{0x01, 0x01, 0x01});
|
||||
|
||||
// when
|
||||
int heartRate = BluetoothUtils.parseHeartRate(characteristic);
|
||||
HeartRate heartRate = BluetoothUtils.parseHeartRate(characteristic);
|
||||
|
||||
// then
|
||||
assertEquals(257, heartRate);
|
||||
assertEquals(HeartRate.of(257), heartRate);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseEnvironmentalSensing_Pa() {
|
||||
// given
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.BAROMETRIC_PRESSURE.serviceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{(byte) 0xB2, (byte) 0x48, (byte) 0x0F, (byte) 0x00});
|
||||
|
||||
// when
|
||||
AtmosphericPressure pressure = BluetoothUtils.parseEnvironmentalSensing(characteristic);
|
||||
|
||||
// then
|
||||
assertEquals(AtmosphericPressure.ofPA(100165), pressure);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCyclingSpeedCadence_crankOnly() {
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.getServiceUUID(), 0, 0);
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{0x02, (byte) 0xC8, 0x00, 0x00, 0x00, 0x06, (byte) 0x99});
|
||||
|
||||
// when
|
||||
@@ -57,7 +72,7 @@ public class BluetoothUtilsTest {
|
||||
|
||||
@Test
|
||||
public void parseCyclingSpeedCadence_wheelOnly() {
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.getServiceUUID(), 0, 0);
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{0x01, (byte) 0xFF, (byte) 0xFF, 0, 1, 0x45, (byte) 0x99});
|
||||
|
||||
// when
|
||||
@@ -70,7 +85,7 @@ public class BluetoothUtilsTest {
|
||||
|
||||
@Test
|
||||
public void parseCyclingSpeedCadence_crankWheel() {
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.getServiceUUID(), 0, 0);
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{0x03, (byte) 0xC8, 0x00, 0x00, 0x01, 0x06, (byte) 0x99, (byte) 0xE1, 0x00, 0x45, (byte) 0x99});
|
||||
|
||||
// when
|
||||
@@ -83,34 +98,34 @@ public class BluetoothUtilsTest {
|
||||
|
||||
@Test
|
||||
public void parseCyclingPower_power() {
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_POWER.getServiceUUID(), 0, 0);
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_POWER.serviceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{0, 0, 40, 0});
|
||||
|
||||
// when
|
||||
SensorDataCyclingPower.Data powerCadence = BluetoothUtils.parseCyclingPower("", "", characteristic);
|
||||
|
||||
// then
|
||||
assertEquals(40, powerCadence.getPower().getValue().getW(), 0.01);
|
||||
assertEquals(40, powerCadence.power().getValue().getW(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseCyclingPower_power_with_cadence() {
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_POWER.getServiceUUID(), 0, 0);
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_POWER.serviceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{0x2C, 0x00, 0x00, 0x00, (byte) 0x9F, 0x00, 0x0C, 0x00, (byte) 0xE5, 0x42});
|
||||
|
||||
// when
|
||||
SensorDataCyclingPower.Data powerCadence = BluetoothUtils.parseCyclingPower("", "", characteristic);
|
||||
|
||||
// then
|
||||
assertEquals(0, powerCadence.getPower().getValue().getW(), 0.01);
|
||||
assertEquals(0, powerCadence.power().getValue().getW(), 0.01);
|
||||
|
||||
assertEquals(12, powerCadence.getCadence().getCrankRevolutionsCount());
|
||||
assertEquals(17125, powerCadence.getCadence().getCrankRevolutionsTime());
|
||||
assertEquals(12, powerCadence.cadence().getCrankRevolutionsCount());
|
||||
assertEquals(17125, powerCadence.cadence().getCrankRevolutionsTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseRunningSpeedAndCadence_with_distance() {
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.RUNNING_SPEED_CADENCE.getServiceUUID(), 0, 0);
|
||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.RUNNING_SPEED_CADENCE.serviceUUID(), 0, 0);
|
||||
characteristic.setValue(new byte[]{2, 0, 5, 80, (byte) 0xFF, (byte) 0xFF, 0, 1});
|
||||
|
||||
// when
|
||||
|
||||
@@ -5,6 +5,8 @@ import static org.junit.Assert.assertNull;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.AtmosphericPressure;
|
||||
|
||||
public class PressureSensorUtilsTest {
|
||||
|
||||
// real data at 500Hz stationary
|
||||
@@ -12,11 +14,11 @@ public class PressureSensorUtilsTest {
|
||||
public void altitudeChanges_none() {
|
||||
// given
|
||||
float[] sensorValues_hPa = new float[]{1015.6876f, 1015.699f, 1015.70905f, 1015.71075f, 1015.7224f, 1015.72f, 1015.7373f, 1015.7481f, 1015.74133f, 1015.73553f, 1015.7462f, 1015.74896f, 1015.752f, 1015.754f, 1015.7456f, 1015.73303f, 1015.7288f, 1015.7239f, 1015.7298f, 1015.7387f, 1015.72534f, 1015.72577f, 1015.72797f, 1015.71796f, 1015.7113f, 1015.7097f, 1015.7096f, 1015.7013f, 1015.7072f, 1015.71063f, 1015.7088f, 1015.7115f, 1015.713f, 1015.7121f, 1015.7062f, 1015.7062f, 1015.6994f, 1015.69617f, 1015.68945f, 1015.698f, 1015.6913f, 1015.69696f, 1015.69293f, 1015.6955f, 1015.6928f, 1015.6939f, 1015.68787f, 1015.6881f, 1015.68536f, 1015.68726f, 1015.6879f, 1015.6904f, 1015.6937f, 1015.69476f, 1015.7046f, 1015.7019f, 1015.7079f, 1015.71124f, 1015.71216f, 1015.71436f, 1015.7171f, 1015.7186f, 1015.7186f, 1015.7246f, 1015.72046f, 1015.7211f, 1015.72375f, 1015.7194f, 1015.72125f, 1015.7161f, 1015.71185f, 1015.7188f, 1015.721f, 1015.7252f, 1015.72266f, 1015.7236f, 1015.72614f, 1015.7266f, 1015.7344f, 1015.7376f, 1015.7337f, 1015.7253f, 1015.71875f, 1015.7219f, 1015.7219f, 1015.7317f, 1015.7284f, 1015.73444f, 1015.73584f, 1015.7369f, 1015.73254f, 1015.7369f, 1015.7336f, 1015.73254f, 1015.73676f, 1015.74176f, 1015.74506f, 1015.7478f, 1015.755f, 1015.7515f, 1015.7543f, 1015.7459f, 1015.7468f, 1015.7492f, 1015.74585f, 1015.74756f, 1015.74756f, 1015.75085f, 1015.754f, 1015.75336f, 1015.7568f, 1015.75165f, 1015.761f, 1015.7527f, 1015.75684f, 1015.7635f, 1015.7585f, 1015.7552f, 1015.7475f, 1015.74506f, 1015.7542f, 1015.75586f, 1015.74176f, 1015.74414f, 1015.7501f, 1015.751f, 1015.7478f, 1015.755f, 1015.75836f, 1015.7592f, 1015.75256f, 1015.7559f, 1015.76654f, 1015.76154f, 1015.766f, 1015.77014f, 1015.76904f, 1015.76013f, 1015.7674f, 1015.76514f, 1015.7674f, 1015.7635f, 1015.76404f, 1015.7607f, 1015.754f, 1015.7574f, 1015.7607f, 1015.7607f, 1015.7607f, 1015.754f, 1015.7567f, 1015.7616f, 1015.7643f, 1015.7666f, 1015.7609f, 1015.766f, 1015.76764f, 1015.755f, 1015.75757f, 1015.74927f, 1015.74927f, 1015.7426f, 1015.745f, 1015.745f, 1015.7409f, 1015.7376f, 1015.7326f, 1015.74164f, 1015.74664f, 1015.745f, 1015.7459f, 1015.7502f, 1015.7493f, 1015.7476f, 1015.7493f, 1015.751f, 1015.7601f, 1015.76434f, 1015.766f, 1015.756f, 1015.7617f, 1015.7623f, 1015.76404f, 1015.76733f, 1015.7607f, 1015.7623f, 1015.76337f, 1015.7584f, 1015.7573f, 1015.7502f, 1015.7534f, 1015.7534f, 1015.7601f, 1015.75684f, 1015.7518f, 1015.75574f, 1015.7524f, 1015.7507f, 1015.7484f, 1015.7502f, 1015.7534f, 1015.7623f, 1015.75903f, 1015.75574f, 1015.75574f, 1015.7573f, 1015.7524f, 1015.75574f, 1015.75574f, 1015.7607f, 1015.7573f, 1015.754f, 1015.74677f, 1015.754f, 1015.75574f, 1015.75903f, 1015.7573f, 1015.7551f, 1015.7573f, 1015.754f, 1015.7507f, 1015.7374f, 1015.739f, 1015.73566f, 1015.73566f, 1015.7367f, 1015.7417f, 1015.7417f, 1015.739f, 1015.74835f, 1015.74567f, 1015.7517f, 1015.74066f, 1015.7374f, 1015.739f, 1015.7417f, 1015.73065f, 1015.72894f, 1015.73505f, 1015.73334f, 1015.74005f, 1015.74005f, 1015.7367f, 1015.7434f, 1015.744f, 1015.7423f, 1015.7384f, 1015.739f, 1015.7374f, 1015.7374f, 1015.7374f, 1015.7341f, 1015.73505f, 1015.7341f, 1015.7267f, 1015.72833f, 1015.7234f, 1015.7167f, 1015.725f, 1015.7217f, 1015.7234f, 1015.73004f, 1015.7324f, 1015.7423f, 1015.74005f, 1015.73505f, 1015.73004f, 1015.739f, 1015.7417f, 1015.74567f, 1015.74066f, 1015.74146f, 1015.74146f, 1015.73816f, 1015.7274f, 1015.72906f, 1015.7274f, 1015.7341f, 1015.739f, 1015.744f, 1015.7417f, 1015.7374f, 1015.7324f, 1015.73816f, 1015.74146f, 1015.7374f, 1015.7341f, 1015.7365f, 1015.7365f, 1015.74146f, 1015.74414f, 1015.7475f, 1015.7448f, 1015.74146f, 1015.74585f, 1015.7475f, 1015.75146f, 1015.75146f, 1015.7591f, 1015.75476f, 1015.7614f, 1015.75476f, 1015.7492f, 1015.74817f, 1015.7498f, 1015.7515f, 1015.75586f, 1015.7509f, 1015.7448f, 1015.7525f, 1015.7581f, 1015.7592f};
|
||||
float firstSensorValue = sensorValues_hPa[0];
|
||||
AtmosphericPressure firstSensorValue = AtmosphericPressure.ofHPA(sensorValues_hPa[0]);
|
||||
|
||||
// when // then
|
||||
for (float v : sensorValues_hPa) {
|
||||
assertNull(PressureSensorUtils.computeChanges(firstSensorValue, v));
|
||||
assertNull(PressureSensorUtils.computeChanges(firstSensorValue, AtmosphericPressure.ofHPA(v)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,25 +27,25 @@ public class PressureSensorUtilsTest {
|
||||
@Test
|
||||
public void computeChanges_downhill() {
|
||||
float[] sensorValues_hPa = new float[]{1015f, 1015.01f, 1015.02f, 1015.03f, 1015.04f, 1015.05f, 1015.06f, 1015.07f, 1015.08f, 1015.09f, 1015.10f, 1015.11f, 1015.12f, 1015.13f, 1018f, 1018.1f, 1018.1f, 1018.1f, 1018.1f};
|
||||
float firstSensorValue = sensorValues_hPa[0];
|
||||
AtmosphericPressure firstSensorValue = AtmosphericPressure.ofHPA(sensorValues_hPa[0]);
|
||||
|
||||
// when
|
||||
float altitudeGain_m = 0;
|
||||
float altitudeLoss = 0;
|
||||
float lastUsedPressureValue_hPa = firstSensorValue;
|
||||
AtmosphericPressure lastUsedPressureValue = firstSensorValue;
|
||||
|
||||
for (float v : sensorValues_hPa) {
|
||||
PressureSensorUtils.AltitudeChange altitudeChange = PressureSensorUtils.computeChanges(lastUsedPressureValue_hPa, v);
|
||||
PressureSensorUtils.AltitudeChange altitudeChange = PressureSensorUtils.computeChanges(lastUsedPressureValue, AtmosphericPressure.ofHPA(v));
|
||||
if (altitudeChange != null) {
|
||||
altitudeGain_m += altitudeChange.getAltitudeGain_m();
|
||||
altitudeLoss += altitudeChange.getAltitudeLoss_m();
|
||||
lastUsedPressureValue_hPa = altitudeChange.getCurrentSensorValue_hPa();
|
||||
lastUsedPressureValue = altitudeChange.currentSensorValue();
|
||||
}
|
||||
}
|
||||
|
||||
// then
|
||||
assertEquals(0f, altitudeGain_m, 0.01);
|
||||
assertEquals(15.0f, altitudeLoss, 0.01);
|
||||
assertEquals(1016.80f, lastUsedPressureValue_hPa, 0.01); //Expect exponential smoothing
|
||||
assertEquals(1016.80f, lastUsedPressureValue.getHPA(), 0.01); //Expect exponential smoothing
|
||||
}
|
||||
}
|
||||
@@ -11,13 +11,44 @@ public class UintUtilsTest {
|
||||
assertEquals(0, UintUtils.diff(1, 1, UintUtils.UINT16_MAX));
|
||||
assertEquals(1, UintUtils.diff(2, 1, UintUtils.UINT16_MAX));
|
||||
assertEquals(3, UintUtils.diff(5, 2, UintUtils.UINT16_MAX));
|
||||
assertEquals(65534, UintUtils.diff(1, 2, UintUtils.UINT16_MAX));
|
||||
|
||||
assertEquals(65535, UintUtils.diff(1, 2, UintUtils.UINT16_MAX)); /* unsigned 16 arithmetic is modulo UINT16_MAX + 1, not modulo UINT16_MAX */
|
||||
assertEquals(65530, UintUtils.diff(UintUtils.UINT16_MAX, 5, UintUtils.UINT16_MAX));
|
||||
|
||||
/* Test modulo arithmetic for arguments that are out of range */
|
||||
|
||||
if (false) { /* false means UintUtils.diff() throws if arguments out of range */
|
||||
assertEquals(0, UintUtils.diff(65537, 1, UintUtils.UINT16_MAX));
|
||||
assertEquals(0, UintUtils.diff(1, 65537, UintUtils.UINT16_MAX));
|
||||
assertEquals(0, UintUtils.diff(65537, 65537, UintUtils.UINT16_MAX));
|
||||
assertEquals(0, UintUtils.diff(-65535, 1, UintUtils.UINT16_MAX));
|
||||
assertEquals(0, UintUtils.diff(1, -65535, UintUtils.UINT16_MAX));
|
||||
assertEquals(0, UintUtils.diff(-65535, -65535, UintUtils.UINT16_MAX));
|
||||
assertEquals(0, UintUtils.diff(-65535, 65537, UintUtils.UINT16_MAX));
|
||||
assertEquals(0, UintUtils.diff(65537, -65535, UintUtils.UINT16_MAX));
|
||||
}
|
||||
|
||||
/* The following tests are the above, but for 32-bit unsigned. */
|
||||
|
||||
assertEquals(0, UintUtils.diff(1, 1, UintUtils.UINT32_MAX));
|
||||
assertEquals(1, UintUtils.diff(2, 1, UintUtils.UINT32_MAX));
|
||||
assertEquals(3, UintUtils.diff(5, 2, UintUtils.UINT32_MAX));
|
||||
assertEquals(4294967295L, UintUtils.diff(1, 2, UintUtils.UINT32_MAX));
|
||||
assertEquals(4294967290L, UintUtils.diff(UintUtils.UINT32_MAX, 5, UintUtils.UINT32_MAX));
|
||||
|
||||
if (false) { /* false means UintUtils.diff() throws if arguments out of range */
|
||||
assertEquals(0, UintUtils.diff(4294967297L, 1, UintUtils.UINT32_MAX));
|
||||
assertEquals(0, UintUtils.diff(1, 4294967297L, UintUtils.UINT32_MAX));
|
||||
assertEquals(0, UintUtils.diff(4294967297L, 4294967297L, UintUtils.UINT32_MAX));
|
||||
assertEquals(0, UintUtils.diff(-4294967295L, 1, UintUtils.UINT32_MAX));
|
||||
assertEquals(0, UintUtils.diff(1, -4294967295L, UintUtils.UINT32_MAX));
|
||||
assertEquals(0, UintUtils.diff(-4294967295L, -4294967295L, UintUtils.UINT32_MAX));
|
||||
assertEquals(0, UintUtils.diff(-4294967295L, 4294967297L, UintUtils.UINT32_MAX));
|
||||
assertEquals(0, UintUtils.diff(65537, -4294967297L, UintUtils.UINT32_MAX));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void realData() {
|
||||
assertEquals(1, UintUtils.diff(381616, 381615, UintUtils.UINT32_MAX));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+20
-17
@@ -45,6 +45,8 @@ import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
|
||||
/**
|
||||
* Tests insert location.
|
||||
* Note: on API23, the TrackRecordingService may not be stopped properly before the next test.
|
||||
* So, if something fails, subsequent tests may be affected.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TrackRecordingServiceTestRecording {
|
||||
@@ -112,7 +114,7 @@ public class TrackRecordingServiceTestRecording {
|
||||
String startTime = "2020-02-02T02:02:02Z";
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// then
|
||||
assertEquals(new TrackStatistics(startTime, startTime, 0, 0, 0, 0, null, null)
|
||||
@@ -149,7 +151,7 @@ public class TrackRecordingServiceTestRecording {
|
||||
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// when
|
||||
String pauseTime = "2020-02-02T02:02:03Z";
|
||||
@@ -195,7 +197,7 @@ public class TrackRecordingServiceTestRecording {
|
||||
String starTime = "2020-02-02T02:02:02Z";
|
||||
trackPointCreator.setClock(starTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
String pauseTime = "2020-02-02T02:02:03Z";
|
||||
trackPointCreator.setClock(pauseTime);
|
||||
@@ -227,7 +229,7 @@ public class TrackRecordingServiceTestRecording {
|
||||
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
String stopTime = "2020-02-02T02:02:03Z";
|
||||
trackPointCreator.setClock(stopTime);
|
||||
@@ -237,7 +239,7 @@ public class TrackRecordingServiceTestRecording {
|
||||
String resumeTime = "2020-02-02T02:02:04Z";
|
||||
trackPointCreator.setClock(resumeTime);
|
||||
service.resumeTrack(trackId);
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// then
|
||||
new TrackPointAssert().assertEquals(List.of(
|
||||
@@ -258,12 +260,13 @@ public class TrackRecordingServiceTestRecording {
|
||||
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
BluetoothRemoteSensorManager remoteSensorManager = trackPointCreator.getRemoteSensorManager();
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
BluetoothRemoteSensorManager remoteSensorManager = trackPointCreator.getSensorManager().getBluetoothSensorManager();
|
||||
|
||||
// when
|
||||
String sensor1 = "2020-02-02T02:02:03Z";
|
||||
trackPointCreator.setClock(sensor1);
|
||||
|
||||
remoteSensorManager.onChanged(new SensorDataHeartRate("", "", HeartRate.of(5))); //Should be ignored
|
||||
|
||||
String sensor3 = "2020-02-02T02:02:13Z";
|
||||
@@ -297,7 +300,7 @@ public class TrackRecordingServiceTestRecording {
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
|
||||
// when
|
||||
@@ -371,7 +374,7 @@ public class TrackRecordingServiceTestRecording {
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:03Z";
|
||||
@@ -436,7 +439,7 @@ public class TrackRecordingServiceTestRecording {
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:03Z";
|
||||
@@ -500,7 +503,7 @@ public class TrackRecordingServiceTestRecording {
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:03Z";
|
||||
@@ -564,7 +567,7 @@ public class TrackRecordingServiceTestRecording {
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:03Z";
|
||||
@@ -652,7 +655,7 @@ public class TrackRecordingServiceTestRecording {
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:03Z";
|
||||
@@ -702,7 +705,7 @@ public class TrackRecordingServiceTestRecording {
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:03Z";
|
||||
@@ -748,7 +751,7 @@ public class TrackRecordingServiceTestRecording {
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:03Z";
|
||||
@@ -812,8 +815,8 @@ public class TrackRecordingServiceTestRecording {
|
||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||
trackPointCreator.setClock(startTime);
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
||||
BluetoothRemoteSensorManager remoteSensorManager = trackPointCreator.getRemoteSensorManager();
|
||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||
BluetoothRemoteSensorManager remoteSensorManager = trackPointCreator.getSensorManager().getBluetoothSensorManager();
|
||||
|
||||
// when
|
||||
String sensor1 = "2020-02-02T02:02:03Z";
|
||||
|
||||
+1
-12
@@ -1,15 +1,12 @@
|
||||
package de.dennisguse.opentracks.services;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.location.Location;
|
||||
|
||||
import androidx.test.rule.ServiceTestRule;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import de.dennisguse.opentracks.sensors.BluetoothRemoteSensorManager;
|
||||
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
@@ -22,14 +19,6 @@ public class TrackRecordingServiceTestUtils {
|
||||
public static void resetService(ServiceTestRule mServiceRule, Context context) throws TimeoutException {
|
||||
// Let's use default values.
|
||||
PreferencesUtils.clear();
|
||||
|
||||
// Reset service (if some previous test failed)
|
||||
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(new Intent(context, TrackRecordingService.class)))
|
||||
.getService();
|
||||
|
||||
service.getTrackPointCreator().setRemoteSensorManager(new BluetoothRemoteSensorManager(context, null, service.getTrackPointCreator()));
|
||||
service.getTrackPointCreator().setClock(Clock.systemUTC());
|
||||
service.endCurrentTrack();
|
||||
}
|
||||
|
||||
static void sendGPSLocation(TrackPointCreator trackPointCreator, String time, double latitude, double longitude, float accuracy, long speed) {
|
||||
@@ -41,6 +30,6 @@ public class TrackRecordingServiceTestUtils {
|
||||
location.setSpeed(speed);
|
||||
|
||||
trackPointCreator.setClock(time);
|
||||
trackPointCreator.getGpsHandler().onLocationChanged(location);
|
||||
trackPointCreator.getSensorManager().getGpsManager().onLocationChanged(location);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -185,7 +185,7 @@ public class VoiceAnnouncementUtilsTest {
|
||||
stats.setTotalAltitudeGain(6000f);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL, true, null, null).toString();
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_FEET, true, null, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 12.4 miles. 1 hour 5 minutes 10 seconds. Average moving speed 11.4 miles per hour.", announcement);
|
||||
@@ -207,7 +207,7 @@ public class VoiceAnnouncementUtilsTest {
|
||||
}
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL, true, lastInterval, null).toString();
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_FEET, true, lastInterval, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 8.8 miles. 16 minutes 39 seconds. Average moving speed 31.8 miles per hour. Lap speed 31.8 miles per hour.", announcement);
|
||||
@@ -223,7 +223,7 @@ public class VoiceAnnouncementUtilsTest {
|
||||
stats.setTotalAltitudeGain(6000f);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL, false, null, null).toString();
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_FEET, false, null, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 12.4 miles. 1 hour 5 minutes 10 seconds. Pace 5 minutes 15 seconds per mile.", announcement);
|
||||
@@ -245,7 +245,7 @@ public class VoiceAnnouncementUtilsTest {
|
||||
}
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL, false, lastInterval, null).toString();
|
||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_FEET, false, lastInterval, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 8.8 miles. 16 minutes 39 seconds. Pace 1 minute 53 seconds per mile. Lap time 1 minute 53 seconds per mile.", announcement);
|
||||
|
||||
+3
-3
@@ -26,7 +26,7 @@ import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class GPSHandlerTest {
|
||||
public class GPSManagerTest {
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
@@ -34,7 +34,7 @@ public class GPSHandlerTest {
|
||||
private TrackPointCreator trackPointCreator;
|
||||
|
||||
@InjectMocks
|
||||
private GPSHandler locationHandler;
|
||||
private GPSManager locationHandler;
|
||||
|
||||
@BeforeClass
|
||||
public static void preSetUp() {
|
||||
@@ -50,7 +50,7 @@ public class GPSHandlerTest {
|
||||
Mockito.when(trackPointCreator.createNow())
|
||||
.thenReturn(Instant.now());
|
||||
|
||||
locationHandler.onStart(context, new Handler());
|
||||
locationHandler.start(context, new Handler());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -0,0 +1,146 @@
|
||||
package de.dennisguse.opentracks.services.handlers;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static de.dennisguse.opentracks.services.handlers.GpsStatusValue.GPS_DISABLED;
|
||||
import static de.dennisguse.opentracks.services.handlers.GpsStatusValue.GPS_ENABLED;
|
||||
import static de.dennisguse.opentracks.services.handlers.GpsStatusValue.GPS_NONE;
|
||||
import static de.dennisguse.opentracks.services.handlers.GpsStatusValue.GPS_SIGNAL_BAD;
|
||||
import static de.dennisguse.opentracks.services.handlers.GpsStatusValue.GPS_SIGNAL_FIX;
|
||||
import static de.dennisguse.opentracks.services.handlers.GpsStatusValue.GPS_SIGNAL_LOST;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.Location;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class GpsStatusTest {
|
||||
|
||||
private 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 static Location badFix = new Location("gps");
|
||||
private final static Location ok = new Location("gps");
|
||||
|
||||
static {
|
||||
badFix.setAccuracy(50);
|
||||
|
||||
ok.setAccuracy(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartDisabledEnabledStop() {
|
||||
ArrayList<GpsStatusValue> statusList = new ArrayList<>();
|
||||
|
||||
// given
|
||||
GpsStatusManager subject = new GpsStatusManager(context, statusList::add, new Handler());
|
||||
|
||||
// when / then
|
||||
subject.start();
|
||||
assertEquals(List.of(GPS_ENABLED), statusList);
|
||||
|
||||
subject.onGpsDisabled();
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_DISABLED), statusList);
|
||||
|
||||
subject.onGpsEnabled();
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_DISABLED, GPS_ENABLED), statusList);
|
||||
|
||||
subject.stop();
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_DISABLED, GPS_ENABLED, GPS_NONE), statusList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartBadfixOk() {
|
||||
ArrayList<GpsStatusValue> statusList = new ArrayList<>();
|
||||
|
||||
// given
|
||||
GpsStatusManager subject = new GpsStatusManager(context, statusList::add, new Handler());
|
||||
subject.onRecordingDistanceChanged(Distance.of(10));
|
||||
|
||||
// when / then
|
||||
subject.start();
|
||||
subject.onNewTrackPoint(new TrackPoint(badFix, Instant.now()));
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_BAD), statusList);
|
||||
|
||||
subject.onNewTrackPoint(new TrackPoint(ok, Instant.now()));
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_BAD, GPS_SIGNAL_FIX), statusList);
|
||||
|
||||
subject.onNewTrackPoint(new TrackPoint(ok, Instant.now()));
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_BAD, GPS_SIGNAL_FIX), statusList);
|
||||
|
||||
subject.onNewTrackPoint(new TrackPoint(badFix, Instant.now()));
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_BAD, GPS_SIGNAL_FIX, GPS_SIGNAL_BAD), statusList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartSignalLost() {
|
||||
ArrayList<GpsStatusValue> statusList = new ArrayList<>();
|
||||
|
||||
// given
|
||||
GpsStatusManager subject = new GpsStatusManager(context, statusList::add, new Handler());
|
||||
subject.onRecordingDistanceChanged(Distance.of(10));
|
||||
subject.onMinRecordingIntervalChanged(GpsStatusManager.SIGNAL_LOST_THRESHOLD.multipliedBy(-1));
|
||||
|
||||
// when / then
|
||||
subject.start();
|
||||
subject.onNewTrackPoint(new TrackPoint(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(ok, Instant.now()));
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_FIX, GPS_SIGNAL_LOST, GPS_SIGNAL_FIX), statusList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartSignalLostByTimer() throws InterruptedException {
|
||||
ArrayList<GpsStatusValue> statusList = new ArrayList<>();
|
||||
|
||||
final HandlerThread handlerThread = new HandlerThread("solution!");
|
||||
handlerThread.start();
|
||||
|
||||
// given
|
||||
GpsStatusManager subject = new GpsStatusManager(context, statusList::add, new Handler(handlerThread.getLooper()));
|
||||
subject.onRecordingDistanceChanged(Distance.of(10));
|
||||
subject.onMinRecordingIntervalChanged(GpsStatusManager.SIGNAL_LOST_THRESHOLD.multipliedBy(-1).plus(Duration.ofMillis(10)));
|
||||
|
||||
// when / then
|
||||
subject.start();
|
||||
Thread.sleep(100);
|
||||
assertEquals(List.of(GPS_ENABLED), statusList);
|
||||
|
||||
subject.onNewTrackPoint(new TrackPoint(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(badFix, Instant.now()));
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_FIX, GPS_SIGNAL_LOST, GPS_SIGNAL_BAD), statusList);
|
||||
|
||||
Thread.sleep(100);
|
||||
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_FIX, GPS_SIGNAL_LOST, GPS_SIGNAL_BAD, GPS_SIGNAL_LOST), statusList);
|
||||
}
|
||||
}
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
package de.dennisguse.opentracks.services.handlers;
|
||||
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class TrackPointCreatorTest {
|
||||
|
||||
@Mock
|
||||
private Context context;
|
||||
|
||||
@Mock
|
||||
private TrackPointCreator.Callback server;
|
||||
|
||||
@Mock
|
||||
private GPSHandler locationHandler;
|
||||
|
||||
private TrackPointCreator subject;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
subject = new TrackPointCreator(locationHandler, server);
|
||||
subject.start(context, null);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
subject.stop();
|
||||
}
|
||||
|
||||
@Ignore("ServiceExecutor disabled for #822")
|
||||
@Test
|
||||
public void sendTrackPoint() throws InterruptedException {
|
||||
// given
|
||||
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT, null);
|
||||
Distance horizontalAccuracyThreshold = Distance.of(50);
|
||||
|
||||
// when
|
||||
subject.onNewTrackPoint(trackPoint);
|
||||
|
||||
// then
|
||||
Thread.sleep(10); // Wait for executor service
|
||||
verify(server).newTrackPoint(trackPoint, horizontalAccuracyThreshold);
|
||||
}
|
||||
}
|
||||
+18
-17
@@ -17,7 +17,8 @@ import org.junit.runner.RunWith;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.ui.customRecordingLayout.Layout;
|
||||
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayout;
|
||||
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayoutIO;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class PreferencesOpenHelperTest {
|
||||
@@ -38,10 +39,10 @@ public class PreferencesOpenHelperTest {
|
||||
PreferencesOpenHelper.newInstance(1).check();
|
||||
|
||||
// then there should be one layout with old custom layout that has the new CSV value.
|
||||
List<Layout> layouts = PreferencesUtils.getAllCustomLayouts();
|
||||
assertNotNull(layouts);
|
||||
assertEquals(layouts.size(), 1);
|
||||
assertEquals(layouts.get(0).toCsv(), PreferencesUtils.getCustomLayout().toCsv());
|
||||
List<RecordingLayout> recordingLayouts = PreferencesUtils.getAllCustomLayouts();
|
||||
assertNotNull(recordingLayouts);
|
||||
assertEquals(recordingLayouts.size(), 1);
|
||||
assertEquals(recordingLayouts.get(0).toCsv(), PreferencesUtils.getCustomLayout().toCsv());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -63,10 +64,10 @@ public class PreferencesOpenHelperTest {
|
||||
+ PreferencesUtils.getLayoutColumnsByDefault() + ";distance,1,1,0;speed,1,1,0;";
|
||||
|
||||
// then there should be one layout with old custom layout that has the new CSV value.
|
||||
List<Layout> layouts = PreferencesUtils.getAllCustomLayouts();
|
||||
assertNotNull(layouts);
|
||||
assertEquals(layouts.size(), 1);
|
||||
assertEquals(layouts.get(0).toCsv(), updatedOldCustomLayoutCsv);
|
||||
List<RecordingLayout> recordingLayouts = PreferencesUtils.getAllCustomLayouts();
|
||||
assertNotNull(recordingLayouts);
|
||||
assertEquals(recordingLayouts.size(), 1);
|
||||
assertEquals(recordingLayouts.get(0).toCsv(), updatedOldCustomLayoutCsv);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -87,10 +88,10 @@ public class PreferencesOpenHelperTest {
|
||||
String updatedOldCustomLayoutCsv = "whatever;3;distance,1,1,0;speed,1,1,0;";
|
||||
|
||||
// then there should be one layout with old custom layout that has the new CSV value.
|
||||
List<Layout> layouts = PreferencesUtils.getAllCustomLayouts();
|
||||
assertNotNull(layouts);
|
||||
assertEquals(layouts.size(), 1);
|
||||
assertEquals(layouts.get(0).toCsv(), updatedOldCustomLayoutCsv);
|
||||
List<RecordingLayout> recordingLayouts = PreferencesUtils.getAllCustomLayouts();
|
||||
assertNotNull(recordingLayouts);
|
||||
assertEquals(recordingLayouts.size(), 1);
|
||||
assertEquals(recordingLayouts.get(0).toCsv(), updatedOldCustomLayoutCsv);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -109,10 +110,10 @@ public class PreferencesOpenHelperTest {
|
||||
PreferencesOpenHelper.newInstance(1).check();
|
||||
|
||||
// then custom layout should be equals to default layout.
|
||||
Layout defaultLayout = Layout.fromCsv(PreferencesUtils.buildDefaultLayout(), resources);
|
||||
List<Layout> customLayout = PreferencesUtils.getAllCustomLayouts();
|
||||
RecordingLayout defaultRecordingLayout = RecordingLayoutIO.fromCsv(PreferencesUtils.buildDefaultLayout(), resources);
|
||||
List<RecordingLayout> customRecordingLayout = PreferencesUtils.getAllCustomLayouts();
|
||||
|
||||
assertEquals(customLayout.size(), 1);
|
||||
assertEquals(defaultLayout.toCsv(), customLayout.get(0).toCsv());
|
||||
assertEquals(customRecordingLayout.size(), 1);
|
||||
assertEquals(defaultRecordingLayout.toCsv(), customRecordingLayout.get(0).toCsv());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ import java.util.List;
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.ui.customRecordingLayout.DataField;
|
||||
import de.dennisguse.opentracks.ui.customRecordingLayout.Layout;
|
||||
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayout;
|
||||
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayoutIO;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class PreferencesUtilsTest {
|
||||
@@ -85,13 +86,13 @@ public class PreferencesUtilsTest {
|
||||
editor.commit();
|
||||
|
||||
// when
|
||||
List<Layout> layouts = PreferencesUtils.getAllCustomLayouts();
|
||||
List<RecordingLayout> recordingLayouts = PreferencesUtils.getAllCustomLayouts();
|
||||
|
||||
// then
|
||||
assertEquals(layouts.size(), 1);
|
||||
assertTrue(layouts.get(0).getFields().size() > 0);
|
||||
assertEquals(layouts.get(0).getName(), context.getString(R.string.stats_custom_layout_default_layout));
|
||||
assertTrue(layouts.get(0).getFields().stream().anyMatch(DataField::isVisible));
|
||||
assertEquals(recordingLayouts.size(), 1);
|
||||
assertTrue(recordingLayouts.get(0).getFields().size() > 0);
|
||||
assertEquals(recordingLayouts.get(0).getName(), context.getString(R.string.stats_custom_layout_default_layout));
|
||||
assertTrue(recordingLayouts.get(0).getFields().stream().anyMatch(DataField::isVisible));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -103,12 +104,12 @@ public class PreferencesUtilsTest {
|
||||
editor.commit();
|
||||
|
||||
// when
|
||||
Layout layout = PreferencesUtils.getCustomLayout();
|
||||
RecordingLayout recordingLayout = PreferencesUtils.getCustomLayout();
|
||||
|
||||
// then
|
||||
assertTrue(layout.getFields().size() > 0);
|
||||
assertEquals(layout.getName(), context.getString(R.string.stats_custom_layout_default_layout));
|
||||
assertTrue(layout.getFields().stream().anyMatch(DataField::isVisible));
|
||||
assertTrue(recordingLayout.getFields().size() > 0);
|
||||
assertEquals(recordingLayout.getName(), context.getString(R.string.stats_custom_layout_default_layout));
|
||||
assertTrue(recordingLayout.getFields().stream().anyMatch(DataField::isVisible));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -126,28 +127,28 @@ public class PreferencesUtilsTest {
|
||||
editor.apply();
|
||||
|
||||
// when
|
||||
Layout layout = PreferencesUtils.getCustomLayout();
|
||||
RecordingLayout recordingLayout = PreferencesUtils.getCustomLayout();
|
||||
|
||||
// then
|
||||
assertEquals(layout.getFields().size(), 4);
|
||||
assertEquals(layout.getName(), "run");
|
||||
assertEquals(layout.getColumnsPerRow(), 2);
|
||||
assertEquals(recordingLayout.getFields().size(), 4);
|
||||
assertEquals(recordingLayout.getName(), "run");
|
||||
assertEquals(recordingLayout.getColumnsPerRow(), 2);
|
||||
|
||||
assertEquals(layout.getFields().get(0).getKey(), context.getString(R.string.stats_custom_layout_moving_time_key));
|
||||
assertTrue(layout.getFields().get(0).isVisible());
|
||||
assertTrue(layout.getFields().get(0).isPrimary());
|
||||
assertEquals(recordingLayout.getFields().get(0).getKey(), context.getString(R.string.stats_custom_layout_moving_time_key));
|
||||
assertTrue(recordingLayout.getFields().get(0).isVisible());
|
||||
assertTrue(recordingLayout.getFields().get(0).isPrimary());
|
||||
|
||||
assertEquals(layout.getFields().get(1).getKey(), context.getString(R.string.stats_custom_layout_distance_key));
|
||||
assertTrue(layout.getFields().get(1).isVisible());
|
||||
assertFalse(layout.getFields().get(1).isPrimary());
|
||||
assertEquals(recordingLayout.getFields().get(1).getKey(), context.getString(R.string.stats_custom_layout_distance_key));
|
||||
assertTrue(recordingLayout.getFields().get(1).isVisible());
|
||||
assertFalse(recordingLayout.getFields().get(1).isPrimary());
|
||||
|
||||
assertEquals(layout.getFields().get(2).getKey(), context.getString(R.string.stats_custom_layout_average_moving_speed_key));
|
||||
assertFalse(layout.getFields().get(2).isVisible());
|
||||
assertTrue(layout.getFields().get(2).isPrimary());
|
||||
assertEquals(recordingLayout.getFields().get(2).getKey(), context.getString(R.string.stats_custom_layout_average_moving_speed_key));
|
||||
assertFalse(recordingLayout.getFields().get(2).isVisible());
|
||||
assertTrue(recordingLayout.getFields().get(2).isPrimary());
|
||||
|
||||
assertEquals(layout.getFields().get(3).getKey(), context.getString(R.string.stats_custom_layout_speed_key));
|
||||
assertFalse(layout.getFields().get(3).isVisible());
|
||||
assertFalse(layout.getFields().get(3).isPrimary());
|
||||
assertEquals(recordingLayout.getFields().get(3).getKey(), context.getString(R.string.stats_custom_layout_speed_key));
|
||||
assertFalse(recordingLayout.getFields().get(3).isVisible());
|
||||
assertFalse(recordingLayout.getFields().get(3).isPrimary());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -165,44 +166,44 @@ public class PreferencesUtilsTest {
|
||||
editor.apply();
|
||||
|
||||
// when
|
||||
Layout layout = PreferencesUtils.getCustomLayout();
|
||||
RecordingLayout recordingLayout = PreferencesUtils.getCustomLayout();
|
||||
|
||||
// then
|
||||
assertEquals(layout.getFields().size(), 4);
|
||||
assertEquals(layout.getName(), "walking");
|
||||
assertEquals(layout.getColumnsPerRow(), 2);
|
||||
assertEquals(recordingLayout.getFields().size(), 4);
|
||||
assertEquals(recordingLayout.getName(), "walking");
|
||||
assertEquals(recordingLayout.getColumnsPerRow(), 2);
|
||||
|
||||
assertEquals(layout.getFields().get(0).getKey(), context.getString(R.string.stats_custom_layout_moving_time_key));
|
||||
assertTrue(layout.getFields().get(0).isVisible());
|
||||
assertTrue(layout.getFields().get(0).isPrimary());
|
||||
assertEquals(recordingLayout.getFields().get(0).getKey(), context.getString(R.string.stats_custom_layout_moving_time_key));
|
||||
assertTrue(recordingLayout.getFields().get(0).isVisible());
|
||||
assertTrue(recordingLayout.getFields().get(0).isPrimary());
|
||||
|
||||
assertEquals(layout.getFields().get(1).getKey(), context.getString(R.string.stats_custom_layout_distance_key));
|
||||
assertTrue(layout.getFields().get(1).isVisible());
|
||||
assertFalse(layout.getFields().get(1).isPrimary());
|
||||
assertEquals(recordingLayout.getFields().get(1).getKey(), context.getString(R.string.stats_custom_layout_distance_key));
|
||||
assertTrue(recordingLayout.getFields().get(1).isVisible());
|
||||
assertFalse(recordingLayout.getFields().get(1).isPrimary());
|
||||
|
||||
assertEquals(layout.getFields().get(2).getKey(), context.getString(R.string.stats_custom_layout_coordinates_key));
|
||||
assertFalse(layout.getFields().get(2).isVisible());
|
||||
assertTrue(layout.getFields().get(2).isPrimary());
|
||||
assertTrue(layout.getFields().get(2).isWide());
|
||||
assertEquals(recordingLayout.getFields().get(2).getKey(), context.getString(R.string.stats_custom_layout_coordinates_key));
|
||||
assertFalse(recordingLayout.getFields().get(2).isVisible());
|
||||
assertTrue(recordingLayout.getFields().get(2).isPrimary());
|
||||
assertTrue(recordingLayout.getFields().get(2).isWide());
|
||||
|
||||
assertEquals(layout.getFields().get(3).getKey(), context.getString(R.string.stats_custom_layout_speed_key));
|
||||
assertFalse(layout.getFields().get(3).isVisible());
|
||||
assertFalse(layout.getFields().get(3).isPrimary());
|
||||
assertEquals(recordingLayout.getFields().get(3).getKey(), context.getString(R.string.stats_custom_layout_speed_key));
|
||||
assertFalse(recordingLayout.getFields().get(3).isVisible());
|
||||
assertFalse(recordingLayout.getFields().get(3).isPrimary());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetCustomLayout() {
|
||||
// given
|
||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
Layout layoutSrc = new Layout("road cycling");
|
||||
layoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_moving_time_key), context.getString(R.string.stats_moving_time), true, true, false));
|
||||
layoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_distance_key), context.getString(R.string.stats_distance), true, false, false));
|
||||
layoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_average_moving_speed_key), context.getString(R.string.stats_average_moving_speed), false, true, false));
|
||||
layoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_speed_key), context.getString(R.string.stats_speed), false, false, false));
|
||||
RecordingLayout recordingLayoutSrc = new RecordingLayout("road cycling");
|
||||
recordingLayoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_moving_time_key), true, true, false));
|
||||
recordingLayoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_distance_key), true, false, false));
|
||||
recordingLayoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_average_moving_speed_key), false, true, false));
|
||||
recordingLayoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_speed_key), false, false, false));
|
||||
|
||||
// when
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.putString(context.getString(R.string.stats_custom_layouts_key), layoutSrc.toCsv());
|
||||
editor.putString(context.getString(R.string.stats_custom_layouts_key), recordingLayoutSrc.toCsv());
|
||||
editor.commit();
|
||||
|
||||
// then
|
||||
@@ -215,13 +216,13 @@ public class PreferencesUtilsTest {
|
||||
+ context.getString(R.string.stats_custom_layout_average_moving_speed_key) + ",0,1,0;"
|
||||
+ context.getString(R.string.stats_custom_layout_speed_key) + ",0,0,0;");
|
||||
|
||||
Layout layoutDst = PreferencesUtils.getCustomLayout();
|
||||
assertEquals(layoutSrc.getName(), layoutDst.getName());
|
||||
assertEquals(layoutSrc.getFields().size(), layoutDst.getFields().size());
|
||||
for (int i = 0; i < layoutSrc.getFields().size(); i++) {
|
||||
assertEquals(layoutSrc.getFields().get(i).getKey(), layoutDst.getFields().get(i).getKey());
|
||||
assertEquals(layoutSrc.getFields().get(i).isVisible(), layoutDst.getFields().get(i).isVisible());
|
||||
assertEquals(layoutSrc.getFields().get(i).isPrimary(), layoutDst.getFields().get(i).isPrimary());
|
||||
RecordingLayout recordingLayoutDst = PreferencesUtils.getCustomLayout();
|
||||
assertEquals(recordingLayoutSrc.getName(), recordingLayoutDst.getName());
|
||||
assertEquals(recordingLayoutSrc.getFields().size(), recordingLayoutDst.getFields().size());
|
||||
for (int i = 0; i < recordingLayoutSrc.getFields().size(); i++) {
|
||||
assertEquals(recordingLayoutSrc.getFields().get(i).getKey(), recordingLayoutDst.getFields().get(i).getKey());
|
||||
assertEquals(recordingLayoutSrc.getFields().get(i).isVisible(), recordingLayoutDst.getFields().get(i).isVisible());
|
||||
assertEquals(recordingLayoutSrc.getFields().get(i).isPrimary(), recordingLayoutDst.getFields().get(i).isPrimary());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,7 +248,7 @@ public class PreferencesUtilsTest {
|
||||
editor.putString(context.getString(R.string.stats_custom_layouts_key), cyclingProfile + "\n" + runningProfile);
|
||||
editor.apply();
|
||||
|
||||
List<Layout> layoutsBefore = PreferencesUtils.getAllCustomLayouts();
|
||||
List<RecordingLayout> layoutsBefore = PreferencesUtils.getAllCustomLayouts();
|
||||
|
||||
// when cyling profile is updated
|
||||
String cyclingProfileUpdated = "cycling;2;"
|
||||
@@ -256,14 +257,14 @@ public class PreferencesUtilsTest {
|
||||
+ context.getString(R.string.stats_custom_layout_average_moving_speed_key) + ",0,0;"
|
||||
+ context.getString(R.string.stats_custom_layout_speed_key) + ",0,0;";
|
||||
|
||||
List<Layout> layoutsToBeUpdated = new ArrayList<>();
|
||||
layoutsToBeUpdated.add(Layout.fromCsv(cyclingProfileUpdated, resources));
|
||||
layoutsToBeUpdated.add(Layout.fromCsv(runningProfile, resources));
|
||||
List<RecordingLayout> layoutsToBeUpdated = new ArrayList<>();
|
||||
layoutsToBeUpdated.add(RecordingLayoutIO.fromCsv(cyclingProfileUpdated, resources));
|
||||
layoutsToBeUpdated.add(RecordingLayoutIO.fromCsv(runningProfile, resources));
|
||||
|
||||
PreferencesUtils.updateCustomLayouts(layoutsToBeUpdated);
|
||||
|
||||
// then only updated profile is modified in the custom layouts
|
||||
List<Layout> layoutsAfter = PreferencesUtils.getAllCustomLayouts();
|
||||
List<RecordingLayout> layoutsAfter = PreferencesUtils.getAllCustomLayouts();
|
||||
|
||||
assertEquals(layoutsBefore.size(), 2);
|
||||
assertEquals(layoutsAfter.size(), 2);
|
||||
@@ -294,7 +295,7 @@ public class PreferencesUtilsTest {
|
||||
editor.putString(context.getString(R.string.stats_custom_layouts_key), cyclingProfile + "\n" + runningProfile);
|
||||
editor.apply();
|
||||
|
||||
List<Layout> layoutsBefore = PreferencesUtils.getAllCustomLayouts();
|
||||
List<RecordingLayout> layoutsBefore = PreferencesUtils.getAllCustomLayouts();
|
||||
|
||||
// when cyling profile is updated
|
||||
String cyclingProfileUpdated = "cycling;2;"
|
||||
@@ -302,11 +303,11 @@ public class PreferencesUtilsTest {
|
||||
+ context.getString(R.string.stats_custom_layout_distance_key) + ",0,0;"
|
||||
+ context.getString(R.string.stats_custom_layout_average_moving_speed_key) + ",0,0;"
|
||||
+ context.getString(R.string.stats_custom_layout_speed_key) + ",0,0;";
|
||||
Layout layoutToBeUpdated = Layout.fromCsv(cyclingProfileUpdated, resources);
|
||||
PreferencesUtils.updateCustomLayout(layoutToBeUpdated);
|
||||
RecordingLayout recordingLayoutToBeUpdated = RecordingLayoutIO.fromCsv(cyclingProfileUpdated, resources);
|
||||
PreferencesUtils.updateCustomLayout(recordingLayoutToBeUpdated);
|
||||
|
||||
// then only updated profile is modified in the custom layouts
|
||||
List<Layout> layoutsAfter = PreferencesUtils.getAllCustomLayouts();
|
||||
List<RecordingLayout> layoutsAfter = PreferencesUtils.getAllCustomLayouts();
|
||||
|
||||
assertEquals(layoutsBefore.size(), 2);
|
||||
assertEquals(layoutsAfter.size(), 2);
|
||||
@@ -337,9 +338,9 @@ public class PreferencesUtilsTest {
|
||||
editor.apply();
|
||||
|
||||
// when it gets the custom layout
|
||||
Layout layout = PreferencesUtils.getCustomLayout();
|
||||
RecordingLayout recordingLayout = PreferencesUtils.getCustomLayout();
|
||||
|
||||
// then the first one was returned
|
||||
assertEquals(layout.getName(), "cycling");
|
||||
assertEquals(recordingLayout.getName(), "cycling");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ public class DescriptionGeneratorTest {
|
||||
stats.setTotalAltitudeLoss(6000f);
|
||||
stats.setStartTime(START_TIME);
|
||||
track.setTrackStatistics(stats);
|
||||
track.setCategory("hiking");
|
||||
track.setActivityType("hiking");
|
||||
String expected = //"Created by"
|
||||
"<a href='https://github.com/OpenTracksApp/OpenTracks'>OpenTracks (Debug)</a><p>"
|
||||
+ "Name: -<br>"
|
||||
@@ -118,7 +118,7 @@ public class DescriptionGeneratorTest {
|
||||
stats.setTotalAltitudeLoss(6000f);
|
||||
stats.setStartTime(START_TIME);
|
||||
track.setTrackStatistics(stats);
|
||||
track.setCategory("hiking");
|
||||
track.setActivityType("hiking");
|
||||
String expected = //"Created by"
|
||||
"<a href='https://github.com/OpenTracksApp/OpenTracks'>OpenTracks (Debug)</a><p>"
|
||||
+ "Name: -<br>"
|
||||
|
||||
@@ -78,7 +78,7 @@ public class TrackStatisticsUpdaterTest {
|
||||
|
||||
// when
|
||||
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
|
||||
data.trackPoints.forEach(subject::addTrackPoint);
|
||||
data.trackPoints().forEach(subject::addTrackPoint);
|
||||
|
||||
// then
|
||||
TrackStatistics statistics = subject.getTrackStatistics();
|
||||
|
||||
+3
-3
@@ -41,7 +41,7 @@ public class AggregatedStatisticsTest {
|
||||
|
||||
Track track = new Track();
|
||||
track.setIcon(TrackIconUtils.getIconValue(context, category));
|
||||
track.setCategory(category);
|
||||
track.setActivityType(category);
|
||||
track.setTrackStatistics(statistics);
|
||||
return track;
|
||||
}
|
||||
@@ -243,8 +243,8 @@ public class AggregatedStatisticsTest {
|
||||
// Check order
|
||||
|
||||
{
|
||||
assertEquals(biking, aggregatedStatistics.getItem(0).getCategory());
|
||||
assertEquals(driving, aggregatedStatistics.getItem(3).getCategory());
|
||||
assertEquals(biking, aggregatedStatistics.getItem(0).getActivityType());
|
||||
assertEquals(driving, aggregatedStatistics.getItem(3).getActivityType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -51,7 +51,7 @@ public class EspressoAggregatedFilterTest {
|
||||
@Before
|
||||
public void setUp() {
|
||||
Pair<Track, List<TrackPoint>> pair = TestDataUtil.createTrack(new Track.Id(System.currentTimeMillis()), 20);
|
||||
pair.first.setCategory(CATEGORY);
|
||||
pair.first.setActivityType(CATEGORY);
|
||||
TestDataUtil.insertTrackWithLocations(new ContentProviderUtils(ApplicationProvider.getApplicationContext()), pair.first, pair.second);
|
||||
}
|
||||
|
||||
@@ -59,8 +59,8 @@ public class EspressoAggregatedFilterTest {
|
||||
public void espressoAggregatedFilterTest() {
|
||||
// open AggregatedStatisticsActivity through toolbar's menu item
|
||||
onView(ViewMatchers.withId(R.id.bottom_app_bar)).check(matches(isDisplayed()));
|
||||
onView(withId(R.id.bottom_app_bar)).check(matches(hasDescendant(withId(R.id.track_list_aggregated_stats))));
|
||||
onView(withId(R.id.track_list_aggregated_stats)).perform(click());
|
||||
onView(withId(R.id.bottom_app_bar)).check(matches(hasDescendant(withId(R.id.aggregated_stats_button))));
|
||||
onView(withId(R.id.aggregated_stats_button)).perform(click());
|
||||
|
||||
// open FilterDialogFragment through toolbar's menu item
|
||||
onView(withId(R.id.bottom_app_bar)).check(matches(isDisplayed()));
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ import org.junit.runner.RunWith;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class CsvLayoutUtilsTest extends TestCase {
|
||||
public class CsvRecordingLayoutUtilsTest extends TestCase {
|
||||
|
||||
@Test
|
||||
public void testGetCsvLineParts() {
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
package de.dennisguse.opentracks.ui.customRecordingLayout;
|
||||
|
||||
|
||||
import static androidx.test.espresso.Espresso.onView;
|
||||
import static androidx.test.espresso.action.ViewActions.click;
|
||||
import static androidx.test.espresso.action.ViewActions.longClick;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.assertThat;
|
||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.test.espresso.NoMatchingViewException;
|
||||
import androidx.test.espresso.ViewAssertion;
|
||||
import androidx.test.espresso.ViewInteraction;
|
||||
import androidx.test.espresso.matcher.ViewMatchers;
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.LargeTest;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.TrackListActivity;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class EspressoCustomLayoutTest {
|
||||
|
||||
@Rule
|
||||
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
||||
|
||||
@Rule
|
||||
public GrantPermissionRule mGrantPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
|
||||
@Test
|
||||
public void customLayoutTest() {
|
||||
// TrackListActivity: start recording
|
||||
ViewInteraction trackControllerRecordButton = onView(ViewMatchers.withId(R.id.track_list_fab_action));
|
||||
trackControllerRecordButton.perform(click());
|
||||
|
||||
// Get custom layout preferences and check all data fields are showed.
|
||||
Layout layout = PreferencesUtils.getCustomLayout();
|
||||
|
||||
onView(withId(R.id.stats_recycler_view)).check(new RecyclerViewItemCountAssertion((int) layout.getFields().stream().filter(DataField::isVisible).count()));
|
||||
|
||||
// stop recording
|
||||
ViewInteraction trackControllerStopButton = onView(withId(R.id.track_recording_fab_action));
|
||||
trackControllerStopButton.perform(longClick());
|
||||
}
|
||||
|
||||
private static class RecyclerViewItemCountAssertion implements ViewAssertion {
|
||||
private final int expectedCount;
|
||||
|
||||
public RecyclerViewItemCountAssertion(int expectedCount) {
|
||||
this.expectedCount = expectedCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void check(View view, NoMatchingViewException noViewFoundException) {
|
||||
if (noViewFoundException != null) {
|
||||
throw noViewFoundException;
|
||||
}
|
||||
|
||||
RecyclerView recyclerView = (RecyclerView) view;
|
||||
RecyclerView.Adapter<?> adapter = recyclerView.getAdapter();
|
||||
assertThat(adapter.getItemCount(), is(expectedCount));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+39
-43
@@ -17,7 +17,7 @@ import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class LayoutTest extends TestCase {
|
||||
public class RecordingLayoutTest extends TestCase {
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
private final Resources resources = ApplicationProvider.getApplicationContext().getResources();
|
||||
@@ -28,21 +28,17 @@ public class LayoutTest extends TestCase {
|
||||
String csv = "running;2;" + context.getString(R.string.stats_custom_layout_speed_key) + ",1,1,0;" + context.getString(R.string.stats_custom_layout_distance_key) + ",1,0,0;" + context.getString(R.string.stats_custom_layout_altitude_key) + ",0,1,0;" + context.getString(R.string.stats_custom_layout_gain_key) + ",0,0,0;";
|
||||
|
||||
// when create a layout from CSV line
|
||||
Layout layout = Layout.fromCsv(csv, resources);
|
||||
List<DataField> dataFieldList = layout.getFields();
|
||||
RecordingLayout recordingLayout = RecordingLayoutIO.fromCsv(csv, resources);
|
||||
List<DataField> dataFieldList = recordingLayout.getFields();
|
||||
|
||||
// then layout and data fields are built correctly
|
||||
assertEquals(layout.getName(), "running");
|
||||
assertEquals(layout.getColumnsPerRow(), 2);
|
||||
assertEquals(recordingLayout.getName(), "running");
|
||||
assertEquals(recordingLayout.getColumnsPerRow(), 2);
|
||||
assertEquals(dataFieldList.size(), 4);
|
||||
assertEquals(dataFieldList.get(0).getKey(), context.getString(R.string.stats_custom_layout_speed_key));
|
||||
assertEquals(dataFieldList.get(1).getKey(), context.getString(R.string.stats_custom_layout_distance_key));
|
||||
assertEquals(dataFieldList.get(2).getKey(), context.getString(R.string.stats_custom_layout_altitude_key));
|
||||
assertEquals(dataFieldList.get(3).getKey(), context.getString(R.string.stats_custom_layout_gain_key));
|
||||
assertEquals(dataFieldList.get(0).getTitle(), context.getString(R.string.stats_speed));
|
||||
assertEquals(dataFieldList.get(1).getTitle(), context.getString(R.string.stats_distance));
|
||||
assertEquals(dataFieldList.get(2).getTitle(), context.getString(R.string.stats_altitude));
|
||||
assertEquals(dataFieldList.get(3).getTitle(), context.getString(R.string.stats_gain));
|
||||
assertTrue(dataFieldList.get(0).isVisible());
|
||||
assertTrue(dataFieldList.get(0).isPrimary());
|
||||
assertFalse(dataFieldList.get(0).isWide());
|
||||
@@ -64,13 +60,13 @@ public class LayoutTest extends TestCase {
|
||||
String csv1 = "Layout Name;speed,1,1;distance,0,0;";
|
||||
|
||||
// when create a layout from CSV line
|
||||
Layout layout = Layout.fromCsv(csv1, resources);
|
||||
List<DataField> dataFieldList = layout.getFields();
|
||||
RecordingLayout recordingLayout = RecordingLayoutIO.fromCsv(csv1, resources);
|
||||
List<DataField> dataFieldList = recordingLayout.getFields();
|
||||
|
||||
// then layout and data fields are built correctly
|
||||
assertEquals(layout.getName(), PreferencesUtils.getDefaultLayoutName());
|
||||
assertEquals(layout.getColumnsPerRow(), PreferencesUtils.getLayoutColumnsByDefault());
|
||||
assertEquals(layout.getFields().size(), 0);
|
||||
assertEquals(recordingLayout.getName(), PreferencesUtils.getDefaultLayoutName());
|
||||
assertEquals(recordingLayout.getColumnsPerRow(), PreferencesUtils.getLayoutColumnsByDefault());
|
||||
assertEquals(recordingLayout.getFields().size(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -79,13 +75,13 @@ public class LayoutTest extends TestCase {
|
||||
String csv1 = "Layout Name;2;";
|
||||
|
||||
// when create a layout from CSV line
|
||||
Layout layout = Layout.fromCsv(csv1, resources);
|
||||
List<DataField> dataFieldList = layout.getFields();
|
||||
RecordingLayout recordingLayout = RecordingLayoutIO.fromCsv(csv1, resources);
|
||||
List<DataField> dataFieldList = recordingLayout.getFields();
|
||||
|
||||
// then layout and data fields are built correctly
|
||||
assertEquals(layout.getName(), PreferencesUtils.getDefaultLayoutName());
|
||||
assertEquals(layout.getColumnsPerRow(), PreferencesUtils.getLayoutColumnsByDefault());
|
||||
assertEquals(layout.getFields().size(), 0);
|
||||
assertEquals(recordingLayout.getName(), PreferencesUtils.getDefaultLayoutName());
|
||||
assertEquals(recordingLayout.getColumnsPerRow(), PreferencesUtils.getLayoutColumnsByDefault());
|
||||
assertEquals(recordingLayout.getFields().size(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -94,13 +90,13 @@ public class LayoutTest extends TestCase {
|
||||
String csv1 = "Layout Name;2;speed,distance,total time;";
|
||||
|
||||
// when create a layout from CSV line
|
||||
Layout layout = Layout.fromCsv(csv1, resources);
|
||||
List<DataField> dataFieldList = layout.getFields();
|
||||
RecordingLayout recordingLayout = RecordingLayoutIO.fromCsv(csv1, resources);
|
||||
List<DataField> dataFieldList = recordingLayout.getFields();
|
||||
|
||||
// then layout and data fields are built correctly
|
||||
assertEquals(layout.getName(), "Layout Name");
|
||||
assertEquals(layout.getColumnsPerRow(), 2);
|
||||
assertEquals(layout.getFields().size(), 0);
|
||||
assertEquals(recordingLayout.getName(), "Layout Name");
|
||||
assertEquals(recordingLayout.getColumnsPerRow(), 2);
|
||||
assertEquals(recordingLayout.getFields().size(), 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -109,27 +105,27 @@ public class LayoutTest extends TestCase {
|
||||
String csv1 = "Layout Name;2;speed,1,0;distance;";
|
||||
|
||||
// when create a layout from CSV line
|
||||
Layout layout = Layout.fromCsv(csv1, resources);
|
||||
List<DataField> dataFieldList = layout.getFields();
|
||||
RecordingLayout recordingLayout = RecordingLayoutIO.fromCsv(csv1, resources);
|
||||
List<DataField> dataFieldList = recordingLayout.getFields();
|
||||
|
||||
// then layout and data fields are built correctly
|
||||
assertEquals(layout.getName(), "Layout Name");
|
||||
assertEquals(layout.getColumnsPerRow(), 2);
|
||||
assertEquals(layout.getFields().size(), 1);
|
||||
assertEquals(recordingLayout.getName(), "Layout Name");
|
||||
assertEquals(recordingLayout.getColumnsPerRow(), 2);
|
||||
assertEquals(recordingLayout.getFields().size(), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToCsv() {
|
||||
// given a layout's object
|
||||
Layout layout = new Layout("Test Layout", 2);
|
||||
layout.addField("key1", "Title 1", false, false, false);
|
||||
layout.addField("key2", "Title 2", false, true, false);
|
||||
layout.addField("key3", "Title 3", true, false, false);
|
||||
layout.addField("key4", "Title 4", true, true, false);
|
||||
layout.addField("key5", "Title 5", true, true, true);
|
||||
RecordingLayout recordingLayout = new RecordingLayout("Test Layout", 2);
|
||||
recordingLayout.addField(new DataField("key1", false, false, false));
|
||||
recordingLayout.addField(new DataField("key2", false, true, false));
|
||||
recordingLayout.addField(new DataField("key3", true, false, false));
|
||||
recordingLayout.addField(new DataField("key4", true, true, false));
|
||||
recordingLayout.addField(new DataField("key5", true, true, true));
|
||||
|
||||
// when converts it to CSV
|
||||
String csv = layout.toCsv();
|
||||
String csv = recordingLayout.toCsv();
|
||||
|
||||
// then csv is well built
|
||||
assertEquals(csv, "Test Layout;2;key1,0,0,0;key2,0,1,0;key3,1,0,0;key4,1,1,0;key5,1,1,1;");
|
||||
@@ -138,15 +134,15 @@ public class LayoutTest extends TestCase {
|
||||
@Test
|
||||
public void testToCsv_columnsByDefault() {
|
||||
// given a layout's object
|
||||
Layout layout = new Layout("Test Layout");
|
||||
layout.addField("key1", "Title 1", false, false, false);
|
||||
layout.addField("key2", "Title 2", false, true, false);
|
||||
layout.addField("key3", "Title 3", true, false, false);
|
||||
layout.addField("key4", "Title 4", true, true, false);
|
||||
layout.addField("key5", "Title 5", true, true, true);
|
||||
RecordingLayout recordingLayout = new RecordingLayout("Test Layout");
|
||||
recordingLayout.addField(new DataField("key1", false, false, false));
|
||||
recordingLayout.addField(new DataField("key2", false, true, false));
|
||||
recordingLayout.addField(new DataField("key3", true, false, false));
|
||||
recordingLayout.addField(new DataField("key4", true, true, false));
|
||||
recordingLayout.addField(new DataField("key5", true, true, true));
|
||||
|
||||
// when converts it to CSV
|
||||
String csv = layout.toCsv();
|
||||
String csv = recordingLayout.toCsv();
|
||||
|
||||
// then csv is well built
|
||||
assertEquals(csv, "Test Layout;" + PreferencesUtils.getLayoutColumnsByDefault() + ";key1,0,0,0;key2,0,1,0;key3,1,0,0;key4,1,1,0;key5,1,1,1;");
|
||||
@@ -34,23 +34,24 @@ public class EGM2008UtilsTest {
|
||||
int expectedLength = 18671444;
|
||||
int expectedHeaderLength = 404;
|
||||
|
||||
String expectedHeader = "P5\n" +
|
||||
"# Geoid file in PGM format for the GeographicLib::Geoid class\n" +
|
||||
"# Description WGS84 EGM2008, 5-minute grid\n" +
|
||||
"# URL http://earth-info.nga.mil/GandG/wgs84/gravitymod/egm2008\n" +
|
||||
"# DateTime 2009-08-29 18:45:00\n" +
|
||||
"# MaxBilinearError 0.478\n" +
|
||||
"# RMSBilinearError 0.012\n" +
|
||||
"# MaxCubicError 0.294\n" +
|
||||
"# RMSCubicError 0.005\n" +
|
||||
"# Offset -108\n" +
|
||||
"# Scale 0.003\n" +
|
||||
"# Origin 90N 0E\n" +
|
||||
"# AREA_OR_POINT Point\n" +
|
||||
"# Vertical_Datum WGS84\n" +
|
||||
"4320 2161\n" +
|
||||
"65535" +
|
||||
"\n";
|
||||
String expectedHeader = """
|
||||
P5
|
||||
# Geoid file in PGM format for the GeographicLib::Geoid class
|
||||
# Description WGS84 EGM2008, 5-minute grid
|
||||
# URL http://earth-info.nga.mil/GandG/wgs84/gravitymod/egm2008
|
||||
# DateTime 2009-08-29 18:45:00
|
||||
# MaxBilinearError 0.478
|
||||
# RMSBilinearError 0.012
|
||||
# MaxCubicError 0.294
|
||||
# RMSCubicError 0.005
|
||||
# Offset -108
|
||||
# Scale 0.003
|
||||
# Origin 90N 0E
|
||||
# AREA_OR_POINT Point
|
||||
# Vertical_Datum WGS84
|
||||
4320 2161
|
||||
65535
|
||||
""";
|
||||
|
||||
// when
|
||||
try (InputStream inputStream = context.getResources().openRawResource(EGM2008Utils.EGM2008_5_DATA)) {
|
||||
@@ -78,7 +79,7 @@ public class EGM2008UtilsTest {
|
||||
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
||||
|
||||
// then
|
||||
assertEquals(-14.8980, altitude_egm2008.correctAltitude(trackPoint.getLocation()), MAX_BILINEAR_ERROR);
|
||||
assertEquals(-14.8980, altitude_egm2008.correctAltitude(trackPoint.getLocation()).toM(), MAX_BILINEAR_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -93,7 +94,7 @@ public class EGM2008UtilsTest {
|
||||
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
||||
|
||||
// then
|
||||
assertEquals(30.15, altitude_egm2008.correctAltitude(trackPoint.getLocation()), MAX_BILINEAR_ERROR);
|
||||
assertEquals(30.15, altitude_egm2008.correctAltitude(trackPoint.getLocation()).toM(), MAX_BILINEAR_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -108,7 +109,7 @@ public class EGM2008UtilsTest {
|
||||
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
||||
|
||||
// then
|
||||
assertEquals(30.15, altitude_egm2008.correctAltitude(trackPoint.getLocation()), MAX_BILINEAR_ERROR);
|
||||
assertEquals(30.15, altitude_egm2008.correctAltitude(trackPoint.getLocation()).toM(), MAX_BILINEAR_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -123,7 +124,7 @@ public class EGM2008UtilsTest {
|
||||
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
||||
|
||||
// then
|
||||
assertEquals(30.15, altitude_egm2008.correctAltitude(trackPoint.getLocation()), MAX_BILINEAR_ERROR);
|
||||
assertEquals(30.15, altitude_egm2008.correctAltitude(trackPoint.getLocation()).toM(), MAX_BILINEAR_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -138,7 +139,7 @@ public class EGM2008UtilsTest {
|
||||
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
||||
|
||||
// then
|
||||
assertEquals(-17.2260, altitude_egm2008.correctAltitude(trackPoint.getLocation()), MAX_BILINEAR_ERROR);
|
||||
assertEquals(-17.2260, altitude_egm2008.correctAltitude(trackPoint.getLocation()).toM(), MAX_BILINEAR_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -153,7 +154,7 @@ public class EGM2008UtilsTest {
|
||||
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
||||
|
||||
// then
|
||||
assertEquals(-39.4865, altitude_egm2008.correctAltitude(trackPoint.getLocation()), MAX_BILINEAR_ERROR);
|
||||
assertEquals(-39.4865, altitude_egm2008.correctAltitude(trackPoint.getLocation()).toM(), MAX_BILINEAR_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -173,7 +174,7 @@ public class EGM2008UtilsTest {
|
||||
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint1.getLocation());
|
||||
|
||||
// then
|
||||
assertNotEquals(altitude_egm2008.correctAltitude(trackPoint1.getLocation()), altitude_egm2008.correctAltitude(trackPoint2.getLocation()), 0.0001);
|
||||
assertNotEquals(altitude_egm2008.correctAltitude(trackPoint1.getLocation()).toM(), altitude_egm2008.correctAltitude(trackPoint2.getLocation()).toM(), 0.0001);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -189,7 +190,7 @@ public class EGM2008UtilsTest {
|
||||
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
||||
|
||||
// then
|
||||
assertEquals(22.99, altitude_egm2008.correctAltitude(trackPoint.getLocation()), MAX_BILINEAR_ERROR);
|
||||
assertEquals(22.99, altitude_egm2008.correctAltitude(trackPoint.getLocation()).toM(), MAX_BILINEAR_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -204,7 +205,7 @@ public class EGM2008UtilsTest {
|
||||
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
||||
|
||||
// then
|
||||
assertEquals(-85.824, altitude_egm2008.correctAltitude(trackPoint.getLocation()), MAX_BILINEAR_ERROR);
|
||||
assertEquals(-85.824, altitude_egm2008.correctAltitude(trackPoint.getLocation()).toM(), MAX_BILINEAR_ERROR);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -17,11 +17,14 @@ package de.dennisguse.opentracks.util;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
@@ -37,7 +40,7 @@ public class FileUtilsTest {
|
||||
*/
|
||||
@Test
|
||||
public void testBuildUniqueFileName_new() {
|
||||
String filename = FileUtils.buildUniqueFileName(new File("/dir"), "Filename", "ext");
|
||||
String filename = FileUtils.buildUniqueFileName(FileUtils.getPhotoDir(ApplicationProvider.getApplicationContext()), "Filename", "ext");
|
||||
assertEquals("Filename.ext", filename);
|
||||
}
|
||||
|
||||
@@ -45,9 +48,14 @@ public class FileUtilsTest {
|
||||
* Tests {@link FileUtils#buildUniqueFileName(File, String, String)} when the file exists already.
|
||||
*/
|
||||
@Test
|
||||
public void testBuildUniqueFileName_exist() {
|
||||
// Expect "/default.prop" to exist on the phone/emulator
|
||||
String filename = FileUtils.buildUniqueFileName(new File("/"), "default", "prop");
|
||||
public void testBuildUniqueFileName_exist() throws IOException {
|
||||
// given
|
||||
new File(FileUtils.getPhotoDir(ApplicationProvider.getApplicationContext()), "default.prop").createNewFile();
|
||||
|
||||
//when
|
||||
String filename = FileUtils.buildUniqueFileName(FileUtils.getPhotoDir(ApplicationProvider.getApplicationContext()), "default", "prop");
|
||||
|
||||
// then
|
||||
assertEquals("default(1).prop", filename);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import org.junit.runner.RunWith;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.ui.customRecordingLayout.DataField;
|
||||
import de.dennisguse.opentracks.ui.customRecordingLayout.Layout;
|
||||
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayout;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class StatisticsUtilsTest extends TestCase {
|
||||
@@ -44,16 +44,16 @@ public class StatisticsUtilsTest extends TestCase {
|
||||
@Test
|
||||
public void testFilterVisible() {
|
||||
// given
|
||||
Layout layout = new Layout("profile");
|
||||
layout.addField(new DataField("key1", "title1", true, true, false));
|
||||
layout.addField(new DataField("key2", "title2", false, true, false));
|
||||
layout.addField(new DataField("key3", "title3", true, true, false));
|
||||
layout.addField(new DataField("key4", "title4", false, true, false));
|
||||
layout.addField(new DataField("key5", "title5", true, true, false));
|
||||
RecordingLayout recordingLayout = new RecordingLayout("profile");
|
||||
recordingLayout.addField(new DataField("key1", true, true, false));
|
||||
recordingLayout.addField(new DataField("key2", false, true, false));
|
||||
recordingLayout.addField(new DataField("key3", true, true, false));
|
||||
recordingLayout.addField(new DataField("key4", false, true, false));
|
||||
recordingLayout.addField(new DataField("key5", true, true, false));
|
||||
|
||||
// when
|
||||
Layout resultTrue = StatisticsUtils.filterVisible(layout, true);
|
||||
Layout resultFalse = StatisticsUtils.filterVisible(layout, false);
|
||||
RecordingLayout resultTrue = recordingLayout.toRecordingLayout(true);
|
||||
RecordingLayout resultFalse = recordingLayout.toRecordingLayout(false);
|
||||
|
||||
// then
|
||||
assertEquals(resultTrue.getFields().size(), 3);
|
||||
|
||||
-199
@@ -1,199 +0,0 @@
|
||||
package de.dennisguse.opentracks.viewmodels;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
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.Power;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.services.RecordingData;
|
||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||
import de.dennisguse.opentracks.stats.SensorStatistics;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.ui.customRecordingLayout.Layout;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class StatisticDataBuilderTest extends TestCase {
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
@Mock
|
||||
private Track trackMock;
|
||||
|
||||
@Mock
|
||||
private TrackStatistics trackStatisticsMock;
|
||||
|
||||
@Mock
|
||||
private RecordingData recordingDataMock;
|
||||
|
||||
@Mock
|
||||
private SensorStatistics sensorStatisticsMock;
|
||||
|
||||
@Test
|
||||
public void testFromRecordingData() {
|
||||
when(trackMock.getTrackStatistics()).thenReturn(trackStatisticsMock);
|
||||
when(trackStatisticsMock.getTotalTime()).thenReturn(Duration.ofMillis(0));
|
||||
when(trackStatisticsMock.getMovingTime()).thenReturn(Duration.ofMillis(0));
|
||||
when(trackStatisticsMock.getTotalDistance()).thenReturn(Distance.of(0));
|
||||
when(recordingDataMock.getTrackStatistics()).thenReturn(trackStatisticsMock);
|
||||
|
||||
// given
|
||||
Layout layout = new Layout(context.getString(R.string.default_activity_default));
|
||||
layout.addField(context.getString(R.string.stats_custom_layout_total_time_key), context.getString(R.string.stats_total_time), true, true, false);
|
||||
layout.addField(context.getString(R.string.stats_custom_layout_moving_time_key), context.getString(R.string.stats_moving_time), true, true, false);
|
||||
layout.addField(context.getString(R.string.stats_custom_layout_distance_key), context.getString(R.string.stats_distance), true, true, false);
|
||||
layout.addField(context.getString(R.string.stats_custom_layout_speed_key), context.getString(R.string.stats_speed), false, true, false);
|
||||
layout.addField(context.getString(R.string.stats_custom_layout_max_speed_key), context.getString(R.string.stats_max_speed), false, true, false);
|
||||
|
||||
// when
|
||||
List<StatisticData> statisticDataList = StatisticDataBuilder.fromRecordingData(context, recordingDataMock, layout, UnitSystem.METRIC);
|
||||
|
||||
// then
|
||||
assertEquals(statisticDataList.size(), 3);
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_total_time))));
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_moving_time))));
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_distance))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromRecordingData_allFields() {
|
||||
when(trackMock.getTrackStatistics()).thenReturn(trackStatisticsMock);
|
||||
when(trackStatisticsMock.getTotalTime()).thenReturn(Duration.ofMillis(0));
|
||||
when(trackStatisticsMock.getMovingTime()).thenReturn(Duration.ofMillis(0));
|
||||
when(trackStatisticsMock.getTotalDistance()).thenReturn(Distance.of(0));
|
||||
when(recordingDataMock.getTrackStatistics()).thenReturn(trackStatisticsMock);
|
||||
|
||||
// given
|
||||
Layout layout = new Layout(context.getString(R.string.default_activity_default));
|
||||
layout.addField(context.getString(R.string.stats_custom_layout_total_time_key), context.getString(R.string.stats_total_time), true, true, false);
|
||||
layout.addField(context.getString(R.string.stats_custom_layout_moving_time_key), context.getString(R.string.stats_moving_time), true, true, false);
|
||||
layout.addField(context.getString(R.string.stats_custom_layout_distance_key), context.getString(R.string.stats_distance), true, true, false);
|
||||
layout.addField(context.getString(R.string.stats_custom_layout_speed_key), context.getString(R.string.stats_speed), true, true, false);
|
||||
layout.addField(context.getString(R.string.stats_custom_layout_average_moving_speed_key), context.getString(R.string.stats_average_moving_speed), true, true, false);
|
||||
layout.addField(context.getString(R.string.stats_custom_layout_average_speed_key), context.getString(R.string.stats_average_speed), true, true, false);
|
||||
layout.addField(context.getString(R.string.stats_custom_layout_max_speed_key), context.getString(R.string.stats_max_speed), true, true, false);
|
||||
layout.addField(context.getString(R.string.stats_custom_layout_pace_key), context.getString(R.string.stats_pace), true, true, false);
|
||||
layout.addField(context.getString(R.string.stats_custom_layout_average_moving_pace_key), context.getString(R.string.stats_average_moving_pace), true, true, false);
|
||||
layout.addField(context.getString(R.string.stats_custom_layout_average_pace_key), context.getString(R.string.stats_average_pace), true, true, false);
|
||||
layout.addField(context.getString(R.string.stats_custom_layout_fastest_pace_key), context.getString(R.string.stats_fastest_pace), true, true, false);
|
||||
layout.addField(context.getString(R.string.stats_custom_layout_altitude_key), context.getString(R.string.stats_altitude), true, true, false);
|
||||
layout.addField(context.getString(R.string.stats_custom_layout_gain_key), context.getString(R.string.stats_gain), true, true, false);
|
||||
layout.addField(context.getString(R.string.stats_custom_layout_loss_key), context.getString(R.string.stats_loss), true, true, false);
|
||||
layout.addField(context.getString(R.string.stats_custom_layout_coordinates_key), context.getString(R.string.stats_coordinates), true, true, false);
|
||||
layout.addField(context.getString(R.string.stats_custom_layout_heart_rate_key), context.getString(R.string.stats_sensors_heart_rate), true, true, false);
|
||||
layout.addField(context.getString(R.string.stats_custom_layout_cadence_key), context.getString(R.string.stats_sensors_cadence), true, true, false);
|
||||
layout.addField(context.getString(R.string.stats_custom_layout_power_key), context.getString(R.string.stats_sensors_power), true, true, false);
|
||||
|
||||
// when
|
||||
List<StatisticData> statisticDataList = StatisticDataBuilder.fromRecordingData(context, recordingDataMock, layout, UnitSystem.METRIC);
|
||||
|
||||
// then
|
||||
assertEquals(statisticDataList.size(), 18);
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_total_time))));
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_moving_time))));
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_distance))));
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_speed))));
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_average_moving_speed))));
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_average_speed))));
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_max_speed))));
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_pace))));
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_average_moving_pace))));
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_average_pace))));
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_fastest_pace))));
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_altitude))));
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_gain))));
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_loss))));
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_coordinates))));
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_sensors_heart_rate))));
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_sensors_cadence))));
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_sensors_power))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromSensorStatistics_onlyHeartRate() {
|
||||
when(sensorStatisticsMock.hasHeartRate()).thenReturn(true);
|
||||
when(sensorStatisticsMock.getMaxHeartRate()).thenReturn(HeartRate.of(200f));
|
||||
when(sensorStatisticsMock.getAvgHeartRate()).thenReturn(HeartRate.of(150f));
|
||||
when(sensorStatisticsMock.hasCadence()).thenReturn(false);
|
||||
when(sensorStatisticsMock.hasPower()).thenReturn(false);
|
||||
|
||||
// when
|
||||
List<StatisticData> statisticDataList = StatisticDataBuilder.fromSensorStatistics(context, sensorStatisticsMock);
|
||||
|
||||
// then
|
||||
assertEquals(statisticDataList.size(), 2);
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.sensor_state_heart_rate_max))));
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.sensor_state_heart_rate_avg))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromSensorStatistics_onlyCadence() {
|
||||
when(sensorStatisticsMock.hasHeartRate()).thenReturn(false);
|
||||
when(sensorStatisticsMock.hasCadence()).thenReturn(true);
|
||||
when(sensorStatisticsMock.getAvgCadence()).thenReturn(Cadence.of(90f));
|
||||
when(sensorStatisticsMock.getMaxCadence()).thenReturn(Cadence.of(110f));
|
||||
when(sensorStatisticsMock.hasPower()).thenReturn(false);
|
||||
|
||||
// when
|
||||
List<StatisticData> statisticDataList = StatisticDataBuilder.fromSensorStatistics(context, sensorStatisticsMock);
|
||||
|
||||
// then
|
||||
assertEquals(statisticDataList.size(), 2);
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.sensor_state_cadence_max))));
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.sensor_state_cadence_avg))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromSensorStatistics_onlyPower() {
|
||||
when(sensorStatisticsMock.hasHeartRate()).thenReturn(false);
|
||||
when(sensorStatisticsMock.hasCadence()).thenReturn(false);
|
||||
when(sensorStatisticsMock.hasPower()).thenReturn(true);
|
||||
when(sensorStatisticsMock.getAvgPower()).thenReturn(Power.of(300f));
|
||||
|
||||
// when
|
||||
List<StatisticData> statisticDataList = StatisticDataBuilder.fromSensorStatistics(context, sensorStatisticsMock);
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.sensor_state_power_avg))));
|
||||
|
||||
// then
|
||||
assertEquals(statisticDataList.size(), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromSensorStatistics() {
|
||||
when(sensorStatisticsMock.hasHeartRate()).thenReturn(true);
|
||||
when(sensorStatisticsMock.getMaxHeartRate()).thenReturn(HeartRate.of(200f));
|
||||
when(sensorStatisticsMock.getAvgHeartRate()).thenReturn(HeartRate.of(150f));
|
||||
when(sensorStatisticsMock.hasCadence()).thenReturn(true);
|
||||
when(sensorStatisticsMock.getAvgCadence()).thenReturn(Cadence.of(90f));
|
||||
when(sensorStatisticsMock.getMaxCadence()).thenReturn(Cadence.of(110f));
|
||||
when(sensorStatisticsMock.hasPower()).thenReturn(true);
|
||||
when(sensorStatisticsMock.getAvgPower()).thenReturn(Power.of(300f));
|
||||
|
||||
// when
|
||||
List<StatisticData> statisticDataList = StatisticDataBuilder.fromSensorStatistics(context, sensorStatisticsMock);
|
||||
|
||||
// then
|
||||
assertEquals(statisticDataList.size(), 5);
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.sensor_state_heart_rate_max))));
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.sensor_state_heart_rate_avg))));
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.sensor_state_cadence_max))));
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.sensor_state_cadence_avg))));
|
||||
assertTrue(statisticDataList.stream().anyMatch(i -> i.getField().getTitle().equals(context.getString(R.string.sensor_state_power_avg))));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<shortcut
|
||||
android:enabled="true"
|
||||
android:icon="@drawable/ic_statistics_24dp"
|
||||
android:shortcutId="stats"
|
||||
android:shortcutLongLabel="@string/menu_aggregated_statistics"
|
||||
android:shortcutShortLabel="@string/menu_aggregated_statistics">
|
||||
|
||||
<intent
|
||||
android:action="android.intent.action.VIEW"
|
||||
android:targetClass="de.dennisguse.opentracks.ui.aggregatedStatistics.AggregatedStatisticsActivity"
|
||||
android:targetPackage="de.dennisguse.opentracks.debug" />
|
||||
</shortcut>
|
||||
</shortcuts>
|
||||
@@ -62,7 +62,9 @@ limitations under the License.
|
||||
|
||||
<application
|
||||
android:name=".Startup"
|
||||
android:allowBackup="false"
|
||||
android:allowBackup="true"
|
||||
android:fullBackupContent="@xml/backup_rules_30_lower"
|
||||
android:dataExtractionRules="@xml/backup_rules_31_higher"
|
||||
android:hardwareAccelerated="true"
|
||||
android:hasFragileUserData="false"
|
||||
android:icon="@drawable/ic_launcher"
|
||||
@@ -87,6 +89,7 @@ limitations under the License.
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- NOTE: the name of the MAIN/LAUNCHER is used by icon packs; if this is changed, icon packs break.-->
|
||||
<activity
|
||||
android:name=".introduction.IntroductionActivity"
|
||||
android:exported="true"
|
||||
@@ -96,6 +99,11 @@ limitations under the License.
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.shortcuts"
|
||||
android:resource="@xml/shortcuts"
|
||||
/>
|
||||
</activity>
|
||||
|
||||
<activity android:name=".AboutActivity" />
|
||||
@@ -346,6 +354,7 @@ limitations under the License.
|
||||
<activity android:name=".ui.aggregatedStatistics.AggregatedStatisticsActivity" />
|
||||
|
||||
<activity android:name=".io.file.importer.DirectoryChooserActivity$ExportDirectoryChooserActivity" />
|
||||
<activity android:name=".io.file.importer.DirectoryChooserActivity$ExportDirectoryChooserOneFileActivity" />
|
||||
<activity android:name=".io.file.importer.DirectoryChooserActivity$DefaultTrackExportDirectoryChooserActivity" />
|
||||
|
||||
<activity android:name=".io.file.exporter.ExportActivity" />
|
||||
|
||||
@@ -24,7 +24,7 @@ public class ShowErrorActivity extends AbstractActivity {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
viewBinding.textViewError.setText(getIntent().getStringExtra(EXTRA_ERROR_TEXT));
|
||||
viewBinding.bottomAppBarLayout.bottomAppBarTitle.setText(createErrorTitle());
|
||||
viewBinding.showErrorToolbar.setTitle(createErrorTitle());
|
||||
setSupportActionBar(viewBinding.bottomAppBarLayout.bottomAppBar);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.widget.ArrayAdapter;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.databinding.TrackEditBinding;
|
||||
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
|
||||
@@ -73,9 +74,9 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
|
||||
|
||||
viewBinding.fields.trackEditName.setText(track.getName());
|
||||
|
||||
viewBinding.fields.trackEditActivityType.setText(track.getCategory());
|
||||
viewBinding.fields.trackEditActivityType.setText(track.getActivityType());
|
||||
|
||||
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.activity_types, android.R.layout.simple_dropdown_item_1line);
|
||||
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_dropdown_item_1line, ActivityType.getLocalizedStrings(this));
|
||||
viewBinding.fields.trackEditActivityType.setAdapter(adapter);
|
||||
viewBinding.fields.trackEditActivityType.setOnItemClickListener((parent, view, position, id) -> setActivityTypeIcon(TrackIconUtils.getIconValue(this, (String) viewBinding.fields.trackEditActivityType.getAdapter().getItem(position))));
|
||||
viewBinding.fields.trackEditActivityType.setOnFocusChangeListener((v, hasFocus) -> {
|
||||
@@ -108,7 +109,6 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
|
||||
viewBinding.trackEditCancel.setOnClickListener(v -> finish());
|
||||
viewBinding.trackEditCancel.setVisibility(View.VISIBLE);
|
||||
|
||||
viewBinding.bottomAppBarLayout.bottomAppBarTitle.setText(getString(R.string.menu_edit));
|
||||
setSupportActionBar(viewBinding.bottomAppBarLayout.bottomAppBar);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ import android.database.Cursor;
|
||||
import android.graphics.drawable.AnimatedVectorDrawable;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Vibrator;
|
||||
import android.provider.Settings;
|
||||
import android.util.Pair;
|
||||
import android.view.KeyEvent;
|
||||
@@ -36,6 +35,7 @@ import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.cursoradapter.widget.ResourceCursorAdapter;
|
||||
@@ -43,12 +43,12 @@ import androidx.loader.app.LoaderManager;
|
||||
import androidx.loader.content.CursorLoader;
|
||||
import androidx.loader.content.Loader;
|
||||
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.DistanceFormatter;
|
||||
@@ -83,7 +83,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
|
||||
private static final String TAG = TrackListActivity.class.getSimpleName();
|
||||
|
||||
// The following are setFrequency in onCreate
|
||||
// The following are set in onCreate
|
||||
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||
private ResourceCursorAdapter resourceCursorAdapter;
|
||||
|
||||
@@ -137,7 +137,6 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
|
||||
// Menu items
|
||||
private MenuItem searchMenuItem;
|
||||
private MenuItem startGpsMenuItem;
|
||||
|
||||
private final TrackRecordingServiceConnection.Callback bindChangedCallback = (service, unused) -> {
|
||||
service.getRecordingStatusObservable()
|
||||
@@ -165,6 +164,25 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
|
||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback);
|
||||
|
||||
viewBinding.aggregatedStatsButton.setOnClickListener((view) -> startActivity(IntentUtils.newIntent(this, AggregatedStatisticsActivity.class)));
|
||||
viewBinding.sensorStartButton.setOnClickListener((view) -> {
|
||||
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
|
||||
if (locationManager != null && !locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
|
||||
startActivity(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));
|
||||
} else {
|
||||
// Invoke trackRecordingService
|
||||
if (!gpsStatusValue.isGpsStarted()) {
|
||||
trackRecordingServiceConnection.startAndBindWithCallback(this);
|
||||
} else {
|
||||
TrackRecordingService trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
||||
if (trackRecordingService != null) {
|
||||
trackRecordingService.stopSensorsAndShutdown(); //TODO Handle this in TrackRecordingServiceConnection
|
||||
}
|
||||
trackRecordingServiceConnection.unbindAndStop(this);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
viewBinding.trackList.setEmptyView(viewBinding.trackListEmptyView);
|
||||
viewBinding.trackList.setOnItemClickListener((parent, view, position, trackIdId) -> {
|
||||
Track.Id trackId = new Track.Id(trackIdId);
|
||||
@@ -194,7 +212,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
int totalDistanceIndex = cursor.getColumnIndexOrThrow(TracksColumns.TOTALDISTANCE);
|
||||
int startTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME);
|
||||
int startTimeOffsetIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME_OFFSET);
|
||||
int categoryIndex = cursor.getColumnIndexOrThrow(TracksColumns.CATEGORY);
|
||||
int activityTypeIndex = cursor.getColumnIndexOrThrow(TracksColumns.ACTIVITY_TYPE);
|
||||
int descriptionIndex = cursor.getColumnIndexOrThrow(TracksColumns.DESCRIPTION);
|
||||
int markerCountIndex = cursor.getColumnIndexOrThrow(TracksColumns.MARKER_COUNT);
|
||||
|
||||
@@ -210,13 +228,13 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
int markerCount = cursor.getInt(markerCountIndex);
|
||||
long startTime = cursor.getLong(startTimeIndex);
|
||||
int startTimeOffset = cursor.getInt(startTimeOffsetIndex);
|
||||
String category = icon != null && !icon.equals("") ? null : cursor.getString(categoryIndex);
|
||||
String activityType = icon != null && !icon.equals("") ? null : cursor.getString(activityTypeIndex);
|
||||
String description = cursor.getString(descriptionIndex);
|
||||
|
||||
ListItemUtils.setListItem(TrackListActivity.this, view, isRecording,
|
||||
iconId, R.string.image_track, name, totalTime, totalDistance, markerCount,
|
||||
OffsetDateTime.ofInstant(Instant.ofEpochMilli(startTime), ZoneOffset.ofTotalSeconds(startTimeOffset)),
|
||||
category, description, false);
|
||||
activityType, description, false);
|
||||
}
|
||||
};
|
||||
viewBinding.trackList.setAdapter(resourceCursorAdapter);
|
||||
@@ -246,7 +264,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
}
|
||||
|
||||
// Recording -> Stop
|
||||
((Vibrator) getSystemService(Context.VIBRATOR_SERVICE)).vibrate(1000);
|
||||
ActivityUtils.vibrate(this, 1000);
|
||||
updateGpsMenuItem(false, false);
|
||||
trackRecordingServiceConnection.stopRecording(TrackListActivity.this);
|
||||
viewBinding.trackListFabAction.setImageResource(R.drawable.ic_baseline_record_24);
|
||||
@@ -254,7 +272,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
return true;
|
||||
});
|
||||
|
||||
setSupportActionBar(viewBinding.bottomAppBar);
|
||||
setSupportActionBar(viewBinding.trackListToolbar);
|
||||
|
||||
loadData(getIntent());
|
||||
}
|
||||
@@ -309,21 +327,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
getMenuInflater().inflate(R.menu.track_list, menu);
|
||||
|
||||
searchMenuItem = menu.findItem(R.id.track_list_search);
|
||||
SearchView searchView = ActivityUtils.configureSearchWidget(this, searchMenuItem);
|
||||
|
||||
searchView.findViewById(R.id.search_edit_frame).setPadding(0, 0, 48, 0);
|
||||
|
||||
SearchView.SearchAutoComplete searchAutoComplete = searchView.findViewById(R.id.search_src_text);
|
||||
searchAutoComplete.setHintTextColor(ContextCompat.getColor(this, android.R.color.white));
|
||||
searchAutoComplete.setTextColor(ContextCompat.getColor(this, android.R.color.white));
|
||||
|
||||
searchView.setOnCloseListener(() -> {
|
||||
searchView.clearFocus();
|
||||
searchMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||
return true;
|
||||
});
|
||||
|
||||
startGpsMenuItem = menu.findItem(R.id.track_list_start_gps);
|
||||
ActivityUtils.configureSearchWidget(this, searchMenuItem);
|
||||
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
@@ -340,34 +344,6 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == R.id.track_list_start_gps) {
|
||||
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
|
||||
if (locationManager != null && !locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
|
||||
startActivity(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));
|
||||
} else {
|
||||
// Invoke trackRecordingService
|
||||
if (!gpsStatusValue.isGpsStarted()) {
|
||||
trackRecordingServiceConnection.startAndBindWithCallback(this);
|
||||
} else {
|
||||
TrackRecordingService trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
||||
if (trackRecordingService != null) {
|
||||
trackRecordingService.stopSensorsAndShutdown(); //TODO Handle this in TrackRecordingServiceConnection
|
||||
}
|
||||
trackRecordingServiceConnection.unbindAndStop(this);
|
||||
}
|
||||
|
||||
// Update menu after starting or stopping gps
|
||||
this.invalidateOptionsMenu();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (item.getItemId() == R.id.track_list_aggregated_stats) {
|
||||
startActivity(IntentUtils.newIntent(this, AggregatedStatisticsActivity.class));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (item.getItemId() == R.id.track_list_markers) {
|
||||
startActivity(IntentUtils.newIntent(this, MarkerListActivity.class));
|
||||
return true;
|
||||
@@ -382,6 +358,12 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
SearchView searchView = (SearchView) searchMenuItem.getActionView();
|
||||
searchView.setIconified(false);
|
||||
searchMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (item.getItemId() == R.id.track_list_help) {
|
||||
startActivity(IntentUtils.newIntent(this, HelpActivity.class));
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
@@ -455,14 +437,12 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
*/
|
||||
//TODO Check if if can be avoided to call this outside of onGpsStatusChanged()
|
||||
private void updateGpsMenuItem(boolean isGpsStarted, boolean isRecording) {
|
||||
if (startGpsMenuItem != null) {
|
||||
startGpsMenuItem.setVisible(!isRecording);
|
||||
if (!isRecording) {
|
||||
startGpsMenuItem.setTitle(isGpsStarted ? R.string.menu_stop_gps : R.string.menu_start_gps);
|
||||
startGpsMenuItem.setIcon(isGpsStarted ? gpsStatusValue.icon : R.drawable.ic_gps_off_24dp);
|
||||
if (startGpsMenuItem.getIcon() instanceof AnimatedVectorDrawable) {
|
||||
((AnimatedVectorDrawable) startGpsMenuItem.getIcon()).start();
|
||||
}
|
||||
MaterialButton startGpsMenuItem = viewBinding.sensorStartButton;
|
||||
startGpsMenuItem.setVisibility(!isRecording ? View.VISIBLE : View.INVISIBLE);
|
||||
if (!isRecording) {
|
||||
startGpsMenuItem.setIcon(AppCompatResources.getDrawable(this, isGpsStarted ? gpsStatusValue.icon : R.drawable.ic_gps_off_24dp));
|
||||
if (startGpsMenuItem.getIcon() instanceof AnimatedVectorDrawable) {
|
||||
((AnimatedVectorDrawable) startGpsMenuItem.getIcon()).start();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -504,13 +484,6 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
return true;
|
||||
}
|
||||
|
||||
if (itemId == R.id.list_context_menu_aggregated_stats) {
|
||||
Intent intent = IntentUtils.newIntent(this, AggregatedStatisticsActivity.class)
|
||||
.putParcelableArrayListExtra(AggregatedStatisticsActivity.EXTRA_TRACK_IDS, new ArrayList<>(Arrays.asList(trackIds)));
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (itemId == R.id.list_context_menu_select_all) {
|
||||
for (int i = 0; i < viewBinding.trackList.getCount(); i++) {
|
||||
viewBinding.trackList.setItemChecked(i, true);
|
||||
@@ -532,11 +505,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
public void setSearch(String searchQuery) {
|
||||
this.searchQuery = searchQuery;
|
||||
restart();
|
||||
if (searchQuery != null) {
|
||||
setTitle(searchQuery);
|
||||
} else {
|
||||
setTitle(R.string.app_name);
|
||||
}
|
||||
viewBinding.trackListToolbar.setTitle(searchQuery == null ? getString(R.string.app_name) : searchQuery);
|
||||
}
|
||||
|
||||
public void restart() {
|
||||
@@ -547,7 +516,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
|
||||
final String[] PROJECTION = new String[]{TracksColumns._ID, TracksColumns.NAME,
|
||||
TracksColumns.DESCRIPTION, TracksColumns.CATEGORY, TracksColumns.STARTTIME, TracksColumns.STARTTIME_OFFSET,
|
||||
TracksColumns.DESCRIPTION, TracksColumns.ACTIVITY_TYPE, TracksColumns.STARTTIME, TracksColumns.STARTTIME_OFFSET,
|
||||
TracksColumns.TOTALDISTANCE, TracksColumns.TOTALTIME, TracksColumns.ICON, TracksColumns.MARKER_COUNT};
|
||||
|
||||
final String sortOrder = TracksColumns.STARTTIME + " DESC";
|
||||
@@ -557,7 +526,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
} else {
|
||||
final String SEARCH_QUERY = TracksColumns.NAME + " LIKE ? OR " +
|
||||
TracksColumns.DESCRIPTION + " LIKE ? OR " +
|
||||
TracksColumns.CATEGORY + " LIKE ?";
|
||||
TracksColumns.ACTIVITY_TYPE + " LIKE ?";
|
||||
final String[] selectionArgs = new String[]{"%" + searchQuery + "%", "%" + searchQuery + "%", "%" + searchQuery + "%"};
|
||||
return new CursorLoader(TrackListActivity.this, TracksColumns.CONTENT_URI, PROJECTION, SEARCH_QUERY, selectionArgs, sortOrder);
|
||||
}
|
||||
@@ -581,7 +550,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
|
||||
private void setFloatButton() {
|
||||
viewBinding.trackListFabAction.setImageResource(recordingStatus.isRecording() ? R.drawable.ic_baseline_stop_24 : R.drawable.ic_baseline_record_24);
|
||||
viewBinding.trackListFabAction.setBackgroundTintList(ContextCompat.getColorStateList(this, recordingStatus.isRecording() ? R.color.opentracks_secondary_color : R.color.red_dark));
|
||||
viewBinding.trackListFabAction.setBackgroundTintList(ContextCompat.getColorStateList(this, recordingStatus.isRecording() ? R.color.opentracks : R.color.red_dark));
|
||||
}
|
||||
|
||||
private void onRecordingStatusChanged(RecordingStatus status) {
|
||||
|
||||
@@ -103,7 +103,6 @@ public class TrackRecordedActivity extends AbstractTrackDeleteActivity implement
|
||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindCallback);
|
||||
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
viewBinding.bottomAppBarLayout.bottomAppBarTitle.setText(track != null ? track.getName() : "");
|
||||
viewBinding.bottomAppBarLayout.bottomAppBar.replaceMenu(R.menu.track_detail);
|
||||
setSupportActionBar(viewBinding.bottomAppBarLayout.bottomAppBar);
|
||||
|
||||
@@ -274,18 +273,14 @@ public class TrackRecordedActivity extends AbstractTrackDeleteActivity implement
|
||||
@NonNull
|
||||
@Override
|
||||
public Fragment createFragment(int position) {
|
||||
switch (position) {
|
||||
case 0:
|
||||
return StatisticsRecordedFragment.newInstance(trackId);
|
||||
case 1:
|
||||
return IntervalsFragment.newInstance(trackId, true);
|
||||
case 2:
|
||||
return ChartFragment.newInstance(false);
|
||||
case 3:
|
||||
return ChartFragment.newInstance(true);
|
||||
default:
|
||||
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
|
||||
}
|
||||
return switch (position) {
|
||||
case 0 -> StatisticsRecordedFragment.newInstance(trackId);
|
||||
case 1 -> IntervalsFragment.newInstance(trackId, true);
|
||||
case 2 -> ChartFragment.newInstance(false);
|
||||
case 3 -> ChartFragment.newInstance(true);
|
||||
default ->
|
||||
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -294,18 +289,14 @@ public class TrackRecordedActivity extends AbstractTrackDeleteActivity implement
|
||||
}
|
||||
|
||||
public CharSequence getPageTitle(int position) {
|
||||
switch (position) {
|
||||
case 0:
|
||||
return getString(R.string.track_detail_stats_tab);
|
||||
case 1:
|
||||
return getString(R.string.track_detail_intervals_tab);
|
||||
case 2:
|
||||
return getString(R.string.settings_chart_by_time);
|
||||
case 3:
|
||||
return getString(R.string.settings_chart_by_distance);
|
||||
default:
|
||||
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
|
||||
}
|
||||
return switch (position) {
|
||||
case 0 -> getString(R.string.track_detail_stats_tab);
|
||||
case 1 -> getString(R.string.track_detail_intervals_tab);
|
||||
case 2 -> getString(R.string.settings_chart_by_time);
|
||||
case 3 -> getString(R.string.settings_chart_by_distance);
|
||||
default ->
|
||||
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Vibrator;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
@@ -45,6 +43,7 @@ import de.dennisguse.opentracks.settings.SettingsActivity;
|
||||
import de.dennisguse.opentracks.ui.intervals.IntervalsFragment;
|
||||
import de.dennisguse.opentracks.ui.markers.MarkerEditActivity;
|
||||
import de.dennisguse.opentracks.ui.markers.MarkerListActivity;
|
||||
import de.dennisguse.opentracks.ui.util.ActivityUtils;
|
||||
import de.dennisguse.opentracks.util.IntentDashboardUtils;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
@@ -134,10 +133,10 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
||||
}
|
||||
|
||||
viewBinding.trackRecordingFabAction.setImageResource(R.drawable.ic_baseline_stop_24);
|
||||
viewBinding.trackRecordingFabAction.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.opentracks_secondary_color));
|
||||
viewBinding.trackRecordingFabAction.setBackgroundColor(ContextCompat.getColor(this, R.color.opentracks_secondary_color));
|
||||
viewBinding.trackRecordingFabAction.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.opentracks));
|
||||
viewBinding.trackRecordingFabAction.setBackgroundColor(ContextCompat.getColor(this, R.color.opentracks));
|
||||
viewBinding.trackRecordingFabAction.setOnLongClickListener((view) -> {
|
||||
((Vibrator) getSystemService(Context.VIBRATOR_SERVICE)).vibrate(1000);
|
||||
ActivityUtils.vibrate(this, 1000);
|
||||
trackRecordingServiceConnection.stopRecording(TrackRecordingActivity.this);
|
||||
Intent newIntent = IntentUtils.newIntent(TrackRecordingActivity.this, TrackStoppedActivity.class)
|
||||
.putExtra(TrackStoppedActivity.EXTRA_TRACK_ID, trackId);
|
||||
@@ -320,8 +319,8 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
||||
@Override
|
||||
public void onChooseActivityTypeDone(String iconValue) {
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
String category = getString(TrackIconUtils.getIconActivityType(iconValue));
|
||||
TrackUtils.updateTrack(this, track, null, category, null, contentProviderUtils);
|
||||
String activityType = getString(TrackIconUtils.getIconActivityType(iconValue));
|
||||
TrackUtils.updateTrack(this, track, null, activityType, null, contentProviderUtils);
|
||||
}
|
||||
|
||||
private class CustomFragmentPagerAdapter extends FragmentStateAdapter {
|
||||
@@ -333,18 +332,14 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
||||
@NonNull
|
||||
@Override
|
||||
public Fragment createFragment(int position) {
|
||||
switch (position) {
|
||||
case 0:
|
||||
return StatisticsRecordingFragment.newInstance();
|
||||
case 1:
|
||||
return IntervalsFragment.newInstance(trackId, false);
|
||||
case 2:
|
||||
return ChartFragment.newInstance(false);
|
||||
case 3:
|
||||
return ChartFragment.newInstance(true);
|
||||
default:
|
||||
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
|
||||
}
|
||||
return switch (position) {
|
||||
case 0 -> StatisticsRecordingFragment.newInstance();
|
||||
case 1 -> IntervalsFragment.newInstance(trackId, false);
|
||||
case 2 -> ChartFragment.newInstance(false);
|
||||
case 3 -> ChartFragment.newInstance(true);
|
||||
default ->
|
||||
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -353,18 +348,14 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
||||
}
|
||||
|
||||
public CharSequence getPageTitle(int position) {
|
||||
switch (position) {
|
||||
case 0:
|
||||
return getString(R.string.track_detail_stats_tab);
|
||||
case 1:
|
||||
return getString(R.string.track_detail_intervals_tab);
|
||||
case 2:
|
||||
return getString(R.string.settings_chart_by_time);
|
||||
case 3:
|
||||
return getString(R.string.settings_chart_by_distance);
|
||||
default:
|
||||
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
|
||||
}
|
||||
return switch (position) {
|
||||
case 0 -> getString(R.string.track_detail_stats_tab);
|
||||
case 1 -> getString(R.string.track_detail_intervals_tab);
|
||||
case 2 -> getString(R.string.settings_chart_by_time);
|
||||
case 3 -> getString(R.string.settings_chart_by_distance);
|
||||
default ->
|
||||
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -381,13 +372,18 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
||||
}
|
||||
|
||||
private void onGpsStatusChanged(GpsStatusValue gpsStatusValue) {
|
||||
if (gpsStatusValue.isGpsStarted() && snackbar != null && snackbar.isShown()) {
|
||||
snackbar.dismiss();
|
||||
boolean snackbarShowing = snackbar != null && snackbar.isShown();
|
||||
if (gpsStatusValue.isGpsStarted()) {
|
||||
if (snackbarShowing) {
|
||||
snackbar.dismiss();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (gpsStatusValue != GpsStatusValue.GPS_DISABLED) {
|
||||
|
||||
if (snackbarShowing) {
|
||||
return;
|
||||
}
|
||||
|
||||
snackbar = Snackbar
|
||||
.make(viewBinding.trackRecordingCoordinatorLayout,
|
||||
getString(R.string.gps_recording_status, getString(gpsStatusValue.message), getString(R.string.gps_recording_without_signal)),
|
||||
|
||||
@@ -8,6 +8,7 @@ import android.view.View;
|
||||
import android.widget.ArrayAdapter;
|
||||
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||
import de.dennisguse.opentracks.data.models.DistanceFormatter;
|
||||
import de.dennisguse.opentracks.data.models.SpeedFormatter;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
@@ -49,9 +50,9 @@ public class TrackStoppedActivity extends AbstractTrackDeleteActivity implements
|
||||
|
||||
viewBinding.trackEditName.setText(track.getName());
|
||||
|
||||
viewBinding.trackEditActivityType.setText(track.getCategory());
|
||||
viewBinding.trackEditActivityType.setText(track.getActivityType());
|
||||
|
||||
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.activity_types, android.R.layout.simple_dropdown_item_1line);
|
||||
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_dropdown_item_1line, ActivityType.getLocalizedStrings(this));
|
||||
viewBinding.trackEditActivityType.setAdapter(adapter);
|
||||
viewBinding.trackEditActivityType.setOnItemClickListener((parent, view, position, id) -> setActivityTypeIcon(TrackIconUtils.getIconValue(this, (String) viewBinding.trackEditActivityType.getAdapter().getItem(position))));
|
||||
viewBinding.trackEditActivityType.setOnFocusChangeListener((v, hasFocus) -> {
|
||||
@@ -73,7 +74,7 @@ public class TrackStoppedActivity extends AbstractTrackDeleteActivity implements
|
||||
{
|
||||
Pair<String, String> parts = SpeedFormatter.Builder()
|
||||
.setUnit(PreferencesUtils.getUnitSystem())
|
||||
.setReportSpeedOrPace(PreferencesUtils.isReportSpeed(track.getCategory()))
|
||||
.setReportSpeedOrPace(PreferencesUtils.isReportSpeed(track.getActivityType()))
|
||||
.build(this)
|
||||
.getSpeedParts(track.getTrackStatistics().getAverageMovingSpeed());
|
||||
viewBinding.speed.setText(parts.first);
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
package de.dennisguse.opentracks.adapters;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextClock;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.ui.customRecordingLayout.CustomLayoutFieldType;
|
||||
import de.dennisguse.opentracks.viewmodels.StatisticData;
|
||||
|
||||
public class StatisticsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||
|
||||
private List<StatisticData> statisticDataList;
|
||||
private final Context context;
|
||||
|
||||
public StatisticsAdapter(Context context) {
|
||||
this.context = context;
|
||||
this.statisticDataList = new ArrayList<>();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view;
|
||||
if (viewType == CustomLayoutFieldType.CLOCK.value()) {
|
||||
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.stats_clock_item, parent, false);
|
||||
return new StatisticsAdapter.ViewClockHolder(view);
|
||||
} else {
|
||||
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.stats_generic_item, parent, false);
|
||||
return new StatisticsAdapter.ViewHolder(view);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||
StatisticData statisticData = statisticDataList.get(position);
|
||||
if (getItemViewType(position) == CustomLayoutFieldType.CLOCK.value()) {
|
||||
StatisticsAdapter.ViewClockHolder viewHolder = (StatisticsAdapter.ViewClockHolder) holder;
|
||||
viewHolder.setData(statisticData);
|
||||
} else {
|
||||
StatisticsAdapter.ViewHolder viewHolder = (StatisticsAdapter.ViewHolder) holder;
|
||||
viewHolder.setData(statisticData);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
if (statisticDataList == null) {
|
||||
return 0;
|
||||
} else {
|
||||
return statisticDataList.size();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
if (statisticDataList.get(position).getField().getType(context) == CustomLayoutFieldType.CLOCK) {
|
||||
return CustomLayoutFieldType.CLOCK.value();
|
||||
} else {
|
||||
return CustomLayoutFieldType.GENERIC.value();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isItemWide(int position) {
|
||||
return statisticDataList.get(position).getField().isWide();
|
||||
}
|
||||
|
||||
public List<StatisticData> swapData(List<StatisticData> data) {
|
||||
if (statisticDataList == data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
statisticDataList = data;
|
||||
|
||||
if (data != null) {
|
||||
this.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public static class WithRecordedLayout extends StatisticsAdapter {
|
||||
public WithRecordedLayout(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.stats_recorded_item, parent, false);
|
||||
return new StatisticsAdapter.ViewRecordedHolder(view);
|
||||
}
|
||||
}
|
||||
|
||||
private class ViewHolder extends RecyclerView.ViewHolder {
|
||||
final TextView value;
|
||||
final TextView unit;
|
||||
final TextView descMain;
|
||||
final TextView descSecondary;
|
||||
|
||||
public ViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
value = itemView.findViewById(R.id.stats_value);
|
||||
unit = itemView.findViewById(R.id.stats_unit);
|
||||
descMain = itemView.findViewById(R.id.stats_description_main);
|
||||
descSecondary = itemView.findViewById(R.id.stats_description_secondary);
|
||||
}
|
||||
|
||||
public void setData(StatisticData statisticData) {
|
||||
if (statisticData == null) {
|
||||
return;
|
||||
}
|
||||
this.value.setText(statisticData.hasValue() ? statisticData.getValue() : context.getString(R.string.value_unknown));
|
||||
this.value.setTextAppearance(context, statisticData.getField().isPrimary() ? R.style.TextAppearance_OpenTracks_PrimaryValue : R.style.TextAppearance_OpenTracks_SecondaryValue);
|
||||
this.unit.setText(statisticData.getUnit());
|
||||
this.descMain.setText(statisticData.getField().getTitle());
|
||||
this.descMain.setTextAppearance(context, statisticData.getField().isPrimary() ? R.style.TextAppearance_OpenTracks_PrimaryHeader : R.style.TextAppearance_OpenTracks_SecondaryHeader);
|
||||
if (statisticData.hasDescription()) {
|
||||
this.descSecondary.setVisibility(View.VISIBLE);
|
||||
this.descSecondary.setText(statisticData.getDescription());
|
||||
} else {
|
||||
this.descSecondary.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ViewClockHolder extends RecyclerView.ViewHolder {
|
||||
final TextClock value;
|
||||
final TextView descMain;
|
||||
|
||||
public ViewClockHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
value = itemView.findViewById(R.id.stats_clock);
|
||||
descMain = itemView.findViewById(R.id.stats_description_main);
|
||||
}
|
||||
|
||||
public void setData(StatisticData statisticData) {
|
||||
if (statisticData == null) {
|
||||
return;
|
||||
}
|
||||
this.value.setTextAppearance(context, statisticData.getField().isPrimary() ? R.style.TextAppearance_OpenTracks_PrimaryValue : R.style.TextAppearance_OpenTracks_SecondaryValue);
|
||||
this.descMain.setTextAppearance(context, statisticData.getField().isPrimary() ? R.style.TextAppearance_OpenTracks_PrimaryHeader : R.style.TextAppearance_OpenTracks_SecondaryHeader);
|
||||
}
|
||||
}
|
||||
|
||||
private class ViewRecordedHolder extends ViewHolder {
|
||||
|
||||
public ViewRecordedHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
}
|
||||
|
||||
public void setData(StatisticData statisticData) {
|
||||
if (statisticData == null) {
|
||||
return;
|
||||
}
|
||||
this.value.setText(statisticData.hasValue() ? statisticData.getValue() : context.getString(R.string.value_unknown));
|
||||
this.unit.setText(statisticData.getUnit());
|
||||
this.descMain.setText(statisticData.getField().getTitle());
|
||||
if (statisticData.hasDescription()) {
|
||||
this.descSecondary.setVisibility(View.VISIBLE);
|
||||
this.descSecondary.setText(statisticData.getDescription());
|
||||
} else {
|
||||
this.descSecondary.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,7 +164,7 @@ public class ChartFragment extends Fragment implements TrackDataHub.Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
category = track.getCategory();
|
||||
category = track.getActivityType();
|
||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(category);
|
||||
if (reportSpeed != viewBinding.chartView.getReportSpeed()) {
|
||||
viewBinding.chartView.setReportSpeed(reportSpeed);
|
||||
|
||||
@@ -210,16 +210,11 @@ abstract class ChartValueSeries {
|
||||
}
|
||||
|
||||
int getTitleId(UnitSystem unitSystem) {
|
||||
switch (unitSystem) {
|
||||
case METRIC:
|
||||
return metricTitleId;
|
||||
case IMPERIAL:
|
||||
return imperialTitleId;
|
||||
case NAUTICAL_IMPERIAL:
|
||||
return nauticalTitleId;
|
||||
default:
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
return switch (unitSystem) {
|
||||
case METRIC -> metricTitleId;
|
||||
case IMPERIAL_FEET, IMPERIAL_METER -> imperialTitleId;
|
||||
case NAUTICAL_IMPERIAL -> nauticalTitleId;
|
||||
};
|
||||
}
|
||||
|
||||
Paint getTitlePaint() {
|
||||
|
||||
@@ -733,16 +733,11 @@ public class ChartView extends View {
|
||||
private String getXAxisLabel() {
|
||||
Context context = getContext();
|
||||
if (chartByDistance) {
|
||||
switch (unitSystem) {
|
||||
case METRIC:
|
||||
return context.getString(R.string.unit_kilometer);
|
||||
case IMPERIAL:
|
||||
return context.getString(R.string.unit_mile);
|
||||
case NAUTICAL_IMPERIAL:
|
||||
return context.getString(R.string.unit_nautical_mile);
|
||||
default:
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
return switch (unitSystem) {
|
||||
case METRIC -> context.getString(R.string.unit_kilometer);
|
||||
case IMPERIAL_FEET, IMPERIAL_METER -> context.getString(R.string.unit_mile);
|
||||
case NAUTICAL_IMPERIAL -> context.getString(R.string.unit_nautical_mile);
|
||||
};
|
||||
} else {
|
||||
return context.getString(R.string.description_time);
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ public class ContentProviderUtils {
|
||||
int uuidIndex = cursor.getColumnIndexOrThrow(TracksColumns.UUID);
|
||||
int nameIndex = cursor.getColumnIndexOrThrow(TracksColumns.NAME);
|
||||
int descriptionIndex = cursor.getColumnIndexOrThrow(TracksColumns.DESCRIPTION);
|
||||
int categoryIndex = cursor.getColumnIndexOrThrow(TracksColumns.CATEGORY);
|
||||
int categoryIndex = cursor.getColumnIndexOrThrow(TracksColumns.ACTIVITY_TYPE);
|
||||
int startTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME);
|
||||
int startTimeOffsetIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME_OFFSET);
|
||||
int stopTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.STOPTIME);
|
||||
@@ -129,7 +129,7 @@ public class ContentProviderUtils {
|
||||
track.setDescription(cursor.getString(descriptionIndex));
|
||||
}
|
||||
if (!cursor.isNull(categoryIndex)) {
|
||||
track.setCategory(cursor.getString(categoryIndex));
|
||||
track.setActivityType(cursor.getString(categoryIndex));
|
||||
}
|
||||
|
||||
if (!cursor.isNull(startTimeIndex)) {
|
||||
@@ -290,7 +290,7 @@ public class ContentProviderUtils {
|
||||
values.put(TracksColumns.UUID, UUIDUtils.toBytes(track.getUuid()));
|
||||
values.put(TracksColumns.NAME, track.getName());
|
||||
values.put(TracksColumns.DESCRIPTION, track.getDescription());
|
||||
values.put(TracksColumns.CATEGORY, track.getCategory());
|
||||
values.put(TracksColumns.ACTIVITY_TYPE, track.getActivityType());
|
||||
values.put(TracksColumns.STARTTIME_OFFSET, track.getZoneOffset().getTotalSeconds());
|
||||
if (trackStatistics.getStartTime() != null) {
|
||||
values.put(TracksColumns.STARTTIME, trackStatistics.getStartTime().toEpochMilli());
|
||||
@@ -403,7 +403,7 @@ public class ContentProviderUtils {
|
||||
public void deleteMarker(Context context, Marker.Id markerId) {
|
||||
final Marker marker = getMarker(markerId);
|
||||
deleteMarkerPhoto(context, marker);
|
||||
contentResolver.delete(MarkerColumns.CONTENT_URI, MarkerColumns._ID + "=?", new String[]{Long.toString(markerId.getId())});
|
||||
contentResolver.delete(MarkerColumns.CONTENT_URI, MarkerColumns._ID + "=?", new String[]{Long.toString(markerId.id())});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -422,7 +422,7 @@ public class ContentProviderUtils {
|
||||
}
|
||||
|
||||
public Marker getMarker(@NonNull Marker.Id markerId) {
|
||||
try (Cursor cursor = getMarkerCursor(null, MarkerColumns._ID + "=?", new String[]{Long.toString(markerId.getId())}, MarkerColumns._ID, 1)) {
|
||||
try (Cursor cursor = getMarkerCursor(null, MarkerColumns._ID + "=?", new String[]{Long.toString(markerId.id())}, MarkerColumns._ID, 1)) {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
return createMarker(cursor);
|
||||
}
|
||||
@@ -442,7 +442,7 @@ public class ContentProviderUtils {
|
||||
String[] selectionArgs;
|
||||
if (minMarkerId != null) {
|
||||
selection = MarkerColumns.TRACKID + "=? AND " + MarkerColumns._ID + ">=?";
|
||||
selectionArgs = new String[]{Long.toString(trackId.getId()), Long.toString(minMarkerId.getId())};
|
||||
selectionArgs = new String[]{Long.toString(trackId.getId()), Long.toString(minMarkerId.id())};
|
||||
} else {
|
||||
selection = MarkerColumns.TRACKID + "=?";
|
||||
selectionArgs = new String[]{Long.toString(trackId.getId())};
|
||||
@@ -495,7 +495,7 @@ public class ContentProviderUtils {
|
||||
if (!updateMarker.hasPhoto()) {
|
||||
deleteMarkerPhoto(context, savedMarker);
|
||||
}
|
||||
int rows = contentResolver.update(MarkerColumns.CONTENT_URI, createContentValues(updateMarker), MarkerColumns._ID + "=?", new String[]{Long.toString(updateMarker.getId().getId())});
|
||||
int rows = contentResolver.update(MarkerColumns.CONTENT_URI, createContentValues(updateMarker), MarkerColumns._ID + "=?", new String[]{Long.toString(updateMarker.getId().id())});
|
||||
return rows == 1;
|
||||
}
|
||||
|
||||
@@ -503,7 +503,7 @@ public class ContentProviderUtils {
|
||||
ContentValues values = new ContentValues();
|
||||
|
||||
if (marker.getId() != null) {
|
||||
values.put(MarkerColumns._ID, marker.getId().getId());
|
||||
values.put(MarkerColumns._ID, marker.getId().id());
|
||||
}
|
||||
values.put(MarkerColumns.NAME, marker.getName());
|
||||
values.put(MarkerColumns.DESCRIPTION, marker.getDescription());
|
||||
|
||||
@@ -133,20 +133,12 @@ public class CustomContentProvider extends ContentProvider {
|
||||
|
||||
@Override
|
||||
public int delete(@NonNull Uri url, String where, String[] selectionArgs) {
|
||||
String table;
|
||||
switch (getUrlType(url)) {
|
||||
case TRACKPOINTS:
|
||||
table = TrackPointsColumns.TABLE_NAME;
|
||||
break;
|
||||
case TRACKS:
|
||||
table = TracksColumns.TABLE_NAME;
|
||||
break;
|
||||
case MARKERS:
|
||||
table = MarkerColumns.TABLE_NAME;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URL " + url);
|
||||
}
|
||||
String table = switch (getUrlType(url)) {
|
||||
case TRACKPOINTS -> TrackPointsColumns.TABLE_NAME;
|
||||
case TRACKS -> TracksColumns.TABLE_NAME;
|
||||
case MARKERS -> MarkerColumns.TABLE_NAME;
|
||||
default -> throw new IllegalArgumentException("Unknown URL " + url);
|
||||
};
|
||||
|
||||
Log.w(TAG, "Deleting from table " + table);
|
||||
int totalChangesBefore = getTotalChanges();
|
||||
@@ -186,24 +178,15 @@ public class CustomContentProvider extends ContentProvider {
|
||||
|
||||
@Override
|
||||
public String getType(@NonNull Uri url) {
|
||||
switch (getUrlType(url)) {
|
||||
case TRACKPOINTS:
|
||||
return TrackPointsColumns.CONTENT_TYPE;
|
||||
case TRACKPOINTS_BY_ID:
|
||||
case TRACKPOINTS_BY_TRACKID:
|
||||
return TrackPointsColumns.CONTENT_ITEMTYPE;
|
||||
case TRACKS:
|
||||
return TracksColumns.CONTENT_TYPE;
|
||||
case TRACKS_BY_ID:
|
||||
return TracksColumns.CONTENT_ITEMTYPE;
|
||||
case MARKERS:
|
||||
return MarkerColumns.CONTENT_TYPE;
|
||||
case MARKERS_BY_ID:
|
||||
case MARKERS_BY_TRACKID:
|
||||
return MarkerColumns.CONTENT_ITEMTYPE;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown URL " + url);
|
||||
}
|
||||
return switch (getUrlType(url)) {
|
||||
case TRACKPOINTS -> TrackPointsColumns.CONTENT_TYPE;
|
||||
case TRACKPOINTS_BY_ID, TRACKPOINTS_BY_TRACKID -> TrackPointsColumns.CONTENT_ITEMTYPE;
|
||||
case TRACKS -> TracksColumns.CONTENT_TYPE;
|
||||
case TRACKS_BY_ID -> TracksColumns.CONTENT_ITEMTYPE;
|
||||
case MARKERS -> MarkerColumns.CONTENT_TYPE;
|
||||
case MARKERS_BY_ID, MARKERS_BY_TRACKID -> MarkerColumns.CONTENT_ITEMTYPE;
|
||||
default -> throw new IllegalArgumentException("Unknown URL " + url);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -251,47 +234,47 @@ public class CustomContentProvider extends ContentProvider {
|
||||
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
|
||||
String sortOrder = null;
|
||||
switch (getUrlType(url)) {
|
||||
case TRACKPOINTS:
|
||||
case TRACKPOINTS -> {
|
||||
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
||||
sortOrder = sort != null ? sort : TrackPointsColumns.DEFAULT_SORT_ORDER;
|
||||
break;
|
||||
case TRACKPOINTS_BY_ID:
|
||||
}
|
||||
case TRACKPOINTS_BY_ID -> {
|
||||
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
||||
queryBuilder.appendWhere(TrackPointsColumns._ID + "=" + ContentUris.parseId(url));
|
||||
break;
|
||||
case TRACKPOINTS_BY_TRACKID:
|
||||
}
|
||||
case TRACKPOINTS_BY_TRACKID -> {
|
||||
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
||||
queryBuilder.appendWhere(TrackPointsColumns.TRACKID + " IN (" + TextUtils.join(SQL_LIST_DELIMITER, ContentProviderUtils.parseTrackIdsFromUri(url)) + ")");
|
||||
break;
|
||||
case TRACKS:
|
||||
}
|
||||
case TRACKS -> {
|
||||
if (projection != null && Arrays.asList(projection).contains(TracksColumns.MARKER_COUNT)) {
|
||||
queryBuilder.setTables(TracksColumns.TABLE_NAME + " LEFT OUTER JOIN (SELECT " + MarkerColumns.TRACKID + " AS markerTrackId, COUNT(*) AS " + TracksColumns.MARKER_COUNT + " FROM " + MarkerColumns.TABLE_NAME + " GROUP BY " + MarkerColumns.TRACKID + ") ON (" + TracksColumns.TABLE_NAME + "." + TracksColumns._ID + "= markerTrackId)");
|
||||
} else {
|
||||
queryBuilder.setTables(TracksColumns.TABLE_NAME);
|
||||
}
|
||||
sortOrder = sort != null ? sort : TracksColumns.DEFAULT_SORT_ORDER;
|
||||
break;
|
||||
case TRACKS_BY_ID:
|
||||
}
|
||||
case TRACKS_BY_ID -> {
|
||||
queryBuilder.setTables(TracksColumns.TABLE_NAME);
|
||||
queryBuilder.appendWhere(TracksColumns._ID + " IN (" + TextUtils.join(SQL_LIST_DELIMITER, ContentProviderUtils.parseTrackIdsFromUri(url)) + ")");
|
||||
break;
|
||||
case TRACKS_SENSOR_STATS:
|
||||
}
|
||||
case TRACKS_SENSOR_STATS -> {
|
||||
long trackId = ContentUris.parseId(url);
|
||||
return db.rawQuery(SENSOR_STATS_QUERY, new String[]{String.valueOf(trackId), String.valueOf(trackId)});
|
||||
case MARKERS:
|
||||
}
|
||||
case MARKERS -> {
|
||||
queryBuilder.setTables(MarkerColumns.TABLE_NAME);
|
||||
sortOrder = sort != null ? sort : MarkerColumns.DEFAULT_SORT_ORDER;
|
||||
break;
|
||||
case MARKERS_BY_ID:
|
||||
}
|
||||
case MARKERS_BY_ID -> {
|
||||
queryBuilder.setTables(MarkerColumns.TABLE_NAME);
|
||||
queryBuilder.appendWhere(MarkerColumns._ID + "=" + ContentUris.parseId(url));
|
||||
break;
|
||||
case MARKERS_BY_TRACKID:
|
||||
}
|
||||
case MARKERS_BY_TRACKID -> {
|
||||
queryBuilder.setTables(MarkerColumns.TABLE_NAME);
|
||||
queryBuilder.appendWhere(MarkerColumns.TRACKID + " IN (" + TextUtils.join(SQL_LIST_DELIMITER, ContentProviderUtils.parseTrackIdsFromUri(url)) + ")");
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown url " + url);
|
||||
}
|
||||
default -> throw new IllegalArgumentException("Unknown url " + url);
|
||||
}
|
||||
Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
|
||||
cursor.setNotificationUri(getContext().getContentResolver(), url);
|
||||
@@ -304,41 +287,40 @@ public class CustomContentProvider extends ContentProvider {
|
||||
String table;
|
||||
String whereClause;
|
||||
switch (getUrlType(url)) {
|
||||
case TRACKPOINTS:
|
||||
case TRACKPOINTS -> {
|
||||
table = TrackPointsColumns.TABLE_NAME;
|
||||
whereClause = where;
|
||||
break;
|
||||
case TRACKPOINTS_BY_ID:
|
||||
}
|
||||
case TRACKPOINTS_BY_ID -> {
|
||||
table = TrackPointsColumns.TABLE_NAME;
|
||||
whereClause = TrackPointsColumns._ID + "=" + ContentUris.parseId(url);
|
||||
if (!TextUtils.isEmpty(where)) {
|
||||
whereClause += " AND (" + where + ")";
|
||||
}
|
||||
break;
|
||||
case TRACKS:
|
||||
}
|
||||
case TRACKS -> {
|
||||
table = TracksColumns.TABLE_NAME;
|
||||
whereClause = where;
|
||||
break;
|
||||
case TRACKS_BY_ID:
|
||||
}
|
||||
case TRACKS_BY_ID -> {
|
||||
table = TracksColumns.TABLE_NAME;
|
||||
whereClause = TracksColumns._ID + "=" + ContentUris.parseId(url);
|
||||
if (!TextUtils.isEmpty(where)) {
|
||||
whereClause += " AND (" + where + ")";
|
||||
}
|
||||
break;
|
||||
case MARKERS:
|
||||
}
|
||||
case MARKERS -> {
|
||||
table = MarkerColumns.TABLE_NAME;
|
||||
whereClause = where;
|
||||
break;
|
||||
case MARKERS_BY_ID:
|
||||
}
|
||||
case MARKERS_BY_ID -> {
|
||||
table = MarkerColumns.TABLE_NAME;
|
||||
whereClause = MarkerColumns._ID + "=" + ContentUris.parseId(url);
|
||||
if (!TextUtils.isEmpty(where)) {
|
||||
whereClause += " AND (" + where + ")";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown url " + url);
|
||||
}
|
||||
default -> throw new IllegalArgumentException("Unknown url " + url);
|
||||
}
|
||||
int count;
|
||||
try {
|
||||
@@ -371,16 +353,12 @@ public class CustomContentProvider extends ContentProvider {
|
||||
* @param contentValues the content values
|
||||
*/
|
||||
private Uri insertContentValues(Uri url, UrlType urlType, ContentValues contentValues) {
|
||||
switch (urlType) {
|
||||
case TRACKPOINTS:
|
||||
return insertTrackPoint(url, contentValues);
|
||||
case TRACKS:
|
||||
return insertTrack(url, contentValues);
|
||||
case MARKERS:
|
||||
return insertMarker(url, contentValues);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown url " + url);
|
||||
}
|
||||
return switch (urlType) {
|
||||
case TRACKPOINTS -> insertTrackPoint(url, contentValues);
|
||||
case TRACKS -> insertTrack(url, contentValues);
|
||||
case MARKERS -> insertMarker(url, contentValues);
|
||||
default -> throw new IllegalArgumentException("Unknown url " + url);
|
||||
};
|
||||
}
|
||||
|
||||
private Uri insertTrackPoint(Uri url, ContentValues values) {
|
||||
|
||||
@@ -61,42 +61,18 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
||||
for (int toVersion = oldVersion + 1; toVersion <= newVersion; toVersion++) {
|
||||
Log.i(TAG, "Upgrade from " + oldVersion + " to " + toVersion);
|
||||
switch (toVersion) {
|
||||
case 24:
|
||||
upgradeFrom23to24(db);
|
||||
break;
|
||||
case 25:
|
||||
upgradeFrom24to25(db);
|
||||
break;
|
||||
case 26:
|
||||
upgradeFrom25to26(db);
|
||||
break;
|
||||
case 27:
|
||||
upgradeFrom26to27(db);
|
||||
break;
|
||||
case 28:
|
||||
upgradeFrom27to28(db);
|
||||
break;
|
||||
case 29:
|
||||
upgradeFrom28to29(db);
|
||||
break;
|
||||
case 30:
|
||||
upgradeFrom29to30(db);
|
||||
break;
|
||||
case 31:
|
||||
upgradeFrom30to31(db);
|
||||
break;
|
||||
case 32:
|
||||
upgradeFrom31to32(db);
|
||||
break;
|
||||
case 33:
|
||||
upgradeFrom32to33(db);
|
||||
break;
|
||||
case 34:
|
||||
upgradeFrom33to34(db);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new RuntimeException("Not implemented: upgrade to " + toVersion);
|
||||
case 24 -> upgradeFrom23to24(db);
|
||||
case 25 -> upgradeFrom24to25(db);
|
||||
case 26 -> upgradeFrom25to26(db);
|
||||
case 27 -> upgradeFrom26to27(db);
|
||||
case 28 -> upgradeFrom27to28(db);
|
||||
case 29 -> upgradeFrom28to29(db);
|
||||
case 30 -> upgradeFrom29to30(db);
|
||||
case 31 -> upgradeFrom30to31(db);
|
||||
case 32 -> upgradeFrom31to32(db);
|
||||
case 33 -> upgradeFrom32to33(db);
|
||||
case 34 -> upgradeFrom33to34(db);
|
||||
default -> throw new RuntimeException("Not implemented: upgrade to " + toVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,41 +82,18 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
||||
for (int toVersion = oldVersion - 1; toVersion >= newVersion; toVersion--) {
|
||||
Log.i(TAG, "Downgrade from " + oldVersion + " to " + toVersion);
|
||||
switch (toVersion) {
|
||||
case 23:
|
||||
downgradeFrom24to23(db);
|
||||
break;
|
||||
case 24:
|
||||
downgradeFrom25to24(db);
|
||||
break;
|
||||
case 25:
|
||||
downgradeFrom26to25(db);
|
||||
break;
|
||||
case 26:
|
||||
downgradeFrom27to26(db);
|
||||
break;
|
||||
case 27:
|
||||
downgradeFrom28to27(db);
|
||||
break;
|
||||
case 28:
|
||||
downgradeFrom29to28(db);
|
||||
break;
|
||||
case 29:
|
||||
downgradeFrom30to29(db);
|
||||
break;
|
||||
case 30:
|
||||
downgradeFrom31to30(db);
|
||||
break;
|
||||
case 31:
|
||||
downgradeFrom32to31(db);
|
||||
break;
|
||||
case 32:
|
||||
downgradeFrom33to32(db);
|
||||
break;
|
||||
case 33:
|
||||
downgradeFrom34to33(db);
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Not implemented: downgrade to " + toVersion);
|
||||
case 23 -> downgradeFrom24to23(db);
|
||||
case 24 -> downgradeFrom25to24(db);
|
||||
case 25 -> downgradeFrom26to25(db);
|
||||
case 26 -> downgradeFrom27to26(db);
|
||||
case 27 -> downgradeFrom28to27(db);
|
||||
case 28 -> downgradeFrom29to28(db);
|
||||
case 29 -> downgradeFrom30to29(db);
|
||||
case 30 -> downgradeFrom31to30(db);
|
||||
case 31 -> downgradeFrom32to31(db);
|
||||
case 32 -> downgradeFrom33to32(db);
|
||||
case 33 -> downgradeFrom34to33(db);
|
||||
default -> throw new RuntimeException("Not implemented: downgrade to " + toVersion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,24 +129,18 @@ public class ShareContentProvider extends CustomContentProvider {
|
||||
}
|
||||
|
||||
private static TrackFileFormat getTrackFileFormat(@NonNull Uri uri) {
|
||||
switch (uriMatcher.match(uri)) {
|
||||
case URI_GPX:
|
||||
return TrackFileFormat.GPX;
|
||||
|
||||
case URI_KML_WITH_TRACKDETAIL_SENSORDATA:
|
||||
return TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA;
|
||||
|
||||
case URI_KMZ_WITH_TRACKDETAIL_AND_SENSORDATA:
|
||||
return TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA;
|
||||
case URI_KMZ_WITH_TRACKDETAIL_SENSORDATA_AND_PICTURES:
|
||||
return TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES;
|
||||
|
||||
case URI_CSV:
|
||||
return TrackFileFormat.CSV;
|
||||
|
||||
default:
|
||||
throw new RuntimeException("Could not derive TrackFileFormat from Uri " + uri);
|
||||
}
|
||||
return switch (uriMatcher.match(uri)) {
|
||||
case URI_GPX -> TrackFileFormat.GPX;
|
||||
case URI_KML_WITH_TRACKDETAIL_SENSORDATA ->
|
||||
TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA;
|
||||
case URI_KMZ_WITH_TRACKDETAIL_AND_SENSORDATA ->
|
||||
TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA;
|
||||
case URI_KMZ_WITH_TRACKDETAIL_SENSORDATA_AND_PICTURES ->
|
||||
TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES;
|
||||
case URI_CSV -> TrackFileFormat.CSV;
|
||||
default ->
|
||||
throw new RuntimeException("Could not derive TrackFileFormat from Uri " + uri);
|
||||
};
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -170,14 +164,14 @@ public class ShareContentProvider extends CustomContentProvider {
|
||||
int i = 0;
|
||||
for (String col : projection) {
|
||||
switch (col) {
|
||||
case OpenableColumns.DISPLAY_NAME:
|
||||
case OpenableColumns.DISPLAY_NAME -> {
|
||||
cols[i] = OpenableColumns.DISPLAY_NAME;
|
||||
values[i++] = uri.getLastPathSegment();
|
||||
break;
|
||||
case OpenableColumns.SIZE:
|
||||
}
|
||||
case OpenableColumns.SIZE -> {
|
||||
cols[i] = OpenableColumns.SIZE;
|
||||
values[i++] = -1; //Report unknown size; if applications need to know, one need to generate the file here also (count bytes that are written to OutputStream.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,12 +208,12 @@ public class ShareContentProvider extends CustomContentProvider {
|
||||
}
|
||||
}
|
||||
|
||||
final TrackExporter trackExporter = getTrackFileFormat(uri).createTrackExporter(getContext());
|
||||
final TrackExporter trackExporter = getTrackFileFormat(uri).createTrackExporter(getContext(), new ContentProviderUtils(getContext()));
|
||||
|
||||
PipeDataWriter<String> pipeDataWriter = (output, uri1, mimeType, opts, args) -> {
|
||||
try (FileOutputStream fileOutputStream = new FileOutputStream(output.getFileDescriptor())) {
|
||||
// TODO handle failure (i.e., do not export an empty file)
|
||||
trackExporter.writeTrack(tracks.toArray(new Track[0]), fileOutputStream);
|
||||
trackExporter.writeTrack(tracks, fileOutputStream);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "there occurred an error while sharing a file: " + e);
|
||||
}
|
||||
|
||||
@@ -30,9 +30,9 @@ public class TrackSelection implements ContentProviderUtils.ContentProviderSelec
|
||||
return this;
|
||||
}
|
||||
|
||||
public TrackSelection addCategory(String category) {
|
||||
if (!this.categories.contains(category)) {
|
||||
this.categories.add(category);
|
||||
public TrackSelection addActivityType(String activityType) {
|
||||
if (!this.categories.contains(activityType)) {
|
||||
this.categories.add(activityType);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -53,7 +53,7 @@ public class TrackSelection implements ContentProviderUtils.ContentProviderSelec
|
||||
}
|
||||
if (!categories.isEmpty()) {
|
||||
selection += selection.isEmpty() ? "" : " AND ";
|
||||
selection += String.format(TracksColumns.CATEGORY + " IN (%s)", TextUtils.join(",", Collections.nCopies(categories.size(), "?")));
|
||||
selection += String.format(TracksColumns.ACTIVITY_TYPE + " IN (%s)", TextUtils.join(",", Collections.nCopies(categories.size(), "?")));
|
||||
}
|
||||
if (from != null && to != null) {
|
||||
selection += selection.isEmpty() ? "" : " AND ";
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package de.dennisguse.opentracks.data.models;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
public enum ActivityType {
|
||||
AIRPLANE("AIRPLANE", R.drawable.ic_activity_flight_24dp, true, R.string.activity_type_airplane, R.string.activity_type_commercial_airplane, R.string.activity_type_rc_airplane),
|
||||
BIKE("BIKE", R.drawable.ic_activity_bike_24dp, true, R.string.activity_type_biking, R.string.activity_type_cycling, R.string.activity_type_dirt_bike, R.string.activity_type_road_biking, R.string.activity_type_track_cycling),
|
||||
MOUNTAIN_BIKE("MOUNTAIN_BIKE", R.drawable.ic_activity_mtb_24dp, true, R.string.activity_type_mountain_biking),
|
||||
MOTOR_BIKE("MOTOR_BIKE", R.drawable.ic_activity_motorbike_24dp, true, R.string.activity_type_motor_bike),
|
||||
KAYAK("KAYAK", R.drawable.ic_activity_kayaking_24dp, true, R.string.activity_type_kayaking),
|
||||
BOAT("BOAT", R.drawable.ic_activity_boat_24dp, true, R.string.activity_type_boat, R.string.activity_type_ferry, R.string.activity_type_motor_boating, R.string.activity_type_rc_boat),
|
||||
SAILING("SAILING", R.drawable.ic_activity_sailing_24dp, true, R.string.activity_type_sailing),
|
||||
DRIVE("DRIVE", R.drawable.ic_activity_drive_24dp, true, R.string.activity_type_atv, R.string.activity_type_driving, R.string.activity_type_driving_bus, R.string.activity_type_driving_car),
|
||||
RUN("RUN", R.drawable.ic_activity_run_24dp, false, R.string.activity_type_running, R.string.activity_type_street_running, R.string.activity_type_track_running, R.string.activity_type_trail_running),
|
||||
SKI("SKI", R.drawable.ic_activity_skiing_24dp, true, R.string.activity_type_cross_country_skiing, R.string.activity_type_skiing),
|
||||
SNOW_BOARDING("SNOW_BOARDING", R.drawable.ic_activity_snowboarding_24dp, true, R.string.activity_type_snow_boarding),
|
||||
UNKNOWN("UNKNOWN", R.drawable.ic_logo_24dp, true, R.string.activity_type_unknown),
|
||||
WALK("WALK", R.drawable.ic_activity_walk_24dp, false, R.string.activity_type_hiking, R.string.activity_type_off_trail_hiking, R.string.activity_type_speed_walking, R.string.activity_type_trail_hiking, R.string.activity_type_walking),
|
||||
ESCOOTER("ESCOOTER", R.drawable.ic_activity_escooter_24dp, true, R.string.activity_type_escooter),
|
||||
INLINE_SKATING("INLINES_SKATING", R.drawable.ic_activity_inline_skating_24dp, true, R.string.activity_type_inline_skating),
|
||||
SKATE_BOARDING("SKATE_BOARDING", R.drawable.ic_activity_skateboarding_24dp, true, R.string.activity_type_skate_boarding),
|
||||
CLIMBING("CLIMBING", R.drawable.ic_activity_climbing_24dp, false, R.string.activity_type_climbing),
|
||||
SWIMMING("SWIMMING", R.drawable.ic_activity_swimming_24dp, false, R.string.activity_type_swimming),
|
||||
SWIMMING_OPEN("SWIMMING_OPEN", R.drawable.ic_activity_swimming_open_24dp, false, R.string.activity_type_swimming_open),
|
||||
WORKOUT("WORKOUT", R.drawable.ic_activity_workout_24dp, false, R.string.activity_type_workout);
|
||||
|
||||
final String id;
|
||||
final int iconId;
|
||||
final boolean showSpeedPreferred;
|
||||
final int[] localizedStringIds;
|
||||
|
||||
//isSpeed?
|
||||
|
||||
ActivityType(String id, int iconId, boolean showSpeedPreferred, int... localizedStringIds) {
|
||||
this.id = id;
|
||||
this.iconId = iconId;
|
||||
this.showSpeedPreferred = showSpeedPreferred;
|
||||
this.localizedStringIds = localizedStringIds;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getIconId() {
|
||||
return iconId;
|
||||
}
|
||||
|
||||
public boolean isShowSpeedPreferred() {
|
||||
return showSpeedPreferred;
|
||||
}
|
||||
|
||||
public int[] getLocalizedStringIds() {
|
||||
return localizedStringIds;
|
||||
}
|
||||
|
||||
public int getFirstLocalizedStringId() {
|
||||
return localizedStringIds[0];
|
||||
}
|
||||
|
||||
public static List<String> getLocalizedStrings(Context context) {
|
||||
List<String> result = new ArrayList<>();
|
||||
for (ActivityType activityType : values()) {
|
||||
List<String> strings = Arrays.stream(activityType.localizedStringIds)
|
||||
.mapToObj(context::getString)
|
||||
.collect(Collectors.toList());
|
||||
result.addAll(strings);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -15,9 +15,7 @@ public abstract class Altitude {
|
||||
public double toM() {
|
||||
return altitude_m;
|
||||
}
|
||||
|
||||
public abstract Altitude replace(double altitude_m);
|
||||
|
||||
|
||||
public abstract int getLabelId();
|
||||
|
||||
public static class WGS84 extends Altitude {
|
||||
@@ -31,12 +29,7 @@ public abstract class Altitude {
|
||||
return R.string.wgs84;
|
||||
}
|
||||
|
||||
public static Altitude of(double altitude_m) {
|
||||
return new WGS84(altitude_m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Altitude replace(double altitude_m) {
|
||||
public static WGS84 of(double altitude_m) {
|
||||
return new WGS84(altitude_m);
|
||||
}
|
||||
}
|
||||
@@ -52,12 +45,7 @@ public abstract class Altitude {
|
||||
return R.string.egm2008;
|
||||
}
|
||||
|
||||
public static Altitude of(double altitude_m) {
|
||||
return new EGM2008(altitude_m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Altitude replace(double altitude_m) {
|
||||
public static EGM2008 of(double altitude_m) {
|
||||
return new EGM2008(altitude_m);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package de.dennisguse.opentracks.data.models;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public record AtmosphericPressure(float value) {
|
||||
|
||||
public static AtmosphericPressure ofPA(float value_Pa) {
|
||||
return new AtmosphericPressure(value_Pa * 100);
|
||||
}
|
||||
|
||||
public static AtmosphericPressure ofHPA(float value_hPa) {
|
||||
return new AtmosphericPressure(value_hPa);
|
||||
}
|
||||
|
||||
public float getPA() {
|
||||
return value * 100;
|
||||
}
|
||||
|
||||
public float getHPA() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package de.dennisguse.opentracks.data.models;
|
||||
|
||||
public record BatteryLevel(int percentage) {
|
||||
|
||||
public static BatteryLevel of(int percentage) {
|
||||
return new BatteryLevel(percentage);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import androidx.annotation.NonNull;
|
||||
import java.time.Duration;
|
||||
import java.util.Objects;
|
||||
|
||||
public class Cadence {
|
||||
public record Cadence(float value_rpm) {
|
||||
|
||||
public static Cadence of(float value, Duration duration) {
|
||||
if (duration.isZero()) {
|
||||
@@ -23,34 +23,7 @@ public class Cadence {
|
||||
return of(0.0f);
|
||||
}
|
||||
|
||||
private final float value_rpm;
|
||||
|
||||
private Cadence(float value) {
|
||||
this.value_rpm = value;
|
||||
}
|
||||
|
||||
public float getRPM() {
|
||||
return value_rpm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Cadence cadence = (Cadence) o;
|
||||
return Float.compare(cadence.value_rpm, value_rpm) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(value_rpm);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Cadence{" +
|
||||
"value=" + value_rpm + " rpm" +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,16 +3,13 @@ package de.dennisguse.opentracks.data.models;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||
|
||||
public class Distance {
|
||||
public record Distance(double distance_m) {
|
||||
|
||||
public static Distance of(double distance_m) {
|
||||
return new Distance(distance_m);
|
||||
}
|
||||
|
||||
public static Distance of(Double distance_m) {
|
||||
if (distance_m == null) {
|
||||
return Distance.of(Double.NaN);
|
||||
@@ -20,11 +17,9 @@ public class Distance {
|
||||
return Distance.of((double) distance_m);
|
||||
}
|
||||
}
|
||||
|
||||
public static Distance of(String distance_m) {
|
||||
return of(Float.parseFloat(distance_m));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Distance ofOrNull(Double distance_m) {
|
||||
if (distance_m == null) {
|
||||
@@ -58,22 +53,11 @@ public class Distance {
|
||||
}
|
||||
|
||||
public static Distance one(UnitSystem unitSystem) {
|
||||
switch (unitSystem) {
|
||||
case METRIC:
|
||||
return Distance.ofKilometer(1);
|
||||
case IMPERIAL:
|
||||
return Distance.ofMile(1);
|
||||
case NAUTICAL_IMPERIAL:
|
||||
return Distance.ofNauticalMile(1);
|
||||
default:
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
private final double distance_m;
|
||||
|
||||
private Distance(double distance_m) {
|
||||
this.distance_m = distance_m;
|
||||
return switch (unitSystem) {
|
||||
case METRIC -> Distance.ofKilometer(1);
|
||||
case IMPERIAL_FEET, IMPERIAL_METER -> Distance.ofMile(1);
|
||||
case NAUTICAL_IMPERIAL -> Distance.ofNauticalMile(1);
|
||||
};
|
||||
}
|
||||
|
||||
public Distance plus(@NonNull Distance distance) {
|
||||
@@ -133,49 +117,18 @@ public class Distance {
|
||||
}
|
||||
|
||||
public double toKM_Miles(UnitSystem unitSystem) {
|
||||
switch (unitSystem) {
|
||||
case METRIC:
|
||||
return toKM();
|
||||
case IMPERIAL:
|
||||
return toMI();
|
||||
case NAUTICAL_IMPERIAL:
|
||||
return toNauticalMiles();
|
||||
default:
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
return switch (unitSystem) {
|
||||
case METRIC -> toKM();
|
||||
case IMPERIAL_FEET, IMPERIAL_METER -> toMI();
|
||||
case NAUTICAL_IMPERIAL -> toNauticalMiles();
|
||||
};
|
||||
}
|
||||
|
||||
public double toM_FT(UnitSystem unitSystem) {
|
||||
switch (unitSystem) {
|
||||
case METRIC:
|
||||
return toM();
|
||||
case NAUTICAL_IMPERIAL:
|
||||
case IMPERIAL:
|
||||
return toFT();
|
||||
default:
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Distance distance = (Distance) o;
|
||||
return Double.compare(distance.distance_m, distance_m) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(distance_m);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Distance{" +
|
||||
"distance_m=" + distance_m +
|
||||
'}';
|
||||
return switch (unitSystem) {
|
||||
case METRIC, IMPERIAL_METER -> toM();
|
||||
case NAUTICAL_IMPERIAL, IMPERIAL_FEET -> toFT();
|
||||
};
|
||||
}
|
||||
|
||||
// multiplication factors for conversion
|
||||
|
||||
@@ -46,38 +46,44 @@ public class DistanceFormatter {
|
||||
public Pair<String, String> getDistanceParts(Distance distance) {
|
||||
if (distance.isInvalid()) {
|
||||
String valueUnknown = resources.getString(R.string.value_unknown);
|
||||
switch (unitSystem) {
|
||||
case METRIC:
|
||||
return new Pair<>(valueUnknown, resources.getString(R.string.unit_meter));
|
||||
case IMPERIAL:
|
||||
case NAUTICAL_IMPERIAL:
|
||||
return new Pair<>(valueUnknown, resources.getString(R.string.unit_feet));
|
||||
default:
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
return switch (unitSystem) {
|
||||
case METRIC, IMPERIAL_METER ->
|
||||
new Pair<>(valueUnknown, resources.getString(R.string.unit_meter));
|
||||
case IMPERIAL_FEET, NAUTICAL_IMPERIAL ->
|
||||
new Pair<>(valueUnknown, resources.getString(R.string.unit_feet));
|
||||
};
|
||||
}
|
||||
|
||||
switch (unitSystem) {
|
||||
case METRIC:
|
||||
case METRIC -> {
|
||||
if (distance.greaterThan(Distance.ofKilometer(threshold))) {
|
||||
return new Pair<>(StringUtils.formatDecimal(distance.toKM(), decimalCount), resources.getString(R.string.unit_kilometer));
|
||||
} else {
|
||||
return new Pair<>(StringUtils.formatDecimal(distance.toM(), decimalCount), resources.getString(R.string.unit_meter));
|
||||
}
|
||||
case IMPERIAL:
|
||||
}
|
||||
case IMPERIAL_FEET -> {
|
||||
if (distance.greaterThan(Distance.ofMile(threshold))) {
|
||||
return new Pair<>(StringUtils.formatDecimal(distance.toMI(), decimalCount), resources.getString(R.string.unit_mile));
|
||||
} else {
|
||||
return new Pair<>(StringUtils.formatDecimal(distance.toFT(), decimalCount), resources.getString(R.string.unit_feet));
|
||||
}
|
||||
case NAUTICAL_IMPERIAL:
|
||||
}
|
||||
case IMPERIAL_METER -> {
|
||||
if (distance.greaterThan(Distance.ofMile(threshold))) {
|
||||
return new Pair<>(StringUtils.formatDecimal(distance.toMI(), decimalCount), resources.getString(R.string.unit_mile));
|
||||
} else {
|
||||
return new Pair<>(StringUtils.formatDecimal(distance.toM(), decimalCount), resources.getString(R.string.unit_meter));
|
||||
}
|
||||
}
|
||||
case NAUTICAL_IMPERIAL -> {
|
||||
if (distance.greaterThan(Distance.ofNauticalMile(threshold))) {
|
||||
return new Pair<>(StringUtils.formatDecimal(distance.toNauticalMiles(), decimalCount), resources.getString(R.string.unit_nautical_mile));
|
||||
} else {
|
||||
return new Pair<>(StringUtils.formatDecimal(distance.toFT(), decimalCount), resources.getString(R.string.unit_feet));
|
||||
}
|
||||
default:
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
default -> throw new RuntimeException("Not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,40 +4,13 @@ import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class HeartRate {
|
||||
public record HeartRate(float value) {
|
||||
|
||||
public static HeartRate of(float value) {
|
||||
return new HeartRate(value);
|
||||
}
|
||||
|
||||
private final float value;
|
||||
|
||||
private HeartRate(float value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public float getBPM() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
HeartRate heartRate = (HeartRate) o;
|
||||
return Float.compare(heartRate.value, value) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(value);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HeartRate{" +
|
||||
"value=" + value + " bpm" +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package de.dennisguse.opentracks.data.models;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.ui.util.ThemeUtils;
|
||||
|
||||
public class HeartRateZones {
|
||||
|
||||
private final HeartRate max;
|
||||
|
||||
public HeartRateZones(@NonNull HeartRate max) {
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
public int getTextColorForZone(Context context, HeartRate current) {
|
||||
if (current != null) {
|
||||
if (current.getBPM() >= max.getBPM() * 0.9) {
|
||||
return ContextCompat.getColor(context, R.color.heart_rate_zone_textcolor_5);
|
||||
}
|
||||
if (current.getBPM() >= max.getBPM() * 0.8) {
|
||||
return ContextCompat.getColor(context, R.color.heart_rate_zone_textcolor_4);
|
||||
}
|
||||
if (current.getBPM() >= max.getBPM() * 0.7) {
|
||||
return ContextCompat.getColor(context, R.color.heart_rate_zone_textcolor_3);
|
||||
}
|
||||
if (current.getBPM() >= max.getBPM() * 0.6) {
|
||||
return ContextCompat.getColor(context, R.color.heart_rate_zone_textcolor_2);
|
||||
}
|
||||
}
|
||||
|
||||
// Zone 1
|
||||
return ThemeUtils.getTextColorPrimary(context);
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,6 @@ import androidx.annotation.Nullable;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Objects;
|
||||
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
|
||||
@@ -267,31 +266,7 @@ public final class Marker {
|
||||
return photoUrl != null && !"".equals(photoUrl);
|
||||
}
|
||||
|
||||
public static class Id implements Parcelable {
|
||||
|
||||
private final long id;
|
||||
|
||||
public Id(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
//TOOD Limit visibility to TrackRecordingService / ContentProvider
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Id id1 = (Id) o;
|
||||
return id == id1.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id);
|
||||
}
|
||||
public record Id(long id) implements Parcelable {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
@@ -309,7 +284,7 @@ public final class Marker {
|
||||
parcel.writeLong(id);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<Id> CREATOR = new Parcelable.Creator<>() {
|
||||
public static final Creator<Id> CREATOR = new Creator<>() {
|
||||
public Id createFromParcel(Parcel in) {
|
||||
return new Id(in.readLong());
|
||||
}
|
||||
|
||||
@@ -4,40 +4,13 @@ import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class Power {
|
||||
public record Power(float value) {
|
||||
|
||||
public static Power of(float value) {
|
||||
return new Power(value);
|
||||
}
|
||||
|
||||
private final float value;
|
||||
|
||||
private Power(float value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public float getW() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Power power = (Power) o;
|
||||
return Float.compare(power.value, value) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(value);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Power{" +
|
||||
"value=" + value + " W" +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
package de.dennisguse.opentracks.data.models;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Objects;
|
||||
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||
|
||||
public class Speed {
|
||||
public record Speed(double speed_mps) {
|
||||
|
||||
public static Speed of(Distance distance, Duration duration) {
|
||||
if (duration.isZero()) {
|
||||
@@ -47,12 +44,6 @@ public class Speed {
|
||||
return Speed.of(Math.abs(speed1.speed_mps - speed2.speed_mps));
|
||||
}
|
||||
|
||||
private final double speed_mps;
|
||||
|
||||
private Speed(double speed_mps) {
|
||||
this.speed_mps = speed_mps;
|
||||
}
|
||||
|
||||
public Speed mul(double factor) {
|
||||
return new Speed(factor * speed_mps);
|
||||
}
|
||||
@@ -115,36 +106,10 @@ public class Speed {
|
||||
}
|
||||
|
||||
public double to(UnitSystem unitSystem) {
|
||||
switch (unitSystem) {
|
||||
case METRIC:
|
||||
return toKMH();
|
||||
case IMPERIAL:
|
||||
return toMPH();
|
||||
case NAUTICAL_IMPERIAL:
|
||||
return toKnots();
|
||||
default:
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Speed speed = (Speed) o;
|
||||
return Double.compare(speed.speed_mps, speed_mps) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(speed_mps);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Speed{" +
|
||||
"speed_mps=" + speed_mps +
|
||||
'}';
|
||||
return switch (unitSystem) {
|
||||
case METRIC -> toKMH();
|
||||
case IMPERIAL_FEET, IMPERIAL_METER -> toMPH();
|
||||
case NAUTICAL_IMPERIAL -> toKnots();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,20 +39,14 @@ public class SpeedFormatter {
|
||||
* @return the formatted speed (or null) and it's unit as {@link Pair}
|
||||
*/
|
||||
public Pair<String, String> getSpeedParts(Speed speed) {
|
||||
int unitId;
|
||||
switch (unitSystem) {
|
||||
case METRIC:
|
||||
unitId = reportSpeedOrPace ? R.string.unit_kilometer_per_hour : R.string.unit_minute_per_kilometer;
|
||||
break;
|
||||
case IMPERIAL:
|
||||
unitId = reportSpeedOrPace ? R.string.unit_mile_per_hour : R.string.unit_minute_per_mile;
|
||||
break;
|
||||
case NAUTICAL_IMPERIAL:
|
||||
unitId = reportSpeedOrPace ? R.string.unit_knots : R.string.unit_minute_per_nautical_mile;
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Not implemented");
|
||||
}
|
||||
int unitId = switch (unitSystem) {
|
||||
case METRIC ->
|
||||
reportSpeedOrPace ? R.string.unit_kilometer_per_hour : R.string.unit_minute_per_kilometer;
|
||||
case IMPERIAL_FEET, IMPERIAL_METER ->
|
||||
reportSpeedOrPace ? R.string.unit_mile_per_hour : R.string.unit_minute_per_mile;
|
||||
case NAUTICAL_IMPERIAL ->
|
||||
reportSpeedOrPace ? R.string.unit_knots : R.string.unit_minute_per_nautical_mile;
|
||||
};
|
||||
|
||||
String unitString = resources.getString(unitId);
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ public class Track {
|
||||
|
||||
private String name = "";
|
||||
private String description = "";
|
||||
private String category = "";
|
||||
private String activityType = "";
|
||||
|
||||
private String icon = "";
|
||||
|
||||
@@ -97,12 +97,12 @@ public class Track {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
public String getActivityType() {
|
||||
return activityType;
|
||||
}
|
||||
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
public void setActivityType(String activityType) {
|
||||
this.activityType = activityType;
|
||||
}
|
||||
|
||||
public String getIcon() {
|
||||
|
||||
@@ -385,10 +385,6 @@ public class TrackPoint {
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean hasSensorData() {
|
||||
return hasHeartRate() || hasCadence() || hasPower();
|
||||
}
|
||||
|
||||
public boolean hasHeartRate() {
|
||||
return heartRate != null;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public interface TracksColumns extends BaseColumns {
|
||||
String UUID = "uuid"; // identifier to make tracks globally unique (prevent re-import)
|
||||
String NAME = "name"; // track name
|
||||
String DESCRIPTION = "description"; // track description
|
||||
String CATEGORY = "category"; // track activity type
|
||||
String ACTIVITY_TYPE = "category"; // track activity type
|
||||
String STARTTIME = "starttime"; // track start time
|
||||
String STARTTIME_OFFSET = "starttime_offset"; // in plus/minus in seconds
|
||||
String STOPTIME = "stoptime"; // track stop time
|
||||
@@ -63,7 +63,7 @@ public interface TracksColumns extends BaseColumns {
|
||||
+ _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||
+ NAME + " TEXT, "
|
||||
+ DESCRIPTION + " TEXT, "
|
||||
+ CATEGORY + " TEXT, "
|
||||
+ ACTIVITY_TYPE + " TEXT, "
|
||||
+ STARTTIME + " INTEGER, "
|
||||
+ STOPTIME + " INTEGER, "
|
||||
+ NUMPOINTS + " INTEGER, "
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ class ChooseActivityTypeImageAdapter extends BaseAdapter {
|
||||
}
|
||||
|
||||
if (position == selected) {
|
||||
imageView.setBackgroundColor(ContextCompat.getColor(parent.getContext(), R.color.opentracks_secondary_color));
|
||||
imageView.setBackgroundColor(ContextCompat.getColor(parent.getContext(), R.color.opentracks));
|
||||
} else {
|
||||
imageView.setBackgroundColor(Color.TRANSPARENT);
|
||||
}
|
||||
|
||||
@@ -31,11 +31,8 @@ import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.TrackRecordedActivity;
|
||||
import de.dennisguse.opentracks.adapters.StatisticsAdapter;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.models.DistanceFormatter;
|
||||
import de.dennisguse.opentracks.data.models.SpeedFormatter;
|
||||
@@ -47,8 +44,6 @@ import de.dennisguse.opentracks.stats.SensorStatistics;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
import de.dennisguse.opentracks.viewmodels.StatisticData;
|
||||
import de.dennisguse.opentracks.viewmodels.StatisticDataBuilder;
|
||||
|
||||
/**
|
||||
* A fragment to display track statistics to the user for a recorded {@link Track}.
|
||||
@@ -72,7 +67,6 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
}
|
||||
|
||||
private SensorStatistics sensorStatistics;
|
||||
private StatisticsAdapter.WithRecordedLayout sensorsAdapter;
|
||||
|
||||
private Track.Id trackId;
|
||||
@Nullable // Lazily loaded.
|
||||
@@ -95,7 +89,7 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
|
||||
if (PreferencesUtils.isKey(R.string.stats_rate_key, key) && track != null) {
|
||||
updateUInecessary = true;
|
||||
preferenceReportSpeed = PreferencesUtils.isReportSpeed(track.getCategory());
|
||||
preferenceReportSpeed = PreferencesUtils.isReportSpeed(track.getActivityType());
|
||||
}
|
||||
|
||||
if (key != null && updateUInecessary && isResumed()) {
|
||||
@@ -113,8 +107,6 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
|
||||
trackId = getArguments().getParcelable(TRACK_ID_KEY);
|
||||
contentProviderUtils = new ContentProviderUtils(getContext());
|
||||
|
||||
sensorsAdapter = new StatisticsAdapter.WithRecordedLayout(getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -123,7 +115,7 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
|
||||
RecyclerView sensorsRecyclerView = viewBinding.statsSensorsRecyclerView;
|
||||
sensorsRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2));
|
||||
sensorsRecyclerView.setAdapter(sensorsAdapter);
|
||||
// sensorsRecyclerView.setAdapter(sensorsAdapter);
|
||||
|
||||
return viewBinding.getRoot();
|
||||
}
|
||||
@@ -163,7 +155,7 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
|
||||
sensorStatistics = contentProviderUtils.getSensorStats(trackId);
|
||||
|
||||
boolean prefsChanged = this.track == null || (!this.track.getCategory().equals(track.getCategory()));
|
||||
boolean prefsChanged = this.track == null || (!this.track.getActivityType().equals(track.getActivityType()));
|
||||
this.track = track;
|
||||
if (prefsChanged) {
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, getString(R.string.stats_rate_key));
|
||||
@@ -199,7 +191,7 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
|
||||
// Set activity type
|
||||
{
|
||||
String trackIconValue = TrackIconUtils.getIconValue(getContext(), track.getCategory());
|
||||
String trackIconValue = TrackIconUtils.getIconValue(getContext(), track.getActivityType());
|
||||
viewBinding.statsActivityTypeIcon.setImageDrawable(ContextCompat.getDrawable(getContext(), TrackIconUtils.getIconDrawable(trackIconValue)));
|
||||
}
|
||||
|
||||
@@ -261,10 +253,6 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
if (sensorStatistics == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<StatisticData> sensorDataList = StatisticDataBuilder.fromSensorStatistics(getContext(), sensorStatistics);
|
||||
if (sensorDataList.size() > 0) {
|
||||
sensorsAdapter.swapData(sensorDataList);
|
||||
}
|
||||
//TODO
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package de.dennisguse.opentracks.fragments;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -9,16 +10,14 @@ import android.view.ViewGroup;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.gridlayout.widget.GridLayout;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.adapters.StatisticsAdapter;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.databinding.StatisticsRecordingBinding;
|
||||
import de.dennisguse.opentracks.services.RecordingData;
|
||||
@@ -26,9 +25,10 @@ import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||
import de.dennisguse.opentracks.ui.customRecordingLayout.Layout;
|
||||
import de.dennisguse.opentracks.viewmodels.StatisticData;
|
||||
import de.dennisguse.opentracks.viewmodels.StatisticsDataModel;
|
||||
import de.dennisguse.opentracks.ui.customRecordingLayout.DataField;
|
||||
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayout;
|
||||
import de.dennisguse.opentracks.viewmodels.Mapping;
|
||||
import de.dennisguse.opentracks.viewmodels.StatisticViewHolder;
|
||||
|
||||
/**
|
||||
* A fragment to display track statistics to the user for a currently recording {@link Track}.
|
||||
@@ -48,32 +48,22 @@ public class StatisticsRecordingFragment extends Fragment {
|
||||
|
||||
private RecordingData recordingData = TrackRecordingService.NOT_RECORDING;
|
||||
|
||||
private Layout layout;
|
||||
private final List<StatisticViewHolder<?>> viewHolders = new LinkedList<>();
|
||||
|
||||
private RecordingLayout recordingLayout;
|
||||
|
||||
private StatisticsRecordingBinding viewBinding;
|
||||
private StatisticsAdapter statisticsAdapter;
|
||||
private GridLayoutManager gridLayoutManager;
|
||||
private StatisticsDataModel viewModel;
|
||||
private LiveData<List<StatisticData>> statisticsLiveData;
|
||||
|
||||
private UnitSystem unitSystem = UnitSystem.defaultUnitSystem();
|
||||
|
||||
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
|
||||
boolean updateUInecessary = false;
|
||||
|
||||
if (PreferencesUtils.isKey(R.string.stats_units_key, key)) {
|
||||
updateUInecessary = true;
|
||||
unitSystem = PreferencesUtils.getUnitSystem();
|
||||
updateDataOnUI();
|
||||
}
|
||||
|
||||
if (PreferencesUtils.isKey(R.string.stats_custom_layouts_key, key) || PreferencesUtils.isKey(R.string.stats_custom_layout_selected_layout_key, key)) {
|
||||
updateUInecessary = true;
|
||||
layout = PreferencesUtils.getCustomLayout();
|
||||
gridLayoutManager.setSpanCount(layout.getColumnsPerRow());
|
||||
}
|
||||
|
||||
if (key != null && updateUInecessary && isResumed()) {
|
||||
getActivity().runOnUiThread(this::updateUI);
|
||||
onLayoutChanged(PreferencesUtils.getCustomLayout());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -84,30 +74,11 @@ public class StatisticsRecordingFragment extends Fragment {
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback);
|
||||
|
||||
statisticsAdapter = new StatisticsAdapter(getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
viewBinding = StatisticsRecordingBinding.inflate(inflater, container, false);
|
||||
|
||||
RecyclerView recyclerView = viewBinding.statsRecyclerView;
|
||||
recyclerView.addItemDecoration(new DividerItemDecoration(getContext(), RecyclerView.VERTICAL));
|
||||
layout = PreferencesUtils.getCustomLayout();
|
||||
gridLayoutManager = new GridLayoutManager(getContext(), layout.getColumnsPerRow());
|
||||
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
|
||||
@Override
|
||||
public int getSpanSize(int position) {
|
||||
if (statisticsAdapter.isItemWide(position)) {
|
||||
return layout.getColumnsPerRow();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
recyclerView.setLayoutManager(gridLayoutManager);
|
||||
recyclerView.setAdapter(statisticsAdapter);
|
||||
|
||||
return viewBinding.getRoot();
|
||||
}
|
||||
|
||||
@@ -117,10 +88,6 @@ public class StatisticsRecordingFragment extends Fragment {
|
||||
|
||||
PreferencesUtils.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
|
||||
viewModel = new ViewModelProvider(getActivity()).get(StatisticsDataModel.class);
|
||||
statisticsLiveData = viewModel.getStatsData();
|
||||
statisticsLiveData.observe(getActivity(), statsDataList -> statisticsAdapter.swapData(statsDataList));
|
||||
|
||||
trackRecordingServiceConnection.startConnection(getContext());
|
||||
}
|
||||
|
||||
@@ -139,6 +106,7 @@ public class StatisticsRecordingFragment extends Fragment {
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
viewHolders.clear();
|
||||
viewBinding = null;
|
||||
}
|
||||
|
||||
@@ -146,17 +114,54 @@ public class StatisticsRecordingFragment extends Fragment {
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
trackRecordingServiceConnection = null;
|
||||
viewModel = null;
|
||||
|
||||
if (statisticsLiveData != null) {
|
||||
statisticsLiveData.removeObservers(getActivity());
|
||||
}
|
||||
statisticsLiveData = null;
|
||||
}
|
||||
|
||||
private void updateUI() {
|
||||
if (isResumed()) {
|
||||
viewModel.update(recordingData, layout, unitSystem);
|
||||
private void onLayoutChanged(@NonNull RecordingLayout newRecordingLayout) {
|
||||
if (newRecordingLayout.equals(recordingLayout)) {
|
||||
return;
|
||||
}
|
||||
recordingLayout = newRecordingLayout;
|
||||
|
||||
viewBinding.statsLayout.removeAllViews(); //Let's start from scratch
|
||||
viewBinding.statsLayout.setColumnCount(recordingLayout.getColumnsPerRow());
|
||||
viewHolders.clear();
|
||||
|
||||
Map<String, Callable<StatisticViewHolder<?>>> m = Mapping.create(getContext());
|
||||
|
||||
int rowIndex = 0;
|
||||
int columnIndex = 0;
|
||||
for (DataField dataField : recordingLayout.toRecordingLayout(true).getFields()) {
|
||||
GridLayout.LayoutParams param = new GridLayout.LayoutParams();
|
||||
param.setGravity(Gravity.FILL_HORIZONTAL);
|
||||
param.width = 0;
|
||||
|
||||
if (dataField.isWide()) {
|
||||
rowIndex++;
|
||||
param.columnSpec = GridLayout.spec(0, recordingLayout.getColumnsPerRow(), 1);
|
||||
param.rowSpec = GridLayout.spec(rowIndex, 1, 1);
|
||||
columnIndex = 0;
|
||||
rowIndex++;
|
||||
} else {
|
||||
if (columnIndex >= recordingLayout.getColumnsPerRow()) {
|
||||
columnIndex = 0;
|
||||
rowIndex++;
|
||||
}
|
||||
param.columnSpec = GridLayout.spec(columnIndex, 1, 1);
|
||||
param.rowSpec = GridLayout.spec(rowIndex, 1, 1);
|
||||
|
||||
columnIndex++;
|
||||
}
|
||||
|
||||
try {
|
||||
StatisticViewHolder<?> viewHolder = m.get(dataField.getKey()).call();
|
||||
viewHolder.initialize(getContext(), getLayoutInflater());
|
||||
viewHolder.configureUI(dataField);
|
||||
viewHolders.add(viewHolder);
|
||||
|
||||
viewBinding.statsLayout.addView(viewHolder.getView(), param);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Could not add " + dataField.getKey(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,6 +174,12 @@ public class StatisticsRecordingFragment extends Fragment {
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, getString(R.string.stats_rate_key));
|
||||
}
|
||||
|
||||
updateUI();
|
||||
updateDataOnUI();
|
||||
}
|
||||
|
||||
private void updateDataOnUI() {
|
||||
if (isResumed()) {
|
||||
viewHolders.forEach(i -> i.onChanged(unitSystem, recordingData));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user