mirror of
https://codeberg.org/OpenTracksApp/OpenTracks.git
synced 2026-10-15 23:33:07 +02:00
Compare commits
119 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1672a5c722 | |||
| bf552a9219 | |||
| 348f697b56 | |||
| 887d4700d4 | |||
| 391dfd30e2 | |||
| b416857001 | |||
| df3720ef0d | |||
| b784a47af6 | |||
| 0f3156e71b | |||
| 0035c85922 | |||
| 976a50ca15 | |||
| c0ad53eef1 | |||
| 1192b02c0a | |||
| 7a3abe9962 | |||
| 60b989c13a | |||
| 340f16dc99 | |||
| 7cf29f7cea | |||
| e39352cc9e | |||
| e2964e805e | |||
| 8cab43d558 | |||
| 13f2a09652 | |||
| 7d4072c0d8 | |||
| 310b35713d | |||
| 7e6237de3a | |||
| 772dad4837 | |||
| 99126fa357 | |||
| b2c91d7592 | |||
| 3c5a1be16b | |||
| 01d83deefd | |||
| bc2a3ddee3 | |||
| da70fca389 | |||
| 67bbd9ca24 | |||
| 5e1293fc82 | |||
| 610da5a106 | |||
| 2b5744ccc1 | |||
| f0341e831f | |||
| 99e5dfbe72 | |||
| 0fb43f6530 | |||
| 6f9c788965 | |||
| f2b1d2a386 | |||
| 18789a0729 | |||
| d0f99be9d3 | |||
| a899be4598 | |||
| 1e518a4c35 | |||
| 1c6012596e | |||
| 978206dee4 | |||
| 8713eb3d0c | |||
| d450e045b1 | |||
| 9f87f67153 | |||
| 3a073a3b0a | |||
| bc1b4e3cd2 | |||
| e09b04308c | |||
| f3cf1a2571 | |||
| 7be4ac436c | |||
| 405120a78b | |||
| e86aeace79 | |||
| afe5c6ee92 | |||
| 055764b462 | |||
| 360738f4c8 | |||
| 3bd3e8fd9b | |||
| 5cf421cf42 | |||
| 455522a03d | |||
| b4c258c686 | |||
| 05ee54d58c | |||
| 28fbcdd2ca | |||
| 0231343aa8 | |||
| 852885cca1 | |||
| d3c1247617 | |||
| 0b30ecabb1 | |||
| 107cc14a2d | |||
| 6d3d8bb83c | |||
| 9c8e0335ef | |||
| 8c648c69e8 | |||
| 4422464f8d | |||
| d094531779 | |||
| a01341d235 | |||
| 83065dad39 | |||
| 68ba3e0c2a | |||
| 3f21edc44c | |||
| aefd1a6a35 | |||
| 74fa73e4a4 | |||
| 0a006d64f9 | |||
| 997ab6060e | |||
| 6d926ca7a8 | |||
| 3c1f1a9ba5 | |||
| ed5da55fba | |||
| d1e9649505 | |||
| 2431d60171 | |||
| e08f5e7273 | |||
| 7d2a6fff59 | |||
| a5158a55d9 | |||
| 15fbd90ca4 | |||
| eebc33f60c | |||
| ba2a755fb7 | |||
| 344c50a20c | |||
| 9a9a610321 | |||
| 8af427ea48 | |||
| 7b7d7c8061 | |||
| 7d6919d3f9 | |||
| 7eceb979c3 | |||
| bd48944ad5 | |||
| 503cba1315 | |||
| a313cb6244 | |||
| 7f0c6309c0 | |||
| 02c578327d | |||
| 7fec0dcff4 | |||
| 34eb7a833a | |||
| 5db3aa7ac1 | |||
| 47fd7c51a9 | |||
| 2f6f0b4e17 | |||
| 41b794f66b | |||
| e2c592367a | |||
| 7a24c56474 | |||
| 3f6bd6926a | |||
| 508af45449 | |||
| 0f89cd7875 | |||
| 29b522b8a2 | |||
| 14e63a1a54 | |||
| e559583d23 |
@@ -10,6 +10,7 @@
|
|||||||
Has updatable firmware that requires a user account at polar.com
|
Has updatable firmware that requires a user account at polar.com
|
||||||
* Wahoo Tickr (Model: WFBTHR02)
|
* Wahoo Tickr (Model: WFBTHR02)
|
||||||
* Mi Band 3, Amazfit Band 5, Amazfit Bip. These devices are manufactured by Huami, and other Huami devices will very likely work. Theoretically, this sensor must be enabled, but testing reveals that it works even without enabling. For details on how to enable see [Gadgetbridge wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Huami-Heartrate-measurement#bluetooth-heart-rate-sensor)
|
* Mi Band 3, Amazfit Band 5, Amazfit Bip. These devices are manufactured by Huami, and other Huami devices will very likely work. Theoretically, this sensor must be enabled, but testing reveals that it works even without enabling. For details on how to enable see [Gadgetbridge wiki](https://codeberg.org/Freeyourgadget/Gadgetbridge/wiki/Huami-Heartrate-measurement#bluetooth-heart-rate-sensor)
|
||||||
|
* Amazfit Neo. But first you have to enable "Discoverable" and "Activity heart rate sharing" in the official Zepp app. (after pairing with OpenTracks you can disable them again)
|
||||||
|
|
||||||
## CSCP: Cycling Cadence and Speed Service
|
## CSCP: Cycling Cadence and Speed Service
|
||||||
|
|
||||||
@@ -19,6 +20,12 @@ However, often only one value is provided.
|
|||||||
### Speed
|
### Speed
|
||||||
|
|
||||||
* Garmin Speed Sensor 2
|
* Garmin Speed Sensor 2
|
||||||
|
* Has updatable firmware that requires an account for garmin.com
|
||||||
|
* This sensor reports speed data as cadence.
|
||||||
|
A workaround is in place.
|
||||||
|
* This sensor sometimes counts backwards (unknown software version).
|
||||||
|
A workaround is in place.
|
||||||
|
|
||||||
* Wahoo Speed (Model: WFRPMSPD)
|
* Wahoo Speed (Model: WFRPMSPD)
|
||||||
|
|
||||||
### Cadence
|
### Cadence
|
||||||
|
|||||||
+4
-4
@@ -6,7 +6,7 @@ buildscript {
|
|||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:4.1.2'
|
classpath 'com.android.tools.build:gradle:4.1.3'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,8 +51,8 @@ android {
|
|||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "de.dennisguse.opentracks"
|
applicationId "de.dennisguse.opentracks"
|
||||||
versionCode 3890
|
versionCode 4009
|
||||||
versionName "v3.15.3"
|
versionName "v3.17.3"
|
||||||
|
|
||||||
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0-alpha04'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0-alpha05'
|
||||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||||
|
|
||||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||||
|
|||||||
@@ -2,17 +2,7 @@
|
|||||||
interface TrackExporter {
|
interface TrackExporter {
|
||||||
}
|
}
|
||||||
class KmzTrackExporter implements TrackExporter
|
class KmzTrackExporter implements TrackExporter
|
||||||
KmzTrackExporter o-- FileTrackExporter
|
class KmlTrackExporter implements TrackExporter
|
||||||
class FileTrackExporter implements TrackExporter {
|
|
||||||
/' One track only '/
|
|
||||||
}
|
|
||||||
FileTrackExporter o-- TrackWriter
|
|
||||||
|
|
||||||
interface TrackWriter {
|
|
||||||
}
|
|
||||||
class GpxTrackWriter implements TrackWriter
|
|
||||||
class KmlTrackWriter implements TrackWriter
|
|
||||||
|
|
||||||
|
|
||||||
interface TrackImporter {
|
interface TrackImporter {
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
@startuml
|
@startuml
|
||||||
SensorManager --> ElevationSumManager
|
SensorManager --> AltitudeSumManager
|
||||||
LocationManager --> TrackRecordingService
|
LocationManager --> TrackRecordingService
|
||||||
ElevationSumManager --> TrackRecordingService
|
ElevationSumManager --> TrackRecordingService
|
||||||
|
|
||||||
@@ -14,6 +14,6 @@ TrackDataHub --> StatisticsRecordingFragment : TrackPoints, TrackStatistics, Way
|
|||||||
TrackDataHub --> StatisticsRecordedFragment : TrackPoints, TrackStatistics, Waypoints (push)
|
TrackDataHub --> StatisticsRecordedFragment : TrackPoints, TrackStatistics, Waypoints (push)
|
||||||
TrackDataHub --> ChartFragment --> ChartView
|
TrackDataHub --> ChartFragment --> ChartView
|
||||||
|
|
||||||
TrackRecordingServiceConnection --> StatisticsRecordingFragment : Sensor data (heartrate, cadence, elevation gain; pull 1s)
|
TrackRecordingServiceConnection --> StatisticsRecordingFragment : Sensor data (heartrate, cadence, altitude gain; pull 1s)
|
||||||
TrackRecordingServiceConnection --> TrackController : Start time (pull, onStart)
|
TrackRecordingServiceConnection --> TrackController : Start time (pull, onStart)
|
||||||
@enduml
|
@enduml
|
||||||
@@ -18,7 +18,7 @@ class TrackStatistics {
|
|||||||
- totalTime
|
- totalTime
|
||||||
- movingTime
|
- movingTime
|
||||||
- maxSpeed
|
- maxSpeed
|
||||||
- totalElevationGain
|
- totalGain
|
||||||
}
|
}
|
||||||
|
|
||||||
class TrackStatisticsUpdater {
|
class TrackStatisticsUpdater {
|
||||||
|
|||||||
@@ -1,8 +1,32 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
targetNamespace="http://opentracksapp.com/xmlschemas/v1">
|
targetNamespace="http://opentracksapp.com/xmlschemas/v1">
|
||||||
|
|
||||||
<xs:element name="trackuuid"
|
<xsd:element name="trackuuid"
|
||||||
type="xs:string" />
|
type="xsd:string">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation xml:lang="en">
|
||||||
|
A unique track identifier stored as UUID.
|
||||||
|
</xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:element>
|
||||||
|
|
||||||
</xs:schema>
|
<xsd:element name="loss"
|
||||||
|
type="xsd:float">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation xml:lang="en">
|
||||||
|
The lost elevation in meters from the previous to the current TrackPoint.
|
||||||
|
Only used in GPX.
|
||||||
|
</xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="gain"
|
||||||
|
type="xsd:string">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation xml:lang="en">
|
||||||
|
The gained elevation in meters from the previous to the current TrackPoint.
|
||||||
|
Only used in GPX.
|
||||||
|
</xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
height="22"
|
||||||
|
width="22"
|
||||||
|
fill="#000000"
|
||||||
|
version="1.0"
|
||||||
|
x="0px"
|
||||||
|
y="0px"
|
||||||
|
viewBox="0 0 21.84952 21.99934"
|
||||||
|
enable-background="new 0 0 99.316 99.997"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg10"
|
||||||
|
sodipodi:docname="ic_activity_kayaking.svg"
|
||||||
|
inkscape:version="1.0.2 (e86c870879, 2021-01-15)"><metadata
|
||||||
|
id="metadata16"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs14" /><sodipodi:namedview
|
||||||
|
pagecolor="#000000"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="1"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1024"
|
||||||
|
id="namedview12"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:pagecheckerboard="false"
|
||||||
|
inkscape:zoom="22.472123"
|
||||||
|
inkscape:cx="11.932326"
|
||||||
|
inkscape:cy="8.4821218"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg10"
|
||||||
|
inkscape:document-rotation="0" /><path
|
||||||
|
id="path2"
|
||||||
|
style="fill:#ffffff;stroke-width:0.20758"
|
||||||
|
d="m 18.296044,13.516712 c -1.017125,-0.01824 -1.903328,0.770666 -2.945647,0.500454 -0.952298,-0.271819 -1.950516,-0.765534 -2.949277,-0.338881 -0.938402,0.492101 -2.078868,0.493759 -3.017025,0 C 8.3845167,13.251481 7.3852162,13.744815 6.4321693,14.017166 5.229901,14.31201 4.2005498,13.256398 2.9984219,13.558671 2.2076549,13.806805 1.0222411,14.11299 1.0222411,14.11299 v 1.764949 c 1.0226762,0.01947 1.9128198,-0.776355 2.9601398,-0.498166 0.4416703,0.216761 1.6682446,0.3639 1.6529583,0.601941 l -1.3879215,1.383539 c -0.7385797,-0.502111 -1.5118619,0.0527 -1.9901253,0.625328 -0.5087615,0.568011 -1.1635382,1.025896 -1.5505475,1.687065 -0.44607206,0.952772 0.6576964,2.056499 1.6104671,1.610525 0.7016688,-0.428027 1.2006595,-1.122907 1.8093513,-1.672458 0.6218606,-0.429609 0.7776032,-1.247655 0.6297837,-1.861615 l 2.0038313,-1.999046 c 0.9553792,-0.44042 2.0942104,-0.606317 3.0559286,-0.07975 0.9566571,0.429062 1.9815791,0.09313 2.8934291,-0.263824 1.038124,-0.339466 1.95525,0.382078 2.955963,0.461915 1.087184,0.105691 2.019821,-0.784126 3.121615,-0.493622 0.55673,-0.01509 1.943197,1.034046 1.977683,0.148315 v -1.471252 c -0.878739,0.05089 -1.592095,-0.588897 -2.468753,-0.540124 z"
|
||||||
|
sodipodi:nodetypes="sccccccccccccccccccccccss" /><path
|
||||||
|
id="path4"
|
||||||
|
style="fill:#ffffff;stroke-width:0.20758"
|
||||||
|
d="M 19.135498,4.6094374 C 19.777729,3.9239548 20.518106,3.3189058 21.090567,2.5760423 21.630775,1.6723294 20.631775,0.46345938 19.642408,0.81572972 18.952145,1.1177762 18.525406,1.7968856 17.966518,2.2833866 17.44088,2.7777653 16.748383,3.4079168 17.123672,4.2079927 17.316456,4.5791871 16.670933,4.8230987 16.479267,5.1359209 L 15.065885,6.5509368 C 13.459538,5.9569743 11.849312,5.3583867 10.246781,4.7652258 9.2045735,4.5310231 8.2546176,5.3827898 8.1060604,6.3824119 7.7002321,7.7689937 7.2645314,9.1479847 6.8776693,10.539386 h -5.851069 v 2.366209 c 1.0341353,-0.06583 1.9719234,-0.790016 3.0482038,-0.491433 0.8514673,0.182595 1.7032854,0.742921 2.5975093,0.377297 1.0893234,-0.553582 2.4097276,-0.562618 3.4983496,-0.0022 0.895986,0.368665 1.751474,-0.190407 2.60469,-0.37517 1.078334,-0.302717 2.015803,0.427862 3.051521,0.490909 0.985146,-0.07753 1.867505,-0.470806 2.884112,-0.579104 0.847652,-0.0903 2.163908,-1.785603 2.163908,-1.785603 0,0 -1.819193,-0.01538 -2.935123,0.0029 -1.990467,-2.45e-4 -3.980934,-0.0021 -5.9714,-0.0044 l 2.483907,-2.4814163 c 0.762424,0.5726687 1.957001,-0.3877847 1.20272,-1.1977389 l 2.032212,-2.039685 c 0.462782,0.2519942 1.079225,0.1706574 1.448288,-0.2104864 z m -8.060346,5.9299486 c -0.486522,0.101516 -0.612149,-0.06543 -0.374498,-0.502616 l 0.909432,-3.029364 2.189559,0.8099787 z"
|
||||||
|
sodipodi:nodetypes="cccccccccccccccccsccccccccccccc" /><path
|
||||||
|
id="path851"
|
||||||
|
style="fill:#ffffff;stroke-width:1.16861"
|
||||||
|
d="m 12.520186,2.6733336 c 0.05505,1.1807821 -1.406378,2.0132887 -2.394197,1.3656163 -1.0445314,-0.553724 -1.0179219,-2.235304 0.04332,-2.7559413 1.018447,-0.59197226 2.378758,0.20782 2.350878,1.390325 z" /></svg>
|
||||||
|
After Width: | Height: | Size: 4.6 KiB |
@@ -0,0 +1,4 @@
|
|||||||
|
v3.15.4: OpenTracks
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Voice annoucements interval should be in minutes (introduced in v3.15.0)
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
v3.16.0: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Cycling: get distance from BLE speed sensor
|
||||||
|
- Reworked UI presented while recording
|
||||||
|
- UI performance optimizations
|
||||||
|
- Exported filenames: <First 32bits UUID>_<track's name>.<extension>
|
||||||
|
- Import multiple files via Intent (e.g., from Gadgetbridge)
|
||||||
|
- GPX: export power data
|
||||||
|
- Updated translations
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Voice announcement use minutes correctly
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
v3.17.0: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Add icon for kayaking
|
||||||
|
- Charts draw missing sensor data properly
|
||||||
|
- Default: show coordinates while recording
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
- GPX schema corrected
|
||||||
|
- GPX exported altitude loss as sensor distance
|
||||||
|
- Statistics not computed correctly if using a speed/distance sensor
|
||||||
|
|
||||||
|
Developer:
|
||||||
|
- Distance/speed value objects
|
||||||
|
- Replaced communication via SharedPreferences
|
||||||
|
- Use non-static service binder
|
||||||
|
- Use altitude in the code base (instead of elevation)
|
||||||
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
v3.17.1: OpenTracks
|
||||||
|
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Interval length in meters instead of kilometers (introduced in v3.17.0)
|
||||||
|
- Elevation gain/loss not recorded (introduced in v3.17.0)
|
||||||
|
- Announcements not working on Android 11
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
v3.17.2: OpenTracks
|
||||||
|
|
||||||
|
WARNING: The v3.17.0+ releases contain a memory leak.
|
||||||
|
This issues is NOT FIXED by this release.
|
||||||
|
It is recommended to not upgrade if you still use a release before v3.17.0
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Crash while using voice announcements
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
v3.17.3: OpenTracks
|
||||||
|
|
||||||
|
WARNING: The v3.17.0+ releases contain a memory leak.
|
||||||
|
This issues is NOT FIXED by this release.
|
||||||
|
It is recommended to not upgrade if you still use a release before v3.17.0
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Crash if using pace for voice announcements (introduced in v3.17.0)
|
||||||
|
- Statistics showed pace initially (introduced in v3.17.0)
|
||||||
|
- Rare crash when deleting tracks
|
||||||
|
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package de.dennisguse.opentracks.chart;
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.data.Distance;
|
||||||
|
import de.dennisguse.opentracks.content.data.Speed;
|
||||||
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
|
import de.dennisguse.opentracks.fragments.TrackStubUtils;
|
||||||
|
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class ChartPointTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void create_by_time() {
|
||||||
|
// given
|
||||||
|
TrackStatistics statistics = new TrackStatistics();
|
||||||
|
statistics.setTotalTime(Duration.ofSeconds(1000));
|
||||||
|
|
||||||
|
// when
|
||||||
|
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), 0, false, false);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals(1000000, (long) point.getTimeOrDistance());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void create_by_distance() {
|
||||||
|
// given
|
||||||
|
TrackStatistics statistics = new TrackStatistics();
|
||||||
|
statistics.setTotalDistance(Distance.of(1000));
|
||||||
|
|
||||||
|
// when
|
||||||
|
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), 0, true, true);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals(1, (long) point.getTimeOrDistance());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void create_get_altitude_speed_and_pace() {
|
||||||
|
// given
|
||||||
|
TrackStatistics statistics = new TrackStatistics();
|
||||||
|
|
||||||
|
// when
|
||||||
|
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(10), 50, false, true);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals(0.05, point.getAltitude(), 0.01);
|
||||||
|
assertEquals(36, point.getSpeed(), 0.01);
|
||||||
|
assertEquals(1.66, point.getPace(), 0.01);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void create_sensorNotAvailable() {
|
||||||
|
// given
|
||||||
|
TrackStatistics statistics = new TrackStatistics();
|
||||||
|
|
||||||
|
// when
|
||||||
|
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(10), 50, false, true);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertNull(point.getHeartRate());
|
||||||
|
assertNull(point.getCadence());
|
||||||
|
assertNull(point.getPower());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void create_sensorAvailable() {
|
||||||
|
// given
|
||||||
|
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint();
|
||||||
|
trackPoint.setHeartRate_bpm(100f);
|
||||||
|
trackPoint.setCyclingCadence_rpm(101f);
|
||||||
|
trackPoint.setPower(102f);
|
||||||
|
|
||||||
|
TrackStatistics statistics = new TrackStatistics();
|
||||||
|
|
||||||
|
// when
|
||||||
|
ChartPoint point = new ChartPoint(statistics, trackPoint, Speed.of(10), 50, false, true);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals(100.0, point.getHeartRate(), 0.01);
|
||||||
|
assertEquals(101.0, point.getCadence(), 0.01);
|
||||||
|
assertEquals(102.0, point.getPower(), 0.01);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -44,16 +44,16 @@ public class ChartValueSeriesTest {
|
|||||||
ApplicationProvider.getApplicationContext(),
|
ApplicationProvider.getApplicationContext(),
|
||||||
Integer.MIN_VALUE,
|
Integer.MIN_VALUE,
|
||||||
Integer.MAX_VALUE,
|
Integer.MAX_VALUE,
|
||||||
new int[]{100, 1000},
|
new int[]{1, 100, 1000},
|
||||||
R.string.description_elevation_metric,
|
R.string.description_altitude_metric,
|
||||||
R.string.description_elevation_imperial,
|
R.string.description_altitude_imperial,
|
||||||
R.color.chart_elevation_fill,
|
R.color.chart_altitude_fill,
|
||||||
R.color.chart_elevation_border,
|
R.color.chart_altitude_border,
|
||||||
15,
|
15,
|
||||||
18) {
|
18) {
|
||||||
@Override
|
@Override
|
||||||
double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
||||||
return chartPoint.getElevation();
|
return chartPoint.getAltitude();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -77,6 +77,17 @@ public class ChartValueSeriesTest {
|
|||||||
assertFalse(series.isEnabled());
|
assertFalse(series.isEnabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVerySmallUpdates() {
|
||||||
|
series.update(new ChartPoint(1f));
|
||||||
|
series.update(new ChartPoint(2f));
|
||||||
|
series.update(new ChartPoint(3f));
|
||||||
|
series.updateDimension();
|
||||||
|
assertEquals(1, series.getInterval());
|
||||||
|
assertEquals(1, series.getMinMarkerValue());
|
||||||
|
assertEquals(6, series.getMaxMarkerValue());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSmallUpdates() {
|
public void testSmallUpdates() {
|
||||||
series.update(new ChartPoint(0));
|
series.update(new ChartPoint(0));
|
||||||
|
|||||||
+29
-27
@@ -29,6 +29,8 @@ import java.time.Duration;
|
|||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
|
import de.dennisguse.opentracks.content.data.Distance;
|
||||||
|
import de.dennisguse.opentracks.content.data.Speed;
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||||
import de.dennisguse.opentracks.util.StringUtils;
|
import de.dennisguse.opentracks.util.StringUtils;
|
||||||
@@ -60,24 +62,24 @@ public class DescriptionGeneratorTest {
|
|||||||
public void testGenerateTrackDescription() {
|
public void testGenerateTrackDescription() {
|
||||||
Track track = new Track();
|
Track track = new Track();
|
||||||
TrackStatistics stats = new TrackStatistics();
|
TrackStatistics stats = new TrackStatistics();
|
||||||
stats.setTotalDistance(20000);
|
stats.setTotalDistance(Distance.of(20000));
|
||||||
stats.setTotalTime(Duration.ofMillis(600000));
|
stats.setTotalTime(Duration.ofMillis(600000));
|
||||||
stats.setMovingTime(Duration.ofMillis(300000));
|
stats.setMovingTime(Duration.ofMillis(300000));
|
||||||
stats.setMaxSpeed(100);
|
stats.setMaxSpeed(Speed.of(100));
|
||||||
stats.setMaxElevation(550);
|
stats.setMaxAltitude(550);
|
||||||
stats.setMinElevation(-500);
|
stats.setMinAltitude(-500);
|
||||||
stats.setTotalElevationGain(6000f);
|
stats.setTotalAltitudeGain(6000f);
|
||||||
stats.setTotalElevationLoss(6000f);
|
stats.setTotalAltitudeLoss(6000f);
|
||||||
stats.setStartTime(START_TIME);
|
stats.setStartTime(START_TIME);
|
||||||
track.setTrackStatistics(stats);
|
track.setTrackStatistics(stats);
|
||||||
track.setCategory("hiking");
|
track.setCategory("hiking");
|
||||||
String expected = //"Created by"
|
String expected = //"Created by"
|
||||||
"<a href='https://github.com/OpenTracksApp/OpenTracks'>OpenTracks (Debug)</a><p>"
|
"<a href='https://github.com/OpenTracksApp/OpenTracks'>OpenTracks (Debug)</a><p>"
|
||||||
+ "Name: -<br>"
|
+ "Name: -<br>"
|
||||||
+ "Activity type: hiking<br>"
|
+ "Activity type: hiking<br>"
|
||||||
+ "Description: -<br>"
|
+ "Description: -<br>"
|
||||||
+ "Total distance: 20.00 km (12.4 mi)<br>"
|
+ "Total distance: 20.00 km (12.4 mi)<br>"
|
||||||
+ "Total time: 10:00<br>"
|
+ "Total time: 10:00<br>"
|
||||||
+ "Moving time: 05:00<br>"
|
+ "Moving time: 05:00<br>"
|
||||||
+ "Average speed: 120.00 km/h (74.6 mi/h)<br>"
|
+ "Average speed: 120.00 km/h (74.6 mi/h)<br>"
|
||||||
+ "Average moving speed: 240.00 km/h (149.1 mi/h)<br>"
|
+ "Average moving speed: 240.00 km/h (149.1 mi/h)<br>"
|
||||||
@@ -95,17 +97,17 @@ public class DescriptionGeneratorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGenerateTrackDescriptionWithoutMaxMinElevation() {
|
public void testGenerateTrackDescriptionWithoutMaxMinAltitude() {
|
||||||
Track track = new Track();
|
Track track = new Track();
|
||||||
TrackStatistics stats = new TrackStatistics();
|
TrackStatistics stats = new TrackStatistics();
|
||||||
stats.setTotalDistance(20000);
|
stats.setTotalDistance(Distance.of(20000));
|
||||||
stats.setTotalTime(Duration.ofMillis(600000));
|
stats.setTotalTime(Duration.ofMillis(600000));
|
||||||
stats.setMovingTime(Duration.ofMillis(300000));
|
stats.setMovingTime(Duration.ofMillis(300000));
|
||||||
stats.setMaxSpeed(100);
|
stats.setMaxSpeed(Speed.of(100));
|
||||||
stats.setMaxElevation(Double.POSITIVE_INFINITY);
|
stats.setMaxAltitude(Double.POSITIVE_INFINITY);
|
||||||
stats.setMinElevation(Double.NEGATIVE_INFINITY);
|
stats.setMinAltitude(Double.NEGATIVE_INFINITY);
|
||||||
stats.setTotalElevationGain(6000f);
|
stats.setTotalAltitudeGain(6000f);
|
||||||
stats.setTotalElevationLoss(6000f);
|
stats.setTotalAltitudeLoss(6000f);
|
||||||
stats.setStartTime(START_TIME);
|
stats.setStartTime(START_TIME);
|
||||||
track.setTrackStatistics(stats);
|
track.setTrackStatistics(stats);
|
||||||
track.setCategory("hiking");
|
track.setCategory("hiking");
|
||||||
@@ -132,12 +134,12 @@ public class DescriptionGeneratorTest {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link DescriptionGenerator#writeDistance(double, StringBuilder, int, String)}.
|
* Tests {@link DescriptionGenerator#writeDistance(Distance, StringBuilder, int, String)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testWriteDistance() {
|
public void testWriteDistance() {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
descriptionGenerator.writeDistance(1100, builder, R.string.description_total_distance, "<br>");
|
descriptionGenerator.writeDistance(Distance.of(1100), builder, R.string.description_total_distance, "<br>");
|
||||||
assertEquals("Total distance: 1.10 km (0.7 mi)<br>", builder.toString());
|
assertEquals("Total distance: 1.10 km (0.7 mi)<br>", builder.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,32 +154,32 @@ public class DescriptionGeneratorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link DescriptionGenerator#writeSpeed(double, StringBuilder, int, String)}.
|
* Tests {@link DescriptionGenerator#writeSpeed(Speed, StringBuilder, int, String)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testWriteSpeed() {
|
public void testWriteSpeed() {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
descriptionGenerator.writeSpeed(1.1, builder, R.string.description_average_speed, "\n");
|
descriptionGenerator.writeSpeed(Speed.of(1.1), builder, R.string.description_average_speed, "\n");
|
||||||
assertEquals("Average speed: 3.96 km/h (2.5 mi/h)\n", builder.toString());
|
assertEquals("Average speed: 3.96 km/h (2.5 mi/h)\n", builder.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link DescriptionGenerator#writeElevation(double, StringBuilder, int, String)}.
|
* Tests {@link DescriptionGenerator#writeAltitude(double, StringBuilder, int, String)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testWriteElevation() {
|
public void testWriteAltitude() {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
descriptionGenerator.writeElevation(4.2, builder, R.string.description_min_elevation, "<br>");
|
descriptionGenerator.writeAltitude(4.2, builder, R.string.description_min_altitude, "<br>");
|
||||||
assertEquals("Min elevation: 4 m (14 ft)<br>", builder.toString());
|
assertEquals("Min elevation: 4 m (14 ft)<br>", builder.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link DescriptionGenerator#writePace(double, StringBuilder, int, String)}.
|
* Tests {@link DescriptionGenerator#writePace(Speed, StringBuilder, int, String)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testWritePace() {
|
public void testWritePace() {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
descriptionGenerator.writePace(1.1, builder, R.string.description_average_pace_in_minute, "\n");
|
descriptionGenerator.writePace(Speed.of(1.1), builder, R.string.description_average_pace_in_minute, "\n");
|
||||||
assertEquals("Average pace: 15:09 min/km (24:23 min/mi)\n", builder.toString());
|
assertEquals("Average pace: 15:09 min/km (24:23 min/mi)\n", builder.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,14 @@ import android.util.Pair;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
|
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
|
||||||
|
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||||
import de.dennisguse.opentracks.util.FileUtils;
|
import de.dennisguse.opentracks.util.FileUtils;
|
||||||
|
|
||||||
public class TestDataUtil {
|
public class TestDataUtil {
|
||||||
@@ -18,8 +21,8 @@ public class TestDataUtil {
|
|||||||
public static final double INITIAL_LATITUDE = 37.0;
|
public static final double INITIAL_LATITUDE = 37.0;
|
||||||
public static final double INITIAL_LONGITUDE = -57.0;
|
public static final double INITIAL_LONGITUDE = -57.0;
|
||||||
public static final double ALTITUDE_INTERVAL = 2.5;
|
public static final double ALTITUDE_INTERVAL = 2.5;
|
||||||
public static final float ELEVATION_GAIN = 3;
|
public static final float ALTITUDE_GAIN = 3;
|
||||||
public static final float ELEVATION_LOSS = 3;
|
public static final float ALTITUDE_LOSS = 3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a track without any trackPoints.
|
* Create a track without any trackPoints.
|
||||||
@@ -56,7 +59,7 @@ public class TestDataUtil {
|
|||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
List<TrackPoint> trackPoints = List.of(
|
List<TrackPoint> trackPoints = List.of(
|
||||||
TrackPoint.createSegmentStartManualWithTime(Instant.ofEpochMilli(i++ + 1)),
|
TrackPoint.createSegmentStartManualWithTime(Instant.ofEpochSecond(i++)),
|
||||||
createTrackPoint(i++),
|
createTrackPoint(i++),
|
||||||
createTrackPoint(i++),
|
createTrackPoint(i++),
|
||||||
createTrackPoint(i++),
|
createTrackPoint(i++),
|
||||||
@@ -64,19 +67,23 @@ public class TestDataUtil {
|
|||||||
createTrackPoint(i++),
|
createTrackPoint(i++),
|
||||||
createTrackPoint(i++),
|
createTrackPoint(i++),
|
||||||
createTrackPoint(i++),
|
createTrackPoint(i++),
|
||||||
TrackPoint.createSegmentEndWithTime(Instant.ofEpochSecond(i++ + 1)),
|
createTrackPoint(i++, TrackPoint.Type.SEGMENT_END_MANUAL),
|
||||||
|
|
||||||
TrackPoint.createSegmentStartManualWithTime(Instant.ofEpochSecond(i++)),
|
TrackPoint.createSegmentStartManualWithTime(Instant.ofEpochSecond(i++)),
|
||||||
createTrackPoint(i++),
|
createTrackPoint(i++),
|
||||||
createTrackPoint(i++),
|
createTrackPoint(i++),
|
||||||
createTrackPoint(i++),
|
createTrackPoint(i++),
|
||||||
TrackPoint.createSegmentEndWithTime(Instant.ofEpochSecond(i++ + 1))
|
createTrackPoint(i, TrackPoint.Type.SEGMENT_END_MANUAL)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
//TODO Use TrackStatisticsUpdater
|
||||||
|
TrackStatistics stats = new TrackStatistics();
|
||||||
|
stats.setTotalDistance(Distance.of(0));
|
||||||
|
stats.setTotalTime(Duration.ofMillis(0));
|
||||||
List<Marker> markers = List.of(
|
List<Marker> markers = List.of(
|
||||||
new Marker("Marker 1", "Marker description 1", "Marker category 3", "", trackId, 0.0, 0, trackPoints.get(1), null),
|
new Marker("Marker 1", "Marker description 1", "Marker category 3", "", trackId, stats, trackPoints.get(1), null),
|
||||||
new Marker("Marker 2", "Marker description 2", "Marker category 3", "", trackId, 0.0, 0, trackPoints.get(4), null),
|
new Marker("Marker 2", "Marker description 2", "Marker category 3", "", trackId, stats, trackPoints.get(4), null),
|
||||||
new Marker("Marker 3", "Marker description 3", "Marker category 3", "", trackId, 0.0, 0, trackPoints.get(5), null)
|
new Marker("Marker 3", "Marker description 3", "Marker category 3", "", trackId, stats, trackPoints.get(5), null)
|
||||||
);
|
);
|
||||||
|
|
||||||
return new TrackData(track, trackPoints, markers);
|
return new TrackData(track, trackPoints, markers);
|
||||||
@@ -103,11 +110,6 @@ public class TestDataUtil {
|
|||||||
return pair.first;
|
return pair.first;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a location.
|
|
||||||
*
|
|
||||||
* @param i the index for the TrackPoint.
|
|
||||||
*/
|
|
||||||
public static TrackPoint createTrackPoint(int i) {
|
public static TrackPoint createTrackPoint(int i) {
|
||||||
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT);
|
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT);
|
||||||
trackPoint.setLatitude(INITIAL_LATITUDE + (double) i / 10000.0);
|
trackPoint.setLatitude(INITIAL_LATITUDE + (double) i / 10000.0);
|
||||||
@@ -115,13 +117,13 @@ public class TestDataUtil {
|
|||||||
trackPoint.setAccuracy((float) i / 100.0f);
|
trackPoint.setAccuracy((float) i / 100.0f);
|
||||||
trackPoint.setAltitude(i * ALTITUDE_INTERVAL);
|
trackPoint.setAltitude(i * ALTITUDE_INTERVAL);
|
||||||
trackPoint.setTime(Instant.ofEpochSecond(i + 1));
|
trackPoint.setTime(Instant.ofEpochSecond(i + 1));
|
||||||
trackPoint.setSpeed(5f + (i / 10f));
|
trackPoint.setSpeed(Speed.of(5f + (i / 10f)));
|
||||||
|
|
||||||
trackPoint.setHeartRate_bpm(100f + i);
|
trackPoint.setHeartRate_bpm(100f + i);
|
||||||
trackPoint.setCyclingCadence_rpm(300f + i);
|
trackPoint.setCyclingCadence_rpm(300f + i);
|
||||||
trackPoint.setPower(400f + i);
|
trackPoint.setPower(400f + i);
|
||||||
trackPoint.setElevationGain(ELEVATION_GAIN);
|
trackPoint.setAltitudeGain(ALTITUDE_GAIN);
|
||||||
trackPoint.setElevationLoss(ELEVATION_LOSS);
|
trackPoint.setAltitudeLoss(ALTITUDE_LOSS);
|
||||||
return trackPoint;
|
return trackPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,6 +150,20 @@ public class TestDataUtil {
|
|||||||
Uri photoUri = FileUtils.getUriForFile(context, dstFile);
|
Uri photoUri = FileUtils.getUriForFile(context, dstFile);
|
||||||
String photoUrl = photoUri.toString();
|
String photoUrl = photoUri.toString();
|
||||||
|
|
||||||
return new Marker("Marker name", "Marker description", "Marker category", "", trackId, 0.0, 0, trackPoint, photoUrl);
|
//TODO Use TrackStatisticsUpdater
|
||||||
|
TrackStatistics stats = new TrackStatistics();
|
||||||
|
stats.setTotalDistance(Distance.of(0));
|
||||||
|
stats.setTotalTime(Duration.ofMillis(0));
|
||||||
|
|
||||||
|
return new Marker("Marker name", "Marker description", "Marker category", "", trackId, stats, trackPoint, photoUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<TrackPoint> getTrackPoints(ContentProviderUtils contentProviderUtils, Track.Id trackId) {
|
||||||
|
TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(trackId, null);
|
||||||
|
ArrayList<TrackPoint> trackPoints = new ArrayList<>();
|
||||||
|
while (trackPointIterator.hasNext()) {
|
||||||
|
trackPoints.add(trackPointIterator.next());
|
||||||
|
}
|
||||||
|
return trackPoints;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-13
@@ -7,8 +7,8 @@ import java.util.List;
|
|||||||
|
|
||||||
public class TestSensorDataUtil {
|
public class TestSensorDataUtil {
|
||||||
|
|
||||||
private List<TrackPoint> trackPointList = new ArrayList<>();
|
private final List<TrackPoint> trackPointList = new ArrayList<>();
|
||||||
private List<SensorData> sensorDataList = new ArrayList<>();
|
private final List<SensorData> sensorDataList = new ArrayList<>();
|
||||||
|
|
||||||
public void add(Instant time, Float hr, Float cadence, Float power, TrackPoint.Type type) {
|
public void add(Instant time, Float hr, Float cadence, Float power, TrackPoint.Type type) {
|
||||||
sensorDataList.add(new TestSensorDataUtil.SensorData(time, hr, cadence, power, type));
|
sensorDataList.add(new TestSensorDataUtil.SensorData(time, hr, cadence, power, type));
|
||||||
@@ -22,9 +22,9 @@ public class TestSensorDataUtil {
|
|||||||
tp.setAccuracy(1f);
|
tp.setAccuracy(1f);
|
||||||
tp.setAltitude(1f);
|
tp.setAltitude(1f);
|
||||||
tp.setTime(time);
|
tp.setTime(time);
|
||||||
tp.setSpeed(5f + (i / 10f));
|
tp.setSpeed(Speed.of(5f + (i / 10f)));
|
||||||
tp.setElevationGain(3f);
|
tp.setAltitudeGain(3f);
|
||||||
tp.setElevationLoss(3f);
|
tp.setAltitudeLoss(3f);
|
||||||
trackPointList.add(tp);
|
trackPointList.add(tp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ public class TestSensorDataUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public SensorDataStats computeStats() {
|
public SensorDataStats computeStats() {
|
||||||
if (sensorDataList == null || sensorDataList.size() <= 1) {
|
if (sensorDataList.size() <= 1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,9 +49,9 @@ public class TestSensorDataUtil {
|
|||||||
if (dataPrev.type != TrackPoint.Type.SEGMENT_START_MANUAL) {
|
if (dataPrev.type != TrackPoint.Type.SEGMENT_START_MANUAL) {
|
||||||
timeElapsed = dataCurrent.type != TrackPoint.Type.SEGMENT_START_MANUAL ? dataCurrent.time.getEpochSecond() - dataPrev.time.getEpochSecond() : 0;
|
timeElapsed = dataCurrent.type != TrackPoint.Type.SEGMENT_START_MANUAL ? dataCurrent.time.getEpochSecond() - dataPrev.time.getEpochSecond() : 0;
|
||||||
stats.avgHr += (dataPrev.hr * timeElapsed);
|
stats.avgHr += (dataPrev.hr * timeElapsed);
|
||||||
stats.maxHr = dataPrev.hr > stats.maxHr ? dataPrev.hr : stats.maxHr;
|
stats.maxHr = Math.max(dataPrev.hr, stats.maxHr);
|
||||||
stats.avgCadence += (dataPrev.cadence * timeElapsed);
|
stats.avgCadence += (dataPrev.cadence * timeElapsed);
|
||||||
stats.maxCadence = dataPrev.cadence > stats.maxCadence ? dataPrev.cadence : stats.maxCadence;
|
stats.maxCadence = Math.max(dataPrev.cadence, stats.maxCadence);
|
||||||
stats.avgPower += (dataPrev.power * timeElapsed);
|
stats.avgPower += (dataPrev.power * timeElapsed);
|
||||||
|
|
||||||
movingTime += timeElapsed;
|
movingTime += timeElapsed;
|
||||||
@@ -67,11 +67,11 @@ public class TestSensorDataUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class SensorData {
|
private static class SensorData {
|
||||||
Instant time;
|
final Instant time;
|
||||||
float hr;
|
final float hr;
|
||||||
float cadence;
|
final float cadence;
|
||||||
float power;
|
final float power;
|
||||||
TrackPoint.Type type;
|
final TrackPoint.Type type;
|
||||||
|
|
||||||
public SensorData(Instant time, Float hr, Float cadence, Float power, TrackPoint.Type type) {
|
public SensorData(Instant time, Float hr, Float cadence, Float power, TrackPoint.Type type) {
|
||||||
this.time = time;
|
this.time = time;
|
||||||
|
|||||||
+30
-35
@@ -42,8 +42,10 @@ import java.util.Random;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.data.Distance;
|
||||||
import de.dennisguse.opentracks.content.data.Marker;
|
import de.dennisguse.opentracks.content.data.Marker;
|
||||||
import de.dennisguse.opentracks.content.data.MarkerColumns;
|
import de.dennisguse.opentracks.content.data.MarkerColumns;
|
||||||
|
import de.dennisguse.opentracks.content.data.Speed;
|
||||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||||
import de.dennisguse.opentracks.content.data.TestSensorDataUtil;
|
import de.dennisguse.opentracks.content.data.TestSensorDataUtil;
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
@@ -94,29 +96,22 @@ public class CustomContentProviderUtilsTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLocationIterator_noPoints() {
|
public void testLocationIterator_noPoints() {
|
||||||
testIterator(new Track.Id(1), 0, 1);
|
testIterator(new Track.Id(1), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLocationIterator_noBatchAscending() {
|
public void testLocationIterator_noAscending() {
|
||||||
testIterator(new Track.Id(1), 50, 100);
|
testIterator(new Track.Id(1), 50);
|
||||||
testIterator(new Track.Id(2), 50, 50);
|
testIterator(new Track.Id(2), 50);
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testLocationIterator_batchAscending() {
|
|
||||||
testIterator(new Track.Id(1), 50, 11);
|
|
||||||
testIterator(new Track.Id(2), 50, 25);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLocationIterator_largeTrack() {
|
public void testLocationIterator_largeTrack() {
|
||||||
testIterator(new Track.Id(1), 20000, 2000);
|
testIterator(new Track.Id(1), 20000 / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testIterator(Track.Id trackId, int numPoints, int batchSize) {
|
private void testIterator(Track.Id trackId, int numPoints) {
|
||||||
long lastPointId = initializeTrack(trackId, numPoints);
|
TrackPoint.Id lastPointId = initializeTrack(trackId, numPoints);
|
||||||
contentProviderUtils.setDefaultCursorBatchSize(batchSize);
|
|
||||||
List<TrackPoint> locations = new ArrayList<>(numPoints);
|
List<TrackPoint> locations = new ArrayList<>(numPoints);
|
||||||
try (TrackPointIterator it = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
try (TrackPointIterator it = contentProviderUtils.getTrackPointLocationIterator(trackId, null)) {
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
@@ -124,13 +119,13 @@ public class CustomContentProviderUtilsTest {
|
|||||||
assertNotNull(trackPoint);
|
assertNotNull(trackPoint);
|
||||||
locations.add(trackPoint);
|
locations.add(trackPoint);
|
||||||
// Make sure the IDs are returned in the right order.
|
// Make sure the IDs are returned in the right order.
|
||||||
assertEquals(lastPointId - numPoints + locations.size(), trackPoint.getId().getId());
|
assertEquals(lastPointId.getId() - numPoints + locations.size(), trackPoint.getId().getId());
|
||||||
}
|
}
|
||||||
assertEquals(numPoints, locations.size());
|
assertEquals(numPoints, locations.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private long initializeTrack(Track.Id id, int numPoints) {
|
private TrackPoint.Id initializeTrack(Track.Id id, int numPoints) {
|
||||||
Track track = new Track();
|
Track track = new Track();
|
||||||
track.setId(id);
|
track.setId(id);
|
||||||
track.setName("Test: " + id.getId());
|
track.setName("Test: " + id.getId());
|
||||||
@@ -150,17 +145,17 @@ public class CustomContentProviderUtilsTest {
|
|||||||
contentProviderUtils.bulkInsertTrackPoint(trackPoints, id);
|
contentProviderUtils.bulkInsertTrackPoint(trackPoints, id);
|
||||||
|
|
||||||
// Load all inserted trackPoints.
|
// Load all inserted trackPoints.
|
||||||
long lastPointId = -1;
|
TrackPoint.Id lastPointId = null;
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
try (TrackPointIterator it = contentProviderUtils.getTrackPointLocationIterator(id, null)) {
|
try (TrackPointIterator it = contentProviderUtils.getTrackPointLocationIterator(id, null)) {
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
TrackPoint trackPoint = it.next();
|
TrackPoint trackPoint = it.next();
|
||||||
lastPointId = trackPoint.getId().getId();
|
lastPointId = trackPoint.getId();
|
||||||
counter++;
|
counter++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue(numPoints == 0 || lastPointId > 0);
|
assertTrue(numPoints == 0 || lastPointId.getId() > 0);
|
||||||
assertEquals(numPoints, counter);
|
assertEquals(numPoints, counter);
|
||||||
|
|
||||||
return lastPointId;
|
return lastPointId;
|
||||||
@@ -435,11 +430,11 @@ public class CustomContentProviderUtilsTest {
|
|||||||
statistics.setStopTime(Instant.ofEpochMilli(2500));
|
statistics.setStopTime(Instant.ofEpochMilli(2500));
|
||||||
statistics.setTotalTime(Duration.ofMillis(1500));
|
statistics.setTotalTime(Duration.ofMillis(1500));
|
||||||
statistics.setMovingTime(Duration.ofMillis(700));
|
statistics.setMovingTime(Duration.ofMillis(700));
|
||||||
statistics.setTotalDistance(750.0);
|
statistics.setTotalDistance(Distance.of(750.0));
|
||||||
statistics.setTotalElevationGain(50.0f);
|
statistics.setTotalAltitudeGain(50.0f);
|
||||||
statistics.setMaxSpeed(60.0);
|
statistics.setMaxSpeed(Speed.of(60.0));
|
||||||
statistics.setMaxElevation(1250.0);
|
statistics.setMaxAltitude(1250.0);
|
||||||
statistics.setMinElevation(1200.0);
|
statistics.setMinAltitude(1200.0);
|
||||||
|
|
||||||
track.first.setTrackStatistics(statistics);
|
track.first.setTrackStatistics(statistics);
|
||||||
contentProviderUtils.insertTrack(track.first);
|
contentProviderUtils.insertTrack(track.first);
|
||||||
@@ -571,10 +566,10 @@ public class CustomContentProviderUtilsTest {
|
|||||||
// statistics.setTotalTime(1500L);
|
// statistics.setTotalTime(1500L);
|
||||||
// statistics.setMovingTime(700L);
|
// statistics.setMovingTime(700L);
|
||||||
// statistics.setTotalDistance(750.0);
|
// statistics.setTotalDistance(750.0);
|
||||||
// statistics.setTotalElevationGain(50.0);
|
// statistics.setTotalAltitudeGain(50.0);
|
||||||
// statistics.setMaxSpeed(60.0);
|
// statistics.setMaxSpeed(60.0);
|
||||||
// statistics.setMaxElevation(1250.0);
|
// statistics.setMaxAltitude(1250.0);
|
||||||
// statistics.setMinElevation(1200.0);
|
// statistics.setMinAltitude(1200.0);
|
||||||
//
|
//
|
||||||
// track.setTrackStatistics(statistics);
|
// track.setTrackStatistics(statistics);
|
||||||
// TestDataUtil.insertTrackWithLocations(contentProviderUtils, track);
|
// TestDataUtil.insertTrackWithLocations(contentProviderUtils, track);
|
||||||
@@ -613,7 +608,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
contentProviderUtils.insertMarker(marker3);
|
contentProviderUtils.insertMarker(marker3);
|
||||||
contentProviderUtils.insertMarker(marker4);
|
contentProviderUtils.insertMarker(marker4);
|
||||||
|
|
||||||
assertEquals(4, contentProviderUtils.getNextMarkerNumber(trackId));
|
assertEquals(Integer.valueOf(4), contentProviderUtils.getNextMarkerNumber(trackId));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -770,9 +765,9 @@ public class CustomContentProviderUtilsTest {
|
|||||||
|
|
||||||
// when / then
|
// when / then
|
||||||
contentProviderUtils.bulkInsertTrackPoint(track.second, trackId);
|
contentProviderUtils.bulkInsertTrackPoint(track.second, trackId);
|
||||||
assertEquals(20, contentProviderUtils.getTrackPointCursor(trackId, null, 1000).getCount());
|
assertEquals(20, contentProviderUtils.getTrackPointCursor(trackId, null).getCount());
|
||||||
contentProviderUtils.bulkInsertTrackPoint(track.second.subList(0, 8), trackId);
|
contentProviderUtils.bulkInsertTrackPoint(track.second.subList(0, 8), trackId);
|
||||||
assertEquals(28, contentProviderUtils.getTrackPointCursor(trackId, null, 1000).getCount());
|
assertEquals(28, contentProviderUtils.getTrackPointCursor(trackId, null).getCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -814,7 +809,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
assertEquals(longitude, trackPoint.getLongitude(), 0.01);
|
assertEquals(longitude, trackPoint.getLongitude(), 0.01);
|
||||||
assertEquals(latitude, trackPoint.getLatitude(), 0.01);
|
assertEquals(latitude, trackPoint.getLatitude(), 0.01);
|
||||||
assertEquals(time, trackPoint.getTime().toEpochMilli());
|
assertEquals(time, trackPoint.getTime().toEpochMilli());
|
||||||
assertEquals(speed, trackPoint.getSpeed(), 0.01);
|
assertEquals(speed, trackPoint.getSpeed().toMPS(), 0.01);
|
||||||
assertFalse(trackPoint.hasHeartRate());
|
assertFalse(trackPoint.hasHeartRate());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -829,7 +824,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
||||||
|
|
||||||
contentProviderUtils.insertTrackPoint(TestDataUtil.createTrackPoint(22), trackId);
|
contentProviderUtils.insertTrackPoint(TestDataUtil.createTrackPoint(22), trackId);
|
||||||
assertEquals(11, contentProviderUtils.getTrackPoints(trackId).size());
|
assertEquals(11, contentProviderUtils.getTrackPointCursor(trackId, null).getCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -847,7 +842,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
contentProviderUtils.insertTrackPoint(trackPoint, trackId);
|
contentProviderUtils.insertTrackPoint(trackPoint, trackId);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||||
assertTrue(trackPoints.get(10).hasHeartRate());
|
assertTrue(trackPoints.get(10).hasHeartRate());
|
||||||
assertEquals(trackPoint.getHeartRate_bpm(), trackPoints.get(10).getHeartRate_bpm(), 0.01);
|
assertEquals(trackPoint.getHeartRate_bpm(), trackPoints.get(10).getHeartRate_bpm(), 0.01);
|
||||||
assertEquals(trackPoint.getCyclingCadence_rpm(), trackPoints.get(10).getCyclingCadence_rpm(), 0.01);
|
assertEquals(trackPoint.getCyclingCadence_rpm(), trackPoints.get(10).getCyclingCadence_rpm(), 0.01);
|
||||||
@@ -879,7 +874,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
.map(TrackPoint.Id::new).collect(Collectors.toList());
|
.map(TrackPoint.Id::new).collect(Collectors.toList());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
Cursor cursor = contentProviderUtils.getTrackPointCursor(trackId, trackpointIds.get(8), 5);
|
Cursor cursor = contentProviderUtils.getTrackPointCursor(trackId, trackpointIds.get(8));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(2, cursor.getCount());
|
assertEquals(2, cursor.getCount());
|
||||||
@@ -1257,7 +1252,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetSensorStats_veryLongActivity12h() {
|
public void testGetSensorStats_veryLongActivity12h() {
|
||||||
testGetSensorStats_randomData(43200, false);
|
testGetSensorStats_randomData(43200 / 6, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
+10
-8
@@ -96,34 +96,36 @@ public class SensorDataCyclingTest {
|
|||||||
@Test
|
@Test
|
||||||
public void compute_speed() {
|
public void compute_speed() {
|
||||||
// given
|
// given
|
||||||
SensorDataCycling.Speed previous = new SensorDataCycling.Speed("sensorAddress", "sensorName", 1, 6184);
|
SensorDataCycling.DistanceSpeed previous = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 1, 6184);
|
||||||
SensorDataCycling.Speed current = new SensorDataCycling.Speed("sensorAddress", "sensorName", 2, 8016);
|
SensorDataCycling.DistanceSpeed current = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 2, 8016);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
current.compute(previous, 2150);
|
current.compute(previous, 2150);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(1.20, current.getValue(), 0.01);
|
assertEquals(2.15, current.getValue().distance.toM(), 0.01);
|
||||||
|
assertEquals(1.20, current.getValue().speed.toMPS(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void compute_speed_rollOverCount() {
|
public void compute_speed_rollOverCount() {
|
||||||
// given
|
// given
|
||||||
SensorDataCycling.Speed previous = new SensorDataCycling.Speed("sensorAddress", "sensorName", UintUtils.UINT16_MAX - 1, 1024);
|
SensorDataCycling.DistanceSpeed previous = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", UintUtils.UINT16_MAX - 1, 1024);
|
||||||
SensorDataCycling.Speed current = new SensorDataCycling.Speed("sensorAddress", "sensorName", 0, 2048);
|
SensorDataCycling.DistanceSpeed current = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 0, 2048);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
current.compute(previous, 2000);
|
current.compute(previous, 2000);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(2, current.getValue(), 0.01);
|
assertEquals(2, current.getValue().distance.toM(), 0.01);
|
||||||
|
assertEquals(2, current.getValue().speed.toMPS(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void equals_speed_with_no_data() {
|
public void equals_speed_with_no_data() {
|
||||||
// given
|
// given
|
||||||
SensorDataCycling.Speed previous = new SensorDataCycling.Speed("sensorAddress");
|
SensorDataCycling.DistanceSpeed previous = new SensorDataCycling.DistanceSpeed("sensorAddress");
|
||||||
SensorDataCycling.Speed current = new SensorDataCycling.Speed("sensorAddress", "sensorName", 0, 2048);
|
SensorDataCycling.DistanceSpeed current = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 0, 2048);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
previous.toString();
|
previous.toString();
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package de.dennisguse.opentracks.fragments;
|
package de.dennisguse.opentracks.fragments;
|
||||||
|
|
||||||
import android.location.Location;
|
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
@@ -28,21 +27,15 @@ import org.junit.BeforeClass;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.time.Instant;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.chart.ChartPoint;
|
|
||||||
import de.dennisguse.opentracks.chart.ChartView;
|
import de.dennisguse.opentracks.chart.ChartView;
|
||||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
import de.dennisguse.opentracks.content.data.Distance;
|
||||||
import de.dennisguse.opentracks.util.UnitConversions;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link ChartFragment}.
|
* Tests {@link ChartFragment}.
|
||||||
*
|
*
|
||||||
* @author Youtao Liu
|
* @author Youtao Liu
|
||||||
*/
|
*/
|
||||||
|
//TODO Add tests that check ChartFragment
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class ChartFragmentTest {
|
public class ChartFragmentTest {
|
||||||
|
|
||||||
@@ -66,263 +59,11 @@ public class ChartFragmentTest {
|
|||||||
boolean chartByDistance = false;
|
boolean chartByDistance = false;
|
||||||
chartFragment = ChartFragment.newInstance(chartByDistance);
|
chartFragment = ChartFragment.newInstance(chartByDistance);
|
||||||
chartFragment.setChartView(new ChartView(ApplicationProvider.getApplicationContext(), chartByDistance));
|
chartFragment.setChartView(new ChartView(ApplicationProvider.getApplicationContext(), chartByDistance));
|
||||||
|
chartFragment.setRecordingDistanceInterval(Distance.of(50));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests the logic to get the incorrect values of sensor in {@link ChartFragment#createPendingPoint(TrackPoint)}.
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreatePendingPoint_sensorIncorrect() {
|
public void nothing() {
|
||||||
// given
|
//TODO
|
||||||
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint();
|
|
||||||
|
|
||||||
// when
|
|
||||||
ChartPoint point = chartFragment.createPendingPoint(trackPoint);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(Float.NaN, point.getHeartRate(), 0.01);
|
|
||||||
assertEquals(Float.NaN, point.getCadence(), 0.01);
|
|
||||||
assertEquals(Float.NaN, point.getPower(), 0.01);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests the logic to get the correct values of sensor in {@link ChartFragment#createPendingPoint(TrackPoint)}.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testCreatePendingPoint_sensorCorrect() {
|
|
||||||
// given
|
|
||||||
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint();
|
|
||||||
trackPoint.setHeartRate_bpm(100f);
|
|
||||||
trackPoint.setCyclingCadence_rpm(101f);
|
|
||||||
trackPoint.setPower(102f);
|
|
||||||
|
|
||||||
// when
|
|
||||||
ChartPoint point = chartFragment.createPendingPoint(trackPoint);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(100.0, point.getHeartRate(), 0.01);
|
|
||||||
assertEquals(101.0, point.getCadence(), 0.01);
|
|
||||||
assertEquals(102.0, point.getPower(), 0.01);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests the logic to get the value of metric Distance in {@link ChartFragment#createPendingPoint(TrackPoint)}.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testCreatePendingPoint_distanceMetric() {
|
|
||||||
chartFragment.setChartByDistance(true);
|
|
||||||
// Resets last location and writes first location.
|
|
||||||
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
|
|
||||||
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
|
|
||||||
assertEquals(0.0, point.getTimeOrDistance(), 0.01);
|
|
||||||
|
|
||||||
// The second is a same location, just different time.
|
|
||||||
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
|
|
||||||
point = chartFragment.createPendingPoint(trackPoint2);
|
|
||||||
assertEquals(0.0, point.getTimeOrDistance(), 0.01);
|
|
||||||
|
|
||||||
// The third location is a new location, and use metric.
|
|
||||||
TrackPoint trackPoint3 = TrackStubUtils.createDefaultTrackPoint();
|
|
||||||
trackPoint3.setLatitude(23);
|
|
||||||
point = chartFragment.createPendingPoint(trackPoint3);
|
|
||||||
|
|
||||||
// Computes the distance between Latitude 22 and 23.
|
|
||||||
float[] results = new float[4];
|
|
||||||
Location.distanceBetween(trackPoint2.getLatitude(), trackPoint2.getLongitude(),
|
|
||||||
trackPoint3.getLatitude(), trackPoint3.getLongitude(), results);
|
|
||||||
double distance1 = results[0] * UnitConversions.M_TO_KM;
|
|
||||||
assertEquals(distance1, point.getTimeOrDistance(), 0.01);
|
|
||||||
|
|
||||||
// The fourth location is a new location, and use metric.
|
|
||||||
TrackPoint trackPoint4 = TrackStubUtils.createDefaultTrackPoint();
|
|
||||||
trackPoint4.setLatitude(24);
|
|
||||||
point = chartFragment.createPendingPoint(trackPoint4);
|
|
||||||
|
|
||||||
// Computes the distance between Latitude 23 and 24.
|
|
||||||
Location.distanceBetween(trackPoint3.getLatitude(), trackPoint3.getLongitude(),
|
|
||||||
trackPoint4.getLatitude(), trackPoint4.getLongitude(), results);
|
|
||||||
double distance2 = results[0] * UnitConversions.M_TO_KM;
|
|
||||||
assertEquals((distance1 + distance2), point.getTimeOrDistance(), 0.01);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests the logic to get the value of imperial Distance in {@link ChartFragment#createPendingPoint(TrackPoint)}.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testCreatePendingPoint_distanceImperial() {
|
|
||||||
// By distance.
|
|
||||||
chartFragment.setChartByDistance(true);
|
|
||||||
// Setups to use imperial.
|
|
||||||
chartFragment.setMetricUnits(false);
|
|
||||||
|
|
||||||
// The first is a same location, just different time.
|
|
||||||
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
|
|
||||||
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
|
|
||||||
assertEquals(0.0, point.getTimeOrDistance(), 0.01);
|
|
||||||
|
|
||||||
// The second location is a new location, and use imperial.
|
|
||||||
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
|
|
||||||
trackPoint2.setLatitude(23);
|
|
||||||
point = chartFragment.createPendingPoint(trackPoint2);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Computes the distance between Latitude 22 and 23.
|
|
||||||
* And for we set using * imperial, the distance should be multiplied by UnitConversions.KM_TO_MI.
|
|
||||||
*/
|
|
||||||
float[] results = new float[4];
|
|
||||||
Location.distanceBetween(trackPoint1.getLatitude(), trackPoint1.getLongitude(), trackPoint2.getLatitude(), trackPoint2.getLongitude(), results);
|
|
||||||
double distance1 = results[0] * UnitConversions.M_TO_KM * UnitConversions.KM_TO_MI;
|
|
||||||
assertEquals(distance1, point.getTimeOrDistance(), 0.01);
|
|
||||||
|
|
||||||
// The third location is a new location, and use imperial.
|
|
||||||
TrackPoint trackPoint3 = TrackStubUtils.createDefaultTrackPoint();
|
|
||||||
trackPoint3.setLatitude(24);
|
|
||||||
point = chartFragment.createPendingPoint(trackPoint3);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Computes the distance between Latitude 23 and 24.
|
|
||||||
* And for we set using * imperial, the distance should be multiplied by UnitConversions.KM_TO_MI.
|
|
||||||
*/
|
|
||||||
Location.distanceBetween(trackPoint2.getLatitude(), trackPoint2.getLongitude(), trackPoint3.getLatitude(), trackPoint3.getLongitude(), results);
|
|
||||||
double distance2 = results[0] * UnitConversions.M_TO_KM * UnitConversions.KM_TO_MI;
|
|
||||||
assertEquals(distance1 + distance2, point.getTimeOrDistance(), 0.01);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests the logic to get the values of time in {@link ChartFragment#createPendingPoint(TrackPoint)}.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testCreatePendingPoint_time() {
|
|
||||||
// given
|
|
||||||
chartFragment.setChartByDistance(false);
|
|
||||||
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
|
|
||||||
trackPoint1.setTime(Instant.ofEpochMilli(TrackStubUtils.INITIAL_TIME)); //Keep old TrackPoint behavior of having time=0 for this test
|
|
||||||
|
|
||||||
// when
|
|
||||||
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(0.0, point.getTimeOrDistance(), 0.01);
|
|
||||||
Duration timeSpan = Duration.ofMillis(222);
|
|
||||||
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
|
|
||||||
trackPoint2.setTime(Instant.ofEpochMilli(TrackStubUtils.INITIAL_TIME).plus(timeSpan));
|
|
||||||
point = chartFragment.createPendingPoint(trackPoint2);
|
|
||||||
assertEquals(timeSpan, Duration.ofMillis((long) point.getTimeOrDistance()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests the logic to get the value of elevation in {@link ChartFragment#createPendingPoint(TrackPoint)} by one and two points.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testCreatePendingPoint_elevation() {
|
|
||||||
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* At first, clear old points of elevation, so give true to the second parameter.
|
|
||||||
* Then only one value INITIAL_ALTITUDE in buffer.
|
|
||||||
*/
|
|
||||||
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
|
|
||||||
assertEquals(TrackStubUtils.INITIAL_ALTITUDE, point.getElevation(), 0.01);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Send another value to buffer, now there are two values, INITIAL_ALTITUDE and INITIAL_ALTITUDE * 2.
|
|
||||||
*/
|
|
||||||
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
|
|
||||||
trackPoint2.setAltitude(TrackStubUtils.INITIAL_ALTITUDE * 2);
|
|
||||||
point = chartFragment.createPendingPoint(trackPoint2);
|
|
||||||
assertEquals((TrackStubUtils.INITIAL_ALTITUDE + TrackStubUtils.INITIAL_ALTITUDE * 2) / 2.0, point.getElevation(), 0.01);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests the logic to get the value of speed in {@link ChartFragment#createPendingPoint(TrackPoint)}.
|
|
||||||
* In this test, firstly remove all points in memory, and then fill in two points one by one.
|
|
||||||
* The speed values of these points are 129, 130.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testCreatePendingPoint_speed() {
|
|
||||||
/*
|
|
||||||
* At first, clear old points of speed, so give true to the second parameter.
|
|
||||||
* It will not be filled in to the speed buffer.
|
|
||||||
*/
|
|
||||||
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
|
|
||||||
trackPoint1.setSpeed(128.5f);
|
|
||||||
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
|
|
||||||
assertEquals(0.0, point.getSpeed(), 0.01);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Tests the logic when both metricUnits and reportSpeed are true.
|
|
||||||
* This location will be filled into speed buffer.
|
|
||||||
*/
|
|
||||||
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Add a time span here to make sure the second point is valid, the value 222 here is doesn't matter.
|
|
||||||
*/
|
|
||||||
trackPoint2.setTime(trackPoint1.getTime().plusMillis(222));
|
|
||||||
trackPoint2.setSpeed(130f);
|
|
||||||
point = chartFragment.createPendingPoint(trackPoint2);
|
|
||||||
assertEquals(130.0 * UnitConversions.MPS_TO_KMH, point.getSpeed(), 0.01);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests the logic to compute speed when use Imperial.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testCreatePendingPoint_speedImperial() {
|
|
||||||
chartFragment.setMetricUnits(false);
|
|
||||||
|
|
||||||
// First data point is not added to the speed buffer
|
|
||||||
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
|
|
||||||
trackPoint1.setSpeed(100.0f);
|
|
||||||
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
|
|
||||||
assertEquals(0.0, point.getSpeed(), 0.01);
|
|
||||||
|
|
||||||
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Add a time span here to make sure the second point and the speed is valid.
|
|
||||||
* Speed is valid if: speedDifference > Constants.MAX_ACCELERATION * timeDifference speedDifference = 102 -100 timeDifference = 222
|
|
||||||
*/
|
|
||||||
trackPoint2.setTime(trackPoint2.getTime().plusMillis(222));
|
|
||||||
trackPoint2.setSpeed(102f);
|
|
||||||
point = chartFragment.createPendingPoint(trackPoint2);
|
|
||||||
assertEquals(102.0 * UnitConversions.MPS_TO_KMH * UnitConversions.KM_TO_MI, point.getSpeed(), 0.01);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests the logic to get pace value when reportSpeed is false.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testCreatePendingPoint_pace_nonZeroSpeed() {
|
|
||||||
chartFragment.setReportSpeed(false);
|
|
||||||
|
|
||||||
// First data point is not added to the speed buffer
|
|
||||||
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
|
|
||||||
trackPoint1.setSpeed(100.0f);
|
|
||||||
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
|
|
||||||
assertEquals(0.0, point.getSpeed(), 0.01);
|
|
||||||
|
|
||||||
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Add a time span here to make sure the second point and the speed is valid.
|
|
||||||
* Speed is valid if: speedDifference > Constants.MAX_ACCELERATION * timeDifference speedDifference = 102 -100 timeDifference = 222
|
|
||||||
*/
|
|
||||||
trackPoint2.setTime(trackPoint2.getTime().plusMillis(222));
|
|
||||||
trackPoint2.setSpeed(102f);
|
|
||||||
point = chartFragment.createPendingPoint(trackPoint2);
|
|
||||||
assertEquals(HOURS_PER_UNIT / (102.0 * UnitConversions.MPS_TO_KMH), point.getPace(), 0.01);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests the logic to get pace value when reportSpeed is false and average speed is zero.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testCreatePendingPoint_pace_zeroSpeed() {
|
|
||||||
chartFragment.setReportSpeed(false);
|
|
||||||
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint();
|
|
||||||
trackPoint.setSpeed(0f);
|
|
||||||
ChartPoint point = chartFragment.createPendingPoint(trackPoint);
|
|
||||||
assertEquals(0.0, point.getPace(), 0.01);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+92
-105
@@ -2,19 +2,18 @@ package de.dennisguse.opentracks.io.file.importer;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.location.Location;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.filters.LargeTest;
|
import androidx.test.filters.LargeTest;
|
||||||
import androidx.test.rule.GrantPermissionRule;
|
import androidx.test.rule.GrantPermissionRule;
|
||||||
import androidx.test.rule.ServiceTestRule;
|
import androidx.test.rule.ServiceTestRule;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@@ -23,6 +22,7 @@ import org.junit.runners.JUnit4;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@@ -30,14 +30,16 @@ import java.util.concurrent.TimeoutException;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
|
import de.dennisguse.opentracks.content.data.Distance;
|
||||||
import de.dennisguse.opentracks.content.data.Marker;
|
import de.dennisguse.opentracks.content.data.Marker;
|
||||||
|
import de.dennisguse.opentracks.content.data.Speed;
|
||||||
|
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||||
import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
|
import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingServiceInterface;
|
|
||||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||||
|
|
||||||
@@ -69,6 +71,7 @@ public class ExportImportTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
private final SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||||
|
|
||||||
private final ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
|
private final ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
|
||||||
|
|
||||||
@@ -83,23 +86,33 @@ public class ExportImportTest {
|
|||||||
private Track.Id trackId;
|
private Track.Id trackId;
|
||||||
private Track.Id importTrackId;
|
private Track.Id importTrackId;
|
||||||
|
|
||||||
@Before
|
public void setUp(boolean hasSensorDistance) throws TimeoutException {
|
||||||
public void setUp() throws TimeoutException {
|
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(new Intent(context, TrackRecordingService.class)))
|
||||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(new Intent(context, TrackRecordingService.class)));
|
.getService();
|
||||||
|
|
||||||
|
|
||||||
trackId = service.startNewTrack();
|
trackId = service.startNewTrack();
|
||||||
|
//TODO Workaround as those managers overwrite input data; We need to refactor TrackRecordingService to make it actually testable
|
||||||
|
service.setAltitudeSumManager(null);
|
||||||
|
service.setRemoteSensorManager(null);
|
||||||
|
|
||||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 14, 10, 15, 10, 0, 66, 3, 50), 0);
|
Distance sensorDistance = hasSensorDistance ? Distance.of(5) : null;
|
||||||
|
|
||||||
|
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 14, 10, 15, 10, 1, 66, 3, 50, sensorDistance), 0);
|
||||||
service.insertMarker("Marker 1", "Marker 1 category", "Marker 1 desc", null);
|
service.insertMarker("Marker 1", "Marker 1 category", "Marker 1 desc", null);
|
||||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 14.001, 10, 15, 10, 0, 66, 3, 50), 0);
|
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 14.001, 10, 15, 10, 0, 66, 3, 50, sensorDistance), 0);
|
||||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 14.002, 10, 15, 10, 0, 66, 3, 50), 0);
|
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 14.002, 10, 15, 10, 0, 66, 3, 50, sensorDistance), 0);
|
||||||
service.insertMarker("Marker 2", "Marker 2 category", "Marker 2 desc", null);
|
service.insertMarker("Marker 2", "Marker 2 category", "Marker 2 desc", null);
|
||||||
service.pauseCurrentTrack();
|
service.pauseCurrentTrack();
|
||||||
|
|
||||||
service.resumeCurrentTrack();
|
service.resumeCurrentTrack();
|
||||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 14.003, 10, 15, 10, 0, 66, 3, 50), 0);
|
//TODO Workaround as those managers overwrite input data; We need to refactor TrackRecordingService to make it actually testable
|
||||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 16, 10, 15, 10, 0, 66, 3, 50), 0);
|
service.setAltitudeSumManager(null);
|
||||||
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 16.001, 10, 15, 10, 0, 66, 3, 50), 0);
|
service.setRemoteSensorManager(null);
|
||||||
|
|
||||||
|
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 14.003, 10, 15, 10, 0, 66, 3, 50, sensorDistance), 0);
|
||||||
|
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 16, 10, 15, 10, 0, 66, 3, 50, sensorDistance), 0);
|
||||||
|
service.newTrackPoint(createTrackPoint(System.currentTimeMillis(), 3, 16.001, 10, 15, 10, 0, 66, 3, 50, sensorDistance), 0);
|
||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
track = contentProviderUtils.getTrack(trackId);
|
track = contentProviderUtils.getTrack(trackId);
|
||||||
@@ -109,7 +122,7 @@ public class ExportImportTest {
|
|||||||
contentProviderUtils.updateTrack(track);
|
contentProviderUtils.updateTrack(track);
|
||||||
|
|
||||||
track = contentProviderUtils.getTrack(trackId);
|
track = contentProviderUtils.getTrack(trackId);
|
||||||
trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||||
markers = contentProviderUtils.getMarkers(trackId);
|
markers = contentProviderUtils.getMarkers(trackId);
|
||||||
assertEquals(10, trackPoints.size());
|
assertEquals(10, trackPoints.size());
|
||||||
assertEquals(2, markers.size());
|
assertEquals(2, markers.size());
|
||||||
@@ -123,17 +136,11 @@ public class ExportImportTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore("Not implemented")
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
@Test
|
@Test
|
||||||
public void kml_only_track() {
|
public void kml_with_trackdetail() throws TimeoutException {
|
||||||
// TODO
|
setUp(false);
|
||||||
Log.e(TAG, "Test not implemented.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@LargeTest
|
|
||||||
@Test
|
|
||||||
public void kml_with_trackdetail() {
|
|
||||||
// given
|
// given
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
|
|
||||||
@@ -148,7 +155,7 @@ public class ExportImportTest {
|
|||||||
// 2. import
|
// 2. import
|
||||||
InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
|
InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
|
||||||
AbstractFileTrackImporter trackImporter = new KmlFileTrackImporter(context);
|
AbstractFileTrackImporter trackImporter = new KmlFileTrackImporter(context);
|
||||||
importTrackId = trackImporter.importFile(inputStream);
|
importTrackId = trackImporter.importFile(inputStream).get(0);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
// 1. track
|
// 1. track
|
||||||
@@ -161,10 +168,10 @@ public class ExportImportTest {
|
|||||||
assertEquals(track.getUuid(), importedTrack.getUuid());
|
assertEquals(track.getUuid(), importedTrack.getUuid());
|
||||||
|
|
||||||
// 2. trackpoints
|
// 2. trackpoints
|
||||||
assertTrackpoints(trackPoints, false, false, false, false, false);
|
assertTrackpoints(trackPoints, false, false, false, false, false, false);
|
||||||
|
|
||||||
// 3. trackstatistics
|
// 3. trackstatistics
|
||||||
assertTrackStatistics(false, false);
|
assertTrackStatistics(false, false, true);
|
||||||
|
|
||||||
// 4. markers
|
// 4. markers
|
||||||
assertMarkers();
|
assertMarkers();
|
||||||
@@ -172,7 +179,9 @@ public class ExportImportTest {
|
|||||||
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
@Test
|
@Test
|
||||||
public void kml_with_trackdetail_and_sensordata() {
|
public void kml_with_trackdetail_and_sensordata() throws TimeoutException {
|
||||||
|
setUp(true);
|
||||||
|
|
||||||
// given
|
// given
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
|
|
||||||
@@ -187,7 +196,7 @@ public class ExportImportTest {
|
|||||||
// 2. import
|
// 2. import
|
||||||
InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
|
InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
|
||||||
AbstractFileTrackImporter trackImporter = new KmlFileTrackImporter(context);
|
AbstractFileTrackImporter trackImporter = new KmlFileTrackImporter(context);
|
||||||
importTrackId = trackImporter.importFile(inputStream);
|
importTrackId = trackImporter.importFile(inputStream).get(0);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
// 1. track
|
// 1. track
|
||||||
@@ -199,10 +208,10 @@ public class ExportImportTest {
|
|||||||
assertEquals(track.getIcon(), importedTrack.getIcon());
|
assertEquals(track.getIcon(), importedTrack.getIcon());
|
||||||
|
|
||||||
// 2. trackpoints
|
// 2. trackpoints
|
||||||
assertTrackpoints(trackPoints, true, true, true, true, true);
|
assertTrackpoints(trackPoints, true, true, true, true, true, true);
|
||||||
|
|
||||||
// 2. trackstatistics
|
// 2. trackstatistics
|
||||||
assertTrackStatistics(false, true);
|
assertTrackStatistics(false, true, true);
|
||||||
|
|
||||||
// 4. markers
|
// 4. markers
|
||||||
assertMarkers();
|
assertMarkers();
|
||||||
@@ -210,9 +219,13 @@ public class ExportImportTest {
|
|||||||
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
@Test(expected = ImportAlreadyExistsException.class)
|
@Test(expected = ImportAlreadyExistsException.class)
|
||||||
public void kml_with_trackdetail_and_sensordata_duplicate_trackUUID() {
|
public void kml_with_trackdetail_and_sensordata_duplicate_trackUUID() throws TimeoutException {
|
||||||
|
setUp(false);
|
||||||
|
|
||||||
// given
|
// given
|
||||||
PreferencesUtils.setBoolean(context, R.string.import_prevent_reimport_key, true);
|
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||||
|
editor.putBoolean(context.getString(R.string.import_prevent_reimport_key), true);
|
||||||
|
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);
|
||||||
@@ -225,48 +238,18 @@ public class ExportImportTest {
|
|||||||
// 2. import
|
// 2. import
|
||||||
InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
|
InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
|
||||||
AbstractFileTrackImporter trackImporter = new KmlFileTrackImporter(context);
|
AbstractFileTrackImporter trackImporter = new KmlFileTrackImporter(context);
|
||||||
importTrackId = trackImporter.importFile(inputStream);
|
importTrackId = trackImporter.importFile(inputStream).get(0);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||||
assertNull(importedTrack);
|
assertNull(importedTrack);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
@Test
|
@Test
|
||||||
public void kmz_only_track() {
|
public void gpx() throws TimeoutException {
|
||||||
// TODO
|
setUp(true);
|
||||||
Log.e(TAG, "Test not implemented.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Ignore
|
|
||||||
@LargeTest
|
|
||||||
@Test
|
|
||||||
public void kmz_with_trackdetail() {
|
|
||||||
// TODO
|
|
||||||
Log.e(TAG, "Test not implemented.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Ignore
|
|
||||||
@LargeTest
|
|
||||||
@Test
|
|
||||||
public void kmz_with_trackdetail_and_sensordata() {
|
|
||||||
// TODO
|
|
||||||
Log.e(TAG, "Test not implemented.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Ignore
|
|
||||||
@LargeTest
|
|
||||||
@Test
|
|
||||||
public void kmz_with_trackdetail_and_sensordata_and_pictures() {
|
|
||||||
// TODO
|
|
||||||
Log.e(TAG, "Test not implemented.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@LargeTest
|
|
||||||
@Test
|
|
||||||
public void gpx() {
|
|
||||||
// given
|
// given
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
|
|
||||||
@@ -281,7 +264,7 @@ public class ExportImportTest {
|
|||||||
// 2. import
|
// 2. import
|
||||||
InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
|
InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
|
||||||
AbstractFileTrackImporter trackImporter = new GpxFileTrackImporter(context, contentProviderUtils);
|
AbstractFileTrackImporter trackImporter = new GpxFileTrackImporter(context, contentProviderUtils);
|
||||||
importTrackId = trackImporter.importFile(inputStream);
|
importTrackId = trackImporter.importFile(inputStream).get(0);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
// 1. track
|
// 1. track
|
||||||
@@ -301,10 +284,10 @@ public class ExportImportTest {
|
|||||||
trackPointsWithCoordinates.get(0).setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
trackPointsWithCoordinates.get(0).setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||||
trackPointsWithCoordinates.get(3).setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
trackPointsWithCoordinates.get(3).setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||||
|
|
||||||
assertTrackpoints(trackPointsWithCoordinates, false, true, true, true, true);
|
assertTrackpoints(trackPointsWithCoordinates, true, true, true, true, true, false);
|
||||||
|
|
||||||
// 3. trackstatistics
|
// 3. trackstatistics
|
||||||
assertTrackStatistics(true, true);
|
assertTrackStatistics(true, true, false);
|
||||||
|
|
||||||
// 4. markers
|
// 4. markers
|
||||||
assertMarkers();
|
assertMarkers();
|
||||||
@@ -312,9 +295,13 @@ public class ExportImportTest {
|
|||||||
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
@Test(expected = ImportAlreadyExistsException.class)
|
@Test(expected = ImportAlreadyExistsException.class)
|
||||||
public void gpx_duplicate_trackUUID() {
|
public void gpx_duplicate_trackUUID() throws TimeoutException {
|
||||||
|
setUp(false);
|
||||||
|
|
||||||
// given
|
// given
|
||||||
PreferencesUtils.setBoolean(context, R.string.import_prevent_reimport_key, true);
|
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||||
|
editor.putBoolean(context.getString(R.string.import_prevent_reimport_key), true);
|
||||||
|
editor.commit();
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
|
|
||||||
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context);
|
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context);
|
||||||
@@ -327,7 +314,7 @@ public class ExportImportTest {
|
|||||||
// 2. import
|
// 2. import
|
||||||
InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
|
InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
|
||||||
AbstractFileTrackImporter trackImporter = new GpxFileTrackImporter(context, contentProviderUtils);
|
AbstractFileTrackImporter trackImporter = new GpxFileTrackImporter(context, contentProviderUtils);
|
||||||
importTrackId = trackImporter.importFile(inputStream);
|
importTrackId = trackImporter.importFile(inputStream).get(0);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
// 1. track
|
// 1. track
|
||||||
@@ -354,9 +341,8 @@ public class ExportImportTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertTrackpoints(List<TrackPoint> trackPoints, boolean verifyPower, boolean verifyHeartrate, boolean verifyCadence, boolean verifyElevationGain, boolean verifyElevationLoss) {
|
private void assertTrackpoints(List<TrackPoint> trackPoints, boolean verifyPower, boolean verifyHeartrate, boolean verifyCadence, boolean verifyAltitudeGain, boolean verifyAltitudeLoss, boolean verifyDistance) {
|
||||||
List<TrackPoint> importedTrackPoints = contentProviderUtils.getTrackPoints(importTrackId);
|
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
||||||
|
|
||||||
assertEquals(trackPoints.size(), importedTrackPoints.size());
|
assertEquals(trackPoints.size(), importedTrackPoints.size());
|
||||||
|
|
||||||
for (int i = 0; i < trackPoints.size(); i++) {
|
for (int i = 0; i < trackPoints.size(); i++) {
|
||||||
@@ -378,7 +364,7 @@ public class ExportImportTest {
|
|||||||
}
|
}
|
||||||
assertEquals(trackPoint.hasSpeed(), importedTrackPoint.hasSpeed());
|
assertEquals(trackPoint.hasSpeed(), importedTrackPoint.hasSpeed());
|
||||||
if (trackPoint.hasSpeed()) {
|
if (trackPoint.hasSpeed()) {
|
||||||
assertEquals(trackPoint.getSpeed(), importedTrackPoint.getSpeed(), 0.001);
|
assertEquals(trackPoint.getSpeed().toMPS(), importedTrackPoint.getSpeed().toMPS(), 0.001);
|
||||||
}
|
}
|
||||||
assertEquals(trackPoint.hasAltitude(), importedTrackPoint.hasAltitude());
|
assertEquals(trackPoint.hasAltitude(), importedTrackPoint.hasAltitude());
|
||||||
if (trackPoint.hasAltitude()) {
|
if (trackPoint.hasAltitude()) {
|
||||||
@@ -391,24 +377,27 @@ public class ExportImportTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (verifyHeartrate) {
|
if (verifyHeartrate) {
|
||||||
assertEquals(trackPoint.getHeartRate_bpm(), importedTrackPoint.getHeartRate_bpm(), 0.01);
|
assertEquals("" + trackPoint, trackPoint.getHeartRate_bpm(), importedTrackPoint.getHeartRate_bpm(), 0.01);
|
||||||
}
|
}
|
||||||
if (verifyCadence) {
|
if (verifyCadence) {
|
||||||
assertEquals(trackPoint.getCyclingCadence_rpm(), importedTrackPoint.getCyclingCadence_rpm(), 0.01);
|
assertEquals("" + trackPoint, trackPoint.getCyclingCadence_rpm(), importedTrackPoint.getCyclingCadence_rpm(), 0.01);
|
||||||
}
|
}
|
||||||
if (verifyPower) {
|
if (verifyPower) {
|
||||||
assertEquals(trackPoint.getPower(), importedTrackPoint.getPower(), 0.01);
|
assertEquals("" + trackPoint, trackPoint.getPower(), importedTrackPoint.getPower(), 0.01);
|
||||||
}
|
}
|
||||||
if (verifyElevationGain) {
|
if (verifyAltitudeGain) {
|
||||||
assertEquals(trackPoint.getElevationGain(), importedTrackPoint.getElevationGain(), 0.01);
|
assertEquals(trackPoint.getAltitudeGain(), importedTrackPoint.getAltitudeGain(), 0.01);
|
||||||
}
|
}
|
||||||
if (verifyElevationLoss) {
|
if (verifyAltitudeLoss) {
|
||||||
assertEquals(trackPoint.getElevationLoss(), importedTrackPoint.getElevationLoss(), 0.01);
|
assertEquals(trackPoint.getAltitudeLoss(), importedTrackPoint.getAltitudeLoss(), 0.01);
|
||||||
|
}
|
||||||
|
if (verifyDistance) {
|
||||||
|
assertEquals(trackPoint.getSensorDistance(), importedTrackPoint.getSensorDistance());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertTrackStatistics(boolean isGpx, boolean verifyElevationGainAndLoss) {
|
private void assertTrackStatistics(boolean isGpx, boolean verifyAltitudeGainAndLoss, boolean verifyDistance) {
|
||||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||||
|
|
||||||
assertNotNull(importedTrack.getTrackStatistics());
|
assertNotNull(importedTrack.getTrackStatistics());
|
||||||
@@ -426,40 +415,38 @@ public class ExportImportTest {
|
|||||||
assertEquals(trackStatistics.getMovingTime(), importedTrackStatistics.getMovingTime());
|
assertEquals(trackStatistics.getMovingTime(), importedTrackStatistics.getMovingTime());
|
||||||
|
|
||||||
// Distance
|
// Distance
|
||||||
assertEquals(trackStatistics.getTotalDistance(), importedTrackStatistics.getTotalDistance(), 0.01);
|
if (verifyDistance) {
|
||||||
|
assertEquals(trackStatistics.getTotalDistance(), importedTrackStatistics.getTotalDistance());
|
||||||
|
}
|
||||||
|
|
||||||
// Speed
|
// Speed
|
||||||
assertEquals(trackStatistics.getMaxSpeed(), importedTrackStatistics.getMaxSpeed(), 0.01);
|
assertEquals(trackStatistics.getMaxSpeed(), importedTrackStatistics.getMaxSpeed());
|
||||||
assertEquals(trackStatistics.getAverageSpeed(), importedTrackStatistics.getAverageSpeed(), 0.01);
|
assertEquals(trackStatistics.getAverageSpeed(), importedTrackStatistics.getAverageSpeed());
|
||||||
assertEquals(trackStatistics.getAverageMovingSpeed(), importedTrackStatistics.getAverageMovingSpeed(), 0.01);
|
assertEquals(trackStatistics.getAverageMovingSpeed(), importedTrackStatistics.getAverageMovingSpeed());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Elevation
|
// Altitude
|
||||||
assertEquals(trackStatistics.getMinElevation(), importedTrackStatistics.getMinElevation(), 0.01);
|
assertEquals(trackStatistics.getMinAltitude(), importedTrackStatistics.getMinAltitude(), 0.01);
|
||||||
assertEquals(trackStatistics.getMaxElevation(), importedTrackStatistics.getMaxElevation(), 0.01);
|
assertEquals(trackStatistics.getMaxAltitude(), importedTrackStatistics.getMaxAltitude(), 0.01);
|
||||||
if (verifyElevationGainAndLoss) {
|
if (verifyAltitudeGainAndLoss) {
|
||||||
assertEquals(trackStatistics.getTotalElevationGain(), importedTrackStatistics.getTotalElevationGain(), 0.01);
|
assertEquals(trackStatistics.getTotalAltitudeGain(), importedTrackStatistics.getTotalAltitudeGain(), 0.01);
|
||||||
assertEquals(trackStatistics.getTotalElevationLoss(), importedTrackStatistics.getTotalElevationLoss(), 0.01);
|
assertEquals(trackStatistics.getTotalAltitudeLoss(), importedTrackStatistics.getTotalAltitudeLoss(), 0.01);
|
||||||
} else {
|
} else {
|
||||||
assertFalse(importedTrackStatistics.hasTotalElevationGain());
|
assertFalse(importedTrackStatistics.hasTotalAltitudeGain());
|
||||||
assertFalse(importedTrackStatistics.hasTotalElevationLoss());
|
assertFalse(importedTrackStatistics.hasTotalAltitudeLoss());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TrackPoint createTrackPoint(long time, double latitude, double longitude, float accuracy, long speed, long altitude, float elevationGain, float heartRate, float cyclingCadence, float power) {
|
private static TrackPoint createTrackPoint(long time, double latitude, double longitude, float accuracy, float speed, float altitude, float altitudeGain, float heartRate, float cyclingCadence, float power, Distance distance) {
|
||||||
Location location = new Location("");
|
TrackPoint tp = new TrackPoint(latitude, longitude, (double) altitude, Instant.ofEpochMilli(time));
|
||||||
location.setTime(time);
|
tp.setAccuracy(accuracy);
|
||||||
location.setLongitude(longitude);
|
tp.setSpeed(Speed.of(speed));
|
||||||
location.setLatitude(latitude);
|
|
||||||
location.setAccuracy(accuracy);
|
|
||||||
location.setAltitude(altitude);
|
|
||||||
location.setSpeed(speed);
|
|
||||||
|
|
||||||
TrackPoint tp = new TrackPoint(location);
|
|
||||||
tp.setHeartRate_bpm(heartRate);
|
tp.setHeartRate_bpm(heartRate);
|
||||||
tp.setCyclingCadence_rpm(cyclingCadence);
|
tp.setCyclingCadence_rpm(cyclingCadence);
|
||||||
tp.setPower(power);
|
tp.setPower(power);
|
||||||
tp.setElevationGain(elevationGain);
|
tp.setAltitudeGain(altitudeGain);
|
||||||
|
tp.setAltitudeLoss(altitudeGain); //TODO
|
||||||
|
tp.setSensorDistance(distance);
|
||||||
return tp;
|
return tp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+6
-5
@@ -14,6 +14,7 @@ import org.junit.runners.JUnit4;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
@@ -54,7 +55,7 @@ public class LegacyImportTest {
|
|||||||
|
|
||||||
// when
|
// when
|
||||||
// 1. import
|
// 1. import
|
||||||
importTrackId = trackImporter.importFile(inputStream);
|
importTrackId = trackImporter.importFile(inputStream).get(0);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
// 1. track
|
// 1. track
|
||||||
@@ -69,7 +70,7 @@ public class LegacyImportTest {
|
|||||||
assertEquals(0, contentProviderUtils.getMarkerCount(importTrackId));
|
assertEquals(0, contentProviderUtils.getMarkerCount(importTrackId));
|
||||||
|
|
||||||
// 3. trackpoints
|
// 3. trackpoints
|
||||||
List<TrackPoint> importedTrackPoints = contentProviderUtils.getTrackPoints(importTrackId);
|
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
||||||
assertEquals(6, importedTrackPoints.size());
|
assertEquals(6, importedTrackPoints.size());
|
||||||
|
|
||||||
// first 3 trackpoints
|
// first 3 trackpoints
|
||||||
@@ -95,7 +96,7 @@ public class LegacyImportTest {
|
|||||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.legacy_kml_empty);
|
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.legacy_kml_empty);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
importTrackId = trackImporter.importFile(inputStream);
|
importTrackId = trackImporter.importFile(inputStream).get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -110,7 +111,7 @@ public class LegacyImportTest {
|
|||||||
|
|
||||||
// when
|
// when
|
||||||
// 1. import
|
// 1. import
|
||||||
importTrackId = trackImporter.importFile(inputStream);
|
importTrackId = trackImporter.importFile(inputStream).get(0);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
// 1. track
|
// 1. track
|
||||||
@@ -122,7 +123,7 @@ public class LegacyImportTest {
|
|||||||
assertEquals("UNKNOWN", importedTrack.getIcon());
|
assertEquals("UNKNOWN", importedTrack.getIcon());
|
||||||
|
|
||||||
// 3. trackpoints
|
// 3. trackpoints
|
||||||
List<TrackPoint> importedTrackPoints = contentProviderUtils.getTrackPoints(importTrackId);
|
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
||||||
assertEquals(6, importedTrackPoints.size());
|
assertEquals(6, importedTrackPoints.size());
|
||||||
|
|
||||||
// first segment
|
// first segment
|
||||||
|
|||||||
+16
-9
@@ -11,10 +11,12 @@ import org.junit.runner.RunWith;
|
|||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.data.Distance;
|
||||||
|
import de.dennisguse.opentracks.content.data.Speed;
|
||||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
|
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
@@ -25,7 +27,10 @@ public class TrackRecordingServiceNotificationManagerTest {
|
|||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private TrackPoint locationMock;
|
private TrackPoint trackPointMock;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private TrackStatistics trackStatisticsMock;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private NotificationCompat.Builder notificationCompatBuilder;
|
private NotificationCompat.Builder notificationCompatBuilder;
|
||||||
@@ -35,18 +40,20 @@ public class TrackRecordingServiceNotificationManagerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void updateLocation_triggersAlertOnlyOnFirstInaccurateLocation() {
|
public void updateLocation_triggersAlertOnlyOnFirstInaccurateLocation() {
|
||||||
when(locationMock.hasAccuracy()).thenReturn(true);
|
when(trackPointMock.hasAccuracy()).thenReturn(true);
|
||||||
when(locationMock.getAccuracy()).thenReturn(999f);
|
when(trackPointMock.getAccuracy()).thenReturn(999f);
|
||||||
when(notificationCompatBuilder.setContentText(anyString())).thenReturn(notificationCompatBuilder);
|
when(trackPointMock.getSpeed()).thenReturn(Speed.of(0));
|
||||||
|
when(trackStatisticsMock.getTotalDistance()).thenReturn(Distance.of(0));
|
||||||
when(notificationCompatBuilder.setOnlyAlertOnce(anyBoolean())).thenReturn(notificationCompatBuilder);
|
when(notificationCompatBuilder.setOnlyAlertOnce(anyBoolean())).thenReturn(notificationCompatBuilder);
|
||||||
|
|
||||||
TrackRecordingServiceNotificationManager subject = new TrackRecordingServiceNotificationManager(notificationManager, notificationCompatBuilder);
|
TrackRecordingServiceNotificationManager subject = new TrackRecordingServiceNotificationManager(notificationManager, notificationCompatBuilder);
|
||||||
|
subject.setMetricUnits(true);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
subject.updateTrackPoint(context, locationMock, 100);
|
subject.updateTrackPoint(context, trackStatisticsMock, trackPointMock, 100);
|
||||||
subject.updateTrackPoint(context, locationMock, 100);
|
subject.updateTrackPoint(context, trackStatisticsMock, trackPointMock, 100);
|
||||||
subject.updateTrackPoint(context, locationMock, 1000);
|
subject.updateTrackPoint(context, trackStatisticsMock, trackPointMock, 1000);
|
||||||
subject.updateTrackPoint(context, locationMock, 100);
|
subject.updateTrackPoint(context, trackStatisticsMock, trackPointMock, 100);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
verify(notificationCompatBuilder, times(6)).setOnlyAlertOnce(true);
|
verify(notificationCompatBuilder, times(6)).setOnlyAlertOnce(true);
|
||||||
|
|||||||
+56
-69
@@ -33,11 +33,11 @@ import androidx.test.rule.ServiceTestRule;
|
|||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@@ -45,6 +45,7 @@ import java.util.concurrent.TimeoutException;
|
|||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.content.data.Marker;
|
import de.dennisguse.opentracks.content.data.Marker;
|
||||||
|
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
@@ -84,6 +85,7 @@ public class TrackRecordingServiceTest {
|
|||||||
|
|
||||||
|
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
private final SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||||
private ContentProviderUtils contentProviderUtils;
|
private ContentProviderUtils contentProviderUtils;
|
||||||
|
|
||||||
private final Track.Id trackId = new Track.Id(Math.abs(new Random().nextLong()));
|
private final Track.Id trackId = new Track.Id(Math.abs(new Random().nextLong()));
|
||||||
@@ -102,7 +104,6 @@ public class TrackRecordingServiceTest {
|
|||||||
contentProviderUtils = new ContentProviderUtils(context);
|
contentProviderUtils = new ContentProviderUtils(context);
|
||||||
|
|
||||||
// Let's use default values.
|
// Let's use default values.
|
||||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
|
||||||
sharedPreferences.edit().clear().commit();
|
sharedPreferences.edit().clear().commit();
|
||||||
|
|
||||||
// Ensure that the database is empty before every test
|
// Ensure that the database is empty before every test
|
||||||
@@ -112,7 +113,8 @@ public class TrackRecordingServiceTest {
|
|||||||
@After
|
@After
|
||||||
public void tearDown() throws TimeoutException {
|
public void tearDown() throws TimeoutException {
|
||||||
// Reset service (if some previous test failed)
|
// Reset service (if some previous test failed)
|
||||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(createStartIntent(context)))
|
||||||
|
.getService();
|
||||||
if (service.isRecording() || service.isPaused()) {
|
if (service.isRecording() || service.isPaused()) {
|
||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
}
|
}
|
||||||
@@ -145,36 +147,38 @@ public class TrackRecordingServiceTest {
|
|||||||
// when
|
// when
|
||||||
Intent startIntent = createStartIntent(context);
|
Intent startIntent = createStartIntent(context);
|
||||||
mServiceRule.startService(startIntent);
|
mServiceRule.startService(startIntent);
|
||||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(startIntent));
|
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(startIntent))
|
||||||
|
.getService();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
// Test if we start in no-recording mode by default.
|
// Test if we start in no-recording mode by default.
|
||||||
assertFalse(service.isRecording());
|
assertFalse(service.isRecording());
|
||||||
assertNull(service.getRecordingTrackId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testRecording_oldTracks() throws TimeoutException {
|
public void testRecording_oldTracks() throws TimeoutException {
|
||||||
// given
|
// given
|
||||||
createDummyTrack(trackId, false);
|
createDummyTrack(trackId);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(createStartIntent(context)))
|
||||||
|
.getService();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertFalse(service.isRecording());
|
assertFalse(service.isRecording());
|
||||||
assertNull(service.getRecordingTrackId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testRecording_serviceRestart_whileRecording() throws TimeoutException {
|
public void testRecording_serviceRestart_whileRecording() throws TimeoutException {
|
||||||
// given
|
// given
|
||||||
createDummyTrack(trackId, true);
|
createDummyTrack(trackId);
|
||||||
|
|
||||||
//when
|
//when
|
||||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(createStartIntent(context)))
|
||||||
|
.getService();
|
||||||
|
service.resumeTrack(trackId);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertTrue(service.isRecording());
|
assertTrue(service.isRecording());
|
||||||
@@ -184,13 +188,14 @@ public class TrackRecordingServiceTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testRecording_start() throws TimeoutException {
|
public void testRecording_start() throws TimeoutException {
|
||||||
// given
|
// given
|
||||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(createStartIntent(context)))
|
||||||
|
.getService();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
Track.Id trackId = service.startNewTrack();
|
Track.Id trackId = service.startNewTrack();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||||
|
|
||||||
assertEquals(1, trackPoints.size());
|
assertEquals(1, trackPoints.size());
|
||||||
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(0).getType());
|
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(0).getType());
|
||||||
@@ -200,14 +205,15 @@ public class TrackRecordingServiceTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testRecording_stop() throws TimeoutException {
|
public void testRecording_stop() throws TimeoutException {
|
||||||
// given
|
// given
|
||||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(createStartIntent(context)))
|
||||||
|
.getService();
|
||||||
Track.Id trackId = service.startNewTrack();
|
Track.Id trackId = service.startNewTrack();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||||
|
|
||||||
assertEquals(2, trackPoints.size());
|
assertEquals(2, trackPoints.size());
|
||||||
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(0).getType());
|
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(0).getType());
|
||||||
@@ -218,23 +224,23 @@ public class TrackRecordingServiceTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testRecording_pauseAndResume() throws TimeoutException {
|
public void testRecording_pauseAndResume() throws TimeoutException {
|
||||||
// given
|
// given
|
||||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(createStartIntent(context)))
|
||||||
|
.getService();
|
||||||
Track.Id trackId = service.startNewTrack();
|
Track.Id trackId = service.startNewTrack();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
service.pauseCurrentTrack();
|
service.pauseCurrentTrack();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(2, contentProviderUtils.getTrackPoints(trackId).size());
|
assertEquals(2, contentProviderUtils.getTrackPointCursor(trackId, null).getCount());
|
||||||
|
|
||||||
//when
|
//when
|
||||||
service.resumeTrack(trackId);
|
service.resumeTrack(trackId);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertTrue(service.isRecording());
|
assertTrue(service.isRecording());
|
||||||
assertEquals(trackId, service.getRecordingTrackId());
|
|
||||||
|
|
||||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||||
assertEquals(3, trackPoints.size());
|
assertEquals(3, trackPoints.size());
|
||||||
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(0).getType());
|
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(0).getType());
|
||||||
assertEquals(TrackPoint.Type.SEGMENT_END_MANUAL, trackPoints.get(1).getType());
|
assertEquals(TrackPoint.Type.SEGMENT_END_MANUAL, trackPoints.get(1).getType());
|
||||||
@@ -245,12 +251,13 @@ public class TrackRecordingServiceTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testRecording_resumeStoppedTrack() throws TimeoutException, InterruptedException {
|
public void testRecording_resumeStoppedTrack() throws TimeoutException, InterruptedException {
|
||||||
// given
|
// given
|
||||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(createStartIntent(context)))
|
||||||
|
.getService();
|
||||||
Track.Id trackId = service.startNewTrack();
|
Track.Id trackId = service.startNewTrack();
|
||||||
assertTrue(service.isRecording());
|
assertTrue(service.isRecording());
|
||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
assertEquals(2, contentProviderUtils.getTrackPoints(trackId).size());
|
assertEquals(2, contentProviderUtils.getTrackPointCursor(trackId, null).getCount());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
service.resumeTrack(trackId);
|
service.resumeTrack(trackId);
|
||||||
@@ -258,9 +265,8 @@ public class TrackRecordingServiceTest {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
assertTrue(service.isRecording());
|
assertTrue(service.isRecording());
|
||||||
assertEquals(trackId, service.getRecordingTrackId());
|
|
||||||
|
|
||||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||||
assertEquals(4, trackPoints.size());
|
assertEquals(4, trackPoints.size());
|
||||||
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(0).getType());
|
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(0).getType());
|
||||||
assertEquals(TrackPoint.Type.SEGMENT_END_MANUAL, trackPoints.get(1).getType());
|
assertEquals(TrackPoint.Type.SEGMENT_END_MANUAL, trackPoints.get(1).getType());
|
||||||
@@ -272,68 +278,49 @@ public class TrackRecordingServiceTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testRecording_stopPausedTrack() throws TimeoutException {
|
public void testRecording_stopPausedTrack() throws TimeoutException {
|
||||||
// given
|
// given
|
||||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(createStartIntent(context)))
|
||||||
|
.getService();
|
||||||
Track.Id trackId = service.startNewTrack();
|
Track.Id trackId = service.startNewTrack();
|
||||||
assertTrue(service.isRecording());
|
assertTrue(service.isRecording());
|
||||||
service.pauseCurrentTrack();
|
service.pauseCurrentTrack();
|
||||||
|
|
||||||
assertEquals(2, contentProviderUtils.getTrackPoints(trackId).size());
|
assertEquals(2, contentProviderUtils.getTrackPointCursor(trackId, null).getCount());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertFalse(service.isRecording());
|
assertFalse(service.isRecording());
|
||||||
assertNull(service.getRecordingTrackId());
|
|
||||||
|
|
||||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||||
assertEquals(2, trackPoints.size());
|
assertEquals(2, trackPoints.size());
|
||||||
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(0).getType());
|
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(0).getType());
|
||||||
assertEquals(TrackPoint.Type.SEGMENT_END_MANUAL, trackPoints.get(1).getType());
|
assertEquals(TrackPoint.Type.SEGMENT_END_MANUAL, trackPoints.get(1).getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore("Sometimes fails on CI.")
|
|
||||||
@MediumTest
|
|
||||||
@Test
|
|
||||||
public void testRecording_orphanedRecordingTrack() throws TimeoutException {
|
|
||||||
// given
|
|
||||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
|
||||||
|
|
||||||
// when
|
|
||||||
// Just set recording track to a bogus value.
|
|
||||||
// Make sure that the service will not start recording and will clear the bogus track.
|
|
||||||
PreferencesUtils.setLong(context, R.string.recording_track_id_key, 123L);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertFalse(service.isRecording());
|
|
||||||
assertNull(service.getRecordingTrackId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testStartNewTrack_alreadyRecording() throws TimeoutException {
|
public void testStartNewTrack_alreadyRecording() throws TimeoutException {
|
||||||
// given
|
// given
|
||||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(createStartIntent(context)))
|
||||||
service.startNewTrack();
|
.getService();
|
||||||
|
Track.Id trackId = service.startNewTrack();
|
||||||
assertTrue(service.isRecording());
|
assertTrue(service.isRecording());
|
||||||
|
|
||||||
Track.Id trackId = service.getRecordingTrackId();
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
Track.Id newTrackId = service.startNewTrack();
|
Track.Id newTrackId = service.startNewTrack();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
|
assertNotNull(trackId);
|
||||||
assertNull(newTrackId);
|
assertNull(newTrackId);
|
||||||
|
|
||||||
assertEquals(trackId, PreferencesUtils.getRecordingTrackId(context));
|
|
||||||
assertEquals(trackId, service.getRecordingTrackId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testEndCurrentTrack_noRecording() throws TimeoutException {
|
public void testEndCurrentTrack_noRecording() throws TimeoutException {
|
||||||
// given
|
// given
|
||||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(createStartIntent(context)))
|
||||||
|
.getService();
|
||||||
assertFalse(service.isRecording());
|
assertFalse(service.isRecording());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@@ -341,15 +328,15 @@ public class TrackRecordingServiceTest {
|
|||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertFalse(PreferencesUtils.isRecording(context));
|
assertFalse(service.isRecording());
|
||||||
assertNull(service.getRecordingTrackId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testInsertWaypointMarker_noRecordingTrack() throws TimeoutException {
|
public void testInsertWaypointMarker_noRecordingTrack() throws TimeoutException {
|
||||||
// given
|
// given
|
||||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(createStartIntent(context)))
|
||||||
|
.getService();
|
||||||
assertFalse(service.isRecording());
|
assertFalse(service.isRecording());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@@ -363,57 +350,58 @@ public class TrackRecordingServiceTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testInsertWaypointMarker_validWaypoint() throws TimeoutException, InterruptedException {
|
public void testInsertWaypointMarker_validWaypoint() throws TimeoutException, InterruptedException {
|
||||||
// given
|
// given
|
||||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(createStartIntent(context)))
|
||||||
service.startNewTrack();
|
.getService();
|
||||||
|
Track.Id trackId = service.startNewTrack();
|
||||||
assertTrue(service.isRecording());
|
assertTrue(service.isRecording());
|
||||||
newTrackPoint(service);
|
newTrackPoint(service);
|
||||||
Track.Id trackId = service.getRecordingTrackId();
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
Marker.Id markerId = service.insertMarker(null, null, null, null);
|
Marker.Id markerId = service.insertMarker(null, null, null, null);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertNotEquals(-1L, markerId);
|
assertNotEquals(new Marker.Id(-1L), markerId);
|
||||||
Marker wpt = contentProviderUtils.getMarker(markerId);
|
Marker wpt = contentProviderUtils.getMarker(markerId);
|
||||||
assertEquals(context.getString(R.string.marker_icon_url), wpt.getIcon());
|
assertEquals(context.getString(R.string.marker_icon_url), wpt.getIcon());
|
||||||
assertEquals(context.getString(R.string.marker_name_format, 1), wpt.getName());
|
assertEquals(context.getString(R.string.marker_name_format, 1), wpt.getName());
|
||||||
assertEquals(trackId, wpt.getTrackId());
|
assertEquals(trackId, wpt.getTrackId());
|
||||||
assertEquals(0.0, wpt.getLength(), 0.01);
|
assertEquals(0.0, wpt.getLength().toM(), 0.01);
|
||||||
assertNotNull(wpt.getLocation());
|
assertNotNull(wpt.getLocation());
|
||||||
|
|
||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addTrack(Track track, boolean isRecording) {
|
private void addTrack(Track track) {
|
||||||
assertNotNull(track.getId());
|
assertNotNull(track.getId());
|
||||||
contentProviderUtils.insertTrack(track);
|
contentProviderUtils.insertTrack(track);
|
||||||
assertEquals(track.getId(), contentProviderUtils.getTrack(track.getId()).getId());
|
assertEquals(track.getId(), contentProviderUtils.getTrack(track.getId()).getId());
|
||||||
PreferencesUtils.setLong(context, R.string.recording_track_id_key, isRecording ? track.getId().getId() : PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
|
|
||||||
PreferencesUtils.setBoolean(context, R.string.recording_track_paused_key, !isRecording);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Do not use to create a track that is currently recording.
|
// NOTE: Do not use to create a track that is currently recording.
|
||||||
private void createDummyTrack(Track.Id id, boolean isRecording) {
|
private void createDummyTrack(Track.Id id) {
|
||||||
Track dummyTrack = new Track();
|
Track dummyTrack = new Track();
|
||||||
dummyTrack.setId(id);
|
dummyTrack.setId(id);
|
||||||
dummyTrack.setName("Dummy Track");
|
dummyTrack.setName("Dummy Track");
|
||||||
TrackStatistics trackStatistics = new TrackStatistics();
|
TrackStatistics trackStatistics = new TrackStatistics();
|
||||||
|
Instant now = Instant.now();
|
||||||
|
trackStatistics.setStartTime(now.minusSeconds(5L));
|
||||||
|
trackStatistics.setStopTime(now.minusSeconds(1L));
|
||||||
dummyTrack.setTrackStatistics(trackStatistics);
|
dummyTrack.setTrackStatistics(trackStatistics);
|
||||||
addTrack(dummyTrack, isRecording);
|
addTrack(dummyTrack);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void newTrackPoint(TrackRecordingServiceInterface trackRecordingService) throws InterruptedException {
|
private static void newTrackPoint(TrackRecordingService trackRecordingService) throws InterruptedException {
|
||||||
newTrackPoint(trackRecordingService, 45.0f, 35f, 5, 10, System.currentTimeMillis());
|
newTrackPoint(trackRecordingService, 45.0f, 35f, 5, 10, System.currentTimeMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void newTrackPoint(TrackRecordingServiceInterface trackRecordingService, double latitude, double longitude, float accuracy, long speed) throws InterruptedException {
|
static void newTrackPoint(TrackRecordingService trackRecordingService, double latitude, double longitude, float accuracy, long speed) throws InterruptedException {
|
||||||
newTrackPoint(trackRecordingService, latitude, longitude, accuracy, speed, System.currentTimeMillis());
|
newTrackPoint(trackRecordingService, latitude, longitude, accuracy, speed, System.currentTimeMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts a location and waits for 200ms.
|
* Inserts a location and waits for 200ms.
|
||||||
*/
|
*/
|
||||||
private static void newTrackPoint(TrackRecordingServiceInterface trackRecordingService, double latitude, double longitude, float accuracy, long speed, long time) throws InterruptedException {
|
private static void newTrackPoint(TrackRecordingService trackRecordingService, double latitude, double longitude, float accuracy, long speed, long time) {
|
||||||
Location location = new Location("");
|
Location location = new Location("");
|
||||||
location.setLongitude(longitude);
|
location.setLongitude(longitude);
|
||||||
location.setLatitude(latitude);
|
location.setLatitude(latitude);
|
||||||
@@ -422,7 +410,6 @@ public class TrackRecordingServiceTest {
|
|||||||
location.setTime(time);
|
location.setTime(time);
|
||||||
location.setBearing(3.0f);
|
location.setBearing(3.0f);
|
||||||
TrackPoint trackPoint = new TrackPoint(location);
|
TrackPoint trackPoint = new TrackPoint(location);
|
||||||
int prefAccuracy = PreferencesUtils.getRecordingGPSAccuracy(ApplicationProvider.getApplicationContext());
|
trackRecordingService.newTrackPoint(trackPoint, 50);
|
||||||
trackRecordingService.newTrackPoint(trackPoint, prefAccuracy);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-8
@@ -23,6 +23,7 @@ import java.util.List;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
@@ -54,7 +55,7 @@ public class TrackRecordingServiceTestLocation {
|
|||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
private ContentProviderUtils contentProviderUtils;
|
private ContentProviderUtils contentProviderUtils;
|
||||||
|
|
||||||
private TrackRecordingServiceInterface service;
|
private TrackRecordingService service;
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void preSetUp() {
|
public static void preSetUp() {
|
||||||
@@ -76,7 +77,8 @@ public class TrackRecordingServiceTestLocation {
|
|||||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||||
sharedPreferences.edit().clear().commit();
|
sharedPreferences.edit().clear().commit();
|
||||||
|
|
||||||
service = ((TrackRecordingServiceInterface) mServiceRule.bindService(TrackRecordingServiceTest.createStartIntent(context)));
|
service = ((TrackRecordingService.Binder) mServiceRule.bindService(TrackRecordingServiceTest.createStartIntent(context)))
|
||||||
|
.getService();
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@@ -104,7 +106,7 @@ public class TrackRecordingServiceTestLocation {
|
|||||||
// then
|
// then
|
||||||
assertFalse(service.isRecording());
|
assertFalse(service.isRecording());
|
||||||
|
|
||||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||||
assertEquals(8, trackPoints.size());
|
assertEquals(8, trackPoints.size());
|
||||||
assertTrackPoints(List.of(
|
assertTrackPoints(List.of(
|
||||||
new Pair<>(TrackPoint.Type.SEGMENT_START_MANUAL, null),
|
new Pair<>(TrackPoint.Type.SEGMENT_START_MANUAL, null),
|
||||||
@@ -138,7 +140,7 @@ public class TrackRecordingServiceTestLocation {
|
|||||||
// then
|
// then
|
||||||
assertFalse(service.isRecording());
|
assertFalse(service.isRecording());
|
||||||
|
|
||||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||||
assertEquals(4, trackPoints.size());
|
assertEquals(4, trackPoints.size());
|
||||||
assertTrackPoints(List.of(
|
assertTrackPoints(List.of(
|
||||||
new Pair<>(TrackPoint.Type.SEGMENT_START_MANUAL, null),
|
new Pair<>(TrackPoint.Type.SEGMENT_START_MANUAL, null),
|
||||||
@@ -185,7 +187,7 @@ public class TrackRecordingServiceTestLocation {
|
|||||||
// then
|
// then
|
||||||
assertFalse(service.isRecording());
|
assertFalse(service.isRecording());
|
||||||
|
|
||||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||||
assertTrackPoints(List.of(
|
assertTrackPoints(List.of(
|
||||||
new Pair<>(TrackPoint.Type.SEGMENT_START_MANUAL, null),
|
new Pair<>(TrackPoint.Type.SEGMENT_START_MANUAL, null),
|
||||||
new Pair<>(TrackPoint.Type.TRACKPOINT, 1),
|
new Pair<>(TrackPoint.Type.TRACKPOINT, 1),
|
||||||
@@ -214,7 +216,7 @@ public class TrackRecordingServiceTestLocation {
|
|||||||
// then
|
// then
|
||||||
assertFalse(service.isRecording());
|
assertFalse(service.isRecording());
|
||||||
|
|
||||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||||
assertEquals(6, trackPoints.size());
|
assertEquals(6, trackPoints.size());
|
||||||
assertTrackPoints(List.of(
|
assertTrackPoints(List.of(
|
||||||
new Pair<>(TrackPoint.Type.SEGMENT_START_MANUAL, null),
|
new Pair<>(TrackPoint.Type.SEGMENT_START_MANUAL, null),
|
||||||
@@ -260,7 +262,7 @@ public class TrackRecordingServiceTestLocation {
|
|||||||
// then
|
// then
|
||||||
assertFalse(service.isRecording());
|
assertFalse(service.isRecording());
|
||||||
|
|
||||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||||
assertEquals(8, trackPoints.size());
|
assertEquals(8, trackPoints.size());
|
||||||
assertTrackPoints(List.of(
|
assertTrackPoints(List.of(
|
||||||
new Pair<>(TrackPoint.Type.SEGMENT_START_MANUAL, null),
|
new Pair<>(TrackPoint.Type.SEGMENT_START_MANUAL, null),
|
||||||
@@ -292,7 +294,7 @@ public class TrackRecordingServiceTestLocation {
|
|||||||
// then
|
// then
|
||||||
assertFalse(service.isRecording());
|
assertFalse(service.isRecording());
|
||||||
|
|
||||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||||
assertEquals(7, trackPoints.size());
|
assertEquals(7, trackPoints.size());
|
||||||
assertTrackPoints(List.of(
|
assertTrackPoints(List.of(
|
||||||
new Pair<>(TrackPoint.Type.SEGMENT_START_MANUAL, null),
|
new Pair<>(TrackPoint.Type.SEGMENT_START_MANUAL, null),
|
||||||
|
|||||||
+42
-23
@@ -6,6 +6,7 @@ import android.content.SharedPreferences;
|
|||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import androidx.test.filters.MediumTest;
|
import androidx.test.filters.MediumTest;
|
||||||
@@ -35,7 +36,6 @@ import de.dennisguse.opentracks.util.PreferencesUtils;
|
|||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -53,6 +53,7 @@ public class TrackRecordingServiceTestLooper {
|
|||||||
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS);
|
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS);
|
||||||
|
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
private final SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||||
|
|
||||||
private ContentProviderUtils contentProviderUtils;
|
private ContentProviderUtils contentProviderUtils;
|
||||||
|
|
||||||
@@ -77,7 +78,6 @@ public class TrackRecordingServiceTestLooper {
|
|||||||
contentProviderUtils = new ContentProviderUtils(context);
|
contentProviderUtils = new ContentProviderUtils(context);
|
||||||
|
|
||||||
// Let's use default values.
|
// Let's use default values.
|
||||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
|
||||||
sharedPreferences.edit().clear().apply();
|
sharedPreferences.edit().clear().apply();
|
||||||
|
|
||||||
// Ensure that the database is empty before every test
|
// Ensure that the database is empty before every test
|
||||||
@@ -87,7 +87,8 @@ public class TrackRecordingServiceTestLooper {
|
|||||||
@After
|
@After
|
||||||
public void tearDown() throws TimeoutException {
|
public void tearDown() throws TimeoutException {
|
||||||
// Reset service (if some previous test failed)
|
// Reset service (if some previous test failed)
|
||||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(TrackRecordingServiceTest.createStartIntent(context)));
|
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(TrackRecordingServiceTest.createStartIntent(context)))
|
||||||
|
.getService();
|
||||||
if (service.isRecording() || service.isPaused()) {
|
if (service.isRecording() || service.isPaused()) {
|
||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
}
|
}
|
||||||
@@ -99,28 +100,36 @@ public class TrackRecordingServiceTestLooper {
|
|||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testWithProperties_minRequiredAccuracy() throws TimeoutException {
|
public void testWithProperties_minRequiredAccuracy() throws TimeoutException {
|
||||||
PreferencesUtils.setInt(context, R.string.recording_gps_accuracy_key, 500);
|
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||||
|
editor.putInt(context.getString(R.string.recording_gps_accuracy_key), 500);
|
||||||
|
editor.commit();
|
||||||
fullRecordingSession();
|
fullRecordingSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testWithProperties_voiceFrequencyDefault() throws TimeoutException {
|
public void testWithProperties_voiceFrequencyDefault() throws TimeoutException {
|
||||||
PreferencesUtils.setInt(context, R.string.voice_frequency_key, Integer.parseInt(context.getResources().getString(R.string.voice_frequency_default)));
|
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||||
|
editor.putInt(context.getString(R.string.voice_frequency_key), Integer.parseInt(context.getResources().getString(R.string.voice_frequency_default)));
|
||||||
|
editor.commit();
|
||||||
fullRecordingSession();
|
fullRecordingSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testWithProperties_voiceFrequencyByDistance() throws TimeoutException {
|
public void testWithProperties_voiceFrequencyByDistance() throws TimeoutException {
|
||||||
PreferencesUtils.setInt(context, R.string.voice_frequency_key, -1);
|
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||||
|
editor.putInt(context.getString(R.string.voice_frequency_key), -1);
|
||||||
|
editor.commit();
|
||||||
fullRecordingSession();
|
fullRecordingSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testWithProperties_voiceFrequencyByTime() throws TimeoutException {
|
public void testWithProperties_voiceFrequencyByTime() throws TimeoutException {
|
||||||
PreferencesUtils.setInt(context, R.string.voice_frequency_key, 1);
|
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||||
|
editor.putInt(context.getString(R.string.voice_frequency_key), 1);
|
||||||
|
editor.commit();
|
||||||
fullRecordingSession();
|
fullRecordingSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,65 +137,77 @@ public class TrackRecordingServiceTestLooper {
|
|||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testWithProperties_maxRecordingDistanceDefault() throws TimeoutException {
|
public void testWithProperties_maxRecordingDistanceDefault() throws TimeoutException {
|
||||||
PreferencesUtils.setInt(context, R.string.max_recording_distance_key, Integer.parseInt(context.getResources().getString(R.string.max_recording_distance_default)));
|
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||||
|
editor.putInt(context.getString(R.string.max_recording_distance_key), Integer.parseInt(context.getResources().getString(R.string.max_recording_distance_default)));
|
||||||
|
editor.commit();
|
||||||
fullRecordingSession();
|
fullRecordingSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testWithProperties_maxRecordingDistance() throws TimeoutException {
|
public void testWithProperties_maxRecordingDistance() throws TimeoutException {
|
||||||
PreferencesUtils.setInt(context, R.string.max_recording_distance_key, 50);
|
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||||
|
editor.putInt(context.getString(R.string.max_recording_distance_key), 50);
|
||||||
|
editor.commit();
|
||||||
fullRecordingSession();
|
fullRecordingSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testWithProperties_minRecordingDistanceDefault() throws TimeoutException {
|
public void testWithProperties_minRecordingDistanceDefault() throws TimeoutException {
|
||||||
int minRecordingIntervalDefault = Integer.parseInt(context.getResources().getString(R.string.min_recording_interval_default));
|
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||||
PreferencesUtils.setInt(context, R.string.recording_distance_interval_key, minRecordingIntervalDefault);
|
editor.putInt(context.getString(R.string.recording_distance_interval_key), Integer.parseInt(context.getResources().getString(R.string.min_recording_interval_default)));
|
||||||
|
editor.commit();
|
||||||
fullRecordingSession();
|
fullRecordingSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testWithProperties_minRecordingDistance() throws TimeoutException {
|
public void testWithProperties_minRecordingDistance() throws TimeoutException {
|
||||||
PreferencesUtils.setInt(context, R.string.recording_distance_interval_key, 2);
|
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||||
|
editor.putInt(context.getString(R.string.recording_distance_interval_key), 2);
|
||||||
|
editor.commit();
|
||||||
fullRecordingSession();
|
fullRecordingSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testWithProperties_metricUnitsDefault() throws TimeoutException {
|
public void testWithProperties_metricUnitsDefault() throws TimeoutException {
|
||||||
PreferencesUtils.setString(context, R.string.stats_units_key, context.getString(R.string.stats_units_default));
|
PreferencesUtils.setString(sharedPreferences, context, R.string.stats_units_key, context.getString(R.string.stats_units_default));
|
||||||
fullRecordingSession();
|
fullRecordingSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testWithProperties_metricUnitsDisabled() throws TimeoutException {
|
public void testWithProperties_metricUnitsDisabled() throws TimeoutException {
|
||||||
PreferencesUtils.setString(context, R.string.stats_units_key, context.getString(R.string.stats_units_imperial));
|
PreferencesUtils.setString(sharedPreferences, context, R.string.stats_units_key, context.getString(R.string.stats_units_imperial));
|
||||||
fullRecordingSession();
|
fullRecordingSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testWithProperties_minRecordingIntervalDefault() throws TimeoutException {
|
public void testWithProperties_minRecordingIntervalDefault() throws TimeoutException {
|
||||||
PreferencesUtils.setInt(context, R.string.min_recording_interval_key, Integer.parseInt(context.getResources().getString(R.string.min_recording_interval_default)));
|
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||||
|
editor.putInt(context.getString(R.string.min_recording_interval_key), Integer.parseInt(context.getResources().getString(R.string.min_recording_interval_default)));
|
||||||
|
editor.commit();
|
||||||
fullRecordingSession();
|
fullRecordingSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testWithProperties_minRecordingInterval() throws TimeoutException {
|
public void testWithProperties_minRecordingInterval() throws TimeoutException {
|
||||||
PreferencesUtils.setInt(context, R.string.min_recording_interval_key, 2);
|
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||||
|
editor.putInt(context.getString(R.string.min_recording_interval_key), 2);
|
||||||
|
editor.commit();
|
||||||
fullRecordingSession();
|
fullRecordingSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testWithProperties_minRequiredAccuracyDefault() throws TimeoutException {
|
public void testWithProperties_minRequiredAccuracyDefault() throws TimeoutException {
|
||||||
int recordingGPSAccuracyDefault = Integer.parseInt(context.getResources().getString(R.string.recording_gps_accuracy_default));
|
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
|
||||||
PreferencesUtils.setInt(context, R.string.recording_gps_accuracy_key, recordingGPSAccuracyDefault);
|
editor.putInt(context.getString(R.string.recording_gps_accuracy_key), Integer.parseInt(context.getResources().getString(R.string.recording_gps_accuracy_default)));
|
||||||
|
editor.commit();
|
||||||
fullRecordingSession();
|
fullRecordingSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,7 +220,8 @@ public class TrackRecordingServiceTestLooper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void fullRecordingSession() throws TimeoutException {
|
private void fullRecordingSession() throws TimeoutException {
|
||||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(TrackRecordingServiceTest.createStartIntent(context)));
|
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(TrackRecordingServiceTest.createStartIntent(context)))
|
||||||
|
.getService();
|
||||||
assertFalse(service.isRecording());
|
assertFalse(service.isRecording());
|
||||||
|
|
||||||
// Start a track.
|
// Start a track.
|
||||||
@@ -209,8 +231,6 @@ public class TrackRecordingServiceTestLooper {
|
|||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
assertNotNull(track);
|
assertNotNull(track);
|
||||||
assertEquals(trackId, track.getId());
|
assertEquals(trackId, track.getId());
|
||||||
assertEquals(trackId, PreferencesUtils.getRecordingTrackId(context));
|
|
||||||
assertEquals(trackId, service.getRecordingTrackId());
|
|
||||||
|
|
||||||
// Insert a few points, markers and statistics.
|
// Insert a few points, markers and statistics.
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
@@ -223,7 +243,7 @@ public class TrackRecordingServiceTestLooper {
|
|||||||
location.setTime(startTime + i * 10000);
|
location.setTime(startTime + i * 10000);
|
||||||
location.setBearing(3.0f);
|
location.setBearing(3.0f);
|
||||||
TrackPoint trackPoint = new TrackPoint(location);
|
TrackPoint trackPoint = new TrackPoint(location);
|
||||||
int prefAccuracy = PreferencesUtils.getRecordingGPSAccuracy(context);
|
int prefAccuracy = PreferencesUtils.getRecordingGPSAccuracy(sharedPreferences, context);
|
||||||
service.newTrackPoint(trackPoint, prefAccuracy);
|
service.newTrackPoint(trackPoint, prefAccuracy);
|
||||||
|
|
||||||
if (i % 7 == 0) {
|
if (i % 7 == 0) {
|
||||||
@@ -234,7 +254,6 @@ public class TrackRecordingServiceTestLooper {
|
|||||||
// Stop the track. Validate if it has correct data.
|
// Stop the track. Validate if it has correct data.
|
||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
assertFalse(service.isRecording());
|
assertFalse(service.isRecording());
|
||||||
assertNull(service.getRecordingTrackId());
|
|
||||||
track = contentProviderUtils.getTrack(trackId);
|
track = contentProviderUtils.getTrack(trackId);
|
||||||
assertNotNull(track);
|
assertNotNull(track);
|
||||||
assertEquals(trackId, track.getId());
|
assertEquals(trackId, track.getId());
|
||||||
|
|||||||
+6
-2
@@ -1,6 +1,7 @@
|
|||||||
package de.dennisguse.opentracks.services.handlers;
|
package de.dennisguse.opentracks.services.handlers;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@@ -25,6 +26,9 @@ public class HandlerServerTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private LocationHandler locationHandler;
|
private LocationHandler locationHandler;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private SharedPreferences sharedPreferences;
|
||||||
|
|
||||||
private HandlerServer subject;
|
private HandlerServer subject;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@@ -41,10 +45,10 @@ public class HandlerServerTest {
|
|||||||
@Test
|
@Test
|
||||||
public void onSharedPreferenceChanged() {
|
public void onSharedPreferenceChanged() {
|
||||||
// when
|
// when
|
||||||
subject.onSharedPreferenceChanged(null, null, null);
|
subject.onSharedPreferenceChanged(context, sharedPreferences, null);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
verify(locationHandler).onSharedPreferenceChanged(null, null, null);
|
verify(locationHandler).onSharedPreferenceChanged(context, sharedPreferences, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
+4
-4
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.verify;
|
|||||||
public class LocationHandlerTest {
|
public class LocationHandlerTest {
|
||||||
|
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
private final SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private HandlerServer handlerServer;
|
private HandlerServer handlerServer;
|
||||||
@@ -43,11 +44,10 @@ public class LocationHandlerTest {
|
|||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
// Let's use default values.
|
// Let's use default values.
|
||||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
|
||||||
sharedPreferences.edit().clear().commit();
|
sharedPreferences.edit().clear().commit();
|
||||||
|
|
||||||
locationHandler.onSharedPreferenceChanged(context, PreferencesUtils.getSharedPreferences(context), context.getString(R.string.recording_gps_accuracy_key));
|
locationHandler.onSharedPreferenceChanged(context, sharedPreferences, context.getString(R.string.recording_gps_accuracy_key));
|
||||||
locationHandler.onSharedPreferenceChanged(context, PreferencesUtils.getSharedPreferences(context), context.getString(R.string.min_recording_interval_key));
|
locationHandler.onSharedPreferenceChanged(context, sharedPreferences, context.getString(R.string.min_recording_interval_key));
|
||||||
locationHandler.onStart(context);
|
locationHandler.onStart(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ public class LocationHandlerTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testOnLocationChanged_poorAccuracy() {
|
public void testOnLocationChanged_poorAccuracy() {
|
||||||
// given
|
// given
|
||||||
int prefAccuracy = PreferencesUtils.getRecordingGPSAccuracy(context);
|
int prefAccuracy = PreferencesUtils.getRecordingGPSAccuracy(sharedPreferences, context);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
locationHandler.onLocationChanged(createLocation(45f, 35f, prefAccuracy + 1, 5, System.currentTimeMillis()));
|
locationHandler.onLocationChanged(createLocation(45f, 35f, prefAccuracy + 1, 5, System.currentTimeMillis()));
|
||||||
|
|||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
package de.dennisguse.opentracks.services.sensors;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NOTE: Test data is completely artificial.
|
||||||
|
*/
|
||||||
|
public class AltitudeSumManagerTest {
|
||||||
|
|
||||||
|
private final AltitudeSumManager subject = new AltitudeSumManager();
|
||||||
|
|
||||||
|
private static void addSensorValue(AltitudeSumManager altitudeSumManager, float[] values) {
|
||||||
|
for (float f : values) {
|
||||||
|
altitudeSumManager.onSensorValueChanged(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
subject.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAltitudeGainLoss_downhill() {
|
||||||
|
// given
|
||||||
|
subject.setConnected(true);
|
||||||
|
|
||||||
|
// then
|
||||||
|
addSensorValue(subject, new float[]{1015f, 1015.01f, 1015.02f, 1015.03f, 1015.04f, 1015.05f, 1015.06f, 1015.07f, 1015.08f, 1015.09f, 1015.10f, 1015.11f, 1015.12f, 1015.13f, 1015.14f, 1015.15f});
|
||||||
|
|
||||||
|
// then
|
||||||
|
Assert.assertEquals(0f, subject.getAltitudeGain_m(), 0.01);
|
||||||
|
Assert.assertEquals(48.0, subject.getAltitudeLoss_m(), 0.01);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sensorUnavailable() {
|
||||||
|
// given
|
||||||
|
subject.setConnected(false);
|
||||||
|
|
||||||
|
// then
|
||||||
|
subject.onSensorValueChanged(999f);
|
||||||
|
|
||||||
|
// then
|
||||||
|
Assert.assertNull(subject.getAltitudeGain_m());
|
||||||
|
Assert.assertNull(subject.getAltitudeLoss_m());
|
||||||
|
}
|
||||||
|
}
|
||||||
-50
@@ -1,50 +0,0 @@
|
|||||||
package de.dennisguse.opentracks.services.sensors;
|
|
||||||
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* NOTE: Test data is completely artificial.
|
|
||||||
*/
|
|
||||||
public class ElevationSumManagerTest {
|
|
||||||
|
|
||||||
private final ElevationSumManager elevationSumManager = new ElevationSumManager();
|
|
||||||
|
|
||||||
private static void addSensorValue(ElevationSumManager elevationSumManager, float[] values) {
|
|
||||||
for (float f : values) {
|
|
||||||
elevationSumManager.onSensorValueChanged(f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
elevationSumManager.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void getElevationGainLoss_downhill() {
|
|
||||||
// given
|
|
||||||
elevationSumManager.setConnected(true);
|
|
||||||
|
|
||||||
// then
|
|
||||||
addSensorValue(elevationSumManager, new float[]{1015f, 1015.01f, 1015.02f, 1015.03f, 1015.04f, 1015.05f, 1015.06f, 1015.07f, 1015.08f, 1015.09f, 1015.10f, 1015.11f, 1015.12f, 1015.13f, 1015.14f, 1015.15f});
|
|
||||||
|
|
||||||
// then
|
|
||||||
Assert.assertEquals(0f, elevationSumManager.getElevationGain_m(), 0.01);
|
|
||||||
Assert.assertEquals(48.0, elevationSumManager.getElevationLoss_m(), 0.01);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void sensorUnavailable() {
|
|
||||||
// given
|
|
||||||
elevationSumManager.setConnected(false);
|
|
||||||
|
|
||||||
// then
|
|
||||||
elevationSumManager.onSensorValueChanged(999f);
|
|
||||||
|
|
||||||
// then
|
|
||||||
Assert.assertNull(elevationSumManager.getElevationGain_m());
|
|
||||||
Assert.assertNull(elevationSumManager.getElevationLoss_m());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -15,14 +15,18 @@
|
|||||||
*/
|
*/
|
||||||
package de.dennisguse.opentracks.stats;
|
package de.dennisguse.opentracks.stats;
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.JUnit4;
|
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.data.Distance;
|
||||||
|
import de.dennisguse.opentracks.content.data.Speed;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
@@ -32,7 +36,7 @@ import static org.junit.Assert.assertNull;
|
|||||||
*
|
*
|
||||||
* @author Rodrigo Damazio
|
* @author Rodrigo Damazio
|
||||||
*/
|
*/
|
||||||
@RunWith(JUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class TrackStatisticsTest {
|
public class TrackStatisticsTest {
|
||||||
|
|
||||||
private TrackStatistics statistics;
|
private TrackStatistics statistics;
|
||||||
@@ -56,13 +60,13 @@ public class TrackStatisticsTest {
|
|||||||
assertEquals(Duration.ofSeconds(0), statistics.getMovingTime());
|
assertEquals(Duration.ofSeconds(0), statistics.getMovingTime());
|
||||||
assertEquals(Duration.ofSeconds(0), statistics.getTotalTime());
|
assertEquals(Duration.ofSeconds(0), statistics.getTotalTime());
|
||||||
|
|
||||||
assertNull(statistics.getTotalElevationGain());
|
assertNull(statistics.getTotalAltitudeGain());
|
||||||
assertNull(statistics.getTotalElevationLoss());
|
assertNull(statistics.getTotalAltitudeLoss());
|
||||||
assertEquals(Double.NEGATIVE_INFINITY, statistics.getMaxElevation(), 0.0);
|
assertEquals(Double.NEGATIVE_INFINITY, statistics.getMaxAltitude(), 0.0);
|
||||||
assertEquals(Double.POSITIVE_INFINITY, statistics.getMinElevation(), 0.0);
|
assertEquals(Double.POSITIVE_INFINITY, statistics.getMinAltitude(), 0.0);
|
||||||
assertEquals(0.0, statistics.getMaxSpeed(), 0.0);
|
assertEquals(0.0, statistics.getMaxSpeed().toMPS(), 0.0);
|
||||||
assertEquals(0.0, statistics.getAverageSpeed(), 0.0);
|
assertEquals(0.0, statistics.getAverageSpeed().toMPS(), 0.0);
|
||||||
assertEquals(0.0, statistics.getAverageMovingSpeed(), 0.0);
|
assertEquals(0.0, statistics.getAverageMovingSpeed().toMPS(), 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -77,16 +81,16 @@ public class TrackStatisticsTest {
|
|||||||
statistics2.setTotalTime(Duration.ofMillis(1000)); // Result: 1500+1000
|
statistics2.setTotalTime(Duration.ofMillis(1000)); // Result: 1500+1000
|
||||||
statistics.setMovingTime(Duration.ofMillis(700));
|
statistics.setMovingTime(Duration.ofMillis(700));
|
||||||
statistics2.setMovingTime(Duration.ofMillis(600)); // Result: 700+600
|
statistics2.setMovingTime(Duration.ofMillis(600)); // Result: 700+600
|
||||||
statistics.setTotalDistance(750.0);
|
statistics.setTotalDistance(Distance.of(750.0));
|
||||||
statistics2.setTotalDistance(350.0); // Result: 750+350
|
statistics2.setTotalDistance(Distance.of(350.0)); // Result: 750+350
|
||||||
statistics.setTotalElevationGain(50.0f);
|
statistics.setTotalAltitudeGain(50.0f);
|
||||||
statistics2.setTotalElevationGain(850.0f); // Result: 850+50
|
statistics2.setTotalAltitudeGain(850.0f); // Result: 850+50
|
||||||
statistics.setMaxSpeed(60.0); // Resulting max speed
|
statistics.setMaxSpeed(Speed.of(60.0)); // Resulting max speed
|
||||||
statistics2.setMaxSpeed(30.0);
|
statistics2.setMaxSpeed(Speed.of(30.0));
|
||||||
statistics.setMaxElevation(1250.0);
|
statistics.setMaxAltitude(1250.0);
|
||||||
statistics.setMinElevation(1200.0); // Resulting min elevation
|
statistics.setMinAltitude(1200.0); // Resulting min altitude
|
||||||
statistics2.setMaxElevation(3575.0); // Resulting max elevation
|
statistics2.setMaxAltitude(3575.0); // Resulting max altitude
|
||||||
statistics2.setMinElevation(2800.0);
|
statistics2.setMinAltitude(2800.0);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
statistics.merge(statistics2);
|
statistics.merge(statistics2);
|
||||||
@@ -96,24 +100,24 @@ public class TrackStatisticsTest {
|
|||||||
assertEquals(Instant.ofEpochMilli(4000), statistics.getStopTime());
|
assertEquals(Instant.ofEpochMilli(4000), statistics.getStopTime());
|
||||||
assertEquals(Duration.ofMillis(2500), statistics.getTotalTime());
|
assertEquals(Duration.ofMillis(2500), statistics.getTotalTime());
|
||||||
assertEquals(Duration.ofMillis(1300), statistics.getMovingTime());
|
assertEquals(Duration.ofMillis(1300), statistics.getMovingTime());
|
||||||
assertEquals(1100.0, statistics.getTotalDistance(), 0.001);
|
assertEquals(1100.0, statistics.getTotalDistance().toM(), 0.001);
|
||||||
assertEquals(900.0, statistics.getTotalElevationGain(), 0.001);
|
assertEquals(900.0, statistics.getTotalAltitudeGain(), 0.001);
|
||||||
assertEquals(statistics.getTotalDistance() / statistics.getMovingTime().getSeconds(), statistics.getMaxSpeed(), 0.001);
|
assertEquals(Speed.of(statistics.getTotalDistance(), statistics.getMovingTime()).toMPS(), statistics.getMaxSpeed().toMPS(), 0.001);
|
||||||
assertEquals(1200.0, statistics.getMinElevation(), 0.001);
|
assertEquals(1200.0, statistics.getMinAltitude(), 0.001);
|
||||||
assertEquals(3575.0, statistics.getMaxElevation(), 0.001);
|
assertEquals(3575.0, statistics.getMaxAltitude(), 0.001);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetAverageSpeed() {
|
public void testGetAverageSpeed() {
|
||||||
statistics.setTotalDistance(1000.0);
|
statistics.setTotalDistance(Distance.of(1000.0));
|
||||||
statistics.setTotalTime(Duration.ofMillis(50000));
|
statistics.setTotalTime(Duration.ofMillis(50000));
|
||||||
assertEquals(20.0, statistics.getAverageSpeed(), 0.001);
|
assertEquals(20.0, statistics.getAverageSpeed().toMPS(), 0.001);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetAverageMovingSpeed() {
|
public void testGetAverageMovingSpeed() {
|
||||||
statistics.setTotalDistance(1000.0);
|
statistics.setTotalDistance(Distance.of(1000.0));
|
||||||
statistics.setMovingTime(Duration.ofMillis(20000));
|
statistics.setMovingTime(Duration.ofMillis(20000));
|
||||||
assertEquals(50.0, statistics.getAverageMovingSpeed(), 0.001);
|
assertEquals(50.0, statistics.getAverageMovingSpeed().toMPS(), 0.001);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+143
-14
@@ -1,38 +1,167 @@
|
|||||||
package de.dennisguse.opentracks.stats;
|
package de.dennisguse.opentracks.stats;
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.data.Distance;
|
||||||
|
import de.dennisguse.opentracks.content.data.Speed;
|
||||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class TrackStatisticsUpdaterTest {
|
public class TrackStatisticsUpdaterTest {
|
||||||
|
|
||||||
|
private static final Distance GPS_DISTANCE = Distance.of(50);
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void addTrackPoint() {
|
public void addTrackPoint_TestingTrack() {
|
||||||
// given
|
// given
|
||||||
TestDataUtil.TrackData data = TestDataUtil.createTestingTrack(new Track.Id(1));
|
TestDataUtil.TrackData data = TestDataUtil.createTestingTrack(new Track.Id(1));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
TrackStatisticsUpdater updater = new TrackStatisticsUpdater();
|
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
|
||||||
data.trackPoints.forEach(it -> updater.addTrackPoint(it, 50));
|
data.trackPoints.forEach(it -> subject.addTrackPoint(it, GPS_DISTANCE));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
TrackStatistics statistics = updater.getTrackStatistics();
|
TrackStatistics statistics = subject.getTrackStatistics();
|
||||||
assertEquals(85.35, statistics.getTotalDistance(), 0.01);
|
assertEquals(99.58, statistics.getTotalDistance().toM(), 0.01);
|
||||||
assertEquals(Duration.ofMillis(13999), statistics.getTotalTime());
|
assertEquals(Duration.ofSeconds(14), statistics.getTotalTime());
|
||||||
assertEquals(Duration.ofSeconds(6), statistics.getMovingTime());
|
assertEquals(Duration.ofSeconds(7), statistics.getMovingTime());
|
||||||
|
|
||||||
assertEquals(2.5, statistics.getMinElevation(), 0.01);
|
assertEquals(2.5, statistics.getMinAltitude(), 0.01);
|
||||||
assertEquals(27.5, statistics.getMaxElevation(), 0.01);
|
assertEquals(27.5, statistics.getMaxAltitude(), 0.01);
|
||||||
assertEquals(27, statistics.getTotalElevationGain(), 0.01);
|
assertEquals(36, statistics.getTotalAltitudeGain(), 0.01);
|
||||||
assertEquals(27.0, statistics.getTotalElevationLoss(), 0.01);
|
assertEquals(36, statistics.getTotalAltitudeLoss(), 0.01);
|
||||||
|
|
||||||
assertEquals(14.226, statistics.getMaxSpeed(), 0.01);
|
assertEquals(14.226, statistics.getMaxSpeed().toMPS(), 0.01);
|
||||||
assertEquals(14.226, statistics.getAverageMovingSpeed(), 0.01);
|
assertEquals(14.226, statistics.getAverageMovingSpeed().toMPS(), 0.01);
|
||||||
assertEquals(6.566, statistics.getAverageSpeed(), 0.01);
|
assertEquals(7.11, statistics.getAverageSpeed().toMPS(), 0.01);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addTrackPoint_distance_from_GPS_not_moving() {
|
||||||
|
// given
|
||||||
|
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
|
||||||
|
|
||||||
|
TrackPoint tp1 = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochMilli(1000));
|
||||||
|
TrackPoint tp2 = new TrackPoint(0, 0, 5.0, Instant.ofEpochMilli(2000));
|
||||||
|
TrackPoint tp3 = new TrackPoint(0.00001, 0, 5.0, Instant.ofEpochMilli(3000));
|
||||||
|
|
||||||
|
// when
|
||||||
|
subject.addTrackPoint(tp1, GPS_DISTANCE);
|
||||||
|
subject.addTrackPoint(tp2, GPS_DISTANCE);
|
||||||
|
subject.addTrackPoint(tp3, GPS_DISTANCE);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals(0, subject.getTrackStatistics().getTotalDistance().toM(), 0.01);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addTrackPoint_distance_from_GPS_moving() {
|
||||||
|
// given
|
||||||
|
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
|
||||||
|
|
||||||
|
TrackPoint tp1 = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochMilli(1000));
|
||||||
|
TrackPoint tp2 = new TrackPoint(0, 0, 5.0, Instant.ofEpochMilli(2000));
|
||||||
|
TrackPoint tp3 = new TrackPoint(0.00001, 0, 5.0, Instant.ofEpochMilli(3000));
|
||||||
|
tp3.setSpeed(Speed.of(5f));
|
||||||
|
|
||||||
|
// when
|
||||||
|
subject.addTrackPoint(tp1, GPS_DISTANCE);
|
||||||
|
subject.addTrackPoint(tp2, GPS_DISTANCE);
|
||||||
|
subject.addTrackPoint(tp3, GPS_DISTANCE);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals(1.10, subject.getTrackStatistics().getTotalDistance().toM(), 0.01);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addTrackPoint_distance_from_GPS_moving_and_sensor_moving() {
|
||||||
|
// given
|
||||||
|
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
|
||||||
|
|
||||||
|
TrackPoint tp1 = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochMilli(1000));
|
||||||
|
TrackPoint tp2 = new TrackPoint(0, 0, 5.0, Instant.ofEpochMilli(2000));
|
||||||
|
tp2.setSpeed(Speed.of(5f));
|
||||||
|
TrackPoint tp3 = new TrackPoint(0.001, 0, 5.0, Instant.ofEpochMilli(3000));
|
||||||
|
tp2.setSpeed(Speed.of(5f));
|
||||||
|
TrackPoint tp4 = new TrackPoint(0.001, 0, 5.0, Instant.ofEpochMilli(4000));
|
||||||
|
tp2.setSpeed(Speed.of(5f));
|
||||||
|
tp4.setSensorDistance(Distance.of(5f));
|
||||||
|
TrackPoint tp5 = new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.ofEpochMilli(5000));
|
||||||
|
tp5.setSensorDistance(Distance.of(10f));
|
||||||
|
|
||||||
|
// when
|
||||||
|
subject.addTrackPoint(tp1, GPS_DISTANCE);
|
||||||
|
subject.addTrackPoint(tp2, GPS_DISTANCE);
|
||||||
|
subject.addTrackPoint(tp3, GPS_DISTANCE);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals(110.57, subject.getTrackStatistics().getTotalDistance().toM(), 0.01);
|
||||||
|
|
||||||
|
// when
|
||||||
|
subject.addTrackPoint(tp4, GPS_DISTANCE);
|
||||||
|
subject.addTrackPoint(tp5, GPS_DISTANCE);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals(125.57, subject.getTrackStatistics().getTotalDistance().toM(), 0.01);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addTrackPoint_distance_from_GPS_not_moving_and_sensor_moving() {
|
||||||
|
// given
|
||||||
|
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
|
||||||
|
|
||||||
|
TrackPoint tp1 = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochMilli(1000));
|
||||||
|
TrackPoint tp2 = new TrackPoint(0, 0, 5.0, Instant.ofEpochMilli(2000));
|
||||||
|
TrackPoint tp3 = new TrackPoint(0.00001, 0, 5.0, Instant.ofEpochMilli(3000));
|
||||||
|
TrackPoint tp4 = new TrackPoint(0.00001, 0, 5.0, Instant.ofEpochMilli(4000));
|
||||||
|
tp4.setSensorDistance(Distance.of(5f));
|
||||||
|
TrackPoint tp5 = new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.ofEpochMilli(5000));
|
||||||
|
tp5.setSensorDistance(Distance.of(10f));
|
||||||
|
|
||||||
|
// when
|
||||||
|
subject.addTrackPoint(tp1, GPS_DISTANCE);
|
||||||
|
subject.addTrackPoint(tp2, GPS_DISTANCE);
|
||||||
|
subject.addTrackPoint(tp3, GPS_DISTANCE);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals(0, subject.getTrackStatistics().getTotalDistance().toM(), 0.01);
|
||||||
|
|
||||||
|
// when
|
||||||
|
subject.addTrackPoint(tp4, GPS_DISTANCE);
|
||||||
|
subject.addTrackPoint(tp5, GPS_DISTANCE);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals(15, subject.getTrackStatistics().getTotalDistance().toM(), 0.01);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Ignore("TODO: create a concept ont to compute speed from GPS and sensor")
|
||||||
|
@Test
|
||||||
|
public void addTrackPoint_speed_from_GPS_not_moving() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Ignore("TODO: create a concept ont to compute speed from GPS and sensor")
|
||||||
|
@Test
|
||||||
|
public void addTrackPoint_speed_from_GPS_moving() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Ignore("TODO: create a concept ont to compute speed from GPS and sensor")
|
||||||
|
@Test
|
||||||
|
public void addTrackPoint_speed_from_GPS_not_moving_and_sensor_speed() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Ignore("TODO: create a concept ont to compute speed from GPS and sensor")
|
||||||
|
@Test
|
||||||
|
public void addTrackPoint_speed_from_GPS_moving_and_sensor_speed() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,8 @@ import org.junit.runner.RunWith;
|
|||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.content.data.Distance;
|
||||||
|
import de.dennisguse.opentracks.content.data.Speed;
|
||||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
@@ -26,44 +27,146 @@ public class AnnouncementUtilsTest {
|
|||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getAnnouncement_metric() {
|
public void getAnnouncement_metric_speed() {
|
||||||
PreferencesUtils.setString(context, R.string.stats_units_key, context.getString(R.string.stats_units_metric));
|
|
||||||
|
|
||||||
TrackStatistics stats = new TrackStatistics();
|
TrackStatistics stats = new TrackStatistics();
|
||||||
stats.setTotalDistance(20000);
|
stats.setTotalDistance(Distance.of(20000));
|
||||||
stats.setTotalTime(Duration.ofMillis(600000));
|
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||||
stats.setMovingTime(Duration.ofMillis(300000));
|
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||||
stats.setMaxSpeed(100);
|
stats.setMaxSpeed(Speed.of(100));
|
||||||
stats.setTotalElevationGain(6000f);
|
stats.setTotalAltitudeGain(6000f);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = AnnouncementUtils.getAnnouncement(context, stats, "airplane", null);
|
String announcement = AnnouncementUtils.getAnnouncement(context, stats, true, true, null);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals("OpenTracks total distance 20.00 kilometers in 5 minutes 0 seconds at 240.0 kilometers per hour", announcement);
|
assertEquals("OpenTracks total distance 20.00 kilometers in 1 hour 5 minutes 10 seconds at 18.4 kilometers per hour", announcement);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getAnnouncement_withInterval_metric() {
|
public void getAnnouncement_withInterval_metric_speed() {
|
||||||
PreferencesUtils.setString(context, R.string.stats_units_key, context.getString(R.string.stats_units_metric));
|
|
||||||
|
|
||||||
TrackStatistics stats = new TrackStatistics();
|
TrackStatistics stats = new TrackStatistics();
|
||||||
stats.setTotalDistance(20000);
|
stats.setTotalDistance(Distance.of(20000));
|
||||||
stats.setTotalTime(Duration.ofMillis(600000));
|
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||||
stats.setMovingTime(Duration.ofMillis(300000));
|
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||||
stats.setMaxSpeed(100);
|
stats.setMaxSpeed(Speed.of(100));
|
||||||
stats.setTotalElevationGain(6000f);
|
stats.setTotalAltitudeGain(6000f);
|
||||||
|
|
||||||
List<TrackPoint> trackPoints = TestDataUtil.createTrack(new Track.Id(System.currentTimeMillis()), 10).second;
|
List<TrackPoint> trackPoints = TestDataUtil.createTrack(new Track.Id(System.currentTimeMillis()), 10).second;
|
||||||
IntervalStatistics intervalStatistics = new IntervalStatistics(trackPoints, 1000);
|
IntervalStatistics intervalStatistics = new IntervalStatistics(trackPoints, Distance.of(1000));
|
||||||
IntervalStatistics.Interval lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
IntervalStatistics.Interval lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||||
|
|
||||||
String expected = "OpenTracks total distance 20.00 kilometers in 5 minutes 0 seconds at 240.0 kilometers per hour Lap speed of 51.2 kilometers per hour";
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = AnnouncementUtils.getAnnouncement(context, stats, "airplane", lastInterval);
|
String announcement = AnnouncementUtils.getAnnouncement(context, stats, true, true, lastInterval);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(expected, announcement);
|
assertEquals("OpenTracks total distance 20.00 kilometers in 1 hour 5 minutes 10 seconds at 18.4 kilometers per hour Lap speed of 51.2 kilometers per hour", announcement);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAnnouncement_metric_pace() {
|
||||||
|
TrackStatistics stats = new TrackStatistics();
|
||||||
|
stats.setTotalDistance(Distance.of(20000));
|
||||||
|
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||||
|
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||||
|
stats.setMaxSpeed(Speed.of(100));
|
||||||
|
stats.setTotalAltitudeGain(6000f);
|
||||||
|
|
||||||
|
// when
|
||||||
|
String announcement = AnnouncementUtils.getAnnouncement(context, stats, true, false, null);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals("OpenTracks total distance 20.00 kilometers in 1 hour 5 minutes 10 seconds at 3 minutes 15 seconds per kilometer", announcement);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAnnouncement_withInterval_metric_pace() {
|
||||||
|
TrackStatistics stats = new TrackStatistics();
|
||||||
|
stats.setTotalDistance(Distance.of(20000));
|
||||||
|
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||||
|
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||||
|
stats.setMaxSpeed(Speed.of(100));
|
||||||
|
stats.setTotalAltitudeGain(6000f);
|
||||||
|
|
||||||
|
List<TrackPoint> trackPoints = TestDataUtil.createTrack(new Track.Id(System.currentTimeMillis()), 10).second;
|
||||||
|
IntervalStatistics intervalStatistics = new IntervalStatistics(trackPoints, Distance.of(1000));
|
||||||
|
IntervalStatistics.Interval lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||||
|
|
||||||
|
// when
|
||||||
|
String announcement = AnnouncementUtils.getAnnouncement(context, stats, true, false, lastInterval);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals("OpenTracks total distance 20.00 kilometers in 1 hour 5 minutes 10 seconds at 3 minutes 15 seconds per kilometer Lap time of 1 minute 10 seconds per kilometer", announcement);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAnnouncement_imperial_speed() {
|
||||||
|
TrackStatistics stats = new TrackStatistics();
|
||||||
|
stats.setTotalDistance(Distance.of(20000));
|
||||||
|
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||||
|
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||||
|
stats.setMaxSpeed(Speed.of(100));
|
||||||
|
stats.setTotalAltitudeGain(6000f);
|
||||||
|
|
||||||
|
// when
|
||||||
|
String announcement = AnnouncementUtils.getAnnouncement(context, stats, false, true, null);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals("OpenTracks total distance 12.43 miles in 1 hour 5 minutes 10 seconds at 11.4 miles per hour", announcement);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAnnouncement_withInterval_imperial_speed() {
|
||||||
|
TrackStatistics stats = new TrackStatistics();
|
||||||
|
stats.setTotalDistance(Distance.of(20000));
|
||||||
|
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||||
|
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||||
|
stats.setMaxSpeed(Speed.of(100));
|
||||||
|
stats.setTotalAltitudeGain(6000f);
|
||||||
|
|
||||||
|
List<TrackPoint> trackPoints = TestDataUtil.createTrack(new Track.Id(System.currentTimeMillis()), 10).second;
|
||||||
|
IntervalStatistics intervalStatistics = new IntervalStatistics(trackPoints, Distance.of(1000));
|
||||||
|
IntervalStatistics.Interval lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||||
|
|
||||||
|
// when
|
||||||
|
String announcement = AnnouncementUtils.getAnnouncement(context, stats, false, true, lastInterval);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals("OpenTracks total distance 12.43 miles in 1 hour 5 minutes 10 seconds at 11.4 miles per hour Lap speed of 31.8 miles per hour", announcement);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAnnouncement_imperial_pace() {
|
||||||
|
TrackStatistics stats = new TrackStatistics();
|
||||||
|
stats.setTotalDistance(Distance.of(20000));
|
||||||
|
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||||
|
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||||
|
stats.setMaxSpeed(Speed.of(100));
|
||||||
|
stats.setTotalAltitudeGain(6000f);
|
||||||
|
|
||||||
|
// when
|
||||||
|
String announcement = AnnouncementUtils.getAnnouncement(context, stats, false, false, null);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals("OpenTracks total distance 12.43 miles in 1 hour 5 minutes 10 seconds at 5 minutes 15 seconds per mile", announcement);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAnnouncement_withInterval_imperial_pace() {
|
||||||
|
TrackStatistics stats = new TrackStatistics();
|
||||||
|
stats.setTotalDistance(Distance.of(20000));
|
||||||
|
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||||
|
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||||
|
stats.setMaxSpeed(Speed.of(100));
|
||||||
|
stats.setTotalAltitudeGain(6000f);
|
||||||
|
|
||||||
|
List<TrackPoint> trackPoints = TestDataUtil.createTrack(new Track.Id(System.currentTimeMillis()), 10).second;
|
||||||
|
IntervalStatistics intervalStatistics = new IntervalStatistics(trackPoints, Distance.of(1000));
|
||||||
|
IntervalStatistics.Interval lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||||
|
|
||||||
|
// when
|
||||||
|
String announcement = AnnouncementUtils.getAnnouncement(context, stats, false, false, lastInterval);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals("OpenTracks total distance 12.43 miles in 1 hour 5 minutes 10 seconds at 5 minutes 15 seconds per mile Lap time of 1 minute 53 seconds per mile", announcement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,7 @@ public class BluetoothUtilsTest {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(200, sensor.getCadence().getCrankRevolutionsCount());
|
assertEquals(200, sensor.getCadence().getCrankRevolutionsCount());
|
||||||
assertNull(sensor.getSpeed());
|
assertNull(sensor.getDistanceSpeed());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -60,7 +60,7 @@ public class BluetoothUtilsTest {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
assertNull(sensor.getCadence());
|
assertNull(sensor.getCadence());
|
||||||
assertEquals(225, sensor.getSpeed().getWheelRevolutionsCount());
|
assertEquals(225, sensor.getDistanceSpeed().getWheelRevolutionsCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -73,7 +73,7 @@ public class BluetoothUtilsTest {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(200, sensor.getCadence().getCrankRevolutionsCount());
|
assertEquals(200, sensor.getCadence().getCrankRevolutionsCount());
|
||||||
assertEquals(225, sensor.getSpeed().getWheelRevolutionsCount());
|
assertEquals(225, sensor.getDistanceSpeed().getWheelRevolutionsCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package de.dennisguse.opentracks.util;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@RunWith(JUnit4.class)
|
||||||
|
public class ExportUtilsTest extends TestCase {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsExportFileExists_exists() {
|
||||||
|
// given
|
||||||
|
UUID uuid = UUID.randomUUID();
|
||||||
|
String format = "kmz";
|
||||||
|
List<String> fileNames = new ArrayList<>();
|
||||||
|
fileNames.add(uuid.toString().substring(0, 8) + "_name.gpx");
|
||||||
|
fileNames.add(uuid.toString().substring(0, 8) + "_other name name.kmz");
|
||||||
|
|
||||||
|
// when
|
||||||
|
boolean exists = ExportUtils.isExportFileExists(uuid, format, fileNames);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertTrue(exists);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsExportFileExists_not_exists1() {
|
||||||
|
// given
|
||||||
|
UUID uuid = UUID.randomUUID();
|
||||||
|
String format = "gpx";
|
||||||
|
List<String> fileNames = new ArrayList<>();
|
||||||
|
fileNames.add(UUID.randomUUID().toString().substring(0, 8) + "_name.gpx");
|
||||||
|
fileNames.add(UUID.randomUUID().toString().substring(0, 8) + "_other name name.gpx");
|
||||||
|
|
||||||
|
// when
|
||||||
|
boolean exists = ExportUtils.isExportFileExists(uuid, format, fileNames);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertFalse(exists);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsExportFileExists_not_exists2() {
|
||||||
|
// given
|
||||||
|
UUID uuid = UUID.randomUUID();
|
||||||
|
String format = "kmz";
|
||||||
|
List<String> fileNames = new ArrayList<>();
|
||||||
|
fileNames.add(uuid.toString().substring(0, 8) + "_name.gpx");
|
||||||
|
fileNames.add(UUID.randomUUID().toString().substring(0, 8) + "_other name name.gpx");
|
||||||
|
|
||||||
|
// when
|
||||||
|
boolean exists = ExportUtils.isExportFileExists(uuid, format, fileNames);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertFalse(exists);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,12 +22,14 @@ public class PreferencesUtilsTest {
|
|||||||
@Test
|
@Test
|
||||||
public void ExportTrackFileFormat_ok() {
|
public void ExportTrackFileFormat_ok() {
|
||||||
// given
|
// given
|
||||||
SharedPreferences.Editor editor = PreferencesUtils.getSharedPreferences(context).edit();
|
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||||
|
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||||
|
|
||||||
editor.putString(context.getString(R.string.export_trackfileformat_key), TrackFileFormat.KMZ_WITH_TRACKDETAIL.name());
|
editor.putString(context.getString(R.string.export_trackfileformat_key), TrackFileFormat.KMZ_WITH_TRACKDETAIL.name());
|
||||||
editor.commit();
|
editor.commit();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(context);
|
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(sharedPreferences, context);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL, trackFileFormat);
|
assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL, trackFileFormat);
|
||||||
@@ -36,12 +38,13 @@ public class PreferencesUtilsTest {
|
|||||||
@Test
|
@Test
|
||||||
public void ExportTrackFileFormat_invalid() {
|
public void ExportTrackFileFormat_invalid() {
|
||||||
// given
|
// given
|
||||||
SharedPreferences.Editor editor = PreferencesUtils.getSharedPreferences(context).edit();
|
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||||
|
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||||
editor.putString(context.getString(R.string.export_trackfileformat_key), "invalid");
|
editor.putString(context.getString(R.string.export_trackfileformat_key), "invalid");
|
||||||
editor.commit();
|
editor.commit();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(context);
|
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(sharedPreferences, context);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, trackFileFormat);
|
assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, trackFileFormat);
|
||||||
@@ -50,12 +53,13 @@ public class PreferencesUtilsTest {
|
|||||||
@Test
|
@Test
|
||||||
public void ExportTrackFileFormat_noValue() {
|
public void ExportTrackFileFormat_noValue() {
|
||||||
// given
|
// given
|
||||||
SharedPreferences.Editor editor = PreferencesUtils.getSharedPreferences(context).edit();
|
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||||
|
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||||
editor.clear();
|
editor.clear();
|
||||||
editor.commit();
|
editor.commit();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(context);
|
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(sharedPreferences, context);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, trackFileFormat);
|
assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, trackFileFormat);
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ public class PressureSensorUtilsTest {
|
|||||||
|
|
||||||
// real data at 500Hz stationary
|
// real data at 500Hz stationary
|
||||||
@Test
|
@Test
|
||||||
public void elevationChanges_none() {
|
public void altitudeChanges_none() {
|
||||||
// given
|
// given
|
||||||
float[] sensorValues_hPa = new float[]{1015.6876f, 1015.699f, 1015.70905f, 1015.71075f, 1015.7224f, 1015.72f, 1015.7373f, 1015.7481f, 1015.74133f, 1015.73553f, 1015.7462f, 1015.74896f, 1015.752f, 1015.754f, 1015.7456f, 1015.73303f, 1015.7288f, 1015.7239f, 1015.7298f, 1015.7387f, 1015.72534f, 1015.72577f, 1015.72797f, 1015.71796f, 1015.7113f, 1015.7097f, 1015.7096f, 1015.7013f, 1015.7072f, 1015.71063f, 1015.7088f, 1015.7115f, 1015.713f, 1015.7121f, 1015.7062f, 1015.7062f, 1015.6994f, 1015.69617f, 1015.68945f, 1015.698f, 1015.6913f, 1015.69696f, 1015.69293f, 1015.6955f, 1015.6928f, 1015.6939f, 1015.68787f, 1015.6881f, 1015.68536f, 1015.68726f, 1015.6879f, 1015.6904f, 1015.6937f, 1015.69476f, 1015.7046f, 1015.7019f, 1015.7079f, 1015.71124f, 1015.71216f, 1015.71436f, 1015.7171f, 1015.7186f, 1015.7186f, 1015.7246f, 1015.72046f, 1015.7211f, 1015.72375f, 1015.7194f, 1015.72125f, 1015.7161f, 1015.71185f, 1015.7188f, 1015.721f, 1015.7252f, 1015.72266f, 1015.7236f, 1015.72614f, 1015.7266f, 1015.7344f, 1015.7376f, 1015.7337f, 1015.7253f, 1015.71875f, 1015.7219f, 1015.7219f, 1015.7317f, 1015.7284f, 1015.73444f, 1015.73584f, 1015.7369f, 1015.73254f, 1015.7369f, 1015.7336f, 1015.73254f, 1015.73676f, 1015.74176f, 1015.74506f, 1015.7478f, 1015.755f, 1015.7515f, 1015.7543f, 1015.7459f, 1015.7468f, 1015.7492f, 1015.74585f, 1015.74756f, 1015.74756f, 1015.75085f, 1015.754f, 1015.75336f, 1015.7568f, 1015.75165f, 1015.761f, 1015.7527f, 1015.75684f, 1015.7635f, 1015.7585f, 1015.7552f, 1015.7475f, 1015.74506f, 1015.7542f, 1015.75586f, 1015.74176f, 1015.74414f, 1015.7501f, 1015.751f, 1015.7478f, 1015.755f, 1015.75836f, 1015.7592f, 1015.75256f, 1015.7559f, 1015.76654f, 1015.76154f, 1015.766f, 1015.77014f, 1015.76904f, 1015.76013f, 1015.7674f, 1015.76514f, 1015.7674f, 1015.7635f, 1015.76404f, 1015.7607f, 1015.754f, 1015.7574f, 1015.7607f, 1015.7607f, 1015.7607f, 1015.754f, 1015.7567f, 1015.7616f, 1015.7643f, 1015.7666f, 1015.7609f, 1015.766f, 1015.76764f, 1015.755f, 1015.75757f, 1015.74927f, 1015.74927f, 1015.7426f, 1015.745f, 1015.745f, 1015.7409f, 1015.7376f, 1015.7326f, 1015.74164f, 1015.74664f, 1015.745f, 1015.7459f, 1015.7502f, 1015.7493f, 1015.7476f, 1015.7493f, 1015.751f, 1015.7601f, 1015.76434f, 1015.766f, 1015.756f, 1015.7617f, 1015.7623f, 1015.76404f, 1015.76733f, 1015.7607f, 1015.7623f, 1015.76337f, 1015.7584f, 1015.7573f, 1015.7502f, 1015.7534f, 1015.7534f, 1015.7601f, 1015.75684f, 1015.7518f, 1015.75574f, 1015.7524f, 1015.7507f, 1015.7484f, 1015.7502f, 1015.7534f, 1015.7623f, 1015.75903f, 1015.75574f, 1015.75574f, 1015.7573f, 1015.7524f, 1015.75574f, 1015.75574f, 1015.7607f, 1015.7573f, 1015.754f, 1015.74677f, 1015.754f, 1015.75574f, 1015.75903f, 1015.7573f, 1015.7551f, 1015.7573f, 1015.754f, 1015.7507f, 1015.7374f, 1015.739f, 1015.73566f, 1015.73566f, 1015.7367f, 1015.7417f, 1015.7417f, 1015.739f, 1015.74835f, 1015.74567f, 1015.7517f, 1015.74066f, 1015.7374f, 1015.739f, 1015.7417f, 1015.73065f, 1015.72894f, 1015.73505f, 1015.73334f, 1015.74005f, 1015.74005f, 1015.7367f, 1015.7434f, 1015.744f, 1015.7423f, 1015.7384f, 1015.739f, 1015.7374f, 1015.7374f, 1015.7374f, 1015.7341f, 1015.73505f, 1015.7341f, 1015.7267f, 1015.72833f, 1015.7234f, 1015.7167f, 1015.725f, 1015.7217f, 1015.7234f, 1015.73004f, 1015.7324f, 1015.7423f, 1015.74005f, 1015.73505f, 1015.73004f, 1015.739f, 1015.7417f, 1015.74567f, 1015.74066f, 1015.74146f, 1015.74146f, 1015.73816f, 1015.7274f, 1015.72906f, 1015.7274f, 1015.7341f, 1015.739f, 1015.744f, 1015.7417f, 1015.7374f, 1015.7324f, 1015.73816f, 1015.74146f, 1015.7374f, 1015.7341f, 1015.7365f, 1015.7365f, 1015.74146f, 1015.74414f, 1015.7475f, 1015.7448f, 1015.74146f, 1015.74585f, 1015.7475f, 1015.75146f, 1015.75146f, 1015.7591f, 1015.75476f, 1015.7614f, 1015.75476f, 1015.7492f, 1015.74817f, 1015.7498f, 1015.7515f, 1015.75586f, 1015.7509f, 1015.7448f, 1015.7525f, 1015.7581f, 1015.7592f};
|
float[] sensorValues_hPa = new float[]{1015.6876f, 1015.699f, 1015.70905f, 1015.71075f, 1015.7224f, 1015.72f, 1015.7373f, 1015.7481f, 1015.74133f, 1015.73553f, 1015.7462f, 1015.74896f, 1015.752f, 1015.754f, 1015.7456f, 1015.73303f, 1015.7288f, 1015.7239f, 1015.7298f, 1015.7387f, 1015.72534f, 1015.72577f, 1015.72797f, 1015.71796f, 1015.7113f, 1015.7097f, 1015.7096f, 1015.7013f, 1015.7072f, 1015.71063f, 1015.7088f, 1015.7115f, 1015.713f, 1015.7121f, 1015.7062f, 1015.7062f, 1015.6994f, 1015.69617f, 1015.68945f, 1015.698f, 1015.6913f, 1015.69696f, 1015.69293f, 1015.6955f, 1015.6928f, 1015.6939f, 1015.68787f, 1015.6881f, 1015.68536f, 1015.68726f, 1015.6879f, 1015.6904f, 1015.6937f, 1015.69476f, 1015.7046f, 1015.7019f, 1015.7079f, 1015.71124f, 1015.71216f, 1015.71436f, 1015.7171f, 1015.7186f, 1015.7186f, 1015.7246f, 1015.72046f, 1015.7211f, 1015.72375f, 1015.7194f, 1015.72125f, 1015.7161f, 1015.71185f, 1015.7188f, 1015.721f, 1015.7252f, 1015.72266f, 1015.7236f, 1015.72614f, 1015.7266f, 1015.7344f, 1015.7376f, 1015.7337f, 1015.7253f, 1015.71875f, 1015.7219f, 1015.7219f, 1015.7317f, 1015.7284f, 1015.73444f, 1015.73584f, 1015.7369f, 1015.73254f, 1015.7369f, 1015.7336f, 1015.73254f, 1015.73676f, 1015.74176f, 1015.74506f, 1015.7478f, 1015.755f, 1015.7515f, 1015.7543f, 1015.7459f, 1015.7468f, 1015.7492f, 1015.74585f, 1015.74756f, 1015.74756f, 1015.75085f, 1015.754f, 1015.75336f, 1015.7568f, 1015.75165f, 1015.761f, 1015.7527f, 1015.75684f, 1015.7635f, 1015.7585f, 1015.7552f, 1015.7475f, 1015.74506f, 1015.7542f, 1015.75586f, 1015.74176f, 1015.74414f, 1015.7501f, 1015.751f, 1015.7478f, 1015.755f, 1015.75836f, 1015.7592f, 1015.75256f, 1015.7559f, 1015.76654f, 1015.76154f, 1015.766f, 1015.77014f, 1015.76904f, 1015.76013f, 1015.7674f, 1015.76514f, 1015.7674f, 1015.7635f, 1015.76404f, 1015.7607f, 1015.754f, 1015.7574f, 1015.7607f, 1015.7607f, 1015.7607f, 1015.754f, 1015.7567f, 1015.7616f, 1015.7643f, 1015.7666f, 1015.7609f, 1015.766f, 1015.76764f, 1015.755f, 1015.75757f, 1015.74927f, 1015.74927f, 1015.7426f, 1015.745f, 1015.745f, 1015.7409f, 1015.7376f, 1015.7326f, 1015.74164f, 1015.74664f, 1015.745f, 1015.7459f, 1015.7502f, 1015.7493f, 1015.7476f, 1015.7493f, 1015.751f, 1015.7601f, 1015.76434f, 1015.766f, 1015.756f, 1015.7617f, 1015.7623f, 1015.76404f, 1015.76733f, 1015.7607f, 1015.7623f, 1015.76337f, 1015.7584f, 1015.7573f, 1015.7502f, 1015.7534f, 1015.7534f, 1015.7601f, 1015.75684f, 1015.7518f, 1015.75574f, 1015.7524f, 1015.7507f, 1015.7484f, 1015.7502f, 1015.7534f, 1015.7623f, 1015.75903f, 1015.75574f, 1015.75574f, 1015.7573f, 1015.7524f, 1015.75574f, 1015.75574f, 1015.7607f, 1015.7573f, 1015.754f, 1015.74677f, 1015.754f, 1015.75574f, 1015.75903f, 1015.7573f, 1015.7551f, 1015.7573f, 1015.754f, 1015.7507f, 1015.7374f, 1015.739f, 1015.73566f, 1015.73566f, 1015.7367f, 1015.7417f, 1015.7417f, 1015.739f, 1015.74835f, 1015.74567f, 1015.7517f, 1015.74066f, 1015.7374f, 1015.739f, 1015.7417f, 1015.73065f, 1015.72894f, 1015.73505f, 1015.73334f, 1015.74005f, 1015.74005f, 1015.7367f, 1015.7434f, 1015.744f, 1015.7423f, 1015.7384f, 1015.739f, 1015.7374f, 1015.7374f, 1015.7374f, 1015.7341f, 1015.73505f, 1015.7341f, 1015.7267f, 1015.72833f, 1015.7234f, 1015.7167f, 1015.725f, 1015.7217f, 1015.7234f, 1015.73004f, 1015.7324f, 1015.7423f, 1015.74005f, 1015.73505f, 1015.73004f, 1015.739f, 1015.7417f, 1015.74567f, 1015.74066f, 1015.74146f, 1015.74146f, 1015.73816f, 1015.7274f, 1015.72906f, 1015.7274f, 1015.7341f, 1015.739f, 1015.744f, 1015.7417f, 1015.7374f, 1015.7324f, 1015.73816f, 1015.74146f, 1015.7374f, 1015.7341f, 1015.7365f, 1015.7365f, 1015.74146f, 1015.74414f, 1015.7475f, 1015.7448f, 1015.74146f, 1015.74585f, 1015.7475f, 1015.75146f, 1015.75146f, 1015.7591f, 1015.75476f, 1015.7614f, 1015.75476f, 1015.7492f, 1015.74817f, 1015.7498f, 1015.7515f, 1015.75586f, 1015.7509f, 1015.7448f, 1015.7525f, 1015.7581f, 1015.7592f};
|
||||||
float firstSensorValue = sensorValues_hPa[0];
|
float firstSensorValue = sensorValues_hPa[0];
|
||||||
@@ -20,7 +20,7 @@ public class PressureSensorUtilsTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simulate a sudden drop of elevation.
|
// Simulate a sudden drop of altitude.
|
||||||
// test data
|
// test data
|
||||||
@Test
|
@Test
|
||||||
public void computeChanges_downhill() {
|
public void computeChanges_downhill() {
|
||||||
@@ -28,22 +28,22 @@ public class PressureSensorUtilsTest {
|
|||||||
float firstSensorValue = sensorValues_hPa[0];
|
float firstSensorValue = sensorValues_hPa[0];
|
||||||
|
|
||||||
// when
|
// when
|
||||||
float elevationGain_m = 0;
|
float altitudeGain_m = 0;
|
||||||
float elevationLoss_m = 0;
|
float altitudeLoss = 0;
|
||||||
float lastUsedPressureValue_hPa = firstSensorValue;
|
float lastUsedPressureValue_hPa = firstSensorValue;
|
||||||
|
|
||||||
for (float v : sensorValues_hPa) {
|
for (float v : sensorValues_hPa) {
|
||||||
PressureSensorUtils.ElevationChange elevationChange = PressureSensorUtils.computeChanges_m(lastUsedPressureValue_hPa, v);
|
PressureSensorUtils.AltitudeChange altitudeChange = PressureSensorUtils.computeChanges_m(lastUsedPressureValue_hPa, v);
|
||||||
if (elevationChange != null) {
|
if (altitudeChange != null) {
|
||||||
elevationGain_m += elevationChange.getElevationGain_m();
|
altitudeGain_m += altitudeChange.getAltitudeGain_m();
|
||||||
elevationLoss_m += elevationChange.getElevationLoss_m();
|
altitudeLoss += altitudeChange.getAltitudeLoss_m();
|
||||||
lastUsedPressureValue_hPa = elevationChange.getCurrentSensorValue_hPa();
|
lastUsedPressureValue_hPa = altitudeChange.getCurrentSensorValue_hPa();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(0f, elevationGain_m, 0.01);
|
assertEquals(0f, altitudeGain_m, 0.01);
|
||||||
assertEquals(15.0f, elevationLoss_m, 0.01);
|
assertEquals(15.0f, altitudeLoss, 0.01);
|
||||||
assertEquals(1016.80f, lastUsedPressureValue_hPa, 0.01); //Expect exponential smoothing
|
assertEquals(1016.80f, lastUsedPressureValue_hPa, 0.01); //Expect exponential smoothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28,6 +28,9 @@ import java.time.Duration;
|
|||||||
import java.util.GregorianCalendar;
|
import java.util.GregorianCalendar;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.data.Distance;
|
||||||
|
import de.dennisguse.opentracks.content.data.Speed;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
@@ -70,19 +73,18 @@ public class StringUtilsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link StringUtils#formatDistance(android.content.Context, double,
|
* Tests {@link StringUtils#formatDistance(android.content.Context, Distance, boolean)}.
|
||||||
* boolean)}.
|
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testFormatDistance() {
|
public void testFormatDistance() {
|
||||||
// A large number in metric
|
// A large number in metric
|
||||||
assertEquals("5.00 km", StringUtils.formatDistance(context, 5000, true));
|
assertEquals("5.00 km", StringUtils.formatDistance(context, Distance.of(5000), true));
|
||||||
// A large number in imperial
|
// A large number in imperial
|
||||||
assertEquals("3.11 mi", StringUtils.formatDistance(context, 5000, false));
|
assertEquals("3.11 mi", StringUtils.formatDistance(context, Distance.of(5000), false));
|
||||||
// A small number in metric
|
// A small number in metric
|
||||||
assertEquals("100.00 m", StringUtils.formatDistance(context, 100, true));
|
assertEquals("100.00 m", StringUtils.formatDistance(context, Distance.of(100), true));
|
||||||
// A small number in imperial
|
// A small number in imperial
|
||||||
assertEquals("328.08 ft", StringUtils.formatDistance(context, 100, false));
|
assertEquals("328.08 ft", StringUtils.formatDistance(context, Distance.of(100), false));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -164,22 +166,22 @@ public class StringUtilsTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetSpeedParts() {
|
public void testGetSpeedParts() {
|
||||||
assertEquals("4:59", StringUtils.getSpeedParts(context, 3.34, true, false).first);
|
assertEquals("4:59", StringUtils.getSpeedParts(context, Speed.of(3.34), true, false).first);
|
||||||
assertEquals("5:00", StringUtils.getSpeedParts(context, 3.33, true, false).first);
|
assertEquals("5:00", StringUtils.getSpeedParts(context, Speed.of(3.33), true, false).first);
|
||||||
|
|
||||||
assertEquals("11.9", StringUtils.getSpeedParts(context, 3.31, true, true).first);
|
assertEquals("11.9", StringUtils.getSpeedParts(context, Speed.of(3.31), true, true).first);
|
||||||
assertEquals("7.5", StringUtils.getSpeedParts(context, 3.34, false, true).first);
|
assertEquals("7.5", StringUtils.getSpeedParts(context, Speed.of(3.34), false, true).first);
|
||||||
|
|
||||||
assertEquals("min/km", StringUtils.getSpeedParts(context, 0, true, false).second);
|
assertEquals("min/km", StringUtils.getSpeedParts(context, Speed.zero(), true, false).second);
|
||||||
assertEquals("min/mi", StringUtils.getSpeedParts(context, 0, false, false).second);
|
assertEquals("min/mi", StringUtils.getSpeedParts(context, Speed.zero(), false, false).second);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFormatSpeed() {
|
public void testFormatSpeed() {
|
||||||
assertEquals("4:59 min/km", StringUtils.formatSpeed(context, 3.34, true, false));
|
assertEquals("4:59 min/km", StringUtils.formatSpeed(context, Speed.of(3.34), true, false));
|
||||||
assertEquals("8:02 min/mi", StringUtils.formatSpeed(context, 3.34, false, false));
|
assertEquals("8:02 min/mi", StringUtils.formatSpeed(context, Speed.of(3.34), false, false));
|
||||||
|
|
||||||
assertEquals("12.02 km/h", StringUtils.formatSpeed(context, 3.34, true, true));
|
assertEquals("12.02 km/h", StringUtils.formatSpeed(context, Speed.of(3.34), true, true));
|
||||||
assertEquals("7.47 mph", StringUtils.formatSpeed(context, 3.34, false, true));
|
assertEquals("7.47 mph", StringUtils.formatSpeed(context, Speed.of(3.34), false, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package de.dennisguse.opentracks.util;
|
package de.dennisguse.opentracks.util;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
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;
|
||||||
@@ -46,12 +47,14 @@ public class TrackNameUtilsTest {
|
|||||||
|
|
||||||
private static final Context CONTEXT = ApplicationProvider.getApplicationContext();
|
private static final Context CONTEXT = ApplicationProvider.getApplicationContext();
|
||||||
|
|
||||||
|
private static final SharedPreferences SHARED_PREFERENCES = PreferencesUtils.getSharedPreferences(CONTEXT);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests when the track_name_key is settings_recording_track_name_date_local_value.
|
* Tests when the track_name_key is settings_recording_track_name_date_local_value.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testTrackName_date_local() {
|
public void testTrackName_date_local() {
|
||||||
PreferencesUtils.setString(CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_date_local_value));
|
PreferencesUtils.setString(SHARED_PREFERENCES, CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_date_local_value));
|
||||||
assertEquals(StringUtils.formatDateTime(CONTEXT, START_TIME), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
|
assertEquals(StringUtils.formatDateTime(CONTEXT, START_TIME), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +63,7 @@ public class TrackNameUtilsTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testTrackName_date_iso_8601() {
|
public void testTrackName_date_iso_8601() {
|
||||||
PreferencesUtils.setString(CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_date_iso_8601_value));
|
PreferencesUtils.setString(SHARED_PREFERENCES, CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_date_iso_8601_value));
|
||||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(TrackNameUtils.ISO_8601_FORMAT, Locale.US);
|
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(TrackNameUtils.ISO_8601_FORMAT, Locale.US);
|
||||||
assertEquals(simpleDateFormat.format(START_TIME.toEpochMilli()), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
|
assertEquals(simpleDateFormat.format(START_TIME.toEpochMilli()), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
|
||||||
}
|
}
|
||||||
@@ -70,7 +73,7 @@ public class TrackNameUtilsTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testTrackName_number() {
|
public void testTrackName_number() {
|
||||||
PreferencesUtils.setString(CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_number_value));
|
PreferencesUtils.setString(SHARED_PREFERENCES, CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_number_value));
|
||||||
assertEquals("Track " + TRACK_ID.getId(), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
|
assertEquals("Track " + TRACK_ID.getId(), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+40
-38
@@ -13,6 +13,8 @@ import java.time.Instant;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
|
import de.dennisguse.opentracks.content.data.Distance;
|
||||||
|
import de.dennisguse.opentracks.content.data.Speed;
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||||
@@ -25,17 +27,17 @@ public class AggregatedStatisticsTest {
|
|||||||
|
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
|
||||||
private static Track createTrack(Context context, long totalDistance, long totalTime, String category) {
|
private static Track createTrack(Context context, Distance totalDistance, Duration totalTime, String category) {
|
||||||
TrackStatistics statistics = new TrackStatistics();
|
TrackStatistics statistics = new TrackStatistics();
|
||||||
statistics.setStartTime(Instant.ofEpochMilli(1000L)); // Resulting start time
|
statistics.setStartTime(Instant.ofEpochMilli(1000L)); // Resulting start time
|
||||||
statistics.setStopTime(Instant.ofEpochMilli(1000L + totalTime));
|
statistics.setStopTime(statistics.getStartTime().plus(totalTime));
|
||||||
statistics.setTotalTime(Duration.ofMillis(totalTime));
|
statistics.setTotalTime(totalTime);
|
||||||
statistics.setMovingTime(Duration.ofMillis(totalTime));
|
statistics.setMovingTime(totalTime);
|
||||||
statistics.setTotalDistance(totalDistance);
|
statistics.setTotalDistance(totalDistance);
|
||||||
statistics.setTotalElevationGain(50.0f);
|
statistics.setTotalAltitudeGain(50.0f);
|
||||||
statistics.setMaxSpeed(50.0); // Resulting max speed
|
statistics.setMaxSpeed(Speed.of(50.0)); // Resulting max speed
|
||||||
statistics.setMaxElevation(1250.0);
|
statistics.setMaxAltitude(1250.0);
|
||||||
statistics.setMinElevation(1200.0); // Resulting min elevation
|
statistics.setMinAltitude(1200.0); // Resulting min altitude
|
||||||
|
|
||||||
Track track = new Track();
|
Track track = new Track();
|
||||||
track.setIcon(TrackIconUtils.getIconValue(context, category));
|
track.setIcon(TrackIconUtils.getIconValue(context, category));
|
||||||
@@ -48,8 +50,8 @@ public class AggregatedStatisticsTest {
|
|||||||
public void testAggregate() {
|
public void testAggregate() {
|
||||||
// given
|
// given
|
||||||
// 10km in 40 minutes.
|
// 10km in 40 minutes.
|
||||||
long totalDistance = 10000;
|
Distance totalDistance = Distance.of(10000);
|
||||||
long totalTime = 2400000;
|
Duration totalTime = Duration.ofMillis(2400000);
|
||||||
String biking = context.getString(R.string.activity_type_biking);
|
String biking = context.getString(R.string.activity_type_biking);
|
||||||
Track track = createTrack(context, totalDistance, totalTime, biking);
|
Track track = createTrack(context, totalDistance, totalTime, biking);
|
||||||
|
|
||||||
@@ -62,16 +64,16 @@ public class AggregatedStatisticsTest {
|
|||||||
assertEquals(1, aggregatedStatistics.get(biking).getCountTracks());
|
assertEquals(1, aggregatedStatistics.get(biking).getCountTracks());
|
||||||
|
|
||||||
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
|
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
|
||||||
assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
|
assertEquals(totalDistance, statistics2.getTotalDistance());
|
||||||
assertEquals(Duration.ofMillis(totalTime), statistics2.getMovingTime());
|
assertEquals(totalTime, statistics2.getMovingTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAggregate_mountainBiking() {
|
public void testAggregate_mountainBiking() {
|
||||||
// given
|
// given
|
||||||
// 10km in 40 minutes.
|
// 10km in 40 minutes.
|
||||||
long totalDistance = 10000;
|
Distance totalDistance = Distance.of(10000);
|
||||||
long totalTime = 2400000;
|
Duration totalTime = Duration.ofMillis(2400000);
|
||||||
String mountainBiking = context.getString(R.string.activity_type_mountain_biking);
|
String mountainBiking = context.getString(R.string.activity_type_mountain_biking);
|
||||||
Track track = createTrack(context, totalDistance, totalTime, mountainBiking);
|
Track track = createTrack(context, totalDistance, totalTime, mountainBiking);
|
||||||
|
|
||||||
@@ -86,8 +88,8 @@ public class AggregatedStatisticsTest {
|
|||||||
public void testAggregate_trailRunning() {
|
public void testAggregate_trailRunning() {
|
||||||
// given
|
// given
|
||||||
// 10km in 40 minutes.
|
// 10km in 40 minutes.
|
||||||
long totalDistance = 10000;
|
Distance totalDistance = Distance.of(10000);
|
||||||
long totalTime = 2400000;
|
Duration totalTime = Duration.ofMillis(2400000);
|
||||||
String trailRunning = context.getString(R.string.activity_type_trail_running);
|
String trailRunning = context.getString(R.string.activity_type_trail_running);
|
||||||
Track track = createTrack(context, totalDistance, totalTime, trailRunning);
|
Track track = createTrack(context, totalDistance, totalTime, trailRunning);
|
||||||
|
|
||||||
@@ -102,8 +104,8 @@ public class AggregatedStatisticsTest {
|
|||||||
public void testAggregate_twoBikingTracks() {
|
public void testAggregate_twoBikingTracks() {
|
||||||
// given
|
// given
|
||||||
// 10km in 40 minutes.
|
// 10km in 40 minutes.
|
||||||
long totalDistance = 10000;
|
Distance totalDistance = Distance.of(10000);
|
||||||
long totalTime = 2400000;
|
Duration totalTime = Duration.ofMillis(2400000);
|
||||||
String biking = context.getString(R.string.activity_type_biking);
|
String biking = context.getString(R.string.activity_type_biking);
|
||||||
List<Track> tracks = List.of(createTrack(context, totalDistance, totalTime, biking), createTrack(context, totalDistance, totalTime, biking));
|
List<Track> tracks = List.of(createTrack(context, totalDistance, totalTime, biking), createTrack(context, totalDistance, totalTime, biking));
|
||||||
|
|
||||||
@@ -116,20 +118,20 @@ public class AggregatedStatisticsTest {
|
|||||||
assertEquals(2, aggregatedStatistics.get(biking).getCountTracks());
|
assertEquals(2, aggregatedStatistics.get(biking).getCountTracks());
|
||||||
|
|
||||||
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
|
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
|
||||||
assertEquals(totalDistance * 2, statistics2.getTotalDistance(), 0);
|
assertEquals(totalDistance.multipliedBy(2), statistics2.getTotalDistance());
|
||||||
assertEquals(Duration.ofMillis(totalTime * 2), statistics2.getMovingTime());
|
assertEquals(totalTime.multipliedBy(2), statistics2.getMovingTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAggregate_threeDifferentTracks() {
|
public void testAggregate_threeDifferentTracks() {
|
||||||
// given
|
// given
|
||||||
// 10km in 40 minutes.
|
// 10km in 40 minutes.
|
||||||
long totalDistance = 10000;
|
Distance totalDistance = Distance.of(10000);
|
||||||
|
|
||||||
String biking = context.getString(R.string.activity_type_biking);
|
String biking = context.getString(R.string.activity_type_biking);
|
||||||
String running = context.getString(R.string.activity_type_running);
|
String running = context.getString(R.string.activity_type_running);
|
||||||
String walking = context.getString(R.string.activity_type_walking);
|
String walking = context.getString(R.string.activity_type_walking);
|
||||||
long totalTime = 2400000;
|
Duration totalTime = Duration.ofMillis(2400000);
|
||||||
List<Track> tracks = List.of(
|
List<Track> tracks = List.of(
|
||||||
createTrack(context, totalDistance, totalTime, biking),
|
createTrack(context, totalDistance, totalTime, biking),
|
||||||
createTrack(context, totalDistance, totalTime, running),
|
createTrack(context, totalDistance, totalTime, running),
|
||||||
@@ -150,20 +152,20 @@ public class AggregatedStatisticsTest {
|
|||||||
|
|
||||||
{
|
{
|
||||||
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
|
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
|
||||||
assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
|
assertEquals(totalDistance, statistics2.getTotalDistance());
|
||||||
assertEquals(Duration.ofMillis(totalTime), statistics2.getMovingTime());
|
assertEquals(totalTime, statistics2.getMovingTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
TrackStatistics statistics2 = aggregatedStatistics.get(running).getTrackStatistics();
|
TrackStatistics statistics2 = aggregatedStatistics.get(running).getTrackStatistics();
|
||||||
assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
|
assertEquals(totalDistance, statistics2.getTotalDistance());
|
||||||
assertEquals(Duration.ofMillis(totalTime), statistics2.getMovingTime());
|
assertEquals(totalTime, statistics2.getMovingTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
TrackStatistics statistics2 = aggregatedStatistics.get(walking).getTrackStatistics();
|
TrackStatistics statistics2 = aggregatedStatistics.get(walking).getTrackStatistics();
|
||||||
assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
|
assertEquals(totalDistance, statistics2.getTotalDistance());
|
||||||
assertEquals(Duration.ofMillis(totalTime), statistics2.getMovingTime());
|
assertEquals(totalTime, statistics2.getMovingTime());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,8 +173,8 @@ public class AggregatedStatisticsTest {
|
|||||||
public void testAggregate_severalTracksWithSeveralActivities() {
|
public void testAggregate_severalTracksWithSeveralActivities() {
|
||||||
// given
|
// given
|
||||||
// 10km in 40 minutes.
|
// 10km in 40 minutes.
|
||||||
long totalDistance = 10000;
|
Distance totalDistance = Distance.of(10000);
|
||||||
long totalTime = 2400000;
|
Duration totalTime = Duration.ofMillis(2400000);
|
||||||
String biking = context.getString(R.string.activity_type_biking);
|
String biking = context.getString(R.string.activity_type_biking);
|
||||||
String running = context.getString(R.string.activity_type_running);
|
String running = context.getString(R.string.activity_type_running);
|
||||||
String walking = context.getString(R.string.activity_type_walking);
|
String walking = context.getString(R.string.activity_type_walking);
|
||||||
@@ -213,29 +215,29 @@ public class AggregatedStatisticsTest {
|
|||||||
// Biking.
|
// Biking.
|
||||||
{
|
{
|
||||||
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
|
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
|
||||||
assertEquals(totalDistance * 5, statistics2.getTotalDistance(), 0);
|
assertEquals(totalDistance.multipliedBy(5), statistics2.getTotalDistance());
|
||||||
assertEquals(Duration.ofMillis(totalTime * 5), statistics2.getMovingTime());
|
assertEquals(totalTime.multipliedBy(5), statistics2.getMovingTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Running.
|
// Running.
|
||||||
{
|
{
|
||||||
TrackStatistics statistics2 = aggregatedStatistics.get(running).getTrackStatistics();
|
TrackStatistics statistics2 = aggregatedStatistics.get(running).getTrackStatistics();
|
||||||
assertEquals(totalDistance * 2, statistics2.getTotalDistance(), 0);
|
assertEquals(totalDistance.multipliedBy(2), statistics2.getTotalDistance());
|
||||||
assertEquals(Duration.ofMillis(totalTime * 2), statistics2.getMovingTime());
|
assertEquals(totalTime.multipliedBy(2), statistics2.getMovingTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Walking.
|
// Walking.
|
||||||
{
|
{
|
||||||
TrackStatistics statistics2 = aggregatedStatistics.get(walking).getTrackStatistics();
|
TrackStatistics statistics2 = aggregatedStatistics.get(walking).getTrackStatistics();
|
||||||
assertEquals(totalDistance * 2, statistics2.getTotalDistance(), 0);
|
assertEquals(totalDistance.multipliedBy(2), statistics2.getTotalDistance());
|
||||||
assertEquals(Duration.ofMillis(totalTime * 2), statistics2.getMovingTime());
|
assertEquals(totalTime.multipliedBy(2), statistics2.getMovingTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Driving.
|
// Driving.
|
||||||
{
|
{
|
||||||
TrackStatistics statistics2 = aggregatedStatistics.get(driving).getTrackStatistics();
|
TrackStatistics statistics2 = aggregatedStatistics.get(driving).getTrackStatistics();
|
||||||
assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
|
assertEquals(totalDistance, statistics2.getTotalDistance());
|
||||||
assertEquals(Duration.ofMillis(totalTime), statistics2.getMovingTime());
|
assertEquals(totalTime, statistics2.getMovingTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check order
|
// Check order
|
||||||
|
|||||||
+95
-93
@@ -6,6 +6,7 @@ import org.junit.runners.JUnit4;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.data.Distance;
|
||||||
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
@@ -18,121 +19,122 @@ import static org.junit.Assert.assertEquals;
|
|||||||
@RunWith(JUnit4.class)
|
@RunWith(JUnit4.class)
|
||||||
public class IntervalStatisticsTest {
|
public class IntervalStatisticsTest {
|
||||||
|
|
||||||
private static final String TAG = IntervalStatisticsTest.class.getSimpleName();
|
private static final String TAG = IntervalStatisticsTest.class.getSimpleName();
|
||||||
|
|
||||||
private List<TrackPoint> buildTrackPoints(int numberOfTrackPoints) {
|
private List<TrackPoint> buildTrackPoints(int numberOfTrackPoints) {
|
||||||
return TestDataUtil.createTrack(new Track.Id(System.currentTimeMillis()), numberOfTrackPoints).second;
|
return TestDataUtil.createTrack(new Track.Id(System.currentTimeMillis()), numberOfTrackPoints).second;
|
||||||
}
|
}
|
||||||
|
|
||||||
private TrackStatistics buildTrackStatistics(List<TrackPoint> trackPoints) {
|
private TrackStatistics buildTrackStatistics(List<TrackPoint> trackPoints) {
|
||||||
TrackStatisticsUpdater trackStatisticsUpdater = new TrackStatisticsUpdater();
|
TrackStatisticsUpdater trackStatisticsUpdater = new TrackStatisticsUpdater();
|
||||||
for (TrackPoint tp : trackPoints) {
|
for (TrackPoint tp : trackPoints) {
|
||||||
trackStatisticsUpdater.addTrackPoint(tp, 0);
|
trackStatisticsUpdater.addTrackPoint(tp, Distance.of(0));
|
||||||
}
|
}
|
||||||
return trackStatisticsUpdater.getTrackStatistics();
|
return trackStatisticsUpdater.getTrackStatistics();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests that build method compute the distance correctly comparing the result with TrackStatisticsUpdater result.
|
* Tests that build method compute the distance correctly comparing the result with TrackStatisticsUpdater result.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testBuild_1() {
|
public void testBuild_1() {
|
||||||
// With 50 points and interval distance of 1000m.
|
// With 50 points and interval distance of 1000m.
|
||||||
|
|
||||||
// given
|
// given
|
||||||
List<TrackPoint> trackPoints = buildTrackPoints(50);
|
List<TrackPoint> trackPoints = buildTrackPoints(50);
|
||||||
TrackStatistics trackStatistics = buildTrackStatistics(trackPoints);
|
TrackStatistics trackStatistics = buildTrackStatistics(trackPoints);
|
||||||
float distanceInterval = 1000f;
|
float distanceInterval = 1000f;
|
||||||
|
|
||||||
// when and then
|
// when and then
|
||||||
whenAndThen(trackPoints, trackStatistics, distanceInterval);
|
whenAndThen(trackPoints, trackStatistics, distanceInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests that build method compute the distance correctly comparing the result with TrackStatisticsUpdater result.
|
* Tests that build method compute the distance correctly comparing the result with TrackStatisticsUpdater result.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testBuild_2() {
|
public void testBuild_2() {
|
||||||
// With 200 points and interval distance of 1000m.
|
// With 200 points and interval distance of 1000m.
|
||||||
|
|
||||||
// given
|
// given
|
||||||
List<TrackPoint> trackPoints = buildTrackPoints(200);
|
List<TrackPoint> trackPoints = buildTrackPoints(200);
|
||||||
TrackStatistics trackStatistics = buildTrackStatistics(trackPoints);
|
TrackStatistics trackStatistics = buildTrackStatistics(trackPoints);
|
||||||
float distanceInterval = 1000f;
|
float distanceInterval = 1000f;
|
||||||
|
|
||||||
// when and then
|
// when and then
|
||||||
whenAndThen(trackPoints, trackStatistics, distanceInterval);
|
whenAndThen(trackPoints, trackStatistics, distanceInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests that build method compute the distance correctly comparing the result with TrackStatisticsUpdater result.
|
* Tests that build method compute the distance correctly comparing the result with TrackStatisticsUpdater result.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testBuild_3() {
|
public void testBuild_3() {
|
||||||
// With 200 points and interval distance of 3000m.
|
// With 200 points and interval distance of 3000m.
|
||||||
|
|
||||||
// given
|
// given
|
||||||
List<TrackPoint> trackPoints = buildTrackPoints(200);
|
List<TrackPoint> trackPoints = buildTrackPoints(200);
|
||||||
TrackStatistics trackStatistics = buildTrackStatistics(trackPoints);
|
TrackStatistics trackStatistics = buildTrackStatistics(trackPoints);
|
||||||
float distanceInterval = 3000f;
|
float distanceInterval = 3000f;
|
||||||
|
|
||||||
// when and then
|
// when and then
|
||||||
whenAndThen(trackPoints, trackStatistics, distanceInterval);
|
whenAndThen(trackPoints, trackStatistics, distanceInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests that build method compute the distance correctly comparing the result with TrackStatisticsUpdater result.
|
* Tests that build method compute the distance correctly comparing the result with TrackStatisticsUpdater result.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testBuild_4() {
|
public void testBuild_4() {
|
||||||
// With 1000 points and interval distance of 3000m.
|
// With 1000 points and interval distance of 3000m.
|
||||||
|
|
||||||
// given
|
// given
|
||||||
List<TrackPoint> trackPoints = buildTrackPoints(1000);
|
List<TrackPoint> trackPoints = buildTrackPoints(1000);
|
||||||
TrackStatistics trackStatistics = buildTrackStatistics(trackPoints);
|
TrackStatistics trackStatistics = buildTrackStatistics(trackPoints);
|
||||||
float distanceInterval = 3000f;
|
float distanceInterval = 3000f;
|
||||||
|
|
||||||
// when and then
|
// when and then
|
||||||
whenAndThen(trackPoints, trackStatistics, distanceInterval);
|
whenAndThen(trackPoints, trackStatistics, distanceInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests that build method compute the distance correctly comparing the result with TrackStatisticsUpdater result.
|
* Tests that build method compute the distance correctly comparing the result with TrackStatisticsUpdater result.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testBuild_5() {
|
public void testBuild_5() {
|
||||||
// With 10000 points and interval distance of 1000m.
|
// With 10000 points and interval distance of 1000m.
|
||||||
|
|
||||||
// given
|
// given
|
||||||
List<TrackPoint> trackPoints = buildTrackPoints(10000);
|
List<TrackPoint> trackPoints = buildTrackPoints(10000);
|
||||||
TrackStatistics trackStatistics = buildTrackStatistics(trackPoints);
|
TrackStatistics trackStatistics = buildTrackStatistics(trackPoints);
|
||||||
float distanceInterval = 1000f;
|
float distanceInterval = 1000f;
|
||||||
|
|
||||||
// when and then
|
// when and then
|
||||||
whenAndThen(trackPoints, trackStatistics, distanceInterval);
|
whenAndThen(trackPoints, trackStatistics, distanceInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void whenAndThen(List<TrackPoint> trackPoints, TrackStatistics trackStatistics, float distanceInterval) {
|
private void whenAndThen(List<TrackPoint> trackPoints, TrackStatistics trackStatistics, float distanceInterval) {
|
||||||
IntervalStatistics intervalStatistics = new IntervalStatistics(trackPoints, distanceInterval);
|
IntervalStatistics intervalStatistics = new IntervalStatistics(trackPoints, Distance.of(distanceInterval));
|
||||||
List<IntervalStatistics.Interval> intervalList = intervalStatistics.getIntervalList();
|
List<IntervalStatistics.Interval> intervalList = intervalStatistics.getIntervalList();
|
||||||
double totalDistance = 0d;
|
Distance totalDistance = Distance.of(0);
|
||||||
float totalTime = 0L;
|
float totalTime = 0L;
|
||||||
float totalGain = 0f;
|
float totalGain = 0f;
|
||||||
for (IntervalStatistics.Interval i : intervalList) {
|
for (IntervalStatistics.Interval i : intervalList) {
|
||||||
totalDistance += i.getDistance_m();
|
totalDistance = totalDistance.plus(i.getDistance());
|
||||||
totalTime += i.getDistance_m() / i.getSpeed_ms();
|
totalTime += i.getDistance().toM() / i.getSpeed().toMPS();
|
||||||
totalGain += i.getGain_m();
|
totalGain += i.getGain_m();
|
||||||
}
|
}
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(trackStatistics.getTotalDistance(), totalDistance, 0.01);
|
assertEquals(trackStatistics.getTotalDistance().toM(), totalDistance.toM(), 0.01);
|
||||||
assertEquals(trackStatistics.getTotalTime().toMillis(), totalTime * UnitConversions.S_TO_MS, 1);
|
assertEquals(trackStatistics.getTotalTime().toMillis(), totalTime * UnitConversions.S_TO_MS, 1);
|
||||||
assertEquals(intervalList.size(), (int) Math.ceil(trackStatistics.getTotalDistance() / distanceInterval));
|
assertEquals(intervalList.size(), (int) Math.ceil(trackStatistics.getTotalDistance().toM() / distanceInterval));
|
||||||
assertEquals(totalGain, trackPoints.size() * TestDataUtil.ELEVATION_GAIN, 0.1);
|
assertEquals(totalGain, trackPoints.size() * TestDataUtil.ALTITUDE_GAIN, 0.1);
|
||||||
for (int i = 0; i < intervalList.size() - 1; i++) {
|
|
||||||
assertEquals(intervalList.get(i).getDistance_m(), distanceInterval, 0.001);
|
for (int i = 0; i < intervalList.size() - 1; i++) {
|
||||||
totalDistance -= intervalList.get(i).getDistance_m();
|
assertEquals(intervalList.get(i).getDistance().toM(), distanceInterval, 0.001);
|
||||||
}
|
totalDistance = totalDistance.minus(intervalList.get(i).getDistance());
|
||||||
assertEquals(intervalList.get(intervalList.size() - 1).getDistance_m(), totalDistance, 0.01);
|
}
|
||||||
}
|
assertEquals(intervalList.get(intervalList.size() - 1).getDistance().toM(), totalDistance.toM(), 0.01);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -73,11 +73,13 @@ limitations under the License.
|
|||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND" />
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<data android:mimeType="application/gpx+xml" />
|
<data android:mimeType="application/gpx+xml" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND" />
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<data android:mimeType="application/gpx" />
|
<data android:mimeType="application/gpx" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
@@ -135,6 +137,7 @@ limitations under the License.
|
|||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND" />
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<data android:mimeType="application/vnd.google-earth.kmz" />
|
<data android:mimeType="application/vnd.google-earth.kmz" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
@@ -192,6 +195,8 @@ limitations under the License.
|
|||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND" />
|
<action android:name="android.intent.action.SEND" />
|
||||||
|
<action android:name="android.intent.action.SEND_MULTIPLE" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<data android:mimeType="application/vnd.google-earth.kml+xml" />
|
<data android:mimeType="application/vnd.google-earth.kml+xml" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
@@ -346,4 +351,10 @@ limitations under the License.
|
|||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:permission="android.permission.BIND_JOB_SERVICE" />
|
android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
<queries>
|
||||||
|
<intent>
|
||||||
|
<action android:name="android.intent.action.TTS_SERVICE" />
|
||||||
|
</intent>
|
||||||
|
</queries>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package de.dennisguse.opentracks;
|
package de.dennisguse.opentracks;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@@ -32,9 +31,4 @@ public class AboutActivity extends AbstractActivity {
|
|||||||
viewBinding = AboutBinding.inflate(getLayoutInflater());
|
viewBinding = AboutBinding.inflate(getLayoutInflater());
|
||||||
return viewBinding.getRoot();
|
return viewBinding.getRoot();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,11 +26,10 @@ import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
|
|||||||
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment.ConfirmDeleteCaller;
|
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment.ConfirmDeleteCaller;
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||||
import de.dennisguse.opentracks.util.IntentUtils;
|
import de.dennisguse.opentracks.util.IntentUtils;
|
||||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An abstract class for the following common tasks across
|
* An abstract class for the following common tasks across
|
||||||
* {@link TrackListActivity}, {@link TrackRecordedActivity}, and {@link TrackRecordedActivity}:
|
* {@link TrackListActivity} and {@link TrackRecordedActivity}:
|
||||||
* <p>
|
* <p>
|
||||||
* - share track <br>
|
* - share track <br>
|
||||||
* - delete tracks <br>
|
* - delete tracks <br>
|
||||||
@@ -44,6 +43,8 @@ public abstract class AbstractListActivity extends AbstractActivity implements C
|
|||||||
protected static final int GPS_REQUEST_CODE = 6;
|
protected static final int GPS_REQUEST_CODE = 6;
|
||||||
private static final int DELETE_REQUEST_CODE = 3;
|
private static final int DELETE_REQUEST_CODE = 3;
|
||||||
|
|
||||||
|
protected Track.Id recordingTrackId;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
if (requestCode == DELETE_REQUEST_CODE) {
|
if (requestCode == DELETE_REQUEST_CODE) {
|
||||||
@@ -66,7 +67,6 @@ public abstract class AbstractListActivity extends AbstractActivity implements C
|
|||||||
public void onConfirmDeleteDone(Track.Id... trackIds) {
|
public void onConfirmDeleteDone(Track.Id... trackIds) {
|
||||||
boolean stopRecording = false;
|
boolean stopRecording = false;
|
||||||
|
|
||||||
Track.Id recordingTrackId = PreferencesUtils.getRecordingTrackId(this);
|
|
||||||
for (Track.Id trackId : trackIds) {
|
for (Track.Id trackId : trackIds) {
|
||||||
if (trackId.equals(recordingTrackId)) {
|
if (trackId.equals(recordingTrackId)) {
|
||||||
stopRecording = true;
|
stopRecording = true;
|
||||||
@@ -75,7 +75,7 @@ public abstract class AbstractListActivity extends AbstractActivity implements C
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (stopRecording) {
|
if (stopRecording) {
|
||||||
getTrackRecordingServiceConnection().stopRecording(this, false);
|
getTrackRecordingServiceConnection().stopRecording(this);
|
||||||
}
|
}
|
||||||
Intent intent = IntentUtils.newIntent(this, TrackDeleteActivity.class);
|
Intent intent = IntentUtils.newIntent(this, TrackDeleteActivity.class);
|
||||||
intent.putParcelableArrayListExtra(TrackDeleteActivity.EXTRA_TRACK_IDS, new ArrayList<>(Arrays.asList(trackIds)));
|
intent.putParcelableArrayListExtra(TrackDeleteActivity.EXTRA_TRACK_IDS, new ArrayList<>(Arrays.asList(trackIds)));
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ package de.dennisguse.opentracks;
|
|||||||
|
|
||||||
import android.app.SearchManager;
|
import android.app.SearchManager;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
@@ -42,16 +40,18 @@ import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
|||||||
import de.dennisguse.opentracks.databinding.MarkerListBinding;
|
import de.dennisguse.opentracks.databinding.MarkerListBinding;
|
||||||
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment;
|
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment;
|
||||||
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment.DeleteMarkerCaller;
|
import de.dennisguse.opentracks.fragments.DeleteMarkerDialogFragment.DeleteMarkerCaller;
|
||||||
|
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||||
|
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||||
|
import de.dennisguse.opentracks.services.TrackRecordingServiceStatus;
|
||||||
import de.dennisguse.opentracks.util.ActivityUtils;
|
import de.dennisguse.opentracks.util.ActivityUtils;
|
||||||
import de.dennisguse.opentracks.util.IntentUtils;
|
import de.dennisguse.opentracks.util.IntentUtils;
|
||||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activity to show a list of markers in a track.
|
* Activity to show a list of markers in a track.
|
||||||
*
|
*
|
||||||
* @author Leif Hendrik Wilden
|
* @author Leif Hendrik Wilden
|
||||||
*/
|
*/
|
||||||
public class MarkerListActivity extends AbstractActivity implements DeleteMarkerCaller {
|
public class MarkerListActivity extends AbstractActivity implements DeleteMarkerCaller, TrackRecordingServiceStatus.Listener {
|
||||||
|
|
||||||
public static final String EXTRA_TRACK_ID = "track_id";
|
public static final String EXTRA_TRACK_ID = "track_id";
|
||||||
|
|
||||||
@@ -59,25 +59,8 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
|||||||
|
|
||||||
private ContentProviderUtils contentProviderUtils;
|
private ContentProviderUtils contentProviderUtils;
|
||||||
|
|
||||||
private SharedPreferences sharedPreferences;
|
|
||||||
|
|
||||||
private Track.Id recordingTrackId;
|
private Track.Id recordingTrackId;
|
||||||
private boolean recordingTrackPaused;
|
private boolean recordingTrackPaused;
|
||||||
|
|
||||||
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
|
||||||
@Override
|
|
||||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
|
||||||
if (PreferencesUtils.isKey(MarkerListActivity.this, R.string.recording_track_id_key, key)) {
|
|
||||||
recordingTrackId = PreferencesUtils.getRecordingTrackId(MarkerListActivity.this);
|
|
||||||
}
|
|
||||||
if (PreferencesUtils.isKey(MarkerListActivity.this, R.string.recording_track_paused_key, key)) {
|
|
||||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(MarkerListActivity.this);
|
|
||||||
}
|
|
||||||
if (key != null) {
|
|
||||||
runOnUiThread(MarkerListActivity.this::invalidateOptionsMenu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
private Track track;
|
private Track track;
|
||||||
private MarkerResourceCursorAdapter resourceCursorAdapter;
|
private MarkerResourceCursorAdapter resourceCursorAdapter;
|
||||||
|
|
||||||
@@ -85,6 +68,18 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
|||||||
|
|
||||||
private final MarkerLoaderCallback loaderCallbacks = new MarkerLoaderCallback();
|
private final MarkerLoaderCallback loaderCallbacks = new MarkerLoaderCallback();
|
||||||
|
|
||||||
|
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||||
|
|
||||||
|
private final Runnable bindCallback = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
TrackRecordingService service = trackRecordingServiceConnection.getServiceIfBound();
|
||||||
|
if (service != null) {
|
||||||
|
service.addListener(MarkerListActivity.this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Callback when an item is selected in the contextual action mode
|
// Callback when an item is selected in the contextual action mode
|
||||||
private final ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
|
private final ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
|
||||||
@Override
|
@Override
|
||||||
@@ -112,10 +107,8 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
|
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
|
||||||
Track.Id trackId = getIntent().getParcelableExtra(EXTRA_TRACK_ID);
|
Track.Id trackId = getIntent().getParcelableExtra(EXTRA_TRACK_ID);
|
||||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPausedDefault(this);
|
|
||||||
|
|
||||||
contentProviderUtils = new ContentProviderUtils(this);
|
contentProviderUtils = new ContentProviderUtils(this);
|
||||||
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
|
||||||
|
|
||||||
track = trackId != null ? contentProviderUtils.getTrack(trackId) : null;
|
track = trackId != null ? contentProviderUtils.getTrack(trackId) : null;
|
||||||
|
|
||||||
@@ -132,18 +125,20 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
|||||||
viewBinding.markerList.setOnScrollListener(scrollVisibleViews);
|
viewBinding.markerList.setOnScrollListener(scrollVisibleViews);
|
||||||
viewBinding.markerList.setAdapter(resourceCursorAdapter);
|
viewBinding.markerList.setAdapter(resourceCursorAdapter);
|
||||||
ActivityUtils.configureListViewContextualMenu(viewBinding.markerList, contextualActionModeCallback);
|
ActivityUtils.configureListViewContextualMenu(viewBinding.markerList, contextualActionModeCallback);
|
||||||
|
|
||||||
|
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStart() {
|
protected void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
trackRecordingServiceConnection.startConnection(this);
|
||||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
trackRecordingServiceConnection.bind(this);
|
||||||
this.invalidateOptionsMenu();
|
this.invalidateOptionsMenu();
|
||||||
loadData(getIntent());
|
loadData(getIntent());
|
||||||
}
|
}
|
||||||
@@ -151,7 +146,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
|||||||
@Override
|
@Override
|
||||||
protected void onStop() {
|
protected void onStop() {
|
||||||
super.onStop();
|
super.onStop();
|
||||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
trackRecordingServiceConnection.unbind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -161,7 +156,6 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
|||||||
viewBinding = null;
|
viewBinding = null;
|
||||||
resourceCursorAdapter = null;
|
resourceCursorAdapter = null;
|
||||||
contentProviderUtils = null;
|
contentProviderUtils = null;
|
||||||
sharedPreferences = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -350,4 +344,14 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
|||||||
resourceCursorAdapter.swapCursor(null);
|
resourceCursorAdapter.swapCursor(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTrackRecordingPaused(boolean isPaused) {
|
||||||
|
recordingTrackPaused = isPaused;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTrackRecordingId(Track.Id trackId) {
|
||||||
|
recordingTrackId = trackId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package de.dennisguse.opentracks;
|
package de.dennisguse.opentracks;
|
||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.os.StrictMode;
|
import android.os.StrictMode;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
@@ -23,35 +24,22 @@ public class Startup extends Application {
|
|||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
|
||||||
//Include version information into stack traces.
|
// Include version information into stack traces.
|
||||||
Log.i(TAG, BuildConfig.APPLICATION_ID + "; BuildType: " + BuildConfig.BUILD_TYPE + "; VersionName: " + BuildConfig.VERSION_NAME + "/" + BuildConfig.VERSION_NAME_FULL + " VersionCode: " + BuildConfig.VERSION_CODE);
|
Log.i(TAG, BuildConfig.APPLICATION_ID + "; BuildType: " + BuildConfig.BUILD_TYPE + "; VersionName: " + BuildConfig.VERSION_NAME + "/" + BuildConfig.VERSION_NAME_FULL + " VersionCode: " + BuildConfig.VERSION_CODE);
|
||||||
|
|
||||||
//Set default values of preferences on first start.
|
// Set default values of preferences on first start.
|
||||||
PreferencesUtils.resetPreferences(this, false);
|
SharedPreferences sharedPreferences = PreferencesUtils.resetPreferences(this, false);
|
||||||
if (PreferencesUtils.getString(this, R.string.stats_units_key, "").equals("")) {
|
if (PreferencesUtils.getString(sharedPreferences, this, R.string.stats_units_key, "").equals("")) {
|
||||||
String statsUnits = getString(Locale.US.equals(Locale.getDefault()) ? R.string.stats_units_imperial : R.string.stats_units_metric);
|
String statsUnits = getString(Locale.US.equals(Locale.getDefault()) ? R.string.stats_units_imperial : R.string.stats_units_metric);
|
||||||
PreferencesUtils.setString(this, R.string.stats_units_key, statsUnits);
|
PreferencesUtils.setString(sharedPreferences, this, R.string.stats_units_key, statsUnits);
|
||||||
}
|
}
|
||||||
|
|
||||||
ActivityUtils.applyNightMode(this);
|
ActivityUtils.applyNightMode(sharedPreferences, this);
|
||||||
|
|
||||||
//TODO Workaround to reset recordingTrackId on app startup as the TrackRecordingService (likely) crashed.
|
// In debug builds: show thread and VM warnings.
|
||||||
if (PreferencesUtils.isRecording(this)) {
|
|
||||||
Log.e(TAG, "Reset recordingTrackId; likely the TrackRecordingService crashed.");
|
|
||||||
PreferencesUtils.setLong(this, R.string.recording_track_id_key, PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
//In debug builds: show thread and VM warnings.
|
|
||||||
if (BuildConfig.DEBUG) {
|
if (BuildConfig.DEBUG) {
|
||||||
Log.d(TAG, "Enabling strict mode");
|
Log.d(TAG, "Enabling strict mode");
|
||||||
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
|
StrictMode.enableDefaults();
|
||||||
.detectAll()
|
|
||||||
.penaltyLog()
|
|
||||||
.build());
|
|
||||||
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
|
|
||||||
.detectAll()
|
|
||||||
.penaltyLog()
|
|
||||||
.build());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,11 +27,10 @@ import android.view.accessibility.AccessibilityEvent;
|
|||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.databinding.TrackControllerBinding;
|
import de.dennisguse.opentracks.databinding.TrackControllerBinding;
|
||||||
|
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingServiceInterface;
|
|
||||||
import de.dennisguse.opentracks.util.ActivityUtils;
|
import de.dennisguse.opentracks.util.ActivityUtils;
|
||||||
import de.dennisguse.opentracks.util.StringUtils;
|
import de.dennisguse.opentracks.util.StringUtils;
|
||||||
import de.dennisguse.opentracks.util.UnitConversions;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Track controller for record, pause, resume, and stop.
|
* Track controller for record, pause, resume, and stop.
|
||||||
@@ -42,6 +41,8 @@ public class TrackController implements View.OnTouchListener {
|
|||||||
|
|
||||||
private static final String TAG = TrackController.class.getSimpleName();
|
private static final String TAG = TrackController.class.getSimpleName();
|
||||||
|
|
||||||
|
private static final Duration UI_UPDATE_INTERVAL = Duration.ofSeconds(1);
|
||||||
|
|
||||||
private final Activity activity;
|
private final Activity activity;
|
||||||
private final TrackRecordingServiceConnection trackRecordingServiceConnection;
|
private final TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||||
private final Handler handlerUpdateTotalTime = new Handler();
|
private final Handler handlerUpdateTotalTime = new Handler();
|
||||||
@@ -66,7 +67,7 @@ public class TrackController implements View.OnTouchListener {
|
|||||||
if (isResumed && isRecording && !isRecordingPaused) {
|
if (isResumed && isRecording && !isRecordingPaused) {
|
||||||
updateTotalTime();
|
updateTotalTime();
|
||||||
setTotalTime();
|
setTotalTime();
|
||||||
handlerUpdateTotalTime.postDelayed(this, UnitConversions.ONE_SECOND_MS);
|
handlerUpdateTotalTime.postDelayed(this, UI_UPDATE_INTERVAL.toMillis());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -129,7 +130,7 @@ public class TrackController implements View.OnTouchListener {
|
|||||||
buttonDelay = () -> {
|
buttonDelay = () -> {
|
||||||
view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
|
view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
|
||||||
view.performClick();
|
view.performClick();
|
||||||
callback.recordStart(); //TODO Should be recordPause();
|
callback.recordPause();
|
||||||
|
|
||||||
transition.resetTransition();
|
transition.resetTransition();
|
||||||
|
|
||||||
@@ -180,7 +181,7 @@ public class TrackController implements View.OnTouchListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isRecording && !isRecordingPaused) {
|
if (isRecording && !isRecordingPaused) {
|
||||||
handlerUpdateTotalTime.postDelayed(updateTotalTimeRunnable, UnitConversions.ONE_SECOND_MS);
|
handlerUpdateTotalTime.postDelayed(updateTotalTimeRunnable, UI_UPDATE_INTERVAL.toMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
viewBinding.trackControllerRecord.setImageResource(isRecording && !isRecordingPaused ? R.drawable.ic_button_pause : R.drawable.button_record);
|
viewBinding.trackControllerRecord.setImageResource(isRecording && !isRecordingPaused ? R.drawable.ic_button_pause : R.drawable.button_record);
|
||||||
@@ -221,7 +222,7 @@ public class TrackController implements View.OnTouchListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateTotalTime() {
|
private void updateTotalTime() {
|
||||||
TrackRecordingServiceInterface trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
TrackRecordingService trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
||||||
if (trackRecordingService != null) {
|
if (trackRecordingService != null) {
|
||||||
totalTime = trackRecordingService.getTotalTime();
|
totalTime = trackRecordingService.getTotalTime();
|
||||||
}
|
}
|
||||||
@@ -239,7 +240,7 @@ public class TrackController implements View.OnTouchListener {
|
|||||||
|
|
||||||
public interface Callback {
|
public interface Callback {
|
||||||
void recordStart();
|
void recordStart();
|
||||||
|
void recordPause();
|
||||||
void recordStop();
|
void recordStop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,26 +64,29 @@ public class TrackDeleteActivity extends AbstractActivity {
|
|||||||
|
|
||||||
runOnUiThread(TrackDeleteActivity.this::onAsyncTaskCompleted);
|
runOnUiThread(TrackDeleteActivity.this::onAsyncTaskCompleted);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStart() {
|
|
||||||
super.onStart();
|
|
||||||
deleteThread.start();
|
deleteThread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onStop() {
|
|
||||||
super.onStop();
|
|
||||||
deleteThread.interrupt();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
viewBinding = null;
|
viewBinding = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
if (deleteThread.getState() == Thread.State.TERMINATED) {
|
||||||
|
onAsyncTaskCompleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
deleteThread.interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected View getRootView() {
|
protected View getRootView() {
|
||||||
viewBinding = TrackDeleteBinding.inflate(getLayoutInflater());
|
viewBinding = TrackDeleteBinding.inflate(getLayoutInflater());
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package de.dennisguse.opentracks;
|
package de.dennisguse.opentracks;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
|
import android.app.ActivityOptions;
|
||||||
import android.app.SearchManager;
|
import android.app.SearchManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -29,6 +30,7 @@ import android.location.LocationManager;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.util.Pair;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
@@ -46,12 +48,14 @@ import androidx.loader.content.Loader;
|
|||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.data.Distance;
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.data.TracksColumns;
|
import de.dennisguse.opentracks.content.data.TracksColumns;
|
||||||
import de.dennisguse.opentracks.databinding.TrackListBinding;
|
import de.dennisguse.opentracks.databinding.TrackListBinding;
|
||||||
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
|
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
|
||||||
|
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingServiceInterface;
|
import de.dennisguse.opentracks.services.TrackRecordingServiceStatus;
|
||||||
import de.dennisguse.opentracks.services.handlers.GpsStatusValue;
|
import de.dennisguse.opentracks.services.handlers.GpsStatusValue;
|
||||||
import de.dennisguse.opentracks.settings.SettingsActivity;
|
import de.dennisguse.opentracks.settings.SettingsActivity;
|
||||||
import de.dennisguse.opentracks.util.ActivityUtils;
|
import de.dennisguse.opentracks.util.ActivityUtils;
|
||||||
@@ -67,7 +71,7 @@ import de.dennisguse.opentracks.util.TrackIconUtils;
|
|||||||
*
|
*
|
||||||
* @author Leif Hendrik Wilden
|
* @author Leif Hendrik Wilden
|
||||||
*/
|
*/
|
||||||
public class TrackListActivity extends AbstractListActivity implements ConfirmDeleteDialogFragment.ConfirmDeleteCaller, TrackController.Callback {
|
public class TrackListActivity extends AbstractListActivity implements ConfirmDeleteDialogFragment.ConfirmDeleteCaller, TrackController.Callback, TrackRecordingServiceStatus.Listener {
|
||||||
|
|
||||||
private static final String TAG = TrackListActivity.class.getSimpleName();
|
private static final String TAG = TrackListActivity.class.getSimpleName();
|
||||||
|
|
||||||
@@ -84,7 +88,8 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
|
|
||||||
// Preferences
|
// Preferences
|
||||||
private boolean metricUnits = true;
|
private boolean metricUnits = true;
|
||||||
private Track.Id recordingTrackId = null;
|
|
||||||
|
private boolean recordingTrackPaused = true;
|
||||||
|
|
||||||
// Callback when an item is selected in the contextual action mode
|
// Callback when an item is selected in the contextual action mode
|
||||||
private final ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
|
private final ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
|
||||||
@@ -103,29 +108,17 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private boolean recordingTrackPaused;
|
|
||||||
|
|
||||||
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||||
if (PreferencesUtils.isKey(TrackListActivity.this, R.string.stats_units_key, key)) {
|
if (PreferencesUtils.isKey(TrackListActivity.this, R.string.stats_units_key, key)) {
|
||||||
metricUnits = PreferencesUtils.isMetricUnits(TrackListActivity.this);
|
metricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, TrackListActivity.this);
|
||||||
}
|
|
||||||
if (PreferencesUtils.isKey(TrackListActivity.this, R.string.recording_track_id_key, key)) {
|
|
||||||
recordingTrackId = PreferencesUtils.getRecordingTrackId(TrackListActivity.this);
|
|
||||||
if (key != null && PreferencesUtils.isRecording(recordingTrackId)) {
|
|
||||||
trackRecordingServiceConnection.startAndBind(TrackListActivity.this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (PreferencesUtils.isKey(TrackListActivity.this, R.string.recording_track_paused_key, key)) {
|
|
||||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(TrackListActivity.this);
|
|
||||||
}
|
}
|
||||||
if (key != null) {
|
if (key != null) {
|
||||||
runOnUiThread(() -> {
|
runOnUiThread(() -> {
|
||||||
TrackListActivity.this.invalidateOptionsMenu();
|
TrackListActivity.this.invalidateOptionsMenu();
|
||||||
loaderCallbacks.restart();
|
loaderCallbacks.restart();
|
||||||
boolean isRecording = PreferencesUtils.isRecording(recordingTrackId);
|
trackController.update(isRecording(), recordingTrackPaused);
|
||||||
trackController.update(isRecording, recordingTrackPaused);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,12 +132,10 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
private final Runnable bindChangedCallback = new Runnable() {
|
private final Runnable bindChangedCallback = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
boolean isRecording = PreferencesUtils.isRecording(recordingTrackId);
|
|
||||||
|
|
||||||
// After binding changes (e.g., becomes available), update the total time in trackController.
|
// After binding changes (e.g., becomes available), update the total time in trackController.
|
||||||
runOnUiThread(() -> trackController.update(isRecording, recordingTrackPaused));
|
runOnUiThread(() -> trackController.update(isRecording(), recordingTrackPaused));
|
||||||
|
|
||||||
TrackRecordingServiceInterface service = trackRecordingServiceConnection.getServiceIfBound();
|
TrackRecordingService service = trackRecordingServiceConnection.getServiceIfBound();
|
||||||
if (service == null) {
|
if (service == null) {
|
||||||
Log.d(TAG, "service not available to start gps or a new recording");
|
Log.d(TAG, "service not available to start gps or a new recording");
|
||||||
gpsStatusValue = GpsStatusValue.GPS_NONE;
|
gpsStatusValue = GpsStatusValue.GPS_NONE;
|
||||||
@@ -153,19 +144,16 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
|
|
||||||
// Get GPS status and listen GPS status changes.
|
// Get GPS status and listen GPS status changes.
|
||||||
gpsStatusValue = service.getGpsStatus();
|
gpsStatusValue = service.getGpsStatus();
|
||||||
updateGpsMenuItem(true, isRecording);
|
updateGpsMenuItem(true, isRecording());
|
||||||
service.addListener(newStatus -> {
|
service.addListener(TrackListActivity.this);
|
||||||
gpsStatusValue = newStatus;
|
|
||||||
updateGpsMenuItem(true, isRecording);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (isGpsStarted()) {
|
if (isGpsStarted()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
service.startGps();
|
service.tryStartGps();
|
||||||
gpsStatusValue = GpsStatusValue.GPS_ENABLED;
|
gpsStatusValue = GpsStatusValue.GPS_ENABLED;
|
||||||
updateGpsMenuItem(true, isRecording);
|
updateGpsMenuItem(true, isRecording());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -179,8 +167,6 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
|
|
||||||
gpsStatusValue = GpsStatusValue.GPS_NONE;
|
gpsStatusValue = GpsStatusValue.GPS_NONE;
|
||||||
|
|
||||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPausedDefault(this);
|
|
||||||
|
|
||||||
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
||||||
|
|
||||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback);
|
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback);
|
||||||
@@ -195,17 +181,20 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
|
|
||||||
viewBinding.trackList.setEmptyView(viewBinding.trackListEmptyView);
|
viewBinding.trackList.setEmptyView(viewBinding.trackListEmptyView);
|
||||||
viewBinding.trackList.setOnItemClickListener((parent, view, position, trackId) -> {
|
viewBinding.trackList.setOnItemClickListener((parent, view, position, trackId) -> {
|
||||||
Intent newIntent;
|
if (isRecording() && trackId == recordingTrackId.getId()) {
|
||||||
if (trackId == recordingTrackId.getId()) {
|
|
||||||
// Is recording -> open record activity.
|
// Is recording -> open record activity.
|
||||||
newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordingActivity.class)
|
Intent newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordingActivity.class)
|
||||||
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, new Track.Id(trackId));
|
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, new Track.Id(trackId));
|
||||||
|
startActivity(newIntent);
|
||||||
} else {
|
} else {
|
||||||
// Not recording -> open detail activity.
|
// Not recording -> open detail activity.
|
||||||
newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordedActivity.class)
|
Intent newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordedActivity.class)
|
||||||
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, new Track.Id(trackId));
|
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, new Track.Id(trackId));
|
||||||
|
ActivityOptions activityOptions = ActivityOptions.makeSceneTransitionAnimation(
|
||||||
|
this,
|
||||||
|
new Pair<>(view.findViewById(R.id.list_item_icon), TrackRecordedActivity.VIEW_TRACK_ICON));
|
||||||
|
startActivity(newIntent, activityOptions.toBundle());
|
||||||
}
|
}
|
||||||
startActivity(newIntent);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
resourceCursorAdapter = new ResourceCursorAdapter(this, R.layout.list_item, null, 0) {
|
resourceCursorAdapter = new ResourceCursorAdapter(this, R.layout.list_item, null, 0) {
|
||||||
@@ -227,7 +216,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
int iconId = TrackIconUtils.getIconDrawable(icon);
|
int iconId = TrackIconUtils.getIconDrawable(icon);
|
||||||
String name = cursor.getString(nameIndex);
|
String name = cursor.getString(nameIndex);
|
||||||
String totalTime = StringUtils.formatElapsedTime(Duration.ofMillis(cursor.getLong(totalTimeIndex)));
|
String totalTime = StringUtils.formatElapsedTime(Duration.ofMillis(cursor.getLong(totalTimeIndex)));
|
||||||
String totalDistance = StringUtils.formatDistance(TrackListActivity.this, cursor.getDouble(totalDistanceIndex), metricUnits);
|
String totalDistance = StringUtils.formatDistance(TrackListActivity.this, Distance.of(cursor.getDouble(totalDistanceIndex)), metricUnits);
|
||||||
int markerCount = cursor.getInt(markerCountIndex);
|
int markerCount = cursor.getInt(markerCountIndex);
|
||||||
long startTime = cursor.getLong(startTimeIndex);
|
long startTime = cursor.getLong(startTimeIndex);
|
||||||
String category = icon != null && !icon.equals("") ? null : cursor.getString(categoryIndex);
|
String category = icon != null && !icon.equals("") ? null : cursor.getString(categoryIndex);
|
||||||
@@ -257,7 +246,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
super.onStart();
|
super.onStart();
|
||||||
|
|
||||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null);
|
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
||||||
trackRecordingServiceConnection.startConnection(this);
|
trackRecordingServiceConnection.startConnection(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,7 +265,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
// Update UI
|
// Update UI
|
||||||
this.invalidateOptionsMenu();
|
this.invalidateOptionsMenu();
|
||||||
LoaderManager.getInstance(this).restartLoader(0, null, loaderCallbacks);
|
LoaderManager.getInstance(this).restartLoader(0, null, loaderCallbacks);
|
||||||
trackController.onResume(PreferencesUtils.isRecording(recordingTrackId), recordingTrackPaused);
|
trackController.onResume(isRecording(), recordingTrackPaused);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -317,9 +306,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||||
boolean isGpsStarted = isGpsStarted();
|
updateMenuItems(isGpsStarted(), isRecording());
|
||||||
boolean isRecording = PreferencesUtils.isRecording(recordingTrackId);
|
|
||||||
updateMenuItems(isGpsStarted, isRecording);
|
|
||||||
|
|
||||||
SearchView searchView = (SearchView) searchMenuItem.getActionView();
|
SearchView searchView = (SearchView) searchMenuItem.getActionView();
|
||||||
searchView.setQuery("", false);
|
searchView.setQuery("", false);
|
||||||
@@ -339,9 +326,9 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
trackRecordingServiceConnection.startAndBind(this);
|
trackRecordingServiceConnection.startAndBind(this);
|
||||||
bindChangedCallback.run();
|
bindChangedCallback.run();
|
||||||
} else {
|
} else {
|
||||||
TrackRecordingServiceInterface trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
TrackRecordingService trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
||||||
if (trackRecordingService != null) {
|
if (trackRecordingService != null) {
|
||||||
trackRecordingService.stopGps();
|
trackRecordingService.stopGpsAndShutdown();
|
||||||
}
|
}
|
||||||
trackRecordingServiceConnection.unbindAndStop(this);
|
trackRecordingServiceConnection.unbindAndStop(this);
|
||||||
}
|
}
|
||||||
@@ -567,7 +554,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void recordStart() {
|
public void recordStart() {
|
||||||
if (!PreferencesUtils.isRecording(recordingTrackId)) {
|
if (recordingTrackId == null) {
|
||||||
// Not recording -> Recording
|
// Not recording -> Recording
|
||||||
updateMenuItems(false, true);
|
updateMenuItems(false, true);
|
||||||
Intent newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordingActivity.class);
|
Intent newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordingActivity.class);
|
||||||
@@ -577,17 +564,45 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
updateMenuItems(false, true);
|
updateMenuItems(false, true);
|
||||||
trackRecordingServiceConnection.resumeTrack();
|
trackRecordingServiceConnection.resumeTrack();
|
||||||
trackController.update(true, false);
|
trackController.update(true, false);
|
||||||
} else {
|
|
||||||
// Recording -> Paused
|
|
||||||
updateMenuItems(false, true);
|
|
||||||
trackRecordingServiceConnection.pauseTrack();
|
|
||||||
trackController.update(true, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void recordPause() {
|
||||||
|
updateMenuItems(false, true);
|
||||||
|
trackRecordingServiceConnection.pauseTrack();
|
||||||
|
trackController.update(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void recordStop() {
|
public void recordStop() {
|
||||||
updateMenuItems(false, false);
|
updateMenuItems(false, false);
|
||||||
trackRecordingServiceConnection.stopRecording(TrackListActivity.this, true);
|
trackRecordingServiceConnection.stopRecording(TrackListActivity.this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isRecording() {
|
||||||
|
return recordingTrackId != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGpsStatus(GpsStatusValue newStatus) {
|
||||||
|
gpsStatusValue = newStatus;
|
||||||
|
updateGpsMenuItem(true, isRecording());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTrackRecordingPaused(boolean isPaused) {
|
||||||
|
if (recordingTrackPaused != isPaused) {
|
||||||
|
trackController.update(isRecording(), isPaused);
|
||||||
|
}
|
||||||
|
recordingTrackPaused = isPaused;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTrackRecordingId(Track.Id trackId) {
|
||||||
|
if (!isRecording() && trackId != null) {
|
||||||
|
trackController.update(true, recordingTrackPaused);
|
||||||
|
}
|
||||||
|
recordingTrackId = trackId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import android.view.MenuItem;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||||
@@ -39,11 +40,12 @@ import de.dennisguse.opentracks.fragments.ChartFragment;
|
|||||||
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
|
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
|
||||||
import de.dennisguse.opentracks.fragments.IntervalsFragment;
|
import de.dennisguse.opentracks.fragments.IntervalsFragment;
|
||||||
import de.dennisguse.opentracks.fragments.StatisticsRecordedFragment;
|
import de.dennisguse.opentracks.fragments.StatisticsRecordedFragment;
|
||||||
|
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||||
|
import de.dennisguse.opentracks.services.TrackRecordingServiceStatus;
|
||||||
import de.dennisguse.opentracks.settings.SettingsActivity;
|
import de.dennisguse.opentracks.settings.SettingsActivity;
|
||||||
import de.dennisguse.opentracks.util.IntentDashboardUtils;
|
import de.dennisguse.opentracks.util.IntentDashboardUtils;
|
||||||
import de.dennisguse.opentracks.util.IntentUtils;
|
import de.dennisguse.opentracks.util.IntentUtils;
|
||||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An activity to show the track detail, record a new track or resumes an existing one.
|
* An activity to show the track detail, record a new track or resumes an existing one.
|
||||||
@@ -51,10 +53,13 @@ import de.dennisguse.opentracks.util.PreferencesUtils;
|
|||||||
* @author Leif Hendrik Wilden
|
* @author Leif Hendrik Wilden
|
||||||
* @author Rodrigo Damazio
|
* @author Rodrigo Damazio
|
||||||
*/
|
*/
|
||||||
public class TrackRecordedActivity extends AbstractListActivity implements ConfirmDeleteDialogFragment.ConfirmDeleteCaller, TrackActivityDataHubInterface {
|
//TODO Should not use TrackRecordingServiceConnection; only used to determine if there is NO current recording, to enable resume functionality.
|
||||||
|
public class TrackRecordedActivity extends AbstractListActivity implements ConfirmDeleteDialogFragment.ConfirmDeleteCaller, TrackActivityDataHubInterface, TrackRecordingServiceStatus.Listener {
|
||||||
|
|
||||||
private static final String TAG = TrackRecordedActivity.class.getSimpleName();
|
private static final String TAG = TrackRecordedActivity.class.getSimpleName();
|
||||||
|
|
||||||
|
public static final String VIEW_TRACK_ICON = "track_icon";
|
||||||
|
|
||||||
public static final String EXTRA_TRACK_ID = "track_id";
|
public static final String EXTRA_TRACK_ID = "track_id";
|
||||||
public static final String EXTRA_MARKER_ID = "marker_id";
|
public static final String EXTRA_MARKER_ID = "marker_id";
|
||||||
|
|
||||||
@@ -68,6 +73,18 @@ public class TrackRecordedActivity extends AbstractListActivity implements Confi
|
|||||||
|
|
||||||
private Track.Id trackId;
|
private Track.Id trackId;
|
||||||
|
|
||||||
|
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||||
|
|
||||||
|
private final Runnable bindCallback = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
TrackRecordingService service = trackRecordingServiceConnection.getServiceIfBound();
|
||||||
|
if (service != null) {
|
||||||
|
service.addListener(TrackRecordedActivity.this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@@ -84,6 +101,10 @@ public class TrackRecordedActivity extends AbstractListActivity implements Confi
|
|||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
viewBinding.trackDetailActivityViewPager.setCurrentItem(savedInstanceState.getInt(CURRENT_TAB_TAG_KEY));
|
viewBinding.trackDetailActivityViewPager.setCurrentItem(savedInstanceState.getInt(CURRENT_TAB_TAG_KEY));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindCallback);
|
||||||
|
|
||||||
|
postponeEnterTransition();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -102,11 +123,14 @@ public class TrackRecordedActivity extends AbstractListActivity implements Confi
|
|||||||
if (trackId != null) {
|
if (trackId != null) {
|
||||||
trackDataHub.loadTrack(trackId);
|
trackDataHub.loadTrack(trackId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trackRecordingServiceConnection.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStop() {
|
protected void onStop() {
|
||||||
super.onStop();
|
super.onStop();
|
||||||
|
trackRecordingServiceConnection.unbind(this);
|
||||||
trackDataHub.stop();
|
trackDataHub.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +162,7 @@ public class TrackRecordedActivity extends AbstractListActivity implements Confi
|
|||||||
@Override
|
@Override
|
||||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||||
menu.findItem(R.id.track_detail_markers).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
menu.findItem(R.id.track_detail_markers).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||||
menu.findItem(R.id.track_detail_resume_track).setVisible(!PreferencesUtils.isRecording(this));
|
menu.findItem(R.id.track_detail_resume_track).setVisible(recordingTrackId == null);
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
setTitle(track != null ? track.getName() : "");
|
setTitle(track != null ? track.getName() : "");
|
||||||
return super.onPrepareOptionsMenu(menu);
|
return super.onPrepareOptionsMenu(menu);
|
||||||
@@ -245,7 +269,7 @@ public class TrackRecordedActivity extends AbstractListActivity implements Confi
|
|||||||
case 0:
|
case 0:
|
||||||
return StatisticsRecordedFragment.newInstance(trackId);
|
return StatisticsRecordedFragment.newInstance(trackId);
|
||||||
case 1:
|
case 1:
|
||||||
return IntervalsFragment.newInstance();
|
return IntervalsFragment.newInstance(true);
|
||||||
case 2:
|
case 2:
|
||||||
return ChartFragment.newInstance(false);
|
return ChartFragment.newInstance(false);
|
||||||
case 3:
|
case 3:
|
||||||
@@ -275,4 +299,14 @@ public class TrackRecordedActivity extends AbstractListActivity implements Confi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void startPostponedEnterTransitionWith(View viewIcon) {
|
||||||
|
ViewCompat.setTransitionName(viewIcon, TrackRecordedActivity.VIEW_TRACK_ICON);
|
||||||
|
startPostponedEnterTransition();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTrackRecordingId(Track.Id trackId) {
|
||||||
|
recordingTrackId = trackId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -26,8 +26,9 @@ import de.dennisguse.opentracks.fragments.ChartFragment;
|
|||||||
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
|
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
|
||||||
import de.dennisguse.opentracks.fragments.IntervalsFragment;
|
import de.dennisguse.opentracks.fragments.IntervalsFragment;
|
||||||
import de.dennisguse.opentracks.fragments.StatisticsRecordingFragment;
|
import de.dennisguse.opentracks.fragments.StatisticsRecordingFragment;
|
||||||
|
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingServiceInterface;
|
import de.dennisguse.opentracks.services.TrackRecordingServiceStatus;
|
||||||
import de.dennisguse.opentracks.settings.SettingsActivity;
|
import de.dennisguse.opentracks.settings.SettingsActivity;
|
||||||
import de.dennisguse.opentracks.util.IntentDashboardUtils;
|
import de.dennisguse.opentracks.util.IntentDashboardUtils;
|
||||||
import de.dennisguse.opentracks.util.IntentUtils;
|
import de.dennisguse.opentracks.util.IntentUtils;
|
||||||
@@ -43,7 +44,7 @@ import de.dennisguse.opentracks.util.TrackUtils;
|
|||||||
*/
|
*/
|
||||||
//NOTE: This activity does NOT react to preference changes of R.string.recording_track_id_key.
|
//NOTE: This activity does NOT react to preference changes of R.string.recording_track_id_key.
|
||||||
//This mode of communication should be removed anyhow.
|
//This mode of communication should be removed anyhow.
|
||||||
public class TrackRecordingActivity extends AbstractActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller, TrackActivityDataHubInterface, TrackController.Callback {
|
public class TrackRecordingActivity extends AbstractActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller, TrackActivityDataHubInterface, TrackController.Callback, TrackRecordingServiceStatus.Listener {
|
||||||
|
|
||||||
public static final String EXTRA_TRACK_ID = "track_id";
|
public static final String EXTRA_TRACK_ID = "track_id";
|
||||||
|
|
||||||
@@ -62,8 +63,6 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
|
|
||||||
// Initialized from Intent; if a new track recording is started, a new TrackId will be provided by TrackRecordingService
|
// Initialized from Intent; if a new track recording is started, a new TrackId will be provided by TrackRecordingService
|
||||||
private Track.Id trackId;
|
private Track.Id trackId;
|
||||||
|
|
||||||
// Preferences
|
|
||||||
private boolean recordingTrackPaused;
|
private boolean recordingTrackPaused;
|
||||||
|
|
||||||
private final Runnable bindChangedCallback = new Runnable() {
|
private final Runnable bindChangedCallback = new Runnable() {
|
||||||
@@ -72,13 +71,12 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
// After binding changes (is available), update the total time in trackController.
|
// After binding changes (is available), update the total time in trackController.
|
||||||
runOnUiThread(() -> trackController.update(true, recordingTrackPaused));
|
runOnUiThread(() -> trackController.update(true, recordingTrackPaused));
|
||||||
|
|
||||||
TrackRecordingServiceInterface service = trackRecordingServiceConnection.getServiceIfBound();
|
TrackRecordingService service = trackRecordingServiceConnection.getServiceIfBound();
|
||||||
if (service == null) {
|
if (service == null) {
|
||||||
Log.d(TAG, "could not get TrackRecordingService");
|
Log.d(TAG, "could not get TrackRecordingService");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!service.isRecording()) {
|
if (!service.isRecording()) {
|
||||||
// Starts or resumes a track.
|
|
||||||
if (trackId == null) {
|
if (trackId == null) {
|
||||||
// trackId isn't initialized -> leads a new recording.
|
// trackId isn't initialized -> leads a new recording.
|
||||||
trackId = service.startNewTrack();
|
trackId = service.startNewTrack();
|
||||||
@@ -89,34 +87,28 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
|
|
||||||
// A recording track is on.
|
// A recording track is on.
|
||||||
trackDataHub.loadTrack(trackId);
|
trackDataHub.loadTrack(trackId);
|
||||||
|
trackDataHub.setRecordingTrackId(trackId);
|
||||||
|
|
||||||
trackController.update(true, false);
|
trackController.update(true, false);
|
||||||
trackController.onResume(true, recordingTrackPaused);
|
trackController.onResume(true, recordingTrackPaused);
|
||||||
}
|
}
|
||||||
|
service.addListener(TrackRecordingActivity.this);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||||
if (PreferencesUtils.isKey(TrackRecordingActivity.this, R.string.recording_track_paused_key, key)) {
|
|
||||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(TrackRecordingActivity.this);
|
|
||||||
setLockscreenPolicy();
|
|
||||||
setScreenOnPolicy();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PreferencesUtils.isKey(TrackRecordingActivity.this, R.string.stats_show_on_lockscreen_while_recording_key, key)) {
|
if (PreferencesUtils.isKey(TrackRecordingActivity.this, R.string.stats_show_on_lockscreen_while_recording_key, key)) {
|
||||||
setLockscreenPolicy();
|
setLockscreenPolicy();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PreferencesUtils.isKey(TrackRecordingActivity.this, R.string.stats_keep_screen_on_while_recording_key, key)) {
|
if (PreferencesUtils.isKey(TrackRecordingActivity.this, R.string.stats_keep_screen_on_while_recording_key, key)) {
|
||||||
setScreenOnPolicy();
|
setScreenOnPolicy();
|
||||||
}
|
}
|
||||||
if (PreferencesUtils.isKey(TrackRecordingActivity.this, R.string.stats_fullscreen_while_recording_key, key)) {
|
if (PreferencesUtils.isKey(TrackRecordingActivity.this, R.string.stats_fullscreen_while_recording_key, key)) {
|
||||||
setFullscreenPolicy();
|
setFullscreenPolicy();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key == null) return;
|
if (key == null) return;
|
||||||
|
|
||||||
runOnUiThread(() -> {
|
runOnUiThread(() -> {
|
||||||
TrackRecordingActivity.this.invalidateOptionsMenu();
|
TrackRecordingActivity.this.invalidateOptionsMenu();
|
||||||
trackController.update(true, recordingTrackPaused);
|
trackController.update(true, recordingTrackPaused);
|
||||||
@@ -145,8 +137,6 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPausedDefault(this);
|
|
||||||
|
|
||||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback);
|
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback);
|
||||||
trackDataHub = new TrackDataHub(this);
|
trackDataHub = new TrackDataHub(this);
|
||||||
|
|
||||||
@@ -170,7 +160,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setLockscreenPolicy() {
|
private void setLockscreenPolicy() {
|
||||||
boolean showOnLockScreen = PreferencesUtils.shouldShowStatsOnLockscreen(TrackRecordingActivity.this);
|
boolean showOnLockScreen = PreferencesUtils.shouldShowStatsOnLockscreen(sharedPreferences, TrackRecordingActivity.this);
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||||
setShowWhenLocked(showOnLockScreen);
|
setShowWhenLocked(showOnLockScreen);
|
||||||
@@ -182,7 +172,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setScreenOnPolicy() {
|
private void setScreenOnPolicy() {
|
||||||
boolean keepScreenOn = PreferencesUtils.shouldKeepScreenOn(TrackRecordingActivity.this);
|
boolean keepScreenOn = PreferencesUtils.shouldKeepScreenOn(sharedPreferences, TrackRecordingActivity.this);
|
||||||
|
|
||||||
if (keepScreenOn) {
|
if (keepScreenOn) {
|
||||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||||
@@ -192,7 +182,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setFullscreenPolicy() {
|
private void setFullscreenPolicy() {
|
||||||
boolean keepScreenOn = PreferencesUtils.shouldUseFullscreen(TrackRecordingActivity.this);
|
boolean keepScreenOn = PreferencesUtils.shouldUseFullscreen(sharedPreferences, TrackRecordingActivity.this);
|
||||||
|
|
||||||
if (keepScreenOn) {
|
if (keepScreenOn) {
|
||||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||||
@@ -206,7 +196,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
super.onStart();
|
super.onStart();
|
||||||
|
|
||||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null);
|
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
||||||
|
|
||||||
trackRecordingServiceConnection.startConnection(this);
|
trackRecordingServiceConnection.startConnection(this);
|
||||||
trackDataHub.start();
|
trackDataHub.start();
|
||||||
@@ -221,6 +211,8 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
|
|
||||||
if (trackId != null) {
|
if (trackId != null) {
|
||||||
trackDataHub.loadTrack(trackId);
|
trackDataHub.loadTrack(trackId);
|
||||||
|
trackDataHub.setRecordingTrackId(trackId);
|
||||||
|
trackDataHub.setRecordingTrackPaused(recordingTrackPaused);
|
||||||
trackController.onResume(true, recordingTrackPaused);
|
trackController.onResume(true, recordingTrackPaused);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,22 +337,21 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void recordStart() {
|
public void recordStart() {
|
||||||
if (recordingTrackPaused) {
|
updateMenuItems(false);
|
||||||
// Paused -> Resume
|
trackRecordingServiceConnection.resumeTrack();
|
||||||
updateMenuItems(false);
|
trackController.update(true, false);
|
||||||
trackRecordingServiceConnection.resumeTrack();
|
}
|
||||||
trackController.update(true, false);
|
|
||||||
} else {
|
@Override
|
||||||
// Recording -> Paused
|
public void recordPause() {
|
||||||
updateMenuItems(true);
|
updateMenuItems(true);
|
||||||
trackRecordingServiceConnection.pauseTrack();
|
trackRecordingServiceConnection.pauseTrack();
|
||||||
trackController.update(true, true);
|
trackController.update(true, true);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void recordStop() {
|
public void recordStop() {
|
||||||
trackRecordingServiceConnection.stopRecording(TrackRecordingActivity.this, true);
|
trackRecordingServiceConnection.stopRecording(TrackRecordingActivity.this);
|
||||||
Intent newIntent = IntentUtils.newIntent(TrackRecordingActivity.this, TrackRecordedActivity.class)
|
Intent newIntent = IntentUtils.newIntent(TrackRecordingActivity.this, TrackRecordedActivity.class)
|
||||||
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, trackId);
|
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, trackId);
|
||||||
startActivity(newIntent);
|
startActivity(newIntent);
|
||||||
@@ -382,7 +373,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
case 0:
|
case 0:
|
||||||
return StatisticsRecordingFragment.newInstance();
|
return StatisticsRecordingFragment.newInstance();
|
||||||
case 1:
|
case 1:
|
||||||
return IntervalsFragment.IntervalsRecordingFragment.newInstance();
|
return IntervalsFragment.newInstance(false);
|
||||||
case 2:
|
case 2:
|
||||||
return ChartFragment.newInstance(false);
|
return ChartFragment.newInstance(false);
|
||||||
case 3:
|
case 3:
|
||||||
@@ -412,4 +403,25 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTrackRecordingPaused(boolean isPaused) {
|
||||||
|
if (recordingTrackPaused != isPaused) {
|
||||||
|
trackController.update(true, isPaused);
|
||||||
|
trackDataHub.setRecordingTrackPaused(isPaused);
|
||||||
|
}
|
||||||
|
recordingTrackPaused = isPaused;
|
||||||
|
setLockscreenPolicy();
|
||||||
|
setScreenOnPolicy();
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO Why should this be interesting? This activity is only used for currently recording tracks.
|
||||||
|
@Override
|
||||||
|
public void onTrackRecordingId(Track.Id newTrackId) {
|
||||||
|
if (newTrackId != null && !newTrackId.equals(trackId)) {
|
||||||
|
trackId = newTrackId;
|
||||||
|
trackController.update(true, recordingTrackPaused);
|
||||||
|
trackDataHub.setRecordingTrackId(newTrackId);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package de.dennisguse.opentracks.adapters;
|
package de.dennisguse.opentracks.adapters;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -134,11 +135,13 @@ public class AggregatedStatisticsAdapter extends BaseAdapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO Check preference handling.
|
||||||
private void setCommonValues(AggregatedStatistics.AggregatedStatistic aggregatedStatistic) {
|
private void setCommonValues(AggregatedStatistics.AggregatedStatistic aggregatedStatistic) {
|
||||||
String category = aggregatedStatistic.getCategory();
|
String category = aggregatedStatistic.getCategory();
|
||||||
|
|
||||||
reportSpeed = PreferencesUtils.isReportSpeed(context, category);
|
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||||
metricsUnits = PreferencesUtils.isMetricUnits(context);
|
reportSpeed = PreferencesUtils.isReportSpeed(sharedPreferences, context, category);
|
||||||
|
metricsUnits = PreferencesUtils.isMetricUnits(sharedPreferences, context);
|
||||||
|
|
||||||
sportIcon.setImageResource(getIcon(aggregatedStatistic));
|
sportIcon.setImageResource(getIcon(aggregatedStatistic));
|
||||||
typeLabel.setText(category);
|
typeLabel.setText(category);
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ class ExecutorListViewService<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
Future future = executorService.submit(runnable);
|
Future<?> future = executorService.submit(runnable);
|
||||||
try {
|
try {
|
||||||
future.get();
|
future.get();
|
||||||
} catch (ExecutionException | InterruptedException e) {
|
} catch (ExecutionException | InterruptedException e) {
|
||||||
|
|||||||
@@ -4,72 +4,87 @@ import android.content.Context;
|
|||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
import de.dennisguse.opentracks.content.data.Distance;
|
||||||
import de.dennisguse.opentracks.util.StringUtils;
|
import de.dennisguse.opentracks.util.StringUtils;
|
||||||
import de.dennisguse.opentracks.viewmodels.IntervalStatistics;
|
import de.dennisguse.opentracks.viewmodels.IntervalStatistics;
|
||||||
|
|
||||||
public class IntervalStatisticsAdapter extends ArrayAdapter<IntervalStatistics.Interval> {
|
public class IntervalStatisticsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||||
|
|
||||||
|
private List<IntervalStatistics.Interval> intervalList;
|
||||||
|
private final Context context;
|
||||||
private final StackMode stackMode;
|
private final StackMode stackMode;
|
||||||
private final boolean metricUnits;
|
private boolean metricUnits;
|
||||||
private final String category;
|
private boolean isReportSpeed;
|
||||||
|
|
||||||
public IntervalStatisticsAdapter(Context context, List<IntervalStatistics.Interval> intervalList, String category, StackMode stackMode) {
|
public IntervalStatisticsAdapter(Context context, StackMode stackMode, boolean metricUnits, boolean isReportSpeed) {
|
||||||
super(context, R.layout.interval_stats_list_item, intervalList);
|
this.metricUnits = metricUnits;
|
||||||
metricUnits = PreferencesUtils.isMetricUnits(context);
|
this.context = context;
|
||||||
this.category = category;
|
|
||||||
this.stackMode = stackMode;
|
this.stackMode = stackMode;
|
||||||
|
this.isReportSpeed = isReportSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public View getView(int position, @Nullable View intervalView, @NonNull ViewGroup parent) {
|
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
int actualPosition = stackMode == StackMode.STACK_FROM_TOP ? position : getCount() - 1 - position;
|
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.interval_stats_list_item, parent, false);
|
||||||
IntervalStatistics.Interval interval = getItem(actualPosition);
|
return new IntervalStatisticsAdapter.ViewHolder(view);
|
||||||
ViewHolder viewHolder;
|
}
|
||||||
|
|
||||||
if (intervalView == null) {
|
@Override
|
||||||
viewHolder = new ViewHolder();
|
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||||
|
int actualPosition = stackMode == StackMode.STACK_FROM_TOP ? position : getItemCount() - 1 - position;
|
||||||
|
int nextPosition = actualPosition + 1;
|
||||||
|
boolean isLast = actualPosition == getItemCount() - 1;
|
||||||
|
IntervalStatisticsAdapter.ViewHolder viewHolder = (IntervalStatisticsAdapter.ViewHolder) holder;
|
||||||
|
IntervalStatistics.Interval interval = intervalList.get(actualPosition);
|
||||||
|
viewHolder.itemView.setTag(actualPosition);
|
||||||
|
|
||||||
intervalView = LayoutInflater.from(getContext()).inflate(R.layout.interval_stats_list_item, parent, false);
|
Distance sumDistance;
|
||||||
|
if (isLast && actualPosition > 0) {
|
||||||
viewHolder.distance = intervalView.findViewById(R.id.interval_item_distance);
|
sumDistance = intervalList.get(actualPosition - 1).getDistance()
|
||||||
viewHolder.rate = intervalView.findViewById(R.id.interval_item_rate);
|
.multipliedBy(actualPosition)
|
||||||
viewHolder.gain = intervalView.findViewById(R.id.interval_item_gain);
|
.plus(interval.getDistance());
|
||||||
viewHolder.loss = intervalView.findViewById(R.id.interval_item_loss);
|
|
||||||
|
|
||||||
intervalView.setTag(viewHolder);
|
|
||||||
} else {
|
} else {
|
||||||
viewHolder = (ViewHolder) intervalView.getTag();
|
sumDistance = interval.getDistance().multipliedBy(nextPosition);
|
||||||
|
}
|
||||||
|
viewHolder.distance.setText(StringUtils.formatDistance(context, sumDistance, metricUnits));
|
||||||
|
|
||||||
|
viewHolder.rate.setText(StringUtils.formatSpeed(context, interval.getSpeed(), metricUnits, isReportSpeed));
|
||||||
|
|
||||||
|
viewHolder.gain.setText(StringUtils.formatDistance(context, Distance.of(interval.getGain_m()), metricUnits));
|
||||||
|
viewHolder.loss.setText(StringUtils.formatDistance(context, Distance.of(interval.getLoss_m()), metricUnits));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
if (intervalList == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return intervalList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<IntervalStatistics.Interval> swapData(List<IntervalStatistics.Interval> data, boolean metricUnits, boolean isReportSpeed) {
|
||||||
|
if (intervalList == data && this.metricUnits == metricUnits && this.isReportSpeed == isReportSpeed) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
float sumDistance_m;
|
this.metricUnits = metricUnits;
|
||||||
if (actualPosition + 1 == getCount() && actualPosition > 0) {
|
this.isReportSpeed = isReportSpeed;
|
||||||
sumDistance_m = actualPosition * getItem(actualPosition - 1).getDistance_m() + interval.getDistance_m();
|
intervalList = data;
|
||||||
} else {
|
|
||||||
sumDistance_m = (actualPosition + 1) * interval.getDistance_m();
|
|
||||||
}
|
|
||||||
viewHolder.distance.setText(StringUtils.formatDistance(getContext(), sumDistance_m, metricUnits));
|
|
||||||
|
|
||||||
if (PreferencesUtils.isReportSpeed(getContext(), category)) {
|
if (data != null) {
|
||||||
viewHolder.rate.setText(StringUtils.formatSpeed(getContext(), interval.getSpeed_ms(), metricUnits, true));
|
this.notifyDataSetChanged();
|
||||||
} else {
|
|
||||||
viewHolder.rate.setText(StringUtils.formatSpeed(getContext(), interval.getSpeed_ms(), metricUnits, false));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
viewHolder.gain.setText(StringUtils.formatDistance(getContext(), interval.getGain_m(), metricUnits));
|
return data;
|
||||||
viewHolder.loss.setText(StringUtils.formatDistance(getContext(), interval.getLoss_m(), metricUnits));
|
|
||||||
|
|
||||||
return intervalView;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -80,10 +95,18 @@ public class IntervalStatisticsAdapter extends ArrayAdapter<IntervalStatistics.I
|
|||||||
STACK_FROM_TOP
|
STACK_FROM_TOP
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ViewHolder {
|
private static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
private TextView distance;
|
TextView distance;
|
||||||
private TextView rate;
|
TextView rate;
|
||||||
private TextView gain;
|
TextView gain;
|
||||||
private TextView loss;
|
TextView loss;
|
||||||
|
|
||||||
|
public ViewHolder(@NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
distance = itemView.findViewById(R.id.interval_item_distance);
|
||||||
|
rate = itemView.findViewById(R.id.interval_item_rate);
|
||||||
|
gain = itemView.findViewById(R.id.interval_item_gain);
|
||||||
|
loss = itemView.findViewById(R.id.interval_item_loss);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public class MarkerResourceCursorAdapter extends ResourceCursorAdapter implement
|
|||||||
private final Activity activity;
|
private final Activity activity;
|
||||||
|
|
||||||
//TODO Should be Marker.Id
|
//TODO Should be Marker.Id
|
||||||
private final ExecutorListViewService executorService = new ExecutorListViewService<Long>(LIST_PREFERRED_ITEM_HEIGHT_DEFAULT);
|
private final ExecutorListViewService<Long> executorService = new ExecutorListViewService<>(LIST_PREFERRED_ITEM_HEIGHT_DEFAULT);
|
||||||
|
|
||||||
private boolean scroll = false;
|
private boolean scroll = false;
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,10 @@ package de.dennisguse.opentracks.chart;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.data.Distance;
|
||||||
|
import de.dennisguse.opentracks.content.data.Speed;
|
||||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||||
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
|
|
||||||
import de.dennisguse.opentracks.util.UnitConversions;
|
import de.dennisguse.opentracks.util.UnitConversions;
|
||||||
|
|
||||||
public class ChartPoint {
|
public class ChartPoint {
|
||||||
@@ -13,51 +14,38 @@ public class ChartPoint {
|
|||||||
private double timeOrDistance;
|
private double timeOrDistance;
|
||||||
|
|
||||||
//Y-axis
|
//Y-axis
|
||||||
private double elevation;
|
private final double altitude;
|
||||||
private double speed;
|
private Double speed;
|
||||||
private double pace;
|
private Double pace;
|
||||||
private double heartRate = Double.NaN;
|
private Double heartRate;
|
||||||
private double cadence = Double.NaN;
|
private Double cadence;
|
||||||
private double power = Double.NaN;
|
private Double power;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
ChartPoint(double elevation) {
|
ChartPoint(double altitude) {
|
||||||
this.elevation = elevation;
|
this.altitude = altitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChartPoint(@NonNull TrackStatisticsUpdater trackStatisticsUpdater, TrackPoint trackPoint, boolean chartByDistance, boolean metricUnits) {
|
public ChartPoint(@NonNull TrackStatistics trackStatistics, @NonNull TrackPoint trackPoint, Speed smoothedSpeed, double smoothedAltitude_m, boolean chartByDistance, boolean metricUnits) {
|
||||||
TrackStatistics trackStatistics = trackStatisticsUpdater.getTrackStatistics();
|
|
||||||
|
|
||||||
if (chartByDistance) {
|
if (chartByDistance) {
|
||||||
double distance = trackStatistics.getTotalDistance() * UnitConversions.M_TO_KM;
|
timeOrDistance = trackStatistics.getTotalDistance().to(metricUnits);
|
||||||
if (!metricUnits) {
|
|
||||||
distance *= UnitConversions.KM_TO_MI;
|
|
||||||
}
|
|
||||||
timeOrDistance = distance;
|
|
||||||
} else {
|
} else {
|
||||||
timeOrDistance = trackStatistics.getTotalTime().toMillis();
|
timeOrDistance = trackStatistics.getTotalTime().toMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
elevation = trackStatisticsUpdater.getSmoothedElevation();
|
altitude = Distance.of(smoothedAltitude_m).to(metricUnits);
|
||||||
if (!metricUnits) {
|
|
||||||
elevation *= UnitConversions.M_TO_FT;
|
|
||||||
}
|
|
||||||
|
|
||||||
speed = trackStatisticsUpdater.getSmoothedSpeed() * UnitConversions.MPS_TO_KMH;
|
speed = smoothedSpeed.to(metricUnits);
|
||||||
if (!metricUnits) {
|
pace = smoothedSpeed.toPace(metricUnits).toMillis() * UnitConversions.MS_TO_S * UnitConversions.S_TO_MIN;
|
||||||
speed *= UnitConversions.KM_TO_MI;
|
if (trackPoint.hasHeartRate()) {
|
||||||
|
heartRate = (double) trackPoint.getHeartRate_bpm();
|
||||||
}
|
}
|
||||||
pace = speed == 0 ? 0.0 : 60.0 / speed;
|
if (trackPoint.hasCyclingCadence()) {
|
||||||
if (trackPoint != null) {
|
cadence = (double) trackPoint.getCyclingCadence_rpm();
|
||||||
if (trackPoint.hasHeartRate()) {
|
}
|
||||||
heartRate = trackPoint.getHeartRate_bpm();
|
if (trackPoint.hasPower()) {
|
||||||
}
|
power = (double) trackPoint.getPower();
|
||||||
if (trackPoint.hasCyclingCadence()) {
|
|
||||||
cadence = trackPoint.getCyclingCadence_rpm();
|
|
||||||
}
|
|
||||||
if (trackPoint.hasPower()) {
|
|
||||||
power = trackPoint.getPower();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,39 +53,27 @@ public class ChartPoint {
|
|||||||
return timeOrDistance;
|
return timeOrDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getElevation() {
|
public Double getAltitude() {
|
||||||
return elevation;
|
return altitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getSpeed() {
|
public Double getSpeed() {
|
||||||
return speed;
|
return speed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getPace() {
|
public Double getPace() {
|
||||||
return pace;
|
return pace;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasHeartRate() {
|
public Double getHeartRate() {
|
||||||
return Double.isNaN(heartRate);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getHeartRate() {
|
|
||||||
return heartRate;
|
return heartRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasCadence() {
|
public Double getCadence() {
|
||||||
return Double.isNaN(cadence);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getCadence() {
|
|
||||||
return cadence;
|
return cadence;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasPower() {
|
public Double getPower() {
|
||||||
return Double.isNaN(power);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getPower() {
|
|
||||||
return power;
|
return power;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,10 +132,10 @@ abstract class ChartValueSeries {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract double extractDataFromChartPoint(@NonNull ChartPoint chartPoint);
|
abstract Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint);
|
||||||
|
|
||||||
boolean isChartPointValid(@NonNull ChartPoint chartPoint) {
|
boolean isChartPointValid(@NonNull ChartPoint chartPoint) {
|
||||||
return !Double.isNaN(extractDataFromChartPoint(chartPoint));
|
return extractDataFromChartPoint(chartPoint) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract boolean drawIfChartPointHasNoData();
|
protected abstract boolean drawIfChartPointHasNoData();
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import java.text.NumberFormat;
|
|||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.MarkerDetailActivity;
|
import de.dennisguse.opentracks.MarkerDetailActivity;
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
@@ -50,7 +51,6 @@ import de.dennisguse.opentracks.util.IntentUtils;
|
|||||||
import de.dennisguse.opentracks.util.MarkerUtils;
|
import de.dennisguse.opentracks.util.MarkerUtils;
|
||||||
import de.dennisguse.opentracks.util.StringUtils;
|
import de.dennisguse.opentracks.util.StringUtils;
|
||||||
import de.dennisguse.opentracks.util.ThemeUtils;
|
import de.dennisguse.opentracks.util.ThemeUtils;
|
||||||
import de.dennisguse.opentracks.util.UnitConversions;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visualization of the chart.
|
* Visualization of the chart.
|
||||||
@@ -199,15 +199,15 @@ public class ChartView extends View {
|
|||||||
Integer.MIN_VALUE,
|
Integer.MIN_VALUE,
|
||||||
Integer.MAX_VALUE,
|
Integer.MAX_VALUE,
|
||||||
new int[]{5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000},
|
new int[]{5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000},
|
||||||
R.string.description_elevation_metric,
|
R.string.description_altitude_metric,
|
||||||
R.string.description_elevation_imperial,
|
R.string.description_altitude_imperial,
|
||||||
R.color.chart_elevation_fill,
|
R.color.chart_altitude_fill,
|
||||||
R.color.chart_elevation_border,
|
R.color.chart_altitude_border,
|
||||||
fontSizeSmall,
|
fontSizeSmall,
|
||||||
fontSizeMedium) {
|
fontSizeMedium) {
|
||||||
@Override
|
@Override
|
||||||
protected double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
protected Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
||||||
return chartPoint.getElevation();
|
return chartPoint.getAltitude();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -227,7 +227,7 @@ public class ChartView extends View {
|
|||||||
fontSizeSmall,
|
fontSizeSmall,
|
||||||
fontSizeMedium) {
|
fontSizeMedium) {
|
||||||
@Override
|
@Override
|
||||||
protected double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
protected Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
||||||
return chartPoint.getSpeed();
|
return chartPoint.getSpeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,7 +249,7 @@ public class ChartView extends View {
|
|||||||
fontSizeSmall,
|
fontSizeSmall,
|
||||||
fontSizeMedium) {
|
fontSizeMedium) {
|
||||||
@Override
|
@Override
|
||||||
protected double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
protected Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
||||||
return chartPoint.getPace();
|
return chartPoint.getPace();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,7 +271,7 @@ public class ChartView extends View {
|
|||||||
fontSizeSmall,
|
fontSizeSmall,
|
||||||
fontSizeMedium) {
|
fontSizeMedium) {
|
||||||
@Override
|
@Override
|
||||||
protected double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
protected Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
||||||
return chartPoint.getHeartRate();
|
return chartPoint.getHeartRate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,7 +292,7 @@ public class ChartView extends View {
|
|||||||
fontSizeSmall,
|
fontSizeSmall,
|
||||||
fontSizeMedium) {
|
fontSizeMedium) {
|
||||||
@Override
|
@Override
|
||||||
protected double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
protected Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
||||||
return chartPoint.getCadence();
|
return chartPoint.getCadence();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,7 +312,7 @@ public class ChartView extends View {
|
|||||||
fontSizeSmall,
|
fontSizeSmall,
|
||||||
fontSizeMedium) {
|
fontSizeMedium) {
|
||||||
@Override
|
@Override
|
||||||
protected double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
protected Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
||||||
return chartPoint.getPower();
|
return chartPoint.getPower();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -893,35 +893,23 @@ public class ChartView extends View {
|
|||||||
|
|
||||||
private void closePaths() {
|
private void closePaths() {
|
||||||
for (ChartValueSeries chartValueSeries : seriesList) {
|
for (ChartValueSeries chartValueSeries : seriesList) {
|
||||||
int first = getFirstPopulatedChartDataIndex(chartValueSeries);
|
Optional<ChartPoint> firstValid = chartPoints.stream().filter(chartValueSeries::isChartPointValid)
|
||||||
|
.findFirst();
|
||||||
|
Optional<ChartPoint> lastValid = chartPoints.stream().filter(chartValueSeries::isChartPointValid)
|
||||||
|
.reduce((first, second) -> second);
|
||||||
|
|
||||||
if (first != -1) {
|
if (firstValid.isPresent() && lastValid.isPresent()) {
|
||||||
int xCorner = getX(chartPoints.get(first).getTimeOrDistance());
|
|
||||||
int yCorner = topBorder + effectiveHeight;
|
|
||||||
Path path = chartValueSeries.getPath();
|
Path path = chartValueSeries.getPath();
|
||||||
// Bottom right corner
|
|
||||||
path.lineTo(getX(chartPoints.get(chartPoints.size() - 1).getTimeOrDistance()), yCorner);
|
|
||||||
// Bottom left corner
|
|
||||||
path.lineTo(xCorner, yCorner);
|
|
||||||
// Top right corner
|
|
||||||
double value = chartValueSeries.extractDataFromChartPoint(chartPoints.get(first));
|
|
||||||
path.lineTo(xCorner, getY(chartValueSeries, value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
int yCorner = topBorder + effectiveHeight;
|
||||||
* Finds the index of the first data point containing data for a series.
|
|
||||||
*
|
path.lineTo(getX(lastValid.get().getTimeOrDistance()), yCorner);
|
||||||
* @return -1 if no data point contains data for the series.
|
path.lineTo(getX(firstValid.get().getTimeOrDistance()), yCorner);
|
||||||
*/
|
|
||||||
private int getFirstPopulatedChartDataIndex(ChartValueSeries chartValueSeries) {
|
double value = chartValueSeries.extractDataFromChartPoint(firstValid.get());
|
||||||
for (int i = 0; i < chartPoints.size(); i++) {
|
path.lineTo(getX(firstValid.get().getTimeOrDistance()), getY(chartValueSeries, value));
|
||||||
if (chartValueSeries.isChartPointValid(chartPoints.get(i))) {
|
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1005,10 +993,9 @@ public class ChartView extends View {
|
|||||||
|
|
||||||
private double getMarkerXValue(Marker marker) {
|
private double getMarkerXValue(Marker marker) {
|
||||||
if (chartByDistance) {
|
if (chartByDistance) {
|
||||||
double length_km = marker.getLength() * UnitConversions.M_TO_KM;
|
return marker.getLength().to(metricUnits);
|
||||||
return metricUnits ? length_km : length_km * UnitConversions.KM_TO_MI;
|
|
||||||
} else {
|
} else {
|
||||||
return marker.getDuration();
|
return marker.getDuration().toMillis();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import androidx.annotation.VisibleForTesting;
|
|||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
|
import de.dennisguse.opentracks.content.data.Distance;
|
||||||
|
import de.dennisguse.opentracks.content.data.Speed;
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||||
import de.dennisguse.opentracks.util.StringUtils;
|
import de.dennisguse.opentracks.util.StringUtils;
|
||||||
@@ -127,24 +129,24 @@ public class DescriptionGenerator {
|
|||||||
// Fastest pace
|
// Fastest pace
|
||||||
writePace(stats.getMaxSpeed(), builder, R.string.description_fastest_pace_in_minute, lineBreak);
|
writePace(stats.getMaxSpeed(), builder, R.string.description_fastest_pace_in_minute, lineBreak);
|
||||||
|
|
||||||
// Max elevation
|
// Max altitude
|
||||||
if (stats.hasElevationMax()) {
|
if (stats.hasAltitudeMax()) {
|
||||||
writeElevation(stats.getMaxElevation(), builder, R.string.description_max_elevation, lineBreak);
|
writeAltitude(stats.getMaxAltitude(), builder, R.string.description_max_altitude, lineBreak);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Min elevation
|
// Min altitude
|
||||||
if (stats.hasElevationMin()) {
|
if (stats.hasAltitudeMin()) {
|
||||||
writeElevation(stats.getMinElevation(), builder, R.string.description_min_elevation, lineBreak);
|
writeAltitude(stats.getMinAltitude(), builder, R.string.description_min_altitude, lineBreak);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Elevation gain
|
// Altitude gain
|
||||||
if (stats.hasTotalElevationGain()) {
|
if (stats.hasTotalAltitudeGain()) {
|
||||||
writeElevation(stats.getTotalElevationGain(), builder, R.string.description_elevation_gain, lineBreak);
|
writeAltitude(stats.getTotalAltitudeGain(), builder, R.string.description_altitude_gain, lineBreak);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Elevation loss
|
// Altitude loss
|
||||||
if (stats.hasTotalElevationLoss()) {
|
if (stats.hasTotalAltitudeLoss()) {
|
||||||
writeElevation(stats.getTotalElevationLoss(), builder, R.string.description_elevation_loss, lineBreak);
|
writeAltitude(stats.getTotalAltitudeLoss(), builder, R.string.description_altitude_loss, lineBreak);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recorded time
|
// Recorded time
|
||||||
@@ -163,9 +165,9 @@ public class DescriptionGenerator {
|
|||||||
* @param lineBreak line break string
|
* @param lineBreak line break string
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void writeDistance(double distance, StringBuilder builder, int resId, String lineBreak) {
|
void writeDistance(Distance distance, StringBuilder builder, int resId, String lineBreak) {
|
||||||
double distanceInKm = distance * UnitConversions.M_TO_KM;
|
double distanceInKm = distance.toKM();
|
||||||
double distanceInMi = distanceInKm * UnitConversions.KM_TO_MI;
|
double distanceInMi = distance.toMI();
|
||||||
builder.append(context.getString(resId, distanceInKm, distanceInMi));
|
builder.append(context.getString(resId, distanceInKm, distanceInMi));
|
||||||
builder.append(lineBreak);
|
builder.append(lineBreak);
|
||||||
}
|
}
|
||||||
@@ -193,23 +195,18 @@ public class DescriptionGenerator {
|
|||||||
* @param lineBreak line break string
|
* @param lineBreak line break string
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void writeSpeed(double speed, StringBuilder builder, int resId, String lineBreak) {
|
void writeSpeed(Speed speed, StringBuilder builder, int resId, String lineBreak) {
|
||||||
double speedInKmHr = speed * UnitConversions.MPS_TO_KMH;
|
builder.append(context.getString(resId, speed.toKMH(), speed.toMPH()));
|
||||||
double speedInMiHr = speedInKmHr * UnitConversions.KM_TO_MI;
|
|
||||||
builder.append(context.getString(resId, speedInKmHr, speedInMiHr));
|
|
||||||
builder.append(lineBreak);
|
builder.append(lineBreak);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes pace.
|
|
||||||
*
|
|
||||||
* @param speed speed in meters per second
|
|
||||||
* @param builder StringBuilder to append pace
|
* @param builder StringBuilder to append pace
|
||||||
* @param resId resource id of pace string
|
* @param resId resource id of pace string
|
||||||
* @param lineBreak line break string
|
* @param lineBreak line break string
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void writePace(double speed, StringBuilder builder, int resId, String lineBreak) {
|
void writePace(Speed speed, StringBuilder builder, int resId, String lineBreak) {
|
||||||
Pair<String, String> paceInMetrics = StringUtils.getSpeedParts(context, speed, true, false);
|
Pair<String, String> paceInMetrics = StringUtils.getSpeedParts(context, speed, true, false);
|
||||||
Pair<String, String> paceInImperial = StringUtils.getSpeedParts(context, speed, false, false);
|
Pair<String, String> paceInImperial = StringUtils.getSpeedParts(context, speed, false, false);
|
||||||
|
|
||||||
@@ -221,18 +218,16 @@ public class DescriptionGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes elevation.
|
* @param altitude_m altitude_m in meters
|
||||||
*
|
* @param builder StringBuilder to append
|
||||||
* @param elevation elevation in meters
|
* @param resId resource id of altitude string
|
||||||
* @param builder StringBuilder to append elevation
|
* @param lineBreak line break string
|
||||||
* @param resId resource id of elevation string
|
|
||||||
* @param lineBreak line break string
|
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void writeElevation(double elevation, StringBuilder builder, int resId, String lineBreak) {
|
void writeAltitude(double altitude_m, StringBuilder builder, int resId, String lineBreak) {
|
||||||
long elevationInM = Math.round(elevation);
|
long altitudeInM = Math.round(altitude_m);
|
||||||
long elevationInFt = Math.round(elevation * UnitConversions.M_TO_FT);
|
long altitudeInFt = Math.round(altitude_m * UnitConversions.M_TO_FT);
|
||||||
builder.append(context.getString(resId, elevationInM, elevationInFt));
|
builder.append(context.getString(resId, altitudeInM, altitudeInFt));
|
||||||
builder.append(lineBreak);
|
builder.append(lineBreak);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ package de.dennisguse.opentracks.content;
|
|||||||
|
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.database.ContentObserver;
|
import android.database.ContentObserver;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@@ -31,7 +30,7 @@ import androidx.annotation.VisibleForTesting;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.content.data.Distance;
|
||||||
import de.dennisguse.opentracks.content.data.Marker;
|
import de.dennisguse.opentracks.content.data.Marker;
|
||||||
import de.dennisguse.opentracks.content.data.MarkerColumns;
|
import de.dennisguse.opentracks.content.data.MarkerColumns;
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
@@ -40,7 +39,7 @@ import de.dennisguse.opentracks.content.data.TrackPointsColumns;
|
|||||||
import de.dennisguse.opentracks.content.data.TracksColumns;
|
import de.dennisguse.opentracks.content.data.TracksColumns;
|
||||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
|
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
|
||||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Track data hub.
|
* Track data hub.
|
||||||
@@ -50,7 +49,8 @@ import de.dennisguse.opentracks.util.PreferencesUtils;
|
|||||||
*
|
*
|
||||||
* @author Rodrigo Damazio
|
* @author Rodrigo Damazio
|
||||||
*/
|
*/
|
||||||
public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeListener {
|
//TODO register contentobserver only for exact URL (incl. trackId) to not filter here.
|
||||||
|
public class TrackDataHub {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Target number of track points displayed by the diagrams (recommended).
|
* Target number of track points displayed by the diagrams (recommended).
|
||||||
@@ -75,9 +75,12 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
|
|||||||
private HandlerThread handlerThread;
|
private HandlerThread handlerThread;
|
||||||
private Handler handler;
|
private Handler handler;
|
||||||
|
|
||||||
// Preference values
|
|
||||||
private Track.Id selectedTrackId;
|
private Track.Id selectedTrackId;
|
||||||
|
@Deprecated
|
||||||
|
//TODO Is there a better way to handle this? e.g., passing information rather from Activity than from Activity to TrackDataHub
|
||||||
private Track.Id recordingTrackId;
|
private Track.Id recordingTrackId;
|
||||||
|
@Deprecated
|
||||||
|
//TODO Is there a better way to handle this? e.g., passing information rather from Activity than from Activity to TrackDataHub
|
||||||
private boolean recordingTrackPaused;
|
private boolean recordingTrackPaused;
|
||||||
|
|
||||||
// Track points sampling state
|
// Track points sampling state
|
||||||
@@ -139,9 +142,6 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
|
|||||||
};
|
};
|
||||||
contentResolver.registerContentObserver(TrackPointsColumns.CONTENT_URI_BY_ID, false, trackPointsTableObserver);
|
contentResolver.registerContentObserver(TrackPointsColumns.CONTENT_URI_BY_ID, false, trackPointsTableObserver);
|
||||||
|
|
||||||
|
|
||||||
PreferencesUtils.register(context, this);
|
|
||||||
onSharedPreferenceChanged(null, null);
|
|
||||||
handler.post(() -> {
|
handler.post(() -> {
|
||||||
if (started) {
|
if (started) {
|
||||||
loadDataForAll();
|
loadDataForAll();
|
||||||
@@ -155,8 +155,6 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PreferencesUtils.unregister(context, this);
|
|
||||||
|
|
||||||
started = false;
|
started = false;
|
||||||
|
|
||||||
//Unregister listeners
|
//Unregister listeners
|
||||||
@@ -210,7 +208,7 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
|
|||||||
* Returns true if the selected track is recording.
|
* Returns true if the selected track is recording.
|
||||||
*/
|
*/
|
||||||
public boolean isSelectedTrackRecording() {
|
public boolean isSelectedTrackRecording() {
|
||||||
return selectedTrackId != null && selectedTrackId.equals(recordingTrackId) && PreferencesUtils.isRecording(recordingTrackId);
|
return selectedTrackId != null && selectedTrackId.equals(recordingTrackId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -220,18 +218,6 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
|
|||||||
return selectedTrackId != null && selectedTrackId.equals(recordingTrackId) && recordingTrackPaused;
|
return selectedTrackId != null && selectedTrackId.equals(recordingTrackId) && recordingTrackPaused;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, final String key) {
|
|
||||||
handler.post(() -> {
|
|
||||||
if (PreferencesUtils.isKey(context, R.string.recording_track_id_key, key)) {
|
|
||||||
recordingTrackId = PreferencesUtils.getRecordingTrackId(context);
|
|
||||||
}
|
|
||||||
if (PreferencesUtils.isKey(context, R.string.recording_track_paused_key, key)) {
|
|
||||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(context);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads data for all listeners. To be run in the {@link #handler} thread.
|
* Loads data for all listeners. To be run in the {@link #handler} thread.
|
||||||
*/
|
*/
|
||||||
@@ -365,6 +351,8 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
|
|||||||
next = new TrackPoint.Id(localLastSeenTrackPointIdId.getId() + 1); //TODO startTrackPointId + 1 is an assumption assumption; should be derived from the DB.
|
next = new TrackPoint.Id(localLastSeenTrackPointIdId.getId() + 1); //TODO startTrackPointId + 1 is an assumption assumption; should be derived from the DB.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TrackStatisticsUpdater trackStatisticsUpdater = new TrackStatisticsUpdater();
|
||||||
|
|
||||||
TrackPoint trackPoint = null;
|
TrackPoint trackPoint = null;
|
||||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(selectedTrackId, next)) {
|
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(selectedTrackId, next)) {
|
||||||
|
|
||||||
@@ -386,15 +374,16 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
|
|||||||
samplingFrequency = 1 + (int) (numTotalPoints / targetNumPoints);
|
samplingFrequency = 1 + (int) (numTotalPoints / targetNumPoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trackStatisticsUpdater.addTrackPoint(trackPoint, Distance.of(50)); //TODO
|
||||||
|
|
||||||
// Also include the last point if the selected track is not recording.
|
// Also include the last point if the selected track is not recording.
|
||||||
if ((localNumLoadedTrackPoints % samplingFrequency == 0) || (trackPointId == lastTrackPointId && !isSelectedTrackRecording())) {
|
if ((localNumLoadedTrackPoints % samplingFrequency == 0) || (trackPointId == lastTrackPointId && !isSelectedTrackRecording())) {
|
||||||
for (TrackDataListener trackDataListener : sampledInListeners) {
|
for (TrackDataListener trackDataListener : sampledInListeners) {
|
||||||
trackDataListener.onSampledInTrackPoint(trackPoint);
|
trackDataListener.onSampledInTrackPoint(trackPoint, trackStatisticsUpdater.getTrackStatistics(), trackStatisticsUpdater.getSmoothedSpeed(), trackStatisticsUpdater.getSmoothedAltitude());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (TrackDataListener trackDataListener : sampledOutListeners) {
|
for (TrackDataListener trackDataListener : sampledOutListeners) {
|
||||||
trackDataListener.onSampledOutTrackPoint(trackPoint);
|
trackDataListener.onSampledOutTrackPoint(trackPoint, trackStatisticsUpdater.getTrackStatistics());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -414,7 +403,7 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
|
|||||||
|
|
||||||
if (trackPoint != null) {
|
if (trackPoint != null) {
|
||||||
for (TrackDataListener listener : sampledInListeners) {
|
for (TrackDataListener listener : sampledInListeners) {
|
||||||
listener.onNewTrackPointsDone(trackPoint);
|
listener.onNewTrackPointsDone(trackPoint, trackStatisticsUpdater.getTrackStatistics());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -427,4 +416,12 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
|
|||||||
firstSeenTrackPointId = null;
|
firstSeenTrackPointId = null;
|
||||||
lastSeenTrackPointId = null;
|
lastSeenTrackPointId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setRecordingTrackId(Track.Id recordingTrackId) {
|
||||||
|
this.recordingTrackId = recordingTrackId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRecordingTrackPaused(boolean recordingTrackPaused) {
|
||||||
|
this.recordingTrackPaused = recordingTrackPaused;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -19,8 +19,10 @@ package de.dennisguse.opentracks.content;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.data.Marker;
|
import de.dennisguse.opentracks.content.data.Marker;
|
||||||
|
import de.dennisguse.opentracks.content.data.Speed;
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
|
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listener for track data changes.
|
* Listener for track data changes.
|
||||||
@@ -47,20 +49,23 @@ public interface TrackDataListener {
|
|||||||
*
|
*
|
||||||
* @param trackPoint the trackPoint
|
* @param trackPoint the trackPoint
|
||||||
*/
|
*/
|
||||||
void onSampledInTrackPoint(@NonNull TrackPoint trackPoint);
|
default void onSampledInTrackPoint(@NonNull TrackPoint trackPoint, @NonNull TrackStatistics trackStatistics, Speed smoothedSpeed, double smoothedAltitude_m) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a sampled out track point is read.
|
* Called when a sampled out track point is read.
|
||||||
*
|
*
|
||||||
* @param trackPoint the trackPoint
|
* @param trackPoint the trackPoint
|
||||||
*/
|
*/
|
||||||
void onSampledOutTrackPoint(@NonNull TrackPoint trackPoint);
|
default void onSampledOutTrackPoint(@NonNull TrackPoint trackPoint, @NonNull TrackStatistics trackStatistics) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when finish sending new track points.
|
* Called when finish sending new track points.
|
||||||
* This gets called after every batch of calls to {@link #onSampledInTrackPoint(TrackPoint)} and {@link #onSampledOutTrackPoint(TrackPoint)}.
|
* This gets called after every batch of calls to {@link #onSampledInTrackPoint(TrackPoint, TrackStatistics, Speed, double)} and {@link #onSampledOutTrackPoint(TrackPoint, TrackStatistics)}.
|
||||||
*/
|
*/
|
||||||
void onNewTrackPointsDone(@NonNull TrackPoint lastTrackPoint);
|
default void onNewTrackPointsDone(@NonNull TrackPoint lastTrackPoint, @NonNull TrackStatistics trackStatistics) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called to clear previously sent markers.
|
* Called to clear previously sent markers.
|
||||||
|
|||||||
@@ -41,10 +41,10 @@ class TrackDataManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void unregisterTrackDataListener(TrackDataListener trackDataListener) {
|
void unregisterTrackDataListener(TrackDataListener trackDataListener) {
|
||||||
dataListenerTracks.add(trackDataListener);
|
dataListenerTracks.remove(trackDataListener);
|
||||||
dataListenerMarkers.add(trackDataListener);
|
dataListenerMarkers.remove(trackDataListener);
|
||||||
dataListenerTrackPoints_SampledIn.add(trackDataListener);
|
dataListenerTrackPoints_SampledIn.remove(trackDataListener);
|
||||||
dataListenerTrackPoints_SampledOut.add(trackDataListener);
|
dataListenerTrackPoints_SampledOut.remove(trackDataListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean hasListeners() {
|
boolean hasListeners() {
|
||||||
|
|||||||
@@ -0,0 +1,140 @@
|
|||||||
|
package de.dennisguse.opentracks.content.data;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.util.UnitConversions;
|
||||||
|
|
||||||
|
public class Distance {
|
||||||
|
|
||||||
|
public static Distance of(double distance_m) {
|
||||||
|
return new Distance(distance_m);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Distance of(String distance_m) {
|
||||||
|
return of(Float.parseFloat(distance_m));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Distance ofMile(double distance_mile) {
|
||||||
|
return of(distance_mile * UnitConversions.MI_TO_M);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Distance ofKilometer(double distance_km) {
|
||||||
|
return of(distance_km * UnitConversions.KM_TO_M);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Distance one(boolean metricUnit) {
|
||||||
|
if (metricUnit) {
|
||||||
|
return Distance.ofKilometer(1);
|
||||||
|
} else {
|
||||||
|
return Distance.ofMile(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Distance invalid() {
|
||||||
|
return of(Double.NaN);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final double distance_m;
|
||||||
|
|
||||||
|
private Distance(double distance_m) {
|
||||||
|
this.distance_m = distance_m;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Distance plus(@NonNull Distance distance) {
|
||||||
|
return new Distance(distance_m + distance.distance_m);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Distance minus(@NonNull Distance distance) {
|
||||||
|
return new Distance(distance_m - distance.distance_m);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Distance multipliedBy(double factor) {
|
||||||
|
return new Distance(factor * distance_m);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Distance dividedBy(double divisor) {
|
||||||
|
return multipliedBy(1 / divisor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double dividedBy(@NonNull Distance divisor) {
|
||||||
|
return distance_m / divisor.distance_m;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isZero() {
|
||||||
|
return distance_m == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInvalid() {
|
||||||
|
return Double.isNaN(distance_m) || Double.isInfinite(distance_m);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean lessThan(@NonNull Distance distance) {
|
||||||
|
return !greaterThan(distance);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean greaterThan(@NonNull Distance distance) {
|
||||||
|
return distance_m > distance.distance_m;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean greaterOrEqualThan(@NonNull Distance distance) {
|
||||||
|
return distance_m >= distance.distance_m;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double toM() {
|
||||||
|
return distance_m;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double toKM() {
|
||||||
|
return distance_m * UnitConversions.M_TO_KM;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double toFT() {
|
||||||
|
return distance_m * UnitConversions.M_TO_FT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double toMI() {
|
||||||
|
return toKM() * UnitConversions.KM_TO_MI;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double to(boolean metricUnit) {
|
||||||
|
return to(metricUnit ? Unit.KM : Unit.MILES);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double to(Unit unit) {
|
||||||
|
switch (unit) {
|
||||||
|
case KM:
|
||||||
|
return toKM();
|
||||||
|
case MILES:
|
||||||
|
return toMI();
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Not implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Unit {
|
||||||
|
KM,
|
||||||
|
MILES
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
Distance distance = (Distance) o;
|
||||||
|
return Double.compare(distance.distance_m, distance_m) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(distance_m);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Distance{" +
|
||||||
|
"distance_m=" + distance_m +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,15 +24,19 @@ import android.os.Parcelable;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NOTE: A marker is indirectly (via it's location) assigned to one {@link TrackPoint} with trackPoint.hasLocation() == true.
|
* NOTE: A marker is indirectly (via it's location) assigned to one {@link TrackPoint} with trackPoint.hasLocation() == true.
|
||||||
*
|
*
|
||||||
* @author Leif Hendrik Wilden
|
* @author Leif Hendrik Wilden
|
||||||
* @author Rodrigo Damazio
|
* @author Rodrigo Damazio
|
||||||
*/
|
*/
|
||||||
|
//TODO All data should be final; no default values.
|
||||||
public final class Marker {
|
public final class Marker {
|
||||||
|
|
||||||
private Id id;
|
private Id id;
|
||||||
@@ -50,8 +54,8 @@ public final class Marker {
|
|||||||
private Float bearing;
|
private Float bearing;
|
||||||
|
|
||||||
//TODO It is the distance from the track starting point; rename to something more meaningful
|
//TODO It is the distance from the track starting point; rename to something more meaningful
|
||||||
private double length = 0.0;
|
private Distance length;
|
||||||
private long duration = 0; //TODO Duration
|
private Duration duration;
|
||||||
|
|
||||||
@Deprecated //TODO Make an URI instead of String
|
@Deprecated //TODO Make an URI instead of String
|
||||||
private String photoUrl = "";
|
private String photoUrl = "";
|
||||||
@@ -61,6 +65,7 @@ public final class Marker {
|
|||||||
this.time = time;
|
this.time = time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated //TODO Marker cannot be created without length AND duration!
|
||||||
public Marker(@Nullable Track.Id trackId, @NonNull TrackPoint trackPoint) {
|
public Marker(@Nullable Track.Id trackId, @NonNull TrackPoint trackPoint) {
|
||||||
this.trackId = trackId;
|
this.trackId = trackId;
|
||||||
|
|
||||||
@@ -74,16 +79,19 @@ public final class Marker {
|
|||||||
if (trackPoint.hasAccuracy()) this.accuracy = trackPoint.getAccuracy();
|
if (trackPoint.hasAccuracy()) this.accuracy = trackPoint.getAccuracy();
|
||||||
if (trackPoint.hasAltitude()) this.altitude_m = trackPoint.getAltitude();
|
if (trackPoint.hasAltitude()) this.altitude_m = trackPoint.getAltitude();
|
||||||
if (trackPoint.hasBearing()) this.bearing = trackPoint.getBearing();
|
if (trackPoint.hasBearing()) this.bearing = trackPoint.getBearing();
|
||||||
|
|
||||||
|
this.length = Distance.of(0); //TODO Not cool!
|
||||||
|
this.duration = Duration.ofMillis(0); //TODO Not cool!
|
||||||
}
|
}
|
||||||
|
|
||||||
public Marker(String name, String description, String category, String icon, @NonNull Track.Id trackId, double length, long duration, @NonNull TrackPoint trackPoint, String photoUrl) {
|
public Marker(String name, String description, String category, String icon, @NonNull Track.Id trackId, @NonNull TrackStatistics statistics, @NonNull TrackPoint trackPoint, String photoUrl) {
|
||||||
this(trackId, trackPoint);
|
this(trackId, trackPoint);
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.category = category;
|
this.category = category;
|
||||||
this.icon = icon;
|
this.icon = icon;
|
||||||
this.length = length;
|
this.length = statistics.getTotalDistance();
|
||||||
this.duration = duration;
|
this.duration = statistics.getTotalTime();
|
||||||
this.photoUrl = photoUrl;
|
this.photoUrl = photoUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,19 +225,19 @@ public final class Marker {
|
|||||||
this.bearing = bearing;
|
this.bearing = bearing;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getLength() {
|
public Distance getLength() {
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLength(double length) {
|
public void setLength(Distance length) {
|
||||||
this.length = length;
|
this.length = length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getDuration() {
|
public Duration getDuration() {
|
||||||
return duration;
|
return duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDuration(long duration) {
|
public void setDuration(@NonNull Duration duration) {
|
||||||
this.duration = duration;
|
this.duration = duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ public interface MarkerColumns extends BaseColumns {
|
|||||||
String LONGITUDE = "longitude"; // longitude
|
String LONGITUDE = "longitude"; // longitude
|
||||||
String LATITUDE = "latitude"; // latitude
|
String LATITUDE = "latitude"; // latitude
|
||||||
String TIME = "time"; // time
|
String TIME = "time"; // time
|
||||||
String ALTITUDE = "elevation"; // altitude
|
String ALTITUDE = "elevation"; // altitude //TODO RENAME column
|
||||||
String ACCURACY = "accuracy"; // accuracy
|
String ACCURACY = "accuracy"; // accuracy
|
||||||
String BEARING = "bearing"; // bearing
|
String BEARING = "bearing"; // bearing
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,145 @@
|
|||||||
|
package de.dennisguse.opentracks.content.data;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.util.UnitConversions;
|
||||||
|
|
||||||
|
public class Speed {
|
||||||
|
|
||||||
|
public static Speed of(Distance distance, Duration duration) {
|
||||||
|
if (duration.isZero()) {
|
||||||
|
return zero();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Speed(distance.toM() / (duration.toMillis() * UnitConversions.MS_TO_S));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Speed of(double speed_mps) {
|
||||||
|
return new Speed(speed_mps);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Speed of(String speed_mps) {
|
||||||
|
return of(Float.parseFloat(speed_mps));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Speed invalid() {
|
||||||
|
return of(Double.NaN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Speed zero() {
|
||||||
|
return of(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Speed max(Speed speed1, Speed speed2) {
|
||||||
|
if (speed1.greaterThan(speed2)) {
|
||||||
|
return speed1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return speed2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Speed absDiff(Speed speed1, Speed speed2) {
|
||||||
|
//TODO Why Math.abs? Seems to be a leftover.
|
||||||
|
return Speed.of(Math.abs(speed1.speed_mps - speed2.speed_mps));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Anything faster than that (in meters per second) will be considered moving.
|
||||||
|
private static final double MAX_NO_MOVEMENT_SPEED = 0.224;
|
||||||
|
|
||||||
|
private final double speed_mps;
|
||||||
|
|
||||||
|
private Speed(double speed_mps) {
|
||||||
|
this.speed_mps = speed_mps;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Speed mul(double factor) {
|
||||||
|
return new Speed(factor * speed_mps);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isZero() {
|
||||||
|
return speed_mps == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInvalid() {
|
||||||
|
return Double.isNaN(speed_mps) || Double.isInfinite(speed_mps);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMoving() {
|
||||||
|
return !isInvalid() && speed_mps >= MAX_NO_MOVEMENT_SPEED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean lessThan(Speed speed) {
|
||||||
|
return !greaterThan(speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean greaterThan(Speed speed) {
|
||||||
|
return speed_mps > speed.speed_mps;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean greaterOrEqualThan(Speed speed) {
|
||||||
|
return speed_mps >= speed.speed_mps;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double toMPS() {
|
||||||
|
return speed_mps;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double toKMH() {
|
||||||
|
return speed_mps * UnitConversions.MPS_TO_KMH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double toMPH() {
|
||||||
|
return toKMH() * UnitConversions.KM_TO_MI;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Duration toPace(boolean metricUnit) {
|
||||||
|
if (isZero()) {
|
||||||
|
return Duration.ofSeconds(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
double distance = speed_mps * (metricUnit ? UnitConversions.M_TO_KM : UnitConversions.M_TO_MI);
|
||||||
|
return Duration.ofSeconds(Math.round(1 / distance));
|
||||||
|
}
|
||||||
|
|
||||||
|
public double to(boolean metricUnit) {
|
||||||
|
return to(metricUnit ? Unit.KMH : Unit.MPH);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double to(Unit unit) {
|
||||||
|
switch (unit) {
|
||||||
|
case KMH:
|
||||||
|
return toKMH();
|
||||||
|
case MPH:
|
||||||
|
return toMPH();
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Not implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Unit {
|
||||||
|
KMH,
|
||||||
|
MPH,
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
Speed speed = (Speed) o;
|
||||||
|
return Double.compare(speed.speed_mps, speed_mps) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(speed_mps);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Speed{" +
|
||||||
|
"speed_mps=" + speed_mps +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -101,14 +101,28 @@ public class Track {
|
|||||||
this.icon = icon;
|
this.icon = icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
public TrackStatistics getTrackStatistics() {
|
public TrackStatistics getTrackStatistics() {
|
||||||
return trackStatistics;
|
return trackStatistics;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTrackStatistics(TrackStatistics trackStatistics) {
|
public void setTrackStatistics(@NonNull TrackStatistics trackStatistics) {
|
||||||
this.trackStatistics = trackStatistics;
|
this.trackStatistics = trackStatistics;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
Track track = (Track) o;
|
||||||
|
return id.equals(track.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(id);
|
||||||
|
}
|
||||||
|
|
||||||
public static class Id implements Parcelable {
|
public static class Id implements Parcelable {
|
||||||
|
|
||||||
private final long id;
|
private final long id;
|
||||||
|
|||||||
@@ -45,9 +45,6 @@ public class TrackPoint {
|
|||||||
|
|
||||||
private static final Duration MAX_LOCATION_AGE = Duration.ofMinutes(1);
|
private static final Duration MAX_LOCATION_AGE = Duration.ofMinutes(1);
|
||||||
|
|
||||||
// Anything faster than that (in meters per second) will be considered moving.
|
|
||||||
private static final double MAX_NO_MOVEMENT_SPEED = 0.224;
|
|
||||||
|
|
||||||
private TrackPoint.Id id;
|
private TrackPoint.Id id;
|
||||||
|
|
||||||
private Instant time;
|
private Instant time;
|
||||||
@@ -55,8 +52,9 @@ public class TrackPoint {
|
|||||||
private Double longitude;
|
private Double longitude;
|
||||||
private Float accuracy;
|
private Float accuracy;
|
||||||
private Double altitude_m;
|
private Double altitude_m;
|
||||||
private Float speed_mps;
|
private Speed speed;
|
||||||
private Float bearing;
|
private Float bearing;
|
||||||
|
private Distance sensorDistance_m;
|
||||||
|
|
||||||
public enum Type {
|
public enum Type {
|
||||||
SEGMENT_START_MANUAL(-2), //Start of a segment due to user interaction (start, resume)
|
SEGMENT_START_MANUAL(-2), //Start of a segment due to user interaction (start, resume)
|
||||||
@@ -92,8 +90,8 @@ public class TrackPoint {
|
|||||||
private Float heartRate_bpm = null;
|
private Float heartRate_bpm = null;
|
||||||
private Float cyclingCadence_rpm = null;
|
private Float cyclingCadence_rpm = null;
|
||||||
private Float power = null;
|
private Float power = null;
|
||||||
private Float elevationGain = null;
|
private Float altitudeGain_m = null;
|
||||||
private Float elevationLoss = null;
|
private Float altitudeLoss_m = null;
|
||||||
|
|
||||||
public TrackPoint(@NonNull Type type) {
|
public TrackPoint(@NonNull Type type) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
@@ -105,7 +103,7 @@ public class TrackPoint {
|
|||||||
this.latitude = location.getLatitude();
|
this.latitude = location.getLatitude();
|
||||||
this.longitude = location.getLongitude();
|
this.longitude = location.getLongitude();
|
||||||
this.altitude_m = location.getAltitude();
|
this.altitude_m = location.getAltitude();
|
||||||
this.speed_mps = location.getSpeed();
|
this.speed = Speed.of(location.getSpeed());
|
||||||
this.accuracy = location.getAccuracy();
|
this.accuracy = location.getAccuracy();
|
||||||
|
|
||||||
setTime(Instant.now());
|
setTime(Instant.now());
|
||||||
@@ -220,28 +218,28 @@ public class TrackPoint {
|
|||||||
return location;
|
return location;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasElevationGain() {
|
public boolean hasAltitudeGain() {
|
||||||
return elevationGain != null;
|
return altitudeGain_m != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Float getElevationGain() {
|
public float getAltitudeGain() {
|
||||||
return elevationGain;
|
return altitudeGain_m;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setElevationGain(Float elevationGain) {
|
public void setAltitudeGain(Float altitudeGain_m) {
|
||||||
this.elevationGain = elevationGain;
|
this.altitudeGain_m = altitudeGain_m;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasElevationLoss() {
|
public boolean hasAltitudeLoss() {
|
||||||
return elevationLoss != null;
|
return altitudeLoss_m != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getElevationLoss() {
|
public float getAltitudeLoss() {
|
||||||
return elevationLoss;
|
return altitudeLoss_m;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setElevationLoss(Float elevationLoss) {
|
public void setAltitudeLoss(Float altitudeLoss_m) {
|
||||||
this.elevationLoss = elevationLoss;
|
this.altitudeLoss_m = altitudeLoss_m;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Instant getTime() {
|
public Instant getTime() {
|
||||||
@@ -271,19 +269,19 @@ public class TrackPoint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasSpeed() {
|
public boolean hasSpeed() {
|
||||||
return speed_mps != null;
|
return speed != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getSpeed() {
|
public Speed getSpeed() {
|
||||||
return speed_mps;
|
return speed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSpeed(Float speed) {
|
public void setSpeed(Speed speed) {
|
||||||
this.speed_mps = speed;
|
this.speed = speed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isMoving() {
|
public boolean isMoving() {
|
||||||
return hasSpeed() && getSpeed() >= MAX_NO_MOVEMENT_SPEED;
|
return hasSpeed() && getSpeed().isMoving();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasBearing() {
|
public boolean hasBearing() {
|
||||||
@@ -310,8 +308,12 @@ public class TrackPoint {
|
|||||||
this.accuracy = horizontalAccuracy;
|
this.accuracy = horizontalAccuracy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float distanceTo(@NonNull TrackPoint dest) {
|
public Distance distanceToPrevious(@NonNull TrackPoint previous) {
|
||||||
return getLocation().distanceTo(dest.getLocation());
|
if (hasSensorDistance()) {
|
||||||
|
return getSensorDistance();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Distance.of(getLocation().distanceTo(previous.getLocation()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean fulfillsAccuracy(int poorAccuracy) {
|
public boolean fulfillsAccuracy(int poorAccuracy) {
|
||||||
@@ -327,6 +329,18 @@ public class TrackPoint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sensor data
|
// Sensor data
|
||||||
|
public boolean hasSensorDistance() {
|
||||||
|
return sensorDistance_m != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Distance getSensorDistance() {
|
||||||
|
return sensorDistance_m;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSensorDistance(Distance distance_m) {
|
||||||
|
this.sensorDistance_m = distance_m;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasSensorData() {
|
public boolean hasSensorData() {
|
||||||
return hasHeartRate() || hasCyclingCadence() || hasPower();
|
return hasHeartRate() || hasCyclingCadence() || hasPower();
|
||||||
}
|
}
|
||||||
@@ -347,7 +361,7 @@ public class TrackPoint {
|
|||||||
return cyclingCadence_rpm != null;
|
return cyclingCadence_rpm != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Float getCyclingCadence_rpm() {
|
public float getCyclingCadence_rpm() {
|
||||||
return cyclingCadence_rpm;
|
return cyclingCadence_rpm;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,7 +373,7 @@ public class TrackPoint {
|
|||||||
return power != null;
|
return power != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Float getPower() {
|
public float getPower() {
|
||||||
return power;
|
return power;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,9 +50,10 @@ public interface TrackPointsColumns extends BaseColumns {
|
|||||||
String BEARING = "bearing";
|
String BEARING = "bearing";
|
||||||
String SENSOR_HEARTRATE = "sensor_heartrate";
|
String SENSOR_HEARTRATE = "sensor_heartrate";
|
||||||
String SENSOR_CADENCE = "sensor_cadence";
|
String SENSOR_CADENCE = "sensor_cadence";
|
||||||
|
String SENSOR_DISTANCE = "sensor_distance"; //DISTANCE from previous TrackPoint
|
||||||
String SENSOR_POWER = "sensor_power";
|
String SENSOR_POWER = "sensor_power";
|
||||||
String ELEVATION_GAIN = "elevation_gain";
|
String ALTITUDE_GAIN = "elevation_gain";
|
||||||
String ELEVATION_LOSS = "elevation_loss";
|
String ALTITUDE_LOSS = "elevation_loss";
|
||||||
|
|
||||||
// Alias for sensor statistics
|
// Alias for sensor statistics
|
||||||
String ALIAS_AVG_HR = "avg_hr";
|
String ALIAS_AVG_HR = "avg_hr";
|
||||||
@@ -74,9 +75,10 @@ public interface TrackPointsColumns extends BaseColumns {
|
|||||||
+ SENSOR_HEARTRATE + " FLOAT, "
|
+ SENSOR_HEARTRATE + " FLOAT, "
|
||||||
+ SENSOR_CADENCE + " FLOAT, "
|
+ SENSOR_CADENCE + " FLOAT, "
|
||||||
+ SENSOR_POWER + " FLOAT, "
|
+ SENSOR_POWER + " FLOAT, "
|
||||||
+ ELEVATION_GAIN + " FLOAT, "
|
+ ALTITUDE_GAIN + " FLOAT, "
|
||||||
+ ELEVATION_LOSS + " FLOAT, "
|
+ ALTITUDE_LOSS + " FLOAT, "
|
||||||
+ TYPE + " TEXT CHECK(type IN (-2, -1, 0, 1)), "
|
+ TYPE + " TEXT CHECK(type IN (-2, -1, 0, 1)), "
|
||||||
|
+ SENSOR_DISTANCE + " FLOAT, "
|
||||||
+ "FOREIGN KEY (" + TRACKID + ") REFERENCES " + TracksColumns.TABLE_NAME + "(" + TracksColumns._ID + ") ON UPDATE CASCADE ON DELETE CASCADE"
|
+ "FOREIGN KEY (" + TRACKID + ") REFERENCES " + TracksColumns.TABLE_NAME + "(" + TracksColumns._ID + ") ON UPDATE CASCADE ON DELETE CASCADE"
|
||||||
+ ")";
|
+ ")";
|
||||||
|
|
||||||
|
|||||||
@@ -52,10 +52,10 @@ public interface TracksColumns extends BaseColumns {
|
|||||||
String AVGSPEED = "avgspeed"; // average speed
|
String AVGSPEED = "avgspeed"; // average speed
|
||||||
String AVGMOVINGSPEED = "avgmovingspeed"; // average moving speed
|
String AVGMOVINGSPEED = "avgmovingspeed"; // average moving speed
|
||||||
String MAXSPEED = "maxspeed"; // maximum speed
|
String MAXSPEED = "maxspeed"; // maximum speed
|
||||||
String MINELEVATION = "minelevation"; // minimum elevation
|
String MIN_ALTITUDE = "minelevation"; // minimum altitude //TODO RENAME column
|
||||||
String MAXELEVATION = "maxelevation"; // maximum elevation
|
String MAX_ALTITUDE = "maxelevation"; // maximum altitude //TODO RENAME column
|
||||||
String ELEVATIONGAIN = "elevationgain"; // elevation gain
|
String ALTITUDE_GAIN = "elevationgain"; // altitude gain //TODO RENAME column
|
||||||
String ELEVATIONLOSS = "elevationloss"; // elevation loss
|
String ALTITUDE_LOSS = "elevationloss"; // altitude loss //TODO RENAME column
|
||||||
String ICON = "icon"; // track activity type icon
|
String ICON = "icon"; // track activity type icon
|
||||||
|
|
||||||
String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " ("
|
String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " ("
|
||||||
@@ -72,12 +72,12 @@ public interface TracksColumns extends BaseColumns {
|
|||||||
+ AVGSPEED + " FLOAT, "
|
+ AVGSPEED + " FLOAT, "
|
||||||
+ AVGMOVINGSPEED + " FLOAT, "
|
+ AVGMOVINGSPEED + " FLOAT, "
|
||||||
+ MAXSPEED + " FLOAT, "
|
+ MAXSPEED + " FLOAT, "
|
||||||
+ MINELEVATION + " FLOAT, "
|
+ MIN_ALTITUDE + " FLOAT, "
|
||||||
+ MAXELEVATION + " FLOAT, "
|
+ MAX_ALTITUDE + " FLOAT, "
|
||||||
+ ELEVATIONGAIN + " FLOAT, "
|
+ ALTITUDE_GAIN + " FLOAT, "
|
||||||
+ ICON + " TEXT, "
|
+ ICON + " TEXT, "
|
||||||
+ UUID + " BLOB, "
|
+ UUID + " BLOB, "
|
||||||
+ ELEVATIONLOSS + " FLOAT)";
|
+ ALTITUDE_LOSS + " FLOAT)";
|
||||||
|
|
||||||
String CREATE_TABLE_INDEX = "CREATE UNIQUE INDEX " + TABLE_NAME + "_" + UUID + "_index ON " + TABLE_NAME + "(" + UUID + ")";
|
String CREATE_TABLE_INDEX = "CREATE UNIQUE INDEX " + TABLE_NAME + "_" + UUID + "_index ON " + TABLE_NAME + "(" + UUID + ")";
|
||||||
|
|
||||||
|
|||||||
+6
-4
@@ -19,9 +19,10 @@ class CachedTrackPointsIndexes {
|
|||||||
final int bearingIndex;
|
final int bearingIndex;
|
||||||
final int sensorHeartRateIndex;
|
final int sensorHeartRateIndex;
|
||||||
final int sensorCadenceIndex;
|
final int sensorCadenceIndex;
|
||||||
|
final int sensorDistanceIndex;
|
||||||
final int sensorPowerIndex;
|
final int sensorPowerIndex;
|
||||||
final int elevationGainIndex;
|
final int altitudeGainIndex;
|
||||||
final int elevationLossIndex;
|
final int altitudeLossIndex;
|
||||||
|
|
||||||
CachedTrackPointsIndexes(Cursor cursor) {
|
CachedTrackPointsIndexes(Cursor cursor) {
|
||||||
idIndex = cursor.getColumnIndex(TrackPointsColumns._ID);
|
idIndex = cursor.getColumnIndex(TrackPointsColumns._ID);
|
||||||
@@ -35,8 +36,9 @@ class CachedTrackPointsIndexes {
|
|||||||
bearingIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.BEARING);
|
bearingIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.BEARING);
|
||||||
sensorHeartRateIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_HEARTRATE);
|
sensorHeartRateIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_HEARTRATE);
|
||||||
sensorCadenceIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_CADENCE);
|
sensorCadenceIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_CADENCE);
|
||||||
|
sensorDistanceIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_DISTANCE);
|
||||||
sensorPowerIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_POWER);
|
sensorPowerIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.SENSOR_POWER);
|
||||||
elevationGainIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.ELEVATION_GAIN);
|
altitudeGainIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.ALTITUDE_GAIN);
|
||||||
elevationLossIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.ELEVATION_LOSS);
|
altitudeLossIndex = cursor.getColumnIndexOrThrow(TrackPointsColumns.ALTITUDE_LOSS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,8 +39,10 @@ import java.util.List;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.BuildConfig;
|
import de.dennisguse.opentracks.BuildConfig;
|
||||||
|
import de.dennisguse.opentracks.content.data.Distance;
|
||||||
import de.dennisguse.opentracks.content.data.Marker;
|
import de.dennisguse.opentracks.content.data.Marker;
|
||||||
import de.dennisguse.opentracks.content.data.MarkerColumns;
|
import de.dennisguse.opentracks.content.data.MarkerColumns;
|
||||||
|
import de.dennisguse.opentracks.content.data.Speed;
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
|
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
|
||||||
@@ -70,7 +72,6 @@ public class ContentProviderUtils {
|
|||||||
private static final String ID_SEPARATOR = ",";
|
private static final String ID_SEPARATOR = ",";
|
||||||
|
|
||||||
private final ContentResolver contentResolver;
|
private final ContentResolver contentResolver;
|
||||||
private int defaultCursorBatchSize = 2000;
|
|
||||||
|
|
||||||
public ContentProviderUtils(Context context) {
|
public ContentProviderUtils(Context context) {
|
||||||
contentResolver = context.getContentResolver();
|
contentResolver = context.getContentResolver();
|
||||||
@@ -98,10 +99,10 @@ public class ContentProviderUtils {
|
|||||||
int totalTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.TOTALTIME);
|
int totalTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.TOTALTIME);
|
||||||
int movingTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.MOVINGTIME);
|
int movingTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.MOVINGTIME);
|
||||||
int maxSpeedIndex = cursor.getColumnIndexOrThrow(TracksColumns.MAXSPEED);
|
int maxSpeedIndex = cursor.getColumnIndexOrThrow(TracksColumns.MAXSPEED);
|
||||||
int minElevationIndex = cursor.getColumnIndexOrThrow(TracksColumns.MINELEVATION);
|
int minAltitudeIndex = cursor.getColumnIndexOrThrow(TracksColumns.MIN_ALTITUDE);
|
||||||
int maxElevationIndex = cursor.getColumnIndexOrThrow(TracksColumns.MAXELEVATION);
|
int maxAltitudeIndex = cursor.getColumnIndexOrThrow(TracksColumns.MAX_ALTITUDE);
|
||||||
int elevationGainIndex = cursor.getColumnIndexOrThrow(TracksColumns.ELEVATIONGAIN);
|
int altitudeGainIndex = cursor.getColumnIndexOrThrow(TracksColumns.ALTITUDE_GAIN);
|
||||||
int elevationLossIndex = cursor.getColumnIndexOrThrow(TracksColumns.ELEVATIONLOSS);
|
int altitudeLossIndex = cursor.getColumnIndexOrThrow(TracksColumns.ALTITUDE_LOSS);
|
||||||
int iconIndex = cursor.getColumnIndexOrThrow(TracksColumns.ICON);
|
int iconIndex = cursor.getColumnIndexOrThrow(TracksColumns.ICON);
|
||||||
|
|
||||||
Track track = new Track();
|
Track track = new Track();
|
||||||
@@ -128,7 +129,7 @@ public class ContentProviderUtils {
|
|||||||
trackStatistics.setStopTime(Instant.ofEpochMilli(cursor.getLong(stopTimeIndex)));
|
trackStatistics.setStopTime(Instant.ofEpochMilli(cursor.getLong(stopTimeIndex)));
|
||||||
}
|
}
|
||||||
if (!cursor.isNull(totalDistanceIndex)) {
|
if (!cursor.isNull(totalDistanceIndex)) {
|
||||||
trackStatistics.setTotalDistance(cursor.getFloat(totalDistanceIndex));
|
trackStatistics.setTotalDistance(Distance.of(cursor.getFloat(totalDistanceIndex)));
|
||||||
}
|
}
|
||||||
if (!cursor.isNull(totalTimeIndex)) {
|
if (!cursor.isNull(totalTimeIndex)) {
|
||||||
trackStatistics.setTotalTime(Duration.ofMillis(cursor.getLong(totalTimeIndex)));
|
trackStatistics.setTotalTime(Duration.ofMillis(cursor.getLong(totalTimeIndex)));
|
||||||
@@ -137,19 +138,19 @@ public class ContentProviderUtils {
|
|||||||
trackStatistics.setMovingTime(Duration.ofMillis(cursor.getLong(movingTimeIndex)));
|
trackStatistics.setMovingTime(Duration.ofMillis(cursor.getLong(movingTimeIndex)));
|
||||||
}
|
}
|
||||||
if (!cursor.isNull(maxSpeedIndex)) {
|
if (!cursor.isNull(maxSpeedIndex)) {
|
||||||
trackStatistics.setMaxSpeed(cursor.getFloat(maxSpeedIndex));
|
trackStatistics.setMaxSpeed(Speed.of(cursor.getFloat(maxSpeedIndex)));
|
||||||
}
|
}
|
||||||
if (!cursor.isNull(minElevationIndex)) {
|
if (!cursor.isNull(minAltitudeIndex)) {
|
||||||
trackStatistics.setMinElevation(cursor.getFloat(minElevationIndex));
|
trackStatistics.setMinAltitude(cursor.getFloat(minAltitudeIndex));
|
||||||
}
|
}
|
||||||
if (!cursor.isNull(maxElevationIndex)) {
|
if (!cursor.isNull(maxAltitudeIndex)) {
|
||||||
trackStatistics.setMaxElevation(cursor.getFloat(maxElevationIndex));
|
trackStatistics.setMaxAltitude(cursor.getFloat(maxAltitudeIndex));
|
||||||
}
|
}
|
||||||
if (!cursor.isNull(elevationGainIndex)) {
|
if (!cursor.isNull(altitudeGainIndex)) {
|
||||||
trackStatistics.setTotalElevationGain(cursor.getFloat(elevationGainIndex));
|
trackStatistics.setTotalAltitudeGain(cursor.getFloat(altitudeGainIndex));
|
||||||
}
|
}
|
||||||
if (!cursor.isNull(elevationLossIndex)) {
|
if (!cursor.isNull(altitudeLossIndex)) {
|
||||||
trackStatistics.setTotalElevationLoss(cursor.getFloat(elevationLossIndex));
|
trackStatistics.setTotalAltitudeLoss(cursor.getFloat(altitudeLossIndex));
|
||||||
}
|
}
|
||||||
if (!cursor.isNull(iconIndex)) {
|
if (!cursor.isNull(iconIndex)) {
|
||||||
track.setIcon(cursor.getString(iconIndex));
|
track.setIcon(cursor.getString(iconIndex));
|
||||||
@@ -176,7 +177,7 @@ public class ContentProviderUtils {
|
|||||||
|
|
||||||
// Delete track last since it triggers a database vacuum call
|
// Delete track last since it triggers a database vacuum call
|
||||||
String whereClause = String.format(TracksColumns._ID + " IN (%s)", TextUtils.join(",", Collections.nCopies(trackIds.size(), "?")));
|
String whereClause = String.format(TracksColumns._ID + " IN (%s)", TextUtils.join(",", Collections.nCopies(trackIds.size(), "?")));
|
||||||
contentResolver.delete(TracksColumns.CONTENT_URI, whereClause, trackIds.stream().map(id->Long.toString(id.getId())).toArray(String[]::new));
|
contentResolver.delete(TracksColumns.CONTENT_URI, whereClause, trackIds.stream().map(id -> Long.toString(id.getId())).toArray(String[]::new));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteTrack(Context context, @NonNull Track.Id trackId) {
|
public void deleteTrack(Context context, @NonNull Track.Id trackId) {
|
||||||
@@ -279,16 +280,16 @@ public class ContentProviderUtils {
|
|||||||
if (trackStatistics.getStopTime() != null) {
|
if (trackStatistics.getStopTime() != null) {
|
||||||
values.put(TracksColumns.STOPTIME, trackStatistics.getStopTime().toEpochMilli());
|
values.put(TracksColumns.STOPTIME, trackStatistics.getStopTime().toEpochMilli());
|
||||||
}
|
}
|
||||||
values.put(TracksColumns.TOTALDISTANCE, trackStatistics.getTotalDistance());
|
values.put(TracksColumns.TOTALDISTANCE, trackStatistics.getTotalDistance().toM());
|
||||||
values.put(TracksColumns.TOTALTIME, trackStatistics.getTotalTime().toMillis());
|
values.put(TracksColumns.TOTALTIME, trackStatistics.getTotalTime().toMillis());
|
||||||
values.put(TracksColumns.MOVINGTIME, trackStatistics.getMovingTime().toMillis());
|
values.put(TracksColumns.MOVINGTIME, trackStatistics.getMovingTime().toMillis());
|
||||||
values.put(TracksColumns.AVGSPEED, trackStatistics.getAverageSpeed());
|
values.put(TracksColumns.AVGSPEED, trackStatistics.getAverageSpeed().toMPS());
|
||||||
values.put(TracksColumns.AVGMOVINGSPEED, trackStatistics.getAverageMovingSpeed());
|
values.put(TracksColumns.AVGMOVINGSPEED, trackStatistics.getAverageMovingSpeed().toMPS());
|
||||||
values.put(TracksColumns.MAXSPEED, trackStatistics.getMaxSpeed());
|
values.put(TracksColumns.MAXSPEED, trackStatistics.getMaxSpeed().toMPS());
|
||||||
values.put(TracksColumns.MINELEVATION, trackStatistics.getMinElevation());
|
values.put(TracksColumns.MIN_ALTITUDE, trackStatistics.getMinAltitude());
|
||||||
values.put(TracksColumns.MAXELEVATION, trackStatistics.getMaxElevation());
|
values.put(TracksColumns.MAX_ALTITUDE, trackStatistics.getMaxAltitude());
|
||||||
values.put(TracksColumns.ELEVATIONGAIN, trackStatistics.getTotalElevationGain());
|
values.put(TracksColumns.ALTITUDE_GAIN, trackStatistics.getTotalAltitudeGain());
|
||||||
values.put(TracksColumns.ELEVATIONLOSS, trackStatistics.getTotalElevationLoss());
|
values.put(TracksColumns.ALTITUDE_LOSS, trackStatistics.getTotalAltitudeLoss());
|
||||||
values.put(TracksColumns.ICON, track.getIcon());
|
values.put(TracksColumns.ICON, track.getIcon());
|
||||||
|
|
||||||
return values;
|
return values;
|
||||||
@@ -344,10 +345,10 @@ public class ContentProviderUtils {
|
|||||||
marker.setIcon(cursor.getString(iconIndex));
|
marker.setIcon(cursor.getString(iconIndex));
|
||||||
}
|
}
|
||||||
if (!cursor.isNull(lengthIndex)) {
|
if (!cursor.isNull(lengthIndex)) {
|
||||||
marker.setLength(cursor.getFloat(lengthIndex));
|
marker.setLength(Distance.of(cursor.getFloat(lengthIndex)));
|
||||||
}
|
}
|
||||||
if (!cursor.isNull(durationIndex)) {
|
if (!cursor.isNull(durationIndex)) {
|
||||||
marker.setDuration(cursor.getLong(durationIndex));
|
marker.setDuration(Duration.ofMillis(cursor.getLong(durationIndex)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cursor.isNull(photoUrlIndex)) {
|
if (!cursor.isNull(photoUrlIndex)) {
|
||||||
@@ -363,9 +364,9 @@ public class ContentProviderUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return -1 if not able to get the next marker number.
|
* @return null if not able to get the next marker number.
|
||||||
*/
|
*/
|
||||||
public int getNextMarkerNumber(@NonNull Track.Id trackId) {
|
public Integer getNextMarkerNumber(@NonNull Track.Id trackId) {
|
||||||
String[] projection = {MarkerColumns._ID};
|
String[] projection = {MarkerColumns._ID};
|
||||||
String selection = MarkerColumns.TRACKID + "=?";
|
String selection = MarkerColumns.TRACKID + "=?";
|
||||||
String[] selectionArgs = new String[]{Long.toString(trackId.getId())};
|
String[] selectionArgs = new String[]{Long.toString(trackId.getId())};
|
||||||
@@ -374,7 +375,7 @@ public class ContentProviderUtils {
|
|||||||
return cursor.getCount();
|
return cursor.getCount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Marker getMarker(@NonNull Marker.Id markerId) {
|
public Marker getMarker(@NonNull Marker.Id markerId) {
|
||||||
@@ -481,8 +482,8 @@ public class ContentProviderUtils {
|
|||||||
values.put(MarkerColumns.CATEGORY, marker.getCategory());
|
values.put(MarkerColumns.CATEGORY, marker.getCategory());
|
||||||
values.put(MarkerColumns.ICON, marker.getIcon());
|
values.put(MarkerColumns.ICON, marker.getIcon());
|
||||||
values.put(MarkerColumns.TRACKID, marker.getTrackId().getId());
|
values.put(MarkerColumns.TRACKID, marker.getTrackId().getId());
|
||||||
values.put(MarkerColumns.LENGTH, marker.getLength());
|
values.put(MarkerColumns.LENGTH, marker.getLength().toM());
|
||||||
values.put(MarkerColumns.DURATION, marker.getDuration());
|
values.put(MarkerColumns.DURATION, marker.getDuration().toMillis());
|
||||||
|
|
||||||
values.put(MarkerColumns.LONGITUDE, (int) (marker.getLongitude() * 1E6));
|
values.put(MarkerColumns.LONGITUDE, (int) (marker.getLongitude() * 1E6));
|
||||||
values.put(MarkerColumns.LATITUDE, (int) (marker.getLatitude() * 1E6));
|
values.put(MarkerColumns.LATITUDE, (int) (marker.getLatitude() * 1E6));
|
||||||
@@ -544,7 +545,7 @@ public class ContentProviderUtils {
|
|||||||
trackPoint.setAccuracy(cursor.getFloat(indexes.accuracyIndex));
|
trackPoint.setAccuracy(cursor.getFloat(indexes.accuracyIndex));
|
||||||
}
|
}
|
||||||
if (!cursor.isNull(indexes.speedIndex)) {
|
if (!cursor.isNull(indexes.speedIndex)) {
|
||||||
trackPoint.setSpeed(cursor.getFloat(indexes.speedIndex));
|
trackPoint.setSpeed(Speed.of(cursor.getFloat(indexes.speedIndex)));
|
||||||
}
|
}
|
||||||
if (!cursor.isNull(indexes.bearingIndex)) {
|
if (!cursor.isNull(indexes.bearingIndex)) {
|
||||||
trackPoint.setBearing(cursor.getFloat(indexes.bearingIndex));
|
trackPoint.setBearing(cursor.getFloat(indexes.bearingIndex));
|
||||||
@@ -556,15 +557,18 @@ public class ContentProviderUtils {
|
|||||||
if (!cursor.isNull(indexes.sensorCadenceIndex)) {
|
if (!cursor.isNull(indexes.sensorCadenceIndex)) {
|
||||||
trackPoint.setCyclingCadence_rpm(cursor.getFloat(indexes.sensorCadenceIndex));
|
trackPoint.setCyclingCadence_rpm(cursor.getFloat(indexes.sensorCadenceIndex));
|
||||||
}
|
}
|
||||||
|
if (!cursor.isNull(indexes.sensorDistanceIndex)) {
|
||||||
|
trackPoint.setSensorDistance(Distance.of(cursor.getFloat(indexes.sensorDistanceIndex)));
|
||||||
|
}
|
||||||
if (!cursor.isNull(indexes.sensorPowerIndex)) {
|
if (!cursor.isNull(indexes.sensorPowerIndex)) {
|
||||||
trackPoint.setPower(cursor.getFloat(indexes.sensorPowerIndex));
|
trackPoint.setPower(cursor.getFloat(indexes.sensorPowerIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cursor.isNull(indexes.elevationGainIndex)) {
|
if (!cursor.isNull(indexes.altitudeGainIndex)) {
|
||||||
trackPoint.setElevationGain(cursor.getFloat(indexes.elevationGainIndex));
|
trackPoint.setAltitudeGain(cursor.getFloat(indexes.altitudeGainIndex));
|
||||||
}
|
}
|
||||||
if (!cursor.isNull(indexes.elevationLossIndex)) {
|
if (!cursor.isNull(indexes.altitudeLossIndex)) {
|
||||||
trackPoint.setElevationLoss(cursor.getFloat(indexes.elevationLossIndex));
|
trackPoint.setAltitudeLoss(cursor.getFloat(indexes.altitudeLossIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
return trackPoint;
|
return trackPoint;
|
||||||
@@ -629,9 +633,9 @@ public class ContentProviderUtils {
|
|||||||
*
|
*
|
||||||
* @param trackId the track id
|
* @param trackId the track id
|
||||||
* @param startTrackPointId the starting trackPoint id. `null` to ignore
|
* @param startTrackPointId the starting trackPoint id. `null` to ignore
|
||||||
* @param maxLocations maximum number of locations to return. `null` for no limit
|
|
||||||
*/
|
*/
|
||||||
public Cursor getTrackPointCursor(Track.Id trackId, TrackPoint.Id startTrackPointId, Integer maxLocations) {
|
@NonNull
|
||||||
|
public Cursor getTrackPointCursor(@NonNull Track.Id trackId, TrackPoint.Id startTrackPointId) {
|
||||||
String selection;
|
String selection;
|
||||||
String[] selectionArgs;
|
String[] selectionArgs;
|
||||||
if (startTrackPointId != null) {
|
if (startTrackPointId != null) {
|
||||||
@@ -642,11 +646,7 @@ public class ContentProviderUtils {
|
|||||||
selectionArgs = new String[]{Long.toString(trackId.getId())};
|
selectionArgs = new String[]{Long.toString(trackId.getId())};
|
||||||
}
|
}
|
||||||
|
|
||||||
String sortOrder = TrackPointsColumns.DEFAULT_SORT_ORDER;
|
return getTrackPointCursor(null, selection, selectionArgs, TrackPointsColumns.DEFAULT_SORT_ORDER);
|
||||||
if (maxLocations != null) {
|
|
||||||
sortOrder += " LIMIT " + maxLocations;
|
|
||||||
}
|
|
||||||
return getTrackPointCursor(null, selection, selectionArgs, sortOrder);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -696,7 +696,7 @@ public class ContentProviderUtils {
|
|||||||
values.put(TrackPointsColumns.ACCURACY, trackPoint.getAccuracy());
|
values.put(TrackPointsColumns.ACCURACY, trackPoint.getAccuracy());
|
||||||
}
|
}
|
||||||
if (trackPoint.hasSpeed()) {
|
if (trackPoint.hasSpeed()) {
|
||||||
values.put(TrackPointsColumns.SPEED, trackPoint.getSpeed());
|
values.put(TrackPointsColumns.SPEED, trackPoint.getSpeed().toMPS());
|
||||||
}
|
}
|
||||||
if (trackPoint.hasBearing()) {
|
if (trackPoint.hasBearing()) {
|
||||||
values.put(TrackPointsColumns.BEARING, trackPoint.getBearing());
|
values.put(TrackPointsColumns.BEARING, trackPoint.getBearing());
|
||||||
@@ -708,15 +708,18 @@ public class ContentProviderUtils {
|
|||||||
if (trackPoint.hasCyclingCadence()) {
|
if (trackPoint.hasCyclingCadence()) {
|
||||||
values.put(TrackPointsColumns.SENSOR_CADENCE, trackPoint.getCyclingCadence_rpm());
|
values.put(TrackPointsColumns.SENSOR_CADENCE, trackPoint.getCyclingCadence_rpm());
|
||||||
}
|
}
|
||||||
|
if (trackPoint.hasSensorDistance()) {
|
||||||
|
values.put(TrackPointsColumns.SENSOR_DISTANCE, trackPoint.getSensorDistance().toM());
|
||||||
|
}
|
||||||
if (trackPoint.hasPower()) {
|
if (trackPoint.hasPower()) {
|
||||||
values.put(TrackPointsColumns.SENSOR_POWER, trackPoint.getPower());
|
values.put(TrackPointsColumns.SENSOR_POWER, trackPoint.getPower());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trackPoint.hasElevationGain()) {
|
if (trackPoint.hasAltitudeGain()) {
|
||||||
values.put(TrackPointsColumns.ELEVATION_GAIN, trackPoint.getElevationGain());
|
values.put(TrackPointsColumns.ALTITUDE_GAIN, trackPoint.getAltitudeGain());
|
||||||
}
|
}
|
||||||
if (trackPoint.hasElevationLoss()) {
|
if (trackPoint.hasAltitudeLoss()) {
|
||||||
values.put(TrackPointsColumns.ELEVATION_LOSS, trackPoint.getElevationLoss());
|
values.put(TrackPointsColumns.ALTITUDE_LOSS, trackPoint.getAltitudeLoss());
|
||||||
}
|
}
|
||||||
|
|
||||||
return values;
|
return values;
|
||||||
@@ -757,39 +760,6 @@ public class ContentProviderUtils {
|
|||||||
return contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, projection, selection, selectionArgs, sortOrder);
|
return contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, projection, selection, selectionArgs, sortOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public List<TrackPoint> getTrackPoints(Track.Id trackId) {
|
|
||||||
List<TrackPoint> trackPoints = null;
|
|
||||||
|
|
||||||
try (Cursor trackPointCursor = getTrackPointCursor(trackId, null, null)) {
|
|
||||||
if (trackPointCursor != null) {
|
|
||||||
trackPointCursor.moveToFirst();
|
|
||||||
trackPoints = new ArrayList<>(trackPointCursor.getCount());
|
|
||||||
for (int i = 0; i < trackPointCursor.getCount(); i++) {
|
|
||||||
trackPoints.add(createTrackPoint(trackPointCursor));
|
|
||||||
trackPointCursor.moveToNext();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return trackPoints;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getDefaultCursorBatchSize() {
|
|
||||||
return defaultCursorBatchSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the default cursor batch size. For testing purpose.
|
|
||||||
*
|
|
||||||
* @param defaultCursorBatchSize the default cursor batch size
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
|
||||||
void setDefaultCursorBatchSize(int defaultCursorBatchSize) {
|
|
||||||
this.defaultCursorBatchSize = defaultCursorBatchSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static String formatIdListForUri(Track.Id... trackIds) {
|
public static String formatIdListForUri(Track.Id... trackIds) {
|
||||||
long[] ids = new long[trackIds.length];
|
long[] ids = new long[trackIds.length];
|
||||||
for (int i = 0; i < trackIds.length; i++) {
|
for (int i = 0; i < trackIds.length; i++) {
|
||||||
|
|||||||
@@ -259,8 +259,7 @@ public class CustomContentProvider extends ContentProvider {
|
|||||||
break;
|
break;
|
||||||
case TRACKS_SENSOR_STATS:
|
case TRACKS_SENSOR_STATS:
|
||||||
long trackId = ContentUris.parseId(url);
|
long trackId = ContentUris.parseId(url);
|
||||||
Cursor cursor = 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)});
|
||||||
return cursor;
|
|
||||||
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;
|
||||||
|
|||||||
+35
-2
@@ -25,7 +25,7 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
|||||||
|
|
||||||
private static final String TAG = CustomSQLiteOpenHelper.class.getSimpleName();
|
private static final String TAG = CustomSQLiteOpenHelper.class.getSimpleName();
|
||||||
|
|
||||||
private static final int DATABASE_VERSION = 30;
|
private static final int DATABASE_VERSION = 31;
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public static final String DATABASE_NAME = "database.db";
|
public static final String DATABASE_NAME = "database.db";
|
||||||
@@ -82,6 +82,9 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
|||||||
case 30:
|
case 30:
|
||||||
upgradeFrom29to30(db);
|
upgradeFrom29to30(db);
|
||||||
break;
|
break;
|
||||||
|
case 31:
|
||||||
|
upgradeFrom30to31(db);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new RuntimeException("Not implemented: upgrade to " + toVersion);
|
throw new RuntimeException("Not implemented: upgrade to " + toVersion);
|
||||||
@@ -115,7 +118,9 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
|||||||
case 29:
|
case 29:
|
||||||
downgradeFrom30to29(db);
|
downgradeFrom30to29(db);
|
||||||
break;
|
break;
|
||||||
|
case 30:
|
||||||
|
downgradeFrom31to30(db);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new RuntimeException("Not implemented: downgrade to " + toVersion);
|
throw new RuntimeException("Not implemented: downgrade to " + toVersion);
|
||||||
}
|
}
|
||||||
@@ -394,4 +399,32 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
|||||||
db.setTransactionSuccessful();
|
db.setTransactionSuccessful();
|
||||||
db.endTransaction();
|
db.endTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add distance column to TrackPoint.
|
||||||
|
*/
|
||||||
|
private void upgradeFrom30to31(SQLiteDatabase db) {
|
||||||
|
db.beginTransaction();
|
||||||
|
|
||||||
|
// TrackPoints
|
||||||
|
db.execSQL("ALTER TABLE trackpoints ADD COLUMN sensor_distance FLOAT");
|
||||||
|
|
||||||
|
db.setTransactionSuccessful();
|
||||||
|
db.endTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void downgradeFrom31to30(SQLiteDatabase db) {
|
||||||
|
db.beginTransaction();
|
||||||
|
|
||||||
|
// TrackPoints; identical to upgradeFrom27to28()
|
||||||
|
db.execSQL("ALTER TABLE trackpoints RENAME TO trackpoints_old");
|
||||||
|
db.execSQL("CREATE TABLE trackpoints (_id INTEGER PRIMARY KEY AUTOINCREMENT, trackid INTEGER NOT NULL, longitude INTEGER, latitude INTEGER, time INTEGER, elevation FLOAT, accuracy FLOAT, speed FLOAT, bearing FLOAT, sensor_heartrate FLOAT, sensor_cadence FLOAT, sensor_power FLOAT, elevation_gain FLOAT, elevation_loss FLOAT, type TEXT CHECK(type IN (-2, -1, 0, 1)), FOREIGN KEY (trackid) REFERENCES tracks(_id) ON UPDATE CASCADE ON DELETE CASCADE)");
|
||||||
|
db.execSQL("INSERT INTO trackpoints SELECT _id, trackid, longitude, latitude, time, elevation, accuracy, speed, bearing, sensor_heartrate, sensor_cadence, sensor_power, elevation_gain, elevation_gain, type FROM trackpoints_old");
|
||||||
|
db.execSQL("DROP TABLE trackpoints_old");
|
||||||
|
|
||||||
|
db.execSQL("CREATE INDEX trackpoints_trackid_index ON trackpoints(trackid)");
|
||||||
|
|
||||||
|
db.setTransactionSuccessful();
|
||||||
|
db.endTransaction();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package de.dennisguse.opentracks.content.provider;
|
package de.dennisguse.opentracks.content.provider;
|
||||||
|
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.util.Log;
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
@@ -10,9 +12,8 @@ import de.dennisguse.opentracks.content.data.Track;
|
|||||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A lightweight wrapper around the original {@link Cursor} with a method to clean up.
|
* A lightweight wrapper around the original {@link Cursor}.
|
||||||
*/
|
*/
|
||||||
//TODO Remove batching; that should be handled by the database/contentprovider (i.e., already in place as we use a cursor)!
|
|
||||||
public class TrackPointIterator implements Iterator<TrackPoint>, AutoCloseable {
|
public class TrackPointIterator implements Iterator<TrackPoint>, AutoCloseable {
|
||||||
|
|
||||||
private static final String TAG = TrackPointIterator.class.getSimpleName();
|
private static final String TAG = TrackPointIterator.class.getSimpleName();
|
||||||
@@ -20,7 +21,6 @@ public class TrackPointIterator implements Iterator<TrackPoint>, AutoCloseable {
|
|||||||
private final ContentProviderUtils contentProviderUtils;
|
private final ContentProviderUtils contentProviderUtils;
|
||||||
private final Track.Id trackId;
|
private final Track.Id trackId;
|
||||||
private final CachedTrackPointsIndexes indexes;
|
private final CachedTrackPointsIndexes indexes;
|
||||||
private TrackPoint.Id lastTrackPointId = null;
|
|
||||||
private Cursor cursor;
|
private Cursor cursor;
|
||||||
|
|
||||||
public TrackPointIterator(ContentProviderUtils contentProviderUtils, Track.Id trackId, TrackPoint.Id startTrackPointId) {
|
public TrackPointIterator(ContentProviderUtils contentProviderUtils, Track.Id trackId, TrackPoint.Id startTrackPointId) {
|
||||||
@@ -28,28 +28,11 @@ public class TrackPointIterator implements Iterator<TrackPoint>, AutoCloseable {
|
|||||||
this.trackId = trackId;
|
this.trackId = trackId;
|
||||||
|
|
||||||
cursor = getCursor(startTrackPointId);
|
cursor = getCursor(startTrackPointId);
|
||||||
indexes = cursor != null ? new CachedTrackPointsIndexes(cursor)
|
indexes = new CachedTrackPointsIndexes(cursor);
|
||||||
: null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the track point cursor.
|
|
||||||
*
|
|
||||||
* @param trackPointId the starting track point id
|
|
||||||
*/
|
|
||||||
private Cursor getCursor(TrackPoint.Id trackPointId) {
|
private Cursor getCursor(TrackPoint.Id trackPointId) {
|
||||||
return contentProviderUtils.getTrackPointCursor(trackId, trackPointId, contentProviderUtils.getDefaultCursorBatchSize());
|
return contentProviderUtils.getTrackPointCursor(trackId, trackPointId);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Advances the cursor to the next batch. Returns true if successful.
|
|
||||||
*/
|
|
||||||
private boolean advanceCursorToNextBatch() {
|
|
||||||
TrackPoint.Id trackPointId = lastTrackPointId == null ? null : new TrackPoint.Id(lastTrackPointId.getId() + 1);
|
|
||||||
Log.d(TAG, "Advancing track point id: " + trackPointId);
|
|
||||||
cursor.close();
|
|
||||||
cursor = getCursor(trackPointId);
|
|
||||||
return cursor != null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -57,32 +40,23 @@ public class TrackPointIterator implements Iterator<TrackPoint>, AutoCloseable {
|
|||||||
if (cursor == null) {
|
if (cursor == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (cursor.isAfterLast()) {
|
return !cursor.isLast() && !cursor.isAfterLast();
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (cursor.isLast()) {
|
|
||||||
if (cursor.getCount() != contentProviderUtils.getDefaultCursorBatchSize()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return advanceCursorToNextBatch() && !cursor.isAfterLast();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@NonNull
|
||||||
public TrackPoint next() {
|
public TrackPoint next() {
|
||||||
if (cursor == null) {
|
if (cursor == null || !cursor.moveToNext()) {
|
||||||
throw new NoSuchElementException();
|
throw new NoSuchElementException();
|
||||||
}
|
}
|
||||||
if (!cursor.moveToNext()) {
|
|
||||||
if (!advanceCursorToNextBatch() || !cursor.moveToNext()) {
|
|
||||||
throw new NoSuchElementException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastTrackPointId = new TrackPoint.Id(cursor.getLong(indexes.idIndex));
|
|
||||||
return ContentProviderUtils.fillTrackPoint(cursor, indexes);
|
return ContentProviderUtils.fillTrackPoint(cursor, indexes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public int getCount() {
|
||||||
|
return cursor.getCount();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
if (cursor != null) {
|
if (cursor != null) {
|
||||||
|
|||||||
@@ -54,6 +54,12 @@ public abstract class SensorData<T> {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset long term aggregated values (more than derived from previous SensorData). e.g. overall distance.
|
||||||
|
*/
|
||||||
|
public void reset() {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is the data recent considering the current time.
|
* Is the data recent considering the current time.
|
||||||
*/
|
*/
|
||||||
@@ -61,4 +67,10 @@ public abstract class SensorData<T> {
|
|||||||
return Instant.now()
|
return Instant.now()
|
||||||
.isBefore(time.plus(BluetoothRemoteSensorManager.MAX_SENSOR_DATE_SET_AGE_MS));
|
.isBefore(time.plus(BluetoothRemoteSensorManager.MAX_SENSOR_DATE_SET_AGE_MS));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "sensorAddress='" + sensorAddress;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.util.Pair;
|
import androidx.core.util.Pair;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.data.Distance;
|
||||||
|
import de.dennisguse.opentracks.content.data.Speed;
|
||||||
import de.dennisguse.opentracks.util.UintUtils;
|
import de.dennisguse.opentracks.util.UintUtils;
|
||||||
import de.dennisguse.opentracks.util.UnitConversions;
|
import de.dennisguse.opentracks.util.UnitConversions;
|
||||||
|
|
||||||
@@ -41,7 +45,7 @@ public final class SensorDataCycling {
|
|||||||
/**
|
/**
|
||||||
* Workaround for Wahoo CADENCE: provides speed instead of cadence
|
* Workaround for Wahoo CADENCE: provides speed instead of cadence
|
||||||
*/
|
*/
|
||||||
public Cadence(@NonNull SensorDataCycling.Speed speed) {
|
public Cadence(@NonNull DistanceSpeed speed) {
|
||||||
this(speed.getSensorAddress(), speed.getSensorName(), speed.wheelRevolutionsCount, speed.wheelRevolutionsTime);
|
this(speed.getSensorAddress(), speed.getSensorName(), speed.wheelRevolutionsCount, speed.wheelRevolutionsTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +78,7 @@ public final class SensorDataCycling {
|
|||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "cadence=" + value + " time=" + crankRevolutionsTime + " count=" + crankRevolutionsCount;
|
return super.toString() + " cadence=" + value + " time=" + crankRevolutionsTime + " count=" + crankRevolutionsCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -90,18 +94,18 @@ public final class SensorDataCycling {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Speed extends SensorData<Float> {
|
public static class DistanceSpeed extends SensorData<DistanceSpeed.Data> {
|
||||||
|
|
||||||
private final Integer wheelRevolutionsCount; // UINT16
|
private final Integer wheelRevolutionsCount; // UINT16
|
||||||
private final Integer wheelRevolutionsTime; // UINT16; 1/1024s
|
private final Integer wheelRevolutionsTime; // UINT16; 1/1024s
|
||||||
|
|
||||||
public Speed(String sensorAddress) {
|
public DistanceSpeed(String sensorAddress) {
|
||||||
super(sensorAddress);
|
super(sensorAddress);
|
||||||
this.wheelRevolutionsCount = null;
|
this.wheelRevolutionsCount = null;
|
||||||
this.wheelRevolutionsTime = null;
|
this.wheelRevolutionsTime = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Speed(String sensorAddress, String sensorName, int wheelRevolutionsCount, int wheelRevolutionsTime) {
|
public DistanceSpeed(String sensorAddress, String sensorName, int wheelRevolutionsCount, int wheelRevolutionsTime) {
|
||||||
super(sensorAddress, sensorName);
|
super(sensorAddress, sensorName);
|
||||||
this.wheelRevolutionsCount = wheelRevolutionsCount;
|
this.wheelRevolutionsCount = wheelRevolutionsCount;
|
||||||
this.wheelRevolutionsTime = wheelRevolutionsTime;
|
this.wheelRevolutionsTime = wheelRevolutionsTime;
|
||||||
@@ -119,51 +123,87 @@ public final class SensorDataCycling {
|
|||||||
return wheelRevolutionsTime;
|
return wheelRevolutionsTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void compute(Speed previous, int wheel_circumference_mm) {
|
public void compute(DistanceSpeed previous, int wheel_circumference_mm) {
|
||||||
if (hasData() && previous != null && previous.hasData()) {
|
if (hasData() && previous != null && previous.hasData()) {
|
||||||
float timeDiff_ms = UintUtils.diff(wheelRevolutionsTime, previous.wheelRevolutionsTime, UintUtils.UINT16_MAX) / 1024f * UnitConversions.S_TO_MS;
|
float timeDiff_ms = UintUtils.diff(wheelRevolutionsTime, previous.wheelRevolutionsTime, UintUtils.UINT16_MAX) / 1024f * UnitConversions.S_TO_MS;
|
||||||
if (timeDiff_ms <= 0) {
|
Duration timeDiff = Duration.ofMillis((long) timeDiff_ms);
|
||||||
|
if (timeDiff.isZero() || timeDiff.isNegative()) {
|
||||||
Log.e(TAG, "Timestamps difference is invalid: cannot compute cadence.");
|
Log.e(TAG, "Timestamps difference is invalid: cannot compute cadence.");
|
||||||
value = null;
|
value = null;
|
||||||
} else {
|
} else {
|
||||||
long wheelDiff = UintUtils.diff(wheelRevolutionsCount, previous.wheelRevolutionsCount, UintUtils.UINT16_MAX);
|
long wheelDiff = UintUtils.diff(wheelRevolutionsCount, previous.wheelRevolutionsCount, UintUtils.UINT16_MAX);
|
||||||
double timeDiff_s = timeDiff_ms * UnitConversions.MS_TO_S;
|
wheelDiff = Math.abs(wheelDiff); //HACK for Garmin Speed 2 as some of those seem to count backwards
|
||||||
value = (float) (wheelDiff * wheel_circumference_mm * UnitConversions.MM_TO_M / timeDiff_s);
|
|
||||||
|
Distance distance = Distance.of(wheelDiff * wheel_circumference_mm * UnitConversions.MM_TO_M);
|
||||||
|
Distance distanceOverall = distance;
|
||||||
|
if (previous.hasValue()) {
|
||||||
|
distanceOverall = distance.plus(previous.getValue().distanceOverall);
|
||||||
|
}
|
||||||
|
Speed speed_mps = Speed.of(distance, timeDiff);
|
||||||
|
value = new Data(distance, distanceOverall, speed_mps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset() {
|
||||||
|
if (value != null) {
|
||||||
|
value = new Data(value.distance, Distance.of(0), value.speed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "speed=" + value + " time=" + wheelRevolutionsTime + " count=" + wheelRevolutionsCount;
|
return super.toString() + " data=" + value + " time=" + wheelRevolutionsTime + " count=" + wheelRevolutionsCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(@Nullable Object obj) {
|
public boolean equals(@Nullable Object obj) {
|
||||||
if (!(obj instanceof Speed)) return false;
|
if (!(obj instanceof DistanceSpeed)) return false;
|
||||||
|
|
||||||
Speed comp = (Speed) obj;
|
DistanceSpeed comp = (DistanceSpeed) obj;
|
||||||
if (hasData() && comp.hasData() == hasData()) {
|
if (hasData() && comp.hasData() == hasData()) {
|
||||||
return getWheelRevolutionsCount() == comp.getWheelRevolutionsCount() && getWheelRevolutionsTime() == comp.getWheelRevolutionsTime();
|
return getWheelRevolutionsCount() == comp.getWheelRevolutionsCount() && getWheelRevolutionsTime() == comp.getWheelRevolutionsTime();
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class Data {
|
||||||
|
public final Distance distance;
|
||||||
|
public final Distance distanceOverall;
|
||||||
|
public final Speed speed;
|
||||||
|
|
||||||
|
private Data(Distance distance, Distance distanceOverall, Speed speed) {
|
||||||
|
this.distance = distance;
|
||||||
|
this.distanceOverall = distanceOverall;
|
||||||
|
this.speed = speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Data{" +
|
||||||
|
"distance_m=" + distance +
|
||||||
|
", distance_overall_m=" + distanceOverall +
|
||||||
|
", speed_mps=" + speed +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class CadenceAndSpeed extends SensorData<Pair<Cadence, Speed>> {
|
public static class CadenceAndSpeed extends SensorData<Pair<Cadence, DistanceSpeed>> {
|
||||||
|
|
||||||
public CadenceAndSpeed(String sensorAddress, String sensorName, @NonNull Cadence cadence, @NonNull Speed speed) {
|
public CadenceAndSpeed(String sensorAddress, String sensorName, @Nullable Cadence cadence, @Nullable DistanceSpeed distanceSpeed) {
|
||||||
super(sensorAddress, sensorName);
|
super(sensorAddress, sensorName);
|
||||||
this.value = new Pair<>(cadence, speed);
|
this.value = new Pair<>(cadence, distanceSpeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cadence getCadence() {
|
public Cadence getCadence() {
|
||||||
return this.value != null ? this.value.first : null;
|
return this.value != null ? this.value.first : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Speed getSpeed() {
|
public DistanceSpeed getDistanceSpeed() {
|
||||||
return this.value != null ? this.value.second : null;
|
return this.value != null ? this.value.second : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,6 @@ public class SensorDataCyclingPower extends SensorData<Float> {
|
|||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "power=" + value;
|
return super.toString() + " power=" + value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,6 @@ public class SensorDataHeartRate extends SensorData<Float> {
|
|||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "heart=" + value;
|
return super.toString() + " heart=" + value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ public final class SensorDataSet {
|
|||||||
|
|
||||||
private SensorDataCycling.Cadence cyclingCadence;
|
private SensorDataCycling.Cadence cyclingCadence;
|
||||||
|
|
||||||
private SensorDataCycling.Speed cyclingSpeed;
|
private SensorDataCycling.DistanceSpeed cyclingDistanceSpeed;
|
||||||
|
|
||||||
private SensorDataCyclingPower cyclingPower;
|
private SensorDataCyclingPower cyclingPower;
|
||||||
|
|
||||||
@@ -25,8 +25,8 @@ public final class SensorDataSet {
|
|||||||
return cyclingCadence;
|
return cyclingCadence;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SensorDataCycling.Speed getCyclingSpeed() {
|
public SensorDataCycling.DistanceSpeed getCyclingDistanceSpeed() {
|
||||||
return cyclingSpeed;
|
return cyclingDistanceSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SensorDataCyclingPower getCyclingPower() {
|
public SensorDataCyclingPower getCyclingPower() {
|
||||||
@@ -44,7 +44,7 @@ public final class SensorDataSet {
|
|||||||
public void clear() {
|
public void clear() {
|
||||||
this.heartRate = null;
|
this.heartRate = null;
|
||||||
this.cyclingCadence = null;
|
this.cyclingCadence = null;
|
||||||
this.cyclingSpeed = null;
|
this.cyclingDistanceSpeed = null;
|
||||||
this.cyclingPower = null;
|
this.cyclingPower = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,8 +57,9 @@ public final class SensorDataSet {
|
|||||||
trackPoint.setCyclingCadence_rpm(cyclingCadence.getValue());
|
trackPoint.setCyclingCadence_rpm(cyclingCadence.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cyclingSpeed != null && cyclingSpeed.hasValue()) {
|
if (cyclingDistanceSpeed != null && cyclingDistanceSpeed.hasValue()) {
|
||||||
trackPoint.setSpeed(cyclingSpeed.getValue());
|
trackPoint.setSensorDistance(cyclingDistanceSpeed.getValue().distanceOverall);
|
||||||
|
trackPoint.setSpeed(cyclingDistanceSpeed.getValue().speed);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cyclingPower != null && cyclingPower.hasValue()) {
|
if (cyclingPower != null && cyclingPower.hasValue()) {
|
||||||
@@ -66,12 +67,19 @@ public final class SensorDataSet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
if (heartRate != null) heartRate.reset();
|
||||||
|
if (cyclingCadence != null) cyclingCadence.reset();
|
||||||
|
if (cyclingDistanceSpeed != null) cyclingDistanceSpeed.reset();
|
||||||
|
if (cyclingPower != null) cyclingPower.reset();
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return (getHeartRate() != null ? "" + getHeartRate() : "")
|
return (getHeartRate() != null ? "" + getHeartRate() : "")
|
||||||
+ (getCyclingCadence() != null ? " " + getCyclingCadence() : "")
|
+ (getCyclingCadence() != null ? " " + getCyclingCadence() : "")
|
||||||
+ (getCyclingSpeed() != null ? " " + getCyclingSpeed() : "")
|
+ (getCyclingDistanceSpeed() != null ? " " + getCyclingDistanceSpeed() : "")
|
||||||
+ (getCyclingPower() != null ? " " + getCyclingPower() : "");
|
+ (getCyclingPower() != null ? " " + getCyclingPower() : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,8 +93,8 @@ public final class SensorDataSet {
|
|||||||
this.cyclingCadence = (SensorDataCycling.Cadence) data;
|
this.cyclingCadence = (SensorDataCycling.Cadence) data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (type instanceof SensorDataCycling.Speed) {
|
if (type instanceof SensorDataCycling.DistanceSpeed) {
|
||||||
this.cyclingSpeed = (SensorDataCycling.Speed) data;
|
this.cyclingDistanceSpeed = (SensorDataCycling.DistanceSpeed) data;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,9 @@ import de.dennisguse.opentracks.chart.ChartPoint;
|
|||||||
import de.dennisguse.opentracks.chart.ChartView;
|
import de.dennisguse.opentracks.chart.ChartView;
|
||||||
import de.dennisguse.opentracks.content.TrackDataHub;
|
import de.dennisguse.opentracks.content.TrackDataHub;
|
||||||
import de.dennisguse.opentracks.content.TrackDataListener;
|
import de.dennisguse.opentracks.content.TrackDataListener;
|
||||||
|
import de.dennisguse.opentracks.content.data.Distance;
|
||||||
import de.dennisguse.opentracks.content.data.Marker;
|
import de.dennisguse.opentracks.content.data.Marker;
|
||||||
|
import de.dennisguse.opentracks.content.data.Speed;
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
import de.dennisguse.opentracks.databinding.ChartBinding;
|
import de.dennisguse.opentracks.databinding.ChartBinding;
|
||||||
@@ -65,31 +67,27 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
return chartFragment;
|
return chartFragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final List<ChartPoint> pendingPoints = new ArrayList<>();
|
private SharedPreferences sharedPreferences;
|
||||||
|
private Distance recordingDistanceInterval;
|
||||||
|
|
||||||
private TrackDataHub trackDataHub;
|
private TrackDataHub trackDataHub;
|
||||||
|
|
||||||
// Stats gathered from the received data
|
// Stats gathered from the received data
|
||||||
private TrackStatisticsUpdater trackStatisticsUpdater = new TrackStatisticsUpdater();
|
private final List<ChartPoint> pendingPoints = new ArrayList<>();
|
||||||
|
|
||||||
private ChartBinding viewBinding;
|
|
||||||
|
|
||||||
//TODO REMOVE: Used to restore the TrackStatistics via TrackStatisticsUpdater; for the diagrams we should consider not this setting and just use all data that is in the database
|
|
||||||
@Deprecated
|
|
||||||
private int recordingDistanceInterval;
|
|
||||||
private String category = "";
|
private String category = "";
|
||||||
|
|
||||||
// Modes of operation
|
// Modes of operation
|
||||||
private boolean chartByDistance;
|
private boolean chartByDistance;
|
||||||
|
|
||||||
|
private ChartBinding viewBinding;
|
||||||
// UI elements
|
// UI elements
|
||||||
private ChartView chartView;
|
private ChartView chartView;
|
||||||
|
|
||||||
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
|
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||||
if (PreferencesUtils.isKey(getContext(), R.string.stats_units_key, key)) {
|
if (PreferencesUtils.isKey(getContext(), R.string.stats_units_key, key)) {
|
||||||
boolean metricUnits = PreferencesUtils.isMetricUnits(getContext());
|
boolean metricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, getContext());
|
||||||
if (metricUnits != chartView.getMetricUnits()) {
|
if (metricUnits != chartView.getMetricUnits()) {
|
||||||
chartView.setMetricUnits(metricUnits);
|
chartView.setMetricUnits(metricUnits);
|
||||||
runOnUiThread(() -> {
|
runOnUiThread(() -> {
|
||||||
@@ -100,7 +98,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key)) {
|
if (PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key)) {
|
||||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(getContext(), category);
|
boolean reportSpeed = PreferencesUtils.isReportSpeed(sharedPreferences, getContext(), category);
|
||||||
if (reportSpeed != chartView.getReportSpeed()) {
|
if (reportSpeed != chartView.getReportSpeed()) {
|
||||||
chartView.setReportSpeed(reportSpeed);
|
chartView.setReportSpeed(reportSpeed);
|
||||||
chartView.applyReportSpeed();
|
chartView.applyReportSpeed();
|
||||||
@@ -114,7 +112,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (PreferencesUtils.isKey(getContext(), R.string.recording_distance_interval_key, key)) {
|
if (PreferencesUtils.isKey(getContext(), R.string.recording_distance_interval_key, key)) {
|
||||||
recordingDistanceInterval = PreferencesUtils.getRecordingDistanceInterval(getContext());
|
recordingDistanceInterval = PreferencesUtils.getRecordingDistanceInterval(sharedPreferences, getContext());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -165,8 +163,9 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
resumeTrackDataHub();
|
resumeTrackDataHub();
|
||||||
PreferencesUtils.register(getContext(), sharedPreferenceChangeListener);
|
sharedPreferences = PreferencesUtils.getSharedPreferences(getContext());
|
||||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null);
|
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||||
|
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
||||||
|
|
||||||
checkChartSettings();
|
checkChartSettings();
|
||||||
getActivity().runOnUiThread(updateChart);
|
getActivity().runOnUiThread(updateChart);
|
||||||
@@ -176,7 +175,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
public void onPause() {
|
public void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
pauseTrackDataHub();
|
pauseTrackDataHub();
|
||||||
PreferencesUtils.unregister(getContext(), sharedPreferenceChangeListener);
|
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -192,6 +191,12 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
viewBinding = null;
|
viewBinding = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
sharedPreferences = null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTrackUpdated(Track track) {
|
public void onTrackUpdated(Track track) {
|
||||||
if (isResumed()) {
|
if (isResumed()) {
|
||||||
@@ -201,7 +206,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
category = track.getCategory();
|
category = track.getCategory();
|
||||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(getContext(), category);
|
boolean reportSpeed = PreferencesUtils.isReportSpeed(PreferencesUtils.getSharedPreferences(getContext()), getContext(), category);
|
||||||
if (reportSpeed != chartView.getReportSpeed()) {
|
if (reportSpeed != chartView.getReportSpeed()) {
|
||||||
chartView.setReportSpeed(reportSpeed);
|
chartView.setReportSpeed(reportSpeed);
|
||||||
chartView.applyReportSpeed();
|
chartView.applyReportSpeed();
|
||||||
@@ -212,7 +217,6 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
@Override
|
@Override
|
||||||
public void clearTrackPoints() {
|
public void clearTrackPoints() {
|
||||||
if (isResumed()) {
|
if (isResumed()) {
|
||||||
trackStatisticsUpdater = new TrackStatisticsUpdater();
|
|
||||||
pendingPoints.clear();
|
pendingPoints.clear();
|
||||||
chartView.reset();
|
chartView.reset();
|
||||||
runOnUiThread(() -> {
|
runOnUiThread(() -> {
|
||||||
@@ -223,24 +227,15 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void onSampledInTrackPoint(@NonNull TrackPoint trackPoint, @NonNull TrackStatistics trackStatistics, Speed smoothedSpeed, double smoothedAltitude_m) {
|
||||||
public void onSampledInTrackPoint(@NonNull TrackPoint trackPoint) {
|
|
||||||
if (isResumed()) {
|
if (isResumed()) {
|
||||||
pendingPoints.add(createPendingPoint(trackPoint));
|
ChartPoint point = new ChartPoint(trackStatistics, trackPoint, smoothedSpeed, smoothedAltitude_m, chartByDistance, chartView.getMetricUnits());
|
||||||
|
pendingPoints.add(point);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSampledOutTrackPoint(@NonNull TrackPoint trackPoint) {
|
public void onNewTrackPointsDone(@NonNull TrackPoint unused, @NonNull TrackStatistics alsoUnused) {
|
||||||
if (isResumed()) {
|
|
||||||
if (trackStatisticsUpdater != null) {
|
|
||||||
trackStatisticsUpdater.addTrackPoint(trackPoint, recordingDistanceInterval);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNewTrackPointsDone(@NonNull TrackPoint unused) {
|
|
||||||
if (isResumed()) {
|
if (isResumed()) {
|
||||||
chartView.addChartPoints(pendingPoints);
|
chartView.addChartPoints(pendingPoints);
|
||||||
pendingPoints.clear();
|
pendingPoints.clear();
|
||||||
@@ -301,6 +296,8 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
* Returns true if the selected track is recording.
|
* Returns true if the selected track is recording.
|
||||||
* Needs to be synchronized because trackDataHub can be accessed by multiple threads.
|
* Needs to be synchronized because trackDataHub can be accessed by multiple threads.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
|
//TODO Should not be dynamic but instead set while instantiating, i.e., newFragment().
|
||||||
private synchronized boolean isSelectedTrackRecording() {
|
private synchronized boolean isSelectedTrackRecording() {
|
||||||
return trackDataHub != null && trackDataHub.isSelectedTrackRecording();
|
return trackDataHub != null && trackDataHub.isSelectedTrackRecording();
|
||||||
}
|
}
|
||||||
@@ -317,12 +314,6 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
ChartPoint createPendingPoint(@NonNull TrackPoint trackPoint) {
|
|
||||||
trackStatisticsUpdater.addTrackPoint(trackPoint, recordingDistanceInterval);
|
|
||||||
return new ChartPoint(trackStatisticsUpdater, trackPoint, chartByDistance, chartView.getMetricUnits());
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void setChartView(ChartView view) {
|
void setChartView(ChartView view) {
|
||||||
chartView = view;
|
chartView = view;
|
||||||
@@ -342,4 +333,9 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
|||||||
void setChartByDistance(boolean value) {
|
void setChartByDistance(boolean value) {
|
||||||
chartByDistance = value;
|
chartByDistance = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void setRecordingDistanceInterval(Distance value) {
|
||||||
|
recordingDistanceInterval = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package de.dennisguse.opentracks.fragments;
|
|||||||
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -14,6 +13,11 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
import androidx.lifecycle.LiveData;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.TrackActivityDataHubInterface;
|
import de.dennisguse.opentracks.TrackActivityDataHubInterface;
|
||||||
@@ -21,11 +25,12 @@ import de.dennisguse.opentracks.adapters.IntervalStatisticsAdapter;
|
|||||||
import de.dennisguse.opentracks.content.TrackDataHub;
|
import de.dennisguse.opentracks.content.TrackDataHub;
|
||||||
import de.dennisguse.opentracks.content.TrackDataListener;
|
import de.dennisguse.opentracks.content.TrackDataListener;
|
||||||
import de.dennisguse.opentracks.content.data.Marker;
|
import de.dennisguse.opentracks.content.data.Marker;
|
||||||
|
import de.dennisguse.opentracks.content.data.Speed;
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
import de.dennisguse.opentracks.databinding.IntervalListViewBinding;
|
import de.dennisguse.opentracks.databinding.IntervalListViewBinding;
|
||||||
|
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||||
import de.dennisguse.opentracks.util.UnitConversions;
|
|
||||||
import de.dennisguse.opentracks.viewmodels.IntervalStatistics;
|
import de.dennisguse.opentracks.viewmodels.IntervalStatistics;
|
||||||
import de.dennisguse.opentracks.viewmodels.IntervalStatisticsModel;
|
import de.dennisguse.opentracks.viewmodels.IntervalStatisticsModel;
|
||||||
|
|
||||||
@@ -36,6 +41,8 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
|
|||||||
|
|
||||||
private static final String TAG = IntervalsFragment.class.getSimpleName();
|
private static final String TAG = IntervalsFragment.class.getSimpleName();
|
||||||
|
|
||||||
|
private static final String FROM_TOP_TO_BOTTOM_KEY = "fromTopToBottom";
|
||||||
|
|
||||||
private IntervalStatisticsModel viewModel;
|
private IntervalStatisticsModel viewModel;
|
||||||
protected IntervalStatisticsAdapter.StackMode stackModeListView;
|
protected IntervalStatisticsAdapter.StackMode stackModeListView;
|
||||||
private IntervalStatisticsModel.IntervalOption selectedInterval;
|
private IntervalStatisticsModel.IntervalOption selectedInterval;
|
||||||
@@ -44,23 +51,41 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
|
|||||||
private IntervalStatisticsAdapter adapter;
|
private IntervalStatisticsAdapter adapter;
|
||||||
private ArrayAdapter<IntervalStatisticsModel.IntervalOption> spinnerAdapter;
|
private ArrayAdapter<IntervalStatisticsModel.IntervalOption> spinnerAdapter;
|
||||||
|
|
||||||
|
private SharedPreferences sharedPreferences;
|
||||||
|
|
||||||
private TrackDataHub trackDataHub;
|
private TrackDataHub trackDataHub;
|
||||||
private String category;
|
private boolean isReportSpeed;
|
||||||
|
|
||||||
private IntervalListViewBinding viewBinding;
|
private IntervalListViewBinding viewBinding;
|
||||||
|
|
||||||
protected final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (preferences, key) -> {
|
protected final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
|
||||||
if (PreferencesUtils.isKey(getContext(), R.string.stats_units_key, key) || PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key)) {
|
if (PreferencesUtils.isKey(getContext(), R.string.stats_units_key, key) || PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key)) {
|
||||||
metricUnits = PreferencesUtils.isMetricUnits(getContext());
|
metricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, getContext());
|
||||||
if (adapter != null) {
|
uploadIntervals();
|
||||||
adapter.notifyDataSetChanged();
|
if (spinnerAdapter != null) {
|
||||||
spinnerAdapter.notifyDataSetChanged();
|
spinnerAdapter.notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public static Fragment newInstance() {
|
/**
|
||||||
return new IntervalsFragment();
|
* Creates an instance of this class.
|
||||||
|
*
|
||||||
|
* @param fromTopToBottom If true then the intervals are shown from top to bottom (the first interval on top). Otherwise the intervals are shown from bottom to top.
|
||||||
|
* @return IntervalsFragment instance.
|
||||||
|
*/
|
||||||
|
public static Fragment newInstance(boolean fromTopToBottom) {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putBoolean(FROM_TOP_TO_BOTTOM_KEY, fromTopToBottom);
|
||||||
|
IntervalsFragment intervalsFragment = new IntervalsFragment();
|
||||||
|
intervalsFragment.setArguments(bundle);
|
||||||
|
return intervalsFragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
stackModeListView = getArguments().getBoolean(FROM_TOP_TO_BOTTOM_KEY, true) ? IntervalStatisticsAdapter.StackMode.STACK_FROM_TOP : IntervalStatisticsAdapter.StackMode.STACK_FROM_BOTTOM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -73,14 +98,12 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
|
|||||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
|
||||||
PreferencesUtils.register(getContext(), sharedPreferenceChangeListener);
|
sharedPreferences = PreferencesUtils.getSharedPreferences(getContext());
|
||||||
metricUnits = PreferencesUtils.isMetricUnits(getContext());
|
|
||||||
|
|
||||||
viewBinding.intervalList.setEmptyView(viewBinding.intervalListEmptyView);
|
adapter = new IntervalStatisticsAdapter(getContext(), stackModeListView, metricUnits, isReportSpeed);
|
||||||
|
viewBinding.intervalList.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
stackModeListView = IntervalStatisticsAdapter.StackMode.STACK_FROM_TOP;
|
// TODO handle empty view: before we did viewBinding.intervalList.setEmptyView(viewBinding.intervalListEmptyView);
|
||||||
|
viewBinding.intervalList.setAdapter(adapter);
|
||||||
viewModel = new IntervalStatisticsModel();
|
|
||||||
|
|
||||||
spinnerAdapter = new ArrayAdapter<IntervalStatisticsModel.IntervalOption>(getContext(), android.R.layout.simple_spinner_dropdown_item, IntervalStatisticsModel.IntervalOption.values()) {
|
spinnerAdapter = new ArrayAdapter<IntervalStatisticsModel.IntervalOption>(getContext(), android.R.layout.simple_spinner_dropdown_item, IntervalStatisticsModel.IntervalOption.values()) {
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -106,7 +129,7 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
|
|||||||
@Override
|
@Override
|
||||||
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
||||||
selectedInterval = IntervalStatisticsModel.IntervalOption.values()[i];
|
selectedInterval = IntervalStatisticsModel.IntervalOption.values()[i];
|
||||||
loadIntervals();
|
uploadIntervals();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -118,6 +141,10 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
|
|||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||||
|
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
||||||
|
viewModel = new ViewModelProvider(getActivity()).get(IntervalStatisticsModel.class);
|
||||||
|
loadIntervals();
|
||||||
resumeTrackDataHub();
|
resumeTrackDataHub();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,6 +152,7 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
|
|||||||
public void onPause() {
|
public void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
pauseTrackDataHub();
|
pauseTrackDataHub();
|
||||||
|
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -137,7 +165,7 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
|
|||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
|
|
||||||
PreferencesUtils.unregister(getContext(), sharedPreferenceChangeListener);
|
sharedPreferences = null;
|
||||||
|
|
||||||
adapter = null;
|
adapter = null;
|
||||||
viewModel = null;
|
viewModel = null;
|
||||||
@@ -150,10 +178,15 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
|
|||||||
if (viewModel == null) {
|
if (viewModel == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
LiveData<List<IntervalStatistics.Interval>> liveData = viewModel.getIntervalStats(metricUnits, selectedInterval);
|
||||||
|
liveData.observe(getActivity(), intervalList -> adapter.swapData(intervalList, metricUnits, isReportSpeed));
|
||||||
|
}
|
||||||
|
|
||||||
IntervalStatistics intervalStatistics = viewModel.getIntervalStats(metricUnits, selectedInterval);
|
private synchronized void uploadIntervals() {
|
||||||
adapter = new IntervalStatisticsAdapter(getContext(), intervalStatistics.getIntervalList(), category, stackModeListView);
|
if (viewModel == null) {
|
||||||
viewBinding.intervalList.setAdapter(adapter);
|
return;
|
||||||
|
}
|
||||||
|
viewModel.upload(metricUnits, selectedInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -180,11 +213,11 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
|
|||||||
getActivity().runOnUiThread(() -> {
|
getActivity().runOnUiThread(() -> {
|
||||||
if (isResumed()) {
|
if (isResumed()) {
|
||||||
// Set category.
|
// Set category.
|
||||||
category = track != null ? track.getCategory() : "";
|
String category = track != null ? track.getCategory() : "";
|
||||||
|
|
||||||
// Set rate label.
|
// Set rate label.
|
||||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(getContext(), category);
|
isReportSpeed = PreferencesUtils.isReportSpeed(sharedPreferences, getContext(), category);
|
||||||
viewBinding.intervalRate.setText(reportSpeed ? R.string.stats_speed : R.string.stats_pace);
|
viewBinding.intervalRate.setText(isReportSpeed ? R.string.stats_speed : R.string.stats_pace);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -198,23 +231,23 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSampledInTrackPoint(@NonNull TrackPoint trackPoint) {
|
public void onSampledInTrackPoint(@NonNull TrackPoint trackPoint, @NonNull TrackStatistics unused, Speed unused2, double unused3) {
|
||||||
if (isResumed()) {
|
if (isResumed()) {
|
||||||
viewModel.add(trackPoint);
|
viewModel.add(trackPoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSampledOutTrackPoint(@NonNull TrackPoint trackPoint) {
|
public void onSampledOutTrackPoint(@NonNull TrackPoint trackPoint, @NonNull TrackStatistics unused) {
|
||||||
if (isResumed()) {
|
if (isResumed()) {
|
||||||
viewModel.add(trackPoint);
|
viewModel.add(trackPoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNewTrackPointsDone(@NonNull TrackPoint unused) {
|
public void onNewTrackPointsDone(@NonNull TrackPoint unused, @NonNull TrackStatistics alsoUnused) {
|
||||||
if (isResumed()) {
|
if (isResumed()) {
|
||||||
runOnUiThread(this::loadIntervals);
|
runOnUiThread(viewModel::onNewTrackPoints);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,48 +277,4 @@ public class IntervalsFragment extends Fragment implements TrackDataListener {
|
|||||||
fragmentActivity.runOnUiThread(runnable);
|
fragmentActivity.runOnUiThread(runnable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class IntervalsRecordingFragment extends IntervalsFragment {
|
|
||||||
// Refreshing intervals stats it's not so demanding so 5 seconds is enough to balance performance and user experience.
|
|
||||||
private static final long UI_UPDATE_INTERVAL = 5 * UnitConversions.ONE_SECOND_MS;
|
|
||||||
|
|
||||||
private Handler intervalHandler;
|
|
||||||
|
|
||||||
private final Runnable intervalRunner = new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (isResumed()) {
|
|
||||||
updateIntervals();
|
|
||||||
intervalHandler.postDelayed(intervalRunner, UI_UPDATE_INTERVAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static Fragment newInstance() {
|
|
||||||
return new IntervalsRecordingFragment();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
|
||||||
super.onViewCreated(view, savedInstanceState);
|
|
||||||
intervalHandler = new Handler();
|
|
||||||
stackModeListView = IntervalStatisticsAdapter.StackMode.STACK_FROM_BOTTOM;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
intervalHandler.post(intervalRunner);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
intervalHandler.removeCallbacks(intervalRunner);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateIntervals() {
|
|
||||||
loadIntervals();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.MarkerEditActivity;
|
import de.dennisguse.opentracks.MarkerEditActivity;
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.content.data.Marker;
|
import de.dennisguse.opentracks.content.data.Marker;
|
||||||
@@ -43,7 +45,6 @@ import de.dennisguse.opentracks.util.IntentUtils;
|
|||||||
import de.dennisguse.opentracks.util.ListItemUtils;
|
import de.dennisguse.opentracks.util.ListItemUtils;
|
||||||
import de.dennisguse.opentracks.util.MarkerUtils;
|
import de.dennisguse.opentracks.util.MarkerUtils;
|
||||||
import de.dennisguse.opentracks.util.StringUtils;
|
import de.dennisguse.opentracks.util.StringUtils;
|
||||||
import de.dennisguse.opentracks.util.UnitConversions;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A fragment to show marker details.
|
* A fragment to show marker details.
|
||||||
@@ -55,7 +56,7 @@ public class MarkerDetailFragment extends Fragment {
|
|||||||
private static final String TAG = MarkerDetailFragment.class.getSimpleName();
|
private static final String TAG = MarkerDetailFragment.class.getSimpleName();
|
||||||
private static final String KEY_MARKER_ID = "markerId";
|
private static final String KEY_MARKER_ID = "markerId";
|
||||||
|
|
||||||
private static final long HIDE_TEXT_DELAY = 4 * UnitConversions.ONE_SECOND_MS;
|
private static final Duration HIDE_TEXT_DELAY = Duration.ofSeconds(4);
|
||||||
|
|
||||||
private MenuItem shareMarkerImageMenuItem;
|
private MenuItem shareMarkerImageMenuItem;
|
||||||
|
|
||||||
@@ -126,7 +127,7 @@ public class MarkerDetailFragment extends Fragment {
|
|||||||
viewBinding.markerDetailMarkerTextGradient.setVisibility(visibility);
|
viewBinding.markerDetailMarkerTextGradient.setVisibility(visibility);
|
||||||
viewBinding.markerDetailMarkerInfo.setVisibility(visibility);
|
viewBinding.markerDetailMarkerInfo.setVisibility(visibility);
|
||||||
if (visibility == View.VISIBLE) {
|
if (visibility == View.VISIBLE) {
|
||||||
handler.postDelayed(hideText, HIDE_TEXT_DELAY);
|
handler.postDelayed(hideText, HIDE_TEXT_DELAY.toMillis());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return viewBinding.getRoot();
|
return viewBinding.getRoot();
|
||||||
@@ -236,7 +237,7 @@ public class MarkerDetailFragment extends Fragment {
|
|||||||
if (hasPhoto) {
|
if (hasPhoto) {
|
||||||
handler.removeCallbacks(hideText);
|
handler.removeCallbacks(hideText);
|
||||||
viewBinding.markerDetailMarkerPhoto.setImageURI(marker.getPhotoURI());
|
viewBinding.markerDetailMarkerPhoto.setImageURI(marker.getPhotoURI());
|
||||||
handler.postDelayed(hideText, HIDE_TEXT_DELAY);
|
handler.postDelayed(hideText, HIDE_TEXT_DELAY.toMillis());
|
||||||
} else {
|
} else {
|
||||||
viewBinding.markerDetailMarkerPhoto.setImageResource(MarkerUtils.ICON_ID);
|
viewBinding.markerDetailMarkerPhoto.setImageResource(MarkerUtils.ICON_ID);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package de.dennisguse.opentracks.fragments;
|
|||||||
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -34,6 +35,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
|
import de.dennisguse.opentracks.TrackRecordedActivity;
|
||||||
import de.dennisguse.opentracks.adapters.SensorsAdapter;
|
import de.dennisguse.opentracks.adapters.SensorsAdapter;
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
@@ -53,6 +55,8 @@ import de.dennisguse.opentracks.viewmodels.SensorDataModel;
|
|||||||
*/
|
*/
|
||||||
public class StatisticsRecordedFragment extends Fragment {
|
public class StatisticsRecordedFragment extends Fragment {
|
||||||
|
|
||||||
|
private static final String TAG = StatisticsRecordedFragment.class.getSimpleName();
|
||||||
|
|
||||||
private static final String TRACK_ID_KEY = "trackId";
|
private static final String TRACK_ID_KEY = "trackId";
|
||||||
|
|
||||||
public static StatisticsRecordedFragment newInstance(Track.Id trackId) {
|
public static StatisticsRecordedFragment newInstance(Track.Id trackId) {
|
||||||
@@ -64,12 +68,13 @@ public class StatisticsRecordedFragment extends Fragment {
|
|||||||
return fragment;
|
return fragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private TrackStatistics trackStatistics;
|
|
||||||
private SensorStatistics sensorStatistics;
|
private SensorStatistics sensorStatistics;
|
||||||
private SensorsAdapter sensorsAdapter;
|
private SensorsAdapter sensorsAdapter;
|
||||||
private String category = "";
|
|
||||||
private Track.Id trackId;
|
private Track.Id trackId;
|
||||||
|
@Nullable // Lazily loaded.
|
||||||
|
private Track track;
|
||||||
|
|
||||||
private ContentProviderUtils contentProviderUtils;
|
private ContentProviderUtils contentProviderUtils;
|
||||||
|
|
||||||
private StatisticsRecordedBinding viewBinding;
|
private StatisticsRecordedBinding viewBinding;
|
||||||
@@ -86,9 +91,9 @@ public class StatisticsRecordedFragment extends Fragment {
|
|||||||
preferenceMetricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, getContext());
|
preferenceMetricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, getContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key)) {
|
if (PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key) && track != null) {
|
||||||
updateUInecessary = true;
|
updateUInecessary = true;
|
||||||
preferenceReportSpeed = PreferencesUtils.isReportSpeed(sharedPreferences, getContext(), category);
|
preferenceReportSpeed = PreferencesUtils.isReportSpeed(sharedPreferences, getContext(), track.getCategory());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key != null && updateUInecessary && isResumed()) {
|
if (key != null && updateUInecessary && isResumed()) {
|
||||||
@@ -152,27 +157,41 @@ public class StatisticsRecordedFragment extends Fragment {
|
|||||||
getActivity().runOnUiThread(() -> {
|
getActivity().runOnUiThread(() -> {
|
||||||
if (isResumed()) {
|
if (isResumed()) {
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
trackStatistics = track != null ? track.getTrackStatistics() : null;
|
if (track == null) {
|
||||||
|
Log.e(TAG, "track cannot be null");
|
||||||
|
getActivity().finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
sensorStatistics = contentProviderUtils.getSensorStats(trackId);
|
sensorStatistics = contentProviderUtils.getSensorStats(trackId);
|
||||||
|
|
||||||
String newCategory = track != null ? track.getCategory() : "";
|
boolean prefsChanged = this.track == null || (!this.track.getCategory().equals(track.getCategory()));
|
||||||
if (!category.equals(newCategory)) {
|
this.track = track;
|
||||||
category = newCategory;
|
if (prefsChanged) {
|
||||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, getString(R.string.stats_rate_key));
|
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, getString(R.string.stats_rate_key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadTrackDescription(track);
|
||||||
updateUI();
|
updateUI();
|
||||||
updateSensorUI();
|
updateSensorUI();
|
||||||
|
|
||||||
|
((TrackRecordedActivity) getActivity()).startPostponedEnterTransitionWith(viewBinding.statsActivityTypeIcon);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void loadTrackDescription(@NonNull Track track) {
|
||||||
|
viewBinding.statsNameValue.setText(track.getName());
|
||||||
|
viewBinding.statsDescriptionValue.setText(track.getDescription());
|
||||||
|
viewBinding.statsStartDatetimeValue.setText(StringUtils.formatDateTime(getContext(), track.getTrackStatistics().getStartTime()));
|
||||||
|
}
|
||||||
|
|
||||||
private void updateUI() {
|
private void updateUI() {
|
||||||
|
TrackStatistics trackStatistics = track.getTrackStatistics();
|
||||||
// Set total distance
|
// Set total distance
|
||||||
{
|
{
|
||||||
double totalDistance = trackStatistics == null ? Double.NaN : trackStatistics.getTotalDistance();
|
Pair<String, String> parts = StringUtils.getDistanceParts(getContext(), trackStatistics.getTotalDistance(), preferenceMetricUnits);
|
||||||
Pair<String, String> parts = StringUtils.getDistanceParts(getContext(), totalDistance, preferenceMetricUnits);
|
|
||||||
|
|
||||||
viewBinding.statsDistanceValue.setText(parts.first);
|
viewBinding.statsDistanceValue.setText(parts.first);
|
||||||
viewBinding.statsDistanceUnit.setText(parts.second);
|
viewBinding.statsDistanceUnit.setText(parts.second);
|
||||||
@@ -180,66 +199,61 @@ public class StatisticsRecordedFragment extends Fragment {
|
|||||||
|
|
||||||
// Set activity type
|
// Set activity type
|
||||||
{
|
{
|
||||||
String trackIconValue = TrackIconUtils.getIconValue(getContext(), category);
|
String trackIconValue = TrackIconUtils.getIconValue(getContext(), track.getCategory());
|
||||||
viewBinding.statsActivityTypeIcon.setImageDrawable(ContextCompat.getDrawable(getContext(), TrackIconUtils.getIconDrawable(trackIconValue)));
|
viewBinding.statsActivityTypeIcon.setImageDrawable(ContextCompat.getDrawable(getContext(), TrackIconUtils.getIconDrawable(trackIconValue)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set time and start datetime
|
// Set time and start datetime
|
||||||
if (trackStatistics != null) {
|
{
|
||||||
viewBinding.statsMovingTimeValue.setText(StringUtils.formatElapsedTime(trackStatistics.getMovingTime()));
|
viewBinding.statsMovingTimeValue.setText(StringUtils.formatElapsedTime(trackStatistics.getMovingTime()));
|
||||||
viewBinding.statsTotalTimeValue.setText(StringUtils.formatElapsedTime(trackStatistics.getTotalTime()));
|
viewBinding.statsTotalTimeValue.setText(StringUtils.formatElapsedTime(trackStatistics.getTotalTime()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set average speed/pace
|
// Set average speed/pace
|
||||||
{
|
{
|
||||||
double speed = trackStatistics != null ? trackStatistics.getAverageSpeed() : Double.NaN;
|
|
||||||
viewBinding.statsAverageSpeedLabel.setText(preferenceReportSpeed ? R.string.stats_average_speed : R.string.stats_average_pace);
|
viewBinding.statsAverageSpeedLabel.setText(preferenceReportSpeed ? R.string.stats_average_speed : R.string.stats_average_pace);
|
||||||
|
|
||||||
Pair<String, String> parts = StringUtils.getSpeedParts(getContext(), speed, preferenceMetricUnits, preferenceReportSpeed);
|
Pair<String, String> parts = StringUtils.getSpeedParts(getContext(), trackStatistics.getAverageSpeed(), preferenceMetricUnits, preferenceReportSpeed);
|
||||||
viewBinding.statsAverageSpeedValue.setText(parts.first);
|
viewBinding.statsAverageSpeedValue.setText(parts.first);
|
||||||
viewBinding.statsAverageSpeedUnit.setText(parts.second);
|
viewBinding.statsAverageSpeedUnit.setText(parts.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set max speed/pace
|
// Set max speed/pace
|
||||||
{
|
{
|
||||||
double speed = trackStatistics == null ? Double.NaN : trackStatistics.getMaxSpeed();
|
|
||||||
|
|
||||||
viewBinding.statsMaxSpeedLabel.setText(preferenceReportSpeed ? R.string.stats_max_speed : R.string.stats_fastest_pace);
|
viewBinding.statsMaxSpeedLabel.setText(preferenceReportSpeed ? R.string.stats_max_speed : R.string.stats_fastest_pace);
|
||||||
|
|
||||||
Pair<String, String> parts = StringUtils.getSpeedParts(getContext(), speed, preferenceMetricUnits, preferenceReportSpeed);
|
Pair<String, String> parts = StringUtils.getSpeedParts(getContext(), trackStatistics.getMaxSpeed(), preferenceMetricUnits, preferenceReportSpeed);
|
||||||
viewBinding.statsMaxSpeedValue.setText(parts.first);
|
viewBinding.statsMaxSpeedValue.setText(parts.first);
|
||||||
viewBinding.statsMaxSpeedUnit.setText(parts.second);
|
viewBinding.statsMaxSpeedUnit.setText(parts.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set moving speed/pace
|
// Set moving speed/pace
|
||||||
{
|
{
|
||||||
double speed = trackStatistics != null ? trackStatistics.getAverageMovingSpeed() : Double.NaN;
|
|
||||||
|
|
||||||
viewBinding.statsMovingSpeedLabel.setText(preferenceReportSpeed ? R.string.stats_average_moving_speed : R.string.stats_average_moving_pace);
|
viewBinding.statsMovingSpeedLabel.setText(preferenceReportSpeed ? R.string.stats_average_moving_speed : R.string.stats_average_moving_pace);
|
||||||
|
|
||||||
Pair<String, String> parts = StringUtils.getSpeedParts(getContext(), speed, preferenceMetricUnits, preferenceReportSpeed);
|
Pair<String, String> parts = StringUtils.getSpeedParts(getContext(), trackStatistics.getAverageMovingSpeed(), preferenceMetricUnits, preferenceReportSpeed);
|
||||||
viewBinding.statsMovingSpeedValue.setText(parts.first);
|
viewBinding.statsMovingSpeedValue.setText(parts.first);
|
||||||
viewBinding.statsMovingSpeedUnit.setText(parts.second);
|
viewBinding.statsMovingSpeedUnit.setText(parts.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set elevation gain and loss
|
// Set altitude gain and loss
|
||||||
{
|
{
|
||||||
// Make elevation visible?
|
// Make altitude visible?
|
||||||
boolean showElevation = PreferencesUtils.isShowStatsElevation(getContext());
|
boolean show = PreferencesUtils.isShowStatsAltitude(sharedPreferences, getContext());
|
||||||
viewBinding.statsElevationGroup.setVisibility(showElevation ? View.VISIBLE : View.GONE);
|
viewBinding.statsAltitudeGroup.setVisibility(show ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
Float elevationGain_m = trackStatistics != null ? trackStatistics.getTotalElevationGain() : null;
|
Float altitudeGain_m = trackStatistics.getTotalAltitudeGain();
|
||||||
Float elevationLoss_m = trackStatistics != null ? trackStatistics.getTotalElevationLoss() : null;
|
Float altitudeLoss_m = trackStatistics.getTotalAltitudeLoss();
|
||||||
|
|
||||||
Pair<String, String> parts;
|
Pair<String, String> parts;
|
||||||
|
|
||||||
parts = StringUtils.formatElevation(getContext(), elevationGain_m, preferenceMetricUnits);
|
parts = StringUtils.formatAltitude(getContext(), altitudeGain_m, preferenceMetricUnits);
|
||||||
viewBinding.statsElevationGainValue.setText(parts.first);
|
viewBinding.statsAltitudeGainValue.setText(parts.first);
|
||||||
viewBinding.statsElevationGainUnit.setText(parts.second);
|
viewBinding.statsAltitudeGainUnit.setText(parts.second);
|
||||||
|
|
||||||
parts = StringUtils.formatElevation(getContext(), elevationLoss_m, preferenceMetricUnits);
|
parts = StringUtils.formatAltitude(getContext(), altitudeLoss_m, preferenceMetricUnits);
|
||||||
viewBinding.statsElevationLossValue.setText(parts.first);
|
viewBinding.statsAltitudeLossValue.setText(parts.first);
|
||||||
viewBinding.statsElevationLossUnit.setText(parts.second);
|
viewBinding.statsAltitudeLossUnit.setText(parts.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,19 +24,20 @@ import de.dennisguse.opentracks.TrackRecordingActivity;
|
|||||||
import de.dennisguse.opentracks.adapters.SensorsAdapter;
|
import de.dennisguse.opentracks.adapters.SensorsAdapter;
|
||||||
import de.dennisguse.opentracks.content.TrackDataHub;
|
import de.dennisguse.opentracks.content.TrackDataHub;
|
||||||
import de.dennisguse.opentracks.content.TrackDataListener;
|
import de.dennisguse.opentracks.content.TrackDataListener;
|
||||||
|
import de.dennisguse.opentracks.content.data.Distance;
|
||||||
import de.dennisguse.opentracks.content.data.Marker;
|
import de.dennisguse.opentracks.content.data.Marker;
|
||||||
|
import de.dennisguse.opentracks.content.data.Speed;
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
import de.dennisguse.opentracks.content.sensor.SensorDataCycling;
|
import de.dennisguse.opentracks.content.sensor.SensorDataCycling;
|
||||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||||
import de.dennisguse.opentracks.databinding.StatisticsRecordingBinding;
|
import de.dennisguse.opentracks.databinding.StatisticsRecordingBinding;
|
||||||
|
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingServiceInterface;
|
|
||||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||||
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.util.UnitConversions;
|
|
||||||
import de.dennisguse.opentracks.viewmodels.SensorDataModel;
|
import de.dennisguse.opentracks.viewmodels.SensorDataModel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,12 +50,12 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
|||||||
|
|
||||||
private static final String TAG = StatisticsRecordingFragment.class.getSimpleName();
|
private static final String TAG = StatisticsRecordingFragment.class.getSimpleName();
|
||||||
|
|
||||||
private static final long UI_UPDATE_INTERVAL = UnitConversions.ONE_SECOND_MS;
|
private static final Duration UI_UPDATE_INTERVAL = Duration.ofSeconds(1);
|
||||||
|
|
||||||
public static Fragment newInstance() {
|
public static Fragment newInstance() {
|
||||||
return new StatisticsRecordingFragment();
|
return new StatisticsRecordingFragment();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private TrackDataHub trackDataHub;
|
private TrackDataHub trackDataHub;
|
||||||
|
|
||||||
private Handler handlerUpdateUI;
|
private Handler handlerUpdateUI;
|
||||||
@@ -62,9 +63,8 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
|||||||
private TrackRecordingServiceConnection trackRecordingServiceConnection = new TrackRecordingServiceConnection();
|
private TrackRecordingServiceConnection trackRecordingServiceConnection = new TrackRecordingServiceConnection();
|
||||||
private TrackPoint lastTrackPoint;
|
private TrackPoint lastTrackPoint;
|
||||||
|
|
||||||
private TrackStatistics lastTrackStatistics;
|
@Nullable // Lazily loaded.
|
||||||
|
private Track track;
|
||||||
private String category = "";
|
|
||||||
|
|
||||||
private StatisticsRecordingBinding viewBinding;
|
private StatisticsRecordingBinding viewBinding;
|
||||||
private SensorsAdapter sensorsAdapter;
|
private SensorsAdapter sensorsAdapter;
|
||||||
@@ -72,7 +72,7 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
|||||||
private SharedPreferences sharedPreferences;
|
private SharedPreferences sharedPreferences;
|
||||||
private boolean preferenceMetricUnits;
|
private boolean preferenceMetricUnits;
|
||||||
private boolean preferenceReportSpeed;
|
private boolean preferenceReportSpeed;
|
||||||
private boolean preferenceShowElevation;
|
private boolean preferenceShowAltitude;
|
||||||
private boolean preferenceShowCoordinate;
|
private boolean preferenceShowCoordinate;
|
||||||
|
|
||||||
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
|
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
|
||||||
@@ -83,14 +83,14 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
|||||||
preferenceMetricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, getContext());
|
preferenceMetricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, getContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key)) {
|
if (PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key) && track != null) {
|
||||||
updateUInecessary = true;
|
updateUInecessary = true;
|
||||||
preferenceReportSpeed = PreferencesUtils.isReportSpeed(sharedPreferences, getContext(), category);
|
preferenceReportSpeed = PreferencesUtils.isReportSpeed(sharedPreferences, getContext(), track.getCategory());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PreferencesUtils.isKey(getContext(), R.string.stats_show_grade_elevation_key, key)) {
|
if (PreferencesUtils.isKey(getContext(), R.string.stats_show_grade_altitude_key, key)) {
|
||||||
updateUInecessary = true;
|
updateUInecessary = true;
|
||||||
preferenceShowElevation = PreferencesUtils.isShowStatsElevation(sharedPreferences, getContext());
|
preferenceShowAltitude = PreferencesUtils.isShowStatsAltitude(sharedPreferences, getContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PreferencesUtils.isKey(getContext(), R.string.stats_show_coordinate_key, key)) {
|
if (PreferencesUtils.isKey(getContext(), R.string.stats_show_coordinate_key, key)) {
|
||||||
@@ -110,7 +110,7 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
|||||||
private final Runnable updateUIeachSecond = new Runnable() {
|
private final Runnable updateUIeachSecond = new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
if (isResumed() && isSelectedTrackRecording()) {
|
if (isResumed() && isSelectedTrackRecording()) {
|
||||||
if (!isSelectedTrackPaused() && lastTrackStatistics != null) {
|
if (!isSelectedTrackPaused() && track != null) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (lastTrackPoint != null && lastTrackPoint.hasLocation() && !lastTrackPoint.isRecent()) {
|
if (lastTrackPoint != null && lastTrackPoint.hasLocation() && !lastTrackPoint.isRecent()) {
|
||||||
lastTrackPoint = null;
|
lastTrackPoint = null;
|
||||||
@@ -121,7 +121,7 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
|||||||
updateSensorDataUI();
|
updateSensorDataUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
handlerUpdateUI.postDelayed(this, UI_UPDATE_INTERVAL);
|
handlerUpdateUI.postDelayed(this, UI_UPDATE_INTERVAL.toMillis());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -139,7 +139,7 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
|||||||
@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);
|
||||||
viewBinding.statsActivityTypeIcon.setOnClickListener(v -> ((TrackRecordingActivity) getActivity()).chooseActivityType(category));
|
viewBinding.statsActivityTypeIcon.setOnClickListener(v -> ((TrackRecordingActivity) getActivity()).chooseActivityType(track != null ? track.getCategory() : "")); //TODO "" would be useless as Track was not yet loaded.
|
||||||
|
|
||||||
RecyclerView sensorsRecyclerView = viewBinding.statsSensorsRecyclerView;
|
RecyclerView sensorsRecyclerView = viewBinding.statsSensorsRecyclerView;
|
||||||
sensorsRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2));
|
sensorsRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2));
|
||||||
@@ -196,12 +196,10 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
|||||||
if (isResumed()) {
|
if (isResumed()) {
|
||||||
getActivity().runOnUiThread(() -> {
|
getActivity().runOnUiThread(() -> {
|
||||||
if (isResumed()) {
|
if (isResumed()) {
|
||||||
lastTrackStatistics = track != null ? track.getTrackStatistics() : null;
|
if ((this.track == null && track != null) || (this.track != null && track != null && !this.track.getCategory().equals(track.getCategory()))) {
|
||||||
String newCategory = track != null ? track.getCategory() : "";
|
|
||||||
if (!category.equals(newCategory)) {
|
|
||||||
category = newCategory;
|
|
||||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, getString(R.string.stats_rate_key));
|
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, getString(R.string.stats_rate_key));
|
||||||
}
|
}
|
||||||
|
this.track = track;
|
||||||
updateUI();
|
updateUI();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -214,17 +212,7 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSampledInTrackPoint(@NonNull TrackPoint trackPoint) {
|
public void onNewTrackPointsDone(@NonNull TrackPoint newLastTrackPoint, @NonNull TrackStatistics trackStatistics) {
|
||||||
// We don't care.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSampledOutTrackPoint(@NonNull TrackPoint trackPoint) {
|
|
||||||
// We don't care.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNewTrackPointsDone(@NonNull TrackPoint newLastTrackPoint) {
|
|
||||||
if (isResumed()) {
|
if (isResumed()) {
|
||||||
getActivity().runOnUiThread(() -> {
|
getActivity().runOnUiThread(() -> {
|
||||||
if (isResumed()) {
|
if (isResumed()) {
|
||||||
@@ -302,15 +290,15 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Tries to fetch most recent {@link SensorDataSet} from {@link de.dennisguse.opentracks.services.TrackRecordingService}.
|
* Tries to fetch most recent {@link SensorDataSet} from {@link de.dennisguse.opentracks.services.TrackRecordingService}.
|
||||||
* Also sets elevation gain and loss.
|
* Also sets altitude gain and loss.
|
||||||
*/
|
*/
|
||||||
private void updateSensorDataUI() {
|
private void updateSensorDataUI() {
|
||||||
TrackRecordingServiceInterface trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
TrackRecordingService trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
||||||
|
|
||||||
if (trackRecordingService == null) {
|
if (trackRecordingService == null) {
|
||||||
Log.d(TAG, "Cannot get the track recording service.");
|
Log.d(TAG, "Cannot get the track recording service.");
|
||||||
} else {
|
} else {
|
||||||
SensorDataSet sensorDataSet = trackRecordingService.getSensorData();
|
SensorDataSet sensorDataSet = trackRecordingService.getSensorDataSet();
|
||||||
if (sensorDataSet != null) {
|
if (sensorDataSet != null) {
|
||||||
List<SensorDataModel> sensorDataList = new ArrayList<>();
|
List<SensorDataModel> sensorDataList = new ArrayList<>();
|
||||||
if (sensorDataSet.getHeartRate() != null) {
|
if (sensorDataSet.getHeartRate() != null) {
|
||||||
@@ -325,83 +313,80 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
|||||||
sensorsAdapter.swapData(sensorDataList);
|
sensorsAdapter.swapData(sensorDataList);
|
||||||
setSpeedSensorData(sensorDataSet);
|
setSpeedSensorData(sensorDataSet);
|
||||||
}
|
}
|
||||||
//TODO Check if we can distribute the total elevation gain and loss via trackStatistics instead of doing some computation in the UI layer.
|
//TODO Check if we can get the total altitude gain and loss via trackStatistics instead of doing some computation in the UI layer.
|
||||||
setTotalElevationGain(trackRecordingService.getElevationGain_m());
|
setTotalAltitudeGain(trackRecordingService.getAltitudeGain_m());
|
||||||
setTotalElevationLoss(trackRecordingService.getElevationLoss_m());
|
setTotalAltitudeLoss(trackRecordingService.getAltitudeLoss_m());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set elevation gain
|
// Set altitude gain
|
||||||
private void setTotalElevationGain(Float elevationGain_m) {
|
private void setTotalAltitudeGain(Float altitudeGain_m) {
|
||||||
Float totalElevationGain = elevationGain_m;
|
Float totalAltitudeGain = altitudeGain_m;
|
||||||
|
|
||||||
if (lastTrackStatistics != null && lastTrackStatistics.hasTotalElevationGain()) {
|
if (track != null && track.getTrackStatistics().hasTotalAltitudeGain()) {
|
||||||
if (elevationGain_m == null) {
|
if (altitudeGain_m == null) {
|
||||||
totalElevationGain = lastTrackStatistics.getTotalElevationGain();
|
totalAltitudeGain = 0f;
|
||||||
} else {
|
|
||||||
totalElevationGain += lastTrackStatistics.getTotalElevationGain();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
totalAltitudeGain += track.getTrackStatistics().getTotalAltitudeGain();
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair<String, String> parts = StringUtils.formatElevation(getContext(), totalElevationGain, preferenceMetricUnits);
|
Pair<String, String> parts = StringUtils.formatAltitude(getContext(), totalAltitudeGain, preferenceMetricUnits);
|
||||||
viewBinding.statsElevationGainValue.setText(parts.first);
|
viewBinding.statsAltitudeGainValue.setText(parts.first);
|
||||||
viewBinding.statsElevationGainUnit.setText(parts.second);
|
viewBinding.statsAltitudeGainUnit.setText(parts.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set elevation loss
|
// Set altitude loss
|
||||||
private void setTotalElevationLoss(Float elevationLoss_m) {
|
private void setTotalAltitudeLoss(Float altitudeLoss_m) {
|
||||||
Float totalElevationLoss = elevationLoss_m;
|
Float totalAltitudeLoss = altitudeLoss_m;
|
||||||
|
|
||||||
if (lastTrackStatistics != null && lastTrackStatistics.hasTotalElevationLoss()) {
|
if (track != null && track.getTrackStatistics().hasTotalAltitudeLoss()) {
|
||||||
if (elevationLoss_m == null) {
|
if (altitudeLoss_m == null) {
|
||||||
totalElevationLoss = lastTrackStatistics.getTotalElevationLoss();
|
totalAltitudeLoss = track.getTrackStatistics().getTotalAltitudeLoss();
|
||||||
} else {
|
} else {
|
||||||
totalElevationLoss += lastTrackStatistics.getTotalElevationLoss();
|
totalAltitudeLoss += track.getTrackStatistics().getTotalAltitudeLoss();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Pair<String, String> parts = StringUtils.formatElevation(getContext(), totalElevationLoss, preferenceMetricUnits);
|
Pair<String, String> parts = StringUtils.formatAltitude(getContext(), totalAltitudeLoss, preferenceMetricUnits);
|
||||||
viewBinding.statsElevationLossValue.setText(parts.first);
|
viewBinding.statsAltitudeLossValue.setText(parts.first);
|
||||||
viewBinding.statsElevationLossUnit.setText(parts.second);
|
viewBinding.statsAltitudeLossUnit.setText(parts.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setSpeedSensorData(SensorDataSet sensorDataSet) {
|
private void setSpeedSensorData(SensorDataSet sensorDataSet) {
|
||||||
if (sensorDataSet != null && sensorDataSet.getCyclingSpeed() != null) {
|
if (sensorDataSet != null && sensorDataSet.getCyclingDistanceSpeed() != null) {
|
||||||
SensorDataCycling.Speed data = sensorDataSet.getCyclingSpeed();
|
SensorDataCycling.DistanceSpeed data = sensorDataSet.getCyclingDistanceSpeed();
|
||||||
|
|
||||||
if (data.hasValue() && data.isRecent()) {
|
if (data.hasValue() && data.isRecent()) {
|
||||||
setSpeed(data.getValue());
|
setTotalDistance(data.getValue().distanceOverall);
|
||||||
|
setSpeed(data.getValue().speed);
|
||||||
|
}
|
||||||
|
if (data.hasValue() && data.isRecent()) {
|
||||||
|
setSpeed(data.getValue().speed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateStats() {
|
private void updateStats() {
|
||||||
String trackIconValue = TrackIconUtils.getIconValue(getContext(), category);
|
|
||||||
|
|
||||||
// Set total distance
|
setTotalDistance(Distance.of(0)); //TODO Why?
|
||||||
{
|
|
||||||
double totalDistance = lastTrackStatistics == null ? Double.NaN : lastTrackStatistics.getTotalDistance();
|
|
||||||
Pair<String, String> parts = StringUtils.getDistanceParts(getContext(), totalDistance, preferenceMetricUnits);
|
|
||||||
|
|
||||||
viewBinding.statsDistanceValue.setText(parts.first);
|
|
||||||
viewBinding.statsDistanceUnit.setText(parts.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set activity type
|
// Set activity type
|
||||||
{
|
if (track != null) {
|
||||||
|
String trackIconValue = TrackIconUtils.getIconValue(getContext(), track.getCategory());
|
||||||
viewBinding.statsActivityTypeIcon.setEnabled(isSelectedTrackRecording());
|
viewBinding.statsActivityTypeIcon.setEnabled(isSelectedTrackRecording());
|
||||||
viewBinding.statsActivityTypeIcon.setImageResource(TrackIconUtils.getIconDrawable(trackIconValue));
|
viewBinding.statsActivityTypeIcon.setImageResource(TrackIconUtils.getIconDrawable(trackIconValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set time
|
// Set time
|
||||||
if (lastTrackStatistics != null) {
|
if (track != null) {
|
||||||
viewBinding.statsMovingTimeValue.setText(StringUtils.formatElapsedTime(lastTrackStatistics.getMovingTime()));
|
viewBinding.statsMovingTimeValue.setText(StringUtils.formatElapsedTime(track.getTrackStatistics().getMovingTime()));
|
||||||
updateTotalTime();
|
updateTotalTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set average speed/pace
|
// Set average speed/pace
|
||||||
{
|
{
|
||||||
double speed = lastTrackStatistics != null ? lastTrackStatistics.getAverageSpeed() : Double.NaN;
|
Speed speed = track != null ? track.getTrackStatistics().getAverageSpeed() : null;
|
||||||
viewBinding.statsAverageSpeedLabel.setText(preferenceReportSpeed ? R.string.stats_average_speed : R.string.stats_average_pace);
|
viewBinding.statsAverageSpeedLabel.setText(preferenceReportSpeed ? R.string.stats_average_speed : R.string.stats_average_pace);
|
||||||
|
|
||||||
Pair<String, String> parts = StringUtils.getSpeedParts(getContext(), speed, preferenceMetricUnits, preferenceReportSpeed);
|
Pair<String, String> parts = StringUtils.getSpeedParts(getContext(), speed, preferenceMetricUnits, preferenceReportSpeed);
|
||||||
@@ -411,7 +396,7 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
|||||||
|
|
||||||
// Set max speed/pace
|
// Set max speed/pace
|
||||||
{
|
{
|
||||||
double speed = lastTrackStatistics == null ? Double.NaN : lastTrackStatistics.getMaxSpeed();
|
Speed speed = track != null ? track.getTrackStatistics().getMaxSpeed() : null;
|
||||||
|
|
||||||
viewBinding.statsMaxSpeedLabel.setText(preferenceReportSpeed ? R.string.stats_max_speed : R.string.stats_fastest_pace);
|
viewBinding.statsMaxSpeedLabel.setText(preferenceReportSpeed ? R.string.stats_max_speed : R.string.stats_fastest_pace);
|
||||||
|
|
||||||
@@ -422,7 +407,7 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
|||||||
|
|
||||||
// Set moving speed/pace
|
// Set moving speed/pace
|
||||||
{
|
{
|
||||||
double speed = lastTrackStatistics != null ? lastTrackStatistics.getAverageMovingSpeed() : Double.NaN;
|
Speed speed = track != null ? track.getTrackStatistics().getAverageMovingSpeed() : null;
|
||||||
|
|
||||||
viewBinding.statsMovingSpeedLabel.setText(preferenceReportSpeed ? R.string.stats_average_moving_speed : R.string.stats_average_moving_pace);
|
viewBinding.statsMovingSpeedLabel.setText(preferenceReportSpeed ? R.string.stats_average_moving_speed : R.string.stats_average_moving_pace);
|
||||||
|
|
||||||
@@ -431,16 +416,16 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
|||||||
viewBinding.statsMovingSpeedUnit.setText(parts.second);
|
viewBinding.statsMovingSpeedUnit.setText(parts.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set elevation gain and loss
|
// Set altitude gain and loss
|
||||||
{
|
{
|
||||||
viewBinding.statsElevationGroup.setVisibility(preferenceShowElevation ? View.VISIBLE : View.GONE);
|
viewBinding.statsAltitudeGroup.setVisibility(preferenceShowAltitude ? View.VISIBLE : View.GONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateTotalTime() {
|
private void updateTotalTime() {
|
||||||
Duration totalTime = lastTrackStatistics.getTotalTime();
|
Duration totalTime = track.getTrackStatistics().getTotalTime();
|
||||||
if (isSelectedTrackRecording()) {
|
if (isSelectedTrackRecording()) {
|
||||||
TrackRecordingServiceInterface trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
TrackRecordingService trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
||||||
if (trackRecordingService != null) {
|
if (trackRecordingService != null) {
|
||||||
totalTime = trackRecordingService.getTotalTime();
|
totalTime = trackRecordingService.getTotalTime();
|
||||||
}
|
}
|
||||||
@@ -451,18 +436,18 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
|||||||
|
|
||||||
private void setLocationValues() {
|
private void setLocationValues() {
|
||||||
// Set speed/pace
|
// Set speed/pace
|
||||||
double speed = lastTrackPoint != null && lastTrackPoint.hasSpeed() ? lastTrackPoint.getSpeed() : Double.NaN;
|
Speed speed = lastTrackPoint != null && lastTrackPoint.hasSpeed() ? lastTrackPoint.getSpeed() : null;
|
||||||
setSpeed(speed);
|
setSpeed(speed);
|
||||||
|
|
||||||
// Set elevation
|
// Set altitude
|
||||||
viewBinding.statsElevationGroup.setVisibility(preferenceShowElevation ? View.VISIBLE : View.GONE);
|
viewBinding.statsAltitudeGroup.setVisibility(preferenceShowAltitude ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
if (preferenceShowElevation) {
|
if (preferenceShowAltitude) {
|
||||||
// Current elevation
|
// Current altitude
|
||||||
Float altitude = lastTrackPoint != null && lastTrackPoint.hasAltitude() ? (float) lastTrackPoint.getAltitude() : null;
|
Float altitude = lastTrackPoint != null && lastTrackPoint.hasAltitude() ? (float) lastTrackPoint.getAltitude() : null;
|
||||||
Pair<String, String> parts = StringUtils.formatElevation(getContext(), altitude, preferenceMetricUnits);
|
Pair<String, String> parts = StringUtils.formatAltitude(getContext(), altitude, preferenceMetricUnits);
|
||||||
viewBinding.statsElevationCurrentValue.setText(parts.first);
|
viewBinding.statsAltitudeCurrentValue.setText(parts.first);
|
||||||
viewBinding.statsElevationCurrentUnit.setText(parts.second);
|
viewBinding.statsAltitudeCurrentUnit.setText(parts.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set coordinate
|
// Set coordinate
|
||||||
@@ -479,7 +464,15 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setSpeed(double speed) {
|
private void setTotalDistance(Distance sensorDistanceSinceLastTrackpoint) {
|
||||||
|
Distance totalDistance = track != null ? (track.getTrackStatistics().getTotalDistance().plus(sensorDistanceSinceLastTrackpoint)) : Distance.invalid();
|
||||||
|
Pair<String, String> parts = StringUtils.getDistanceParts(getContext(), totalDistance, preferenceMetricUnits);
|
||||||
|
|
||||||
|
viewBinding.statsDistanceValue.setText(parts.first);
|
||||||
|
viewBinding.statsDistanceUnit.setText(parts.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setSpeed(Speed speed) {
|
||||||
viewBinding.statsSpeedLabel.setText(preferenceReportSpeed ? R.string.stats_speed : R.string.stats_pace);
|
viewBinding.statsSpeedLabel.setText(preferenceReportSpeed ? R.string.stats_speed : R.string.stats_pace);
|
||||||
|
|
||||||
Pair<String, String> parts = StringUtils.getSpeedParts(getContext(), speed, preferenceMetricUnits, preferenceReportSpeed);
|
Pair<String, String> parts = StringUtils.getSpeedParts(getContext(), speed, preferenceMetricUnits, preferenceReportSpeed);
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
|||||||
private ContentProviderUtils contentProviderUtils;
|
private ContentProviderUtils contentProviderUtils;
|
||||||
|
|
||||||
// List of tracks to be exported.
|
// List of tracks to be exported.
|
||||||
private final ArrayList<Track.Id> trackIds = new ArrayList<>();
|
private final ArrayList<Track> tracks = new ArrayList<>();
|
||||||
|
|
||||||
private final LinkedBlockingQueue<PendingConflict> conflictsQueue = new LinkedBlockingQueue<>();
|
private final LinkedBlockingQueue<PendingConflict> conflictsQueue = new LinkedBlockingQueue<>();
|
||||||
private final Handler conflictsHandler = new Handler();
|
private final Handler conflictsHandler = new Handler();
|
||||||
@@ -187,7 +187,7 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
|||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
conflictsQueue.clear();
|
conflictsQueue.clear();
|
||||||
trackIds.clear();
|
tracks.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -215,11 +215,11 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
|||||||
for (int i = from; i < trackExportTotalCount; i++) {
|
for (int i = from; i < trackExportTotalCount; i++) {
|
||||||
cursor.moveToPosition(i);
|
cursor.moveToPosition(i);
|
||||||
Track track = ContentProviderUtils.createTrack(cursor);
|
Track track = ContentProviderUtils.createTrack(cursor);
|
||||||
trackIds.add(track.getId());
|
tracks.add(track);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!trackIds.isEmpty()) {
|
if (!tracks.isEmpty()) {
|
||||||
export(trackIds.get(0));
|
export(tracks.get(0));
|
||||||
} else {
|
} else {
|
||||||
onExportEnded();
|
onExportEnded();
|
||||||
}
|
}
|
||||||
@@ -227,26 +227,26 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enqueue track identified by trackId to be exported if not exported already or there is a conflict resolution.
|
* Enqueue track identified by UUID to be exported if not exported already or there is a conflict resolution.
|
||||||
*
|
*
|
||||||
* @param trackId the track's id.
|
* @param track Track object.
|
||||||
* @param conflictResolution conflict resolution to be applied if needed.
|
* @param conflictResolution conflict resolution to be applied if needed.
|
||||||
*/
|
*/
|
||||||
private void export(Track.Id trackId, int conflictResolution) {
|
private void export(Track track, int conflictResolution) {
|
||||||
boolean fileExists = ExportUtils.isExportFileExists(trackId, trackFileFormat.getExtension(), directoryFiles);
|
boolean fileExists = ExportUtils.isExportFileExists(track.getUuid(), trackFileFormat.getExtension(), directoryFiles);
|
||||||
|
|
||||||
if (fileExists && conflictResolution == CONFLICT_NONE) {
|
if (fileExists && conflictResolution == CONFLICT_NONE) {
|
||||||
conflict(trackId);
|
conflict(track);
|
||||||
} else if (fileExists && conflictResolution == CONFLICT_SKIP) {
|
} else if (fileExists && conflictResolution == CONFLICT_SKIP) {
|
||||||
trackExportSkippedCount++;
|
trackExportSkippedCount++;
|
||||||
onExportCompleted(trackId);
|
onExportCompleted(track);
|
||||||
} else {
|
} else {
|
||||||
ExportService.enqueue(this, resultReceiver, trackId, trackFileFormat, directoryUri);
|
ExportService.enqueue(this, resultReceiver, track.getId(), trackFileFormat, directoryUri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void export(Track.Id trackId) {
|
private void export(Track track) {
|
||||||
export(trackId, autoConflict);
|
export(track, autoConflict);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setConflictVisibility(int visibility) {
|
private void setConflictVisibility(int visibility) {
|
||||||
@@ -278,14 +278,15 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
|||||||
viewBinding.exportProgressSummaryErrorsGroup.setVisibility(trackExportErrorCount > 0 ? View.VISIBLE : View.GONE);
|
viewBinding.exportProgressSummaryErrorsGroup.setVisibility(trackExportErrorCount > 0 ? View.VISIBLE : View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onExportCompleted(Track.Id trackId) {
|
private void onExportCompleted(Track track) {
|
||||||
trackIds.remove(trackId);
|
tracks.remove(track);
|
||||||
|
|
||||||
setProgress();
|
setProgress();
|
||||||
if (trackIds.isEmpty()) {
|
if (tracks.isEmpty()) {
|
||||||
onExportEnded();
|
onExportEnded();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
export(trackIds.get(0));
|
export(tracks.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onExportEnded() {
|
private void onExportEnded() {
|
||||||
@@ -317,15 +318,15 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
|||||||
}
|
}
|
||||||
|
|
||||||
Track.Id trackId = resultData.getParcelable(ExportServiceResultReceiver.RESULT_EXTRA_TRACK_ID);
|
Track.Id trackId = resultData.getParcelable(ExportServiceResultReceiver.RESULT_EXTRA_TRACK_ID);
|
||||||
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
|
|
||||||
switch (resultCode) {
|
switch (resultCode) {
|
||||||
case ExportServiceResultReceiver.RESULT_CODE_ERROR:
|
case ExportServiceResultReceiver.RESULT_CODE_ERROR:
|
||||||
trackExportErrorCount++;
|
trackExportErrorCount++;
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
|
||||||
trackErrors.add(track.getName());
|
trackErrors.add(track.getName());
|
||||||
break;
|
break;
|
||||||
case ExportServiceResultReceiver.RESULT_CODE_SUCCESS:
|
case ExportServiceResultReceiver.RESULT_CODE_SUCCESS:
|
||||||
if (ExportUtils.isExportFileExists(trackId, trackFileFormat.getExtension(), directoryFiles)) {
|
if (ExportUtils.isExportFileExists(track.getUuid(), trackFileFormat.getExtension(), directoryFiles)) {
|
||||||
trackExportOverwrittenCount++;
|
trackExportOverwrittenCount++;
|
||||||
} else {
|
} else {
|
||||||
trackExportSuccessCount++;
|
trackExportSuccessCount++;
|
||||||
@@ -335,11 +336,11 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
|||||||
throw new RuntimeException(TAG + ": export service result code invalid: " + resultCode);
|
throw new RuntimeException(TAG + ": export service result code invalid: " + resultCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
onExportCompleted(trackId);
|
onExportCompleted(track);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void conflict(Track.Id trackId) {
|
private void conflict(Track track) {
|
||||||
PendingConflict newConflict = new PendingConflict(trackId);
|
PendingConflict newConflict = new PendingConflict(track);
|
||||||
conflictsQueue.add(newConflict);
|
conflictsQueue.add(newConflict);
|
||||||
|
|
||||||
if (conflictsQueue.size() == 1) {
|
if (conflictsQueue.size() == 1) {
|
||||||
@@ -351,10 +352,10 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
|||||||
* Handle conflicts (exporting file already exists).
|
* Handle conflicts (exporting file already exists).
|
||||||
*/
|
*/
|
||||||
private class PendingConflict {
|
private class PendingConflict {
|
||||||
private final Track.Id trackId;
|
private final Track track;
|
||||||
|
|
||||||
public PendingConflict(Track.Id trackId) {
|
public PendingConflict(Track track) {
|
||||||
this.trackId = trackId;
|
this.track = track;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -365,14 +366,13 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
|||||||
*/
|
*/
|
||||||
public boolean resolve() {
|
public boolean resolve() {
|
||||||
if (autoConflict == CONFLICT_NONE) {
|
if (autoConflict == CONFLICT_NONE) {
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
|
||||||
viewBinding.exportProgressAlertIcon.setImageDrawable(ContextCompat.getDrawable(ExportActivity.this, R.drawable.ic_report_problem_24));
|
viewBinding.exportProgressAlertIcon.setImageDrawable(ContextCompat.getDrawable(ExportActivity.this, R.drawable.ic_report_problem_24));
|
||||||
viewBinding.exportProgressAlertMsg.setText(getString(R.string.export_track_already_exists_msg, track.getName()));
|
viewBinding.exportProgressAlertMsg.setText(getString(R.string.export_track_already_exists_msg, track.getName()));
|
||||||
setConflictVisibility(View.VISIBLE);
|
setConflictVisibility(View.VISIBLE);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export(trackId);
|
export(track);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,7 +380,7 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
|||||||
* Overwrite the export file and set the autoConflict if user set the "do it for all" switch button.
|
* Overwrite the export file and set the autoConflict if user set the "do it for all" switch button.
|
||||||
*/
|
*/
|
||||||
public void overwrite() {
|
public void overwrite() {
|
||||||
export(trackId, CONFLICT_OVERWRITE);
|
export(track, CONFLICT_OVERWRITE);
|
||||||
|
|
||||||
if (viewBinding.exportProgressApplyToAll.isChecked()) {
|
if (viewBinding.exportProgressApplyToAll.isChecked()) {
|
||||||
autoConflict = CONFLICT_OVERWRITE;
|
autoConflict = CONFLICT_OVERWRITE;
|
||||||
@@ -391,7 +391,7 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
|||||||
* Skip the export file and set the autoConflict if user set the "do it for all" switch button.
|
* Skip the export file and set the autoConflict if user set the "do it for all" switch button.
|
||||||
*/
|
*/
|
||||||
public void skip() {
|
public void skip() {
|
||||||
export(trackId, CONFLICT_SKIP);
|
export(track, CONFLICT_SKIP);
|
||||||
|
|
||||||
if (viewBinding.exportProgressApplyToAll.isChecked()) {
|
if (viewBinding.exportProgressApplyToAll.isChecked()) {
|
||||||
autoConflict = CONFLICT_SKIP;
|
autoConflict = CONFLICT_SKIP;
|
||||||
|
|||||||
@@ -46,19 +46,21 @@ public class GPXTrackExporter implements TrackExporter {
|
|||||||
|
|
||||||
private static final String TAG = GPXTrackExporter.class.getSimpleName();
|
private static final String TAG = GPXTrackExporter.class.getSimpleName();
|
||||||
|
|
||||||
private static final NumberFormat ELEVATION_FORMAT = NumberFormat.getInstance(Locale.US);
|
private static final NumberFormat ALTITUDE_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||||
private static final NumberFormat COORDINATE_FORMAT = NumberFormat.getInstance(Locale.US);
|
private static final NumberFormat COORDINATE_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||||
private static final NumberFormat SPEED_FORMAT = NumberFormat.getInstance(Locale.US);
|
private static final NumberFormat SPEED_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||||
|
private static final NumberFormat DISTANCE_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||||
private static final NumberFormat HEARTRATE_FORMAT = NumberFormat.getInstance(Locale.US);
|
private static final NumberFormat HEARTRATE_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||||
private static final NumberFormat CADENCE_FORMAT = NumberFormat.getInstance(Locale.US);
|
private static final NumberFormat CADENCE_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||||
|
private static final NumberFormat POWER_FORMAT = NumberFormat.getInstance(Locale.US);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
/*
|
/*
|
||||||
* GPX readers expect to see fractional numbers with US-style punctuation.
|
* GPX readers expect to see fractional numbers with US-style punctuation.
|
||||||
* That is, they want periods for decimal points, rather than commas.
|
* That is, they want periods for decimal points, rather than commas.
|
||||||
*/
|
*/
|
||||||
ELEVATION_FORMAT.setMaximumFractionDigits(1);
|
ALTITUDE_FORMAT.setMaximumFractionDigits(1);
|
||||||
ELEVATION_FORMAT.setGroupingUsed(false);
|
ALTITUDE_FORMAT.setGroupingUsed(false);
|
||||||
|
|
||||||
COORDINATE_FORMAT.setMaximumFractionDigits(6);
|
COORDINATE_FORMAT.setMaximumFractionDigits(6);
|
||||||
COORDINATE_FORMAT.setMaximumIntegerDigits(3);
|
COORDINATE_FORMAT.setMaximumIntegerDigits(3);
|
||||||
@@ -72,6 +74,9 @@ public class GPXTrackExporter implements TrackExporter {
|
|||||||
|
|
||||||
CADENCE_FORMAT.setMaximumFractionDigits(0);
|
CADENCE_FORMAT.setMaximumFractionDigits(0);
|
||||||
CADENCE_FORMAT.setGroupingUsed(false);
|
CADENCE_FORMAT.setGroupingUsed(false);
|
||||||
|
|
||||||
|
POWER_FORMAT.setMaximumFractionDigits(0);
|
||||||
|
POWER_FORMAT.setGroupingUsed(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ContentProviderUtils contentProviderUtils;
|
private final ContentProviderUtils contentProviderUtils;
|
||||||
@@ -91,15 +96,16 @@ public class GPXTrackExporter implements TrackExporter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean writeTrack(Track[] tracks, @NonNull OutputStream outputStream) {
|
public boolean writeTrack(Track[] tracks, @NonNull OutputStream outputStream) {
|
||||||
Track mainTrack = tracks[0];
|
|
||||||
try {
|
try {
|
||||||
prepare(outputStream);
|
prepare(outputStream);
|
||||||
writeHeader(mainTrack);
|
writeHeader();
|
||||||
|
|
||||||
writeMarkers(mainTrack);
|
|
||||||
|
|
||||||
for (Track track : tracks) {
|
for (Track track : tracks) {
|
||||||
writeLocations(track);
|
writeMarkers(track);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Track track : tracks) {
|
||||||
|
writeTrackPoints(track);
|
||||||
}
|
}
|
||||||
|
|
||||||
writeFooter();
|
writeFooter();
|
||||||
@@ -112,7 +118,7 @@ public class GPXTrackExporter implements TrackExporter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeLocations(Track track) throws InterruptedException {
|
private void writeTrackPoints(Track track) throws InterruptedException {
|
||||||
boolean wroteTrack = false;
|
boolean wroteTrack = false;
|
||||||
boolean wroteSegment = false;
|
boolean wroteSegment = false;
|
||||||
|
|
||||||
@@ -183,7 +189,7 @@ public class GPXTrackExporter implements TrackExporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void writeHeader(Track track) {
|
public void writeHeader() {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
||||||
printWriter.println("<gpx");
|
printWriter.println("<gpx");
|
||||||
@@ -194,18 +200,14 @@ public class GPXTrackExporter implements TrackExporter {
|
|||||||
printWriter.println("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
|
printWriter.println("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
|
||||||
printWriter.println("xmlns:atom=\"http://www.w3.org/2005/Atom\"");
|
printWriter.println("xmlns:atom=\"http://www.w3.org/2005/Atom\"");
|
||||||
printWriter.println("xmlns:opentracks=\"http://opentracksapp.com/xmlschemas/v1\"");
|
printWriter.println("xmlns:opentracks=\"http://opentracksapp.com/xmlschemas/v1\"");
|
||||||
printWriter.println("xmlns:gpxtpx=\"http://www.garmin.com/xmlschemes/TrackPointExtension/v2\"");
|
printWriter.println("xmlns:gpxtpx=\"http://www.garmin.com/xmlschemas/TrackPointExtension/v2\"");
|
||||||
|
printWriter.println("xmlns:pwr=\"http://www.garmin.com/xmlschemas/PowerExtension/v1\"");
|
||||||
printWriter.println("xsi:schemaLocation=" +
|
printWriter.println("xsi:schemaLocation=" +
|
||||||
"\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"
|
"\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"
|
||||||
+ " http://www.topografix.com/GPX/Private/TopoGrafix/0/1 http://www.topografix.com/GPX/Private/TopoGrafix/0/1/topografix.xsd"
|
+ " http://www.topografix.com/GPX/Private/TopoGrafix/0/1 http://www.topografix.com/GPX/Private/TopoGrafix/0/1/topografix.xsd"
|
||||||
+ " http://www.garmin.com/xmlschemas/TrackPointExtension/v2 https://www8.garmin.com/xmlschemas/TrackPointExtensionv2.xsd"
|
+ " http://www.garmin.com/xmlschemas/TrackPointExtension/v2 https://www8.garmin.com/xmlschemas/TrackPointExtensionv2.xsd"
|
||||||
|
+ " http://www.garmin.com/xmlschemas/PowerExtension/v1 https://www8.garmin.com/xmlschemas/PowerExtensionv1.xsd"
|
||||||
+ " http://opentracksapp.com/xmlschemas/v1 http://opentracksapp.com/xmlschemas/OpenTracks_v1.xsd\">");
|
+ " http://opentracksapp.com/xmlschemas/v1 http://opentracksapp.com/xmlschemas/OpenTracks_v1.xsd\">");
|
||||||
|
|
||||||
printWriter.println("<metadata>");
|
|
||||||
|
|
||||||
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
|
||||||
printWriter.println("<desc>" + StringUtils.formatCData(track.getDescription()) + "</desc>");
|
|
||||||
printWriter.println("</metadata>");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,7 +237,7 @@ public class GPXTrackExporter implements TrackExporter {
|
|||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.println("<wpt " + formatLocation(marker.getLatitude(), marker.getLongitude()) + ">");
|
printWriter.println("<wpt " + formatLocation(marker.getLatitude(), marker.getLongitude()) + ">");
|
||||||
if (marker.hasAltitude()) {
|
if (marker.hasAltitude()) {
|
||||||
printWriter.println("<ele>" + ELEVATION_FORMAT.format(marker.getAltitude()) + "</ele>");
|
printWriter.println("<ele>" + ALTITUDE_FORMAT.format(marker.getAltitude()) + "</ele>");
|
||||||
}
|
}
|
||||||
printWriter.println("<time>" + StringUtils.formatDateTimeIso8601(marker.getTime()) + "</time>");
|
printWriter.println("<time>" + StringUtils.formatDateTimeIso8601(marker.getTime()) + "</time>");
|
||||||
printWriter.println("<name>" + StringUtils.formatCData(marker.getName()) + "</name>");
|
printWriter.println("<name>" + StringUtils.formatCData(marker.getName()) + "</name>");
|
||||||
@@ -279,16 +281,16 @@ public class GPXTrackExporter implements TrackExporter {
|
|||||||
printWriter.println("<trkpt " + formatLocation(trackPoint.getLatitude(), trackPoint.getLongitude()) + ">");
|
printWriter.println("<trkpt " + formatLocation(trackPoint.getLatitude(), trackPoint.getLongitude()) + ">");
|
||||||
|
|
||||||
if (trackPoint.hasAltitude()) {
|
if (trackPoint.hasAltitude()) {
|
||||||
printWriter.println("<ele>" + ELEVATION_FORMAT.format(trackPoint.getAltitude()) + "</ele>");
|
printWriter.println("<ele>" + ALTITUDE_FORMAT.format(trackPoint.getAltitude()) + "</ele>");
|
||||||
}
|
}
|
||||||
|
|
||||||
printWriter.println("<time>" + StringUtils.formatDateTimeIso8601(trackPoint.getTime()) + "</time>");
|
printWriter.println("<time>" + StringUtils.formatDateTimeIso8601(trackPoint.getTime()) + "</time>");
|
||||||
|
|
||||||
if (trackPoint.hasSpeed() || trackPoint.hasHeartRate() || trackPoint.hasCyclingCadence() || trackPoint.hasElevationGain() || trackPoint.hasElevationLoss()) {
|
if (trackPoint.hasSpeed() || trackPoint.hasHeartRate() || trackPoint.hasCyclingCadence() || trackPoint.hasAltitudeGain() || trackPoint.hasAltitudeLoss()) {
|
||||||
printWriter.println("<extensions><gpxtpx:TrackPointExtension>");
|
printWriter.println("<extensions><gpxtpx:TrackPointExtension>");
|
||||||
|
|
||||||
if (trackPoint.hasSpeed()) {
|
if (trackPoint.hasSpeed()) {
|
||||||
printWriter.println("<gpxtpx:speed>" + SPEED_FORMAT.format(trackPoint.getSpeed()) + "</gpxtpx:speed>");
|
printWriter.println("<gpxtpx:speed>" + SPEED_FORMAT.format(trackPoint.getSpeed().toMPS()) + "</gpxtpx:speed>");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trackPoint.hasHeartRate()) {
|
if (trackPoint.hasHeartRate()) {
|
||||||
@@ -299,12 +301,20 @@ public class GPXTrackExporter implements TrackExporter {
|
|||||||
printWriter.println("<gpxtpx:cad>" + CADENCE_FORMAT.format(trackPoint.getCyclingCadence_rpm()) + "</gpxtpx:cad>");
|
printWriter.println("<gpxtpx:cad>" + CADENCE_FORMAT.format(trackPoint.getCyclingCadence_rpm()) + "</gpxtpx:cad>");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trackPoint.hasElevationGain()) {
|
if (trackPoint.hasPower()) {
|
||||||
printWriter.println("<opentracks:gain>" + ELEVATION_FORMAT.format(trackPoint.getElevationGain()) + "</opentracks:gain>");
|
printWriter.println("<pwr:PowerInWatts>" + POWER_FORMAT.format(trackPoint.getPower()) + "</pwr:PowerInWatts>");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trackPoint.hasElevationLoss()) {
|
if (trackPoint.hasAltitudeGain()) {
|
||||||
printWriter.println("<opentracks:loss>" + ELEVATION_FORMAT.format(trackPoint.getElevationLoss()) + "</opentracks:loss>");
|
printWriter.println("<opentracks:gain>" + ALTITUDE_FORMAT.format(trackPoint.getAltitudeGain()) + "</opentracks:gain>");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trackPoint.hasAltitudeLoss()) {
|
||||||
|
printWriter.println("<opentracks:loss>" + ALTITUDE_FORMAT.format(trackPoint.getAltitudeLoss()) + "</opentracks:loss>");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trackPoint.hasSensorDistance()) {
|
||||||
|
printWriter.println("<opentracks:distance>" + DISTANCE_FORMAT.format(trackPoint.getSensorDistance().toM()) + "</opentracks:distance>");
|
||||||
}
|
}
|
||||||
|
|
||||||
printWriter.println("</gpxtpx:TrackPointExtension></extensions>");
|
printWriter.println("</gpxtpx:TrackPointExtension></extensions>");
|
||||||
|
|||||||
@@ -57,11 +57,12 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
private static final String SCHEMA_ID = "schema";
|
private static final String SCHEMA_ID = "schema";
|
||||||
|
|
||||||
public static final String EXTENDED_DATA_TYPE_SPEED = "speed";
|
public static final String EXTENDED_DATA_TYPE_SPEED = "speed";
|
||||||
|
public static final String EXTENDED_DATA_TYPE_DISTANCE = "distance";
|
||||||
public static final String EXTENDED_DATA_TYPE_CADENCE = "cadence";
|
public static final String EXTENDED_DATA_TYPE_CADENCE = "cadence";
|
||||||
public static final String EXTENDED_DATA_TYPE_HEART_RATE = "heart_rate";
|
public static final String EXTENDED_DATA_TYPE_HEART_RATE = "heart_rate";
|
||||||
public static final String EXTENDED_DATA_TYPE_POWER = "power";
|
public static final String EXTENDED_DATA_TYPE_POWER = "power";
|
||||||
public static final String EXTENDED_DATA_TYPE_ELEVATION_GAIN = "elevation_gain";
|
public static final String EXTENDED_DATA_TYPE_ALTITUDE_GAIN = "elevation_gain";
|
||||||
public static final String EXTENDED_DATA_TYPE_ELEVATION_LOSS = "elevation_loss";
|
public static final String EXTENDED_DATA_TYPE_ALTITUDE_LOSS = "elevation_loss";
|
||||||
|
|
||||||
private static final String MARKER_ICON = "http://maps.google.com/mapfiles/kml/pushpin/blue-pushpin.png";
|
private static final String MARKER_ICON = "http://maps.google.com/mapfiles/kml/pushpin/blue-pushpin.png";
|
||||||
private static final String TRACK_ICON = "http://earth.google.com/images/kml-icons/track-directional/track-0.png";
|
private static final String TRACK_ICON = "http://earth.google.com/images/kml-icons/track-directional/track-0.png";
|
||||||
@@ -74,11 +75,12 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
|
|
||||||
private PrintWriter printWriter;
|
private PrintWriter printWriter;
|
||||||
private final List<Float> speedList = new ArrayList<>();
|
private final List<Float> speedList = new ArrayList<>();
|
||||||
|
private final List<Float> distanceList = new ArrayList<>();
|
||||||
private final List<Float> powerList = new ArrayList<>();
|
private final List<Float> powerList = new ArrayList<>();
|
||||||
private final List<Float> cadenceList = new ArrayList<>();
|
private final List<Float> cadenceList = new ArrayList<>();
|
||||||
private final List<Float> heartRateList = new ArrayList<>();
|
private final List<Float> heartRateList = new ArrayList<>();
|
||||||
private final List<Float> elevationGainList = new ArrayList<>();
|
private final List<Float> altitudeGainList = new ArrayList<>();
|
||||||
private final List<Float> elevationLossList = new ArrayList<>();
|
private final List<Float> altitudeLossList = new ArrayList<>();
|
||||||
|
|
||||||
@Deprecated // Figure out a better way to do this! (if needed)
|
@Deprecated // Figure out a better way to do this! (if needed)
|
||||||
private TrackPoint startTrackPoint;
|
private TrackPoint startTrackPoint;
|
||||||
@@ -338,11 +340,12 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.println("<gx:Track>");
|
printWriter.println("<gx:Track>");
|
||||||
speedList.clear();
|
speedList.clear();
|
||||||
|
distanceList.clear();
|
||||||
powerList.clear();
|
powerList.clear();
|
||||||
cadenceList.clear();
|
cadenceList.clear();
|
||||||
heartRateList.clear();
|
heartRateList.clear();
|
||||||
elevationGainList.clear();
|
altitudeGainList.clear();
|
||||||
elevationLossList.clear();
|
altitudeLossList.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,6 +358,9 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
writeSimpleArrayData(speedList, EXTENDED_DATA_TYPE_SPEED);
|
writeSimpleArrayData(speedList, EXTENDED_DATA_TYPE_SPEED);
|
||||||
}
|
}
|
||||||
if (exportSensorData) {
|
if (exportSensorData) {
|
||||||
|
if (distanceList.stream().anyMatch(Objects::nonNull)) {
|
||||||
|
writeSimpleArrayData(distanceList, EXTENDED_DATA_TYPE_DISTANCE);
|
||||||
|
}
|
||||||
if (powerList.stream().anyMatch(Objects::nonNull)) {
|
if (powerList.stream().anyMatch(Objects::nonNull)) {
|
||||||
writeSimpleArrayData(powerList, EXTENDED_DATA_TYPE_POWER);
|
writeSimpleArrayData(powerList, EXTENDED_DATA_TYPE_POWER);
|
||||||
}
|
}
|
||||||
@@ -364,11 +370,11 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
if (heartRateList.stream().anyMatch(Objects::nonNull)) {
|
if (heartRateList.stream().anyMatch(Objects::nonNull)) {
|
||||||
writeSimpleArrayData(heartRateList, EXTENDED_DATA_TYPE_HEART_RATE);
|
writeSimpleArrayData(heartRateList, EXTENDED_DATA_TYPE_HEART_RATE);
|
||||||
}
|
}
|
||||||
if (elevationGainList.stream().anyMatch(Objects::nonNull)) {
|
if (altitudeGainList.stream().anyMatch(Objects::nonNull)) {
|
||||||
writeSimpleArrayData(elevationGainList, EXTENDED_DATA_TYPE_ELEVATION_GAIN);
|
writeSimpleArrayData(altitudeGainList, EXTENDED_DATA_TYPE_ALTITUDE_GAIN);
|
||||||
}
|
}
|
||||||
if (elevationLossList.stream().anyMatch(Objects::nonNull)) {
|
if (altitudeLossList.stream().anyMatch(Objects::nonNull)) {
|
||||||
writeSimpleArrayData(elevationLossList, EXTENDED_DATA_TYPE_ELEVATION_LOSS);
|
writeSimpleArrayData(altitudeLossList, EXTENDED_DATA_TYPE_ALTITUDE_LOSS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
printWriter.println("</SchemaData>");
|
printWriter.println("</SchemaData>");
|
||||||
@@ -389,15 +395,16 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
} else {
|
} else {
|
||||||
printWriter.println("<gx:coord/>");
|
printWriter.println("<gx:coord/>");
|
||||||
}
|
}
|
||||||
speedList.add(trackPoint.hasSpeed() ? trackPoint.getSpeed() : null);
|
speedList.add(trackPoint.hasSpeed() ? (float) trackPoint.getSpeed().toMPS() : null);
|
||||||
|
|
||||||
if (exportSensorData) {
|
if (exportSensorData) {
|
||||||
|
distanceList.add(trackPoint.hasSensorDistance() ? (float) trackPoint.getSensorDistance().toM() : null);
|
||||||
heartRateList.add(trackPoint.hasHeartRate() ? trackPoint.getHeartRate_bpm() : null);
|
heartRateList.add(trackPoint.hasHeartRate() ? trackPoint.getHeartRate_bpm() : null);
|
||||||
cadenceList.add(trackPoint.hasCyclingCadence() ? trackPoint.getCyclingCadence_rpm() : null);
|
cadenceList.add(trackPoint.hasCyclingCadence() ? trackPoint.getCyclingCadence_rpm() : null);
|
||||||
powerList.add(trackPoint.hasPower() ? trackPoint.getPower() : null);
|
powerList.add(trackPoint.hasPower() ? trackPoint.getPower() : null);
|
||||||
|
|
||||||
elevationGainList.add(trackPoint.hasElevationGain() ? trackPoint.getElevationGain() : null);
|
altitudeGainList.add(trackPoint.hasAltitudeGain() ? trackPoint.getAltitudeGain() : null);
|
||||||
elevationLossList.add(trackPoint.hasElevationLoss() ? trackPoint.getElevationLoss() : null);
|
altitudeLossList.add(trackPoint.hasAltitudeLoss() ? trackPoint.getAltitudeLoss() : null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+40
-21
@@ -17,6 +17,7 @@
|
|||||||
package de.dennisguse.opentracks.io.file.importer;
|
package de.dennisguse.opentracks.io.file.importer;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.database.sqlite.SQLiteConstraintException;
|
import android.database.sqlite.SQLiteConstraintException;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -41,11 +42,14 @@ import javax.xml.parsers.ParserConfigurationException;
|
|||||||
import javax.xml.parsers.SAXParserFactory;
|
import javax.xml.parsers.SAXParserFactory;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
|
import de.dennisguse.opentracks.content.data.Distance;
|
||||||
import de.dennisguse.opentracks.content.data.Marker;
|
import de.dennisguse.opentracks.content.data.Marker;
|
||||||
|
import de.dennisguse.opentracks.content.data.Speed;
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
|
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
|
||||||
|
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||||
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
|
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
|
||||||
import de.dennisguse.opentracks.util.FileUtils;
|
import de.dennisguse.opentracks.util.FileUtils;
|
||||||
import de.dennisguse.opentracks.util.LocationUtils;
|
import de.dennisguse.opentracks.util.LocationUtils;
|
||||||
@@ -64,7 +68,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final ContentProviderUtils contentProviderUtils;
|
private final ContentProviderUtils contentProviderUtils;
|
||||||
private final int recordingDistanceInterval;
|
private final Distance recordingDistanceInterval;
|
||||||
|
|
||||||
private final List<Track.Id> trackIds = new ArrayList<>();
|
private final List<Track.Id> trackIds = new ArrayList<>();
|
||||||
private final List<Marker> markers = new ArrayList<>();
|
private final List<Marker> markers = new ArrayList<>();
|
||||||
@@ -90,6 +94,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
protected String uuid;
|
protected String uuid;
|
||||||
protected String gain;
|
protected String gain;
|
||||||
protected String loss;
|
protected String loss;
|
||||||
|
protected String distance;
|
||||||
|
|
||||||
// The current track data
|
// The current track data
|
||||||
private TrackData trackData;
|
private TrackData trackData;
|
||||||
@@ -97,10 +102,13 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
// The SAX locator to get the current line information
|
// The SAX locator to get the current line information
|
||||||
private Locator locator;
|
private Locator locator;
|
||||||
|
|
||||||
|
private final SharedPreferences sharedPreferences;
|
||||||
|
|
||||||
AbstractFileTrackImporter(Context context, ContentProviderUtils contentProviderUtils) {
|
AbstractFileTrackImporter(Context context, ContentProviderUtils contentProviderUtils) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.contentProviderUtils = contentProviderUtils;
|
this.contentProviderUtils = contentProviderUtils;
|
||||||
this.recordingDistanceInterval = PreferencesUtils.getRecordingDistanceInterval(context);
|
sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||||
|
this.recordingDistanceInterval = PreferencesUtils.getRecordingDistanceInterval(sharedPreferences, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -120,10 +128,11 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Track.Id importFile(InputStream inputStream) {
|
@NonNull
|
||||||
|
public List<Track.Id> importFile(InputStream inputStream) {
|
||||||
try {
|
try {
|
||||||
SAXParserFactory.newInstance().newSAXParser().parse(inputStream, this);
|
SAXParserFactory.newInstance().newSAXParser().parse(inputStream, this);
|
||||||
return trackIds.get(0);
|
return trackIds;
|
||||||
} catch (IOException | SAXException | ParserConfigurationException | ParsingException e) {
|
} catch (IOException | SAXException | ParserConfigurationException | ParsingException e) {
|
||||||
Log.e(TAG, "Unable to import file", e);
|
Log.e(TAG, "Unable to import file", e);
|
||||||
if (trackIds.size() > 0) {
|
if (trackIds.size() > 0) {
|
||||||
@@ -136,9 +145,6 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* On file end.
|
|
||||||
*/
|
|
||||||
protected void onFileEnd() {
|
protected void onFileEnd() {
|
||||||
// Add markers to the last imported track
|
// Add markers to the last imported track
|
||||||
int size = trackIds.size();
|
int size = trackIds.size();
|
||||||
@@ -199,11 +205,10 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
if (trackPoint.getLatitude() == marker.getLatitude() && trackPoint.getLongitude() == marker.getLongitude()) {
|
if (trackPoint.getLatitude() == marker.getLatitude() && trackPoint.getLongitude() == marker.getLongitude()) {
|
||||||
String markerDescription = marker.getDescription();
|
String markerDescription = marker.getDescription();
|
||||||
String icon = context.getString(R.string.marker_icon_url);
|
String icon = context.getString(R.string.marker_icon_url);
|
||||||
double length = trackStatisticsUpdater.getTrackStatistics().getTotalDistance();
|
TrackStatistics stats = trackStatisticsUpdater.getTrackStatistics();
|
||||||
long duration = trackStatisticsUpdater.getTrackStatistics().getTotalTime().toMillis();
|
|
||||||
|
|
||||||
// Insert marker
|
// Insert marker
|
||||||
Marker newMarker = new Marker(marker.getName(), markerDescription, marker.getCategory(), icon, track.getId(), length, duration, trackPoint, marker.getPhotoUrl());
|
Marker newMarker = new Marker(marker.getName(), markerDescription, marker.getCategory(), icon, track.getId(), stats, trackPoint, marker.getPhotoUrl());
|
||||||
contentProviderUtils.insertMarker(newMarker);
|
contentProviderUtils.insertMarker(newMarker);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,7 +266,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
|
|
||||||
Track track = contentProviderUtils.getTrack(trackData.track.getUuid());
|
Track track = contentProviderUtils.getTrack(trackData.track.getUuid());
|
||||||
if (track != null) {
|
if (track != null) {
|
||||||
if (PreferencesUtils.getPreventReimportTracks(context)) {
|
if (PreferencesUtils.getPreventReimportTracks(sharedPreferences, context)) {
|
||||||
throw new ImportAlreadyExistsException(context.getString(R.string.import_prevent_reimport));
|
throw new ImportAlreadyExistsException(context.getString(R.string.import_prevent_reimport));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,17 +376,16 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
* GPS points tend to have some inherent imprecision, speed and bearing will likely be off, so the statistics for things like max speed will also be off.
|
* GPS points tend to have some inherent imprecision, speed and bearing will likely be off, so the statistics for things like max speed will also be off.
|
||||||
*/
|
*/
|
||||||
if (trackPoint.hasLocation() && trackData.lastLocationInCurrentSegment.hasLocation()) {
|
if (trackPoint.hasLocation() && trackData.lastLocationInCurrentSegment.hasLocation()) {
|
||||||
float speed = trackData.lastLocationInCurrentSegment.distanceTo(trackPoint) / timeDifference.toMillis();
|
trackPoint.setSpeed(Speed.of(trackData.lastLocationInCurrentSegment.distanceToPrevious(trackPoint), timeDifference));
|
||||||
trackPoint.setSpeed(speed);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (trackPoint.hasLocation() && trackData.lastLocationInCurrentSegment.hasLocation()) {
|
if (trackPoint.hasLocation() && trackData.lastLocationInCurrentSegment.hasLocation()) {
|
||||||
trackPoint.setBearing(trackData.lastLocationInCurrentSegment.bearingTo(trackPoint));
|
trackPoint.setBearing(trackData.lastLocationInCurrentSegment.bearingTo(trackPoint));
|
||||||
|
|
||||||
long maxRecordingDistance = PreferencesUtils.getMaxRecordingDistance(context);
|
Distance maxRecordingDistance = PreferencesUtils.getMaxRecordingDistance(sharedPreferences, context); //TODO Should only be read once!
|
||||||
double distanceToLastTrackLocation = trackPoint.distanceTo(trackData.lastLocationInCurrentSegment);
|
Distance distanceToLastTrackLocation = trackPoint.distanceToPrevious(trackData.lastLocationInCurrentSegment);
|
||||||
if (distanceToLastTrackLocation > maxRecordingDistance) {
|
if (distanceToLastTrackLocation.greaterThan(maxRecordingDistance)) {
|
||||||
trackPoint.setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
trackPoint.setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -457,7 +461,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
|
|
||||||
if (speed != null) {
|
if (speed != null) {
|
||||||
try {
|
try {
|
||||||
trackPoint.setSpeed(Float.parseFloat(speed));
|
trackPoint.setSpeed(Speed.of(speed));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse speed: %s", speed)), e);
|
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse speed: %s", speed)), e);
|
||||||
}
|
}
|
||||||
@@ -478,18 +482,33 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (power != null) {
|
||||||
|
try {
|
||||||
|
trackPoint.setPower(Float.parseFloat(power));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse power: %s", power)), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (gain != null) {
|
if (gain != null) {
|
||||||
try {
|
try {
|
||||||
trackPoint.setElevationGain(Float.parseFloat(gain));
|
trackPoint.setAltitudeGain(Float.parseFloat(gain));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse elevation gain: %s", gain)), e);
|
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse altitude gain: %s", gain)), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (loss != null) {
|
if (loss != null) {
|
||||||
try {
|
try {
|
||||||
trackPoint.setElevationLoss(Float.parseFloat(loss));
|
trackPoint.setAltitudeLoss(Float.parseFloat(loss));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse elevation loss: %s", loss)), e);
|
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse altitude loss: %s", loss)), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (distance != null) {
|
||||||
|
try {
|
||||||
|
trackPoint.setSensorDistance(Distance.of(distance));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse distance: %s", distance)), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+17
-6
@@ -2,6 +2,7 @@ package de.dennisguse.opentracks.io.file.importer;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.provider.DocumentsContract;
|
import android.provider.DocumentsContract;
|
||||||
@@ -18,10 +19,14 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
private static final int DIRECTORY_PICKER_REQUEST_CODE = 6;
|
private static final int DIRECTORY_PICKER_REQUEST_CODE = 6;
|
||||||
|
|
||||||
|
protected SharedPreferences sharedPreferences;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
||||||
|
|
||||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||||
DocumentFile directoryUri = configureDirectoryChooserIntent(intent);
|
DocumentFile directoryUri = configureDirectoryChooserIntent(intent);
|
||||||
if (directoryUri == null) {
|
if (directoryUri == null) {
|
||||||
@@ -38,6 +43,12 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
|||||||
onActivityResultCustom(requestCode, resultCode, resultData);
|
onActivityResultCustom(requestCode, resultCode, resultData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
sharedPreferences = null;
|
||||||
|
}
|
||||||
|
|
||||||
protected void onActivityResultCustom(int requestCode, int resultCode, @Nullable Intent resultData) {
|
protected void onActivityResultCustom(int requestCode, int resultCode, @Nullable Intent resultData) {
|
||||||
if (requestCode == DIRECTORY_PICKER_REQUEST_CODE) {
|
if (requestCode == DIRECTORY_PICKER_REQUEST_CODE) {
|
||||||
if (resultCode == Activity.RESULT_OK) {
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
@@ -80,14 +91,14 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
|||||||
super.configureDirectoryChooserIntent(intent);
|
super.configureDirectoryChooserIntent(intent);
|
||||||
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
|
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
|
||||||
|
|
||||||
return PreferencesUtils.getDefaultExportDirectoryUri(this);
|
return PreferencesUtils.getDefaultExportDirectoryUri(sharedPreferences, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Intent createNextActivityIntent(Uri directoryUri) {
|
protected Intent createNextActivityIntent(Uri directoryUri) {
|
||||||
Intent intent = IntentUtils.newIntent(this, ExportActivity.class);
|
Intent intent = IntentUtils.newIntent(this, ExportActivity.class);
|
||||||
intent.putExtra(ExportActivity.EXTRA_DIRECTORY_URI_KEY, directoryUri);
|
intent.putExtra(ExportActivity.EXTRA_DIRECTORY_URI_KEY, directoryUri);
|
||||||
intent.putExtra(ExportActivity.EXTRA_TRACKFILEFORMAT_KEY, PreferencesUtils.getExportTrackFileFormat(this));
|
intent.putExtra(ExportActivity.EXTRA_TRACKFILEFORMAT_KEY, PreferencesUtils.getExportTrackFileFormat(sharedPreferences, this));
|
||||||
return intent;
|
return intent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,11 +112,11 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
|||||||
case RESULT_OK:
|
case RESULT_OK:
|
||||||
Uri directoryUri = resultData.getData();
|
Uri directoryUri = resultData.getData();
|
||||||
|
|
||||||
PreferencesUtils.setDefaultExportDirectoryUri(this, directoryUri);
|
PreferencesUtils.setDefaultExportDirectoryUri(sharedPreferences, this, directoryUri);
|
||||||
IntentUtils.persistDirectoryAccessPermission(getApplicationContext(), resultData, directoryUri);
|
IntentUtils.persistDirectoryAccessPermission(getApplicationContext(), resultData, directoryUri);
|
||||||
break;
|
break;
|
||||||
case RESULT_CANCELED:
|
case RESULT_CANCELED:
|
||||||
PreferencesUtils.setDefaultExportDirectoryUri(this, null);
|
PreferencesUtils.setDefaultExportDirectoryUri(sharedPreferences, this, null);
|
||||||
//TODO Remove stored permission contentResolver.releasePersistableUriPermission
|
//TODO Remove stored permission contentResolver.releasePersistableUriPermission
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -118,8 +129,8 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
|||||||
protected DocumentFile configureDirectoryChooserIntent(Intent intent) {
|
protected DocumentFile configureDirectoryChooserIntent(Intent intent) {
|
||||||
super.configureDirectoryChooserIntent(intent);
|
super.configureDirectoryChooserIntent(intent);
|
||||||
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||||
if (PreferencesUtils.isDefaultExportDirectoryUri(this)) {
|
if (PreferencesUtils.isDefaultExportDirectoryUri(sharedPreferences, this)) {
|
||||||
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, PreferencesUtils.getDefaultExportDirectoryUri(this).getUri());
|
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, PreferencesUtils.getDefaultExportDirectoryUri(sharedPreferences, this).getUri());
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,9 @@ import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Imports a GPX file.
|
* Imports a GPX file.
|
||||||
* Uses https://www8.garmin.com/xmlschemas/TrackPointExtensionv2.xsd
|
* Uses:
|
||||||
|
* * https://www8.garmin.com/xmlschemas/TrackPointExtensionv2.xsd
|
||||||
|
* * https://www8.garmin.com/xmlschemas/PowerExtensionv1.xsd
|
||||||
* <p>
|
* <p>
|
||||||
* {@link de.dennisguse.opentracks.io.file.exporter.GPXTrackExporter} does not export information if a segment was started automatic or manually.
|
* {@link de.dennisguse.opentracks.io.file.exporter.GPXTrackExporter} does not export information if a segment was started automatic or manually.
|
||||||
* Therefore, all segments starts are marked as SEGMENT_START_AUTOMATIC.
|
* Therefore, all segments starts are marked as SEGMENT_START_AUTOMATIC.
|
||||||
@@ -40,7 +42,7 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
|
|
||||||
private static final String TAG_DESCRIPTION = "desc";
|
private static final String TAG_DESCRIPTION = "desc";
|
||||||
private static final String TAG_COMMENT = "cmt";
|
private static final String TAG_COMMENT = "cmt";
|
||||||
private static final String TAG_ELEVATION = "ele";
|
private static final String TAG_ALTITUDE = "ele";
|
||||||
private static final String TAG_GPX = "gpx";
|
private static final String TAG_GPX = "gpx";
|
||||||
private static final String TAG_NAME = "name";
|
private static final String TAG_NAME = "name";
|
||||||
private static final String TAG_TIME = "time";
|
private static final String TAG_TIME = "time";
|
||||||
@@ -57,9 +59,11 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
private static final String TAG_EXTENSION_SPEED = "gpxtpx:speed";
|
private static final String TAG_EXTENSION_SPEED = "gpxtpx:speed";
|
||||||
private static final String TAG_EXTENSION_HEARTRATE = "gpxtpx:hr";
|
private static final String TAG_EXTENSION_HEARTRATE = "gpxtpx:hr";
|
||||||
private static final String TAG_EXTENSION_CADENCE = "gpxtpx:cad";
|
private static final String TAG_EXTENSION_CADENCE = "gpxtpx:cad";
|
||||||
|
private static final String TAG_EXTENSION_POWER = "pwr:PowerInWatts";
|
||||||
|
|
||||||
private static final String TAG_EXTENSION_GAIN = "opentracks:gain";
|
private static final String TAG_EXTENSION_GAIN = "opentracks:gain";
|
||||||
private static final String TAG_EXTENSION_LOSS = "opentracks:loss";
|
private static final String TAG_EXTENSION_LOSS = "opentracks:loss";
|
||||||
|
private static final String TAG_EXTENSION_DISTANCE = "opentracks:distance";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
@@ -94,7 +98,7 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void endElement(String uri, String localName, String tag) throws SAXException {
|
public void endElement(String uri, String localName, String tag) {
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case TAG_GPX:
|
case TAG_GPX:
|
||||||
onFileEnd();
|
onFileEnd();
|
||||||
@@ -131,7 +135,7 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
time = content.trim();
|
time = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TAG_ELEVATION:
|
case TAG_ALTITUDE:
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
altitude = content.trim();
|
altitude = content.trim();
|
||||||
}
|
}
|
||||||
@@ -156,6 +160,11 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
cadence = content.trim();
|
cadence = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case TAG_EXTENSION_POWER:
|
||||||
|
if (content != null) {
|
||||||
|
power = content.trim();
|
||||||
|
}
|
||||||
|
break;
|
||||||
case TAG_ID:
|
case TAG_ID:
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
uuid = content.trim();
|
uuid = content.trim();
|
||||||
@@ -171,6 +180,11 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
loss = content.trim();
|
loss = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case TAG_EXTENSION_DISTANCE:
|
||||||
|
if (content != null) {
|
||||||
|
distance = content.trim();
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset element content
|
// Reset element content
|
||||||
@@ -196,11 +210,12 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
altitude = null;
|
altitude = null;
|
||||||
time = null;
|
time = null;
|
||||||
speed = null;
|
speed = null;
|
||||||
|
power = null;
|
||||||
gain = null;
|
gain = null;
|
||||||
loss = null;
|
loss = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onTrackPointEnd() throws SAXException {
|
private void onTrackPointEnd() {
|
||||||
boolean isFirstTrackPointInSegment = isFirstTrackPointInSegment();
|
boolean isFirstTrackPointInSegment = isFirstTrackPointInSegment();
|
||||||
TrackPoint trackPoint = getTrackPoint();
|
TrackPoint trackPoint = getTrackPoint();
|
||||||
if (isFirstTrackPointInSegment) {
|
if (isFirstTrackPointInSegment) {
|
||||||
@@ -221,7 +236,7 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
markerType = null;
|
markerType = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onMarkerEnd() throws SAXException {
|
private void onMarkerEnd() {
|
||||||
addMarker();
|
addMarker();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package de.dennisguse.opentracks.io.file.importer;
|
package de.dennisguse.opentracks.io.file.importer;
|
||||||
|
|
||||||
|
import android.content.ClipData;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@@ -30,6 +31,10 @@ import androidx.documentfile.provider.DocumentFile;
|
|||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.TrackRecordedActivity;
|
import de.dennisguse.opentracks.TrackRecordedActivity;
|
||||||
import de.dennisguse.opentracks.databinding.ImportActivityBinding;
|
import de.dennisguse.opentracks.databinding.ImportActivityBinding;
|
||||||
@@ -47,17 +52,15 @@ public class ImportActivity extends FragmentActivity {
|
|||||||
|
|
||||||
public static final String EXTRA_DIRECTORY_URI_KEY = "directory_uri";
|
public static final String EXTRA_DIRECTORY_URI_KEY = "directory_uri";
|
||||||
|
|
||||||
private static final String BUNDLE_TOOLBAR_TITLE = "toolbar_title";
|
private static final String BUNDLE_DOCUMENT_URIS = "document_uris";
|
||||||
private static final String BUNDLE_DOCUMENT_URI = "document_uri";
|
|
||||||
private static final String BUNDLE_IS_DIRECTORY = "is_directory";
|
private static final String BUNDLE_IS_DIRECTORY = "is_directory";
|
||||||
|
|
||||||
private ImportActivityBinding viewBinding;
|
private ImportActivityBinding viewBinding;
|
||||||
|
|
||||||
boolean doubleBackToCancel = false;
|
boolean doubleBackToCancel = false;
|
||||||
|
|
||||||
private Uri documentUri;
|
private ArrayList<Uri> documentUris = new ArrayList<>();
|
||||||
private boolean isDirectory;
|
private boolean isDirectory;
|
||||||
private String toolbarTitle;
|
|
||||||
|
|
||||||
private ImportViewModel viewModel;
|
private ImportViewModel viewModel;
|
||||||
private ImportViewModel.Summary summary;
|
private ImportViewModel.Summary summary;
|
||||||
@@ -70,35 +73,46 @@ public class ImportActivity extends FragmentActivity {
|
|||||||
|
|
||||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||||
|
|
||||||
final DocumentFile documentFile;
|
final Intent intent = getIntent();
|
||||||
|
final ClipData intentClipData = intent.getClipData();
|
||||||
|
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
if (getIntent().getData() != null) {
|
if (intent.getData() != null) {
|
||||||
documentUri = getIntent().getData();
|
documentUris.add(intent.getData());
|
||||||
isDirectory = false;
|
|
||||||
} else if (getIntent().getClipData() != null && getIntent().getClipData().getItemCount() > 0) {
|
|
||||||
documentUri = getIntent().getClipData().getItemAt(0).getUri();
|
|
||||||
isDirectory = false;
|
isDirectory = false;
|
||||||
} else {
|
} else {
|
||||||
// Started from DirectoryChooserActivity
|
if (intentClipData != null && intentClipData.getItemCount() > 0) {
|
||||||
documentUri = getIntent().getParcelableExtra(EXTRA_DIRECTORY_URI_KEY);
|
for (int i = 0; i < intentClipData.getItemCount(); i++) {
|
||||||
isDirectory = true;
|
documentUris.add(intentClipData.getItemAt(i).getUri());
|
||||||
|
}
|
||||||
|
isDirectory = false;
|
||||||
|
} else {
|
||||||
|
// Started from DirectoryChooserActivity
|
||||||
|
documentUris.add(intent.getParcelableExtra(EXTRA_DIRECTORY_URI_KEY));
|
||||||
|
isDirectory = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
documentFile = isDirectory ? DocumentFile.fromTreeUri(this, documentUri) : DocumentFile.fromSingleUri(this, documentUri);
|
|
||||||
toolbarTitle = getString(R.string.import_progress_message, documentFile.getName());
|
|
||||||
} else {
|
} else {
|
||||||
documentUri = savedInstanceState.getParcelable(BUNDLE_DOCUMENT_URI);
|
documentUris = savedInstanceState.getParcelable(BUNDLE_DOCUMENT_URIS);
|
||||||
toolbarTitle = savedInstanceState.getString(BUNDLE_TOOLBAR_TITLE);
|
|
||||||
isDirectory = savedInstanceState.getBoolean(BUNDLE_IS_DIRECTORY);
|
isDirectory = savedInstanceState.getBoolean(BUNDLE_IS_DIRECTORY);
|
||||||
|
|
||||||
documentFile = isDirectory ? DocumentFile.fromTreeUri(this, documentUri) : DocumentFile.fromSingleUri(this, documentUri);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toolbar.setTitle(toolbarTitle);
|
final List<DocumentFile> documentFiles;
|
||||||
|
if (isDirectory) {
|
||||||
|
documentFiles = new ArrayList<>();
|
||||||
|
documentFiles.add(DocumentFile.fromTreeUri(this, documentUris.get(0)));
|
||||||
|
} else {
|
||||||
|
documentFiles = documentUris.stream().map(it -> DocumentFile.fromSingleUri(this, it)).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
//Works for a directory, but we might have received multiple files via SEND_MULTIPLE.
|
||||||
|
toolbar.setTitle(getString(R.string.import_progress_message, documentFiles.get(0).getName()));
|
||||||
|
|
||||||
initViews();
|
initViews();
|
||||||
|
|
||||||
viewModel = new ViewModelProvider(this).get(ImportViewModel.class);
|
viewModel = new ViewModelProvider(this).get(ImportViewModel.class);
|
||||||
viewModel.getImportData(documentFile).observe(this, data -> {
|
viewModel.getImportData(documentFiles).observe(this, data -> {
|
||||||
summary = data;
|
summary = data;
|
||||||
setProgress();
|
setProgress();
|
||||||
});
|
});
|
||||||
@@ -107,9 +121,8 @@ public class ImportActivity extends FragmentActivity {
|
|||||||
@Override
|
@Override
|
||||||
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
outState.putParcelable(BUNDLE_DOCUMENT_URI, documentUri);
|
outState.putParcelableArrayList(BUNDLE_DOCUMENT_URIS, documentUris);
|
||||||
outState.putBoolean(BUNDLE_IS_DIRECTORY, isDirectory);
|
outState.putBoolean(BUNDLE_IS_DIRECTORY, isDirectory);
|
||||||
outState.putString(BUNDLE_TOOLBAR_TITLE, toolbarTitle);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import androidx.documentfile.provider.DocumentFile;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
@@ -62,11 +63,12 @@ public class ImportService extends JobIntentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try (InputStream inputStream = getContentResolver().openInputStream(file.getUri())) {
|
try (InputStream inputStream = getContentResolver().openInputStream(file.getUri())) {
|
||||||
Track.Id trackId = trackImporter.importFile(inputStream);
|
ArrayList<Track.Id> trackIds = new ArrayList<>(trackImporter.importFile(inputStream));
|
||||||
if (trackId != null) {
|
|
||||||
sendResult(ImportServiceResultReceiver.RESULT_CODE_IMPORTED, trackId, file, getString(R.string.import_file_imported, file.getName()));
|
if (!trackIds.isEmpty()) {
|
||||||
|
sendResult(ImportServiceResultReceiver.RESULT_CODE_IMPORTED, trackIds, file, getString(R.string.import_file_imported, file.getName()));
|
||||||
} else {
|
} else {
|
||||||
sendResult(ImportServiceResultReceiver.RESULT_CODE_ERROR, trackId, file, getString(R.string.import_unable_to_import_file, file.getName()));
|
sendResult(ImportServiceResultReceiver.RESULT_CODE_ERROR, trackIds, file, getString(R.string.import_unable_to_import_file, file.getName()));
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.d(TAG, "Unable to import file", e);
|
Log.d(TAG, "Unable to import file", e);
|
||||||
@@ -80,9 +82,9 @@ public class ImportService extends JobIntentService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendResult(int resultCode, Track.Id trackId, DocumentFile file, String message) {
|
private void sendResult(int resultCode, ArrayList<Track.Id> trackId, DocumentFile file, String message) {
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putParcelable(ImportServiceResultReceiver.RESULT_EXTRA_TRACK_ID, trackId);
|
bundle.putParcelableArrayList(ImportServiceResultReceiver.RESULT_EXTRA_LIST_TRACK_ID, trackId);
|
||||||
bundle.putString(ImportServiceResultReceiver.RESULT_EXTRA_FILENAME, file.getName());
|
bundle.putString(ImportServiceResultReceiver.RESULT_EXTRA_FILENAME, file.getName());
|
||||||
bundle.putString(ImportServiceResultReceiver.RESULT_EXTRA_MESSAGE, message);
|
bundle.putString(ImportServiceResultReceiver.RESULT_EXTRA_MESSAGE, message);
|
||||||
resultReceiver.send(resultCode, bundle);
|
resultReceiver.send(resultCode, bundle);
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ public class ImportServiceResultReceiver extends ResultReceiver {
|
|||||||
public static final int RESULT_CODE_IMPORTED = 1;
|
public static final int RESULT_CODE_IMPORTED = 1;
|
||||||
public static final int RESULT_CODE_ALREADY_EXISTS = 2;
|
public static final int RESULT_CODE_ALREADY_EXISTS = 2;
|
||||||
|
|
||||||
public static final String RESULT_EXTRA_TRACK_ID = "result_track_id";
|
public static final String RESULT_EXTRA_LIST_TRACK_ID = "result_track_id";
|
||||||
public static final String RESULT_EXTRA_FILENAME = "result_extra_filename";
|
public static final String RESULT_EXTRA_FILENAME = "result_extra_filename";
|
||||||
public static final String RESULT_EXTRA_MESSAGE = "result_extra_message";
|
public static final String RESULT_EXTRA_MESSAGE = "result_extra_message";
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ import androidx.lifecycle.LiveData;
|
|||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
@@ -33,20 +35,20 @@ public class ImportViewModel extends AndroidViewModel implements ImportServiceRe
|
|||||||
summary = new Summary();
|
summary = new Summary();
|
||||||
}
|
}
|
||||||
|
|
||||||
public LiveData<Summary> getImportData(DocumentFile documentFile) {
|
LiveData<Summary> getImportData(List<DocumentFile> documentFiles) {
|
||||||
if (importData == null) {
|
if (importData == null) {
|
||||||
importData = new MutableLiveData<>();
|
importData = new MutableLiveData<>();
|
||||||
loadData(documentFile);
|
loadData(documentFiles);
|
||||||
}
|
}
|
||||||
return importData;
|
return importData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cancel() {
|
void cancel() {
|
||||||
cancel = true;
|
cancel = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadData(DocumentFile documentFile) {
|
private void loadData(List<DocumentFile> documentFiles) {
|
||||||
List<DocumentFile> fileList = FileUtils.getFiles(documentFile);
|
List<DocumentFile> fileList = documentFiles.stream().map(FileUtils::getFiles).flatMap(Collection::stream).collect(Collectors.toList());
|
||||||
summary.totalCount = fileList.size();
|
summary.totalCount = fileList.size();
|
||||||
filesToImport.addAll(fileList);
|
filesToImport.addAll(fileList);
|
||||||
importNextFile();
|
importNextFile();
|
||||||
@@ -66,7 +68,7 @@ public class ImportViewModel extends AndroidViewModel implements ImportServiceRe
|
|||||||
throw new RuntimeException(TAG + ": onReceiveResult resultData NULL");
|
throw new RuntimeException(TAG + ": onReceiveResult resultData NULL");
|
||||||
}
|
}
|
||||||
|
|
||||||
Track.Id trackId = resultData.getParcelable(ImportServiceResultReceiver.RESULT_EXTRA_TRACK_ID);
|
ArrayList<Track.Id> trackIds = resultData.getParcelableArrayList(ImportServiceResultReceiver.RESULT_EXTRA_LIST_TRACK_ID);
|
||||||
String fileName = resultData.getString(ImportServiceResultReceiver.RESULT_EXTRA_FILENAME);
|
String fileName = resultData.getString(ImportServiceResultReceiver.RESULT_EXTRA_FILENAME);
|
||||||
String message = resultData.getString(ImportServiceResultReceiver.RESULT_EXTRA_MESSAGE);
|
String message = resultData.getString(ImportServiceResultReceiver.RESULT_EXTRA_MESSAGE);
|
||||||
|
|
||||||
@@ -76,7 +78,7 @@ public class ImportViewModel extends AndroidViewModel implements ImportServiceRe
|
|||||||
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;
|
break;
|
||||||
case ImportServiceResultReceiver.RESULT_CODE_IMPORTED:
|
case ImportServiceResultReceiver.RESULT_CODE_IMPORTED:
|
||||||
summary.importedTrackIds.add(trackId);
|
summary.importedTrackIds.addAll(trackIds);
|
||||||
summary.successCount++;
|
summary.successCount++;
|
||||||
break;
|
break;
|
||||||
case ImportServiceResultReceiver.RESULT_CODE_ALREADY_EXISTS:
|
case ImportServiceResultReceiver.RESULT_CODE_ALREADY_EXISTS:
|
||||||
@@ -90,7 +92,7 @@ public class ImportViewModel extends AndroidViewModel implements ImportServiceRe
|
|||||||
importNextFile();
|
importNextFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Summary {
|
static class Summary {
|
||||||
private int totalCount;
|
private int totalCount;
|
||||||
private int successCount;
|
private int successCount;
|
||||||
private int existsCount;
|
private int existsCount;
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import org.xml.sax.SAXException;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.data.Distance;
|
||||||
|
import de.dennisguse.opentracks.content.data.Speed;
|
||||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.io.file.exporter.KMLTrackExporter;
|
import de.dennisguse.opentracks.io.file.exporter.KMLTrackExporter;
|
||||||
@@ -35,6 +37,7 @@ import de.dennisguse.opentracks.io.file.exporter.KMLTrackExporter;
|
|||||||
*
|
*
|
||||||
* @author Jimmy Shih
|
* @author Jimmy Shih
|
||||||
*/
|
*/
|
||||||
|
//TODO Do not use AbstractFileTrackImporter as TrackPoint sensor generation is done differently here.
|
||||||
public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
||||||
|
|
||||||
private static final String TAG = KmlFileTrackImporter.class.getSimpleName();
|
private static final String TAG = KmlFileTrackImporter.class.getSimpleName();
|
||||||
@@ -65,11 +68,12 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
private String extendedDataType;
|
private String extendedDataType;
|
||||||
private final ArrayList<TrackPoint> trackPoints = new ArrayList<>();
|
private final ArrayList<TrackPoint> trackPoints = new ArrayList<>();
|
||||||
private final ArrayList<Float> speedList = new ArrayList<>();
|
private final ArrayList<Float> speedList = new ArrayList<>();
|
||||||
|
private final ArrayList<Float> distanceList = new ArrayList<>();
|
||||||
private final ArrayList<Float> cadenceList = new ArrayList<>();
|
private final ArrayList<Float> cadenceList = new ArrayList<>();
|
||||||
private final ArrayList<Float> heartRateList = new ArrayList<>();
|
private final ArrayList<Float> heartRateList = new ArrayList<>();
|
||||||
private final ArrayList<Float> powerList = new ArrayList<>();
|
private final ArrayList<Float> powerList = new ArrayList<>();
|
||||||
private final ArrayList<Float> elevationGainList = new ArrayList<>();
|
private final ArrayList<Float> altitudeGainList = new ArrayList<>();
|
||||||
private final ArrayList<Float> elevationLossList = new ArrayList<>();
|
private final ArrayList<Float> altitudeLossList = new ArrayList<>();
|
||||||
|
|
||||||
public KmlFileTrackImporter(Context context) {
|
public KmlFileTrackImporter(Context context) {
|
||||||
this(context, new ContentProviderUtils(context));
|
this(context, new ContentProviderUtils(context));
|
||||||
@@ -191,7 +195,7 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
markerType = null;
|
markerType = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onMarkerEnd() throws SAXException {
|
private void onMarkerEnd() {
|
||||||
if (!MARKER_STYLE.equals(markerType)) {
|
if (!MARKER_STYLE.equals(markerType)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -215,11 +219,12 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
super.onTrackSegmentStart();
|
super.onTrackSegmentStart();
|
||||||
trackPoints.clear();
|
trackPoints.clear();
|
||||||
speedList.clear();
|
speedList.clear();
|
||||||
|
distanceList.clear();
|
||||||
heartRateList.clear();
|
heartRateList.clear();
|
||||||
cadenceList.clear();
|
cadenceList.clear();
|
||||||
powerList.clear();
|
powerList.clear();
|
||||||
elevationGainList.clear();
|
altitudeGainList.clear();
|
||||||
elevationLossList.clear();
|
altitudeLossList.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onTrackSegmentEnd() {
|
protected void onTrackSegmentEnd() {
|
||||||
@@ -228,8 +233,11 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
for (int i = 0; i < trackPoints.size(); i++) {
|
for (int i = 0; i < trackPoints.size(); i++) {
|
||||||
TrackPoint trackPoint = trackPoints.get(i);
|
TrackPoint trackPoint = trackPoints.get(i);
|
||||||
|
|
||||||
if (i < speedList.size()) {
|
if (i < speedList.size() && speedList.get(i) != null) {
|
||||||
trackPoint.setSpeed(speedList.get(i));
|
trackPoint.setSpeed(Speed.of(speedList.get(i)));
|
||||||
|
}
|
||||||
|
if (i < distanceList.size() && distanceList.get(i) != null) {
|
||||||
|
trackPoint.setSensorDistance(Distance.of(distanceList.get(i)));
|
||||||
}
|
}
|
||||||
if (i < heartRateList.size()) {
|
if (i < heartRateList.size()) {
|
||||||
trackPoint.setHeartRate_bpm(heartRateList.get(i));
|
trackPoint.setHeartRate_bpm(heartRateList.get(i));
|
||||||
@@ -240,11 +248,11 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
if (i < powerList.size()) {
|
if (i < powerList.size()) {
|
||||||
trackPoint.setPower(powerList.get(i));
|
trackPoint.setPower(powerList.get(i));
|
||||||
}
|
}
|
||||||
if (i < elevationGainList.size()) {
|
if (i < altitudeGainList.size()) {
|
||||||
trackPoint.setElevationGain(elevationGainList.get(i));
|
trackPoint.setAltitudeGain(altitudeGainList.get(i));
|
||||||
}
|
}
|
||||||
if (i < elevationLossList.size()) {
|
if (i < altitudeLossList.size()) {
|
||||||
trackPoint.setElevationLoss(elevationLossList.get(i));
|
trackPoint.setAltitudeLoss(altitudeLossList.get(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
insertTrackPoint(trackPoint);
|
insertTrackPoint(trackPoint);
|
||||||
@@ -254,7 +262,7 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
/**
|
/**
|
||||||
* On track point end. gx:coord end tag.
|
* On track point end. gx:coord end tag.
|
||||||
*/
|
*/
|
||||||
private void onTrackPointEnd() throws SAXException {
|
private void onTrackPointEnd() {
|
||||||
// Add trackPoint to trackPoints
|
// Add trackPoint to trackPoints
|
||||||
if (content == null) {
|
if (content == null) {
|
||||||
return;
|
return;
|
||||||
@@ -309,6 +317,9 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_SPEED:
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_SPEED:
|
||||||
speedList.add(value);
|
speedList.add(value);
|
||||||
break;
|
break;
|
||||||
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_DISTANCE:
|
||||||
|
distanceList.add(value);
|
||||||
|
break;
|
||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_POWER:
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_POWER:
|
||||||
powerList.add(value);
|
powerList.add(value);
|
||||||
break;
|
break;
|
||||||
@@ -318,11 +329,11 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
|
|||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_CADENCE:
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_CADENCE:
|
||||||
cadenceList.add(value);
|
cadenceList.add(value);
|
||||||
break;
|
break;
|
||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_ELEVATION_GAIN:
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_ALTITUDE_GAIN:
|
||||||
elevationGainList.add(value);
|
altitudeGainList.add(value);
|
||||||
break;
|
break;
|
||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_ELEVATION_LOSS:
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_ALTITUDE_LOSS:
|
||||||
elevationLossList.add(value);
|
altitudeLossList.add(value);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Log.w(TAG, "Data from extended data " + extendedDataType + " is not (yet) supported.");
|
Log.w(TAG, "Data from extended data " + extendedDataType + " is not (yet) supported.");
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ import android.content.Context;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -38,7 +40,6 @@ import de.dennisguse.opentracks.content.data.Track;
|
|||||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.io.file.exporter.KmzTrackExporter;
|
import de.dennisguse.opentracks.io.file.exporter.KmzTrackExporter;
|
||||||
import de.dennisguse.opentracks.util.FileUtils;
|
import de.dennisguse.opentracks.util.FileUtils;
|
||||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Imports a KMZ file.
|
* Imports a KMZ file.
|
||||||
@@ -66,17 +67,22 @@ public class KmzTrackImporter implements TrackImporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Track.Id importFile(InputStream inputStream) {
|
public @NonNull
|
||||||
Track.Id trackId = findAndParseKmlFile(inputStream);
|
List<Track.Id> importFile(InputStream inputStream) {
|
||||||
|
List<Track.Id> trackIds = findAndParseKmlFile(inputStream);
|
||||||
|
|
||||||
if (!copyKmzImages(trackId)) {
|
ArrayList<Track.Id> trackIdsWithImages = new ArrayList<>();
|
||||||
cleanImport(context, trackId);
|
|
||||||
return null;
|
for (Track.Id trackId : trackIds) {
|
||||||
|
if (copyKmzImages(trackId)) {
|
||||||
|
trackIdsWithImages.add(trackId);
|
||||||
|
deleteOrphanImages(trackId);
|
||||||
|
} else {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteOrphanImages(context, trackId);
|
return trackIdsWithImages;
|
||||||
|
|
||||||
return trackId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -150,14 +156,13 @@ public class KmzTrackImporter implements TrackImporter {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds KmzTrackExporter.KMZ_KML_FILE file inside kmz file (inputStream) and it parses it.
|
* Finds KmzTrackExporter.KMZ_KML_FILE file inside kmz file (inputStream) and it parses it.
|
||||||
* TODO: May load multiple tracks, but only returns the last Track.Id.
|
|
||||||
*
|
*
|
||||||
* @param inputStream kmz input stream.
|
* @param inputStream kmz input stream.
|
||||||
*/
|
*/
|
||||||
private Track.Id findAndParseKmlFile(InputStream inputStream) {
|
private List<Track.Id> findAndParseKmlFile(InputStream inputStream) {
|
||||||
try (ZipInputStream zipInputStream = new ZipInputStream(inputStream)) {
|
try (ZipInputStream zipInputStream = new ZipInputStream(inputStream)) {
|
||||||
ZipEntry zipEntry;
|
ZipEntry zipEntry;
|
||||||
Track.Id trackId = null;
|
ArrayList<Track.Id> trackIds = new ArrayList<>();
|
||||||
|
|
||||||
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
|
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
|
||||||
if (Thread.interrupted()) {
|
if (Thread.interrupted()) {
|
||||||
@@ -167,20 +172,21 @@ public class KmzTrackImporter implements TrackImporter {
|
|||||||
|
|
||||||
String fileName = zipEntry.getName();
|
String fileName = zipEntry.getName();
|
||||||
if (KmzTrackExporter.KMZ_KML_FILE.equals(fileName)) {
|
if (KmzTrackExporter.KMZ_KML_FILE.equals(fileName)) {
|
||||||
trackId = parseKml(zipInputStream);
|
List<Track.Id> trackId = parseKml(zipInputStream);
|
||||||
if (trackId == null) {
|
if (trackId.isEmpty()) {
|
||||||
Log.d(TAG, "Unable to parse kml in kmz");
|
Log.d(TAG, "Unable to parse kml in kmz");
|
||||||
throw new ImportParserException(context.getString(R.string.import_unable_to_import_file, fileName));
|
throw new ImportParserException(context.getString(R.string.import_unable_to_import_file, fileName));
|
||||||
}
|
}
|
||||||
|
trackIds.addAll(trackId);
|
||||||
}
|
}
|
||||||
|
|
||||||
zipInputStream.closeEntry();
|
zipInputStream.closeEntry();
|
||||||
}
|
}
|
||||||
if (trackId == null) {
|
if (trackIds.isEmpty()) {
|
||||||
Log.d(TAG, "Unable to find doc.kml in kmz");
|
Log.d(TAG, "Unable to find doc.kml in kmz");
|
||||||
throw new ImportParserException(context.getString(R.string.import_no_kml_file_found));
|
throw new ImportParserException(context.getString(R.string.import_no_kml_file_found));
|
||||||
}
|
}
|
||||||
return trackId;
|
return trackIds;
|
||||||
} catch (ImportParserException | ImportAlreadyExistsException e) {
|
} catch (ImportParserException | ImportAlreadyExistsException e) {
|
||||||
Log.e(TAG, "Unable to import file", e);
|
Log.e(TAG, "Unable to import file", e);
|
||||||
throw e;
|
throw e;
|
||||||
@@ -193,10 +199,9 @@ public class KmzTrackImporter implements TrackImporter {
|
|||||||
/**
|
/**
|
||||||
* Deletes all images that remained in external storage that doesn't have a marker associated.
|
* Deletes all images that remained in external storage that doesn't have a marker associated.
|
||||||
*
|
*
|
||||||
* @param context the Context object.
|
|
||||||
* @param trackId the id of the Track.
|
* @param trackId the id of the Track.
|
||||||
*/
|
*/
|
||||||
private void deleteOrphanImages(Context context, Track.Id trackId) {
|
private void deleteOrphanImages(Track.Id trackId) {
|
||||||
// 1.- Gets all photo names in the markers of the track identified by id.
|
// 1.- Gets all photo names in the markers of the track identified by id.
|
||||||
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
|
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
|
||||||
List<Marker> markers = contentProviderUtils.getMarkers(trackId);
|
List<Marker> markers = contentProviderUtils.getMarkers(trackId);
|
||||||
@@ -222,25 +227,7 @@ public class KmzTrackImporter implements TrackImporter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private List<Track.Id> parseKml(ZipInputStream zipInputStream) {
|
||||||
* Cleans up import.
|
|
||||||
*
|
|
||||||
* @param trackId the trackId
|
|
||||||
*/
|
|
||||||
private void cleanImport(Context context, Track.Id trackId) {
|
|
||||||
if (PreferencesUtils.isRecording(trackId)) {
|
|
||||||
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
|
|
||||||
contentProviderUtils.deleteTrack(context, trackId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses kml
|
|
||||||
*
|
|
||||||
* @param zipInputStream the zip input stream
|
|
||||||
* @return the imported track id or -1L
|
|
||||||
*/
|
|
||||||
private Track.Id parseKml(ZipInputStream zipInputStream) {
|
|
||||||
KmlFileTrackImporter kmlFileTrackImporter = new KmlFileTrackImporter(context);
|
KmlFileTrackImporter kmlFileTrackImporter = new KmlFileTrackImporter(context);
|
||||||
|
|
||||||
try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(getKml(zipInputStream))) {
|
try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(getKml(zipInputStream))) {
|
||||||
|
|||||||
@@ -15,7 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
package de.dennisguse.opentracks.io.file.importer;
|
package de.dennisguse.opentracks.io.file.importer;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
|
|
||||||
@@ -30,8 +33,7 @@ public interface TrackImporter {
|
|||||||
* Import a file.
|
* Import a file.
|
||||||
*
|
*
|
||||||
* @param inputStream the file's input stream
|
* @param inputStream the file's input stream
|
||||||
* @return the imported track id or null.
|
|
||||||
*/
|
*/
|
||||||
//TODO Figure out how can make the import an atomic operation (incl. database transaction rollback).
|
@NonNull
|
||||||
Track.Id importFile(InputStream inputStream);
|
List<Track.Id> importFile(InputStream inputStream) throws ImportParserException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import android.database.sqlite.SQLiteException;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.IBinder;
|
|
||||||
import android.os.PowerManager.WakeLock;
|
import android.os.PowerManager.WakeLock;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
@@ -37,24 +36,22 @@ import androidx.core.app.TaskStackBuilder;
|
|||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.TrackListActivity;
|
import de.dennisguse.opentracks.TrackListActivity;
|
||||||
import de.dennisguse.opentracks.TrackRecordingActivity;
|
import de.dennisguse.opentracks.TrackRecordingActivity;
|
||||||
|
import de.dennisguse.opentracks.content.data.Distance;
|
||||||
import de.dennisguse.opentracks.content.data.Marker;
|
import de.dennisguse.opentracks.content.data.Marker;
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.content.provider.CustomContentProvider;
|
import de.dennisguse.opentracks.content.provider.CustomContentProvider;
|
||||||
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
|
|
||||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||||
import de.dennisguse.opentracks.io.file.exporter.ExportServiceResultReceiver;
|
import de.dennisguse.opentracks.io.file.exporter.ExportServiceResultReceiver;
|
||||||
import de.dennisguse.opentracks.services.handlers.GpsStatusValue;
|
import de.dennisguse.opentracks.services.handlers.GpsStatusValue;
|
||||||
import de.dennisguse.opentracks.services.handlers.HandlerServer;
|
import de.dennisguse.opentracks.services.handlers.HandlerServer;
|
||||||
|
import de.dennisguse.opentracks.services.sensors.AltitudeSumManager;
|
||||||
import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager;
|
import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager;
|
||||||
import de.dennisguse.opentracks.services.sensors.ElevationSumManager;
|
|
||||||
import de.dennisguse.opentracks.services.tasks.AnnouncementPeriodicTaskFactory;
|
import de.dennisguse.opentracks.services.tasks.AnnouncementPeriodicTaskFactory;
|
||||||
import de.dennisguse.opentracks.services.tasks.PeriodicTaskExecutor;
|
import de.dennisguse.opentracks.services.tasks.PeriodicTaskExecutor;
|
||||||
import de.dennisguse.opentracks.settings.SettingsActivity;
|
import de.dennisguse.opentracks.settings.SettingsActivity;
|
||||||
@@ -82,56 +79,47 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
private PeriodicTaskExecutor voiceExecutor;
|
private PeriodicTaskExecutor voiceExecutor;
|
||||||
private TrackRecordingServiceNotificationManager notificationManager;
|
private TrackRecordingServiceNotificationManager notificationManager;
|
||||||
|
|
||||||
private Track.Id recordingTrackId;
|
private SharedPreferences sharedPreferences;
|
||||||
private boolean recordingTrackPaused;
|
|
||||||
private int recordingDistanceInterval;
|
|
||||||
private int maxRecordingDistance;
|
|
||||||
|
|
||||||
|
private Distance recordingDistanceInterval;
|
||||||
|
private Distance maxRecordingDistance;
|
||||||
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
|
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||||
Context context = TrackRecordingService.this;
|
Context context = TrackRecordingService.this;
|
||||||
if (PreferencesUtils.isKey(TrackRecordingService.this, R.string.recording_track_id_key, key)) {
|
|
||||||
// Only through the TrackRecordingService can one stop a recording and set the recordingTrackId to -1L.
|
|
||||||
if (PreferencesUtils.isRecording(TrackRecordingService.this)) {
|
|
||||||
recordingTrackId = PreferencesUtils.getRecordingTrackId(TrackRecordingService.this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (PreferencesUtils.isKey(context, R.string.recording_track_paused_key, key)) {
|
|
||||||
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(context);
|
|
||||||
}
|
|
||||||
if (PreferencesUtils.isKey(context, R.string.stats_units_key, key)) {
|
if (PreferencesUtils.isKey(context, R.string.stats_units_key, key)) {
|
||||||
boolean metricUnits = PreferencesUtils.isMetricUnits(context);
|
boolean metricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, context);
|
||||||
voiceExecutor.setMetricUnits(metricUnits);
|
voiceExecutor.setMetricUnits(metricUnits);
|
||||||
|
notificationManager.setMetricUnits(metricUnits);
|
||||||
}
|
}
|
||||||
if (PreferencesUtils.isKey(context, R.string.voice_frequency_key, key)) {
|
if (PreferencesUtils.isKey(context, R.string.voice_frequency_key, key)) {
|
||||||
voiceExecutor.setTaskFrequency(PreferencesUtils.getVoiceFrequency(context));
|
voiceExecutor.setTaskFrequency(PreferencesUtils.getVoiceFrequency(sharedPreferences, context));
|
||||||
}
|
}
|
||||||
if (PreferencesUtils.isKey(context, R.string.recording_distance_interval_key, key)) {
|
if (PreferencesUtils.isKey(context, R.string.recording_distance_interval_key, key)) {
|
||||||
recordingDistanceInterval = PreferencesUtils.getRecordingDistanceInterval(context);
|
recordingDistanceInterval = PreferencesUtils.getRecordingDistanceInterval(sharedPreferences, context);
|
||||||
}
|
}
|
||||||
if (PreferencesUtils.isKey(context, R.string.max_recording_distance_key, key)) {
|
if (PreferencesUtils.isKey(context, R.string.max_recording_distance_key, key)) {
|
||||||
maxRecordingDistance = PreferencesUtils.getMaxRecordingDistance(context);
|
maxRecordingDistance = PreferencesUtils.getMaxRecordingDistance(sharedPreferences, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
handlerServer.onSharedPreferenceChanged(context, preferences, key);
|
handlerServer.onSharedPreferenceChanged(context, sharedPreferences, key);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// The following variables are set when recording:
|
// The following variables are set when recording:
|
||||||
private WakeLock wakeLock;
|
private WakeLock wakeLock;
|
||||||
private BluetoothRemoteSensorManager remoteSensorManager;
|
private BluetoothRemoteSensorManager remoteSensorManager;
|
||||||
private ElevationSumManager elevationSumManager;
|
private AltitudeSumManager altitudeSumManager;
|
||||||
|
|
||||||
private TrackStatisticsUpdater trackStatisticsUpdater;
|
private TrackStatisticsUpdater trackStatisticsUpdater;
|
||||||
private TrackPoint lastTrackPoint;
|
private TrackPoint lastTrackPoint;
|
||||||
private boolean isIdle;
|
private boolean isIdle;
|
||||||
|
|
||||||
private TrackRecordingServiceBinder binder = new TrackRecordingServiceBinder(this);
|
private final Binder binder = new Binder();
|
||||||
|
|
||||||
private HandlerServer handlerServer;
|
private HandlerServer handlerServer;
|
||||||
|
|
||||||
private List<TrackRecordingServiceCallback> listeners = new ArrayList<>();
|
private final TrackRecordingServiceStatus serviceStatus = new TrackRecordingServiceStatus();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
@@ -144,13 +132,9 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
|
|
||||||
notificationManager = new TrackRecordingServiceNotificationManager(this);
|
notificationManager = new TrackRecordingServiceNotificationManager(this);
|
||||||
|
|
||||||
// onSharedPreferenceChanged might not set recordingTrackId.
|
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
||||||
recordingTrackId = null;
|
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||||
|
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
||||||
PreferencesUtils.register(this, sharedPreferenceChangeListener);
|
|
||||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null);
|
|
||||||
|
|
||||||
restartTrackAfterServiceRestart();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -159,19 +143,13 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBinder onBind(Intent intent) {
|
public Binder onBind(Intent intent) {
|
||||||
return binder;
|
return binder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
if (listeners != null) {
|
serviceStatus.onStop();
|
||||||
for (TrackRecordingServiceCallback listener : listeners) {
|
|
||||||
listener.onGpsStatusChange(GpsStatusValue.GPS_NONE);
|
|
||||||
}
|
|
||||||
listeners.clear();
|
|
||||||
listeners = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
handlerServer.stop(this);
|
handlerServer.stop(this);
|
||||||
handlerServer = null;
|
handlerServer = null;
|
||||||
@@ -181,16 +159,16 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
remoteSensorManager = null;
|
remoteSensorManager = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (elevationSumManager != null) {
|
if (altitudeSumManager != null) {
|
||||||
elevationSumManager.stop(this);
|
altitudeSumManager.stop(this);
|
||||||
elevationSumManager = null;
|
altitudeSumManager = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reverse order from onCreate
|
// Reverse order from onCreate
|
||||||
showNotification(false); //TODO Why?
|
showNotification(false); //TODO Why?
|
||||||
|
|
||||||
PreferencesUtils.unregister(this, sharedPreferenceChangeListener);
|
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||||
|
sharedPreferences = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
voiceExecutor.shutdown();
|
voiceExecutor.shutdown();
|
||||||
@@ -200,9 +178,6 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
|
|
||||||
contentProviderUtils = null;
|
contentProviderUtils = null;
|
||||||
|
|
||||||
binder.detachFromService();
|
|
||||||
binder = null;
|
|
||||||
|
|
||||||
// This should be the next to last operation
|
// This should be the next to last operation
|
||||||
wakeLock = SystemUtils.releaseWakeLock(wakeLock);
|
wakeLock = SystemUtils.releaseWakeLock(wakeLock);
|
||||||
|
|
||||||
@@ -210,15 +185,15 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRecording() {
|
public boolean isRecording() {
|
||||||
return PreferencesUtils.isRecording(recordingTrackId);
|
return serviceStatus.isRecording();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPaused() {
|
public boolean isPaused() {
|
||||||
return recordingTrackPaused;
|
return serviceStatus.getRecordingTrackPaused();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Track.Id getRecordingTrackId() {
|
public Track.Id getRecordingTrackId() {
|
||||||
return recordingTrackId;
|
return serviceStatus.getRecordingTrackId();
|
||||||
}
|
}
|
||||||
|
|
||||||
public TrackStatistics getTrackStatistics() {
|
public TrackStatistics getTrackStatistics() {
|
||||||
@@ -248,14 +223,14 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (name == null) {
|
if (name == null) {
|
||||||
int nextMarkerNumber = contentProviderUtils.getNextMarkerNumber(recordingTrackId);
|
Integer nextMarkerNumber = contentProviderUtils.getNextMarkerNumber(serviceStatus.getRecordingTrackId());
|
||||||
if (nextMarkerNumber == -1) {
|
if (nextMarkerNumber == null) {
|
||||||
nextMarkerNumber = 1;
|
nextMarkerNumber = 1;
|
||||||
}
|
}
|
||||||
name = getString(R.string.marker_name_format, nextMarkerNumber + 1);
|
name = getString(R.string.marker_name_format, nextMarkerNumber + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
TrackPoint trackPoint = getLastValidTrackPointInCurrentSegment(recordingTrackId);
|
TrackPoint trackPoint = getLastValidTrackPointInCurrentSegment(serviceStatus.getRecordingTrackId());
|
||||||
if (trackPoint == null) {
|
if (trackPoint == null) {
|
||||||
Log.i(TAG, "Could not create a marker as trackPoint is unknown.");
|
Log.i(TAG, "Could not create a marker as trackPoint is unknown.");
|
||||||
return null;
|
return null;
|
||||||
@@ -267,11 +242,9 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
photoUrl = photoUrl != null ? photoUrl : "";
|
photoUrl = photoUrl != null ? photoUrl : "";
|
||||||
|
|
||||||
TrackStatistics stats = trackStatisticsUpdater.getTrackStatistics();
|
TrackStatistics stats = trackStatisticsUpdater.getTrackStatistics();
|
||||||
double length = stats.getTotalDistance();
|
|
||||||
long duration = stats.getTotalTime().toMillis();
|
|
||||||
|
|
||||||
// Insert marker
|
// Insert marker
|
||||||
Marker marker = new Marker(name, description, category, icon, recordingTrackId, length, duration, trackPoint, photoUrl);
|
Marker marker = new Marker(name, description, category, icon, serviceStatus.getRecordingTrackId(), stats, trackPoint, photoUrl);
|
||||||
Uri uri = contentProviderUtils.insertMarker(marker);
|
Uri uri = contentProviderUtils.insertMarker(marker);
|
||||||
return new Marker.Id(ContentUris.parseId(uri));
|
return new Marker.Id(ContentUris.parseId(uri));
|
||||||
}
|
}
|
||||||
@@ -281,9 +254,9 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
*
|
*
|
||||||
* @return the track id
|
* @return the track id
|
||||||
*/
|
*/
|
||||||
Track.Id startNewTrack() {
|
public Track.Id startNewTrack() {
|
||||||
if (isRecording()) {
|
if (isRecording()) {
|
||||||
Log.d(TAG, "Ignore startNewTrack. Already recording.");
|
Log.w(TAG, "Ignore startNewTrack. Already recording.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,8 +265,8 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
Uri uri = contentProviderUtils.insertTrack(track);
|
Uri uri = contentProviderUtils.insertTrack(track);
|
||||||
Track.Id trackId = new Track.Id(ContentUris.parseId(uri));
|
Track.Id trackId = new Track.Id(ContentUris.parseId(uri));
|
||||||
|
|
||||||
// Update shared preferences
|
// Set recording status
|
||||||
updateRecordingState(trackId, false);
|
serviceStatus.onChange(trackId, false);
|
||||||
|
|
||||||
// Update database
|
// Update database
|
||||||
track.setId(trackId);
|
track.setId(trackId);
|
||||||
@@ -305,7 +278,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
//TODO Pass TrackPoint
|
//TODO Pass TrackPoint
|
||||||
track.setName(TrackNameUtils.getTrackName(this, trackId, segmentStartTrackPoint.getTime()));
|
track.setName(TrackNameUtils.getTrackName(this, trackId, segmentStartTrackPoint.getTime()));
|
||||||
|
|
||||||
String category = PreferencesUtils.getDefaultActivity(this);
|
String category = PreferencesUtils.getDefaultActivity(sharedPreferences, this);
|
||||||
track.setCategory(category);
|
track.setCategory(category);
|
||||||
track.setIcon(TrackIconUtils.getIconValue(this, category));
|
track.setIcon(TrackIconUtils.getIconValue(this, category));
|
||||||
track.setTrackStatistics(trackStatisticsUpdater.getTrackStatistics());
|
track.setTrackStatistics(trackStatisticsUpdater.getTrackStatistics());
|
||||||
@@ -321,7 +294,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
*
|
*
|
||||||
* @param trackId the id of the track to be resumed.
|
* @param trackId the id of the track to be resumed.
|
||||||
*/
|
*/
|
||||||
void resumeTrack(Track.Id trackId) {
|
public void resumeTrack(Track.Id trackId) {
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
if (track == null) {
|
if (track == null) {
|
||||||
Log.e(TAG, "Ignore resumeTrack. Track " + trackId.getId() + " does not exists.");
|
Log.e(TAG, "Ignore resumeTrack. Track " + trackId.getId() + " does not exists.");
|
||||||
@@ -334,48 +307,24 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
|
|
||||||
insertTrackPoint(track, TrackPoint.createSegmentStartManual());
|
insertTrackPoint(track, TrackPoint.createSegmentStartManual());
|
||||||
|
|
||||||
// Update shared preferences.
|
// Set recording status
|
||||||
updateRecordingState(trackId, false);
|
serviceStatus.onChange(trackId, false);
|
||||||
|
|
||||||
startRecording();
|
startRecording();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
|
||||||
* Try to restart the previous recording track in case the service has been restarted by the system, which can sometimes happen.
|
public void resumeCurrentTrack() {
|
||||||
*/
|
|
||||||
private void restartTrackAfterServiceRestart() {
|
|
||||||
Track track = contentProviderUtils.getTrack(recordingTrackId);
|
|
||||||
if (track == null) {
|
|
||||||
if (isRecording()) {
|
|
||||||
Log.w(TAG, "track is null, but recordingTrackId not -1L. " + recordingTrackId.getId());
|
|
||||||
updateRecordingState(null, true);
|
|
||||||
}
|
|
||||||
showNotification(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.d(TAG, "Restarting track: " + track.getId());
|
|
||||||
|
|
||||||
trackStatisticsUpdater = new TrackStatisticsUpdater();
|
|
||||||
|
|
||||||
try (TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), null)) {
|
|
||||||
trackStatisticsUpdater.addTrackPoint(trackPointIterator, recordingDistanceInterval);
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
Log.e(TAG, "RuntimeException", e);
|
|
||||||
}
|
|
||||||
startRecording();
|
|
||||||
}
|
|
||||||
|
|
||||||
void resumeCurrentTrack() {
|
|
||||||
if (!isRecording() || !isPaused()) {
|
if (!isRecording() || !isPaused()) {
|
||||||
Log.d(TAG, "Ignore resumeCurrentTrack. Not recording or not paused.");
|
Log.w(TAG, "Ignore resumeCurrentTrack. Not recording or not paused.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateRecordingState(recordingTrackId, false);
|
// Set recording status
|
||||||
|
serviceStatus.onChange(false);
|
||||||
|
|
||||||
// Update database
|
// Update database
|
||||||
Track track = contentProviderUtils.getTrack(recordingTrackId);
|
Track track = contentProviderUtils.getTrack(serviceStatus.getRecordingTrackId());
|
||||||
if (track != null) {
|
if (track != null) {
|
||||||
insertTrackPoint(track, TrackPoint.createSegmentStartManual());
|
insertTrackPoint(track, TrackPoint.createSegmentStartManual());
|
||||||
}
|
}
|
||||||
@@ -391,8 +340,8 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
remoteSensorManager = new BluetoothRemoteSensorManager(this);
|
remoteSensorManager = new BluetoothRemoteSensorManager(this);
|
||||||
remoteSensorManager.start();
|
remoteSensorManager.start();
|
||||||
|
|
||||||
elevationSumManager = new ElevationSumManager();
|
altitudeSumManager = new AltitudeSumManager();
|
||||||
elevationSumManager.start(this);
|
altitudeSumManager.start(this);
|
||||||
|
|
||||||
lastTrackPoint = null;
|
lastTrackPoint = null;
|
||||||
isIdle = false;
|
isIdle = false;
|
||||||
@@ -403,7 +352,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
voiceExecutor.restore();
|
voiceExecutor.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
void tryStartGps() {
|
public void tryStartGps() {
|
||||||
if (isRecording()) return;
|
if (isRecording()) return;
|
||||||
|
|
||||||
startGps();
|
startGps();
|
||||||
@@ -415,17 +364,19 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
showNotification(true);
|
showNotification(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void endCurrentTrack() {
|
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
|
||||||
|
public void endCurrentTrack() {
|
||||||
if (!isRecording()) {
|
if (!isRecording()) {
|
||||||
Log.d(TAG, "Ignore endCurrentTrack. Not recording.");
|
Log.w(TAG, "Ignore endCurrentTrack. Not recording.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Need to remember the recordingTrackId before setting it to -1L
|
// Need to remember the recordingTrackId before setting it to null
|
||||||
boolean wasPause = isPaused();
|
boolean wasPause = isPaused();
|
||||||
Track.Id trackId = recordingTrackId;
|
Track.Id trackId = serviceStatus.getRecordingTrackId();
|
||||||
|
|
||||||
updateRecordingState(null, true);
|
// Set recording status
|
||||||
|
serviceStatus.onChange(null, true);
|
||||||
|
|
||||||
if (!wasPause) {
|
if (!wasPause) {
|
||||||
// Update database
|
// Update database
|
||||||
@@ -445,16 +396,18 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
endRecording(true);
|
endRecording(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pauseCurrentTrack() {
|
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
|
||||||
|
public void pauseCurrentTrack() {
|
||||||
if (!isRecording() || isPaused()) {
|
if (!isRecording() || isPaused()) {
|
||||||
Log.d(TAG, "Ignore pauseCurrentTrack. Not recording or paused.");
|
Log.w(TAG, "Ignore pauseCurrentTrack. Not recording or paused.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateRecordingState(recordingTrackId, true);
|
// Set recording status
|
||||||
|
serviceStatus.onChange(true);
|
||||||
|
|
||||||
// Update database
|
// Update database
|
||||||
Track track = contentProviderUtils.getTrack(recordingTrackId);
|
Track track = contentProviderUtils.getTrack(serviceStatus.getRecordingTrackId());
|
||||||
if (track != null) {
|
if (track != null) {
|
||||||
if (lastTrackPoint != null) {
|
if (lastTrackPoint != null) {
|
||||||
insertTrackPointIfNewer(track, lastTrackPoint);
|
insertTrackPointIfNewer(track, lastTrackPoint);
|
||||||
@@ -481,9 +434,9 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
remoteSensorManager.stop();
|
remoteSensorManager.stop();
|
||||||
remoteSensorManager = null;
|
remoteSensorManager = null;
|
||||||
}
|
}
|
||||||
if (elevationSumManager != null) {
|
if (altitudeSumManager != null) {
|
||||||
elevationSumManager.stop(this);
|
altitudeSumManager.stop(this);
|
||||||
elevationSumManager = null;
|
altitudeSumManager = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
lastTrackPoint = null;
|
lastTrackPoint = null;
|
||||||
@@ -493,6 +446,10 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
stopGps(trackStopped);
|
stopGps(trackStopped);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void stopGpsAndShutdown() {
|
||||||
|
stopGps(true);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stops gps.
|
* Stops gps.
|
||||||
*
|
*
|
||||||
@@ -526,21 +483,6 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
return lastTrackPoint != null;
|
return lastTrackPoint != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the recording states.
|
|
||||||
* This will inform subscribed {@link OnSharedPreferenceChangeListener}.
|
|
||||||
*
|
|
||||||
* @param trackId the recording track id
|
|
||||||
* @param paused true if the recording is paused
|
|
||||||
*/
|
|
||||||
private void updateRecordingState(Track.Id trackId, boolean paused) {
|
|
||||||
recordingTrackId = trackId;
|
|
||||||
long currentTrackId = trackId != null ? trackId.getId() : PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
|
||||||
PreferencesUtils.setLong(this, R.string.recording_track_id_key, currentTrackId);
|
|
||||||
recordingTrackPaused = paused;
|
|
||||||
PreferencesUtils.setBoolean(this, R.string.recording_track_paused_key, recordingTrackPaused);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void newTrackPoint(TrackPoint trackPoint, int recordingGpsAccuracy) {
|
public void newTrackPoint(TrackPoint trackPoint, int recordingGpsAccuracy) {
|
||||||
if (!isRecording() || isPaused()) {
|
if (!isRecording() || isPaused()) {
|
||||||
@@ -548,7 +490,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Track track = contentProviderUtils.getTrack(recordingTrackId);
|
Track track = contentProviderUtils.getTrack(serviceStatus.getRecordingTrackId());
|
||||||
if (track == null) {
|
if (track == null) {
|
||||||
Log.w(TAG, "Ignore newTrackPoint. No track.");
|
Log.w(TAG, "Ignore newTrackPoint. No track.");
|
||||||
return;
|
return;
|
||||||
@@ -556,7 +498,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
|
|
||||||
fillWithSensorDataSet(trackPoint);
|
fillWithSensorDataSet(trackPoint);
|
||||||
|
|
||||||
notificationManager.updateTrackPoint(this, trackPoint, recordingGpsAccuracy);
|
notificationManager.updateTrackPoint(this, track.getTrackStatistics(), trackPoint, recordingGpsAccuracy);
|
||||||
|
|
||||||
//TODO Figure out how to avoid loading the lastValidTrackPoint from the database
|
//TODO Figure out how to avoid loading the lastValidTrackPoint from the database
|
||||||
TrackPoint lastValidTrackPoint = getLastValidTrackPointInCurrentSegment(track.getId());
|
TrackPoint lastValidTrackPoint = getLastValidTrackPointInCurrentSegment(track.getId());
|
||||||
@@ -570,8 +512,8 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
double distanceToLastTrackLocation = trackPoint.distanceTo(lastValidTrackPoint);
|
Distance distanceToLastTrackLocation = trackPoint.distanceToPrevious(lastValidTrackPoint);
|
||||||
if (distanceToLastTrackLocation > maxRecordingDistance) {
|
if (distanceToLastTrackLocation.greaterThan(maxRecordingDistance)) {
|
||||||
insertTrackPointIfNewer(track, lastTrackPoint);
|
insertTrackPointIfNewer(track, lastTrackPoint);
|
||||||
|
|
||||||
trackPoint.setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
trackPoint.setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||||
@@ -582,7 +524,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trackPoint.hasSensorData() || distanceToLastTrackLocation >= recordingDistanceInterval) {
|
if (trackPoint.hasSensorData() || distanceToLastTrackLocation.greaterOrEqualThan(recordingDistanceInterval)) {
|
||||||
insertTrackPointIfNewer(track, lastTrackPoint);
|
insertTrackPointIfNewer(track, lastTrackPoint);
|
||||||
|
|
||||||
insertTrackPoint(track, trackPoint);
|
insertTrackPoint(track, trackPoint);
|
||||||
@@ -621,16 +563,18 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void newGpsStatus(GpsStatusValue gpsStatusValue) {
|
public void newGpsStatus(GpsStatusValue gpsStatusValue) {
|
||||||
if (listeners != null) {
|
notificationManager.updateContent(getString(gpsStatusValue.message));
|
||||||
notificationManager.updateContent(getString(gpsStatusValue.message));
|
serviceStatus.onChange(gpsStatusValue);
|
||||||
for (TrackRecordingServiceCallback listener : listeners) {
|
|
||||||
listener.onGpsStatusChange(gpsStatusValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addListener(TrackRecordingServiceCallback listener) {
|
public void addListener(@NonNull TrackRecordingServiceStatus.Listener listener) {
|
||||||
listeners.add(listener);
|
serviceStatus.addListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO Check that this is used everywhere, where addListener is called!
|
||||||
|
//Otherwise, we keep objects in referenced and waste memory until this service instance is terminated.
|
||||||
|
public void removeListener(@NonNull TrackRecordingServiceStatus.Listener listener) {
|
||||||
|
serviceStatus.removeListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -658,10 +602,14 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
*/
|
*/
|
||||||
private void insertTrackPoint(@NonNull Track track, @NonNull TrackPoint trackPoint) {
|
private void insertTrackPoint(@NonNull Track track, @NonNull TrackPoint trackPoint) {
|
||||||
try {
|
try {
|
||||||
if (elevationSumManager != null) {
|
if (altitudeSumManager != null) {
|
||||||
trackPoint.setElevationGain(elevationSumManager.getElevationGain_m());
|
trackPoint.setAltitudeGain(getAltitudeGain_m());
|
||||||
trackPoint.setElevationLoss(elevationSumManager.getElevationLoss_m());
|
trackPoint.setAltitudeLoss(getAltitudeLoss_m());
|
||||||
elevationSumManager.reset();
|
altitudeSumManager.reset();
|
||||||
|
}
|
||||||
|
if (remoteSensorManager != null) {
|
||||||
|
fillWithSensorDataSet(trackPoint);
|
||||||
|
remoteSensorManager.reset();
|
||||||
}
|
}
|
||||||
contentProviderUtils.insertTrackPoint(trackPoint, track.getId());
|
contentProviderUtils.insertTrackPoint(trackPoint, track.getId());
|
||||||
trackStatisticsUpdater.addTrackPoint(trackPoint, recordingDistanceInterval);
|
trackStatisticsUpdater.addTrackPoint(trackPoint, recordingDistanceInterval);
|
||||||
@@ -678,7 +626,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
voiceExecutor.update();
|
voiceExecutor.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
SensorDataSet getSensorDataSet() {
|
public SensorDataSet getSensorDataSet() {
|
||||||
if (remoteSensorManager == null) {
|
if (remoteSensorManager == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -694,31 +642,31 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the relative elevation gain (since last trackpoint).
|
* Returns the relative altitude gain (since last trackpoint).
|
||||||
*/
|
*/
|
||||||
Float getElevationGain_m() {
|
public Float getAltitudeGain_m() {
|
||||||
if (elevationSumManager == null || !elevationSumManager.isConnected()) {
|
if (altitudeSumManager == null || !altitudeSumManager.isConnected()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return elevationSumManager.getElevationGain_m();
|
return altitudeSumManager.getAltitudeGain_m();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the relative elevation loss (since last trackpoint).
|
* Returns the relative altitude loss (since last trackpoint).
|
||||||
*/
|
*/
|
||||||
Float getElevationLoss_m() {
|
public Float getAltitudeLoss_m() {
|
||||||
if (elevationSumManager == null || !elevationSumManager.isConnected()) {
|
if (altitudeSumManager == null || !altitudeSumManager.isConnected()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return elevationSumManager.getElevationLoss_m();
|
return altitudeSumManager.getAltitudeLoss_m();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showNotification(boolean isGpsStarted) {
|
private void showNotification(boolean isGpsStarted) {
|
||||||
if (isRecording()) {
|
if (isRecording()) {
|
||||||
Intent intent = IntentUtils.newIntent(this, TrackRecordingActivity.class)
|
Intent intent = IntentUtils.newIntent(this, TrackRecordingActivity.class)
|
||||||
.putExtra(TrackRecordingActivity.EXTRA_TRACK_ID, recordingTrackId);
|
.putExtra(TrackRecordingActivity.EXTRA_TRACK_ID, serviceStatus.getRecordingTrackId());
|
||||||
PendingIntent pendingIntent = TaskStackBuilder.create(this)
|
PendingIntent pendingIntent = TaskStackBuilder.create(this)
|
||||||
.addParentStack(TrackRecordingActivity.class)
|
.addParentStack(TrackRecordingActivity.class)
|
||||||
.addNextIntent(intent)
|
.addNextIntent(intent)
|
||||||
@@ -749,6 +697,11 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
this.remoteSensorManager = remoteSensorManager;
|
this.remoteSensorManager = remoteSensorManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public void setAltitudeSumManager(AltitudeSumManager altitudeSumManager) {
|
||||||
|
this.altitudeSumManager = altitudeSumManager;
|
||||||
|
}
|
||||||
|
|
||||||
public GpsStatusValue getGpsStatus() {
|
public GpsStatusValue getGpsStatus() {
|
||||||
return handlerServer.getGpsStatus();
|
return handlerServer.getGpsStatus();
|
||||||
}
|
}
|
||||||
@@ -764,4 +717,14 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class Binder extends android.os.Binder {
|
||||||
|
|
||||||
|
private Binder() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TrackRecordingService getService() {
|
||||||
|
return TrackRecordingService.this;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user