forked from upstream-mirrors/OpenTracks
Compare commits
131 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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
|
- uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: 'adopt'
|
distribution: 'adopt'
|
||||||
java-version: '11'
|
java-version: '17'
|
||||||
|
|
||||||
- name: Assemble APK
|
- name: Assemble APK
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|||||||
@@ -16,6 +16,6 @@ jobs:
|
|||||||
- uses: actions/setup-java@v3
|
- uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: 'zulu'
|
distribution: 'zulu'
|
||||||
java-version: '11'
|
java-version: '17'
|
||||||
|
|
||||||
- run: ./gradlew lint
|
- run: ./gradlew lint
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
_OpenTracks_ is a sport tracking application that completely respects your privacy.
|
_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>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -40,8 +40,10 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<a href="https://weblate.bubu1.eu/engage/OpenTracks/">
|
Translations are hosted on <a href="https://hosted.weblate.org/engage/opentracks/">
|
||||||
<img src="https://weblate.bubu1.eu/widgets/OpenTracks/-/opentracks/horizontal-auto.svg" alt="Translation status" />
|
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>
|
</a>
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
@@ -150,7 +152,7 @@ The developer documentation is in [README_API.md](README_API.md).
|
|||||||
## Project history
|
## Project history
|
||||||
|
|
||||||
_OpenTracks_ is based upon Google _My Tracks app_ ([code](https://code.google.com/archive/p/mytracks/)).
|
_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.
|
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).
|
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).
|
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.
|
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).
|
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
|
## Google Play Store
|
||||||
Application id: de.dennisguse.opentracks.playstore
|
Application id: `de.dennisguse.opentracks.playstore`
|
||||||
|
|
||||||
Requirement: `gem install fastlane`
|
Requirement: `gem install fastlane`
|
||||||
|
|
||||||
@@ -35,4 +35,6 @@ Supported languages: https://support.google.com/googleplay/android-developer/tab
|
|||||||
|
|
||||||
## Translations (localization)
|
## 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)
|
||||||
|
.
|
||||||
+25
-18
@@ -6,7 +6,7 @@ buildscript {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:7.3.1'
|
classpath 'com.android.tools.build:gradle:8.1.0-beta02'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,20 +47,25 @@ android {
|
|||||||
compileOptions {
|
compileOptions {
|
||||||
coreLibraryDesugaringEnabled true
|
coreLibraryDesugaringEnabled true
|
||||||
|
|
||||||
sourceCompatibility JavaVersion.VERSION_11
|
sourceCompatibility JavaVersion.VERSION_17
|
||||||
targetCompatibility JavaVersion.VERSION_11
|
targetCompatibility JavaVersion.VERSION_17
|
||||||
}
|
}
|
||||||
|
|
||||||
compileSdk 33
|
compileSdk 33
|
||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding true
|
viewBinding true
|
||||||
|
buildConfig true
|
||||||
|
}
|
||||||
|
|
||||||
|
androidResources {
|
||||||
|
generateLocaleConfig = true
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "de.dennisguse.opentracks"
|
applicationId "de.dennisguse.opentracks"
|
||||||
versionCode 5080
|
versionCode 5211
|
||||||
versionName "v4.1.2"
|
versionName "v4.3.5"
|
||||||
|
|
||||||
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
||||||
|
|
||||||
@@ -106,11 +111,11 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
applicationVariants.all { variant ->
|
applicationVariants.configureEach { variant ->
|
||||||
variant.resValue "string", "applicationId", variant.applicationId
|
variant.resValue "string", "applicationId", variant.applicationId
|
||||||
|
|
||||||
if (variant.buildType.name == 'nightly') {
|
if (variant.buildType.name == 'nightly') {
|
||||||
variant.outputs.all {
|
variant.outputs.configureEach {
|
||||||
setVersionCodeOverride(getVersionCode())
|
setVersionCodeOverride(getVersionCode())
|
||||||
setVersionNameOverride(getVersionName())
|
setVersionNameOverride(getVersionName())
|
||||||
outputFileName = "${applicationId}_${variant.versionCode}.apk"
|
outputFileName = "${applicationId}_${variant.versionCode}.apk"
|
||||||
@@ -120,21 +125,23 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
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.preference:preference:1.2.0'
|
||||||
implementation 'androidx.documentfile:documentfile:1.0.1'
|
implementation 'androidx.documentfile:documentfile:1.0.1'
|
||||||
implementation 'com.google.android.material:material:1.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.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:core:1.5.0'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||||
androidTestImplementation 'androidx.test:rules:1.4.0'
|
androidTestImplementation 'androidx.test:rules:1.5.0'
|
||||||
androidTestImplementation 'androidx.test:runner:1.4.0'
|
androidTestImplementation 'androidx.test:runner:1.5.2'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||||
androidTestImplementation 'org.mockito:mockito-android:4.8.1'
|
androidTestImplementation 'org.mockito:mockito-android:5.3.1'
|
||||||
|
|
||||||
androidTestUtil 'androidx.test:orchestrator:1.4.1'
|
androidTestUtil 'androidx.test:orchestrator:1.4.2'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,5 +7,5 @@ Changes:
|
|||||||
Bugfix:
|
Bugfix:
|
||||||
- Speech announcements use MAY_DUCK (again)
|
- Speech announcements use MAY_DUCK (again)
|
||||||
- ChartView shows markers (again)
|
- ChartView shows markers (again)
|
||||||
- Fixed landscape layout of TrackListActivity
|
- Fixed landscape recordingLayout of TrackListActivity
|
||||||
- Request legacy file API behavior (necessary for Android 10)
|
- 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)
|
||||||
|
|
||||||
@@ -2,13 +2,13 @@ v3.19.0: OpenTracks
|
|||||||
|
|
||||||
Wijzigingen:
|
Wijzigingen:
|
||||||
- Ondersteuning voor Running Speed en Cadence (RCS) sensoren
|
- 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
|
- 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:
|
Bugfix:
|
||||||
- Berekening van totale tijd mag geen pauze-tijd bevatten (vereist opnieuw importeren)
|
- Berekening totale tijd mag geen pauze-tijd bevatten (vereist opnieuw importeren)
|
||||||
- (opnieuw) Gebruik aangepaste overzichten indien aanwezig
|
- (opnieuw) Gebruik aanwezige aangepaste overzichten
|
||||||
|
|
||||||
Ontwikkelaar:
|
Ontwikkelaar:
|
||||||
- Upgrade naar SDK31 en AGP7.0.0 (vereist JDK11)
|
- Upgrade naar SDK31 en AGP7.0.0 (vereist JDK11)
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ Wijzigingen:
|
|||||||
- Gesproken meldingen van hartslag
|
- Gesproken meldingen van hartslag
|
||||||
|
|
||||||
Bugfix:
|
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
|
android.useAndroidX=true
|
||||||
org.gradle.jvmargs=-Xmx1024m -XX:MaxPermSize=256m
|
org.gradle.jvmargs=-Xmx1024m
|
||||||
|
|||||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
@@ -205,6 +205,12 @@ set -- \
|
|||||||
org.gradle.wrapper.GradleWrapperMain \
|
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.
|
# Use "xargs" to parse quoted args.
|
||||||
#
|
#
|
||||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
|||||||
Vendored
+8
-6
@@ -14,7 +14,7 @@
|
|||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%" == "" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
@rem Gradle startup script for Windows
|
@rem Gradle startup script for Windows
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
if "%OS%"=="Windows_NT" setlocal
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
set DIRNAME=%~dp0
|
set DIRNAME=%~dp0
|
||||||
if "%DIRNAME%" == "" set DIRNAME=.
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
|||||||
|
|
||||||
set JAVA_EXE=java.exe
|
set JAVA_EXE=java.exe
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if "%ERRORLEVEL%" == "0" goto execute
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
@@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
|||||||
|
|
||||||
:end
|
:end
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem End local scope for the variables with windows NT shell
|
||||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||||
|
|
||||||
:fail
|
:fail
|
||||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
rem the _cmd.exe /c_ return code!
|
rem the _cmd.exe /c_ return code!
|
||||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
exit /b 1
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
|
exit /b %EXIT_CODE%
|
||||||
|
|
||||||
:mainEnd
|
:mainEnd
|
||||||
if "%OS%"=="Windows_NT" endlocal
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|||||||
+12
-10
@@ -27,21 +27,23 @@ public class KmlTrackExporterTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void writeCloseSegment_only_write_sensordata_if_present() {
|
public void writeCloseSegment_only_write_sensordata_if_present() {
|
||||||
String expected = "<when>1970-01-01T00:00:00Z</when>\n" +
|
String expected = """
|
||||||
"<coord/>\n" +
|
<when>1970-01-01T00:00:00Z</when>
|
||||||
"<when>1970-01-01T01:00:00+01:00</when>\n" +
|
<coord/>
|
||||||
"<coord/>\n" +
|
<when>1970-01-01T01:00:00+01:00</when>
|
||||||
"<ExtendedData>\n" +
|
<coord/>
|
||||||
"<SchemaData schemaUrl=\"#schema\">\n" +
|
<ExtendedData>
|
||||||
"</SchemaData>\n" +
|
<SchemaData schemaUrl="#schema">
|
||||||
"</ExtendedData>\n" +
|
</SchemaData>
|
||||||
"</Track>\n";
|
</ExtendedData>
|
||||||
|
</Track>
|
||||||
|
""";
|
||||||
|
|
||||||
// given
|
// given
|
||||||
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0));
|
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0));
|
||||||
|
|
||||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
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.prepare(outputStream);
|
||||||
|
|
||||||
kmlTrackWriter.writeTrackPoint(ZoneOffset.UTC, trackPoint);
|
kmlTrackWriter.writeTrackPoint(ZoneOffset.UTC, trackPoint);
|
||||||
|
|||||||
+14
-13
@@ -12,7 +12,6 @@ import android.net.Uri;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
@@ -166,7 +165,8 @@ public class ExportImportTest {
|
|||||||
service.insertMarker("Marker 2", "Marker 2 category", "Marker 2 desc", null);
|
service.insertMarker("Marker 2", "Marker 2 category", "Marker 2 desc", null);
|
||||||
|
|
||||||
trackPointCreator.setClock("2020-02-02T02:02:18Z");
|
trackPointCreator.setClock("2020-02-02T02:02:18Z");
|
||||||
trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, null, trackPointCreator));
|
BluetoothRemoteSensorManager mockRemoteSensorManager = Mockito.mock(BluetoothRemoteSensorManager.class);
|
||||||
|
trackPointCreator.getSensorManager().setBluetoothSensorManager(mockRemoteSensorManager);
|
||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
trackPointCreator.setClock("2020-02-02T02:03:20Z");
|
trackPointCreator.setClock("2020-02-02T02:03:20Z");
|
||||||
@@ -179,7 +179,7 @@ public class ExportImportTest {
|
|||||||
sendLocation(trackPointCreator, "2020-02-02T02:03:23Z", 3, 16.001, 10, 27, 15, 10, 0);
|
sendLocation(trackPointCreator, "2020-02-02T02:03:23Z", 3, 16.001, 10, 27, 15, 10, 0);
|
||||||
|
|
||||||
trackPointCreator.setClock("2020-02-02T02:03:24Z");
|
trackPointCreator.setClock("2020-02-02T02:03:24Z");
|
||||||
trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, null, trackPointCreator));
|
trackPointCreator.getSensorManager().setBluetoothSensorManager(mockRemoteSensorManager);
|
||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
@@ -204,7 +204,7 @@ public class ExportImportTest {
|
|||||||
// given
|
// given
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
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
|
// when
|
||||||
// 1. export
|
// 1. export
|
||||||
@@ -287,7 +287,7 @@ public class ExportImportTest {
|
|||||||
editor.commit();
|
editor.commit();
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
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
|
// when
|
||||||
trackExporter.writeTrack(track, context.getContentResolver().openOutputStream(tmpFileUri));
|
trackExporter.writeTrack(track, context.getContentResolver().openOutputStream(tmpFileUri));
|
||||||
@@ -310,7 +310,7 @@ public class ExportImportTest {
|
|||||||
// given
|
// given
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
|
|
||||||
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context);
|
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context, contentProviderUtils);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
// 1. export
|
// 1. export
|
||||||
@@ -428,7 +428,7 @@ public class ExportImportTest {
|
|||||||
editor.commit();
|
editor.commit();
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
|
|
||||||
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context);
|
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context, contentProviderUtils);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
// 1. export
|
// 1. export
|
||||||
@@ -453,7 +453,7 @@ public class ExportImportTest {
|
|||||||
// given
|
// given
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
|
|
||||||
TrackExporter trackExporter = TrackFileFormat.CSV.createTrackExporter(context);
|
TrackExporter trackExporter = TrackFileFormat.CSV.createTrackExporter(context, contentProviderUtils);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
// 1. export
|
// 1. export
|
||||||
@@ -495,9 +495,10 @@ public class ExportImportTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void mockBLESensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power) {
|
private void mockBLESensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power) {
|
||||||
trackPointCreator.setRemoteSensorManager(new BluetoothRemoteSensorManager(context, null, trackPointCreator) {
|
BluetoothRemoteSensorManager remoteSensorManager = Mockito.mock(BluetoothRemoteSensorManager.class);
|
||||||
@Override
|
Mockito.when(remoteSensorManager.fill(Mockito.any())).thenAnswer(invocation -> {
|
||||||
public SensorDataSet fill(@NonNull TrackPoint trackPoint) {
|
TrackPoint trackPoint = invocation.getArgument(0);
|
||||||
|
|
||||||
SensorDataSet sensorDataSet = new SensorDataSet();
|
SensorDataSet sensorDataSet = new SensorDataSet();
|
||||||
sensorDataSet.set(new SensorDataCyclingPower("power", "power", Power.of(power)));
|
sensorDataSet.set(new SensorDataCyclingPower("power", "power", Power.of(power)));
|
||||||
sensorDataSet.set(new SensorDataHeartRate("heartRate", "heartRate", HeartRate.of(heartRate)));
|
sensorDataSet.set(new SensorDataHeartRate("heartRate", "heartRate", HeartRate.of(heartRate)));
|
||||||
@@ -519,13 +520,13 @@ public class ExportImportTest {
|
|||||||
|
|
||||||
sensorDataSet.fillTrackPoint(trackPoint);
|
sensorDataSet.fillTrackPoint(trackPoint);
|
||||||
return sensorDataSet;
|
return sensorDataSet;
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
trackPointCreator.getSensorManager().setBluetoothSensorManager(remoteSensorManager);
|
||||||
trackPointCreator.onChange(new SensorDataSet());
|
trackPointCreator.onChange(new SensorDataSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mockAltitudeChange(TrackPointCreator trackPointCreator, float altitudeGain) {
|
private void mockAltitudeChange(TrackPointCreator trackPointCreator, float altitudeGain) {
|
||||||
AltitudeSumManager altitudeSumManager = trackPointCreator.getAltitudeSumManager();
|
AltitudeSumManager altitudeSumManager = trackPointCreator.getSensorManager().getAltitudeSumManager();
|
||||||
altitudeSumManager.setAltitudeGain_m(altitudeGain);
|
altitudeSumManager.setAltitudeGain_m(altitudeGain);
|
||||||
altitudeSumManager.setAltitudeLoss_m(altitudeGain);
|
altitudeSumManager.setAltitudeLoss_m(altitudeGain);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -231,7 +231,7 @@ public class GPXTrackImporterTest {
|
|||||||
importTrackId = importer.importFile(inputStream).get(0);
|
importTrackId = importer.importFile(inputStream).get(0);
|
||||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||||
|
|
||||||
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context);
|
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context, contentProviderUtils);
|
||||||
trackExporter.writeTrack(importedTrack, outputStream);
|
trackExporter.writeTrack(importedTrack, outputStream);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import org.junit.Test;
|
|||||||
|
|
||||||
import de.dennisguse.opentracks.data.models.Cadence;
|
import de.dennisguse.opentracks.data.models.Cadence;
|
||||||
import de.dennisguse.opentracks.data.models.Distance;
|
import de.dennisguse.opentracks.data.models.Distance;
|
||||||
|
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||||
import de.dennisguse.opentracks.data.models.Speed;
|
import de.dennisguse.opentracks.data.models.Speed;
|
||||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDistanceSpeed;
|
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDistanceSpeed;
|
||||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower;
|
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower;
|
||||||
@@ -23,10 +24,10 @@ public class BluetoothUtilsTest {
|
|||||||
characteristic.setValue(new byte[]{0x02, 0x3C});
|
characteristic.setValue(new byte[]{0x02, 0x3C});
|
||||||
|
|
||||||
// when
|
// when
|
||||||
int heartRate = BluetoothUtils.parseHeartRate(characteristic);
|
HeartRate heartRate = BluetoothUtils.parseHeartRate(characteristic);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(60, heartRate);
|
assertEquals(HeartRate.of(60), heartRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -36,10 +37,10 @@ public class BluetoothUtilsTest {
|
|||||||
characteristic.setValue(new byte[]{0x01, 0x01, 0x01});
|
characteristic.setValue(new byte[]{0x01, 0x01, 0x01});
|
||||||
|
|
||||||
// when
|
// when
|
||||||
int heartRate = BluetoothUtils.parseHeartRate(characteristic);
|
HeartRate heartRate = BluetoothUtils.parseHeartRate(characteristic);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(257, heartRate);
|
assertEquals(HeartRate.of(257), heartRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -11,9 +11,40 @@ public class UintUtilsTest {
|
|||||||
assertEquals(0, UintUtils.diff(1, 1, UintUtils.UINT16_MAX));
|
assertEquals(0, UintUtils.diff(1, 1, UintUtils.UINT16_MAX));
|
||||||
assertEquals(1, UintUtils.diff(2, 1, UintUtils.UINT16_MAX));
|
assertEquals(1, UintUtils.diff(2, 1, UintUtils.UINT16_MAX));
|
||||||
assertEquals(3, UintUtils.diff(5, 2, 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));
|
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
|
@Test
|
||||||
|
|||||||
+20
-17
@@ -45,6 +45,8 @@ import de.dennisguse.opentracks.stats.TrackStatistics;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests insert location.
|
* 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)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class TrackRecordingServiceTestRecording {
|
public class TrackRecordingServiceTestRecording {
|
||||||
@@ -112,7 +114,7 @@ public class TrackRecordingServiceTestRecording {
|
|||||||
String startTime = "2020-02-02T02:02:02Z";
|
String startTime = "2020-02-02T02:02:02Z";
|
||||||
trackPointCreator.setClock(startTime);
|
trackPointCreator.setClock(startTime);
|
||||||
Track.Id trackId = service.startNewTrack();
|
Track.Id trackId = service.startNewTrack();
|
||||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(new TrackStatistics(startTime, startTime, 0, 0, 0, 0, null, null)
|
assertEquals(new TrackStatistics(startTime, startTime, 0, 0, 0, 0, null, null)
|
||||||
@@ -149,7 +151,7 @@ public class TrackRecordingServiceTestRecording {
|
|||||||
|
|
||||||
trackPointCreator.setClock(startTime);
|
trackPointCreator.setClock(startTime);
|
||||||
Track.Id trackId = service.startNewTrack();
|
Track.Id trackId = service.startNewTrack();
|
||||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String pauseTime = "2020-02-02T02:02:03Z";
|
String pauseTime = "2020-02-02T02:02:03Z";
|
||||||
@@ -195,7 +197,7 @@ public class TrackRecordingServiceTestRecording {
|
|||||||
String starTime = "2020-02-02T02:02:02Z";
|
String starTime = "2020-02-02T02:02:02Z";
|
||||||
trackPointCreator.setClock(starTime);
|
trackPointCreator.setClock(starTime);
|
||||||
Track.Id trackId = service.startNewTrack();
|
Track.Id trackId = service.startNewTrack();
|
||||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||||
|
|
||||||
String pauseTime = "2020-02-02T02:02:03Z";
|
String pauseTime = "2020-02-02T02:02:03Z";
|
||||||
trackPointCreator.setClock(pauseTime);
|
trackPointCreator.setClock(pauseTime);
|
||||||
@@ -227,7 +229,7 @@ public class TrackRecordingServiceTestRecording {
|
|||||||
|
|
||||||
trackPointCreator.setClock(startTime);
|
trackPointCreator.setClock(startTime);
|
||||||
Track.Id trackId = service.startNewTrack();
|
Track.Id trackId = service.startNewTrack();
|
||||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||||
|
|
||||||
String stopTime = "2020-02-02T02:02:03Z";
|
String stopTime = "2020-02-02T02:02:03Z";
|
||||||
trackPointCreator.setClock(stopTime);
|
trackPointCreator.setClock(stopTime);
|
||||||
@@ -237,7 +239,7 @@ public class TrackRecordingServiceTestRecording {
|
|||||||
String resumeTime = "2020-02-02T02:02:04Z";
|
String resumeTime = "2020-02-02T02:02:04Z";
|
||||||
trackPointCreator.setClock(resumeTime);
|
trackPointCreator.setClock(resumeTime);
|
||||||
service.resumeTrack(trackId);
|
service.resumeTrack(trackId);
|
||||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
new TrackPointAssert().assertEquals(List.of(
|
new TrackPointAssert().assertEquals(List.of(
|
||||||
@@ -258,12 +260,13 @@ public class TrackRecordingServiceTestRecording {
|
|||||||
|
|
||||||
trackPointCreator.setClock(startTime);
|
trackPointCreator.setClock(startTime);
|
||||||
Track.Id trackId = service.startNewTrack();
|
Track.Id trackId = service.startNewTrack();
|
||||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||||
BluetoothRemoteSensorManager remoteSensorManager = trackPointCreator.getRemoteSensorManager();
|
BluetoothRemoteSensorManager remoteSensorManager = trackPointCreator.getSensorManager().getBluetoothSensorManager();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String sensor1 = "2020-02-02T02:02:03Z";
|
String sensor1 = "2020-02-02T02:02:03Z";
|
||||||
trackPointCreator.setClock(sensor1);
|
trackPointCreator.setClock(sensor1);
|
||||||
|
|
||||||
remoteSensorManager.onChanged(new SensorDataHeartRate("", "", HeartRate.of(5))); //Should be ignored
|
remoteSensorManager.onChanged(new SensorDataHeartRate("", "", HeartRate.of(5))); //Should be ignored
|
||||||
|
|
||||||
String sensor3 = "2020-02-02T02:02:13Z";
|
String sensor3 = "2020-02-02T02:02:13Z";
|
||||||
@@ -297,7 +300,7 @@ public class TrackRecordingServiceTestRecording {
|
|||||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||||
trackPointCreator.setClock(startTime);
|
trackPointCreator.setClock(startTime);
|
||||||
Track.Id trackId = service.startNewTrack();
|
Track.Id trackId = service.startNewTrack();
|
||||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||||
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@@ -371,7 +374,7 @@ public class TrackRecordingServiceTestRecording {
|
|||||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||||
trackPointCreator.setClock(startTime);
|
trackPointCreator.setClock(startTime);
|
||||||
Track.Id trackId = service.startNewTrack();
|
Track.Id trackId = service.startNewTrack();
|
||||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String gps1 = "2020-02-02T02:02:03Z";
|
String gps1 = "2020-02-02T02:02:03Z";
|
||||||
@@ -436,7 +439,7 @@ public class TrackRecordingServiceTestRecording {
|
|||||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||||
trackPointCreator.setClock(startTime);
|
trackPointCreator.setClock(startTime);
|
||||||
Track.Id trackId = service.startNewTrack();
|
Track.Id trackId = service.startNewTrack();
|
||||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String gps1 = "2020-02-02T02:02:03Z";
|
String gps1 = "2020-02-02T02:02:03Z";
|
||||||
@@ -500,7 +503,7 @@ public class TrackRecordingServiceTestRecording {
|
|||||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||||
trackPointCreator.setClock(startTime);
|
trackPointCreator.setClock(startTime);
|
||||||
Track.Id trackId = service.startNewTrack();
|
Track.Id trackId = service.startNewTrack();
|
||||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String gps1 = "2020-02-02T02:02:03Z";
|
String gps1 = "2020-02-02T02:02:03Z";
|
||||||
@@ -564,7 +567,7 @@ public class TrackRecordingServiceTestRecording {
|
|||||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||||
trackPointCreator.setClock(startTime);
|
trackPointCreator.setClock(startTime);
|
||||||
Track.Id trackId = service.startNewTrack();
|
Track.Id trackId = service.startNewTrack();
|
||||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String gps1 = "2020-02-02T02:02:03Z";
|
String gps1 = "2020-02-02T02:02:03Z";
|
||||||
@@ -652,7 +655,7 @@ public class TrackRecordingServiceTestRecording {
|
|||||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||||
trackPointCreator.setClock(startTime);
|
trackPointCreator.setClock(startTime);
|
||||||
Track.Id trackId = service.startNewTrack();
|
Track.Id trackId = service.startNewTrack();
|
||||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String gps1 = "2020-02-02T02:02:03Z";
|
String gps1 = "2020-02-02T02:02:03Z";
|
||||||
@@ -702,7 +705,7 @@ public class TrackRecordingServiceTestRecording {
|
|||||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||||
trackPointCreator.setClock(startTime);
|
trackPointCreator.setClock(startTime);
|
||||||
Track.Id trackId = service.startNewTrack();
|
Track.Id trackId = service.startNewTrack();
|
||||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String gps1 = "2020-02-02T02:02:03Z";
|
String gps1 = "2020-02-02T02:02:03Z";
|
||||||
@@ -748,7 +751,7 @@ public class TrackRecordingServiceTestRecording {
|
|||||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||||
trackPointCreator.setClock(startTime);
|
trackPointCreator.setClock(startTime);
|
||||||
Track.Id trackId = service.startNewTrack();
|
Track.Id trackId = service.startNewTrack();
|
||||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String gps1 = "2020-02-02T02:02:03Z";
|
String gps1 = "2020-02-02T02:02:03Z";
|
||||||
@@ -812,8 +815,8 @@ public class TrackRecordingServiceTestRecording {
|
|||||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
||||||
trackPointCreator.setClock(startTime);
|
trackPointCreator.setClock(startTime);
|
||||||
Track.Id trackId = service.startNewTrack();
|
Track.Id trackId = service.startNewTrack();
|
||||||
trackPointCreator.setAltitudeSumManager(altitudeSumManager);
|
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
||||||
BluetoothRemoteSensorManager remoteSensorManager = trackPointCreator.getRemoteSensorManager();
|
BluetoothRemoteSensorManager remoteSensorManager = trackPointCreator.getSensorManager().getBluetoothSensorManager();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String sensor1 = "2020-02-02T02:02:03Z";
|
String sensor1 = "2020-02-02T02:02:03Z";
|
||||||
|
|||||||
-11
@@ -1,15 +1,12 @@
|
|||||||
package de.dennisguse.opentracks.services;
|
package de.dennisguse.opentracks.services;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
|
||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
|
|
||||||
import androidx.test.rule.ServiceTestRule;
|
import androidx.test.rule.ServiceTestRule;
|
||||||
|
|
||||||
import java.time.Clock;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.sensors.BluetoothRemoteSensorManager;
|
|
||||||
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
||||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||||
|
|
||||||
@@ -22,14 +19,6 @@ public class TrackRecordingServiceTestUtils {
|
|||||||
public static void resetService(ServiceTestRule mServiceRule, Context context) throws TimeoutException {
|
public static void resetService(ServiceTestRule mServiceRule, Context context) throws TimeoutException {
|
||||||
// Let's use default values.
|
// Let's use default values.
|
||||||
PreferencesUtils.clear();
|
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) {
|
static void sendGPSLocation(TrackPointCreator trackPointCreator, String time, double latitude, double longitude, float accuracy, long speed) {
|
||||||
|
|||||||
+3
-3
@@ -26,7 +26,7 @@ import de.dennisguse.opentracks.data.models.Distance;
|
|||||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||||
|
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
public class GPSHandlerTest {
|
public class GPSManagerTest {
|
||||||
|
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ public class GPSHandlerTest {
|
|||||||
private TrackPointCreator trackPointCreator;
|
private TrackPointCreator trackPointCreator;
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
private GPSHandler locationHandler;
|
private GPSManager locationHandler;
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void preSetUp() {
|
public static void preSetUp() {
|
||||||
@@ -50,7 +50,7 @@ public class GPSHandlerTest {
|
|||||||
Mockito.when(trackPointCreator.createNow())
|
Mockito.when(trackPointCreator.createNow())
|
||||||
.thenReturn(Instant.now());
|
.thenReturn(Instant.now());
|
||||||
|
|
||||||
locationHandler.onStart(context, new Handler());
|
locationHandler.start(context, new Handler());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
+1
-1
@@ -25,7 +25,7 @@ public class TrackPointCreatorTest {
|
|||||||
private TrackPointCreator.Callback server;
|
private TrackPointCreator.Callback server;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private GPSHandler locationHandler;
|
private GPSManager locationHandler;
|
||||||
|
|
||||||
private TrackPointCreator subject;
|
private TrackPointCreator subject;
|
||||||
|
|
||||||
|
|||||||
+18
-17
@@ -17,7 +17,8 @@ import org.junit.runner.RunWith;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
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)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class PreferencesOpenHelperTest {
|
public class PreferencesOpenHelperTest {
|
||||||
@@ -38,10 +39,10 @@ public class PreferencesOpenHelperTest {
|
|||||||
PreferencesOpenHelper.newInstance(1).check();
|
PreferencesOpenHelper.newInstance(1).check();
|
||||||
|
|
||||||
// then there should be one layout with old custom layout that has the new CSV value.
|
// then there should be one layout with old custom layout that has the new CSV value.
|
||||||
List<Layout> layouts = PreferencesUtils.getAllCustomLayouts();
|
List<RecordingLayout> recordingLayouts = PreferencesUtils.getAllCustomLayouts();
|
||||||
assertNotNull(layouts);
|
assertNotNull(recordingLayouts);
|
||||||
assertEquals(layouts.size(), 1);
|
assertEquals(recordingLayouts.size(), 1);
|
||||||
assertEquals(layouts.get(0).toCsv(), PreferencesUtils.getCustomLayout().toCsv());
|
assertEquals(recordingLayouts.get(0).toCsv(), PreferencesUtils.getCustomLayout().toCsv());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -63,10 +64,10 @@ public class PreferencesOpenHelperTest {
|
|||||||
+ PreferencesUtils.getLayoutColumnsByDefault() + ";distance,1,1,0;speed,1,1,0;";
|
+ 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.
|
// then there should be one layout with old custom layout that has the new CSV value.
|
||||||
List<Layout> layouts = PreferencesUtils.getAllCustomLayouts();
|
List<RecordingLayout> recordingLayouts = PreferencesUtils.getAllCustomLayouts();
|
||||||
assertNotNull(layouts);
|
assertNotNull(recordingLayouts);
|
||||||
assertEquals(layouts.size(), 1);
|
assertEquals(recordingLayouts.size(), 1);
|
||||||
assertEquals(layouts.get(0).toCsv(), updatedOldCustomLayoutCsv);
|
assertEquals(recordingLayouts.get(0).toCsv(), updatedOldCustomLayoutCsv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -87,10 +88,10 @@ public class PreferencesOpenHelperTest {
|
|||||||
String updatedOldCustomLayoutCsv = "whatever;3;distance,1,1,0;speed,1,1,0;";
|
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.
|
// then there should be one layout with old custom layout that has the new CSV value.
|
||||||
List<Layout> layouts = PreferencesUtils.getAllCustomLayouts();
|
List<RecordingLayout> recordingLayouts = PreferencesUtils.getAllCustomLayouts();
|
||||||
assertNotNull(layouts);
|
assertNotNull(recordingLayouts);
|
||||||
assertEquals(layouts.size(), 1);
|
assertEquals(recordingLayouts.size(), 1);
|
||||||
assertEquals(layouts.get(0).toCsv(), updatedOldCustomLayoutCsv);
|
assertEquals(recordingLayouts.get(0).toCsv(), updatedOldCustomLayoutCsv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -109,10 +110,10 @@ public class PreferencesOpenHelperTest {
|
|||||||
PreferencesOpenHelper.newInstance(1).check();
|
PreferencesOpenHelper.newInstance(1).check();
|
||||||
|
|
||||||
// then custom layout should be equals to default layout.
|
// then custom layout should be equals to default layout.
|
||||||
Layout defaultLayout = Layout.fromCsv(PreferencesUtils.buildDefaultLayout(), resources);
|
RecordingLayout defaultRecordingLayout = RecordingLayoutIO.fromCsv(PreferencesUtils.buildDefaultLayout(), resources);
|
||||||
List<Layout> customLayout = PreferencesUtils.getAllCustomLayouts();
|
List<RecordingLayout> customRecordingLayout = PreferencesUtils.getAllCustomLayouts();
|
||||||
|
|
||||||
assertEquals(customLayout.size(), 1);
|
assertEquals(customRecordingLayout.size(), 1);
|
||||||
assertEquals(defaultLayout.toCsv(), customLayout.get(0).toCsv());
|
assertEquals(defaultRecordingLayout.toCsv(), customRecordingLayout.get(0).toCsv());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ import java.util.List;
|
|||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.DataField;
|
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)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class PreferencesUtilsTest {
|
public class PreferencesUtilsTest {
|
||||||
@@ -85,13 +86,13 @@ public class PreferencesUtilsTest {
|
|||||||
editor.commit();
|
editor.commit();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
List<Layout> layouts = PreferencesUtils.getAllCustomLayouts();
|
List<RecordingLayout> recordingLayouts = PreferencesUtils.getAllCustomLayouts();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(layouts.size(), 1);
|
assertEquals(recordingLayouts.size(), 1);
|
||||||
assertTrue(layouts.get(0).getFields().size() > 0);
|
assertTrue(recordingLayouts.get(0).getFields().size() > 0);
|
||||||
assertEquals(layouts.get(0).getName(), context.getString(R.string.stats_custom_layout_default_layout));
|
assertEquals(recordingLayouts.get(0).getName(), context.getString(R.string.stats_custom_layout_default_layout));
|
||||||
assertTrue(layouts.get(0).getFields().stream().anyMatch(DataField::isVisible));
|
assertTrue(recordingLayouts.get(0).getFields().stream().anyMatch(DataField::isVisible));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -103,12 +104,12 @@ public class PreferencesUtilsTest {
|
|||||||
editor.commit();
|
editor.commit();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
Layout layout = PreferencesUtils.getCustomLayout();
|
RecordingLayout recordingLayout = PreferencesUtils.getCustomLayout();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertTrue(layout.getFields().size() > 0);
|
assertTrue(recordingLayout.getFields().size() > 0);
|
||||||
assertEquals(layout.getName(), context.getString(R.string.stats_custom_layout_default_layout));
|
assertEquals(recordingLayout.getName(), context.getString(R.string.stats_custom_layout_default_layout));
|
||||||
assertTrue(layout.getFields().stream().anyMatch(DataField::isVisible));
|
assertTrue(recordingLayout.getFields().stream().anyMatch(DataField::isVisible));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -126,28 +127,28 @@ public class PreferencesUtilsTest {
|
|||||||
editor.apply();
|
editor.apply();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
Layout layout = PreferencesUtils.getCustomLayout();
|
RecordingLayout recordingLayout = PreferencesUtils.getCustomLayout();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(layout.getFields().size(), 4);
|
assertEquals(recordingLayout.getFields().size(), 4);
|
||||||
assertEquals(layout.getName(), "run");
|
assertEquals(recordingLayout.getName(), "run");
|
||||||
assertEquals(layout.getColumnsPerRow(), 2);
|
assertEquals(recordingLayout.getColumnsPerRow(), 2);
|
||||||
|
|
||||||
assertEquals(layout.getFields().get(0).getKey(), context.getString(R.string.stats_custom_layout_moving_time_key));
|
assertEquals(recordingLayout.getFields().get(0).getKey(), context.getString(R.string.stats_custom_layout_moving_time_key));
|
||||||
assertTrue(layout.getFields().get(0).isVisible());
|
assertTrue(recordingLayout.getFields().get(0).isVisible());
|
||||||
assertTrue(layout.getFields().get(0).isPrimary());
|
assertTrue(recordingLayout.getFields().get(0).isPrimary());
|
||||||
|
|
||||||
assertEquals(layout.getFields().get(1).getKey(), context.getString(R.string.stats_custom_layout_distance_key));
|
assertEquals(recordingLayout.getFields().get(1).getKey(), context.getString(R.string.stats_custom_layout_distance_key));
|
||||||
assertTrue(layout.getFields().get(1).isVisible());
|
assertTrue(recordingLayout.getFields().get(1).isVisible());
|
||||||
assertFalse(layout.getFields().get(1).isPrimary());
|
assertFalse(recordingLayout.getFields().get(1).isPrimary());
|
||||||
|
|
||||||
assertEquals(layout.getFields().get(2).getKey(), context.getString(R.string.stats_custom_layout_average_moving_speed_key));
|
assertEquals(recordingLayout.getFields().get(2).getKey(), context.getString(R.string.stats_custom_layout_average_moving_speed_key));
|
||||||
assertFalse(layout.getFields().get(2).isVisible());
|
assertFalse(recordingLayout.getFields().get(2).isVisible());
|
||||||
assertTrue(layout.getFields().get(2).isPrimary());
|
assertTrue(recordingLayout.getFields().get(2).isPrimary());
|
||||||
|
|
||||||
assertEquals(layout.getFields().get(3).getKey(), context.getString(R.string.stats_custom_layout_speed_key));
|
assertEquals(recordingLayout.getFields().get(3).getKey(), context.getString(R.string.stats_custom_layout_speed_key));
|
||||||
assertFalse(layout.getFields().get(3).isVisible());
|
assertFalse(recordingLayout.getFields().get(3).isVisible());
|
||||||
assertFalse(layout.getFields().get(3).isPrimary());
|
assertFalse(recordingLayout.getFields().get(3).isPrimary());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -165,44 +166,44 @@ public class PreferencesUtilsTest {
|
|||||||
editor.apply();
|
editor.apply();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
Layout layout = PreferencesUtils.getCustomLayout();
|
RecordingLayout recordingLayout = PreferencesUtils.getCustomLayout();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(layout.getFields().size(), 4);
|
assertEquals(recordingLayout.getFields().size(), 4);
|
||||||
assertEquals(layout.getName(), "walking");
|
assertEquals(recordingLayout.getName(), "walking");
|
||||||
assertEquals(layout.getColumnsPerRow(), 2);
|
assertEquals(recordingLayout.getColumnsPerRow(), 2);
|
||||||
|
|
||||||
assertEquals(layout.getFields().get(0).getKey(), context.getString(R.string.stats_custom_layout_moving_time_key));
|
assertEquals(recordingLayout.getFields().get(0).getKey(), context.getString(R.string.stats_custom_layout_moving_time_key));
|
||||||
assertTrue(layout.getFields().get(0).isVisible());
|
assertTrue(recordingLayout.getFields().get(0).isVisible());
|
||||||
assertTrue(layout.getFields().get(0).isPrimary());
|
assertTrue(recordingLayout.getFields().get(0).isPrimary());
|
||||||
|
|
||||||
assertEquals(layout.getFields().get(1).getKey(), context.getString(R.string.stats_custom_layout_distance_key));
|
assertEquals(recordingLayout.getFields().get(1).getKey(), context.getString(R.string.stats_custom_layout_distance_key));
|
||||||
assertTrue(layout.getFields().get(1).isVisible());
|
assertTrue(recordingLayout.getFields().get(1).isVisible());
|
||||||
assertFalse(layout.getFields().get(1).isPrimary());
|
assertFalse(recordingLayout.getFields().get(1).isPrimary());
|
||||||
|
|
||||||
assertEquals(layout.getFields().get(2).getKey(), context.getString(R.string.stats_custom_layout_coordinates_key));
|
assertEquals(recordingLayout.getFields().get(2).getKey(), context.getString(R.string.stats_custom_layout_coordinates_key));
|
||||||
assertFalse(layout.getFields().get(2).isVisible());
|
assertFalse(recordingLayout.getFields().get(2).isVisible());
|
||||||
assertTrue(layout.getFields().get(2).isPrimary());
|
assertTrue(recordingLayout.getFields().get(2).isPrimary());
|
||||||
assertTrue(layout.getFields().get(2).isWide());
|
assertTrue(recordingLayout.getFields().get(2).isWide());
|
||||||
|
|
||||||
assertEquals(layout.getFields().get(3).getKey(), context.getString(R.string.stats_custom_layout_speed_key));
|
assertEquals(recordingLayout.getFields().get(3).getKey(), context.getString(R.string.stats_custom_layout_speed_key));
|
||||||
assertFalse(layout.getFields().get(3).isVisible());
|
assertFalse(recordingLayout.getFields().get(3).isVisible());
|
||||||
assertFalse(layout.getFields().get(3).isPrimary());
|
assertFalse(recordingLayout.getFields().get(3).isPrimary());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetCustomLayout() {
|
public void testSetCustomLayout() {
|
||||||
// given
|
// given
|
||||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
Layout layoutSrc = new Layout("road cycling");
|
RecordingLayout recordingLayoutSrc = new RecordingLayout("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));
|
recordingLayoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_moving_time_key), 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));
|
recordingLayoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_distance_key), 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));
|
recordingLayoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_average_moving_speed_key), 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));
|
recordingLayoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_speed_key), false, false, false));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
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();
|
editor.commit();
|
||||||
|
|
||||||
// then
|
// 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_average_moving_speed_key) + ",0,1,0;"
|
||||||
+ context.getString(R.string.stats_custom_layout_speed_key) + ",0,0,0;");
|
+ context.getString(R.string.stats_custom_layout_speed_key) + ",0,0,0;");
|
||||||
|
|
||||||
Layout layoutDst = PreferencesUtils.getCustomLayout();
|
RecordingLayout recordingLayoutDst = PreferencesUtils.getCustomLayout();
|
||||||
assertEquals(layoutSrc.getName(), layoutDst.getName());
|
assertEquals(recordingLayoutSrc.getName(), recordingLayoutDst.getName());
|
||||||
assertEquals(layoutSrc.getFields().size(), layoutDst.getFields().size());
|
assertEquals(recordingLayoutSrc.getFields().size(), recordingLayoutDst.getFields().size());
|
||||||
for (int i = 0; i < layoutSrc.getFields().size(); i++) {
|
for (int i = 0; i < recordingLayoutSrc.getFields().size(); i++) {
|
||||||
assertEquals(layoutSrc.getFields().get(i).getKey(), layoutDst.getFields().get(i).getKey());
|
assertEquals(recordingLayoutSrc.getFields().get(i).getKey(), recordingLayoutDst.getFields().get(i).getKey());
|
||||||
assertEquals(layoutSrc.getFields().get(i).isVisible(), layoutDst.getFields().get(i).isVisible());
|
assertEquals(recordingLayoutSrc.getFields().get(i).isVisible(), recordingLayoutDst.getFields().get(i).isVisible());
|
||||||
assertEquals(layoutSrc.getFields().get(i).isPrimary(), layoutDst.getFields().get(i).isPrimary());
|
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.putString(context.getString(R.string.stats_custom_layouts_key), cyclingProfile + "\n" + runningProfile);
|
||||||
editor.apply();
|
editor.apply();
|
||||||
|
|
||||||
List<Layout> layoutsBefore = PreferencesUtils.getAllCustomLayouts();
|
List<RecordingLayout> layoutsBefore = PreferencesUtils.getAllCustomLayouts();
|
||||||
|
|
||||||
// when cyling profile is updated
|
// when cyling profile is updated
|
||||||
String cyclingProfileUpdated = "cycling;2;"
|
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_average_moving_speed_key) + ",0,0;"
|
||||||
+ context.getString(R.string.stats_custom_layout_speed_key) + ",0,0;";
|
+ context.getString(R.string.stats_custom_layout_speed_key) + ",0,0;";
|
||||||
|
|
||||||
List<Layout> layoutsToBeUpdated = new ArrayList<>();
|
List<RecordingLayout> layoutsToBeUpdated = new ArrayList<>();
|
||||||
layoutsToBeUpdated.add(Layout.fromCsv(cyclingProfileUpdated, resources));
|
layoutsToBeUpdated.add(RecordingLayoutIO.fromCsv(cyclingProfileUpdated, resources));
|
||||||
layoutsToBeUpdated.add(Layout.fromCsv(runningProfile, resources));
|
layoutsToBeUpdated.add(RecordingLayoutIO.fromCsv(runningProfile, resources));
|
||||||
|
|
||||||
PreferencesUtils.updateCustomLayouts(layoutsToBeUpdated);
|
PreferencesUtils.updateCustomLayouts(layoutsToBeUpdated);
|
||||||
|
|
||||||
// then only updated profile is modified in the custom layouts
|
// 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(layoutsBefore.size(), 2);
|
||||||
assertEquals(layoutsAfter.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.putString(context.getString(R.string.stats_custom_layouts_key), cyclingProfile + "\n" + runningProfile);
|
||||||
editor.apply();
|
editor.apply();
|
||||||
|
|
||||||
List<Layout> layoutsBefore = PreferencesUtils.getAllCustomLayouts();
|
List<RecordingLayout> layoutsBefore = PreferencesUtils.getAllCustomLayouts();
|
||||||
|
|
||||||
// when cyling profile is updated
|
// when cyling profile is updated
|
||||||
String cyclingProfileUpdated = "cycling;2;"
|
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_distance_key) + ",0,0;"
|
||||||
+ context.getString(R.string.stats_custom_layout_average_moving_speed_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;";
|
+ context.getString(R.string.stats_custom_layout_speed_key) + ",0,0;";
|
||||||
Layout layoutToBeUpdated = Layout.fromCsv(cyclingProfileUpdated, resources);
|
RecordingLayout recordingLayoutToBeUpdated = RecordingLayoutIO.fromCsv(cyclingProfileUpdated, resources);
|
||||||
PreferencesUtils.updateCustomLayout(layoutToBeUpdated);
|
PreferencesUtils.updateCustomLayout(recordingLayoutToBeUpdated);
|
||||||
|
|
||||||
// then only updated profile is modified in the custom layouts
|
// 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(layoutsBefore.size(), 2);
|
||||||
assertEquals(layoutsAfter.size(), 2);
|
assertEquals(layoutsAfter.size(), 2);
|
||||||
@@ -337,9 +338,9 @@ public class PreferencesUtilsTest {
|
|||||||
editor.apply();
|
editor.apply();
|
||||||
|
|
||||||
// when it gets the custom layout
|
// when it gets the custom layout
|
||||||
Layout layout = PreferencesUtils.getCustomLayout();
|
RecordingLayout recordingLayout = PreferencesUtils.getCustomLayout();
|
||||||
|
|
||||||
// then the first one was returned
|
// then the first one was returned
|
||||||
assertEquals(layout.getName(), "cycling");
|
assertEquals(recordingLayout.getName(), "cycling");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ import org.junit.runner.RunWith;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class CsvLayoutUtilsTest extends TestCase {
|
public class CsvRecordingLayoutUtilsTest extends TestCase {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetCsvLineParts() {
|
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;
|
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class LayoutTest extends TestCase {
|
public class RecordingLayoutTest extends TestCase {
|
||||||
|
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
private final Resources resources = ApplicationProvider.getApplicationContext().getResources();
|
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;";
|
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
|
// when create a layout from CSV line
|
||||||
Layout layout = Layout.fromCsv(csv, resources);
|
RecordingLayout recordingLayout = RecordingLayoutIO.fromCsv(csv, resources);
|
||||||
List<DataField> dataFieldList = layout.getFields();
|
List<DataField> dataFieldList = recordingLayout.getFields();
|
||||||
|
|
||||||
// then layout and data fields are built correctly
|
// then layout and data fields are built correctly
|
||||||
assertEquals(layout.getName(), "running");
|
assertEquals(recordingLayout.getName(), "running");
|
||||||
assertEquals(layout.getColumnsPerRow(), 2);
|
assertEquals(recordingLayout.getColumnsPerRow(), 2);
|
||||||
assertEquals(dataFieldList.size(), 4);
|
assertEquals(dataFieldList.size(), 4);
|
||||||
assertEquals(dataFieldList.get(0).getKey(), context.getString(R.string.stats_custom_layout_speed_key));
|
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(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(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(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).isVisible());
|
||||||
assertTrue(dataFieldList.get(0).isPrimary());
|
assertTrue(dataFieldList.get(0).isPrimary());
|
||||||
assertFalse(dataFieldList.get(0).isWide());
|
assertFalse(dataFieldList.get(0).isWide());
|
||||||
@@ -64,13 +60,13 @@ public class LayoutTest extends TestCase {
|
|||||||
String csv1 = "Layout Name;speed,1,1;distance,0,0;";
|
String csv1 = "Layout Name;speed,1,1;distance,0,0;";
|
||||||
|
|
||||||
// when create a layout from CSV line
|
// when create a layout from CSV line
|
||||||
Layout layout = Layout.fromCsv(csv1, resources);
|
RecordingLayout recordingLayout = RecordingLayoutIO.fromCsv(csv1, resources);
|
||||||
List<DataField> dataFieldList = layout.getFields();
|
List<DataField> dataFieldList = recordingLayout.getFields();
|
||||||
|
|
||||||
// then layout and data fields are built correctly
|
// then layout and data fields are built correctly
|
||||||
assertEquals(layout.getName(), PreferencesUtils.getDefaultLayoutName());
|
assertEquals(recordingLayout.getName(), PreferencesUtils.getDefaultLayoutName());
|
||||||
assertEquals(layout.getColumnsPerRow(), PreferencesUtils.getLayoutColumnsByDefault());
|
assertEquals(recordingLayout.getColumnsPerRow(), PreferencesUtils.getLayoutColumnsByDefault());
|
||||||
assertEquals(layout.getFields().size(), 0);
|
assertEquals(recordingLayout.getFields().size(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -79,13 +75,13 @@ public class LayoutTest extends TestCase {
|
|||||||
String csv1 = "Layout Name;2;";
|
String csv1 = "Layout Name;2;";
|
||||||
|
|
||||||
// when create a layout from CSV line
|
// when create a layout from CSV line
|
||||||
Layout layout = Layout.fromCsv(csv1, resources);
|
RecordingLayout recordingLayout = RecordingLayoutIO.fromCsv(csv1, resources);
|
||||||
List<DataField> dataFieldList = layout.getFields();
|
List<DataField> dataFieldList = recordingLayout.getFields();
|
||||||
|
|
||||||
// then layout and data fields are built correctly
|
// then layout and data fields are built correctly
|
||||||
assertEquals(layout.getName(), PreferencesUtils.getDefaultLayoutName());
|
assertEquals(recordingLayout.getName(), PreferencesUtils.getDefaultLayoutName());
|
||||||
assertEquals(layout.getColumnsPerRow(), PreferencesUtils.getLayoutColumnsByDefault());
|
assertEquals(recordingLayout.getColumnsPerRow(), PreferencesUtils.getLayoutColumnsByDefault());
|
||||||
assertEquals(layout.getFields().size(), 0);
|
assertEquals(recordingLayout.getFields().size(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -94,13 +90,13 @@ public class LayoutTest extends TestCase {
|
|||||||
String csv1 = "Layout Name;2;speed,distance,total time;";
|
String csv1 = "Layout Name;2;speed,distance,total time;";
|
||||||
|
|
||||||
// when create a layout from CSV line
|
// when create a layout from CSV line
|
||||||
Layout layout = Layout.fromCsv(csv1, resources);
|
RecordingLayout recordingLayout = RecordingLayoutIO.fromCsv(csv1, resources);
|
||||||
List<DataField> dataFieldList = layout.getFields();
|
List<DataField> dataFieldList = recordingLayout.getFields();
|
||||||
|
|
||||||
// then layout and data fields are built correctly
|
// then layout and data fields are built correctly
|
||||||
assertEquals(layout.getName(), "Layout Name");
|
assertEquals(recordingLayout.getName(), "Layout Name");
|
||||||
assertEquals(layout.getColumnsPerRow(), 2);
|
assertEquals(recordingLayout.getColumnsPerRow(), 2);
|
||||||
assertEquals(layout.getFields().size(), 0);
|
assertEquals(recordingLayout.getFields().size(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -109,27 +105,27 @@ public class LayoutTest extends TestCase {
|
|||||||
String csv1 = "Layout Name;2;speed,1,0;distance;";
|
String csv1 = "Layout Name;2;speed,1,0;distance;";
|
||||||
|
|
||||||
// when create a layout from CSV line
|
// when create a layout from CSV line
|
||||||
Layout layout = Layout.fromCsv(csv1, resources);
|
RecordingLayout recordingLayout = RecordingLayoutIO.fromCsv(csv1, resources);
|
||||||
List<DataField> dataFieldList = layout.getFields();
|
List<DataField> dataFieldList = recordingLayout.getFields();
|
||||||
|
|
||||||
// then layout and data fields are built correctly
|
// then layout and data fields are built correctly
|
||||||
assertEquals(layout.getName(), "Layout Name");
|
assertEquals(recordingLayout.getName(), "Layout Name");
|
||||||
assertEquals(layout.getColumnsPerRow(), 2);
|
assertEquals(recordingLayout.getColumnsPerRow(), 2);
|
||||||
assertEquals(layout.getFields().size(), 1);
|
assertEquals(recordingLayout.getFields().size(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testToCsv() {
|
public void testToCsv() {
|
||||||
// given a layout's object
|
// given a layout's object
|
||||||
Layout layout = new Layout("Test Layout", 2);
|
RecordingLayout recordingLayout = new RecordingLayout("Test Layout", 2);
|
||||||
layout.addField("key1", "Title 1", false, false, false);
|
recordingLayout.addField(new DataField("key1", false, false, false));
|
||||||
layout.addField("key2", "Title 2", false, true, false);
|
recordingLayout.addField(new DataField("key2", false, true, false));
|
||||||
layout.addField("key3", "Title 3", true, false, false);
|
recordingLayout.addField(new DataField("key3", true, false, false));
|
||||||
layout.addField("key4", "Title 4", true, true, false);
|
recordingLayout.addField(new DataField("key4", true, true, false));
|
||||||
layout.addField("key5", "Title 5", true, true, true);
|
recordingLayout.addField(new DataField("key5", true, true, true));
|
||||||
|
|
||||||
// when converts it to CSV
|
// when converts it to CSV
|
||||||
String csv = layout.toCsv();
|
String csv = recordingLayout.toCsv();
|
||||||
|
|
||||||
// then csv is well built
|
// 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;");
|
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
|
@Test
|
||||||
public void testToCsv_columnsByDefault() {
|
public void testToCsv_columnsByDefault() {
|
||||||
// given a layout's object
|
// given a layout's object
|
||||||
Layout layout = new Layout("Test Layout");
|
RecordingLayout recordingLayout = new RecordingLayout("Test Layout");
|
||||||
layout.addField("key1", "Title 1", false, false, false);
|
recordingLayout.addField(new DataField("key1", false, false, false));
|
||||||
layout.addField("key2", "Title 2", false, true, false);
|
recordingLayout.addField(new DataField("key2", false, true, false));
|
||||||
layout.addField("key3", "Title 3", true, false, false);
|
recordingLayout.addField(new DataField("key3", true, false, false));
|
||||||
layout.addField("key4", "Title 4", true, true, false);
|
recordingLayout.addField(new DataField("key4", true, true, false));
|
||||||
layout.addField("key5", "Title 5", true, true, true);
|
recordingLayout.addField(new DataField("key5", true, true, true));
|
||||||
|
|
||||||
// when converts it to CSV
|
// when converts it to CSV
|
||||||
String csv = layout.toCsv();
|
String csv = recordingLayout.toCsv();
|
||||||
|
|
||||||
// then csv is well built
|
// 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;");
|
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 expectedLength = 18671444;
|
||||||
int expectedHeaderLength = 404;
|
int expectedHeaderLength = 404;
|
||||||
|
|
||||||
String expectedHeader = "P5\n" +
|
String expectedHeader = """
|
||||||
"# Geoid file in PGM format for the GeographicLib::Geoid class\n" +
|
P5
|
||||||
"# Description WGS84 EGM2008, 5-minute grid\n" +
|
# Geoid file in PGM format for the GeographicLib::Geoid class
|
||||||
"# URL http://earth-info.nga.mil/GandG/wgs84/gravitymod/egm2008\n" +
|
# Description WGS84 EGM2008, 5-minute grid
|
||||||
"# DateTime 2009-08-29 18:45:00\n" +
|
# URL http://earth-info.nga.mil/GandG/wgs84/gravitymod/egm2008
|
||||||
"# MaxBilinearError 0.478\n" +
|
# DateTime 2009-08-29 18:45:00
|
||||||
"# RMSBilinearError 0.012\n" +
|
# MaxBilinearError 0.478
|
||||||
"# MaxCubicError 0.294\n" +
|
# RMSBilinearError 0.012
|
||||||
"# RMSCubicError 0.005\n" +
|
# MaxCubicError 0.294
|
||||||
"# Offset -108\n" +
|
# RMSCubicError 0.005
|
||||||
"# Scale 0.003\n" +
|
# Offset -108
|
||||||
"# Origin 90N 0E\n" +
|
# Scale 0.003
|
||||||
"# AREA_OR_POINT Point\n" +
|
# Origin 90N 0E
|
||||||
"# Vertical_Datum WGS84\n" +
|
# AREA_OR_POINT Point
|
||||||
"4320 2161\n" +
|
# Vertical_Datum WGS84
|
||||||
"65535" +
|
4320 2161
|
||||||
"\n";
|
65535
|
||||||
|
""";
|
||||||
|
|
||||||
// when
|
// when
|
||||||
try (InputStream inputStream = context.getResources().openRawResource(EGM2008Utils.EGM2008_5_DATA)) {
|
try (InputStream inputStream = context.getResources().openRawResource(EGM2008Utils.EGM2008_5_DATA)) {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import org.junit.runner.RunWith;
|
|||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.DataField;
|
import de.dennisguse.opentracks.ui.customRecordingLayout.DataField;
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.Layout;
|
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayout;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class StatisticsUtilsTest extends TestCase {
|
public class StatisticsUtilsTest extends TestCase {
|
||||||
@@ -44,16 +44,16 @@ public class StatisticsUtilsTest extends TestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testFilterVisible() {
|
public void testFilterVisible() {
|
||||||
// given
|
// given
|
||||||
Layout layout = new Layout("profile");
|
RecordingLayout recordingLayout = new RecordingLayout("profile");
|
||||||
layout.addField(new DataField("key1", "title1", true, true, false));
|
recordingLayout.addField(new DataField("key1", true, true, false));
|
||||||
layout.addField(new DataField("key2", "title2", false, true, false));
|
recordingLayout.addField(new DataField("key2", false, true, false));
|
||||||
layout.addField(new DataField("key3", "title3", true, true, false));
|
recordingLayout.addField(new DataField("key3", true, true, false));
|
||||||
layout.addField(new DataField("key4", "title4", false, true, false));
|
recordingLayout.addField(new DataField("key4", false, true, false));
|
||||||
layout.addField(new DataField("key5", "title5", true, true, false));
|
recordingLayout.addField(new DataField("key5", true, true, false));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
Layout resultTrue = StatisticsUtils.filterVisible(layout, true);
|
RecordingLayout resultTrue = recordingLayout.toRecordingLayout(true);
|
||||||
Layout resultFalse = StatisticsUtils.filterVisible(layout, false);
|
RecordingLayout resultFalse = recordingLayout.toRecordingLayout(false);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(resultTrue.getFields().size(), 3);
|
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))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -62,7 +62,9 @@ limitations under the License.
|
|||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".Startup"
|
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:hardwareAccelerated="true"
|
||||||
android:hasFragileUserData="false"
|
android:hasFragileUserData="false"
|
||||||
android:icon="@drawable/ic_launcher"
|
android:icon="@drawable/ic_launcher"
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import android.widget.ArrayAdapter;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||||
|
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
import de.dennisguse.opentracks.databinding.TrackEditBinding;
|
import de.dennisguse.opentracks.databinding.TrackEditBinding;
|
||||||
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
|
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
|
||||||
@@ -75,7 +76,7 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
|
|||||||
|
|
||||||
viewBinding.fields.trackEditActivityType.setText(track.getCategory());
|
viewBinding.fields.trackEditActivityType.setText(track.getCategory());
|
||||||
|
|
||||||
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.setAdapter(adapter);
|
||||||
viewBinding.fields.trackEditActivityType.setOnItemClickListener((parent, view, position, id) -> setActivityTypeIcon(TrackIconUtils.getIconValue(this, (String) viewBinding.fields.trackEditActivityType.getAdapter().getItem(position))));
|
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) -> {
|
viewBinding.fields.trackEditActivityType.setOnFocusChangeListener((v, hasFocus) -> {
|
||||||
|
|||||||
@@ -311,9 +311,9 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
searchMenuItem = menu.findItem(R.id.track_list_search);
|
searchMenuItem = menu.findItem(R.id.track_list_search);
|
||||||
SearchView searchView = ActivityUtils.configureSearchWidget(this, searchMenuItem);
|
SearchView searchView = ActivityUtils.configureSearchWidget(this, searchMenuItem);
|
||||||
|
|
||||||
searchView.findViewById(R.id.search_edit_frame).setPadding(0, 0, 48, 0);
|
searchView.findViewById(androidx.appcompat.R.id.search_edit_frame).setPadding(0, 0, 48, 0);
|
||||||
|
|
||||||
SearchView.SearchAutoComplete searchAutoComplete = searchView.findViewById(R.id.search_src_text);
|
SearchView.SearchAutoComplete searchAutoComplete = searchView.findViewById(androidx.appcompat.R.id.search_src_text);
|
||||||
searchAutoComplete.setHintTextColor(ContextCompat.getColor(this, android.R.color.white));
|
searchAutoComplete.setHintTextColor(ContextCompat.getColor(this, android.R.color.white));
|
||||||
searchAutoComplete.setTextColor(ContextCompat.getColor(this, android.R.color.white));
|
searchAutoComplete.setTextColor(ContextCompat.getColor(this, android.R.color.white));
|
||||||
|
|
||||||
|
|||||||
@@ -274,18 +274,14 @@ public class TrackRecordedActivity extends AbstractTrackDeleteActivity implement
|
|||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Fragment createFragment(int position) {
|
public Fragment createFragment(int position) {
|
||||||
switch (position) {
|
return switch (position) {
|
||||||
case 0:
|
case 0 -> StatisticsRecordedFragment.newInstance(trackId);
|
||||||
return StatisticsRecordedFragment.newInstance(trackId);
|
case 1 -> IntervalsFragment.newInstance(trackId, true);
|
||||||
case 1:
|
case 2 -> ChartFragment.newInstance(false);
|
||||||
return IntervalsFragment.newInstance(trackId, true);
|
case 3 -> ChartFragment.newInstance(true);
|
||||||
case 2:
|
default ->
|
||||||
return ChartFragment.newInstance(false);
|
|
||||||
case 3:
|
|
||||||
return ChartFragment.newInstance(true);
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
|
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -294,18 +290,14 @@ public class TrackRecordedActivity extends AbstractTrackDeleteActivity implement
|
|||||||
}
|
}
|
||||||
|
|
||||||
public CharSequence getPageTitle(int position) {
|
public CharSequence getPageTitle(int position) {
|
||||||
switch (position) {
|
return switch (position) {
|
||||||
case 0:
|
case 0 -> getString(R.string.track_detail_stats_tab);
|
||||||
return getString(R.string.track_detail_stats_tab);
|
case 1 -> getString(R.string.track_detail_intervals_tab);
|
||||||
case 1:
|
case 2 -> getString(R.string.settings_chart_by_time);
|
||||||
return getString(R.string.track_detail_intervals_tab);
|
case 3 -> getString(R.string.settings_chart_by_distance);
|
||||||
case 2:
|
default ->
|
||||||
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);
|
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -333,18 +333,14 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Fragment createFragment(int position) {
|
public Fragment createFragment(int position) {
|
||||||
switch (position) {
|
return switch (position) {
|
||||||
case 0:
|
case 0 -> StatisticsRecordingFragment.newInstance();
|
||||||
return StatisticsRecordingFragment.newInstance();
|
case 1 -> IntervalsFragment.newInstance(trackId, false);
|
||||||
case 1:
|
case 2 -> ChartFragment.newInstance(false);
|
||||||
return IntervalsFragment.newInstance(trackId, false);
|
case 3 -> ChartFragment.newInstance(true);
|
||||||
case 2:
|
default ->
|
||||||
return ChartFragment.newInstance(false);
|
|
||||||
case 3:
|
|
||||||
return ChartFragment.newInstance(true);
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
|
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -353,18 +349,14 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
}
|
}
|
||||||
|
|
||||||
public CharSequence getPageTitle(int position) {
|
public CharSequence getPageTitle(int position) {
|
||||||
switch (position) {
|
return switch (position) {
|
||||||
case 0:
|
case 0 -> getString(R.string.track_detail_stats_tab);
|
||||||
return getString(R.string.track_detail_stats_tab);
|
case 1 -> getString(R.string.track_detail_intervals_tab);
|
||||||
case 1:
|
case 2 -> getString(R.string.settings_chart_by_time);
|
||||||
return getString(R.string.track_detail_intervals_tab);
|
case 3 -> getString(R.string.settings_chart_by_distance);
|
||||||
case 2:
|
default ->
|
||||||
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);
|
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,13 +373,18 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onGpsStatusChanged(GpsStatusValue gpsStatusValue) {
|
private void onGpsStatusChanged(GpsStatusValue gpsStatusValue) {
|
||||||
if (gpsStatusValue.isGpsStarted() && snackbar != null && snackbar.isShown()) {
|
boolean snackbarShowing = snackbar != null && snackbar.isShown();
|
||||||
|
if (gpsStatusValue.isGpsStarted()) {
|
||||||
|
if (snackbarShowing) {
|
||||||
snackbar.dismiss();
|
snackbar.dismiss();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (gpsStatusValue != GpsStatusValue.GPS_DISABLED) {
|
|
||||||
|
if (snackbarShowing) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
snackbar = Snackbar
|
snackbar = Snackbar
|
||||||
.make(viewBinding.trackRecordingCoordinatorLayout,
|
.make(viewBinding.trackRecordingCoordinatorLayout,
|
||||||
getString(R.string.gps_recording_status, getString(gpsStatusValue.message), getString(R.string.gps_recording_without_signal)),
|
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 android.widget.ArrayAdapter;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
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.DistanceFormatter;
|
||||||
import de.dennisguse.opentracks.data.models.SpeedFormatter;
|
import de.dennisguse.opentracks.data.models.SpeedFormatter;
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
@@ -51,7 +52,7 @@ public class TrackStoppedActivity extends AbstractTrackDeleteActivity implements
|
|||||||
|
|
||||||
viewBinding.trackEditActivityType.setText(track.getCategory());
|
viewBinding.trackEditActivityType.setText(track.getCategory());
|
||||||
|
|
||||||
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.setAdapter(adapter);
|
||||||
viewBinding.trackEditActivityType.setOnItemClickListener((parent, view, position, id) -> setActivityTypeIcon(TrackIconUtils.getIconValue(this, (String) viewBinding.trackEditActivityType.getAdapter().getItem(position))));
|
viewBinding.trackEditActivityType.setOnItemClickListener((parent, view, position, id) -> setActivityTypeIcon(TrackIconUtils.getIconValue(this, (String) viewBinding.trackEditActivityType.getAdapter().getItem(position))));
|
||||||
viewBinding.trackEditActivityType.setOnFocusChangeListener((v, hasFocus) -> {
|
viewBinding.trackEditActivityType.setOnFocusChangeListener((v, hasFocus) -> {
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -210,16 +210,11 @@ abstract class ChartValueSeries {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int getTitleId(UnitSystem unitSystem) {
|
int getTitleId(UnitSystem unitSystem) {
|
||||||
switch (unitSystem) {
|
return switch (unitSystem) {
|
||||||
case METRIC:
|
case METRIC -> metricTitleId;
|
||||||
return metricTitleId;
|
case IMPERIAL -> imperialTitleId;
|
||||||
case IMPERIAL:
|
case NAUTICAL_IMPERIAL -> nauticalTitleId;
|
||||||
return imperialTitleId;
|
};
|
||||||
case NAUTICAL_IMPERIAL:
|
|
||||||
return nauticalTitleId;
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Not implemented");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Paint getTitlePaint() {
|
Paint getTitlePaint() {
|
||||||
|
|||||||
@@ -733,16 +733,11 @@ public class ChartView extends View {
|
|||||||
private String getXAxisLabel() {
|
private String getXAxisLabel() {
|
||||||
Context context = getContext();
|
Context context = getContext();
|
||||||
if (chartByDistance) {
|
if (chartByDistance) {
|
||||||
switch (unitSystem) {
|
return switch (unitSystem) {
|
||||||
case METRIC:
|
case METRIC -> context.getString(R.string.unit_kilometer);
|
||||||
return context.getString(R.string.unit_kilometer);
|
case IMPERIAL -> context.getString(R.string.unit_mile);
|
||||||
case IMPERIAL:
|
case NAUTICAL_IMPERIAL -> context.getString(R.string.unit_nautical_mile);
|
||||||
return context.getString(R.string.unit_mile);
|
};
|
||||||
case NAUTICAL_IMPERIAL:
|
|
||||||
return context.getString(R.string.unit_nautical_mile);
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Not implemented");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return context.getString(R.string.description_time);
|
return context.getString(R.string.description_time);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,20 +133,12 @@ public class CustomContentProvider extends ContentProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int delete(@NonNull Uri url, String where, String[] selectionArgs) {
|
public int delete(@NonNull Uri url, String where, String[] selectionArgs) {
|
||||||
String table;
|
String table = switch (getUrlType(url)) {
|
||||||
switch (getUrlType(url)) {
|
case TRACKPOINTS -> TrackPointsColumns.TABLE_NAME;
|
||||||
case TRACKPOINTS:
|
case TRACKS -> TracksColumns.TABLE_NAME;
|
||||||
table = TrackPointsColumns.TABLE_NAME;
|
case MARKERS -> MarkerColumns.TABLE_NAME;
|
||||||
break;
|
default -> throw new IllegalArgumentException("Unknown URL " + url);
|
||||||
case TRACKS:
|
};
|
||||||
table = TracksColumns.TABLE_NAME;
|
|
||||||
break;
|
|
||||||
case MARKERS:
|
|
||||||
table = MarkerColumns.TABLE_NAME;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Unknown URL " + url);
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.w(TAG, "Deleting from table " + table);
|
Log.w(TAG, "Deleting from table " + table);
|
||||||
int totalChangesBefore = getTotalChanges();
|
int totalChangesBefore = getTotalChanges();
|
||||||
@@ -186,24 +178,15 @@ public class CustomContentProvider extends ContentProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getType(@NonNull Uri url) {
|
public String getType(@NonNull Uri url) {
|
||||||
switch (getUrlType(url)) {
|
return switch (getUrlType(url)) {
|
||||||
case TRACKPOINTS:
|
case TRACKPOINTS -> TrackPointsColumns.CONTENT_TYPE;
|
||||||
return TrackPointsColumns.CONTENT_TYPE;
|
case TRACKPOINTS_BY_ID, TRACKPOINTS_BY_TRACKID -> TrackPointsColumns.CONTENT_ITEMTYPE;
|
||||||
case TRACKPOINTS_BY_ID:
|
case TRACKS -> TracksColumns.CONTENT_TYPE;
|
||||||
case TRACKPOINTS_BY_TRACKID:
|
case TRACKS_BY_ID -> TracksColumns.CONTENT_ITEMTYPE;
|
||||||
return TrackPointsColumns.CONTENT_ITEMTYPE;
|
case MARKERS -> MarkerColumns.CONTENT_TYPE;
|
||||||
case TRACKS:
|
case MARKERS_BY_ID, MARKERS_BY_TRACKID -> MarkerColumns.CONTENT_ITEMTYPE;
|
||||||
return TracksColumns.CONTENT_TYPE;
|
default -> throw new IllegalArgumentException("Unknown URL " + url);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -251,47 +234,47 @@ public class CustomContentProvider extends ContentProvider {
|
|||||||
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
|
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
|
||||||
String sortOrder = null;
|
String sortOrder = null;
|
||||||
switch (getUrlType(url)) {
|
switch (getUrlType(url)) {
|
||||||
case TRACKPOINTS:
|
case TRACKPOINTS -> {
|
||||||
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
||||||
sortOrder = sort != null ? sort : TrackPointsColumns.DEFAULT_SORT_ORDER;
|
sortOrder = sort != null ? sort : TrackPointsColumns.DEFAULT_SORT_ORDER;
|
||||||
break;
|
}
|
||||||
case TRACKPOINTS_BY_ID:
|
case TRACKPOINTS_BY_ID -> {
|
||||||
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
||||||
queryBuilder.appendWhere(TrackPointsColumns._ID + "=" + ContentUris.parseId(url));
|
queryBuilder.appendWhere(TrackPointsColumns._ID + "=" + ContentUris.parseId(url));
|
||||||
break;
|
}
|
||||||
case TRACKPOINTS_BY_TRACKID:
|
case TRACKPOINTS_BY_TRACKID -> {
|
||||||
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
||||||
queryBuilder.appendWhere(TrackPointsColumns.TRACKID + " IN (" + TextUtils.join(SQL_LIST_DELIMITER, ContentProviderUtils.parseTrackIdsFromUri(url)) + ")");
|
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)) {
|
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)");
|
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 {
|
} else {
|
||||||
queryBuilder.setTables(TracksColumns.TABLE_NAME);
|
queryBuilder.setTables(TracksColumns.TABLE_NAME);
|
||||||
}
|
}
|
||||||
sortOrder = sort != null ? sort : TracksColumns.DEFAULT_SORT_ORDER;
|
sortOrder = sort != null ? sort : TracksColumns.DEFAULT_SORT_ORDER;
|
||||||
break;
|
}
|
||||||
case TRACKS_BY_ID:
|
case TRACKS_BY_ID -> {
|
||||||
queryBuilder.setTables(TracksColumns.TABLE_NAME);
|
queryBuilder.setTables(TracksColumns.TABLE_NAME);
|
||||||
queryBuilder.appendWhere(TracksColumns._ID + " IN (" + TextUtils.join(SQL_LIST_DELIMITER, ContentProviderUtils.parseTrackIdsFromUri(url)) + ")");
|
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);
|
long trackId = ContentUris.parseId(url);
|
||||||
return db.rawQuery(SENSOR_STATS_QUERY, new String[]{String.valueOf(trackId), String.valueOf(trackId)});
|
return db.rawQuery(SENSOR_STATS_QUERY, new String[]{String.valueOf(trackId), String.valueOf(trackId)});
|
||||||
case MARKERS:
|
}
|
||||||
|
case MARKERS -> {
|
||||||
queryBuilder.setTables(MarkerColumns.TABLE_NAME);
|
queryBuilder.setTables(MarkerColumns.TABLE_NAME);
|
||||||
sortOrder = sort != null ? sort : MarkerColumns.DEFAULT_SORT_ORDER;
|
sortOrder = sort != null ? sort : MarkerColumns.DEFAULT_SORT_ORDER;
|
||||||
break;
|
}
|
||||||
case MARKERS_BY_ID:
|
case MARKERS_BY_ID -> {
|
||||||
queryBuilder.setTables(MarkerColumns.TABLE_NAME);
|
queryBuilder.setTables(MarkerColumns.TABLE_NAME);
|
||||||
queryBuilder.appendWhere(MarkerColumns._ID + "=" + ContentUris.parseId(url));
|
queryBuilder.appendWhere(MarkerColumns._ID + "=" + ContentUris.parseId(url));
|
||||||
break;
|
}
|
||||||
case MARKERS_BY_TRACKID:
|
case MARKERS_BY_TRACKID -> {
|
||||||
queryBuilder.setTables(MarkerColumns.TABLE_NAME);
|
queryBuilder.setTables(MarkerColumns.TABLE_NAME);
|
||||||
queryBuilder.appendWhere(MarkerColumns.TRACKID + " IN (" + TextUtils.join(SQL_LIST_DELIMITER, ContentProviderUtils.parseTrackIdsFromUri(url)) + ")");
|
queryBuilder.appendWhere(MarkerColumns.TRACKID + " IN (" + TextUtils.join(SQL_LIST_DELIMITER, ContentProviderUtils.parseTrackIdsFromUri(url)) + ")");
|
||||||
break;
|
}
|
||||||
default:
|
default -> throw new IllegalArgumentException("Unknown url " + url);
|
||||||
throw new IllegalArgumentException("Unknown url " + url);
|
|
||||||
}
|
}
|
||||||
Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
|
Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
|
||||||
cursor.setNotificationUri(getContext().getContentResolver(), url);
|
cursor.setNotificationUri(getContext().getContentResolver(), url);
|
||||||
@@ -304,41 +287,40 @@ public class CustomContentProvider extends ContentProvider {
|
|||||||
String table;
|
String table;
|
||||||
String whereClause;
|
String whereClause;
|
||||||
switch (getUrlType(url)) {
|
switch (getUrlType(url)) {
|
||||||
case TRACKPOINTS:
|
case TRACKPOINTS -> {
|
||||||
table = TrackPointsColumns.TABLE_NAME;
|
table = TrackPointsColumns.TABLE_NAME;
|
||||||
whereClause = where;
|
whereClause = where;
|
||||||
break;
|
}
|
||||||
case TRACKPOINTS_BY_ID:
|
case TRACKPOINTS_BY_ID -> {
|
||||||
table = TrackPointsColumns.TABLE_NAME;
|
table = TrackPointsColumns.TABLE_NAME;
|
||||||
whereClause = TrackPointsColumns._ID + "=" + ContentUris.parseId(url);
|
whereClause = TrackPointsColumns._ID + "=" + ContentUris.parseId(url);
|
||||||
if (!TextUtils.isEmpty(where)) {
|
if (!TextUtils.isEmpty(where)) {
|
||||||
whereClause += " AND (" + where + ")";
|
whereClause += " AND (" + where + ")";
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TRACKS:
|
case TRACKS -> {
|
||||||
table = TracksColumns.TABLE_NAME;
|
table = TracksColumns.TABLE_NAME;
|
||||||
whereClause = where;
|
whereClause = where;
|
||||||
break;
|
}
|
||||||
case TRACKS_BY_ID:
|
case TRACKS_BY_ID -> {
|
||||||
table = TracksColumns.TABLE_NAME;
|
table = TracksColumns.TABLE_NAME;
|
||||||
whereClause = TracksColumns._ID + "=" + ContentUris.parseId(url);
|
whereClause = TracksColumns._ID + "=" + ContentUris.parseId(url);
|
||||||
if (!TextUtils.isEmpty(where)) {
|
if (!TextUtils.isEmpty(where)) {
|
||||||
whereClause += " AND (" + where + ")";
|
whereClause += " AND (" + where + ")";
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case MARKERS:
|
case MARKERS -> {
|
||||||
table = MarkerColumns.TABLE_NAME;
|
table = MarkerColumns.TABLE_NAME;
|
||||||
whereClause = where;
|
whereClause = where;
|
||||||
break;
|
}
|
||||||
case MARKERS_BY_ID:
|
case MARKERS_BY_ID -> {
|
||||||
table = MarkerColumns.TABLE_NAME;
|
table = MarkerColumns.TABLE_NAME;
|
||||||
whereClause = MarkerColumns._ID + "=" + ContentUris.parseId(url);
|
whereClause = MarkerColumns._ID + "=" + ContentUris.parseId(url);
|
||||||
if (!TextUtils.isEmpty(where)) {
|
if (!TextUtils.isEmpty(where)) {
|
||||||
whereClause += " AND (" + where + ")";
|
whereClause += " AND (" + where + ")";
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
default:
|
default -> throw new IllegalArgumentException("Unknown url " + url);
|
||||||
throw new IllegalArgumentException("Unknown url " + url);
|
|
||||||
}
|
}
|
||||||
int count;
|
int count;
|
||||||
try {
|
try {
|
||||||
@@ -371,16 +353,12 @@ public class CustomContentProvider extends ContentProvider {
|
|||||||
* @param contentValues the content values
|
* @param contentValues the content values
|
||||||
*/
|
*/
|
||||||
private Uri insertContentValues(Uri url, UrlType urlType, ContentValues contentValues) {
|
private Uri insertContentValues(Uri url, UrlType urlType, ContentValues contentValues) {
|
||||||
switch (urlType) {
|
return switch (urlType) {
|
||||||
case TRACKPOINTS:
|
case TRACKPOINTS -> insertTrackPoint(url, contentValues);
|
||||||
return insertTrackPoint(url, contentValues);
|
case TRACKS -> insertTrack(url, contentValues);
|
||||||
case TRACKS:
|
case MARKERS -> insertMarker(url, contentValues);
|
||||||
return insertTrack(url, contentValues);
|
default -> throw new IllegalArgumentException("Unknown url " + url);
|
||||||
case MARKERS:
|
};
|
||||||
return insertMarker(url, contentValues);
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Unknown url " + url);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Uri insertTrackPoint(Uri url, ContentValues values) {
|
private Uri insertTrackPoint(Uri url, ContentValues values) {
|
||||||
|
|||||||
@@ -61,42 +61,18 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
|||||||
for (int toVersion = oldVersion + 1; toVersion <= newVersion; toVersion++) {
|
for (int toVersion = oldVersion + 1; toVersion <= newVersion; toVersion++) {
|
||||||
Log.i(TAG, "Upgrade from " + oldVersion + " to " + toVersion);
|
Log.i(TAG, "Upgrade from " + oldVersion + " to " + toVersion);
|
||||||
switch (toVersion) {
|
switch (toVersion) {
|
||||||
case 24:
|
case 24 -> upgradeFrom23to24(db);
|
||||||
upgradeFrom23to24(db);
|
case 25 -> upgradeFrom24to25(db);
|
||||||
break;
|
case 26 -> upgradeFrom25to26(db);
|
||||||
case 25:
|
case 27 -> upgradeFrom26to27(db);
|
||||||
upgradeFrom24to25(db);
|
case 28 -> upgradeFrom27to28(db);
|
||||||
break;
|
case 29 -> upgradeFrom28to29(db);
|
||||||
case 26:
|
case 30 -> upgradeFrom29to30(db);
|
||||||
upgradeFrom25to26(db);
|
case 31 -> upgradeFrom30to31(db);
|
||||||
break;
|
case 32 -> upgradeFrom31to32(db);
|
||||||
case 27:
|
case 33 -> upgradeFrom32to33(db);
|
||||||
upgradeFrom26to27(db);
|
case 34 -> upgradeFrom33to34(db);
|
||||||
break;
|
default -> throw new RuntimeException("Not implemented: upgrade to " + toVersion);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,41 +82,18 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
|||||||
for (int toVersion = oldVersion - 1; toVersion >= newVersion; toVersion--) {
|
for (int toVersion = oldVersion - 1; toVersion >= newVersion; toVersion--) {
|
||||||
Log.i(TAG, "Downgrade from " + oldVersion + " to " + toVersion);
|
Log.i(TAG, "Downgrade from " + oldVersion + " to " + toVersion);
|
||||||
switch (toVersion) {
|
switch (toVersion) {
|
||||||
case 23:
|
case 23 -> downgradeFrom24to23(db);
|
||||||
downgradeFrom24to23(db);
|
case 24 -> downgradeFrom25to24(db);
|
||||||
break;
|
case 25 -> downgradeFrom26to25(db);
|
||||||
case 24:
|
case 26 -> downgradeFrom27to26(db);
|
||||||
downgradeFrom25to24(db);
|
case 27 -> downgradeFrom28to27(db);
|
||||||
break;
|
case 28 -> downgradeFrom29to28(db);
|
||||||
case 25:
|
case 29 -> downgradeFrom30to29(db);
|
||||||
downgradeFrom26to25(db);
|
case 30 -> downgradeFrom31to30(db);
|
||||||
break;
|
case 31 -> downgradeFrom32to31(db);
|
||||||
case 26:
|
case 32 -> downgradeFrom33to32(db);
|
||||||
downgradeFrom27to26(db);
|
case 33 -> downgradeFrom34to33(db);
|
||||||
break;
|
default -> throw new RuntimeException("Not implemented: downgrade to " + toVersion);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,24 +129,18 @@ public class ShareContentProvider extends CustomContentProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static TrackFileFormat getTrackFileFormat(@NonNull Uri uri) {
|
private static TrackFileFormat getTrackFileFormat(@NonNull Uri uri) {
|
||||||
switch (uriMatcher.match(uri)) {
|
return switch (uriMatcher.match(uri)) {
|
||||||
case URI_GPX:
|
case URI_GPX -> TrackFileFormat.GPX;
|
||||||
return TrackFileFormat.GPX;
|
case URI_KML_WITH_TRACKDETAIL_SENSORDATA ->
|
||||||
|
TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA;
|
||||||
case URI_KML_WITH_TRACKDETAIL_SENSORDATA:
|
case URI_KMZ_WITH_TRACKDETAIL_AND_SENSORDATA ->
|
||||||
return TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA;
|
TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA;
|
||||||
|
case URI_KMZ_WITH_TRACKDETAIL_SENSORDATA_AND_PICTURES ->
|
||||||
case URI_KMZ_WITH_TRACKDETAIL_AND_SENSORDATA:
|
TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES;
|
||||||
return TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA;
|
case URI_CSV -> TrackFileFormat.CSV;
|
||||||
case URI_KMZ_WITH_TRACKDETAIL_SENSORDATA_AND_PICTURES:
|
default ->
|
||||||
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);
|
throw new RuntimeException("Could not derive TrackFileFormat from Uri " + uri);
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -170,14 +164,14 @@ public class ShareContentProvider extends CustomContentProvider {
|
|||||||
int i = 0;
|
int i = 0;
|
||||||
for (String col : projection) {
|
for (String col : projection) {
|
||||||
switch (col) {
|
switch (col) {
|
||||||
case OpenableColumns.DISPLAY_NAME:
|
case OpenableColumns.DISPLAY_NAME -> {
|
||||||
cols[i] = OpenableColumns.DISPLAY_NAME;
|
cols[i] = OpenableColumns.DISPLAY_NAME;
|
||||||
values[i++] = uri.getLastPathSegment();
|
values[i++] = uri.getLastPathSegment();
|
||||||
break;
|
}
|
||||||
case OpenableColumns.SIZE:
|
case OpenableColumns.SIZE -> {
|
||||||
cols[i] = 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.
|
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,7 +208,7 @@ 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) -> {
|
PipeDataWriter<String> pipeDataWriter = (output, uri1, mimeType, opts, args) -> {
|
||||||
try (FileOutputStream fileOutputStream = new FileOutputStream(output.getFileDescriptor())) {
|
try (FileOutputStream fileOutputStream = new FileOutputStream(output.getFileDescriptor())) {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -58,16 +58,11 @@ public class Distance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Distance one(UnitSystem unitSystem) {
|
public static Distance one(UnitSystem unitSystem) {
|
||||||
switch (unitSystem) {
|
return switch (unitSystem) {
|
||||||
case METRIC:
|
case METRIC -> Distance.ofKilometer(1);
|
||||||
return Distance.ofKilometer(1);
|
case IMPERIAL -> Distance.ofMile(1);
|
||||||
case IMPERIAL:
|
case NAUTICAL_IMPERIAL -> Distance.ofNauticalMile(1);
|
||||||
return Distance.ofMile(1);
|
};
|
||||||
case NAUTICAL_IMPERIAL:
|
|
||||||
return Distance.ofNauticalMile(1);
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Not implemented");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final double distance_m;
|
private final double distance_m;
|
||||||
@@ -133,28 +128,18 @@ public class Distance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public double toKM_Miles(UnitSystem unitSystem) {
|
public double toKM_Miles(UnitSystem unitSystem) {
|
||||||
switch (unitSystem) {
|
return switch (unitSystem) {
|
||||||
case METRIC:
|
case METRIC -> toKM();
|
||||||
return toKM();
|
case IMPERIAL -> toMI();
|
||||||
case IMPERIAL:
|
case NAUTICAL_IMPERIAL -> toNauticalMiles();
|
||||||
return toMI();
|
};
|
||||||
case NAUTICAL_IMPERIAL:
|
|
||||||
return toNauticalMiles();
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Not implemented");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public double toM_FT(UnitSystem unitSystem) {
|
public double toM_FT(UnitSystem unitSystem) {
|
||||||
switch (unitSystem) {
|
return switch (unitSystem) {
|
||||||
case METRIC:
|
case METRIC -> toM();
|
||||||
return toM();
|
case NAUTICAL_IMPERIAL, IMPERIAL -> toFT();
|
||||||
case NAUTICAL_IMPERIAL:
|
};
|
||||||
case IMPERIAL:
|
|
||||||
return toFT();
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Not implemented");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -46,38 +46,36 @@ public class DistanceFormatter {
|
|||||||
public Pair<String, String> getDistanceParts(Distance distance) {
|
public Pair<String, String> getDistanceParts(Distance distance) {
|
||||||
if (distance.isInvalid()) {
|
if (distance.isInvalid()) {
|
||||||
String valueUnknown = resources.getString(R.string.value_unknown);
|
String valueUnknown = resources.getString(R.string.value_unknown);
|
||||||
switch (unitSystem) {
|
return switch (unitSystem) {
|
||||||
case METRIC:
|
case METRIC -> new Pair<>(valueUnknown, resources.getString(R.string.unit_meter));
|
||||||
return new Pair<>(valueUnknown, resources.getString(R.string.unit_meter));
|
case IMPERIAL, NAUTICAL_IMPERIAL ->
|
||||||
case IMPERIAL:
|
new Pair<>(valueUnknown, resources.getString(R.string.unit_feet));
|
||||||
case NAUTICAL_IMPERIAL:
|
};
|
||||||
return new Pair<>(valueUnknown, resources.getString(R.string.unit_feet));
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Not implemented");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (unitSystem) {
|
switch (unitSystem) {
|
||||||
case METRIC:
|
case METRIC -> {
|
||||||
if (distance.greaterThan(Distance.ofKilometer(threshold))) {
|
if (distance.greaterThan(Distance.ofKilometer(threshold))) {
|
||||||
return new Pair<>(StringUtils.formatDecimal(distance.toKM(), decimalCount), resources.getString(R.string.unit_kilometer));
|
return new Pair<>(StringUtils.formatDecimal(distance.toKM(), decimalCount), resources.getString(R.string.unit_kilometer));
|
||||||
} else {
|
} else {
|
||||||
return new Pair<>(StringUtils.formatDecimal(distance.toM(), decimalCount), resources.getString(R.string.unit_meter));
|
return new Pair<>(StringUtils.formatDecimal(distance.toM(), decimalCount), resources.getString(R.string.unit_meter));
|
||||||
}
|
}
|
||||||
case IMPERIAL:
|
}
|
||||||
|
case IMPERIAL -> {
|
||||||
if (distance.greaterThan(Distance.ofMile(threshold))) {
|
if (distance.greaterThan(Distance.ofMile(threshold))) {
|
||||||
return new Pair<>(StringUtils.formatDecimal(distance.toMI(), decimalCount), resources.getString(R.string.unit_mile));
|
return new Pair<>(StringUtils.formatDecimal(distance.toMI(), decimalCount), resources.getString(R.string.unit_mile));
|
||||||
} else {
|
} else {
|
||||||
return new Pair<>(StringUtils.formatDecimal(distance.toFT(), decimalCount), resources.getString(R.string.unit_feet));
|
return new Pair<>(StringUtils.formatDecimal(distance.toFT(), decimalCount), resources.getString(R.string.unit_feet));
|
||||||
}
|
}
|
||||||
case NAUTICAL_IMPERIAL:
|
}
|
||||||
|
case NAUTICAL_IMPERIAL -> {
|
||||||
if (distance.greaterThan(Distance.ofNauticalMile(threshold))) {
|
if (distance.greaterThan(Distance.ofNauticalMile(threshold))) {
|
||||||
return new Pair<>(StringUtils.formatDecimal(distance.toNauticalMiles(), decimalCount), resources.getString(R.string.unit_nautical_mile));
|
return new Pair<>(StringUtils.formatDecimal(distance.toNauticalMiles(), decimalCount), resources.getString(R.string.unit_nautical_mile));
|
||||||
} else {
|
} else {
|
||||||
return new Pair<>(StringUtils.formatDecimal(distance.toFT(), decimalCount), resources.getString(R.string.unit_feet));
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -115,16 +115,11 @@ public class Speed {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public double to(UnitSystem unitSystem) {
|
public double to(UnitSystem unitSystem) {
|
||||||
switch (unitSystem) {
|
return switch (unitSystem) {
|
||||||
case METRIC:
|
case METRIC -> toKMH();
|
||||||
return toKMH();
|
case IMPERIAL -> toMPH();
|
||||||
case IMPERIAL:
|
case NAUTICAL_IMPERIAL -> toKnots();
|
||||||
return toMPH();
|
};
|
||||||
case NAUTICAL_IMPERIAL:
|
|
||||||
return toKnots();
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Not implemented");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -39,20 +39,14 @@ public class SpeedFormatter {
|
|||||||
* @return the formatted speed (or null) and it's unit as {@link Pair}
|
* @return the formatted speed (or null) and it's unit as {@link Pair}
|
||||||
*/
|
*/
|
||||||
public Pair<String, String> getSpeedParts(Speed speed) {
|
public Pair<String, String> getSpeedParts(Speed speed) {
|
||||||
int unitId;
|
int unitId = switch (unitSystem) {
|
||||||
switch (unitSystem) {
|
case METRIC ->
|
||||||
case METRIC:
|
reportSpeedOrPace ? R.string.unit_kilometer_per_hour : R.string.unit_minute_per_kilometer;
|
||||||
unitId = reportSpeedOrPace ? R.string.unit_kilometer_per_hour : R.string.unit_minute_per_kilometer;
|
case IMPERIAL ->
|
||||||
break;
|
reportSpeedOrPace ? R.string.unit_mile_per_hour : R.string.unit_minute_per_mile;
|
||||||
case IMPERIAL:
|
case NAUTICAL_IMPERIAL ->
|
||||||
unitId = reportSpeedOrPace ? R.string.unit_mile_per_hour : R.string.unit_minute_per_mile;
|
reportSpeedOrPace ? R.string.unit_knots : R.string.unit_minute_per_nautical_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");
|
|
||||||
}
|
|
||||||
|
|
||||||
String unitString = resources.getString(unitId);
|
String unitString = resources.getString(unitId);
|
||||||
|
|
||||||
|
|||||||
@@ -385,10 +385,6 @@ public class TrackPoint {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasSensorData() {
|
|
||||||
return hasHeartRate() || hasCadence() || hasPower();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasHeartRate() {
|
public boolean hasHeartRate() {
|
||||||
return heartRate != null;
|
return heartRate != null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,11 +31,8 @@ import androidx.fragment.app.Fragment;
|
|||||||
import androidx.recyclerview.widget.GridLayoutManager;
|
import androidx.recyclerview.widget.GridLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.TrackRecordedActivity;
|
import de.dennisguse.opentracks.TrackRecordedActivity;
|
||||||
import de.dennisguse.opentracks.adapters.StatisticsAdapter;
|
|
||||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.data.models.DistanceFormatter;
|
import de.dennisguse.opentracks.data.models.DistanceFormatter;
|
||||||
import de.dennisguse.opentracks.data.models.SpeedFormatter;
|
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.stats.TrackStatistics;
|
||||||
import de.dennisguse.opentracks.util.StringUtils;
|
import de.dennisguse.opentracks.util.StringUtils;
|
||||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
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}.
|
* 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 SensorStatistics sensorStatistics;
|
||||||
private StatisticsAdapter.WithRecordedLayout sensorsAdapter;
|
|
||||||
|
|
||||||
private Track.Id trackId;
|
private Track.Id trackId;
|
||||||
@Nullable // Lazily loaded.
|
@Nullable // Lazily loaded.
|
||||||
@@ -113,8 +107,6 @@ public class StatisticsRecordedFragment extends Fragment {
|
|||||||
|
|
||||||
trackId = getArguments().getParcelable(TRACK_ID_KEY);
|
trackId = getArguments().getParcelable(TRACK_ID_KEY);
|
||||||
contentProviderUtils = new ContentProviderUtils(getContext());
|
contentProviderUtils = new ContentProviderUtils(getContext());
|
||||||
|
|
||||||
sensorsAdapter = new StatisticsAdapter.WithRecordedLayout(getContext());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -123,7 +115,7 @@ public class StatisticsRecordedFragment extends Fragment {
|
|||||||
|
|
||||||
RecyclerView sensorsRecyclerView = viewBinding.statsSensorsRecyclerView;
|
RecyclerView sensorsRecyclerView = viewBinding.statsSensorsRecyclerView;
|
||||||
sensorsRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2));
|
sensorsRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2));
|
||||||
sensorsRecyclerView.setAdapter(sensorsAdapter);
|
// sensorsRecyclerView.setAdapter(sensorsAdapter);
|
||||||
|
|
||||||
return viewBinding.getRoot();
|
return viewBinding.getRoot();
|
||||||
}
|
}
|
||||||
@@ -261,10 +253,6 @@ public class StatisticsRecordedFragment extends Fragment {
|
|||||||
if (sensorStatistics == null) {
|
if (sensorStatistics == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
//TODO
|
||||||
List<StatisticData> sensorDataList = StatisticDataBuilder.fromSensorStatistics(getContext(), sensorStatistics);
|
|
||||||
if (sensorDataList.size() > 0) {
|
|
||||||
sensorsAdapter.swapData(sensorDataList);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package de.dennisguse.opentracks.fragments;
|
|||||||
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.view.Gravity;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -9,16 +10,14 @@ import android.view.ViewGroup;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.lifecycle.LiveData;
|
import androidx.gridlayout.widget.GridLayout;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
|
||||||
import androidx.recyclerview.widget.GridLayoutManager;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.adapters.StatisticsAdapter;
|
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
import de.dennisguse.opentracks.databinding.StatisticsRecordingBinding;
|
import de.dennisguse.opentracks.databinding.StatisticsRecordingBinding;
|
||||||
import de.dennisguse.opentracks.services.RecordingData;
|
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.services.TrackRecordingServiceConnection;
|
||||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.Layout;
|
import de.dennisguse.opentracks.ui.customRecordingLayout.DataField;
|
||||||
import de.dennisguse.opentracks.viewmodels.StatisticData;
|
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayout;
|
||||||
import de.dennisguse.opentracks.viewmodels.StatisticsDataModel;
|
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}.
|
* 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 RecordingData recordingData = TrackRecordingService.NOT_RECORDING;
|
||||||
|
|
||||||
private Layout layout;
|
private final List<StatisticViewHolder<?>> viewHolders = new LinkedList<>();
|
||||||
|
|
||||||
|
private RecordingLayout recordingLayout;
|
||||||
|
|
||||||
private StatisticsRecordingBinding viewBinding;
|
private StatisticsRecordingBinding viewBinding;
|
||||||
private StatisticsAdapter statisticsAdapter;
|
|
||||||
private GridLayoutManager gridLayoutManager;
|
|
||||||
private StatisticsDataModel viewModel;
|
|
||||||
private LiveData<List<StatisticData>> statisticsLiveData;
|
|
||||||
|
|
||||||
private UnitSystem unitSystem = UnitSystem.defaultUnitSystem();
|
private UnitSystem unitSystem = UnitSystem.defaultUnitSystem();
|
||||||
|
|
||||||
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
|
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
|
||||||
boolean updateUInecessary = false;
|
|
||||||
|
|
||||||
if (PreferencesUtils.isKey(R.string.stats_units_key, key)) {
|
if (PreferencesUtils.isKey(R.string.stats_units_key, key)) {
|
||||||
updateUInecessary = true;
|
|
||||||
unitSystem = PreferencesUtils.getUnitSystem();
|
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)) {
|
if (PreferencesUtils.isKey(R.string.stats_custom_layouts_key, key) || PreferencesUtils.isKey(R.string.stats_custom_layout_selected_layout_key, key)) {
|
||||||
updateUInecessary = true;
|
onLayoutChanged(PreferencesUtils.getCustomLayout());
|
||||||
layout = PreferencesUtils.getCustomLayout();
|
|
||||||
gridLayoutManager.setSpanCount(layout.getColumnsPerRow());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key != null && updateUInecessary && isResumed()) {
|
|
||||||
getActivity().runOnUiThread(this::updateUI);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -84,30 +74,11 @@ public class StatisticsRecordingFragment extends Fragment {
|
|||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback);
|
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback);
|
||||||
|
|
||||||
statisticsAdapter = new StatisticsAdapter(getContext());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
viewBinding = StatisticsRecordingBinding.inflate(inflater, container, false);
|
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();
|
return viewBinding.getRoot();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,10 +88,6 @@ public class StatisticsRecordingFragment extends Fragment {
|
|||||||
|
|
||||||
PreferencesUtils.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
PreferencesUtils.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||||
|
|
||||||
viewModel = new ViewModelProvider(getActivity()).get(StatisticsDataModel.class);
|
|
||||||
statisticsLiveData = viewModel.getStatsData();
|
|
||||||
statisticsLiveData.observe(getActivity(), statsDataList -> statisticsAdapter.swapData(statsDataList));
|
|
||||||
|
|
||||||
trackRecordingServiceConnection.startConnection(getContext());
|
trackRecordingServiceConnection.startConnection(getContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,6 +106,7 @@ public class StatisticsRecordingFragment extends Fragment {
|
|||||||
@Override
|
@Override
|
||||||
public void onDestroyView() {
|
public void onDestroyView() {
|
||||||
super.onDestroyView();
|
super.onDestroyView();
|
||||||
|
viewHolders.clear();
|
||||||
viewBinding = null;
|
viewBinding = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,17 +114,54 @@ public class StatisticsRecordingFragment extends Fragment {
|
|||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
trackRecordingServiceConnection = null;
|
trackRecordingServiceConnection = null;
|
||||||
viewModel = null;
|
|
||||||
|
|
||||||
if (statisticsLiveData != null) {
|
|
||||||
statisticsLiveData.removeObservers(getActivity());
|
|
||||||
}
|
|
||||||
statisticsLiveData = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateUI() {
|
private void onLayoutChanged(@NonNull RecordingLayout newRecordingLayout) {
|
||||||
if (isResumed()) {
|
if (newRecordingLayout.equals(recordingLayout)) {
|
||||||
viewModel.update(recordingData, layout, unitSystem);
|
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));
|
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, getString(R.string.stats_rate_key));
|
||||||
}
|
}
|
||||||
|
|
||||||
updateUI();
|
updateDataOnUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDataOnUI() {
|
||||||
|
if (isResumed()) {
|
||||||
|
viewHolders.forEach(i -> i.onChanged(unitSystem, recordingData));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package de.dennisguse.opentracks.io.file;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@@ -25,8 +27,8 @@ public enum TrackFileFormat {
|
|||||||
|
|
||||||
KML_WITH_TRACKDETAIL_AND_SENSORDATA("KML_WITH_TRACKDETAIL_AND_SENSORDATA") {
|
KML_WITH_TRACKDETAIL_AND_SENSORDATA("KML_WITH_TRACKDETAIL_AND_SENSORDATA") {
|
||||||
@Override
|
@Override
|
||||||
public TrackExporter createTrackExporter(Context context) {
|
public TrackExporter createTrackExporter(@NonNull Context context, @NonNull ContentProviderUtils contentProviderUtils) {
|
||||||
return new KMLTrackExporter(context, false);
|
return new KMLTrackExporter(context, contentProviderUtils, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -45,9 +47,9 @@ public enum TrackFileFormat {
|
|||||||
private static final boolean exportPhotos = false;
|
private static final boolean exportPhotos = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TrackExporter createTrackExporter(Context context) {
|
public TrackExporter createTrackExporter(@NonNull Context context, @NonNull ContentProviderUtils contentProviderUtils) {
|
||||||
KMLTrackExporter exporter = new KMLTrackExporter(context, exportPhotos);
|
KMLTrackExporter exporter = new KMLTrackExporter(context, contentProviderUtils, exportPhotos);
|
||||||
return new KmzTrackExporter(context, new ContentProviderUtils(context), exporter, exportPhotos);
|
return new KmzTrackExporter(context, contentProviderUtils, exporter, exportPhotos);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -70,9 +72,9 @@ public enum TrackFileFormat {
|
|||||||
private static final boolean exportPhotos = true;
|
private static final boolean exportPhotos = true;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TrackExporter createTrackExporter(Context context) {
|
public TrackExporter createTrackExporter(@NonNull Context context, @NonNull ContentProviderUtils contentProviderUtils) {
|
||||||
KMLTrackExporter exporter = new KMLTrackExporter(context, exportPhotos);
|
KMLTrackExporter exporter = new KMLTrackExporter(context, contentProviderUtils, exportPhotos);
|
||||||
return new KmzTrackExporter(context, new ContentProviderUtils(context), exporter, exportPhotos);
|
return new KmzTrackExporter(context, contentProviderUtils, exporter, exportPhotos);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -93,8 +95,8 @@ public enum TrackFileFormat {
|
|||||||
|
|
||||||
GPX("GPX") {
|
GPX("GPX") {
|
||||||
@Override
|
@Override
|
||||||
public TrackExporter createTrackExporter(Context context) {
|
public TrackExporter createTrackExporter(@NonNull Context context, @NonNull ContentProviderUtils contentProviderUtils) {
|
||||||
return new GPXTrackExporter(new ContentProviderUtils(context), context.getString(R.string.app_name));
|
return new GPXTrackExporter(contentProviderUtils, context.getString(R.string.app_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -109,8 +111,8 @@ public enum TrackFileFormat {
|
|||||||
|
|
||||||
CSV("CSV") {
|
CSV("CSV") {
|
||||||
@Override
|
@Override
|
||||||
public TrackExporter createTrackExporter(Context context) {
|
public TrackExporter createTrackExporter(@NonNull Context context, @NonNull ContentProviderUtils contentProviderUtils) {
|
||||||
return new CSVTrackExporter(new ContentProviderUtils(context));
|
return new CSVTrackExporter(contentProviderUtils);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -161,7 +163,7 @@ public enum TrackFileFormat {
|
|||||||
*
|
*
|
||||||
* @param context the context
|
* @param context the context
|
||||||
*/
|
*/
|
||||||
public abstract TrackExporter createTrackExporter(Context context);
|
public abstract TrackExporter createTrackExporter(@NonNull Context context, @NonNull ContentProviderUtils contentProviderUtils);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the file extension for each format.
|
* Returns the file extension for each format.
|
||||||
|
|||||||
@@ -93,23 +93,9 @@ public class CSVTrackExporter implements TrackExporter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean writeTrack(Track[] tracks, @NonNull OutputStream outputStream) {
|
public boolean writeTrack(Track[] tracks, @NonNull OutputStream outputStream) {
|
||||||
List<Column> columns = Collections.unmodifiableList(Arrays.asList(
|
List<Column> columns = Collections.unmodifiableList(Arrays.asList(new Column("time", null), new Column("trackpoint_type", t -> quote(t.getType().name())), new Column("latitude", t -> t.hasLocation() ? COORDINATE_FORMAT.format(t.getLatitude()) : ""), new Column("longitude", t -> t.hasLocation() ? COORDINATE_FORMAT.format(t.getLongitude()) : ""), new Column("altitude", t -> t.hasAltitude() ? COORDINATE_FORMAT.format(t.getAltitude().toM()) : ""), new Column("accuracy_horizontal", t -> t.hasHorizontalAccuracy() ? DISTANCE_FORMAT.format(t.getHorizontalAccuracy().toM()) : ""), new Column("accuracy_vertical", t -> t.hasVerticalAccuracy() ? DISTANCE_FORMAT.format(t.getVerticalAccuracy().toM()) : ""),
|
||||||
new Column("time", null),
|
|
||||||
new Column("trackpoint_type", t -> quote(t.getType().name())),
|
|
||||||
new Column("latitude", t -> t.hasLocation() ? COORDINATE_FORMAT.format(t.getLatitude()) : ""),
|
|
||||||
new Column("longitude", t -> t.hasLocation() ? COORDINATE_FORMAT.format(t.getLongitude()) : ""),
|
|
||||||
new Column("altitude", t -> t.hasAltitude() ? COORDINATE_FORMAT.format(t.getAltitude().toM()) : ""),
|
|
||||||
new Column("accuracy_horizontal", t -> t.hasHorizontalAccuracy() ? DISTANCE_FORMAT.format(t.getHorizontalAccuracy().toM()) : ""),
|
|
||||||
new Column("accuracy_vertical", t -> t.hasVerticalAccuracy() ? DISTANCE_FORMAT.format(t.getVerticalAccuracy().toM()) : ""),
|
|
||||||
|
|
||||||
new Column("speed", t -> t.hasSpeed() ? SPEED_FORMAT.format(t.getSpeed().toKMH()) : ""),
|
new Column("speed", t -> t.hasSpeed() ? SPEED_FORMAT.format(t.getSpeed().toKMH()) : ""), new Column("altitude_gain", t -> t.hasAltitudeGain() ? DISTANCE_FORMAT.format(t.getAltitudeGain()) : ""), new Column("altitude_loss", t -> t.hasAltitudeLoss() ? DISTANCE_FORMAT.format(t.getAltitudeLoss()) : ""), new Column("sensor_distance", t -> t.hasSensorDistance() ? DISTANCE_FORMAT.format(t.getSensorDistance().toM()) : ""), new Column("heartrate", t -> t.hasHeartRate() ? HEARTRATE_FORMAT.format(t.getHeartRate().getBPM()) : ""), new Column("cadence", t -> t.hasCadence() ? CADENCE_FORMAT.format(t.getCadence().getRPM()) : ""), new Column("power", t -> t.hasPower() ? ALTITUDE_FORMAT.format(t.getPower().getW()) : "")));
|
||||||
new Column("altitude_gain", t -> t.hasAltitudeGain() ? DISTANCE_FORMAT.format(t.getAltitudeGain()) : ""),
|
|
||||||
new Column("altitude_loss", t -> t.hasAltitudeLoss() ? DISTANCE_FORMAT.format(t.getAltitudeLoss()) : ""),
|
|
||||||
new Column("sensor_distance", t -> t.hasSensorDistance() ? DISTANCE_FORMAT.format(t.getSensorDistance().toM()) : ""),
|
|
||||||
new Column("heartrate", t -> t.hasHeartRate() ? HEARTRATE_FORMAT.format(t.getHeartRate().getBPM()) : ""),
|
|
||||||
new Column("cadence", t -> t.hasCadence() ? CADENCE_FORMAT.format(t.getCadence().getRPM()) : ""),
|
|
||||||
new Column("power", t -> t.hasPower() ? ALTITUDE_FORMAT.format(t.getPower().getW()) : "")
|
|
||||||
));
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
prepare(outputStream);
|
prepare(outputStream);
|
||||||
@@ -144,14 +130,9 @@ public class CSVTrackExporter implements TrackExporter {
|
|||||||
TrackPoint trackPoint = trackPointIterator.next();
|
TrackPoint trackPoint = trackPointIterator.next();
|
||||||
|
|
||||||
switch (trackPoint.getType()) {
|
switch (trackPoint.getType()) {
|
||||||
case SEGMENT_START_MANUAL:
|
case SEGMENT_START_MANUAL, SEGMENT_END_MANUAL, SEGMENT_START_AUTOMATIC, SENSORPOINT, TRACKPOINT ->
|
||||||
case SEGMENT_END_MANUAL:
|
|
||||||
case SEGMENT_START_AUTOMATIC:
|
|
||||||
case SENSORPOINT:
|
|
||||||
case TRACKPOINT:
|
|
||||||
writeTrackPoint(columns, trackPoint);
|
writeTrackPoint(columns, trackPoint);
|
||||||
break;
|
default ->
|
||||||
default:
|
|
||||||
throw new RuntimeException("Exporting this TrackPoint type is not implemented: " + trackPoint.getType());
|
throw new RuntimeException("Exporting this TrackPoint type is not implemented: " + trackPoint.getType());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -171,18 +152,14 @@ public class CSVTrackExporter implements TrackExporter {
|
|||||||
|
|
||||||
public void writeHeader(List<Column> columns) {
|
public void writeHeader(List<Column> columns) {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
String columnNames = columns.stream().map(c -> c.columnName)
|
String columnNames = columns.stream().map(c -> c.columnName).reduce((s, s2) -> s + "," + s2).orElseThrow(() -> new RuntimeException("No columns defined"));
|
||||||
.reduce((s, s2) -> s + "," + s2)
|
|
||||||
.orElseThrow(() -> new RuntimeException("No columns defined"));
|
|
||||||
printWriter.println("#" + columnNames);
|
printWriter.println("#" + columnNames);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeTrackPoint(List<Column> columns, TrackPoint trackPoint) {
|
public void writeTrackPoint(List<Column> columns, TrackPoint trackPoint) {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
String columnNames = columns.stream().map(c -> c.extractor.apply(trackPoint))
|
String columnNames = columns.stream().map(c -> c.extractor.apply(trackPoint)).reduce((s, s2) -> s + "," + s2).orElseThrow(() -> new RuntimeException("No columns defined"));
|
||||||
.reduce((s, s2) -> s + "," + s2)
|
|
||||||
.orElseThrow(() -> new RuntimeException("No columns defined"));
|
|
||||||
printWriter.println(columnNames);
|
printWriter.println(columnNames);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-8
@@ -30,14 +30,9 @@ public class ExportServiceResultReceiver extends ResultReceiver {
|
|||||||
protected void onReceiveResult(int resultCode, Bundle resultData) {
|
protected void onReceiveResult(int resultCode, Bundle resultData) {
|
||||||
Track.Id trackId = resultData.getParcelable(ExportServiceResultReceiver.RESULT_EXTRA_TRACK_ID);
|
Track.Id trackId = resultData.getParcelable(ExportServiceResultReceiver.RESULT_EXTRA_TRACK_ID);
|
||||||
switch (resultCode) {
|
switch (resultCode) {
|
||||||
case RESULT_CODE_SUCCESS:
|
case RESULT_CODE_SUCCESS -> receiver.onExportSuccess(trackId);
|
||||||
receiver.onExportSuccess(trackId);
|
case RESULT_CODE_ERROR -> receiver.onExportError(trackId);
|
||||||
break;
|
default -> throw new RuntimeException("Unknown resultCode.");
|
||||||
case RESULT_CODE_ERROR:
|
|
||||||
receiver.onExportError(trackId);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Unknown resultCode.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -145,36 +145,31 @@ public class GPXTrackExporter implements TrackExporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (trackPoint.getType()) {
|
switch (trackPoint.getType()) {
|
||||||
case SEGMENT_START_MANUAL:
|
case SEGMENT_START_MANUAL ->
|
||||||
Log.i(TAG, "Exporting " + TrackPoint.Type.SEGMENT_START_MANUAL.name() + " is not supported.");
|
Log.i(TAG, "Exporting " + TrackPoint.Type.SEGMENT_START_MANUAL.name() + " is not supported.");
|
||||||
break;
|
case SEGMENT_END_MANUAL -> {
|
||||||
case SEGMENT_END_MANUAL:
|
|
||||||
if (wroteSegment) writeCloseSegment();
|
if (wroteSegment) writeCloseSegment();
|
||||||
wroteSegment = false;
|
wroteSegment = false;
|
||||||
Log.i(TAG, "Exporting " + TrackPoint.Type.SEGMENT_END_MANUAL.name() + " is not supported.");
|
Log.i(TAG, "Exporting " + TrackPoint.Type.SEGMENT_END_MANUAL.name() + " is not supported.");
|
||||||
break;
|
}
|
||||||
case SEGMENT_START_AUTOMATIC:
|
case SEGMENT_START_AUTOMATIC -> {
|
||||||
if (wroteSegment) writeCloseSegment();
|
if (wroteSegment) writeCloseSegment();
|
||||||
writeOpenSegment();
|
writeOpenSegment();
|
||||||
wroteSegment = true;
|
wroteSegment = true;
|
||||||
|
|
||||||
trackDistance = trackDistance.plus(writeTrackPoint(track.getZoneOffset(), trackPoint, sensorPoints, trackDistance));
|
trackDistance = trackDistance.plus(writeTrackPoint(track.getZoneOffset(), trackPoint, sensorPoints, trackDistance));
|
||||||
sensorPoints.clear();
|
sensorPoints.clear();
|
||||||
break;
|
}
|
||||||
case SENSORPOINT:
|
case SENSORPOINT -> sensorPoints.add(trackPoint);
|
||||||
sensorPoints.add(trackPoint);
|
case TRACKPOINT -> {
|
||||||
break;
|
|
||||||
case TRACKPOINT:
|
|
||||||
if (!wroteSegment) {
|
if (!wroteSegment) {
|
||||||
// Might happen for older data (pre v3.15.0)
|
// Might happen for older data (pre v3.15.0)
|
||||||
writeOpenSegment();
|
writeOpenSegment();
|
||||||
wroteSegment = true;
|
wroteSegment = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
trackDistance = trackDistance.plus(writeTrackPoint(track.getZoneOffset(), trackPoint, sensorPoints, trackDistance));
|
trackDistance = trackDistance.plus(writeTrackPoint(track.getZoneOffset(), trackPoint, sensorPoints, trackDistance));
|
||||||
sensorPoints.clear();
|
sensorPoints.clear();
|
||||||
break;
|
}
|
||||||
default:
|
default ->
|
||||||
throw new RuntimeException("Exporting this TrackPoint type is not implemented: " + trackPoint.getType());
|
throw new RuntimeException("Exporting this TrackPoint type is not implemented: " + trackPoint.getType());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,10 +93,10 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
private final List<Float> accuracyHorizontal = new ArrayList<>();
|
private final List<Float> accuracyHorizontal = new ArrayList<>();
|
||||||
private final List<Float> accuracyVertical = new ArrayList<>();
|
private final List<Float> accuracyVertical = new ArrayList<>();
|
||||||
|
|
||||||
public KMLTrackExporter(Context context, boolean exportPhotos) {
|
public KMLTrackExporter(Context context, ContentProviderUtils contentProviderUtils, boolean exportPhotos) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.exportPhotos = exportPhotos;
|
this.exportPhotos = exportPhotos;
|
||||||
this.contentProviderUtils = new ContentProviderUtils(context);
|
this.contentProviderUtils = contentProviderUtils;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean writeTrack(Track track, @NonNull OutputStream outputStream) {
|
public boolean writeTrack(Track track, @NonNull OutputStream outputStream) {
|
||||||
@@ -169,29 +169,27 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (trackPoint.getType()) {
|
switch (trackPoint.getType()) {
|
||||||
case SEGMENT_START_MANUAL:
|
case SEGMENT_START_MANUAL, SEGMENT_START_AUTOMATIC -> {
|
||||||
case SEGMENT_START_AUTOMATIC:
|
|
||||||
if (wroteSegment) writeCloseSegment();
|
if (wroteSegment) writeCloseSegment();
|
||||||
writeOpenSegment();
|
writeOpenSegment();
|
||||||
writeTrackPoint(track.getZoneOffset(), trackPoint);
|
writeTrackPoint(track.getZoneOffset(), trackPoint);
|
||||||
wroteSegment = true;
|
wroteSegment = true;
|
||||||
break;
|
}
|
||||||
case SEGMENT_END_MANUAL:
|
case SEGMENT_END_MANUAL -> {
|
||||||
if (!wroteSegment) writeOpenSegment();
|
if (!wroteSegment) writeOpenSegment();
|
||||||
writeTrackPoint(track.getZoneOffset(), trackPoint);
|
writeTrackPoint(track.getZoneOffset(), trackPoint);
|
||||||
writeCloseSegment();
|
writeCloseSegment();
|
||||||
wroteSegment = false;
|
wroteSegment = false;
|
||||||
break;
|
}
|
||||||
case SENSORPOINT:
|
case SENSORPOINT, TRACKPOINT -> {
|
||||||
case TRACKPOINT:
|
|
||||||
if (!wroteSegment) {
|
if (!wroteSegment) {
|
||||||
// Might happen for older data (pre v3.15.0)
|
// Might happen for older data (pre v3.15.0)
|
||||||
writeOpenSegment();
|
writeOpenSegment();
|
||||||
wroteSegment = true;
|
wroteSegment = true;
|
||||||
}
|
}
|
||||||
writeTrackPoint(track.getZoneOffset(), trackPoint);
|
writeTrackPoint(track.getZoneOffset(), trackPoint);
|
||||||
break;
|
}
|
||||||
default:
|
default ->
|
||||||
throw new RuntimeException("Exporting this TrackPoint type is not implemented: " + trackPoint.getType());
|
throw new RuntimeException("Exporting this TrackPoint type is not implemented: " + trackPoint.getType());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,15 +224,19 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
|
|
||||||
private void writeHeader(Track[] tracks) {
|
private void writeHeader(Track[] tracks) {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
printWriter.println(
|
||||||
printWriter.println("<kml xmlns=\"http://www.opengis.net/kml/2.3\"");
|
"""
|
||||||
printWriter.println("xmlns:atom=\"http://www.w3.org/2005/Atom\"");
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
printWriter.println("xmlns:opentracks=\"http://opentracksapp.com/xmlschemas/v1\">");
|
""");
|
||||||
//TODO ADD xsi:schemaLocation for atom
|
printWriter.println(
|
||||||
printWriter.println("xsi:schemaLocation=" +
|
"""
|
||||||
"\"http://www.opengis.net/kml/2.3 http://schemas.opengis.net/kml/2.3/ogckml23.xsd"
|
<kml xmlns="http://www.opengis.net/kml/2.3"
|
||||||
+ " http://opentracksapp.com/xmlschemas/v1 http://opentracksapp.com/xmlschemas/OpenTracks_v1.xsd\">");
|
xmlns:atom="http://www.w3.org/2005/Atom"
|
||||||
|
xmlns:opentracks="http://opentracksapp.com/xmlschemas/v1"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.opengis.net/kml/2.3 http://schemas.opengis.net/kml/2.3/ogckml23.xsd
|
||||||
|
http://opentracksapp.com/xmlschemas/v1 http://opentracksapp.com/xmlschemas/OpenTracks_v1.xsd">
|
||||||
|
"""); //TODO ADD xsi:schemaLocation for atom
|
||||||
printWriter.println("<Document>");
|
printWriter.println("<Document>");
|
||||||
printWriter.println("<open>1</open>");
|
printWriter.println("<open>1</open>");
|
||||||
printWriter.println("<visibility>1</visibility>");
|
printWriter.println("<visibility>1</visibility>");
|
||||||
|
|||||||
@@ -156,103 +156,94 @@ public class GpxTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
|||||||
@Override
|
@Override
|
||||||
public void endElement(String uri, String localName, String tag) {
|
public void endElement(String uri, String localName, String tag) {
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case TAG_GPX:
|
case TAG_GPX -> onFileEnd();
|
||||||
onFileEnd();
|
case TAG_MARKER -> onMarkerEnd();
|
||||||
break;
|
case TAG_TRACK -> {
|
||||||
case TAG_MARKER:
|
|
||||||
onMarkerEnd();
|
|
||||||
break;
|
|
||||||
case TAG_TRACK:
|
|
||||||
trackImporter.setTrack(context, name, uuid, description, category, null, zoneOffset);
|
trackImporter.setTrack(context, name, uuid, description, category, null, zoneOffset);
|
||||||
zoneOffset = null;
|
zoneOffset = null;
|
||||||
break;
|
}
|
||||||
case TAG_TRACK_SEGMENT:
|
case TAG_TRACK_SEGMENT -> onTrackSegmentEnd();
|
||||||
onTrackSegmentEnd();
|
case TAG_TRACK_POINT -> currentSegment.add(createTrackPoint());
|
||||||
break;
|
case TAG_NAME -> {
|
||||||
case TAG_TRACK_POINT:
|
|
||||||
currentSegment.add(createTrackPoint());
|
|
||||||
break;
|
|
||||||
case TAG_NAME:
|
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
name = content.trim();
|
name = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_DESCRIPTION:
|
case TAG_DESCRIPTION -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
description = content.trim();
|
description = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_TYPE:
|
case TAG_TYPE -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
category = content.trim();
|
category = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_TIME:
|
case TAG_TIME -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
time = content.trim();
|
time = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_ALTITUDE:
|
case TAG_ALTITUDE -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
altitude = content.trim();
|
altitude = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_COMMENT:
|
case TAG_COMMENT -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
markerType = content.trim();
|
markerType = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_EXTENSION_SPEED:
|
case TAG_EXTENSION_SPEED, TAG_EXTENSION_SPEED_COMPAT -> {
|
||||||
case TAG_EXTENSION_SPEED_COMPAT:
|
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
speed = content.trim();
|
speed = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_EXTENSION_HEARTRATE:
|
case TAG_EXTENSION_HEARTRATE -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
heartrate = content.trim();
|
heartrate = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_EXTENSION_CADENCE:
|
case TAG_EXTENSION_CADENCE -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
cadence = content.trim();
|
cadence = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_EXTENSION_POWER:
|
case TAG_EXTENSION_POWER -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
power = content.trim();
|
power = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_ID:
|
case TAG_ID -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
uuid = content.trim();
|
uuid = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_EXTENSION_GAIN:
|
case TAG_EXTENSION_GAIN -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
gain = content.trim();
|
gain = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_EXTENSION_LOSS:
|
case TAG_EXTENSION_LOSS -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
loss = content.trim();
|
loss = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_EXTENSION_DISTANCE:
|
case TAG_EXTENSION_DISTANCE -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
sensorDistance = content.trim();
|
sensorDistance = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_EXTENSION_ACCURACY_HORIZONTAL:
|
case TAG_EXTENSION_ACCURACY_HORIZONTAL -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
accuracyHorizontal = content.trim();
|
accuracyHorizontal = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_EXTENSION_ACCURACY_VERTICAL:
|
case TAG_EXTENSION_ACCURACY_VERTICAL -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
accuracyVertical = content.trim();
|
accuracyVertical = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
content = "";
|
content = "";
|
||||||
|
|||||||
@@ -79,18 +79,16 @@ public class ImportViewModel extends AndroidViewModel implements ImportServiceRe
|
|||||||
String message = resultData.getString(ImportServiceResultReceiver.RESULT_EXTRA_MESSAGE);
|
String message = resultData.getString(ImportServiceResultReceiver.RESULT_EXTRA_MESSAGE);
|
||||||
|
|
||||||
switch (resultCode) {
|
switch (resultCode) {
|
||||||
case ImportServiceResultReceiver.RESULT_CODE_ERROR:
|
case ImportServiceResultReceiver.RESULT_CODE_ERROR -> {
|
||||||
summary.errorCount++;
|
summary.errorCount++;
|
||||||
summary.fileErrors.add(getApplication().getString(R.string.import_error_info, fileName, message));
|
summary.fileErrors.add(getApplication().getString(R.string.import_error_info, fileName, message));
|
||||||
break;
|
}
|
||||||
case ImportServiceResultReceiver.RESULT_CODE_IMPORTED:
|
case ImportServiceResultReceiver.RESULT_CODE_IMPORTED -> {
|
||||||
summary.importedTrackIds.addAll(trackIds);
|
summary.importedTrackIds.addAll(trackIds);
|
||||||
summary.successCount++;
|
summary.successCount++;
|
||||||
break;
|
}
|
||||||
case ImportServiceResultReceiver.RESULT_CODE_ALREADY_EXISTS:
|
case ImportServiceResultReceiver.RESULT_CODE_ALREADY_EXISTS -> summary.existsCount++;
|
||||||
summary.existsCount++;
|
default ->
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new RuntimeException(TAG + ": import service result code invalid: " + resultCode);
|
throw new RuntimeException(TAG + ": import service result code invalid: " + resultCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -136,27 +136,18 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
|||||||
@Override
|
@Override
|
||||||
public void startElement(String uri, String localName, String tag, Attributes attributes) throws SAXException {
|
public void startElement(String uri, String localName, String tag, Attributes attributes) throws SAXException {
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case TAG_PLACEMARK:
|
case TAG_PLACEMARK, TAG_PHOTO_OVERLAY ->
|
||||||
case TAG_PHOTO_OVERLAY:
|
|
||||||
// Note that a track is contained in a Placemark, calling onMarkerStart will clear various track variables like name, category, and description.
|
// Note that a track is contained in a Placemark, calling onMarkerStart will clear various track variables like name, category, and description.
|
||||||
onMarkerStart();
|
onMarkerStart();
|
||||||
break;
|
case TAG_MULTI_TRACK, TAG_KML22_MULTI_TRACK -> trackImporter.newTrack();
|
||||||
case TAG_MULTI_TRACK:
|
case TAG_TRACK, TAG_KML22_TRACK -> {
|
||||||
case TAG_KML22_MULTI_TRACK:
|
|
||||||
trackImporter.newTrack();
|
|
||||||
break;
|
|
||||||
case TAG_TRACK:
|
|
||||||
case TAG_KML22_TRACK:
|
|
||||||
if (trackImporter == null) {
|
if (trackImporter == null) {
|
||||||
throw new SAXException("Missing " + TAG_MULTI_TRACK);
|
throw new SAXException("Missing " + TAG_MULTI_TRACK);
|
||||||
}
|
}
|
||||||
onTrackSegmentStart();
|
onTrackSegmentStart();
|
||||||
break;
|
}
|
||||||
case TAG_DATA_CATEGORY:
|
case TAG_DATA_CATEGORY, TAG_SIMPLE_ARRAY_DATA, TAG_KML22_SIMPLE_ARRAY_DATA ->
|
||||||
case TAG_SIMPLE_ARRAY_DATA:
|
|
||||||
case TAG_KML22_SIMPLE_ARRAY_DATA:
|
|
||||||
dataType = attributes.getValue(ATTRIBUTE_NAME);
|
dataType = attributes.getValue(ATTRIBUTE_NAME);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,32 +159,18 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
|||||||
@Override
|
@Override
|
||||||
public void endElement(String uri, String localName, String tag) throws SAXException {
|
public void endElement(String uri, String localName, String tag) throws SAXException {
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case TAG_KML:
|
case TAG_KML -> onFileEnd();
|
||||||
onFileEnd();
|
case TAG_PLACEMARK, TAG_PHOTO_OVERLAY ->
|
||||||
break;
|
|
||||||
case TAG_PLACEMARK:
|
|
||||||
case TAG_PHOTO_OVERLAY:
|
|
||||||
// Note that a track is contained in a Placemark, calling onMarkerEnd is save since markerType is not set for a track.
|
// Note that a track is contained in a Placemark, calling onMarkerEnd is save since markerType is not set for a track.
|
||||||
onMarkerEnd();
|
onMarkerEnd();
|
||||||
break;
|
case TAG_COORDINATES -> onMarkerLocationEnd();
|
||||||
case TAG_COORDINATES:
|
case TAG_MULTI_TRACK, TAG_KML22_MULTI_TRACK -> {
|
||||||
onMarkerLocationEnd();
|
|
||||||
break;
|
|
||||||
case TAG_MULTI_TRACK:
|
|
||||||
case TAG_KML22_MULTI_TRACK:
|
|
||||||
trackImporter.setTrack(context, name, uuid, description, category, icon, zoneOffset);
|
trackImporter.setTrack(context, name, uuid, description, category, icon, zoneOffset);
|
||||||
zoneOffset = null;
|
zoneOffset = null;
|
||||||
break;
|
}
|
||||||
case TAG_TRACK:
|
case TAG_TRACK, TAG_KML22_TRACK -> onTrackSegmentEnd();
|
||||||
case TAG_KML22_TRACK:
|
case TAG_COORD, TAG_KML22_COORD -> onCoordEnded();
|
||||||
onTrackSegmentEnd();
|
case TAG_VALUE, TAG_KML22_VALUE -> {
|
||||||
break;
|
|
||||||
case TAG_COORD:
|
|
||||||
case TAG_KML22_COORD:
|
|
||||||
onCoordEnded();
|
|
||||||
break;
|
|
||||||
case TAG_VALUE:
|
|
||||||
case TAG_KML22_VALUE:
|
|
||||||
if (KMLTrackExporter.EXTENDED_DATA_TYPE_CATEGORY.equals(dataType)) {
|
if (KMLTrackExporter.EXTENDED_DATA_TYPE_CATEGORY.equals(dataType)) {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
category = content.trim();
|
category = content.trim();
|
||||||
@@ -201,28 +178,28 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
|||||||
} else {
|
} else {
|
||||||
onExtendedDataValueEnd();
|
onExtendedDataValueEnd();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_NAME:
|
case TAG_NAME -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
name = content.trim();
|
name = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_UUID:
|
case TAG_UUID -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
uuid = content.trim();
|
uuid = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_DESCRIPTION:
|
case TAG_DESCRIPTION -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
description = content.trim();
|
description = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_ICON:
|
case TAG_ICON -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
icon = content.trim();
|
icon = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_WHEN:
|
case TAG_WHEN -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
try {
|
try {
|
||||||
OffsetDateTime time = StringUtils.parseTime(content.trim());
|
OffsetDateTime time = StringUtils.parseTime(content.trim());
|
||||||
@@ -234,18 +211,17 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
|||||||
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse time: %s", content.trim())), e);
|
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse time: %s", content.trim())), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
case TAG_STYLE_URL -> {
|
||||||
case TAG_STYLE_URL:
|
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
markerType = content.trim();
|
markerType = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_HREF:
|
case TAG_HREF -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
photoUrl = content.trim();
|
photoUrl = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset element content
|
// Reset element content
|
||||||
@@ -436,34 +412,18 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch (dataType) {
|
switch (dataType) {
|
||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_SPEED:
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_SPEED -> sensorSpeedList.add(value);
|
||||||
sensorSpeedList.add(value);
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_DISTANCE -> sensorDistanceList.add(value);
|
||||||
break;
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_POWER -> sensorPowerList.add(value);
|
||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_DISTANCE:
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_HEART_RATE -> sensorHeartRateList.add(value);
|
||||||
sensorDistanceList.add(value);
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_CADENCE -> sensorCadenceList.add(value);
|
||||||
break;
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_ALTITUDE_GAIN -> altitudeGainList.add(value);
|
||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_POWER:
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_ALTITUDE_LOSS -> altitudeLossList.add(value);
|
||||||
sensorPowerList.add(value);
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_ACCURACY_HORIZONTAL ->
|
||||||
break;
|
|
||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_HEART_RATE:
|
|
||||||
sensorHeartRateList.add(value);
|
|
||||||
break;
|
|
||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_CADENCE:
|
|
||||||
sensorCadenceList.add(value);
|
|
||||||
break;
|
|
||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_ALTITUDE_GAIN:
|
|
||||||
altitudeGainList.add(value);
|
|
||||||
break;
|
|
||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_ALTITUDE_LOSS:
|
|
||||||
altitudeLossList.add(value);
|
|
||||||
break;
|
|
||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_ACCURACY_HORIZONTAL:
|
|
||||||
accuracyHorizontal.add(value);
|
accuracyHorizontal.add(value);
|
||||||
break;
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_ACCURACY_VERTICAL ->
|
||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_ACCURACY_VERTICAL:
|
|
||||||
accuracyVertical.add(value);
|
accuracyVertical.add(value);
|
||||||
break;
|
default ->
|
||||||
default:
|
|
||||||
Log.w(TAG, "Data from extended data " + dataType + " is not (yet) supported.");
|
Log.w(TAG, "Data from extended data " + dataType + " is not (yet) supported.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-19
@@ -40,9 +40,9 @@ import de.dennisguse.opentracks.sensors.sensorData.SensorData;
|
|||||||
* Manages connection to a Bluetooth LE sensor and subscribes for onChange-notifications.
|
* Manages connection to a Bluetooth LE sensor and subscribes for onChange-notifications.
|
||||||
* Also parses the transferred data into {@link SensorDataObserver}.
|
* Also parses the transferred data into {@link SensorDataObserver}.
|
||||||
*/
|
*/
|
||||||
public abstract class BluetoothConnectionManager<DataType> {
|
public abstract class AbstractBluetoothConnectionManager<DataType> {
|
||||||
|
|
||||||
private static final String TAG = BluetoothConnectionManager.class.getSimpleName();
|
private static final String TAG = AbstractBluetoothConnectionManager.class.getSimpleName();
|
||||||
|
|
||||||
private final SensorDataObserver observer;
|
private final SensorDataObserver observer;
|
||||||
|
|
||||||
@@ -53,23 +53,22 @@ public abstract class BluetoothConnectionManager<DataType> {
|
|||||||
@Override
|
@Override
|
||||||
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
|
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
|
||||||
switch (newState) {
|
switch (newState) {
|
||||||
case BluetoothProfile.STATE_CONNECTING:
|
case BluetoothProfile.STATE_CONNECTING ->
|
||||||
Log.d(TAG, "Connecting to sensor: " + gatt.getDevice());
|
Log.i(TAG, "Connecting to sensor: " + gatt.getDevice());
|
||||||
break;
|
case BluetoothProfile.STATE_CONNECTED -> {
|
||||||
case BluetoothProfile.STATE_CONNECTED:
|
Log.i(TAG, "Connected to sensor: " + gatt.getDevice() + "; discovering services.");
|
||||||
Log.d(TAG, "Connected to sensor: " + gatt.getDevice());
|
|
||||||
|
|
||||||
gatt.discoverServices();
|
gatt.discoverServices();
|
||||||
break;
|
}
|
||||||
case BluetoothProfile.STATE_DISCONNECTING:
|
case BluetoothProfile.STATE_DISCONNECTING ->
|
||||||
Log.d(TAG, "Disconnecting from sensor: " + gatt.getDevice());
|
Log.i(TAG, "Disconnecting from sensor: " + gatt.getDevice());
|
||||||
break;
|
case BluetoothProfile.STATE_DISCONNECTED -> {
|
||||||
|
//This is also triggered, if no connection was established (ca. 30s)
|
||||||
case BluetoothProfile.STATE_DISCONNECTED:
|
Log.i(TAG, "Disconnected from sensor: " + gatt.getDevice() + "; trying to reconnect");
|
||||||
Log.d(TAG, "Disconnected from sensor: " + gatt.getDevice());
|
if (gatt.connect()) {
|
||||||
|
Log.e(TAG, "Could not trigger reconnect for sensor: " + gatt.getDevice());
|
||||||
|
}
|
||||||
clearData();
|
clearData();
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,12 +132,12 @@ public abstract class BluetoothConnectionManager<DataType> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
BluetoothConnectionManager(ServiceMeasurementUUID serviceUUUID, SensorDataObserver observer) {
|
AbstractBluetoothConnectionManager(ServiceMeasurementUUID serviceUUUID, SensorDataObserver observer) {
|
||||||
this.serviceMeasurementUUIDs = List.of(serviceUUUID);
|
this.serviceMeasurementUUIDs = List.of(serviceUUUID);
|
||||||
this.observer = observer;
|
this.observer = observer;
|
||||||
}
|
}
|
||||||
|
|
||||||
BluetoothConnectionManager(List<ServiceMeasurementUUID> serviceUUUID, SensorDataObserver observer) {
|
AbstractBluetoothConnectionManager(List<ServiceMeasurementUUID> serviceUUUID, SensorDataObserver observer) {
|
||||||
this.serviceMeasurementUUIDs = serviceUUUID;
|
this.serviceMeasurementUUIDs = serviceUUUID;
|
||||||
this.observer = observer;
|
this.observer = observer;
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,7 @@ import de.dennisguse.opentracks.data.models.TrackPoint;
|
|||||||
/**
|
/**
|
||||||
* Estimates the altitude gain and altitude loss using the device's pressure sensor (i.e., barometer).
|
* Estimates the altitude gain and altitude loss using the device's pressure sensor (i.e., barometer).
|
||||||
*/
|
*/
|
||||||
public class AltitudeSumManager implements SensorEventListener {
|
public class AltitudeSumManager implements SensorConnector, SensorEventListener {
|
||||||
|
|
||||||
private static final String TAG = AltitudeSumManager.class.getSimpleName();
|
private static final String TAG = AltitudeSumManager.class.getSimpleName();
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -8,11 +8,11 @@ import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadence;
|
|||||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDistanceSpeed;
|
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDistanceSpeed;
|
||||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower;
|
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower;
|
||||||
|
|
||||||
public class BluetoothRemoteSensorManagerCyclingCadence extends BluetoothConnectionManager<Cadence> {
|
public class BluetoothConnectionManagerCyclingCadence extends AbstractBluetoothConnectionManager<Cadence> {
|
||||||
|
|
||||||
private static final String TAG = BluetoothRemoteSensorManagerCyclingCadence.class.getSimpleName();
|
private static final String TAG = BluetoothConnectionManagerCyclingCadence.class.getSimpleName();
|
||||||
|
|
||||||
BluetoothRemoteSensorManagerCyclingCadence(SensorDataObserver observer) {
|
BluetoothConnectionManagerCyclingCadence(SensorDataObserver observer) {
|
||||||
super(BluetoothUtils.CYCLING_CADENCE, observer);
|
super(BluetoothUtils.CYCLING_CADENCE, observer);
|
||||||
}
|
}
|
||||||
|
|
||||||
+2
-2
@@ -5,9 +5,9 @@ import android.bluetooth.BluetoothGattCharacteristic;
|
|||||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDistanceSpeed;
|
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadenceAndDistanceSpeed;
|
||||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingDistanceSpeed;
|
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingDistanceSpeed;
|
||||||
|
|
||||||
public class BluetoothRemoteSensorManagerCyclingDistanceSpeed extends BluetoothConnectionManager<SensorDataCyclingDistanceSpeed.Data> {
|
public class BluetoothConnectionManagerCyclingDistanceSpeed extends AbstractBluetoothConnectionManager<SensorDataCyclingDistanceSpeed.Data> {
|
||||||
|
|
||||||
BluetoothRemoteSensorManagerCyclingDistanceSpeed(SensorDataObserver observer) {
|
BluetoothConnectionManagerCyclingDistanceSpeed(SensorDataObserver observer) {
|
||||||
super(BluetoothUtils.CYCLING_SPEED_CADENCE, observer);
|
super(BluetoothUtils.CYCLING_SPEED_CADENCE, observer);
|
||||||
}
|
}
|
||||||
|
|
||||||
+2
-2
@@ -7,9 +7,9 @@ import androidx.annotation.NonNull;
|
|||||||
import de.dennisguse.opentracks.data.models.Power;
|
import de.dennisguse.opentracks.data.models.Power;
|
||||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower;
|
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower;
|
||||||
|
|
||||||
public class BluetoothRemoteSensorManagerCyclingPower extends BluetoothConnectionManager<Power> {
|
public class BluetoothConnectionManagerCyclingPower extends AbstractBluetoothConnectionManager<Power> {
|
||||||
|
|
||||||
BluetoothRemoteSensorManagerCyclingPower(@NonNull SensorDataObserver observer) {
|
BluetoothConnectionManagerCyclingPower(@NonNull SensorDataObserver observer) {
|
||||||
super(BluetoothUtils.CYCLING_POWER, observer);
|
super(BluetoothUtils.CYCLING_POWER, observer);
|
||||||
}
|
}
|
||||||
|
|
||||||
+4
-4
@@ -7,9 +7,9 @@ import androidx.annotation.NonNull;
|
|||||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataHeartRate;
|
import de.dennisguse.opentracks.sensors.sensorData.SensorDataHeartRate;
|
||||||
|
|
||||||
public class BluetoothRemoteSensorManagerHeartRate extends BluetoothConnectionManager<HeartRate> {
|
public class BluetoothConnectionManagerHeartRate extends AbstractBluetoothConnectionManager<HeartRate> {
|
||||||
|
|
||||||
BluetoothRemoteSensorManagerHeartRate(@NonNull SensorDataObserver observer) {
|
BluetoothConnectionManagerHeartRate(@NonNull SensorDataObserver observer) {
|
||||||
super(BluetoothUtils.HEARTRATE, observer);
|
super(BluetoothUtils.HEARTRATE, observer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,8 +20,8 @@ public class BluetoothRemoteSensorManagerHeartRate extends BluetoothConnectionMa
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected SensorDataHeartRate parsePayload(@NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) {
|
protected SensorDataHeartRate parsePayload(@NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) {
|
||||||
Integer heartRate = BluetoothUtils.parseHeartRate(characteristic);
|
HeartRate heartRate = BluetoothUtils.parseHeartRate(characteristic);
|
||||||
|
|
||||||
return heartRate != null ? new SensorDataHeartRate(address, sensorName, HeartRate.of(heartRate)) : null;
|
return heartRate != null ? new SensorDataHeartRate(address, sensorName, heartRate) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+2
-2
@@ -6,9 +6,9 @@ import androidx.annotation.NonNull;
|
|||||||
|
|
||||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning;
|
import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning;
|
||||||
|
|
||||||
public class BluetoothRemoteSensorManagerRunningSpeedAndCadence extends BluetoothConnectionManager<SensorDataRunning.Data> {
|
public class BluetoothConnectionRunningSpeedAndCadence extends AbstractBluetoothConnectionManager<SensorDataRunning.Data> {
|
||||||
|
|
||||||
BluetoothRemoteSensorManagerRunningSpeedAndCadence(@NonNull SensorDataObserver observer) {
|
BluetoothConnectionRunningSpeedAndCadence(@NonNull SensorDataObserver observer) {
|
||||||
super(BluetoothUtils.RUNNING_SPEED_CADENCE, observer);
|
super(BluetoothUtils.RUNNING_SPEED_CADENCE, observer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@ import de.dennisguse.opentracks.util.PermissionRequester;
|
|||||||
*
|
*
|
||||||
* @author Sandor Dornbush
|
* @author Sandor Dornbush
|
||||||
*/
|
*/
|
||||||
public class BluetoothRemoteSensorManager implements BluetoothConnectionManager.SensorDataObserver {
|
public class BluetoothRemoteSensorManager implements SensorConnector, AbstractBluetoothConnectionManager.SensorDataObserver {
|
||||||
|
|
||||||
private static final String TAG = BluetoothRemoteSensorManager.class.getSimpleName();
|
private static final String TAG = BluetoothRemoteSensorManager.class.getSimpleName();
|
||||||
|
|
||||||
@@ -65,15 +65,15 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager.
|
|||||||
|
|
||||||
private Distance preferenceWheelCircumference;
|
private Distance preferenceWheelCircumference;
|
||||||
|
|
||||||
private final BluetoothRemoteSensorManagerHeartRate heartRate = new BluetoothRemoteSensorManagerHeartRate(this);
|
private final BluetoothConnectionManagerHeartRate heartRate = new BluetoothConnectionManagerHeartRate(this);
|
||||||
private final BluetoothRemoteSensorManagerCyclingCadence cyclingCadence = new BluetoothRemoteSensorManagerCyclingCadence(this);
|
private final BluetoothConnectionManagerCyclingCadence cyclingCadence = new BluetoothConnectionManagerCyclingCadence(this);
|
||||||
private final BluetoothRemoteSensorManagerCyclingDistanceSpeed cyclingSpeed = new BluetoothRemoteSensorManagerCyclingDistanceSpeed(this);
|
private final BluetoothConnectionManagerCyclingDistanceSpeed cyclingSpeed = new BluetoothConnectionManagerCyclingDistanceSpeed(this);
|
||||||
private final BluetoothRemoteSensorManagerCyclingPower cyclingPower = new BluetoothRemoteSensorManagerCyclingPower(this);
|
private final BluetoothConnectionManagerCyclingPower cyclingPower = new BluetoothConnectionManagerCyclingPower(this);
|
||||||
private final BluetoothRemoteSensorManagerRunningSpeedAndCadence runningSpeedAndCadence = new BluetoothRemoteSensorManagerRunningSpeedAndCadence(this);
|
private final BluetoothConnectionRunningSpeedAndCadence runningSpeedAndCadence = new BluetoothConnectionRunningSpeedAndCadence(this);
|
||||||
|
|
||||||
private final SensorDataSet sensorDataSet = new SensorDataSet();
|
private final SensorDataSet sensorDataSet = new SensorDataSet();
|
||||||
|
|
||||||
private final SensorDataSetChangeObserver observer;
|
private final SensorManager.SensorDataSetChangeObserver observer;
|
||||||
|
|
||||||
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
|
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
@@ -114,21 +114,23 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager.
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public BluetoothRemoteSensorManager(@NonNull Context context, @NonNull Handler handler, @NonNull SensorDataSetChangeObserver observer) {
|
public BluetoothRemoteSensorManager(@NonNull Context context, @NonNull Handler handler, @NonNull SensorManager.SensorDataSetChangeObserver observer) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
this.observer = observer;
|
this.observer = observer;
|
||||||
bluetoothAdapter = BluetoothUtils.getAdapter(context);
|
bluetoothAdapter = BluetoothUtils.getAdapter(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
@Override
|
||||||
|
public void start(Context context, Handler handler) {
|
||||||
started = true;
|
started = true;
|
||||||
|
|
||||||
//Registering triggers connection startup
|
//Registering triggers connection startup
|
||||||
PreferencesUtils.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
PreferencesUtils.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void stop() {
|
@Override
|
||||||
|
public synchronized void stop(Context context) {
|
||||||
heartRate.disconnect();
|
heartRate.disconnect();
|
||||||
cyclingCadence.disconnect();
|
cyclingCadence.disconnect();
|
||||||
cyclingSpeed.disconnect();
|
cyclingSpeed.disconnect();
|
||||||
@@ -145,7 +147,7 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager.
|
|||||||
return bluetoothAdapter != null && bluetoothAdapter.isEnabled();
|
return bluetoothAdapter != null && bluetoothAdapter.isEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void connect(BluetoothConnectionManager<?> connectionManager, String address) {
|
private synchronized void connect(AbstractBluetoothConnectionManager<?> connectionManager, String address) {
|
||||||
if (!isEnabled()) {
|
if (!isEnabled()) {
|
||||||
Log.w(TAG, "Bluetooth not enabled.");
|
Log.w(TAG, "Bluetooth not enabled.");
|
||||||
return;
|
return;
|
||||||
@@ -230,8 +232,4 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager.
|
|||||||
public Handler getHandler() {
|
public Handler getHandler() {
|
||||||
return handler;
|
return handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface SensorDataSetChangeObserver {
|
|
||||||
void onChange(SensorDataSet sensorDataSet);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import java.util.UUID;
|
|||||||
|
|
||||||
import de.dennisguse.opentracks.data.models.Cadence;
|
import de.dennisguse.opentracks.data.models.Cadence;
|
||||||
import de.dennisguse.opentracks.data.models.Distance;
|
import de.dennisguse.opentracks.data.models.Distance;
|
||||||
|
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||||
import de.dennisguse.opentracks.data.models.Power;
|
import de.dennisguse.opentracks.data.models.Power;
|
||||||
import de.dennisguse.opentracks.data.models.Speed;
|
import de.dennisguse.opentracks.data.models.Speed;
|
||||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadence;
|
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadence;
|
||||||
@@ -101,7 +102,7 @@ public class BluetoothUtils {
|
|||||||
return BluetoothUtils.getAdapter(context) != null;
|
return BluetoothUtils.getAdapter(context) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Integer parseHeartRate(BluetoothGattCharacteristic characteristic) {
|
public static HeartRate parseHeartRate(BluetoothGattCharacteristic characteristic) {
|
||||||
//DOCUMENTATION https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.heart_rate_measurement.xml
|
//DOCUMENTATION https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.heart_rate_measurement.xml
|
||||||
byte[] raw = characteristic.getValue();
|
byte[] raw = characteristic.getValue();
|
||||||
if (raw.length == 0) {
|
if (raw.length == 0) {
|
||||||
@@ -110,10 +111,10 @@ public class BluetoothUtils {
|
|||||||
|
|
||||||
boolean formatUINT16 = ((raw[0] & 0x1) == 1);
|
boolean formatUINT16 = ((raw[0] & 0x1) == 1);
|
||||||
if (formatUINT16 && raw.length >= 3) {
|
if (formatUINT16 && raw.length >= 3) {
|
||||||
return characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 1);
|
return HeartRate.of(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 1));
|
||||||
}
|
}
|
||||||
if (!formatUINT16 && raw.length >= 2) {
|
if (!formatUINT16 && raw.length >= 2) {
|
||||||
return characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 1);
|
return HeartRate.of(characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package de.dennisguse.opentracks.sensors;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Handler;
|
||||||
|
|
||||||
|
public interface SensorConnector {
|
||||||
|
|
||||||
|
void start(Context context, Handler handler);
|
||||||
|
|
||||||
|
void stop(Context context);
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package de.dennisguse.opentracks.sensors;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||||
|
import de.dennisguse.opentracks.sensors.sensorData.SensorData;
|
||||||
|
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
|
||||||
|
|
||||||
|
public class SensorManager {
|
||||||
|
|
||||||
|
private static final String TAG = SensorManager.class.getSimpleName();
|
||||||
|
|
||||||
|
private BluetoothRemoteSensorManager bluetoothSensorManager;
|
||||||
|
|
||||||
|
private AltitudeSumManager altitudeSumManager;
|
||||||
|
|
||||||
|
public SensorManager(Context context, Handler handler, SensorDataSetChangeObserver observer) {
|
||||||
|
bluetoothSensorManager = new BluetoothRemoteSensorManager(context, handler, observer);
|
||||||
|
altitudeSumManager = new AltitudeSumManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start(Context context, Handler handler) {
|
||||||
|
bluetoothSensorManager.start(context, handler);
|
||||||
|
altitudeSumManager.start(context, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop(Context context) {
|
||||||
|
if (bluetoothSensorManager != null) {
|
||||||
|
bluetoothSensorManager.stop(context);
|
||||||
|
bluetoothSensorManager = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (altitudeSumManager != null) {
|
||||||
|
altitudeSumManager.stop(context);
|
||||||
|
altitudeSumManager = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SensorDataSet fill(TrackPoint trackPoint) {
|
||||||
|
altitudeSumManager.fill(trackPoint);
|
||||||
|
return bluetoothSensorManager.fill(trackPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
if (bluetoothSensorManager == null || altitudeSumManager == null) {
|
||||||
|
Log.d(TAG, "No recording running and no reset necessary.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bluetoothSensorManager.reset();
|
||||||
|
altitudeSumManager.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
@VisibleForTesting
|
||||||
|
public BluetoothRemoteSensorManager getBluetoothSensorManager() {
|
||||||
|
return bluetoothSensorManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
@VisibleForTesting
|
||||||
|
public void setBluetoothSensorManager(BluetoothRemoteSensorManager remoteSensorManager) {
|
||||||
|
this.bluetoothSensorManager = remoteSensorManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
@VisibleForTesting
|
||||||
|
public AltitudeSumManager getAltitudeSumManager() {
|
||||||
|
return altitudeSumManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
@VisibleForTesting
|
||||||
|
public void setAltitudeSumManager(AltitudeSumManager altitudeSumManager) {
|
||||||
|
this.altitudeSumManager = altitudeSumManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public interface SensorDataSetChangeObserver {
|
||||||
|
void onChange(SensorDataSet sensorDataSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ public class UintUtils {
|
|||||||
* @return diff
|
* @return diff
|
||||||
*/
|
*/
|
||||||
public static long diff(long a, long b, final long UINT_MAX) {
|
public static long diff(long a, long b, final long UINT_MAX) {
|
||||||
|
|
||||||
if (a < 0 || b < 0) {
|
if (a < 0 || b < 0) {
|
||||||
throw new RuntimeException("a or b cannot be less than zero.");
|
throw new RuntimeException("a or b cannot be less than zero.");
|
||||||
}
|
}
|
||||||
@@ -25,6 +26,6 @@ public class UintUtils {
|
|||||||
return a - b;
|
return a - b;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (UINT_MAX - b) + a;
|
return (UINT_MAX + 1 - b) + a;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package de.dennisguse.opentracks.sensors.sensorData;
|
package de.dennisguse.opentracks.sensors.sensorData;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
@@ -32,16 +31,6 @@ public abstract class SensorData<T> {
|
|||||||
this.time = time;
|
this.time = time;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public String getSensorAddress() {
|
|
||||||
return sensorAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public String getSensorName() {
|
|
||||||
return sensorName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSensorNameOrAddress() {
|
public String getSensorNameOrAddress() {
|
||||||
return sensorName != null ? sensorName : sensorAddress;
|
return sensorName != null ? sensorName : sensorAddress;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -76,9 +76,8 @@ public class SensorDataCyclingCadence extends SensorData<Cadence> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(@Nullable Object obj) {
|
public boolean equals(@Nullable Object obj) {
|
||||||
if (!(obj instanceof SensorDataCyclingCadence)) return false;
|
if (!(obj instanceof SensorDataCyclingCadence comp)) return false;
|
||||||
|
|
||||||
SensorDataCyclingCadence comp = (SensorDataCyclingCadence) obj;
|
|
||||||
if (hasData() && comp.hasData() == hasData()) {
|
if (hasData() && comp.hasData() == hasData()) {
|
||||||
return getCrankRevolutionsCount() == comp.getCrankRevolutionsCount() && getCrankRevolutionsTime() == comp.getCrankRevolutionsTime();
|
return getCrankRevolutionsCount() == comp.getCrankRevolutionsCount() && getCrankRevolutionsTime() == comp.getCrankRevolutionsTime();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+1
-2
@@ -93,9 +93,8 @@ public class SensorDataCyclingDistanceSpeed extends SensorData<SensorDataCycling
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(@Nullable Object obj) {
|
public boolean equals(@Nullable Object obj) {
|
||||||
if (!(obj instanceof SensorDataCyclingDistanceSpeed)) return false;
|
if (!(obj instanceof SensorDataCyclingDistanceSpeed comp)) return false;
|
||||||
|
|
||||||
SensorDataCyclingDistanceSpeed comp = (SensorDataCyclingDistanceSpeed) obj;
|
|
||||||
if (!(hasData() && comp.hasData())) {
|
if (!(hasData() && comp.hasData())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
|||||||
egm2008CorrectionManager = new EGM2008CorrectionManager();
|
egm2008CorrectionManager = new EGM2008CorrectionManager();
|
||||||
trackRecordingManager = new TrackRecordingManager(this);
|
trackRecordingManager = new TrackRecordingManager(this);
|
||||||
trackRecordingManager.start();
|
trackRecordingManager.start();
|
||||||
trackPointCreator = new TrackPointCreator(this);
|
trackPointCreator = new TrackPointCreator(this, this, handler);
|
||||||
|
|
||||||
voiceAnnouncementManager = new VoiceAnnouncementManager(this);
|
voiceAnnouncementManager = new VoiceAnnouncementManager(this);
|
||||||
notificationManager = new TrackRecordingServiceNotificationManager(this);
|
notificationManager = new TrackRecordingServiceNotificationManager(this);
|
||||||
@@ -177,7 +177,6 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void resumeTrack(Track.Id trackId) {
|
public void resumeTrack(Track.Id trackId) {
|
||||||
trackPointCreator.reset();
|
|
||||||
if (!trackRecordingManager.resumeExistingTrack(trackId, trackPointCreator)) {
|
if (!trackRecordingManager.resumeExistingTrack(trackId, trackPointCreator)) {
|
||||||
Log.w(TAG, "Cannot resume a non-existing track.");
|
Log.w(TAG, "Cannot resume a non-existing track.");
|
||||||
return;
|
return;
|
||||||
@@ -328,7 +327,7 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
|||||||
TrackPoint trackPoint = data.second.first;
|
TrackPoint trackPoint = data.second.first;
|
||||||
egm2008CorrectionManager.correctAltitude(this, trackPoint);
|
egm2008CorrectionManager.correctAltitude(this, trackPoint);
|
||||||
|
|
||||||
localVoiceAnnouncementManager.update(data.first);
|
localVoiceAnnouncementManager.update(this, data.first);
|
||||||
|
|
||||||
recordingDataObservable.postValue(new RecordingData(data.first, trackPoint, data.second.second));
|
recordingDataObservable.postValue(new RecordingData(data.first, trackPoint, data.second.second));
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-11
@@ -28,6 +28,7 @@ import android.util.Log;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
@@ -67,17 +68,8 @@ public class VoiceAnnouncement {
|
|||||||
public void onAudioFocusChange(int focusChange) {
|
public void onAudioFocusChange(int focusChange) {
|
||||||
Log.d(TAG, "Audio focus changed to " + focusChange);
|
Log.d(TAG, "Audio focus changed to " + focusChange);
|
||||||
|
|
||||||
boolean stop = false;
|
boolean stop = List.of(AudioManager.AUDIOFOCUS_LOSS, AudioManager.AUDIOFOCUS_LOSS_TRANSIENT, AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK)
|
||||||
switch (focusChange) {
|
.contains(focusChange);
|
||||||
case AudioManager.AUDIOFOCUS_GAIN:
|
|
||||||
stop = false;
|
|
||||||
break;
|
|
||||||
case AudioManager.AUDIOFOCUS_LOSS:
|
|
||||||
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
|
|
||||||
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
|
|
||||||
stop = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stop && tts != null && tts.isSpeaking()) {
|
if (stop && tts != null && tts.isSpeaking()) {
|
||||||
tts.stop();
|
tts.stop();
|
||||||
|
|||||||
+11
-1
@@ -15,12 +15,14 @@
|
|||||||
*/
|
*/
|
||||||
package de.dennisguse.opentracks.services.announcement;
|
package de.dennisguse.opentracks.services.announcement;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
import androidx.mediarouter.media.MediaRouter;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
|
||||||
@@ -73,12 +75,20 @@ public class VoiceAnnouncementManager implements SharedPreferences.OnSharedPrefe
|
|||||||
updateNextTaskDistance();
|
updateNextTaskDistance();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update(@NonNull Track track) {
|
public void update(@NonNull Context context, @NonNull Track track) {
|
||||||
if (voiceAnnouncement == null) {
|
if (voiceAnnouncement == null) {
|
||||||
Log.e(TAG, "Cannot update when in status shutdown.");
|
Log.e(TAG, "Cannot update when in status shutdown.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!PreferencesUtils.shouldVoiceAnnouncementOnDeviceSpeaker()
|
||||||
|
&& MediaRouter.getInstance(context)
|
||||||
|
.getSelectedRoute()
|
||||||
|
.isDeviceSpeaker()) {
|
||||||
|
Log.i(TAG, "No voice announcement on device speaker.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
boolean announce = false;
|
boolean announce = false;
|
||||||
this.trackStatistics = track.getTrackStatistics();
|
this.trackStatistics = track.getTrackStatistics();
|
||||||
if (trackStatistics.getTotalDistance().greaterThan(nextTotalDistance)) {
|
if (trackStatistics.getTotalDistance().greaterThan(nextTotalDistance)) {
|
||||||
|
|||||||
+7
-8
@@ -43,29 +43,28 @@ class VoiceAnnouncementUtils {
|
|||||||
String unitDistanceTTS;
|
String unitDistanceTTS;
|
||||||
String unitSpeedTTS;
|
String unitSpeedTTS;
|
||||||
switch (unitSystem) {
|
switch (unitSystem) {
|
||||||
case METRIC:
|
case METRIC -> {
|
||||||
perUnitStringId = R.string.voice_per_kilometer;
|
perUnitStringId = R.string.voice_per_kilometer;
|
||||||
distanceId = R.plurals.voiceDistanceKilometers;
|
distanceId = R.plurals.voiceDistanceKilometers;
|
||||||
speedId = R.plurals.voiceSpeedKilometersPerHour;
|
speedId = R.plurals.voiceSpeedKilometersPerHour;
|
||||||
unitDistanceTTS = "kilometer";
|
unitDistanceTTS = "kilometer";
|
||||||
unitSpeedTTS = "kilometer per hour";
|
unitSpeedTTS = "kilometer per hour";
|
||||||
break;
|
}
|
||||||
case IMPERIAL:
|
case IMPERIAL -> {
|
||||||
perUnitStringId = R.string.voice_per_mile;
|
perUnitStringId = R.string.voice_per_mile;
|
||||||
distanceId = R.plurals.voiceDistanceMiles;
|
distanceId = R.plurals.voiceDistanceMiles;
|
||||||
speedId = R.plurals.voiceSpeedMilesPerHour;
|
speedId = R.plurals.voiceSpeedMilesPerHour;
|
||||||
unitDistanceTTS = "mile";
|
unitDistanceTTS = "mile";
|
||||||
unitSpeedTTS = "mile per hour";
|
unitSpeedTTS = "mile per hour";
|
||||||
break;
|
}
|
||||||
case NAUTICAL_IMPERIAL:
|
case NAUTICAL_IMPERIAL -> {
|
||||||
perUnitStringId = R.string.voice_per_nautical_mile;
|
perUnitStringId = R.string.voice_per_nautical_mile;
|
||||||
distanceId = R.plurals.voiceDistanceNauticalMiles;
|
distanceId = R.plurals.voiceDistanceNauticalMiles;
|
||||||
speedId = R.plurals.voiceSpeedMKnots;
|
speedId = R.plurals.voiceSpeedMKnots;
|
||||||
unitDistanceTTS = "nautical mile";
|
unitDistanceTTS = "nautical mile";
|
||||||
unitSpeedTTS = "knots";
|
unitSpeedTTS = "knots";
|
||||||
break;
|
}
|
||||||
default:
|
default -> throw new RuntimeException("Not implemented");
|
||||||
throw new RuntimeException("Not implemented");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double distanceInUnit = totalDistance.toKM_Miles(unitSystem);
|
double distanceInUnit = totalDistance.toKM_Miles(unitSystem);
|
||||||
|
|||||||
+6
-5
@@ -20,14 +20,15 @@ import java.time.Instant;
|
|||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.data.models.Distance;
|
import de.dennisguse.opentracks.data.models.Distance;
|
||||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||||
|
import de.dennisguse.opentracks.sensors.SensorConnector;
|
||||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||||
import de.dennisguse.opentracks.util.LocationUtils;
|
import de.dennisguse.opentracks.util.LocationUtils;
|
||||||
import de.dennisguse.opentracks.util.PermissionRequester;
|
import de.dennisguse.opentracks.util.PermissionRequester;
|
||||||
|
|
||||||
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
|
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
|
||||||
public class GPSHandler implements LocationListenerCompat, GpsStatus.GpsStatusListener, SharedPreferences.OnSharedPreferenceChangeListener {
|
public class GPSManager implements SensorConnector, LocationListenerCompat, GpsStatus.GpsStatusListener, SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
|
|
||||||
private final String TAG = GPSHandler.class.getSimpleName();
|
private final String TAG = GPSManager.class.getSimpleName();
|
||||||
|
|
||||||
public static final String LOCATION_PROVIDER = LocationManager.GPS_PROVIDER;
|
public static final String LOCATION_PROVIDER = LocationManager.GPS_PROVIDER;
|
||||||
|
|
||||||
@@ -40,11 +41,11 @@ public class GPSHandler implements LocationListenerCompat, GpsStatus.GpsStatusLi
|
|||||||
private Duration gpsInterval;
|
private Duration gpsInterval;
|
||||||
private Distance thresholdHorizontalAccuracy;
|
private Distance thresholdHorizontalAccuracy;
|
||||||
|
|
||||||
public GPSHandler(TrackPointCreator trackPointCreator) {
|
public GPSManager(TrackPointCreator trackPointCreator) {
|
||||||
this.trackPointCreator = trackPointCreator;
|
this.trackPointCreator = trackPointCreator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onStart(@NonNull Context context, @NonNull Handler handler) {
|
public void start(@NonNull Context context, @NonNull Handler handler) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
|
|
||||||
@@ -61,7 +62,7 @@ public class GPSHandler implements LocationListenerCompat, GpsStatus.GpsStatusLi
|
|||||||
|
|
||||||
@SuppressWarnings({"MissingPermission"})
|
@SuppressWarnings({"MissingPermission"})
|
||||||
//TODO upgrade to AGP7.0.0/API31 started complaining about removeUpdates.
|
//TODO upgrade to AGP7.0.0/API31 started complaining about removeUpdates.
|
||||||
public void onStop() {
|
public void stop(Context context) {
|
||||||
if (locationManager != null && context != null) {
|
if (locationManager != null && context != null) {
|
||||||
if (PermissionRequester.GPS.hasPermission(context)) {
|
if (PermissionRequester.GPS.hasPermission(context)) {
|
||||||
LocationManagerCompat.removeUpdates(locationManager, this);
|
LocationManagerCompat.removeUpdates(locationManager, this);
|
||||||
@@ -16,14 +16,13 @@ import java.time.ZoneId;
|
|||||||
|
|
||||||
import de.dennisguse.opentracks.data.models.Distance;
|
import de.dennisguse.opentracks.data.models.Distance;
|
||||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||||
import de.dennisguse.opentracks.sensors.AltitudeSumManager;
|
import de.dennisguse.opentracks.sensors.SensorManager;
|
||||||
import de.dennisguse.opentracks.sensors.BluetoothRemoteSensorManager;
|
|
||||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
|
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates TrackPoints while recording by fusing data from different sensors (e.g., GNSS, barometer, BLE sensors).
|
* Creates TrackPoints while recording by fusing data from different sensors (e.g., GNSS, barometer, BLE sensors).
|
||||||
*/
|
*/
|
||||||
public class TrackPointCreator implements BluetoothRemoteSensorManager.SensorDataSetChangeObserver {
|
public class TrackPointCreator implements SensorManager.SensorDataSetChangeObserver {
|
||||||
|
|
||||||
private static final String TAG = TrackPointCreator.class.getSimpleName();
|
private static final String TAG = TrackPointCreator.class.getSimpleName();
|
||||||
|
|
||||||
@@ -34,45 +33,34 @@ public class TrackPointCreator implements BluetoothRemoteSensorManager.SensorDat
|
|||||||
@NonNull
|
@NonNull
|
||||||
private Clock clock = new MonotonicClock();
|
private Clock clock = new MonotonicClock();
|
||||||
|
|
||||||
private final GPSHandler gpsHandler;
|
private final GPSManager gpsManager;
|
||||||
private BluetoothRemoteSensorManager remoteSensorManager;
|
private SensorManager sensorManager;
|
||||||
private AltitudeSumManager altitudeSumManager;
|
|
||||||
|
|
||||||
public TrackPointCreator(Callback service) {
|
public TrackPointCreator(Callback service, Context context, Handler handler) {
|
||||||
this.service = service;
|
this.service = service;
|
||||||
this.gpsHandler = new GPSHandler(this);
|
this.gpsManager = new GPSManager(this);
|
||||||
|
this.sensorManager = new SensorManager(context, handler, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
TrackPointCreator(GPSHandler gpsHandler, Callback service) {
|
TrackPointCreator(GPSManager gpsManager, Callback service) {
|
||||||
this.service = service;
|
this.service = service;
|
||||||
this.gpsHandler = gpsHandler;
|
this.gpsManager = gpsManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void start(@NonNull Context context, @NonNull Handler handler) {
|
public synchronized void start(@NonNull Context context, @NonNull Handler handler) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
|
||||||
gpsHandler.onStart(context, handler);
|
gpsManager.start(context, handler);
|
||||||
|
sensorManager.start(context, handler);
|
||||||
remoteSensorManager = new BluetoothRemoteSensorManager(context, handler, this);
|
|
||||||
altitudeSumManager = new AltitudeSumManager();
|
|
||||||
|
|
||||||
remoteSensorManager.start();
|
|
||||||
altitudeSumManager.start(context, handler);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isStarted() {
|
private boolean isStarted() {
|
||||||
return context != null;
|
return context != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void reset() {
|
private synchronized void reset() {
|
||||||
if (remoteSensorManager == null || altitudeSumManager == null) {
|
sensorManager.reset();
|
||||||
Log.d(TAG, "No recording running and no reset necessary.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
remoteSensorManager.reset();
|
|
||||||
altitudeSumManager.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private SensorDataSet addSensorData(TrackPoint trackPoint) {
|
private SensorDataSet addSensorData(TrackPoint trackPoint) {
|
||||||
@@ -80,31 +68,12 @@ public class TrackPointCreator implements BluetoothRemoteSensorManager.SensorDat
|
|||||||
Log.w(TAG, "Not started, should not be called.");
|
Log.w(TAG, "Not started, should not be called.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
SensorDataSet sensorDataSet = null;
|
|
||||||
BluetoothRemoteSensorManager localRemoteSensorManager = remoteSensorManager;
|
|
||||||
if (localRemoteSensorManager != null) {
|
|
||||||
sensorDataSet = localRemoteSensorManager.fill(trackPoint);
|
|
||||||
}
|
|
||||||
AltitudeSumManager localAltitudeSumManager = altitudeSumManager;
|
|
||||||
if (localAltitudeSumManager != null) {
|
|
||||||
localAltitudeSumManager.fill(trackPoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
return sensorDataSet;
|
return sensorManager.fill(trackPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void stop() {
|
public synchronized void stop() {
|
||||||
gpsHandler.onStop();
|
gpsManager.stop(context);
|
||||||
|
|
||||||
if (remoteSensorManager != null) {
|
|
||||||
remoteSensorManager.stop();
|
|
||||||
remoteSensorManager = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (altitudeSumManager != null) {
|
|
||||||
altitudeSumManager.stop(context);
|
|
||||||
altitudeSumManager = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.context = null;
|
this.context = null;
|
||||||
}
|
}
|
||||||
@@ -125,7 +94,7 @@ public class TrackPointCreator implements BluetoothRemoteSensorManager.SensorDat
|
|||||||
public void onNewTrackPoint(@NonNull TrackPoint trackPoint) {
|
public void onNewTrackPoint(@NonNull TrackPoint trackPoint) {
|
||||||
addSensorData(trackPoint);
|
addSensorData(trackPoint);
|
||||||
|
|
||||||
boolean stored = service.newTrackPoint(trackPoint, gpsHandler.getThresholdHorizontalAccuracy());
|
boolean stored = service.newTrackPoint(trackPoint, gpsManager.getThresholdHorizontalAccuracy());
|
||||||
if (stored) {
|
if (stored) {
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
@@ -169,26 +138,8 @@ public class TrackPointCreator implements BluetoothRemoteSensorManager.SensorDat
|
|||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public AltitudeSumManager getAltitudeSumManager() {
|
public SensorManager getSensorManager() {
|
||||||
return altitudeSumManager;
|
return sensorManager;
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
@VisibleForTesting
|
|
||||||
public void setAltitudeSumManager(AltitudeSumManager altitudeSumManager) {
|
|
||||||
this.altitudeSumManager = altitudeSumManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
@VisibleForTesting
|
|
||||||
public BluetoothRemoteSensorManager getRemoteSensorManager() {
|
|
||||||
return remoteSensorManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
@VisibleForTesting
|
|
||||||
public void setRemoteSensorManager(BluetoothRemoteSensorManager remoteSensorManager) {
|
|
||||||
this.remoteSensorManager = remoteSensorManager;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -202,8 +153,8 @@ public class TrackPointCreator implements BluetoothRemoteSensorManager.SensorDat
|
|||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public GPSHandler getGpsHandler() {
|
public GPSManager getGpsHandler() {
|
||||||
return gpsHandler;
|
return gpsManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendGpsStatus(GpsStatusValue gpsStatusValue) {
|
void sendGpsStatus(GpsStatusValue gpsStatusValue) {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import androidx.preference.DialogPreference;
|
|||||||
import androidx.preference.PreferenceDialogFragmentCompat;
|
import androidx.preference.PreferenceDialogFragmentCompat;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
|
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||||
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
|
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
|
||||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||||
|
|
||||||
@@ -79,7 +80,7 @@ public class ActivityTypePreference extends DialogPreference {
|
|||||||
textView = view.findViewById(R.id.activity_type_preference_text_view);
|
textView = view.findViewById(R.id.activity_type_preference_text_view);
|
||||||
String category = PreferencesUtils.getDefaultActivity();
|
String category = PreferencesUtils.getDefaultActivity();
|
||||||
textView.setText(category);
|
textView.setText(category);
|
||||||
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(context, R.array.activity_types, android.R.layout.simple_dropdown_item_1line);
|
ArrayAdapter<String> adapter = new ArrayAdapter<>(context, android.R.layout.simple_dropdown_item_1line, ActivityType.getLocalizedStrings(context));
|
||||||
textView.setAdapter(adapter);
|
textView.setAdapter(adapter);
|
||||||
textView.setOnItemClickListener((parent, v, position, id) -> {
|
textView.setOnItemClickListener((parent, v, position, id) -> {
|
||||||
String iconValue = TrackIconUtils.getIconValue(context, (String) textView.getAdapter().getItem(position));
|
String iconValue = TrackIconUtils.getIconValue(context, (String) textView.getAdapter().getItem(position));
|
||||||
|
|||||||
@@ -68,18 +68,10 @@ public class DefaultsSettingsFragment extends PreferenceFragmentCompat implement
|
|||||||
|
|
||||||
ListPreference statsRatePreferences = findPreference(getString(R.string.stats_rate_key));
|
ListPreference statsRatePreferences = findPreference(getString(R.string.stats_rate_key));
|
||||||
|
|
||||||
int entriesId;
|
int entriesId = switch (unitSystem) {
|
||||||
switch (unitSystem) {
|
case METRIC -> R.array.stats_rate_metric_options;
|
||||||
case METRIC:
|
case IMPERIAL, NAUTICAL_IMPERIAL -> R.array.stats_rate_imperial_options;
|
||||||
entriesId = R.array.stats_rate_metric_options;
|
};
|
||||||
break;
|
|
||||||
case IMPERIAL:
|
|
||||||
case NAUTICAL_IMPERIAL:
|
|
||||||
entriesId = R.array.stats_rate_imperial_options;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] entries = getResources().getStringArray(entriesId);
|
String[] entries = getResources().getStringArray(entriesId);
|
||||||
statsRatePreferences.setEntries(entries);
|
statsRatePreferences.setEntries(entries);
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ public class ImportExportSettingsFragment extends PreferenceFragmentCompat {
|
|||||||
DocumentFile directory = IntentUtils.toDocumentFile(getContext(), directoryUri);
|
DocumentFile directory = IntentUtils.toDocumentFile(getContext(), directoryUri);
|
||||||
//Use same value for not set as Androidx ListPreference and EditTextPreference
|
//Use same value for not set as Androidx ListPreference and EditTextPreference
|
||||||
if (directory == null) {
|
if (directory == null) {
|
||||||
return getString(R.string.not_set);
|
return getString(androidx.preference.R.string.not_set);
|
||||||
}
|
}
|
||||||
|
|
||||||
return directoryUri + (directory.canWrite() ? "" : getString(R.string.export_dir_not_writable));
|
return directoryUri + (directory.canWrite() ? "" : getString(R.string.export_dir_not_writable));
|
||||||
|
|||||||
@@ -32,14 +32,9 @@ class PreferencesOpenHelper {
|
|||||||
PreferencesUtils.setInt(R.string.prefs_last_version_key, version);
|
PreferencesUtils.setInt(R.string.prefs_last_version_key, version);
|
||||||
for (int i = 1; i <= version; i++) {
|
for (int i = 1; i <= version; i++) {
|
||||||
switch (i) {
|
switch (i) {
|
||||||
case 1:
|
case 1 -> upgradeFrom0to1();
|
||||||
upgradeFrom0to1();
|
case 2 -> upgradeFrom1to2();
|
||||||
break;
|
default -> throw new RuntimeException("Not implemented: upgrade to " + version);
|
||||||
case 2:
|
|
||||||
upgradeFrom1to2();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Not implemented: upgrade to " + version);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,11 +42,14 @@ import java.util.stream.Stream;
|
|||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.data.models.Distance;
|
import de.dennisguse.opentracks.data.models.Distance;
|
||||||
import de.dennisguse.opentracks.data.models.DistanceFormatter;
|
import de.dennisguse.opentracks.data.models.DistanceFormatter;
|
||||||
|
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||||
|
import de.dennisguse.opentracks.data.models.HeartRateZones;
|
||||||
import de.dennisguse.opentracks.data.models.Speed;
|
import de.dennisguse.opentracks.data.models.Speed;
|
||||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||||
import de.dennisguse.opentracks.io.file.TrackFilenameGenerator;
|
import de.dennisguse.opentracks.io.file.TrackFilenameGenerator;
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.CsvLayoutUtils;
|
import de.dennisguse.opentracks.ui.customRecordingLayout.CsvLayoutUtils;
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.Layout;
|
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayout;
|
||||||
|
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayoutIO;
|
||||||
import de.dennisguse.opentracks.util.IntentDashboardUtils;
|
import de.dennisguse.opentracks.util.IntentDashboardUtils;
|
||||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||||
|
|
||||||
@@ -278,6 +281,12 @@ public class PreferencesUtils {
|
|||||||
return getBoolean(R.string.settings_sensor_bluetooth_service_filter_enabled_key, DEFAULT);
|
return getBoolean(R.string.settings_sensor_bluetooth_service_filter_enabled_key, DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static HeartRateZones getHeartRateZones() {
|
||||||
|
final int DEFAULT = Integer.parseInt(resources.getString(R.string.settings_sensor_heart_rate_max_default));
|
||||||
|
int value = getInt(R.string.settings_sensor_heart_rate_max_key, DEFAULT);
|
||||||
|
return new HeartRateZones(HeartRate.of(value));
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean shouldShowStatsOnLockscreen() {
|
public static boolean shouldShowStatsOnLockscreen() {
|
||||||
final boolean STATS_SHOW_ON_LOCKSCREEN_DEFAULT = resources.getBoolean(R.bool.stats_show_on_lockscreen_while_recording_default);
|
final boolean STATS_SHOW_ON_LOCKSCREEN_DEFAULT = resources.getBoolean(R.bool.stats_show_on_lockscreen_while_recording_default);
|
||||||
return getBoolean(R.string.stats_show_on_lockscreen_while_recording_key, STATS_SHOW_ON_LOCKSCREEN_DEFAULT);
|
return getBoolean(R.string.stats_show_on_lockscreen_while_recording_key, STATS_SHOW_ON_LOCKSCREEN_DEFAULT);
|
||||||
@@ -293,6 +302,11 @@ public class PreferencesUtils {
|
|||||||
return getBoolean(R.string.stats_fullscreen_while_recording_key, DEFAULT);
|
return getBoolean(R.string.stats_fullscreen_while_recording_key, DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean shouldVoiceAnnouncementOnDeviceSpeaker() {
|
||||||
|
final boolean DEFAULT = resources.getBoolean(R.bool.voice_on_device_speaker_default);
|
||||||
|
return getBoolean(R.string.voice_on_device_speaker_key, DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
public static Duration getVoiceAnnouncementFrequency() {
|
public static Duration getVoiceAnnouncementFrequency() {
|
||||||
final int DEFAULT = Integer.parseInt(resources.getString(R.string.voice_announcement_frequency_default));
|
final int DEFAULT = Integer.parseInt(resources.getString(R.string.voice_announcement_frequency_default));
|
||||||
int value = getInt(R.string.voice_announcement_frequency_key, DEFAULT);
|
int value = getInt(R.string.voice_announcement_frequency_key, DEFAULT);
|
||||||
@@ -434,23 +448,21 @@ public class PreferencesUtils {
|
|||||||
|
|
||||||
String displayValue = formatter.formatDistance(distance);
|
String displayValue = formatter.formatDistance(distance);
|
||||||
switch (unitSystem) {
|
switch (unitSystem) {
|
||||||
case METRIC:
|
case METRIC -> {
|
||||||
if (value == recordingDistanceIntervalDefault) {
|
if (value == recordingDistanceIntervalDefault) {
|
||||||
entries[i] = resources.getString(R.string.value_integer_meter_recommended, value);
|
entries[i] = resources.getString(R.string.value_integer_meter_recommended, value);
|
||||||
} else {
|
} else {
|
||||||
entries[i] = displayValue;
|
entries[i] = displayValue;
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case IMPERIAL:
|
case IMPERIAL, NAUTICAL_IMPERIAL -> {
|
||||||
case NAUTICAL_IMPERIAL:
|
|
||||||
if (value == recordingDistanceIntervalDefault) {
|
if (value == recordingDistanceIntervalDefault) {
|
||||||
entries[i] = resources.getString(R.string.value_integer_feet_recommended, (int) distance.toFT());
|
entries[i] = resources.getString(R.string.value_integer_feet_recommended, (int) distance.toFT());
|
||||||
} else {
|
} else {
|
||||||
entries[i] = displayValue;
|
entries[i] = displayValue;
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
default:
|
default -> throw new RuntimeException("Not implemented");
|
||||||
throw new RuntimeException("Not implemented");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -480,23 +492,21 @@ public class PreferencesUtils {
|
|||||||
|
|
||||||
String displayValue = formatter.formatDistance(distance);
|
String displayValue = formatter.formatDistance(distance);
|
||||||
switch (unitSystem) {
|
switch (unitSystem) {
|
||||||
case METRIC:
|
case METRIC -> {
|
||||||
if (value == maxRecordingDistanceDefault) {
|
if (value == maxRecordingDistanceDefault) {
|
||||||
entries[i] = resources.getString(R.string.value_integer_meter_recommended, value);
|
entries[i] = resources.getString(R.string.value_integer_meter_recommended, value);
|
||||||
} else {
|
} else {
|
||||||
entries[i] = displayValue;
|
entries[i] = displayValue;
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case IMPERIAL:
|
case IMPERIAL, NAUTICAL_IMPERIAL -> {
|
||||||
case NAUTICAL_IMPERIAL:
|
|
||||||
if (value == maxRecordingDistanceDefault) {
|
if (value == maxRecordingDistanceDefault) {
|
||||||
entries[i] = resources.getString(R.string.value_integer_feet_recommended, (int) distance.toFT());
|
entries[i] = resources.getString(R.string.value_integer_feet_recommended, (int) distance.toFT());
|
||||||
} else {
|
} else {
|
||||||
entries[i] = displayValue;
|
entries[i] = displayValue;
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
default:
|
default -> throw new RuntimeException("Not implemented");
|
||||||
throw new RuntimeException("Not implemented");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -555,7 +565,7 @@ public class PreferencesUtils {
|
|||||||
|
|
||||||
String displayValue = formatter.formatDistance(distance);
|
String displayValue = formatter.formatDistance(distance);
|
||||||
switch (unitSystem) {
|
switch (unitSystem) {
|
||||||
case METRIC:
|
case METRIC -> {
|
||||||
if (value == recordingGPSAccuracyDefault) {
|
if (value == recordingGPSAccuracyDefault) {
|
||||||
entries[i] = resources.getString(R.string.value_integer_meter_recommended, value);
|
entries[i] = resources.getString(R.string.value_integer_meter_recommended, value);
|
||||||
} else if (value == recordingGPSAccuracyExcellent) {
|
} else if (value == recordingGPSAccuracyExcellent) {
|
||||||
@@ -565,9 +575,8 @@ public class PreferencesUtils {
|
|||||||
} else {
|
} else {
|
||||||
entries[i] = displayValue;
|
entries[i] = displayValue;
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case IMPERIAL:
|
case IMPERIAL, NAUTICAL_IMPERIAL -> {
|
||||||
case NAUTICAL_IMPERIAL:
|
|
||||||
if (value == recordingGPSAccuracyDefault) {
|
if (value == recordingGPSAccuracyDefault) {
|
||||||
entries[i] = resources.getString(R.string.value_integer_feet_recommended, (int) distance.toFT());
|
entries[i] = resources.getString(R.string.value_integer_feet_recommended, (int) distance.toFT());
|
||||||
} else if (value == recordingGPSAccuracyExcellent) {
|
} else if (value == recordingGPSAccuracyExcellent) {
|
||||||
@@ -575,9 +584,8 @@ public class PreferencesUtils {
|
|||||||
} else {
|
} else {
|
||||||
entries[i] = displayValue;
|
entries[i] = displayValue;
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
default:
|
default -> throw new RuntimeException("Not implemented");
|
||||||
throw new RuntimeException("Not implemented");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -603,32 +611,30 @@ public class PreferencesUtils {
|
|||||||
float value = Float.parseFloat(entryValues[i]);
|
float value = Float.parseFloat(entryValues[i]);
|
||||||
|
|
||||||
switch (unitSystem) {
|
switch (unitSystem) {
|
||||||
case METRIC:
|
case METRIC -> {
|
||||||
if (value == idleSpeedDefault) {
|
if (value == idleSpeedDefault) {
|
||||||
entries[i] = resources.getString(R.string.value_float_kilometer_hour_recommended, value);
|
entries[i] = resources.getString(R.string.value_float_kilometer_hour_recommended, value);
|
||||||
} else {
|
} else {
|
||||||
entries[i] = resources.getString(R.string.value_float_kilometer_hour, value);
|
entries[i] = resources.getString(R.string.value_float_kilometer_hour, value);
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case IMPERIAL:
|
case IMPERIAL -> {
|
||||||
double valueMPH = Speed.ofKMH(value).toMPH();
|
double valueMPH = Speed.ofKMH(value).toMPH();
|
||||||
|
|
||||||
if (value == idleSpeedDefault) {
|
if (value == idleSpeedDefault) {
|
||||||
entries[i] = resources.getString(R.string.value_float_mile_hour_recommended, valueMPH);
|
entries[i] = resources.getString(R.string.value_float_mile_hour_recommended, valueMPH);
|
||||||
} else {
|
} else {
|
||||||
entries[i] = resources.getString(R.string.value_float_mile_hour, valueMPH);
|
entries[i] = resources.getString(R.string.value_float_mile_hour, valueMPH);
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case NAUTICAL_IMPERIAL:
|
case NAUTICAL_IMPERIAL -> {
|
||||||
double valueKnots = Speed.ofKMH(value).toKnots();
|
double valueKnots = Speed.ofKMH(value).toKnots();
|
||||||
if (value == idleSpeedDefault) {
|
if (value == idleSpeedDefault) {
|
||||||
entries[i] = resources.getString(R.string.value_float_knots_recommended, valueKnots);
|
entries[i] = resources.getString(R.string.value_float_knots_recommended, valueKnots);
|
||||||
} else {
|
} else {
|
||||||
entries[i] = resources.getString(R.string.value_float_knots, valueKnots);
|
entries[i] = resources.getString(R.string.value_float_knots, valueKnots);
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
default:
|
default -> throw new RuntimeException("Not implemented");
|
||||||
throw new RuntimeException("Not implemented");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -753,35 +759,35 @@ public class PreferencesUtils {
|
|||||||
/**
|
/**
|
||||||
* @return custom layout selected or the first one if any has been selected or the one selected is not exists anymore.
|
* @return custom layout selected or the first one if any has been selected or the one selected is not exists anymore.
|
||||||
*/
|
*/
|
||||||
public static Layout getCustomLayout() {
|
public static RecordingLayout getCustomLayout() {
|
||||||
String csvCustomLayouts = getString(R.string.stats_custom_layouts_key, buildDefaultLayout());
|
String csvCustomLayouts = getString(R.string.stats_custom_layouts_key, buildDefaultLayout());
|
||||||
String[] csvLines = csvCustomLayouts.split(CsvLayoutUtils.LINE_SEPARATOR);
|
String[] csvLines = csvCustomLayouts.split(CsvLayoutUtils.LINE_SEPARATOR);
|
||||||
String layoutSelected = getString(R.string.stats_custom_layout_selected_layout_key, null);
|
String layoutSelected = getString(R.string.stats_custom_layout_selected_layout_key, null);
|
||||||
if (layoutSelected == null) {
|
if (layoutSelected == null) {
|
||||||
return Layout.fromCsv(csvLines[0], resources);
|
return RecordingLayoutIO.fromCsv(csvLines[0], resources);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String line : csvLines) {
|
for (String line : csvLines) {
|
||||||
Layout layout = Layout.fromCsv(line, resources);
|
RecordingLayout recordingLayout = RecordingLayoutIO.fromCsv(line, resources);
|
||||||
if (layout.sameName(layoutSelected)) {
|
if (recordingLayout.sameName(layoutSelected)) {
|
||||||
return layout;
|
return recordingLayout;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Layout.fromCsv(csvLines[0], resources);
|
return RecordingLayoutIO.fromCsv(csvLines[0], resources);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void updateCustomLayouts(@NonNull List<Layout> layouts) {
|
public static void updateCustomLayouts(@NonNull List<RecordingLayout> recordingLayouts) {
|
||||||
setString(R.string.stats_custom_layouts_key, layouts.stream().map(Layout::toCsv).collect(Collectors.joining(CsvLayoutUtils.LINE_SEPARATOR)));
|
setString(R.string.stats_custom_layouts_key, RecordingLayoutIO.toCSV(recordingLayouts));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void updateCustomLayout(@NonNull Layout layout) {
|
public static void updateCustomLayout(@NonNull RecordingLayout recordingLayout) {
|
||||||
List<Layout> preferenceLayouts = PreferencesUtils.getAllCustomLayouts();
|
List<RecordingLayout> preferenceRecordingLayouts = PreferencesUtils.getAllCustomLayouts();
|
||||||
Optional<Layout> layoutToBeUpdated = preferenceLayouts.stream().filter(l -> l.sameName(layout)).findFirst();
|
Optional<RecordingLayout> layoutToBeUpdated = preferenceRecordingLayouts.stream().filter(l -> l.sameName(recordingLayout)).findFirst();
|
||||||
if (layoutToBeUpdated.isPresent()) {
|
if (layoutToBeUpdated.isPresent()) {
|
||||||
layoutToBeUpdated.get().replaceAllFields(layout.getFields());
|
layoutToBeUpdated.get().replaceAllFields(recordingLayout.getFields());
|
||||||
layoutToBeUpdated.get().setColumnsPerRow(layout.getColumnsPerRow());
|
layoutToBeUpdated.get().setColumnsPerRow(recordingLayout.getColumnsPerRow());
|
||||||
PreferencesUtils.updateCustomLayouts(preferenceLayouts);
|
PreferencesUtils.updateCustomLayouts(preferenceRecordingLayouts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -795,19 +801,19 @@ public class PreferencesUtils {
|
|||||||
setString(R.string.stats_custom_layout_selected_layout_key, layoutName);
|
setString(R.string.stats_custom_layout_selected_layout_key, layoutName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Layout> getAllCustomLayouts() {
|
public static List<RecordingLayout> getAllCustomLayouts() {
|
||||||
List<Layout> layouts = new ArrayList<>();
|
List<RecordingLayout> recordingLayouts = new ArrayList<>();
|
||||||
String csvCustomLayout = getString(R.string.stats_custom_layouts_key, buildDefaultLayout());
|
String csvCustomLayout = getString(R.string.stats_custom_layouts_key, buildDefaultLayout());
|
||||||
String[] csvLines = csvCustomLayout.split(CsvLayoutUtils.LINE_SEPARATOR);
|
String[] csvLines = csvCustomLayout.split(CsvLayoutUtils.LINE_SEPARATOR);
|
||||||
for (String line : csvLines) {
|
for (String line : csvLines) {
|
||||||
layouts.add(Layout.fromCsv(line, resources));
|
recordingLayouts.add(RecordingLayoutIO.fromCsv(line, resources));
|
||||||
}
|
}
|
||||||
|
|
||||||
return layouts;
|
return recordingLayouts;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<String> getAllCustomLayoutNames() {
|
public static List<String> getAllCustomLayoutNames() {
|
||||||
return getAllCustomLayouts().stream().map(Layout::getName).collect(Collectors.toList());
|
return getAllCustomLayouts().stream().map(RecordingLayout::getName).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void resetCustomLayoutPreferences() {
|
public static void resetCustomLayoutPreferences() {
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public class SensorsSettingsFragment extends PreferenceFragmentCompat {
|
|||||||
if (newValue instanceof String) {
|
if (newValue instanceof String) {
|
||||||
try {
|
try {
|
||||||
int newValueInt = Integer.parseInt((String) newValue);
|
int newValueInt = Integer.parseInt((String) newValue);
|
||||||
return newValueInt > 500 && newValueInt < 4000;
|
return newValueInt >= 100 && newValueInt < 4000;
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
Log.w(TAG, "Entered string is no number.");
|
Log.w(TAG, "Entered string is no number.");
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-26
@@ -17,10 +17,9 @@ import de.dennisguse.opentracks.AbstractActivity;
|
|||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.databinding.ActivitySettingsCustomLayoutBinding;
|
import de.dennisguse.opentracks.databinding.ActivitySettingsCustomLayoutBinding;
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.DataField;
|
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.SettingsCustomLayoutEditAdapter;
|
import de.dennisguse.opentracks.ui.customRecordingLayout.SettingsCustomLayoutEditAdapter;
|
||||||
import de.dennisguse.opentracks.ui.util.ArrayAdapterFilterDisabled;
|
import de.dennisguse.opentracks.ui.util.ArrayAdapterFilterDisabled;
|
||||||
import de.dennisguse.opentracks.util.StatisticsUtils;
|
|
||||||
|
|
||||||
public class SettingsCustomLayoutEditActivity extends AbstractActivity implements SettingsCustomLayoutEditAdapter.SettingsCustomLayoutItemClickListener {
|
public class SettingsCustomLayoutEditActivity extends AbstractActivity implements SettingsCustomLayoutEditAdapter.SettingsCustomLayoutItemClickListener {
|
||||||
|
|
||||||
@@ -30,8 +29,8 @@ public class SettingsCustomLayoutEditActivity extends AbstractActivity implement
|
|||||||
private SettingsCustomLayoutEditAdapter adapterFieldsVisible;
|
private SettingsCustomLayoutEditAdapter adapterFieldsVisible;
|
||||||
private SettingsCustomLayoutEditAdapter adapterFieldsHidden;
|
private SettingsCustomLayoutEditAdapter adapterFieldsHidden;
|
||||||
private String profile;
|
private String profile;
|
||||||
private Layout layoutFieldsVisible;
|
private RecordingLayout recordingLayoutFieldsVisible;
|
||||||
private Layout layoutFieldsHidden;
|
private RecordingLayout recordingLayoutFieldsHidden;
|
||||||
private int numColumns;
|
private int numColumns;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -39,12 +38,12 @@ public class SettingsCustomLayoutEditActivity extends AbstractActivity implement
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
// Recycler view with visible stats.
|
// Recycler view with visible stats.
|
||||||
Layout layout = getIntent().getParcelableExtra(EXTRA_LAYOUT);
|
RecordingLayout recordingLayout = getIntent().getParcelableExtra(EXTRA_LAYOUT);
|
||||||
profile = layout.getName();
|
profile = recordingLayout.getName();
|
||||||
layoutFieldsVisible = StatisticsUtils.filterVisible(layout, true);
|
recordingLayoutFieldsVisible = recordingLayout.toRecordingLayout(true);
|
||||||
adapterFieldsVisible = new SettingsCustomLayoutEditAdapter(this, this, layoutFieldsVisible);
|
adapterFieldsVisible = new SettingsCustomLayoutEditAdapter(this, this, recordingLayoutFieldsVisible);
|
||||||
|
|
||||||
numColumns = layout.getColumnsPerRow();
|
numColumns = recordingLayout.getColumnsPerRow();
|
||||||
RecyclerView recyclerViewVisible = viewBinding.recyclerViewVisible;
|
RecyclerView recyclerViewVisible = viewBinding.recyclerViewVisible;
|
||||||
gridLayoutManager = new GridLayoutManager(this, numColumns);
|
gridLayoutManager = new GridLayoutManager(this, numColumns);
|
||||||
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
|
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
|
||||||
@@ -65,7 +64,7 @@ public class SettingsCustomLayoutEditActivity extends AbstractActivity implement
|
|||||||
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
|
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
|
||||||
int fromPosition = viewHolder.getAdapterPosition();
|
int fromPosition = viewHolder.getAdapterPosition();
|
||||||
int toPosition = target.getAdapterPosition();
|
int toPosition = target.getAdapterPosition();
|
||||||
layoutFieldsVisible = adapterFieldsVisible.move(fromPosition, toPosition);
|
recordingLayoutFieldsVisible = adapterFieldsVisible.move(fromPosition, toPosition);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,8 +89,8 @@ public class SettingsCustomLayoutEditActivity extends AbstractActivity implement
|
|||||||
viewBinding.rowsOptions.setText(rowsOptionAdapter.getItem(numColumns - 1).toString(), false);
|
viewBinding.rowsOptions.setText(rowsOptionAdapter.getItem(numColumns - 1).toString(), false);
|
||||||
|
|
||||||
// Recycler view with not visible stats.
|
// Recycler view with not visible stats.
|
||||||
layoutFieldsHidden = StatisticsUtils.filterVisible(layout, false);
|
recordingLayoutFieldsHidden = recordingLayout.toRecordingLayout(false);
|
||||||
adapterFieldsHidden = new SettingsCustomLayoutEditAdapter(this, this, layoutFieldsHidden);
|
adapterFieldsHidden = new SettingsCustomLayoutEditAdapter(this, this, recordingLayoutFieldsHidden);
|
||||||
RecyclerView recyclerViewNotVisible = viewBinding.recyclerViewNotVisible;
|
RecyclerView recyclerViewNotVisible = viewBinding.recyclerViewNotVisible;
|
||||||
recyclerViewNotVisible.setLayoutManager(new LinearLayoutManager(this));
|
recyclerViewNotVisible.setLayoutManager(new LinearLayoutManager(this));
|
||||||
recyclerViewNotVisible.setAdapter(adapterFieldsHidden);
|
recyclerViewNotVisible.setAdapter(adapterFieldsHidden);
|
||||||
@@ -103,18 +102,18 @@ public class SettingsCustomLayoutEditActivity extends AbstractActivity implement
|
|||||||
@Override
|
@Override
|
||||||
protected void onPause() {
|
protected void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
if (!layoutFieldsVisible.getFields().isEmpty() || !layoutFieldsHidden.getFields().isEmpty()) {
|
if (!recordingLayoutFieldsVisible.getFields().isEmpty() || !recordingLayoutFieldsHidden.getFields().isEmpty()) {
|
||||||
Layout layout = new Layout(profile, numColumns);
|
RecordingLayout recordingLayout = new RecordingLayout(profile, numColumns);
|
||||||
layout.addFields(layoutFieldsVisible.getFields());
|
recordingLayout.addFields(recordingLayoutFieldsVisible.getFields());
|
||||||
layout.addFields(layoutFieldsHidden.getFields());
|
recordingLayout.addFields(recordingLayoutFieldsHidden.getFields());
|
||||||
PreferencesUtils.updateCustomLayout(layout);
|
PreferencesUtils.updateCustomLayout(recordingLayout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
layoutFieldsVisible = null;
|
recordingLayoutFieldsVisible = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -132,24 +131,24 @@ public class SettingsCustomLayoutEditActivity extends AbstractActivity implement
|
|||||||
if (which == 0) {
|
if (which == 0) {
|
||||||
field.togglePrimary();
|
field.togglePrimary();
|
||||||
} else {
|
} else {
|
||||||
layoutFieldsVisible.removeField(field);
|
recordingLayoutFieldsVisible.removeField(field);
|
||||||
field.toggleVisibility();
|
field.toggleVisibility();
|
||||||
layoutFieldsHidden.addField(field);
|
recordingLayoutFieldsHidden.addField(field);
|
||||||
}
|
}
|
||||||
|
|
||||||
adapterFieldsVisible.swapValues(layoutFieldsVisible);
|
adapterFieldsVisible.swapValues(recordingLayoutFieldsVisible);
|
||||||
adapterFieldsHidden.swapValues(layoutFieldsHidden);
|
adapterFieldsHidden.swapValues(recordingLayoutFieldsHidden);
|
||||||
})
|
})
|
||||||
.create()
|
.create()
|
||||||
.show();
|
.show();
|
||||||
} else {
|
} else {
|
||||||
layoutFieldsHidden.removeField(field);
|
recordingLayoutFieldsHidden.removeField(field);
|
||||||
field.toggleVisibility();
|
field.toggleVisibility();
|
||||||
layoutFieldsVisible.addField(field);
|
recordingLayoutFieldsVisible.addField(field);
|
||||||
viewBinding.scrollView.fullScroll(ScrollView.FOCUS_UP);
|
viewBinding.scrollView.fullScroll(ScrollView.FOCUS_UP);
|
||||||
|
|
||||||
adapterFieldsVisible.swapValues(layoutFieldsVisible);
|
adapterFieldsVisible.swapValues(recordingLayoutFieldsVisible);
|
||||||
adapterFieldsHidden.swapValues(layoutFieldsHidden);
|
adapterFieldsHidden.swapValues(recordingLayoutFieldsHidden);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -23,7 +23,7 @@ import com.google.android.material.textfield.TextInputLayout;
|
|||||||
import de.dennisguse.opentracks.AbstractActivity;
|
import de.dennisguse.opentracks.AbstractActivity;
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.databinding.ActivitySettingsCustomLayoutListBinding;
|
import de.dennisguse.opentracks.databinding.ActivitySettingsCustomLayoutListBinding;
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.Layout;
|
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayout;
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.SettingsCustomLayoutListAdapter;
|
import de.dennisguse.opentracks.ui.customRecordingLayout.SettingsCustomLayoutListAdapter;
|
||||||
import de.dennisguse.opentracks.ui.util.RecyclerViewSwipeDeleteCallback;
|
import de.dennisguse.opentracks.ui.util.RecyclerViewSwipeDeleteCallback;
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ public class SettingsCustomLayoutListActivity extends AbstractActivity implement
|
|||||||
@Override
|
@Override
|
||||||
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
|
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
|
||||||
final int position = viewHolder.getAdapterPosition();
|
final int position = viewHolder.getAdapterPosition();
|
||||||
final Layout item = adapter.getLayouts().get(position);
|
final RecordingLayout item = adapter.getLayouts().get(position);
|
||||||
|
|
||||||
adapter.removeLayout(position);
|
adapter.removeLayout(position);
|
||||||
|
|
||||||
@@ -150,9 +150,9 @@ public class SettingsCustomLayoutListActivity extends AbstractActivity implement
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSettingsCustomLayoutProfileClicked(@NonNull Layout layout) {
|
public void onSettingsCustomLayoutProfileClicked(@NonNull RecordingLayout recordingLayout) {
|
||||||
Intent intent = new Intent(this, SettingsCustomLayoutEditActivity.class);
|
Intent intent = new Intent(this, SettingsCustomLayoutEditActivity.class);
|
||||||
intent.putExtra(SettingsCustomLayoutEditActivity.EXTRA_LAYOUT, layout);
|
intent.putExtra(SettingsCustomLayoutEditActivity.EXTRA_LAYOUT, recordingLayout);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user