Compare commits
169 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| 3b9711d82e | |||
| 114fc8255f | |||
| 26e655d58f | |||
| 815b6a3cf1 | |||
| 3dc8a724d8 | |||
| 70f571cd09 | |||
| 3f5c3a5c08 | |||
| a13465391f | |||
| 97454b5369 | |||
| f6a47c4594 | |||
| fce76230e1 | |||
| fdf4336a9b | |||
| 844c6e9609 | |||
| 784942ffd2 | |||
| edf4ccc941 | |||
| d90258294f | |||
| 01f4c13307 | |||
| 873956426d | |||
| 4128c54960 | |||
| 9978bddf21 | |||
| a9726b9a94 | |||
| 05ba52bab5 | |||
| ca95f40e77 | |||
| 5cb85c4383 | |||
| d0a477b112 | |||
| ec365a18ca | |||
| 0ff735e559 | |||
| 8a35835791 | |||
| e3569dab56 | |||
| 4040e3f45b | |||
| eac7efba64 | |||
| 21a3971a56 | |||
| 4e4a9abf6f | |||
| a9624f72e7 | |||
| bb38d274fb | |||
| 418f092db9 | |||
| fa12086951 | |||
| 4a340380ac | |||
| cd4e0abd6f | |||
| 854c8727e5 | |||
| 2f3d9a29f3 | |||
| 18cdbe94a9 | |||
| 18e179cfe3 | |||
| 63e3c7a4e7 | |||
| c6deca9bbd | |||
| 9ef6ae5e25 | |||
| 29b1ded68c | |||
| 6239e2fcad | |||
| bdfcd9c89f | |||
| f8f78d28dd | |||
| cbe469d5e4 | |||
| e0553f812f | |||
| 5aa016b5b9 | |||
| c3741611dc | |||
| d157e94220 | |||
| 022d8146fe | |||
| 912fd1aa2f | |||
| 1d46981717 | |||
| e990b413cd | |||
| 3bbcbfe202 | |||
| 9d8f49f1dc | |||
| 9f6d6711e5 | |||
| f635d56ceb | |||
| ab47320216 | |||
| 47b2d0748a | |||
| bc66cd33a9 | |||
| fcc17413e3 | |||
| 163af2dab2 | |||
| 393e2022a1 |
@@ -18,4 +18,6 @@ jobs:
|
|||||||
api-level: ${{ matrix.api-level }}
|
api-level: ${{ matrix.api-level }}
|
||||||
target: ${{ matrix.target }}
|
target: ${{ matrix.target }}
|
||||||
arch: x86_64
|
arch: x86_64
|
||||||
|
profile: Nexus 6
|
||||||
script: ./gradlew connectedCheck
|
script: ./gradlew connectedCheck
|
||||||
|
|
||||||
|
|||||||
@@ -84,18 +84,16 @@ However, this is rather slow and does not provide updates while recording.
|
|||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
<div>
|
<div>
|
||||||
<img width="18%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_1.png">
|
<img width="23%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot1.png">
|
||||||
<img width="18%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_2.png">
|
<img width="23%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot2.png">
|
||||||
<img width="18%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_3.png">
|
<img width="23%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot3.png">
|
||||||
<img width="18%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_4.png">
|
<img width="23%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot4.png">
|
||||||
<img width="18%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_5.png">
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<img width="18%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_6.png">
|
<img width="23%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot5.png">
|
||||||
<img width="18%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_7.png">
|
<img width="23%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot6.png">
|
||||||
<img width="18%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_8.png">
|
<img width="23%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot7.png">
|
||||||
<img width="18%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_9.png">
|
<img width="23%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot8.png">
|
||||||
<img width="18%" src="fastlane/metadata/android/en-US/phoneScreenshots/screenshot_10.png">
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## Project history
|
## Project history
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Folder where svg screenshots are expected.
|
||||||
|
SRC_DIR="screenshots-svg/"
|
||||||
|
# Folder where png files will be saved.
|
||||||
|
DST_DIR="fastlane/metadata/android/en-US/phoneScreenshots/"
|
||||||
|
|
||||||
|
echo "Exporting svg files to png..."
|
||||||
|
|
||||||
|
# Source and destination folder must exist.
|
||||||
|
if [ ! -d "$SRC_DIR" ]
|
||||||
|
then
|
||||||
|
echo "\nERROR: ${SRC_DIR} doesn't exist."
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "$DST_DIR" ]
|
||||||
|
then
|
||||||
|
echo "\nERROR: ${DST_DIR} doesn't exist."
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
# It needs Inkscape that is the tool used to convert svg to png.
|
||||||
|
if ! command -v inkscape > /dev/null
|
||||||
|
then
|
||||||
|
echo "\nERROR: inkscape couldn't be found and it's needed to convert screenshots from svg to png."
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Convert all svg files from source directory.
|
||||||
|
for path in ${SRC_DIR}/*.svg
|
||||||
|
do
|
||||||
|
filename="${path##*/}"
|
||||||
|
basename="${filename%.svg}"
|
||||||
|
`inkscape ${path} --export-filename ${DST_DIR}/${basename}.png`
|
||||||
|
done
|
||||||
@@ -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 3821
|
versionCode 3990
|
||||||
versionName "v3.14.2"
|
versionName "v3.17.2"
|
||||||
|
|
||||||
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
||||||
|
|
||||||
@@ -86,18 +86,18 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0-alpha03'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0-alpha05'
|
||||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.1'
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||||
|
|
||||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||||
implementation 'androidx.preference:preference:1.1.1'
|
implementation 'androidx.preference:preference:1.1.1'
|
||||||
implementation 'androidx.documentfile:documentfile:1.0.1'
|
implementation 'androidx.documentfile:documentfile:1.0.1'
|
||||||
implementation 'com.google.android.material:material:1.2.1'
|
implementation 'com.google.android.material:material:1.3.0'
|
||||||
implementation 'com.android.support.constraint:constraint-layout:2.0.4'
|
implementation 'com.android.support.constraint:constraint-layout:2.0.4'
|
||||||
|
|
||||||
androidTestImplementation 'androidx.test:core:1.3.0'
|
androidTestImplementation 'androidx.test:core:1.3.0'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
||||||
androidTestImplementation 'androidx.test:rules:1.3.0'
|
androidTestImplementation 'androidx.test:rules:1.3.0'
|
||||||
androidTestImplementation 'androidx.test:runner:1.3.0'
|
androidTestImplementation 'androidx.test:runner:1.3.0'
|
||||||
androidTestImplementation 'org.mockito:mockito-android:3.7.7'
|
androidTestImplementation 'org.mockito:mockito-android:3.8.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 {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -9,25 +9,27 @@ class Track {
|
|||||||
}
|
}
|
||||||
Track o-- TrackPoint
|
Track o-- TrackPoint
|
||||||
Track o-- Marker
|
Track o-- Marker
|
||||||
Track "1" - "1" TripStatistics
|
Track "1" - "1" TrackStatistics
|
||||||
|
|
||||||
class TripStatistics {
|
class TrackStatistics {
|
||||||
- startTime
|
- startTime
|
||||||
- stopTime
|
- stopTime
|
||||||
- totalDistance
|
- totalDistance
|
||||||
- totalTime
|
- totalTime
|
||||||
- movingTime
|
- movingTime
|
||||||
- maxSpeed
|
- maxSpeed
|
||||||
- totalElevationGain
|
- totalGain
|
||||||
}
|
}
|
||||||
|
|
||||||
note left of TripStatistics
|
class TrackStatisticsUpdater {
|
||||||
The time is based upon device time.
|
}
|
||||||
end note
|
|
||||||
|
|
||||||
|
TrackStatisticsUpdater ..> TrackStatistics : creates/updates
|
||||||
|
TrackStatisticsUpdater ..> TrackPoint : uses
|
||||||
|
|
||||||
class TrackPoint {
|
class TrackPoint {
|
||||||
- id
|
- id (database id, order)
|
||||||
|
- type
|
||||||
- trackId
|
- trackId
|
||||||
- longitude
|
- longitude
|
||||||
- latitude
|
- latitude
|
||||||
@@ -41,30 +43,6 @@ class TrackPoint {
|
|||||||
- sensor_power
|
- sensor_power
|
||||||
}
|
}
|
||||||
|
|
||||||
note right of TrackPoint
|
|
||||||
TODO
|
|
||||||
TrackPoints are mixing two concepts:
|
|
||||||
1. a GPS measurement provided by the GPS hardware
|
|
||||||
2. and also segment markers.
|
|
||||||
These are marked by PAUSE / RESUME TrackPoints.
|
|
||||||
|
|
||||||
While exporting PAUSE/RESUME are not used to create these segments and these are partly recreated when imported.
|
|
||||||
However, the timestamp of PAUSE/RESUME is not exported and can therefore not be restored.
|
|
||||||
This prevents us from restoring the exact TripStatistics.
|
|
||||||
It is also noteworthy that neither PAUSE/RESUME TrackPoints are used for computing the TrackStatistics.
|
|
||||||
Only the timestamp to _create_ those is used.
|
|
||||||
end note
|
|
||||||
|
|
||||||
note left of TrackPoint
|
|
||||||
TODO
|
|
||||||
The time stored in TrackPoints is either:
|
|
||||||
1. created via GPS: provided by the GPS hardware
|
|
||||||
2. created by the user (pause/resume): device time.
|
|
||||||
|
|
||||||
We know that the GPS time and device time are often not in sync.
|
|
||||||
end note
|
|
||||||
|
|
||||||
TrackPoint "1" - "1" android.location.Location
|
|
||||||
|
|
||||||
class Marker {
|
class Marker {
|
||||||
- id
|
- id
|
||||||
@@ -75,8 +53,28 @@ class Marker {
|
|||||||
- icon
|
- icon
|
||||||
- length
|
- length
|
||||||
- duration
|
- duration
|
||||||
- location
|
- longitude
|
||||||
- tripStatistics
|
- latitude
|
||||||
- photoUrl
|
- photoUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
note left of TrackPoint
|
||||||
|
As of OpenTracks version 3.15.0, all times are using device time.
|
||||||
|
Before that TrackPoint.time contained GPS time (determined by GPS hardware).
|
||||||
|
However, start/pause/stop events (also stored as TrackPoints) used device time.
|
||||||
|
end note
|
||||||
|
|
||||||
|
note right of Track
|
||||||
|
A track is an ordered collection of one or more segments (i.e., continuous parts of distance covered).
|
||||||
|
Segments may be started by a user (i.e., start a track recording, continue a paused track, or resume a track) as well as stopped by the user (i.e., pausing or stopping a recording).
|
||||||
|
Also segments may started automatically while recording (i.e., distance to previous location was to large).
|
||||||
|
Note that this finishes the previous segment.
|
||||||
|
|
||||||
|
Segment data is stored as TrackPoints (Type.SEGMENT_START_MANUAL, Type.SEGMENT_START_AUTOMATIC, or Type.SEGMENT_END_MANUAL).
|
||||||
|
Trackpoints with Type.SEGMENT_START_AUTOMATIC also mark the end of the previous segment.
|
||||||
|
All TrackPoints of Type.TRACKPOINT belong to the segment started by the prior TrackPoint with Type.SEGMENT_START_(MANUAL|AUTOMATIC).
|
||||||
|
Trackpoints of Type.SEGMENT_START_MANUAL or Type.SEGMENT_END_MANUAL do not contain location data or sensor data.
|
||||||
|
|
||||||
|
Tracks recorded prior to OpenTracks version 3.15.0, do neither begin with a Type.SEGMENT_START_MANUAL nor end with a Type.SEGMENT_END_MANUAL.
|
||||||
|
end note
|
||||||
@enduml
|
@enduml
|
||||||
@@ -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.14.3: OpenTracks
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Downgrading database from upcoming release v3.15.0
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
v3.15.0: OpenTracks
|
||||||
|
|
||||||
|
WARNING:
|
||||||
|
This release applies database changes that CANNOT BE REVERTED.
|
||||||
|
As KMZ/KML export/import behaviour changes as well, it is recommended to (A) take a backup BEFORE upgrading and (B) afterwards.
|
||||||
|
Recommended format: KMZ.
|
||||||
|
|
||||||
|
BREAKING CHANGES:
|
||||||
|
- Internal storage of track segments is changed
|
||||||
|
(user generated events are now considered trackpoints without a location but with timestamp)
|
||||||
|
- Device time instead of GPS time is stored
|
||||||
|
(existing data is not modified)
|
||||||
|
- Dashboard API v2
|
||||||
|
(please upgrade OSMDashboard 2.10.0)
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- KML/KMZ can used to backup and restore exactly (incl. statistics)
|
||||||
|
|
||||||
|
Developer:
|
||||||
|
- Reworked track segmentation and it's storage (formerly PAUSE/RESUME marker)
|
||||||
|
- TrackStatistics are computed solely using TrackPoints
|
||||||
|
- Replaced long with Instant/Duration
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
v3.15.1: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- After recording show sensor statistics (e.g., avg HR)
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Crash on recording start with show latitude/longitude enabled
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
v3.15.2: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Updated translations
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Crash on GPX export
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
v3.15.3: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Computation avg/max: heart rate, cadence, and power
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- KML/KMZ do not export #start/#end placemarks anymore
|
||||||
|
- While recording: Speed/location was sometimes falsly shown as 0.
|
||||||
@@ -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
|
||||||
|
After Width: | Height: | Size: 303 KiB |
|
After Width: | Height: | Size: 574 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 456 KiB |
|
After Width: | Height: | Size: 630 KiB |
|
After Width: | Height: | Size: 416 KiB |
|
After Width: | Height: | Size: 508 KiB |
|
After Width: | Height: | Size: 798 KiB |
|
Before Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 139 KiB |
|
Before Width: | Height: | Size: 249 KiB |
|
Before Width: | Height: | Size: 132 KiB |
|
Before Width: | Height: | Size: 254 KiB |
|
Before Width: | Height: | Size: 159 KiB |
|
Before Width: | Height: | Size: 159 KiB |
|
Before Width: | Height: | Size: 123 KiB |
|
Before Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 144 KiB |
|
After Width: | Height: | Size: 174 KiB |
|
After Width: | Height: | Size: 862 KiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 338 KiB |
|
After Width: | Height: | Size: 665 KiB |
|
After Width: | Height: | Size: 248 KiB |
|
After Width: | Height: | Size: 661 KiB |
|
After Width: | Height: | Size: 1001 KiB |
@@ -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));
|
||||||
|
|||||||
@@ -25,7 +25,12 @@ import org.junit.Before;
|
|||||||
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.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;
|
||||||
@@ -40,7 +45,7 @@ import static org.junit.Assert.assertEquals;
|
|||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class DescriptionGeneratorTest {
|
public class DescriptionGeneratorTest {
|
||||||
|
|
||||||
private static final long START_TIME = 1288721514000L;
|
private static final Instant START_TIME = Instant.ofEpochMilli(1288721514000L);
|
||||||
private DescriptionGenerator descriptionGenerator;
|
private DescriptionGenerator descriptionGenerator;
|
||||||
|
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
@@ -57,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(600000);
|
stats.setTotalTime(Duration.ofMillis(600000));
|
||||||
stats.setMovingTime(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(6000);
|
stats.setTotalAltitudeGain(6000f);
|
||||||
stats.setTotalElevationLoss(6000);
|
stats.setTotalAltitudeLoss(6000f);
|
||||||
stats.setStartTime_ms(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>"
|
||||||
@@ -92,18 +97,18 @@ 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(600000);
|
stats.setTotalTime(Duration.ofMillis(600000));
|
||||||
stats.setMovingTime(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(6000);
|
stats.setTotalAltitudeGain(6000f);
|
||||||
stats.setTotalElevationLoss(6000);
|
stats.setTotalAltitudeLoss(6000f);
|
||||||
stats.setStartTime_ms(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"
|
||||||
@@ -129,52 +134,52 @@ 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());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link DescriptionGenerator#writeTime(long, StringBuilder, int, String)}.
|
* Tests {@link DescriptionGenerator#writeTime(Duration, StringBuilder, int, String)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testWriteTime() {
|
public void testWriteTime() {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
descriptionGenerator.writeTime(1000, builder, R.string.description_total_time, "<br>");
|
descriptionGenerator.writeTime(Duration.ofMillis(1000), builder, R.string.description_total_time, "<br>");
|
||||||
assertEquals("Total time: 00:01<br>", builder.toString());
|
assertEquals("Total time: 00:01<br>", builder.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
package de.dennisguse.opentracks.content.data;
|
package de.dennisguse.opentracks.content.data;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.location.Location;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.util.Pair;
|
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.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.
|
||||||
@@ -38,6 +41,7 @@ public class TestDataUtil {
|
|||||||
* @param trackId the trackId of the track
|
* @param trackId the trackId of the track
|
||||||
* @param numPoints the trackPoints number in the track
|
* @param numPoints the trackPoints number in the track
|
||||||
*/
|
*/
|
||||||
|
@Deprecated //TODO Should start with SEGMENT_START_MANUAL and end with SEGMENT_END_MANUAL.
|
||||||
public static Pair<Track, List<TrackPoint>> createTrack(Track.Id trackId, int numPoints) {
|
public static Pair<Track, List<TrackPoint>> createTrack(Track.Id trackId, int numPoints) {
|
||||||
Track track = createTrack(trackId);
|
Track track = createTrack(trackId);
|
||||||
|
|
||||||
@@ -49,6 +53,55 @@ public class TestDataUtil {
|
|||||||
return new Pair<>(track, trackPoints);
|
return new Pair<>(track, trackPoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static TrackData createTestingTrack(Track.Id trackId) {
|
||||||
|
Track track = createTrack(trackId);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
List<TrackPoint> trackPoints = List.of(
|
||||||
|
TrackPoint.createSegmentStartManualWithTime(Instant.ofEpochSecond(i++)),
|
||||||
|
createTrackPoint(i++),
|
||||||
|
createTrackPoint(i++),
|
||||||
|
createTrackPoint(i++),
|
||||||
|
createTrackPoint(i++, TrackPoint.Type.SEGMENT_START_AUTOMATIC),
|
||||||
|
createTrackPoint(i++),
|
||||||
|
createTrackPoint(i++),
|
||||||
|
createTrackPoint(i++),
|
||||||
|
createTrackPoint(i++, TrackPoint.Type.SEGMENT_END_MANUAL),
|
||||||
|
|
||||||
|
TrackPoint.createSegmentStartManualWithTime(Instant.ofEpochSecond(i++)),
|
||||||
|
createTrackPoint(i++),
|
||||||
|
createTrackPoint(i++),
|
||||||
|
createTrackPoint(i++),
|
||||||
|
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(
|
||||||
|
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, stats, trackPoints.get(4), null),
|
||||||
|
new Marker("Marker 3", "Marker description 3", "Marker category 3", "", trackId, stats, trackPoints.get(5), null)
|
||||||
|
);
|
||||||
|
|
||||||
|
return new TrackData(track, trackPoints, markers);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TrackData {
|
||||||
|
public final Track track;
|
||||||
|
public final List<TrackPoint> trackPoints;
|
||||||
|
public final List<Marker> markers;
|
||||||
|
|
||||||
|
public TrackData(Track track, List<TrackPoint> trackPoints, List<Marker> markers) {
|
||||||
|
this.track = track;
|
||||||
|
this.trackPoints = trackPoints;
|
||||||
|
this.markers = markers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static Track createTrackAndInsert(ContentProviderUtils contentProviderUtils, Track.Id trackId, int numPoints) {
|
public static Track createTrackAndInsert(ContentProviderUtils contentProviderUtils, Track.Id trackId, int numPoints) {
|
||||||
Pair<Track, List<TrackPoint>> pair = createTrack(trackId, numPoints);
|
Pair<Track, List<TrackPoint>> pair = createTrack(trackId, numPoints);
|
||||||
|
|
||||||
@@ -57,25 +110,26 @@ 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 trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT);
|
||||||
trackPoint.setLatitude(INITIAL_LATITUDE + (double) i / 10000.0);
|
trackPoint.setLatitude(INITIAL_LATITUDE + (double) i / 10000.0);
|
||||||
trackPoint.setLongitude(INITIAL_LONGITUDE - (double) i / 10000.0);
|
trackPoint.setLongitude(INITIAL_LONGITUDE - (double) i / 10000.0);
|
||||||
trackPoint.setAccuracy((float) i / 100.0f);
|
trackPoint.setAccuracy((float) i / 100.0f);
|
||||||
trackPoint.setAltitude(i * ALTITUDE_INTERVAL);
|
trackPoint.setAltitude(i * ALTITUDE_INTERVAL);
|
||||||
trackPoint.setTime(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TrackPoint createTrackPoint(int i, TrackPoint.Type type) {
|
||||||
|
TrackPoint trackPoint = createTrackPoint(i);
|
||||||
|
trackPoint.setType(type);
|
||||||
return trackPoint;
|
return trackPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,20 +144,26 @@ public class TestDataUtil {
|
|||||||
contentProviderUtils.bulkInsertTrackPoint(trackPoints, track.getId());
|
contentProviderUtils.bulkInsertTrackPoint(trackPoints, track.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static Marker createMarkerWithPhoto(Context context, Track.Id trackId, TrackPoint trackPoint) throws IOException {
|
||||||
* Creates a Marker with a photo.
|
|
||||||
*
|
|
||||||
* @param context The context.
|
|
||||||
* @param trackId The track id.
|
|
||||||
* @param location The location.
|
|
||||||
* @return the Marker created.
|
|
||||||
*/
|
|
||||||
public static Marker createMarkerWithPhoto(Context context, Track.Id trackId, Location location) throws IOException {
|
|
||||||
File dstFile = new File(FileUtils.getImageUrl(context, trackId));
|
File dstFile = new File(FileUtils.getImageUrl(context, trackId));
|
||||||
dstFile.createNewFile();
|
dstFile.createNewFile();
|
||||||
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, location, 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package de.dennisguse.opentracks.content.data;
|
||||||
|
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TestSensorDataUtil {
|
||||||
|
|
||||||
|
private final List<TrackPoint> trackPointList = new ArrayList<>();
|
||||||
|
private final List<SensorData> sensorDataList = new ArrayList<>();
|
||||||
|
|
||||||
|
public void add(Instant time, Float hr, Float cadence, Float power, TrackPoint.Type type) {
|
||||||
|
sensorDataList.add(new TestSensorDataUtil.SensorData(time, hr, cadence, power, type));
|
||||||
|
TrackPoint tp = new TrackPoint(type);
|
||||||
|
int i = trackPointList.size() + 1;
|
||||||
|
tp.setLatitude(TestDataUtil.INITIAL_LATITUDE + (double) i / 10000.0);
|
||||||
|
tp.setLongitude(TestDataUtil.INITIAL_LONGITUDE - (double) i / 10000.0);
|
||||||
|
tp.setHeartRate_bpm(hr);
|
||||||
|
tp.setCyclingCadence_rpm(cadence);
|
||||||
|
tp.setPower(power);
|
||||||
|
tp.setAccuracy(1f);
|
||||||
|
tp.setAltitude(1f);
|
||||||
|
tp.setTime(time);
|
||||||
|
tp.setSpeed(Speed.of(5f + (i / 10f)));
|
||||||
|
tp.setAltitudeGain(3f);
|
||||||
|
tp.setAltitudeLoss(3f);
|
||||||
|
trackPointList.add(tp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TrackPoint> getTrackPointList() {
|
||||||
|
return this.trackPointList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SensorDataStats computeStats() {
|
||||||
|
if (sensorDataList.size() <= 1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
SensorDataStats stats = new SensorDataStats();
|
||||||
|
long timeElapsed;
|
||||||
|
long movingTime = 0;
|
||||||
|
SensorData dataPrev = sensorDataList.get(0);
|
||||||
|
stats.maxHr = dataPrev.hr;
|
||||||
|
stats.maxCadence = dataPrev.cadence;
|
||||||
|
SensorData dataCurrent;
|
||||||
|
for (int i = 1; i < sensorDataList.size(); i++) {
|
||||||
|
dataCurrent = sensorDataList.get(i);
|
||||||
|
if (dataPrev.type != TrackPoint.Type.SEGMENT_START_MANUAL) {
|
||||||
|
timeElapsed = dataCurrent.type != TrackPoint.Type.SEGMENT_START_MANUAL ? dataCurrent.time.getEpochSecond() - dataPrev.time.getEpochSecond() : 0;
|
||||||
|
stats.avgHr += (dataPrev.hr * timeElapsed);
|
||||||
|
stats.maxHr = Math.max(dataPrev.hr, stats.maxHr);
|
||||||
|
stats.avgCadence += (dataPrev.cadence * timeElapsed);
|
||||||
|
stats.maxCadence = Math.max(dataPrev.cadence, stats.maxCadence);
|
||||||
|
stats.avgPower += (dataPrev.power * timeElapsed);
|
||||||
|
|
||||||
|
movingTime += timeElapsed;
|
||||||
|
}
|
||||||
|
dataPrev = dataCurrent;
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.avgHr /= movingTime;
|
||||||
|
stats.avgCadence /= movingTime;
|
||||||
|
stats.avgPower /= movingTime;
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SensorData {
|
||||||
|
final Instant time;
|
||||||
|
final float hr;
|
||||||
|
final float cadence;
|
||||||
|
final float power;
|
||||||
|
final TrackPoint.Type type;
|
||||||
|
|
||||||
|
public SensorData(Instant time, Float hr, Float cadence, Float power, TrackPoint.Type type) {
|
||||||
|
this.time = time;
|
||||||
|
this.hr = hr == null ? 0 : hr;
|
||||||
|
this.cadence = cadence == null ? 0 : cadence;
|
||||||
|
this.power = power == null ? 0 : power;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SensorDataStats {
|
||||||
|
public float avgHr = 0f;
|
||||||
|
public float maxHr = 0f;
|
||||||
|
public float avgCadence = 0f;
|
||||||
|
public float maxCadence = 0f;
|
||||||
|
public float avgPower = 0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package de.dennisguse.opentracks.content.data;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
public class TrackPointTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isRecent_true() {
|
||||||
|
TrackPoint tp = new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL);
|
||||||
|
tp.setTime(Instant.now());
|
||||||
|
|
||||||
|
assertTrue(tp.isRecent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isRecent_false() {
|
||||||
|
TrackPoint tp = new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL);
|
||||||
|
tp.setTime(Instant.now().minus(2, ChronoUnit.MINUTES));
|
||||||
|
|
||||||
|
assertFalse(tp.isRecent());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,18 +33,26 @@ import org.mockito.junit.MockitoJUnitRunner;
|
|||||||
|
|
||||||
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.temporal.ChronoUnit;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
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.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;
|
||||||
import de.dennisguse.opentracks.content.data.TracksColumns;
|
import de.dennisguse.opentracks.content.data.TracksColumns;
|
||||||
|
import de.dennisguse.opentracks.stats.SensorStatistics;
|
||||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||||
import de.dennisguse.opentracks.util.FileUtils;
|
import de.dennisguse.opentracks.util.FileUtils;
|
||||||
import de.dennisguse.opentracks.util.UUIDUtils;
|
import de.dennisguse.opentracks.util.UUIDUtils;
|
||||||
@@ -88,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()) {
|
||||||
@@ -118,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());
|
||||||
@@ -144,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;
|
||||||
@@ -231,7 +232,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
Track track = TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId, 10);
|
||||||
|
|
||||||
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
||||||
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint.getLocation());
|
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
|
||||||
contentProviderUtils.insertMarker(marker);
|
contentProviderUtils.insertMarker(marker);
|
||||||
|
|
||||||
ContentResolver contentResolver = context.getContentResolver();
|
ContentResolver contentResolver = context.getContentResolver();
|
||||||
@@ -270,7 +271,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
Track.Id trackId2 = new Track.Id(random + 1);
|
Track.Id trackId2 = new Track.Id(random + 1);
|
||||||
Track.Id trackId3 = new Track.Id(random + 2);
|
Track.Id trackId3 = new Track.Id(random + 2);
|
||||||
|
|
||||||
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId1, 0);
|
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId1, 10);
|
||||||
|
|
||||||
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId2, 10);
|
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId2, 10);
|
||||||
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId3, 10);
|
TestDataUtil.createTrackAndInsert(contentProviderUtils, trackId3, 10);
|
||||||
@@ -282,7 +283,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
Cursor tracksCursor = contentResolver.query(TracksColumns.CONTENT_URI, null, null, null, TracksColumns._ID);
|
||||||
assertEquals(3, tracksCursor.getCount());
|
assertEquals(3, tracksCursor.getCount());
|
||||||
Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
|
Cursor tracksPointsCursor = contentResolver.query(TrackPointsColumns.CONTENT_URI_BY_ID, null, null, null, TrackPointsColumns._ID);
|
||||||
assertEquals(20, tracksPointsCursor.getCount());
|
assertEquals(30, tracksPointsCursor.getCount());
|
||||||
Cursor markerCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID);
|
Cursor markerCursor = contentResolver.query(MarkerColumns.CONTENT_URI, null, null, null, MarkerColumns._ID);
|
||||||
assertEquals(1, markerCursor.getCount());
|
assertEquals(1, markerCursor.getCount());
|
||||||
// Delete one track.
|
// Delete one track.
|
||||||
@@ -313,12 +314,12 @@ public class CustomContentProviderUtilsTest {
|
|||||||
|
|
||||||
// Insert a marker in tracks trackId and trackId + 1.
|
// Insert a marker in tracks trackId and trackId + 1.
|
||||||
TrackPoint trackPoint1 = contentProviderUtils.getLastValidTrackPoint(trackId1);
|
TrackPoint trackPoint1 = contentProviderUtils.getLastValidTrackPoint(trackId1);
|
||||||
Marker marker1 = TestDataUtil.createMarkerWithPhoto(context, trackId1, trackPoint1.getLocation());
|
Marker marker1 = TestDataUtil.createMarkerWithPhoto(context, trackId1, trackPoint1);
|
||||||
contentProviderUtils.insertMarker(marker1);
|
contentProviderUtils.insertMarker(marker1);
|
||||||
File dir1 = FileUtils.getPhotoDir(context, trackId1);
|
File dir1 = FileUtils.getPhotoDir(context, trackId1);
|
||||||
|
|
||||||
TrackPoint trackPoint2 = contentProviderUtils.getLastValidTrackPoint(trackId2);
|
TrackPoint trackPoint2 = contentProviderUtils.getLastValidTrackPoint(trackId2);
|
||||||
Marker marker2 = TestDataUtil.createMarkerWithPhoto(context, trackId2, trackPoint2.getLocation());
|
Marker marker2 = TestDataUtil.createMarkerWithPhoto(context, trackId2, trackPoint2);
|
||||||
contentProviderUtils.insertMarker(marker2);
|
contentProviderUtils.insertMarker(marker2);
|
||||||
File dir2 = FileUtils.getPhotoDir(context, trackId2);
|
File dir2 = FileUtils.getPhotoDir(context, trackId2);
|
||||||
|
|
||||||
@@ -423,19 +424,17 @@ public class CustomContentProviderUtilsTest {
|
|||||||
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
||||||
Pair<Track, List<TrackPoint>> track = TestDataUtil.createTrack(trackId, 10);
|
Pair<Track, List<TrackPoint>> track = TestDataUtil.createTrack(trackId, 10);
|
||||||
|
|
||||||
// Bottom
|
|
||||||
long startTime = 1000L;
|
|
||||||
// AverageSpeed
|
// AverageSpeed
|
||||||
TrackStatistics statistics = new TrackStatistics();
|
TrackStatistics statistics = new TrackStatistics();
|
||||||
statistics.setStartTime_ms(startTime);
|
statistics.setStartTime(Instant.ofEpochMilli(1000));
|
||||||
statistics.setStopTime_ms(2500L);
|
statistics.setStopTime(Instant.ofEpochMilli(2500));
|
||||||
statistics.setTotalTime(1500L);
|
statistics.setTotalTime(Duration.ofMillis(1500));
|
||||||
statistics.setMovingTime(700L);
|
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);
|
||||||
@@ -527,7 +526,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
|
|
||||||
// Insert at first.
|
// Insert at first.
|
||||||
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
||||||
Marker marker1 = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint.getLocation());
|
Marker marker1 = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
|
||||||
contentProviderUtils.insertMarker(marker1);
|
contentProviderUtils.insertMarker(marker1);
|
||||||
|
|
||||||
// Check insert was done.
|
// Check insert was done.
|
||||||
@@ -567,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);
|
||||||
@@ -609,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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -661,7 +660,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
|
|
||||||
// Insert at first.
|
// Insert at first.
|
||||||
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
||||||
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint.getLocation());
|
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
|
||||||
marker.setDescription(TEST_DESC);
|
marker.setDescription(TEST_DESC);
|
||||||
Marker.Id markerId = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(marker)));
|
Marker.Id markerId = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(marker)));
|
||||||
|
|
||||||
@@ -696,7 +695,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
|
|
||||||
// Insert at first.
|
// Insert at first.
|
||||||
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
||||||
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint.getLocation());
|
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
|
||||||
marker.setDescription(TEST_DESC);
|
marker.setDescription(TEST_DESC);
|
||||||
Marker.Id markerId = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(marker)));
|
Marker.Id markerId = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(marker)));
|
||||||
|
|
||||||
@@ -730,9 +729,9 @@ public class CustomContentProviderUtilsTest {
|
|||||||
|
|
||||||
// Insert two markers with photos.
|
// Insert two markers with photos.
|
||||||
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
||||||
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint.getLocation());
|
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
|
||||||
marker.setDescription(TEST_DESC);
|
marker.setDescription(TEST_DESC);
|
||||||
Marker otherMarker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint.getLocation());
|
Marker otherMarker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
|
||||||
otherMarker.setDescription(TEST_DESC);
|
otherMarker.setDescription(TEST_DESC);
|
||||||
Marker.Id markerId = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(marker)));
|
Marker.Id markerId = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(marker)));
|
||||||
contentProviderUtils.insertMarker(otherMarker);
|
contentProviderUtils.insertMarker(otherMarker);
|
||||||
@@ -766,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());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -809,8 +808,8 @@ public class CustomContentProviderUtilsTest {
|
|||||||
// then
|
// then
|
||||||
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(), 0.01);
|
assertEquals(time, trackPoint.getTime().toEpochMilli());
|
||||||
assertEquals(speed, trackPoint.getSpeed(), 0.01);
|
assertEquals(speed, trackPoint.getSpeed().toMPS(), 0.01);
|
||||||
assertFalse(trackPoint.hasHeartRate());
|
assertFalse(trackPoint.hasHeartRate());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -825,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
|
||||||
@@ -843,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);
|
||||||
@@ -875,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());
|
||||||
@@ -927,4 +926,337 @@ public class CustomContentProviderUtilsTest {
|
|||||||
assertEquals("12", ContentProviderUtils.formatIdListForUri(new Track.Id(12)));
|
assertEquals("12", ContentProviderUtils.formatIdListForUri(new Track.Id(12)));
|
||||||
assertEquals("42,43,44", ContentProviderUtils.formatIdListForUri(new Track.Id(42), new Track.Id(43), new Track.Id(44)));
|
assertEquals("42,43,44", ContentProviderUtils.formatIdListForUri(new Track.Id(42), new Track.Id(43), new Track.Id(44)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetSensorStats_noSensorData() {
|
||||||
|
// given
|
||||||
|
List<TrackPoint> trackPointList = new ArrayList<>();
|
||||||
|
TrackPoint trackPoint = TestDataUtil.createTrackPoint(1);
|
||||||
|
trackPoint.setType(TrackPoint.Type.TRACKPOINT);
|
||||||
|
trackPoint.setPower(null);
|
||||||
|
trackPoint.setCyclingCadence_rpm(null);
|
||||||
|
trackPoint.setHeartRate_bpm(null);
|
||||||
|
trackPointList.add(trackPoint);
|
||||||
|
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
||||||
|
Track track = TestDataUtil.createTrack(trackId);
|
||||||
|
TestDataUtil.insertTrackWithLocations(contentProviderUtils, track, trackPointList);
|
||||||
|
|
||||||
|
// when
|
||||||
|
SensorStatistics sensorStatistics = contentProviderUtils.getSensorStats(trackId);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertFalse(sensorStatistics.hasHeartRate());
|
||||||
|
assertFalse(sensorStatistics.hasCadence());
|
||||||
|
assertFalse(sensorStatistics.hasPower());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetSensorStats_needAtLeastTwoTrackPointsFalse() {
|
||||||
|
// given
|
||||||
|
/*
|
||||||
|
* time elapsed hr cadence power track type
|
||||||
|
* 0 140 90 300 0
|
||||||
|
*/
|
||||||
|
Instant start = Instant.now();
|
||||||
|
TestSensorDataUtil sensorDataUtil = new TestSensorDataUtil();
|
||||||
|
sensorDataUtil.add(start, 140f, 90f, 300f, TrackPoint.Type.TRACKPOINT);
|
||||||
|
|
||||||
|
Track.Id trackId = new Track.Id(start.toEpochMilli());
|
||||||
|
Track track = TestDataUtil.createTrack(trackId);
|
||||||
|
TestDataUtil.insertTrackWithLocations(contentProviderUtils, track, sensorDataUtil.getTrackPointList());
|
||||||
|
|
||||||
|
// when
|
||||||
|
SensorStatistics sensorStatistics = contentProviderUtils.getSensorStats(trackId);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertFalse(sensorStatistics.hasHeartRate());
|
||||||
|
assertFalse(sensorStatistics.hasCadence());
|
||||||
|
assertFalse(sensorStatistics.hasPower());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetSensorStats_needAtLeastTwoTrackPointsTrue() {
|
||||||
|
// given
|
||||||
|
/*
|
||||||
|
* time elapsed hr cadence power track type
|
||||||
|
* 0 140 90 300 -1
|
||||||
|
* 1 140 90 300 1
|
||||||
|
*/
|
||||||
|
Instant start = Instant.now();
|
||||||
|
TestSensorDataUtil sensorDataUtil = new TestSensorDataUtil();
|
||||||
|
sensorDataUtil.add(start, 140f, 90f, 300f, TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||||
|
sensorDataUtil.add(start.plus(1, ChronoUnit.SECONDS), 140f, 90f, 300f, TrackPoint.Type.SEGMENT_END_MANUAL);
|
||||||
|
|
||||||
|
Track.Id trackId = new Track.Id(start.toEpochMilli());
|
||||||
|
Track track = TestDataUtil.createTrack(trackId);
|
||||||
|
TestDataUtil.insertTrackWithLocations(contentProviderUtils, track, sensorDataUtil.getTrackPointList());
|
||||||
|
|
||||||
|
// when
|
||||||
|
SensorStatistics sensorStatistics = contentProviderUtils.getSensorStats(trackId);
|
||||||
|
TestSensorDataUtil.SensorDataStats stats = sensorDataUtil.computeStats();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertTrue(sensorStatistics.hasHeartRate());
|
||||||
|
assertEquals(sensorStatistics.getAvgHeartRate(), stats.avgHr, 0f);
|
||||||
|
assertEquals(sensorStatistics.getMaxHeartRate(), stats.maxHr, 0f);
|
||||||
|
assertTrue(sensorStatistics.hasCadence());
|
||||||
|
assertEquals(sensorStatistics.getAvgCadence(), stats.avgCadence, 0f);
|
||||||
|
assertEquals(sensorStatistics.getMaxCadence(), stats.maxCadence, 0f);
|
||||||
|
assertTrue(sensorStatistics.hasPower());
|
||||||
|
assertEquals(sensorStatistics.getAvgPower(), stats.avgPower, 0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetSensorStats_onlyHr() {
|
||||||
|
// given
|
||||||
|
/*
|
||||||
|
* time elapsed hr cadence power track type
|
||||||
|
* 0 140 NULL NULL -1
|
||||||
|
* 1 140 NULL NULL 1
|
||||||
|
*/
|
||||||
|
Instant start = Instant.now();
|
||||||
|
TestSensorDataUtil sensorDataUtil = new TestSensorDataUtil();
|
||||||
|
sensorDataUtil.add(start, 140f, null, null, TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||||
|
sensorDataUtil.add(start.plus(1, ChronoUnit.SECONDS), 140f, null, null, TrackPoint.Type.SEGMENT_END_MANUAL);
|
||||||
|
|
||||||
|
Track.Id trackId = new Track.Id(start.toEpochMilli());
|
||||||
|
Track track = TestDataUtil.createTrack(trackId);
|
||||||
|
TestDataUtil.insertTrackWithLocations(contentProviderUtils, track, sensorDataUtil.getTrackPointList());
|
||||||
|
|
||||||
|
// when
|
||||||
|
SensorStatistics sensorStatistics = contentProviderUtils.getSensorStats(trackId);
|
||||||
|
TestSensorDataUtil.SensorDataStats stats = sensorDataUtil.computeStats();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertTrue(sensorStatistics.hasHeartRate());
|
||||||
|
assertEquals(sensorStatistics.getAvgHeartRate(), stats.avgHr, 0f);
|
||||||
|
assertEquals(sensorStatistics.getMaxHeartRate(), stats.maxHr, 0f);
|
||||||
|
assertFalse(sensorStatistics.hasCadence());
|
||||||
|
assertFalse(sensorStatistics.hasPower());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetSensorStats_onlyCadence() {
|
||||||
|
// given
|
||||||
|
/*
|
||||||
|
* time elapsed hr cadence power track type
|
||||||
|
* 0 NULL 90 NULL -1
|
||||||
|
* 1 NULL 90 NULL 1
|
||||||
|
*/
|
||||||
|
Instant start = Instant.now();
|
||||||
|
TestSensorDataUtil sensorDataUtil = new TestSensorDataUtil();
|
||||||
|
sensorDataUtil.add(start, null, 90f, null, TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||||
|
sensorDataUtil.add(start.plus(1, ChronoUnit.SECONDS), null, 90f, null, TrackPoint.Type.SEGMENT_END_MANUAL);
|
||||||
|
|
||||||
|
Track.Id trackId = new Track.Id(start.toEpochMilli());
|
||||||
|
Track track = TestDataUtil.createTrack(trackId);
|
||||||
|
TestDataUtil.insertTrackWithLocations(contentProviderUtils, track, sensorDataUtil.getTrackPointList());
|
||||||
|
|
||||||
|
// when
|
||||||
|
SensorStatistics sensorStatistics = contentProviderUtils.getSensorStats(trackId);
|
||||||
|
TestSensorDataUtil.SensorDataStats stats = sensorDataUtil.computeStats();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertFalse(sensorStatistics.hasHeartRate());
|
||||||
|
assertTrue(sensorStatistics.hasCadence());
|
||||||
|
assertEquals(sensorStatistics.getAvgCadence(), stats.avgCadence, 0f);
|
||||||
|
assertEquals(sensorStatistics.getMaxCadence(), stats.maxCadence, 0f);
|
||||||
|
assertFalse(sensorStatistics.hasPower());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetSensorStats_onlyPower() {
|
||||||
|
// given
|
||||||
|
/*
|
||||||
|
* time elapsed hr cadence power track type
|
||||||
|
* 0 NULL NULL 300 -1
|
||||||
|
* 1 NULL NULL 300 1
|
||||||
|
*/
|
||||||
|
Instant start = Instant.now();
|
||||||
|
TestSensorDataUtil sensorDataUtil = new TestSensorDataUtil();
|
||||||
|
sensorDataUtil.add(start, null, null, 300f, TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||||
|
sensorDataUtil.add(start.plus(1, ChronoUnit.SECONDS), null, null, 300f, TrackPoint.Type.SEGMENT_END_MANUAL);
|
||||||
|
|
||||||
|
Track.Id trackId = new Track.Id(start.toEpochMilli());
|
||||||
|
Track track = TestDataUtil.createTrack(trackId);
|
||||||
|
TestDataUtil.insertTrackWithLocations(contentProviderUtils, track, sensorDataUtil.getTrackPointList());
|
||||||
|
|
||||||
|
// when
|
||||||
|
SensorStatistics sensorStatistics = contentProviderUtils.getSensorStats(trackId);
|
||||||
|
TestSensorDataUtil.SensorDataStats stats = sensorDataUtil.computeStats();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertFalse(sensorStatistics.hasHeartRate());
|
||||||
|
assertFalse(sensorStatistics.hasCadence());
|
||||||
|
assertTrue(sensorStatistics.hasPower());
|
||||||
|
assertEquals(sensorStatistics.getAvgPower(), stats.avgPower, 0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetSensorStats() {
|
||||||
|
// given
|
||||||
|
/*
|
||||||
|
* time elapsed hr cadence power track type
|
||||||
|
* 0 140 75 250 -1
|
||||||
|
* 2 148 80 300 0
|
||||||
|
* 1 150 82 325 0
|
||||||
|
* 7 160 90 275 0
|
||||||
|
* 4 155 85 280 0
|
||||||
|
* 1 155 84 295 1
|
||||||
|
*/
|
||||||
|
Instant start = Instant.now();
|
||||||
|
TestSensorDataUtil sensorDataUtil = new TestSensorDataUtil();
|
||||||
|
sensorDataUtil.add(start, 140f, 75f, 250f, TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||||
|
sensorDataUtil.add(start.plus(2, ChronoUnit.SECONDS), 148f, 80f, 300f, TrackPoint.Type.TRACKPOINT);
|
||||||
|
sensorDataUtil.add(start.plus(3, ChronoUnit.SECONDS), 150f, 82f, 325f, TrackPoint.Type.TRACKPOINT);
|
||||||
|
sensorDataUtil.add(start.plus(10, ChronoUnit.SECONDS), 160f, 90f, 275f, TrackPoint.Type.TRACKPOINT);
|
||||||
|
sensorDataUtil.add(start.plus(14, ChronoUnit.SECONDS), 155f, 85f, 280f, TrackPoint.Type.TRACKPOINT);
|
||||||
|
sensorDataUtil.add(start.plus(15, ChronoUnit.SECONDS), 155f, 84f, 295f, TrackPoint.Type.SEGMENT_END_MANUAL);
|
||||||
|
|
||||||
|
Track.Id trackId = new Track.Id(start.toEpochMilli());
|
||||||
|
Track track = TestDataUtil.createTrack(trackId);
|
||||||
|
TestDataUtil.insertTrackWithLocations(contentProviderUtils, track, sensorDataUtil.getTrackPointList());
|
||||||
|
|
||||||
|
// when
|
||||||
|
SensorStatistics sensorStatistics = contentProviderUtils.getSensorStats(trackId);
|
||||||
|
TestSensorDataUtil.SensorDataStats stats = sensorDataUtil.computeStats();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals(sensorStatistics.getAvgHeartRate(), stats.avgHr, 0f);
|
||||||
|
assertEquals(sensorStatistics.getMaxHeartRate(), stats.maxHr, 0f);
|
||||||
|
assertEquals(sensorStatistics.getAvgCadence(), stats.avgCadence, 0f);
|
||||||
|
assertEquals(sensorStatistics.getMaxCadence(), stats.maxCadence, 0f);
|
||||||
|
assertEquals(sensorStatistics.getAvgPower(), stats.avgPower, 0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetSensorStats_withManualResume() {
|
||||||
|
// given
|
||||||
|
/*
|
||||||
|
* time elapsed hr cadence power track type
|
||||||
|
* 0 140 75 250 -1
|
||||||
|
* 2 148 80 300 0
|
||||||
|
* 1 150 82 325 0
|
||||||
|
* 3 174 88 400 0
|
||||||
|
* 20 127 54 175 -2
|
||||||
|
* 3 160 90 275 0
|
||||||
|
* 7 155 85 280 0
|
||||||
|
* 3 150 90 267 0
|
||||||
|
* 3 170 90 240 0
|
||||||
|
* 2 155 84 295 1
|
||||||
|
*/
|
||||||
|
Instant start = Instant.now();
|
||||||
|
TestSensorDataUtil sensorDataUtil = new TestSensorDataUtil();
|
||||||
|
sensorDataUtil.add(start, 140f, 75f, 250f, TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||||
|
sensorDataUtil.add(start.plus(2, ChronoUnit.SECONDS), 148f, 80f, 300f, TrackPoint.Type.TRACKPOINT);
|
||||||
|
sensorDataUtil.add(start.plus(3, ChronoUnit.SECONDS), 150f, 82f, 325f, TrackPoint.Type.TRACKPOINT);
|
||||||
|
sensorDataUtil.add(start.plus(6, ChronoUnit.SECONDS), 174f, 88f, 400f, TrackPoint.Type.TRACKPOINT);
|
||||||
|
sensorDataUtil.add(start.plus(26, ChronoUnit.SECONDS), 127f, 54f, 175f, TrackPoint.Type.SEGMENT_START_MANUAL);
|
||||||
|
sensorDataUtil.add(start.plus(29, ChronoUnit.SECONDS), 160f, 90f, 275f, TrackPoint.Type.TRACKPOINT);
|
||||||
|
sensorDataUtil.add(start.plus(36, ChronoUnit.SECONDS), 155f, 85f, 280f, TrackPoint.Type.TRACKPOINT);
|
||||||
|
sensorDataUtil.add(start.plus(39, ChronoUnit.SECONDS), 150f, 90f, 267f, TrackPoint.Type.TRACKPOINT);
|
||||||
|
sensorDataUtil.add(start.plus(42, ChronoUnit.SECONDS), 170f, 90f, 240f, TrackPoint.Type.TRACKPOINT);
|
||||||
|
sensorDataUtil.add(start.plus(44, ChronoUnit.SECONDS), 155f, 84f, 295f, TrackPoint.Type.SEGMENT_END_MANUAL);
|
||||||
|
|
||||||
|
Track.Id trackId = new Track.Id(start.toEpochMilli());
|
||||||
|
Track track = TestDataUtil.createTrack(trackId);
|
||||||
|
TestDataUtil.insertTrackWithLocations(contentProviderUtils, track, sensorDataUtil.getTrackPointList());
|
||||||
|
|
||||||
|
// when
|
||||||
|
SensorStatistics sensorStatistics = contentProviderUtils.getSensorStats(trackId);
|
||||||
|
TestSensorDataUtil.SensorDataStats stats = sensorDataUtil.computeStats();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals(sensorStatistics.getAvgHeartRate(), stats.avgHr, 0f);
|
||||||
|
assertEquals(sensorStatistics.getMaxHeartRate(), stats.maxHr, 0f);
|
||||||
|
assertEquals(sensorStatistics.getAvgCadence(), stats.avgCadence, 0f);
|
||||||
|
assertEquals(sensorStatistics.getMaxCadence(), stats.maxCadence, 0f);
|
||||||
|
assertEquals(sensorStatistics.getAvgPower(), stats.avgPower, 0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetSensorStats_withStartAutomatic() {
|
||||||
|
// given
|
||||||
|
/*
|
||||||
|
* time elapsed hr cadence power track type
|
||||||
|
* 0 140 75 250 -1
|
||||||
|
* 2 148 80 300 0
|
||||||
|
* 1 150 82 325 0
|
||||||
|
* 3 174 88 400 0
|
||||||
|
* 20 127 54 175 -1
|
||||||
|
* 3 160 90 275 0
|
||||||
|
* 7 155 85 280 0
|
||||||
|
* 3 150 90 267 0
|
||||||
|
* 3 170 90 240 0
|
||||||
|
* 2 155 84 295 1
|
||||||
|
*/
|
||||||
|
Instant start = Instant.now();
|
||||||
|
TestSensorDataUtil sensorDataUtil = new TestSensorDataUtil();
|
||||||
|
sensorDataUtil.add(start, 140f, 75f, 250f, TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||||
|
sensorDataUtil.add(start.plus(2, ChronoUnit.SECONDS), 148f, 80f, 300f, TrackPoint.Type.TRACKPOINT);
|
||||||
|
sensorDataUtil.add(start.plus(3, ChronoUnit.SECONDS), 150f, 82f, 325f, TrackPoint.Type.TRACKPOINT);
|
||||||
|
sensorDataUtil.add(start.plus(6, ChronoUnit.SECONDS), 174f, 88f, 400f, TrackPoint.Type.TRACKPOINT);
|
||||||
|
sensorDataUtil.add(start.plus(26, ChronoUnit.SECONDS), 127f, 54f, 175f, TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||||
|
sensorDataUtil.add(start.plus(29, ChronoUnit.SECONDS), 160f, 90f, 275f, TrackPoint.Type.TRACKPOINT);
|
||||||
|
sensorDataUtil.add(start.plus(36, ChronoUnit.SECONDS), 155f, 85f, 280f, TrackPoint.Type.TRACKPOINT);
|
||||||
|
sensorDataUtil.add(start.plus(39, ChronoUnit.SECONDS), 150f, 90f, 267f, TrackPoint.Type.TRACKPOINT);
|
||||||
|
sensorDataUtil.add(start.plus(42, ChronoUnit.SECONDS), 170f, 90f, 240f, TrackPoint.Type.TRACKPOINT);
|
||||||
|
sensorDataUtil.add(start.plus(44, ChronoUnit.SECONDS), 155f, 84f, 295f, TrackPoint.Type.SEGMENT_END_MANUAL);
|
||||||
|
|
||||||
|
Track.Id trackId = new Track.Id(start.toEpochMilli());
|
||||||
|
Track track = TestDataUtil.createTrack(trackId);
|
||||||
|
TestDataUtil.insertTrackWithLocations(contentProviderUtils, track, sensorDataUtil.getTrackPointList());
|
||||||
|
|
||||||
|
// when
|
||||||
|
SensorStatistics sensorStatistics = contentProviderUtils.getSensorStats(trackId);
|
||||||
|
TestSensorDataUtil.SensorDataStats stats = sensorDataUtil.computeStats();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals(sensorStatistics.getAvgHeartRate(), stats.avgHr, 0f);
|
||||||
|
assertEquals(sensorStatistics.getMaxHeartRate(), stats.maxHr, 0f);
|
||||||
|
assertEquals(sensorStatistics.getAvgCadence(), stats.avgCadence, 0f);
|
||||||
|
assertEquals(sensorStatistics.getMaxCadence(), stats.maxCadence, 0f);
|
||||||
|
assertEquals(sensorStatistics.getAvgPower(), stats.avgPower, 0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testGetSensorStats_randomData(int totalPoints, boolean withStartSegments) {
|
||||||
|
// given
|
||||||
|
Instant start = Instant.now();
|
||||||
|
TestSensorDataUtil sensorDataUtil = new TestSensorDataUtil();
|
||||||
|
Random random = new Random();
|
||||||
|
for (int i = 0; i < totalPoints; i++) {
|
||||||
|
int randomNum = withStartSegments ? random.nextInt(50) - 2 : 0;
|
||||||
|
TrackPoint.Type type = randomNum >= 0 ? TrackPoint.Type.TRACKPOINT : TrackPoint.Type.getById(randomNum);
|
||||||
|
float randomHr = random.nextFloat() * (200f - 90f) + 90f;
|
||||||
|
float randomCadence = random.nextFloat() * (110f - 40f) + 40f;
|
||||||
|
float randomPower = random.nextFloat() * (500f - 100f) + 100f;
|
||||||
|
sensorDataUtil.add(start.plus(i, ChronoUnit.SECONDS), randomHr, randomCadence, randomPower, type);
|
||||||
|
}
|
||||||
|
sensorDataUtil.add(start.plus(totalPoints, ChronoUnit.SECONDS), 155f, 84f, 295f, TrackPoint.Type.SEGMENT_END_MANUAL);
|
||||||
|
|
||||||
|
Track.Id trackId = new Track.Id(start.toEpochMilli());
|
||||||
|
Track track = TestDataUtil.createTrack(trackId);
|
||||||
|
TestDataUtil.insertTrackWithLocations(contentProviderUtils, track, sensorDataUtil.getTrackPointList());
|
||||||
|
|
||||||
|
// when
|
||||||
|
SensorStatistics sensorStatistics = contentProviderUtils.getSensorStats(trackId);
|
||||||
|
TestSensorDataUtil.SensorDataStats stats = sensorDataUtil.computeStats();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals(sensorStatistics.getAvgHeartRate(), stats.avgHr, 0.01f);
|
||||||
|
assertEquals(sensorStatistics.getMaxHeartRate(), stats.maxHr, 0.01f);
|
||||||
|
assertEquals(sensorStatistics.getAvgCadence(), stats.avgCadence, 0.01f);
|
||||||
|
assertEquals(sensorStatistics.getMaxCadence(), stats.maxCadence, 0.01f);
|
||||||
|
assertEquals(sensorStatistics.getAvgPower(), stats.avgPower, 0.01f);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetSensorStats_veryLongActivity12h() {
|
||||||
|
testGetSensorStats_randomData(43200 / 6, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetSensorStats_withSeveralRandomStartSegments() {
|
||||||
|
testGetSensorStats_randomData(5000, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public class SensorDataCyclingTest {
|
|||||||
current.compute(previous);
|
current.compute(previous);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(60, current.getCadence_rpm(), 0.01);
|
assertEquals(60, current.getValue(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -37,7 +37,7 @@ public class SensorDataCyclingTest {
|
|||||||
current.compute(previous);
|
current.compute(previous);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(33.53, current.getCadence_rpm(), 0.01);
|
assertEquals(33.53, current.getValue(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -50,7 +50,7 @@ public class SensorDataCyclingTest {
|
|||||||
current.compute(previous);
|
current.compute(previous);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(0, current.getCadence_rpm(), 0.01);
|
assertEquals(0, current.getValue(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ public class SensorDataCyclingTest {
|
|||||||
current.compute(previous);
|
current.compute(previous);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertFalse(current.hasCadence_rpm());
|
assertFalse(current.hasValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -77,7 +77,7 @@ public class SensorDataCyclingTest {
|
|||||||
current.compute(previous);
|
current.compute(previous);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(60, current.getCadence_rpm(), 0.01);
|
assertEquals(60, current.getValue(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -90,40 +90,42 @@ public class SensorDataCyclingTest {
|
|||||||
current.compute(previous);
|
current.compute(previous);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(60, current.getCadence_rpm(), 0.01);
|
assertEquals(60, current.getValue(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
@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.getSpeed_mps(), 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.getSpeed_mps(), 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();
|
||||||
|
|||||||
@@ -28,12 +28,18 @@ 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.ChartPoint;
|
||||||
import de.dennisguse.opentracks.chart.ChartView;
|
import de.dennisguse.opentracks.chart.ChartView;
|
||||||
|
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.util.UnitConversions;
|
import de.dennisguse.opentracks.util.UnitConversions;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link ChartFragment}.
|
* Tests {@link ChartFragment}.
|
||||||
@@ -61,9 +67,9 @@ public class ChartFragmentTest {
|
|||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
boolean chartByDistance = false;
|
boolean chartByDistance = false;
|
||||||
chartFragment = (ChartFragment) ChartFragment.newInstance(chartByDistance);
|
chartFragment = ChartFragment.newInstance(chartByDistance);
|
||||||
chartFragment.setChartView(new ChartView(ApplicationProvider.getApplicationContext(), chartByDistance));
|
chartFragment.setChartView(new ChartView(ApplicationProvider.getApplicationContext(), chartByDistance));
|
||||||
chartFragment.setTrackStatisticsUpdater(TrackStubUtils.INITIAL_TIME);
|
chartFragment.setRecordingDistanceInterval(Distance.of(50));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -78,9 +84,9 @@ public class ChartFragmentTest {
|
|||||||
ChartPoint point = chartFragment.createPendingPoint(trackPoint);
|
ChartPoint point = chartFragment.createPendingPoint(trackPoint);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(Float.NaN, point.getHeartRate(), 0.01);
|
assertNull(point.getHeartRate());
|
||||||
assertEquals(Float.NaN, point.getCadence(), 0.01);
|
assertNull(point.getCadence());
|
||||||
assertEquals(Float.NaN, point.getPower(), 0.01);
|
assertNull(point.getPower());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -191,30 +197,36 @@ public class ChartFragmentTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testCreatePendingPoint_time() {
|
public void testCreatePendingPoint_time() {
|
||||||
|
// given
|
||||||
chartFragment.setChartByDistance(false);
|
chartFragment.setChartByDistance(false);
|
||||||
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
|
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);
|
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
|
||||||
|
|
||||||
|
// then
|
||||||
assertEquals(0.0, point.getTimeOrDistance(), 0.01);
|
assertEquals(0.0, point.getTimeOrDistance(), 0.01);
|
||||||
long timeSpan = 222;
|
Duration timeSpan = Duration.ofMillis(222);
|
||||||
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
|
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
|
||||||
trackPoint2.setTime(trackPoint1.getTime() + timeSpan);
|
trackPoint2.setTime(Instant.ofEpochMilli(TrackStubUtils.INITIAL_TIME).plus(timeSpan));
|
||||||
point = chartFragment.createPendingPoint(trackPoint2);
|
point = chartFragment.createPendingPoint(trackPoint2);
|
||||||
assertEquals((double) timeSpan, point.getTimeOrDistance(), 0.01);
|
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.
|
* Tests the logic to get the value of altitude in {@link ChartFragment#createPendingPoint(TrackPoint)} by one and two points.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testCreatePendingPoint_elevation() {
|
public void testCreatePendingPoint_altitude() {
|
||||||
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
|
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* At first, clear old points of elevation, so give true to the second parameter.
|
* At first, clear old points of altitude, so give true to the second parameter.
|
||||||
* Then only one value INITIAL_ALTITUDE in buffer.
|
* Then only one value INITIAL_ALTITUDE in buffer.
|
||||||
*/
|
*/
|
||||||
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
|
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
|
||||||
assertEquals(TrackStubUtils.INITIAL_ALTITUDE, point.getElevation(), 0.01);
|
assertEquals(TrackStubUtils.INITIAL_ALTITUDE, point.getAltitude(), 0.01);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Send another value to buffer, now there are two values, INITIAL_ALTITUDE and INITIAL_ALTITUDE * 2.
|
* Send another value to buffer, now there are two values, INITIAL_ALTITUDE and INITIAL_ALTITUDE * 2.
|
||||||
@@ -222,7 +234,7 @@ public class ChartFragmentTest {
|
|||||||
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
|
TrackPoint trackPoint2 = TrackStubUtils.createDefaultTrackPoint();
|
||||||
trackPoint2.setAltitude(TrackStubUtils.INITIAL_ALTITUDE * 2);
|
trackPoint2.setAltitude(TrackStubUtils.INITIAL_ALTITUDE * 2);
|
||||||
point = chartFragment.createPendingPoint(trackPoint2);
|
point = chartFragment.createPendingPoint(trackPoint2);
|
||||||
assertEquals((TrackStubUtils.INITIAL_ALTITUDE + TrackStubUtils.INITIAL_ALTITUDE * 2) / 2.0, point.getElevation(), 0.01);
|
assertEquals((TrackStubUtils.INITIAL_ALTITUDE + TrackStubUtils.INITIAL_ALTITUDE * 2) / 2.0, point.getAltitude(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -237,7 +249,7 @@ public class ChartFragmentTest {
|
|||||||
* It will not be filled in to the speed buffer.
|
* It will not be filled in to the speed buffer.
|
||||||
*/
|
*/
|
||||||
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
|
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
|
||||||
trackPoint1.setSpeed(128.5f);
|
trackPoint1.setSpeed(Speed.of(128.5f));
|
||||||
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
|
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
|
||||||
assertEquals(0.0, point.getSpeed(), 0.01);
|
assertEquals(0.0, point.getSpeed(), 0.01);
|
||||||
|
|
||||||
@@ -250,8 +262,8 @@ public class ChartFragmentTest {
|
|||||||
/*
|
/*
|
||||||
* Add a time span here to make sure the second point is valid, the value 222 here is doesn't matter.
|
* 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() + 222);
|
trackPoint2.setTime(trackPoint1.getTime().plusMillis(222));
|
||||||
trackPoint2.setSpeed(130);
|
trackPoint2.setSpeed(Speed.of(130f));
|
||||||
point = chartFragment.createPendingPoint(trackPoint2);
|
point = chartFragment.createPendingPoint(trackPoint2);
|
||||||
assertEquals(130.0 * UnitConversions.MPS_TO_KMH, point.getSpeed(), 0.01);
|
assertEquals(130.0 * UnitConversions.MPS_TO_KMH, point.getSpeed(), 0.01);
|
||||||
}
|
}
|
||||||
@@ -265,7 +277,7 @@ public class ChartFragmentTest {
|
|||||||
|
|
||||||
// First data point is not added to the speed buffer
|
// First data point is not added to the speed buffer
|
||||||
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
|
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
|
||||||
trackPoint1.setSpeed(100.0f);
|
trackPoint1.setSpeed(Speed.of(100.0f));
|
||||||
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
|
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
|
||||||
assertEquals(0.0, point.getSpeed(), 0.01);
|
assertEquals(0.0, point.getSpeed(), 0.01);
|
||||||
|
|
||||||
@@ -275,8 +287,8 @@ public class ChartFragmentTest {
|
|||||||
* Add a time span here to make sure the second point and the speed is valid.
|
* 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
|
* Speed is valid if: speedDifference > Constants.MAX_ACCELERATION * timeDifference speedDifference = 102 -100 timeDifference = 222
|
||||||
*/
|
*/
|
||||||
trackPoint2.setTime(trackPoint2.getTime() + 222);
|
trackPoint2.setTime(trackPoint2.getTime().plusMillis(222));
|
||||||
trackPoint2.setSpeed(102);
|
trackPoint2.setSpeed(Speed.of(102f));
|
||||||
point = chartFragment.createPendingPoint(trackPoint2);
|
point = chartFragment.createPendingPoint(trackPoint2);
|
||||||
assertEquals(102.0 * UnitConversions.MPS_TO_KMH * UnitConversions.KM_TO_MI, point.getSpeed(), 0.01);
|
assertEquals(102.0 * UnitConversions.MPS_TO_KMH * UnitConversions.KM_TO_MI, point.getSpeed(), 0.01);
|
||||||
}
|
}
|
||||||
@@ -290,7 +302,7 @@ public class ChartFragmentTest {
|
|||||||
|
|
||||||
// First data point is not added to the speed buffer
|
// First data point is not added to the speed buffer
|
||||||
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
|
TrackPoint trackPoint1 = TrackStubUtils.createDefaultTrackPoint();
|
||||||
trackPoint1.setSpeed(100.0f);
|
trackPoint1.setSpeed(Speed.of(100.0f));
|
||||||
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
|
ChartPoint point = chartFragment.createPendingPoint(trackPoint1);
|
||||||
assertEquals(0.0, point.getSpeed(), 0.01);
|
assertEquals(0.0, point.getSpeed(), 0.01);
|
||||||
|
|
||||||
@@ -300,8 +312,8 @@ public class ChartFragmentTest {
|
|||||||
* Add a time span here to make sure the second point and the speed is valid.
|
* 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
|
* Speed is valid if: speedDifference > Constants.MAX_ACCELERATION * timeDifference speedDifference = 102 -100 timeDifference = 222
|
||||||
*/
|
*/
|
||||||
trackPoint2.setTime(trackPoint2.getTime() + 222);
|
trackPoint2.setTime(trackPoint2.getTime().plusMillis(222));
|
||||||
trackPoint2.setSpeed(102);
|
trackPoint2.setSpeed(Speed.of(102f));
|
||||||
point = chartFragment.createPendingPoint(trackPoint2);
|
point = chartFragment.createPendingPoint(trackPoint2);
|
||||||
assertEquals(HOURS_PER_UNIT / (102.0 * UnitConversions.MPS_TO_KMH), point.getPace(), 0.01);
|
assertEquals(HOURS_PER_UNIT / (102.0 * UnitConversions.MPS_TO_KMH), point.getPace(), 0.01);
|
||||||
}
|
}
|
||||||
@@ -313,7 +325,7 @@ public class ChartFragmentTest {
|
|||||||
public void testCreatePendingPoint_pace_zeroSpeed() {
|
public void testCreatePendingPoint_pace_zeroSpeed() {
|
||||||
chartFragment.setReportSpeed(false);
|
chartFragment.setReportSpeed(false);
|
||||||
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint();
|
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint();
|
||||||
trackPoint.setSpeed(0);
|
trackPoint.setSpeed(Speed.of(0f));
|
||||||
ChartPoint point = chartFragment.createPendingPoint(trackPoint);
|
ChartPoint point = chartFragment.createPendingPoint(trackPoint);
|
||||||
assertEquals(0.0, point.getPace(), 0.01);
|
assertEquals(0.0, point.getPace(), 0.01);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public class TrackStubUtils {
|
|||||||
location.setAltitude(altitude);
|
location.setAltitude(altitude);
|
||||||
location.setAccuracy(INITIAL_ACCURACY);
|
location.setAccuracy(INITIAL_ACCURACY);
|
||||||
location.setSpeed(INITIAL_SPEED);
|
location.setSpeed(INITIAL_SPEED);
|
||||||
location.setTime(INITIAL_TIME);
|
location.setTime(INITIAL_TIME); //TODO This is nowadays ignored as the constructor will replace the time.
|
||||||
location.setBearing(INITIAL_BEARING);
|
location.setBearing(INITIAL_BEARING);
|
||||||
|
|
||||||
return new TrackPoint(location);
|
return new TrackPoint(location);
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package de.dennisguse.opentracks.io.file.exporter;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
|
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
@RunWith(JUnit4.class)
|
||||||
|
public class KmlTrackExporterTest {
|
||||||
|
|
||||||
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sensor data by type should only be created if present in at least on TrackPoint.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void writeCloseSegment_only_write_sensordata_if_present() {
|
||||||
|
String expected = "<when>1970-01-01T00:00:00Z</when>\n" +
|
||||||
|
"<gx:coord/>\n" +
|
||||||
|
"<ExtendedData>\n" +
|
||||||
|
"<SchemaData schemaUrl=\"#schema\">\n" +
|
||||||
|
"</SchemaData>\n" +
|
||||||
|
"</ExtendedData>\n" +
|
||||||
|
"</gx:Track>\n";
|
||||||
|
|
||||||
|
// given
|
||||||
|
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0));
|
||||||
|
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
KMLTrackExporter kmlTrackWriter = (KMLTrackExporter) TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context);
|
||||||
|
kmlTrackWriter.prepare(outputStream);
|
||||||
|
|
||||||
|
kmlTrackWriter.writeTrackPoint(trackPoint);
|
||||||
|
|
||||||
|
// when
|
||||||
|
kmlTrackWriter.writeCloseSegment();
|
||||||
|
kmlTrackWriter.close();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals(expected, outputStream.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,20 @@
|
|||||||
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.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Looper;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Pair;
|
|
||||||
|
|
||||||
|
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.ServiceTestRule;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Ignore;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.JUnit4;
|
import org.junit.runners.JUnit4;
|
||||||
@@ -17,35 +22,56 @@ 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.TimeoutException;
|
||||||
|
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.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.stats.TrackStatistics;
|
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
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.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export a track to {@link TrackFileFormat} and verify that the import is identical.
|
* Export a track to {@link TrackFileFormat} and verify that the import is identical.
|
||||||
* <p>
|
* <p>
|
||||||
* TODO: test ignores {@link TrackStatistics} for now.
|
|
||||||
*/
|
*/
|
||||||
@RunWith(JUnit4.class)
|
@RunWith(JUnit4.class)
|
||||||
public class ExportImportTest {
|
public class ExportImportTest {
|
||||||
|
|
||||||
private static final String TAG = ExportImportTest.class.getSimpleName();
|
private static final String TAG = ExportImportTest.class.getSimpleName();
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void preSetUp() {
|
||||||
|
// Prepare looper for Android's message queue
|
||||||
|
if (Looper.myLooper() == null) Looper.prepare();
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
@@ -53,37 +79,53 @@ public class ExportImportTest {
|
|||||||
private static final String TRACK_CATEGORY = "the category";
|
private static final String TRACK_CATEGORY = "the category";
|
||||||
private static final String TRACK_DESCRIPTION = "the description";
|
private static final String TRACK_DESCRIPTION = "the description";
|
||||||
|
|
||||||
private final List<Marker> markers = new ArrayList<>();
|
private Track track;
|
||||||
private final List<TrackPoint> trackPoints = new ArrayList<>();
|
private List<Marker> markers = new ArrayList<>();
|
||||||
|
private List<TrackPoint> trackPoints = new ArrayList<>();
|
||||||
|
|
||||||
|
private Track.Id trackId;
|
||||||
private Track.Id importTrackId;
|
private Track.Id importTrackId;
|
||||||
private final Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
|
||||||
|
|
||||||
@Before
|
public void setUp(boolean hasSensorDistance) throws TimeoutException {
|
||||||
public void setUp() {
|
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(new Intent(context, TrackRecordingService.class)))
|
||||||
Pair<Track, List<TrackPoint>> track = TestDataUtil.createTrack(trackId, 10);
|
.getService();
|
||||||
track.first.setIcon(TRACK_ICON);
|
|
||||||
track.first.setCategory(TRACK_CATEGORY);
|
|
||||||
track.first.setDescription(TRACK_DESCRIPTION);
|
|
||||||
contentProviderUtils.insertTrack(track.first);
|
|
||||||
contentProviderUtils.bulkInsertTrackPoint(track.second, track.first.getId());
|
|
||||||
|
|
||||||
trackPoints.clear();
|
|
||||||
trackPoints.addAll(track.second);
|
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
trackId = service.startNewTrack();
|
||||||
Marker marker = new Marker(trackId, track.second.get(i).getLocation());
|
//TODO Workaround as those managers overwrite input data; We need to refactor TrackRecordingService to make it actually testable
|
||||||
marker.setName("the marker " + i);
|
service.setAltitudeSumManager(null);
|
||||||
marker.setDescription("the marker description " + i);
|
service.setRemoteSensorManager(null);
|
||||||
marker.setCategory("the marker category" + i);
|
|
||||||
marker.setIcon("the marker icon" + i);
|
|
||||||
marker.setPhotoUrl("the photo url" + i);
|
|
||||||
contentProviderUtils.insertMarker(marker);
|
|
||||||
|
|
||||||
markers.add(marker);
|
Distance sensorDistance = hasSensorDistance ? Distance.of(5) : null;
|
||||||
}
|
|
||||||
|
|
||||||
assertEquals(markers.size(), contentProviderUtils.getMarkerCount(trackId));
|
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.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, sensorDistance), 0);
|
||||||
|
service.insertMarker("Marker 2", "Marker 2 category", "Marker 2 desc", null);
|
||||||
|
service.pauseCurrentTrack();
|
||||||
|
|
||||||
|
service.resumeCurrentTrack();
|
||||||
|
//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.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();
|
||||||
|
|
||||||
|
track = contentProviderUtils.getTrack(trackId);
|
||||||
|
track.setIcon(TRACK_ICON);
|
||||||
|
track.setCategory(TRACK_CATEGORY);
|
||||||
|
track.setDescription(TRACK_DESCRIPTION);
|
||||||
|
contentProviderUtils.updateTrack(track);
|
||||||
|
|
||||||
|
track = contentProviderUtils.getTrack(trackId);
|
||||||
|
trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||||
|
markers = contentProviderUtils.getMarkers(trackId);
|
||||||
|
assertEquals(10, trackPoints.size());
|
||||||
|
assertEquals(2, markers.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@@ -96,18 +138,13 @@ public class ExportImportTest {
|
|||||||
|
|
||||||
@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);
|
||||||
|
|
||||||
TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL.newTrackExporter(context);
|
TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL.createTrackExporter(context);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
// 1. export
|
// 1. export
|
||||||
@@ -118,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
|
||||||
@@ -130,20 +167,25 @@ public class ExportImportTest {
|
|||||||
assertEquals(track.getIcon(), importedTrack.getIcon());
|
assertEquals(track.getIcon(), importedTrack.getIcon());
|
||||||
assertEquals(track.getUuid(), importedTrack.getUuid());
|
assertEquals(track.getUuid(), importedTrack.getUuid());
|
||||||
|
|
||||||
// 2. markers
|
// 2. trackpoints
|
||||||
assertMarkers();
|
assertTrackpoints(trackPoints, false, false, false, false, false, false);
|
||||||
|
|
||||||
// 3. trackpoints
|
// 3. trackstatistics
|
||||||
assertTrackpoints(false, false, false, false, false);
|
assertTrackStatistics(false, false, true);
|
||||||
|
|
||||||
|
// 4. markers
|
||||||
|
assertMarkers();
|
||||||
}
|
}
|
||||||
|
|
||||||
@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);
|
||||||
|
|
||||||
TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.newTrackExporter(context);
|
TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
// 1. export
|
// 1. export
|
||||||
@@ -154,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
|
||||||
@@ -165,21 +207,28 @@ public class ExportImportTest {
|
|||||||
assertEquals(track.getName(), importedTrack.getName());
|
assertEquals(track.getName(), importedTrack.getName());
|
||||||
assertEquals(track.getIcon(), importedTrack.getIcon());
|
assertEquals(track.getIcon(), importedTrack.getIcon());
|
||||||
|
|
||||||
// 2. markers
|
// 2. trackpoints
|
||||||
assertMarkers();
|
assertTrackpoints(trackPoints, true, true, true, true, true, true);
|
||||||
|
|
||||||
// 3. trackpoints
|
// 2. trackstatistics
|
||||||
assertTrackpoints(true, true, true, true, true);
|
assertTrackStatistics(false, true, true);
|
||||||
|
|
||||||
|
// 4. markers
|
||||||
|
assertMarkers();
|
||||||
}
|
}
|
||||||
|
|
||||||
@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.newTrackExporter(context);
|
TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
// 1. export
|
// 1. export
|
||||||
@@ -189,52 +238,22 @@ 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);
|
||||||
|
|
||||||
TrackExporter trackExporter = TrackFileFormat.GPX.newTrackExporter(context);
|
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
// 1. export
|
// 1. export
|
||||||
@@ -245,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
|
||||||
@@ -258,21 +277,34 @@ public class ExportImportTest {
|
|||||||
//TODO exporting and importing a track icon is not yet supported by GpxTrackWriter.
|
//TODO exporting and importing a track icon is not yet supported by GpxTrackWriter.
|
||||||
//assertEquals(track.getIcon(), trackImported.getIcon());
|
//assertEquals(track.getIcon(), trackImported.getIcon());
|
||||||
|
|
||||||
// 2. markers
|
// 2. trackpoints
|
||||||
assertMarkers();
|
// The GPX exporter does not support exporting TrackPoints without lat/lng.
|
||||||
|
// Therefore, the track segmentation is changes.
|
||||||
|
List<TrackPoint> trackPointsWithCoordinates = trackPoints.stream().filter(it -> TrackPoint.Type.SEGMENT_START_AUTOMATIC.equals(it.getType()) || TrackPoint.Type.TRACKPOINT.equals(it.getType())).collect(Collectors.toList());
|
||||||
|
trackPointsWithCoordinates.get(0).setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||||
|
trackPointsWithCoordinates.get(3).setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||||
|
|
||||||
// 3. trackpoints
|
assertTrackpoints(trackPointsWithCoordinates, true, true, true, true, true, false);
|
||||||
assertTrackpoints(false, true, true, true, true);
|
|
||||||
|
// 3. trackstatistics
|
||||||
|
assertTrackStatistics(true, true, false);
|
||||||
|
|
||||||
|
// 4. markers
|
||||||
|
assertMarkers();
|
||||||
}
|
}
|
||||||
|
|
||||||
@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.newTrackExporter(context);
|
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
// 1. export
|
// 1. export
|
||||||
@@ -282,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
|
||||||
@@ -309,38 +341,112 @@ public class ExportImportTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertTrackpoints(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++) {
|
||||||
TrackPoint trackPoint = trackPoints.get(i);
|
TrackPoint trackPoint = trackPoints.get(i);
|
||||||
TrackPoint importedTrackPoint = importedTrackPoints.get(i);
|
TrackPoint importedTrackPoint = importedTrackPoints.get(i);
|
||||||
|
|
||||||
assertEquals(trackPoint.getTime(), importedTrackPoint.getTime(), 0.01);
|
assertEquals(trackPoint.getTime(), importedTrackPoint.getTime());
|
||||||
|
TrackPoint.Type type = trackPoint.getType();
|
||||||
|
Log.e(TAG, "" + importedTrackPoint.getType().equals(type));
|
||||||
|
assertEquals("" + i, type, importedTrackPoint.getType());
|
||||||
|
|
||||||
// TODO Not exported for GPX/KML
|
// TODO Not exported for GPX/KML
|
||||||
// assertEquals(trackPoint.getAccuracy(), importedTrackPoint.getAccuracy(), 0.01);
|
// assertEquals(trackPoint.getAccuracy(), importedTrackPoint.getAccuracy(), 0.01);
|
||||||
|
|
||||||
assertEquals(trackPoint.getLatitude(), importedTrackPoint.getLatitude(), 0.001);
|
assertEquals(trackPoint.hasLocation(), importedTrackPoint.hasLocation());
|
||||||
assertEquals(trackPoint.getLongitude(), importedTrackPoint.getLongitude(), 0.001);
|
if (trackPoint.hasLocation()) {
|
||||||
assertEquals(trackPoint.getAltitude(), importedTrackPoint.getAltitude(), 0.001);
|
assertEquals(trackPoint.getLatitude(), importedTrackPoint.getLatitude(), 0.001);
|
||||||
assertEquals(trackPoint.getSpeed(), importedTrackPoint.getSpeed(), 0.001);
|
assertEquals(trackPoint.getLongitude(), importedTrackPoint.getLongitude(), 0.001);
|
||||||
|
}
|
||||||
|
assertEquals(trackPoint.hasSpeed(), importedTrackPoint.hasSpeed());
|
||||||
|
if (trackPoint.hasSpeed()) {
|
||||||
|
assertEquals(trackPoint.getSpeed().toMPS(), importedTrackPoint.getSpeed().toMPS(), 0.001);
|
||||||
|
}
|
||||||
|
assertEquals(trackPoint.hasAltitude(), importedTrackPoint.hasAltitude());
|
||||||
|
if (trackPoint.hasAltitude()) {
|
||||||
|
assertEquals(trackPoint.getAltitude(), importedTrackPoint.getAltitude(), 0.001);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.equals(TrackPoint.Type.SEGMENT_START_MANUAL) || type.equals(TrackPoint.Type.SEGMENT_END_MANUAL)) {
|
||||||
|
//TODO REMOVE
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
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 verifyAltitudeGainAndLoss, boolean verifyDistance) {
|
||||||
|
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||||
|
|
||||||
|
assertNotNull(importedTrack.getTrackStatistics());
|
||||||
|
|
||||||
|
TrackStatistics trackStatistics = track.getTrackStatistics();
|
||||||
|
TrackStatistics importedTrackStatistics = importedTrack.getTrackStatistics();
|
||||||
|
|
||||||
|
// Time
|
||||||
|
assertTrue(trackStatistics.getStartTime().isBefore(trackStatistics.getStopTime())); //Just to be sure.
|
||||||
|
if (!isGpx) {
|
||||||
|
assertEquals(trackStatistics.getStartTime(), importedTrackStatistics.getStartTime());
|
||||||
|
assertEquals(trackStatistics.getStopTime(), importedTrackStatistics.getStopTime());
|
||||||
|
|
||||||
|
assertEquals(trackStatistics.getTotalTime(), importedTrackStatistics.getTotalTime());
|
||||||
|
assertEquals(trackStatistics.getMovingTime(), importedTrackStatistics.getMovingTime());
|
||||||
|
|
||||||
|
// Distance
|
||||||
|
if (verifyDistance) {
|
||||||
|
assertEquals(trackStatistics.getTotalDistance(), importedTrackStatistics.getTotalDistance());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Speed
|
||||||
|
assertEquals(trackStatistics.getMaxSpeed(), importedTrackStatistics.getMaxSpeed());
|
||||||
|
assertEquals(trackStatistics.getAverageSpeed(), importedTrackStatistics.getAverageSpeed());
|
||||||
|
assertEquals(trackStatistics.getAverageMovingSpeed(), importedTrackStatistics.getAverageMovingSpeed());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Altitude
|
||||||
|
assertEquals(trackStatistics.getMinAltitude(), importedTrackStatistics.getMinAltitude(), 0.01);
|
||||||
|
assertEquals(trackStatistics.getMaxAltitude(), importedTrackStatistics.getMaxAltitude(), 0.01);
|
||||||
|
if (verifyAltitudeGainAndLoss) {
|
||||||
|
assertEquals(trackStatistics.getTotalAltitudeGain(), importedTrackStatistics.getTotalAltitudeGain(), 0.01);
|
||||||
|
assertEquals(trackStatistics.getTotalAltitudeLoss(), importedTrackStatistics.getTotalAltitudeLoss(), 0.01);
|
||||||
|
} else {
|
||||||
|
assertFalse(importedTrackStatistics.hasTotalAltitudeGain());
|
||||||
|
assertFalse(importedTrackStatistics.hasTotalAltitudeLoss());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
TrackPoint tp = new TrackPoint(latitude, longitude, (double) altitude, Instant.ofEpochMilli(time));
|
||||||
|
tp.setAccuracy(accuracy);
|
||||||
|
tp.setSpeed(Speed.of(speed));
|
||||||
|
tp.setHeartRate_bpm(heartRate);
|
||||||
|
tp.setCyclingCadence_rpm(cyclingCadence);
|
||||||
|
tp.setPower(power);
|
||||||
|
tp.setAltitudeGain(altitudeGain);
|
||||||
|
tp.setAltitudeLoss(altitudeGain); //TODO
|
||||||
|
tp.setSensorDistance(distance);
|
||||||
|
return tp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
package de.dennisguse.opentracks.io.file.importer;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
import androidx.test.filters.LargeTest;
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||||
|
import de.dennisguse.opentracks.content.data.Track;
|
||||||
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
|
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||||
|
import de.dennisguse.opentracks.util.StringUtils;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that legacy KML/GPX formats can still be imported.
|
||||||
|
*/
|
||||||
|
@RunWith(JUnit4.class)
|
||||||
|
public class LegacyImportTest {
|
||||||
|
|
||||||
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
private final ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
|
||||||
|
|
||||||
|
private Track.Id importTrackId;
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
if (importTrackId != null) {
|
||||||
|
contentProviderUtils.deleteTrack(context, importTrackId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that data with statistics markers is imported and those ignored.
|
||||||
|
* Statistics marker were created to avoid recomputing the track statistics (e.g., distance until a certain time).
|
||||||
|
*/
|
||||||
|
@LargeTest
|
||||||
|
@Test
|
||||||
|
public void kml_with_statistics_marker() {
|
||||||
|
// given
|
||||||
|
KmlFileTrackImporter trackImporter = new KmlFileTrackImporter(context);
|
||||||
|
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.legacy_kml_statistics_marker);
|
||||||
|
|
||||||
|
// when
|
||||||
|
// 1. import
|
||||||
|
importTrackId = trackImporter.importFile(inputStream).get(0);
|
||||||
|
|
||||||
|
// then
|
||||||
|
// 1. track
|
||||||
|
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||||
|
assertNotNull(importedTrack);
|
||||||
|
assertEquals("unknown", importedTrack.getCategory());
|
||||||
|
assertEquals("Test Track", importedTrack.getDescription());
|
||||||
|
assertEquals("2020-11-28 18:06", importedTrack.getName());
|
||||||
|
assertEquals("UNKNOWN", importedTrack.getIcon());
|
||||||
|
|
||||||
|
// 2. markers
|
||||||
|
assertEquals(0, contentProviderUtils.getMarkerCount(importTrackId));
|
||||||
|
|
||||||
|
// 3. trackpoints
|
||||||
|
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
||||||
|
assertEquals(6, importedTrackPoints.size());
|
||||||
|
|
||||||
|
// first 3 trackpoints
|
||||||
|
assertTrackpoint(importedTrackPoints.get(0), TrackPoint.Type.SEGMENT_START_AUTOMATIC, "2020-11-28T17:06:22.401Z", 1.234156, 12.340097, 469.286376953125);
|
||||||
|
assertTrackpoint(importedTrackPoints.get(1), TrackPoint.Type.TRACKPOINT, "2020-11-28T17:06:25.448Z", 1.23415, 12.340036, 439.1626281738281);
|
||||||
|
assertTrackpoint(importedTrackPoints.get(2), TrackPoint.Type.TRACKPOINT, "2020-11-28T17:06:47.888Z", 1.23405, 12.340057, 421.8070983886719);
|
||||||
|
|
||||||
|
// created resume trackpoint with time of next valid trackpoint
|
||||||
|
assertTrackpoint(importedTrackPoints.get(3), TrackPoint.Type.SEGMENT_START_AUTOMATIC, "2020-11-28T17:06:55.861Z", 1.23405, 12.340057, 419.93902587890625);
|
||||||
|
assertTrackpoint(importedTrackPoints.get(4), TrackPoint.Type.TRACKPOINT, "2020-11-28T17:06:56.905Z", 1.23405, 12.340057, 419.9036560058594);
|
||||||
|
assertTrackpoint(importedTrackPoints.get(5), TrackPoint.Type.TRACKPOINT, "2020-11-28T17:07:20.870Z", 1.234046, 12.340082, 417.99432373046875);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* At least one valid location is required.
|
||||||
|
* Before v3.15.0, Tracks without TrackPoints could be created; such tracks cannot be imported as we cannot restore the TrackStatistics (especially startTime and stopTime).
|
||||||
|
*/
|
||||||
|
@LargeTest
|
||||||
|
@Test(expected = ImportParserException.class)
|
||||||
|
public void kml_without_locations() {
|
||||||
|
// given
|
||||||
|
KmlFileTrackImporter trackImporter = new KmlFileTrackImporter(context);
|
||||||
|
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.legacy_kml_empty);
|
||||||
|
|
||||||
|
// when
|
||||||
|
importTrackId = trackImporter.importFile(inputStream).get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that data that contains pause (lat=100, lng=0) and resume (lat=200, lng=0) locations are restored to a segment break.
|
||||||
|
*/
|
||||||
|
@LargeTest
|
||||||
|
@Test
|
||||||
|
public void gpx_with_pause_resume() {
|
||||||
|
// given
|
||||||
|
GpxFileTrackImporter trackImporter = new GpxFileTrackImporter(context);
|
||||||
|
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.legacy_gpx_pause_resume);
|
||||||
|
|
||||||
|
// when
|
||||||
|
// 1. import
|
||||||
|
importTrackId = trackImporter.importFile(inputStream).get(0);
|
||||||
|
|
||||||
|
// then
|
||||||
|
// 1. track
|
||||||
|
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||||
|
assertNotNull(importedTrack);
|
||||||
|
assertEquals("the category", importedTrack.getCategory());
|
||||||
|
assertEquals("the description", importedTrack.getDescription());
|
||||||
|
assertEquals("2021-01-07 22:51", importedTrack.getName());
|
||||||
|
assertEquals("UNKNOWN", importedTrack.getIcon());
|
||||||
|
|
||||||
|
// 3. trackpoints
|
||||||
|
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
||||||
|
assertEquals(6, importedTrackPoints.size());
|
||||||
|
|
||||||
|
// first segment
|
||||||
|
assertTrackpoint(importedTrackPoints.get(0), TrackPoint.Type.SEGMENT_START_AUTOMATIC, "2021-01-07T21:51:59.179Z", 14.0, 3.0, 10.0);
|
||||||
|
assertTrackpoint(importedTrackPoints.get(1), TrackPoint.Type.TRACKPOINT, "2021-01-07T21:52:00.653Z", 14.001, 3.0, 10.0);
|
||||||
|
assertTrackpoint(importedTrackPoints.get(2), TrackPoint.Type.TRACKPOINT, "2021-01-07T21:52:01.010Z", 14.002, 3.0, 10.0);
|
||||||
|
assertTrackpoint(importedTrackPoints.get(3), TrackPoint.Type.SEGMENT_END_MANUAL, "2021-01-07T21:52:02.658Z", null, null, null);
|
||||||
|
|
||||||
|
// created resume trackpoint with time of next valid trackpoint
|
||||||
|
assertTrackpoint(importedTrackPoints.get(4), TrackPoint.Type.SEGMENT_START_MANUAL, "2021-01-07T21:52:03.873Z", null, null, null);
|
||||||
|
assertTrackpoint(importedTrackPoints.get(5), TrackPoint.Type.TRACKPOINT, "2021-01-07T21:52:04.103Z", 14.003, 3.0, 10.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertTrackpoint(final TrackPoint trackPoint, final TrackPoint.Type type, final String when, final Double longitude, final Double latitude, final Double altitude) {
|
||||||
|
assertEquals(StringUtils.parseTime(when), trackPoint.getTime());
|
||||||
|
assertEquals(type, trackPoint.getType());
|
||||||
|
|
||||||
|
if (longitude == null) {
|
||||||
|
assertFalse(trackPoint.hasLocation());
|
||||||
|
} else {
|
||||||
|
assertEquals(latitude, (Double) trackPoint.getLatitude());
|
||||||
|
assertEquals(longitude, (Double) trackPoint.getLongitude());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (altitude == null) {
|
||||||
|
assertFalse(trackPoint.hasAltitude());
|
||||||
|
} else {
|
||||||
|
assertEquals(altitude, (Double) trackPoint.getAltitude());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
package de.dennisguse.opentracks.io.file.importer;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
|
||||||
import androidx.test.filters.LargeTest;
|
|
||||||
import androidx.test.platform.app.InstrumentationRegistry;
|
|
||||||
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.JUnit4;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.data.Track;
|
|
||||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
|
||||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
|
||||||
import de.dennisguse.opentracks.util.StringUtils;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Imports a legacy kml file with a statistics marker at the beginning
|
|
||||||
*/
|
|
||||||
@RunWith(JUnit4.class)
|
|
||||||
public class LegacyKmlImportTest {
|
|
||||||
|
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
|
||||||
private final ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
|
|
||||||
|
|
||||||
private Track.Id importTrackId;
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void tearDown() {
|
|
||||||
if (importTrackId != null) {
|
|
||||||
contentProviderUtils.deleteTrack(context, importTrackId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@LargeTest
|
|
||||||
@Test
|
|
||||||
public void kml_legacy_track_import() {
|
|
||||||
// given
|
|
||||||
KmlFileTrackImporter trackImporter = new KmlFileTrackImporter(context);
|
|
||||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.legacy_track);
|
|
||||||
|
|
||||||
// when
|
|
||||||
// 1. import
|
|
||||||
importTrackId = trackImporter.importFile(inputStream);
|
|
||||||
|
|
||||||
// then
|
|
||||||
// 1. track
|
|
||||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
|
||||||
assertNotNull(importedTrack);
|
|
||||||
assertEquals("unknown", importedTrack.getCategory());
|
|
||||||
assertEquals("Test Track", importedTrack.getDescription());
|
|
||||||
assertEquals("2020-11-28 18:06", importedTrack.getName());
|
|
||||||
assertEquals("UNKNOWN", importedTrack.getIcon());
|
|
||||||
|
|
||||||
// 2. markers
|
|
||||||
assertEquals(0, contentProviderUtils.getMarkerCount(importTrackId));
|
|
||||||
|
|
||||||
// 3. trackpoints
|
|
||||||
List<TrackPoint> importedTrackPoints = contentProviderUtils.getTrackPoints(importTrackId);
|
|
||||||
assertEquals(8, importedTrackPoints.size()); // 6 trackpoints + pause and resume
|
|
||||||
|
|
||||||
// first 3 trackpoints
|
|
||||||
assertTrackpoint(importedTrackPoints.get(0), "2020-11-28T17:06:22.401Z", 1.234156, 12.340097, 469.286376953125);
|
|
||||||
assertTrackpoint(importedTrackPoints.get(1), "2020-11-28T17:06:25.448Z", 1.23415, 12.340036, 439.1626281738281);
|
|
||||||
assertTrackpoint(importedTrackPoints.get(2), "2020-11-28T17:06:47.888Z", 1.23405, 12.340057, 421.8070983886719);
|
|
||||||
|
|
||||||
// created pause trackpoint with time of previous valid trackpoint
|
|
||||||
assertTrackpoint(importedTrackPoints.get(3), "2020-11-28T17:06:47.888Z", 0.0, 100, 0.0);
|
|
||||||
// created resume trackpoint with time of next valid trackpoint
|
|
||||||
assertTrackpoint(importedTrackPoints.get(4), "2020-11-28T17:06:55.861Z", 0.0, 200, 0.0);
|
|
||||||
|
|
||||||
// last 3 trackpoints
|
|
||||||
assertTrackpoint(importedTrackPoints.get(5), "2020-11-28T17:06:55.861Z", 1.23405, 12.340057, 419.93902587890625);
|
|
||||||
assertTrackpoint(importedTrackPoints.get(6), "2020-11-28T17:06:56.905Z", 1.23405, 12.340057, 419.9036560058594);
|
|
||||||
assertTrackpoint(importedTrackPoints.get(7), "2020-11-28T17:07:20.870Z", 1.234046, 12.340082, 417.99432373046875);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertTrackpoint(final TrackPoint trackPoint, final String when, final double longitude, final double latitude, final double altitude) {
|
|
||||||
assertEquals(StringUtils.parseTime(when), trackPoint.getTime());
|
|
||||||
assertEquals(latitude, trackPoint.getLatitude(), 0.001d);
|
|
||||||
assertEquals(longitude, trackPoint.getLongitude(), 0.001d);
|
|
||||||
assertEquals(altitude, trackPoint.getAltitude(), 0.001d);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -20,6 +20,8 @@ import org.junit.Test;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.JUnit4;
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.services.handlers.AdaptiveLocationListenerPolicy;
|
import de.dennisguse.opentracks.services.handlers.AdaptiveLocationListenerPolicy;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
@@ -32,13 +34,13 @@ import static org.junit.Assert.assertEquals;
|
|||||||
@RunWith(JUnit4.class)
|
@RunWith(JUnit4.class)
|
||||||
public class AdaptiveLocationListenerPolicyTest {
|
public class AdaptiveLocationListenerPolicyTest {
|
||||||
|
|
||||||
private static final long MIN = 1000;
|
private static final Duration MIN = Duration.ofMillis(1000);
|
||||||
private static final long MAX = 3000;
|
private static final Duration MAX = Duration.ofMillis(3000);
|
||||||
private static final int MIN_DISTANCE = 10;
|
private static final int MIN_DISTANCE = 10;
|
||||||
private static final long NEW_IDLE_TIME_BIG = 10000;
|
private static final Duration NEW_IDLE_TIME_BIG = Duration.ofMillis(10000);
|
||||||
private static final long NEW_IDLE_TIME_NORMAL = 5000;
|
private static final Duration NEW_IDLE_TIME_NORMAL = Duration.ofMillis(5000);
|
||||||
private static final long NEW_IDLE_TIME_SMALL = 2000;
|
private static final Duration NEW_IDLE_TIME_SMALL = Duration.ofMillis(2000);
|
||||||
private static final long NEW_IDLE_TIME_LESS_THAN_MIN = 500;
|
private static final Duration NEW_IDLE_TIME_LESS_THAN_MIN = Duration.ofMillis(500);
|
||||||
private AdaptiveLocationListenerPolicy adaptiveLocationListenerPolicy;
|
private AdaptiveLocationListenerPolicy adaptiveLocationListenerPolicy;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@@ -62,7 +64,7 @@ public class AdaptiveLocationListenerPolicyTest {
|
|||||||
|
|
||||||
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_NORMAL);
|
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_NORMAL);
|
||||||
// First get the half of NEW_IDLE_TIME_NORMAL, and then round it to the nearest second.
|
// First get the half of NEW_IDLE_TIME_NORMAL, and then round it to the nearest second.
|
||||||
assertEquals((NEW_IDLE_TIME_NORMAL / 2 / 1000) * 1000, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
|
assertEquals(NEW_IDLE_TIME_NORMAL.dividedBy(2).getSeconds(), adaptiveLocationListenerPolicy.getDesiredPollingInterval().getSeconds());
|
||||||
|
|
||||||
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_SMALL);
|
adaptiveLocationListenerPolicy.updateIdleTime(NEW_IDLE_TIME_SMALL);
|
||||||
assertEquals(MIN, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
|
assertEquals(MIN, adaptiveLocationListenerPolicy.getDesiredPollingInterval());
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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,9 +45,9 @@ 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.data.TrackPointsColumns;
|
|
||||||
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.stats.TrackStatistics;
|
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||||
@@ -85,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()));
|
||||||
@@ -103,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
|
||||||
@@ -113,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();
|
||||||
}
|
}
|
||||||
@@ -146,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, -1L, 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, -1L, 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());
|
||||||
@@ -183,43 +186,78 @@ public class TrackRecordingServiceTest {
|
|||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testRecording_pauseAndResume() throws TimeoutException, InterruptedException {
|
public void testRecording_start() throws TimeoutException {
|
||||||
// given
|
// given
|
||||||
createDummyTrack(trackId, -1L, true);
|
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(createStartIntent(context)))
|
||||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
.getService();
|
||||||
newTrackPoint(service);
|
|
||||||
|
// when
|
||||||
|
Track.Id trackId = service.startNewTrack();
|
||||||
|
|
||||||
|
// then
|
||||||
|
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||||
|
|
||||||
|
assertEquals(1, trackPoints.size());
|
||||||
|
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(0).getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
@Test
|
||||||
|
public void testRecording_stop() throws TimeoutException {
|
||||||
|
// given
|
||||||
|
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(createStartIntent(context)))
|
||||||
|
.getService();
|
||||||
|
Track.Id trackId = service.startNewTrack();
|
||||||
|
|
||||||
|
// when
|
||||||
|
service.endCurrentTrack();
|
||||||
|
|
||||||
|
// then
|
||||||
|
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||||
|
|
||||||
|
assertEquals(2, trackPoints.size());
|
||||||
|
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(0).getType());
|
||||||
|
assertEquals(TrackPoint.Type.SEGMENT_END_MANUAL, trackPoints.get(1).getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@MediumTest
|
||||||
|
@Test
|
||||||
|
public void testRecording_pauseAndResume() throws TimeoutException {
|
||||||
|
// given
|
||||||
|
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(createStartIntent(context)))
|
||||||
|
.getService();
|
||||||
|
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);
|
||||||
newTrackPoint(service);
|
|
||||||
|
|
||||||
// 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(5, trackPoints.size());
|
assertEquals(3, trackPoints.size());
|
||||||
assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(1).getLatitude(), 0.01);
|
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(0).getType());
|
||||||
assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(2).getLatitude(), 0.01);
|
assertEquals(TrackPoint.Type.SEGMENT_END_MANUAL, trackPoints.get(1).getType());
|
||||||
assertEquals(TrackPointsColumns.RESUME_LATITUDE, trackPoints.get(3).getLatitude(), 0.01);
|
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(2).getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testRecording_resumeStoppedTrack() throws TimeoutException, InterruptedException {
|
public void testRecording_resumeStoppedTrack() throws TimeoutException, InterruptedException {
|
||||||
// given
|
// given
|
||||||
createDummyTrack(trackId, -1L, true);
|
TrackRecordingService service = ((TrackRecordingService.Binder) mServiceRule.bindService(createStartIntent(context)))
|
||||||
TrackRecordingServiceInterface service = ((TrackRecordingServiceInterface) mServiceRule.bindService(createStartIntent(context)));
|
.getService();
|
||||||
newTrackPoint(service);
|
Track.Id trackId = service.startNewTrack();
|
||||||
|
assertTrue(service.isRecording());
|
||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
assertEquals(1, contentProviderUtils.getTrackPoints(trackId).size());
|
assertEquals(2, contentProviderUtils.getTrackPointCursor(trackId, null).getCount());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
service.resumeTrack(trackId);
|
service.resumeTrack(trackId);
|
||||||
@@ -227,56 +265,62 @@ 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(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(1).getLatitude(), 0.01);
|
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(0).getType());
|
||||||
assertEquals(TrackPointsColumns.RESUME_LATITUDE, trackPoints.get(2).getLatitude(), 0.01);
|
assertEquals(TrackPoint.Type.SEGMENT_END_MANUAL, trackPoints.get(1).getType());
|
||||||
|
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(2).getType());
|
||||||
|
assertEquals(TrackPoint.Type.TRACKPOINT, trackPoints.get(3).getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Ignore("Sometimes fails on CI.")
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testRecording_orphanedRecordingTrack() 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();
|
||||||
|
assertTrue(service.isRecording());
|
||||||
|
service.pauseCurrentTrack();
|
||||||
|
|
||||||
|
assertEquals(2, contentProviderUtils.getTrackPointCursor(trackId, null).getCount());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
// Just set recording track to a bogus value.
|
service.endCurrentTrack();
|
||||||
// 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
|
// then
|
||||||
assertFalse(service.isRecording());
|
assertFalse(service.isRecording());
|
||||||
assertNull(service.getRecordingTrackId());
|
|
||||||
|
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||||
|
assertEquals(2, trackPoints.size());
|
||||||
|
assertEquals(TrackPoint.Type.SEGMENT_START_MANUAL, trackPoints.get(0).getType());
|
||||||
|
assertEquals(TrackPoint.Type.SEGMENT_END_MANUAL, trackPoints.get(1).getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@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
|
||||||
@@ -284,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
|
||||||
@@ -306,59 +350,59 @@ 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, long stopTime, 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();
|
||||||
trackStatistics.setStopTime_ms(stopTime);
|
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("gps");
|
Location location = new Location("");
|
||||||
location.setLongitude(longitude);
|
location.setLongitude(longitude);
|
||||||
location.setLatitude(latitude);
|
location.setLatitude(latitude);
|
||||||
location.setAccuracy(accuracy);
|
location.setAccuracy(accuracy);
|
||||||
@@ -366,10 +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);
|
|
||||||
|
|
||||||
//TODO Needed?
|
|
||||||
Thread.sleep(200);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import android.content.ContentProvider;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
@@ -22,9 +23,9 @@ 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.data.TrackPointsColumns;
|
|
||||||
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.sensor.SensorDataHeartRate;
|
import de.dennisguse.opentracks.content.sensor.SensorDataHeartRate;
|
||||||
@@ -34,6 +35,7 @@ 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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests insert location.
|
* Tests insert location.
|
||||||
@@ -53,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() {
|
||||||
@@ -69,21 +71,18 @@ public class TrackRecordingServiceTestLocation {
|
|||||||
customContentProvider.attachInfo(context, null);
|
customContentProvider.attachInfo(context, null);
|
||||||
|
|
||||||
contentProviderUtils = new ContentProviderUtils(context);
|
contentProviderUtils = new ContentProviderUtils(context);
|
||||||
|
tearDown();
|
||||||
|
|
||||||
// Let's use default values.
|
// Let's use default values.
|
||||||
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
|
||||||
public void tearDown() {
|
public void tearDown() {
|
||||||
// Reset service (if some previous test failed)
|
|
||||||
if (service.isRecording() || service.isPaused()) {
|
|
||||||
service.endCurrentTrack();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that the database is empty after every test
|
// Ensure that the database is empty after every test
|
||||||
contentProviderUtils.deleteAllTracks(context);
|
contentProviderUtils.deleteAllTracks(context);
|
||||||
}
|
}
|
||||||
@@ -95,21 +94,30 @@ public class TrackRecordingServiceTestLocation {
|
|||||||
Track.Id trackId = service.startNewTrack();
|
Track.Id trackId = service.startNewTrack();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 5, 15);
|
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 1, 15);
|
||||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0001, 35.0, 5, 15);
|
TrackRecordingServiceTest.newTrackPoint(service, 45.0001, 35.0, 2, 15);
|
||||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0002, 35.0, 5, 15);
|
TrackRecordingServiceTest.newTrackPoint(service, 45.0002, 35.0, 3, 15);
|
||||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0003, 35.0, 5, 15);
|
TrackRecordingServiceTest.newTrackPoint(service, 45.0003, 35.0, 4, 15);
|
||||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0004, 35.0, 5, 15);
|
TrackRecordingServiceTest.newTrackPoint(service, 45.0004, 35.0, 5, 15);
|
||||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0005, 35.0, 5, 15);
|
TrackRecordingServiceTest.newTrackPoint(service, 45.0005, 35.0, 6, 15);
|
||||||
|
|
||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
// 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(8, trackPoints.size());
|
||||||
assertEquals(45.0005, trackPoints.get(5).getLatitude(), 0.01);
|
assertTrackPoints(List.of(
|
||||||
|
new Pair<>(TrackPoint.Type.SEGMENT_START_MANUAL, null),
|
||||||
|
new Pair<>(TrackPoint.Type.TRACKPOINT, 1),
|
||||||
|
new Pair<>(TrackPoint.Type.TRACKPOINT, 2),
|
||||||
|
new Pair<>(TrackPoint.Type.TRACKPOINT, 3),
|
||||||
|
new Pair<>(TrackPoint.Type.TRACKPOINT, 4),
|
||||||
|
new Pair<>(TrackPoint.Type.TRACKPOINT, 5),
|
||||||
|
new Pair<>(TrackPoint.Type.TRACKPOINT, 6),
|
||||||
|
new Pair<>(TrackPoint.Type.SEGMENT_END_MANUAL, null)
|
||||||
|
), trackPoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@@ -117,23 +125,29 @@ public class TrackRecordingServiceTestLocation {
|
|||||||
public void testOnLocationChangedAsync_slowMovingAccurate() throws Exception {
|
public void testOnLocationChangedAsync_slowMovingAccurate() throws Exception {
|
||||||
// given
|
// given
|
||||||
Track.Id trackId = service.startNewTrack();
|
Track.Id trackId = service.startNewTrack();
|
||||||
|
assertNotNull(trackId);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 5, 15);
|
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 1, 15);
|
||||||
TrackRecordingServiceTest.newTrackPoint(service, 45.000001, 35.0, 5, 15);
|
TrackRecordingServiceTest.newTrackPoint(service, 45.000001, 35.0, 2, 15);
|
||||||
TrackRecordingServiceTest.newTrackPoint(service, 45.000002, 35.0, 5, 15);
|
TrackRecordingServiceTest.newTrackPoint(service, 45.000002, 35.0, 3, 15);
|
||||||
TrackRecordingServiceTest.newTrackPoint(service, 45.000003, 35.0, 5, 15);
|
TrackRecordingServiceTest.newTrackPoint(service, 45.000003, 35.0, 4, 15);
|
||||||
TrackRecordingServiceTest.newTrackPoint(service, 45.000004, 35.0, 5, 15);
|
TrackRecordingServiceTest.newTrackPoint(service, 45.000004, 35.0, 5, 15);
|
||||||
TrackRecordingServiceTest.newTrackPoint(service, 45.000005, 35.0, 5, 15);
|
TrackRecordingServiceTest.newTrackPoint(service, 45.000005, 35.0, 6, 15);
|
||||||
|
|
||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertFalse(service.isRecording());
|
assertFalse(service.isRecording());
|
||||||
|
|
||||||
List<TrackPoint> trackPoints = contentProviderUtils.getTrackPoints(trackId);
|
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||||
assertEquals(2, trackPoints.size());
|
assertEquals(4, trackPoints.size());
|
||||||
assertEquals(45.000005, trackPoints.get(1).getLatitude(), 0.01);
|
assertTrackPoints(List.of(
|
||||||
|
new Pair<>(TrackPoint.Type.SEGMENT_START_MANUAL, null),
|
||||||
|
new Pair<>(TrackPoint.Type.TRACKPOINT, 1),
|
||||||
|
new Pair<>(TrackPoint.Type.TRACKPOINT, 6),
|
||||||
|
new Pair<>(TrackPoint.Type.SEGMENT_END_MANUAL, null)
|
||||||
|
), trackPoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @MediumTest
|
// @MediumTest
|
||||||
@@ -173,11 +187,14 @@ 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(3, trackPoints.size());
|
assertTrackPoints(List.of(
|
||||||
assertEquals(1, trackPoints.get(0).getAccuracy(), 0.01);
|
new Pair<>(TrackPoint.Type.SEGMENT_START_MANUAL, null),
|
||||||
assertEquals(2, trackPoints.get(1).getAccuracy(), 0.01);
|
new Pair<>(TrackPoint.Type.TRACKPOINT, 1),
|
||||||
assertEquals(6, trackPoints.get(2).getAccuracy(), 0.01);
|
new Pair<>(TrackPoint.Type.TRACKPOINT, 2),
|
||||||
|
new Pair<>(TrackPoint.Type.TRACKPOINT, 6),
|
||||||
|
new Pair<>(TrackPoint.Type.SEGMENT_END_MANUAL, null)
|
||||||
|
), trackPoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@@ -199,15 +216,18 @@ 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(6, trackPoints.size());
|
||||||
assertEquals(1, trackPoints.get(0).getAccuracy(), 0.01);
|
assertTrackPoints(List.of(
|
||||||
assertEquals(2, trackPoints.get(1).getAccuracy(), 0.01);
|
new Pair<>(TrackPoint.Type.SEGMENT_START_MANUAL, null),
|
||||||
assertEquals(5, trackPoints.get(2).getAccuracy(), 0.01); //TODO Check why this trackPoint is inserted.
|
new Pair<>(TrackPoint.Type.TRACKPOINT, 1),
|
||||||
assertEquals(6, trackPoints.get(3).getAccuracy(), 0.01);
|
new Pair<>(TrackPoint.Type.TRACKPOINT, 2),
|
||||||
|
new Pair<>(TrackPoint.Type.TRACKPOINT, 5), //TODO Check why this trackPoint is inserted.
|
||||||
|
new Pair<>(TrackPoint.Type.TRACKPOINT, 6),
|
||||||
|
new Pair<>(TrackPoint.Type.SEGMENT_END_MANUAL, null)
|
||||||
|
), trackPoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testOnLocationChangedAsync_idle_withSensorData() throws Exception {
|
public void testOnLocationChangedAsync_idle_withSensorData() throws Exception {
|
||||||
@@ -230,26 +250,30 @@ public class TrackRecordingServiceTestLocation {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// when
|
// when
|
||||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 0, 0);
|
|
||||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 1, 0);
|
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 1, 0);
|
||||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 2, 0);
|
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 2, 0);
|
||||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 3, 0);
|
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 3, 0);
|
||||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 4, 0);
|
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 4, 0);
|
||||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 5, 0);
|
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 5, 0);
|
||||||
|
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 6, 0);
|
||||||
|
|
||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
// 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(8, trackPoints.size());
|
||||||
assertEquals(0, trackPoints.get(0).getAccuracy(), 0.01);
|
assertTrackPoints(List.of(
|
||||||
assertEquals(1, trackPoints.get(1).getAccuracy(), 0.01);
|
new Pair<>(TrackPoint.Type.SEGMENT_START_MANUAL, null),
|
||||||
assertEquals(2, trackPoints.get(2).getAccuracy(), 0.01);
|
new Pair<>(TrackPoint.Type.TRACKPOINT, 1),
|
||||||
assertEquals(3, trackPoints.get(3).getAccuracy(), 0.01);
|
new Pair<>(TrackPoint.Type.TRACKPOINT, 2),
|
||||||
assertEquals(4, trackPoints.get(4).getAccuracy(), 0.01);
|
new Pair<>(TrackPoint.Type.TRACKPOINT, 3),
|
||||||
assertEquals(5, trackPoints.get(5).getAccuracy(), 0.01);
|
new Pair<>(TrackPoint.Type.TRACKPOINT, 4),
|
||||||
|
new Pair<>(TrackPoint.Type.TRACKPOINT, 5),
|
||||||
|
new Pair<>(TrackPoint.Type.TRACKPOINT, 6),
|
||||||
|
new Pair<>(TrackPoint.Type.SEGMENT_END_MANUAL, null)
|
||||||
|
), trackPoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@@ -270,14 +294,28 @@ 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());
|
||||||
assertEquals(1, trackPoints.get(0).getAccuracy(), 0.01);
|
assertTrackPoints(List.of(
|
||||||
assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(1).getLatitude(), 0.01);
|
new Pair<>(TrackPoint.Type.SEGMENT_START_MANUAL, null),
|
||||||
assertEquals(2, trackPoints.get(2).getAccuracy(), 0.01);
|
new Pair<>(TrackPoint.Type.TRACKPOINT, 1),
|
||||||
assertEquals(3, trackPoints.get(3).getAccuracy(), 0.01);
|
|
||||||
assertEquals(TrackPointsColumns.PAUSE_LATITUDE, trackPoints.get(4).getLatitude(), 0.01);
|
new Pair<>(TrackPoint.Type.SEGMENT_START_AUTOMATIC, 2),
|
||||||
assertEquals(4, trackPoints.get(5).getAccuracy(), 0.01);
|
new Pair<>(TrackPoint.Type.TRACKPOINT, 3),
|
||||||
assertEquals(5, trackPoints.get(6).getAccuracy(), 0.01);
|
|
||||||
|
new Pair<>(TrackPoint.Type.SEGMENT_START_AUTOMATIC, 4),
|
||||||
|
new Pair<>(TrackPoint.Type.TRACKPOINT, 5),
|
||||||
|
new Pair<>(TrackPoint.Type.SEGMENT_END_MANUAL, null)
|
||||||
|
), trackPoints);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertTrackPoints(List<Pair<TrackPoint.Type, Object>> typeAndAccuracy, List<TrackPoint> actual) {
|
||||||
|
assertEquals(typeAndAccuracy.size(), actual.size());
|
||||||
|
for (int i = 0; i < typeAndAccuracy.size(); i++) {
|
||||||
|
assertEquals(typeAndAccuracy.get(i).first, actual.get(i).getType());
|
||||||
|
if (typeAndAccuracy.get(i).second != null) {
|
||||||
|
assertEquals((Integer) typeAndAccuracy.get(i).second, actual.get(i).getAccuracy(), 0.01);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -19,6 +20,7 @@ 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.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
@@ -34,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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -52,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;
|
||||||
|
|
||||||
@@ -76,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
|
||||||
@@ -86,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();
|
||||||
}
|
}
|
||||||
@@ -98,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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,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.
|
||||||
@@ -208,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();
|
||||||
@@ -222,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) {
|
||||||
@@ -233,13 +254,12 @@ 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());
|
||||||
TrackStatistics trackStatistics = track.getTrackStatistics();
|
TrackStatistics trackStatistics = track.getTrackStatistics();
|
||||||
assertNotNull(trackStatistics);
|
assertNotNull(trackStatistics);
|
||||||
assertTrue(trackStatistics.getStartTime_ms() > 0);
|
assertTrue(trackStatistics.getStartTime().isAfter(Instant.ofEpochMilli(0)));
|
||||||
assertTrue(trackStatistics.getStopTime_ms() >= trackStatistics.getStartTime_ms());
|
assertTrue(trackStatistics.getStopTime().isAfter(trackStatistics.getStartTime()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,16 +45,16 @@ 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
|
||||||
public void sendTrackPoint() throws InterruptedException {
|
public void sendTrackPoint() throws InterruptedException {
|
||||||
// given
|
// given
|
||||||
TrackPoint trackPoint = new TrackPoint();
|
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT);
|
||||||
int accuracy = 50;
|
int accuracy = 50;
|
||||||
|
|
||||||
// when
|
// when
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ package de.dennisguse.opentracks.services.handlers;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
|
import android.os.Looper;
|
||||||
|
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.InjectMocks;
|
import org.mockito.InjectMocks;
|
||||||
@@ -25,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;
|
||||||
@@ -32,14 +35,19 @@ public class LocationHandlerTest {
|
|||||||
@InjectMocks
|
@InjectMocks
|
||||||
private LocationHandler locationHandler;
|
private LocationHandler locationHandler;
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void preSetUp() {
|
||||||
|
// Prepare looper for Android's message queue
|
||||||
|
if (Looper.myLooper() == null) Looper.prepare();
|
||||||
|
}
|
||||||
|
|
||||||
@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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,10 +85,9 @@ 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
|
||||||
// poor latitude
|
|
||||||
locationHandler.onLocationChanged(createLocation(45f, 35f, prefAccuracy + 1, 5, System.currentTimeMillis()));
|
locationHandler.onLocationChanged(createLocation(45f, 35f, prefAccuracy + 1, 5, System.currentTimeMillis()));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
|
|||||||
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,12 +15,20 @@
|
|||||||
*/
|
*/
|
||||||
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.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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link TrackStatistics}.
|
* Tests for {@link TrackStatistics}.
|
||||||
@@ -28,7 +36,7 @@ import static org.junit.Assert.assertEquals;
|
|||||||
*
|
*
|
||||||
* @author Rodrigo Damazio
|
* @author Rodrigo Damazio
|
||||||
*/
|
*/
|
||||||
@RunWith(JUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class TrackStatisticsTest {
|
public class TrackStatisticsTest {
|
||||||
|
|
||||||
private TrackStatistics statistics;
|
private TrackStatistics statistics;
|
||||||
@@ -39,51 +47,77 @@ public class TrackStatisticsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMerge() {
|
public void testMerge_no_data() {
|
||||||
|
// given
|
||||||
TrackStatistics statistics2 = new TrackStatistics();
|
TrackStatistics statistics2 = new TrackStatistics();
|
||||||
statistics.setStartTime_ms(1000L); // Resulting start time
|
|
||||||
statistics.setStopTime_ms(2500L);
|
|
||||||
statistics2.setStartTime_ms(3000L);
|
|
||||||
statistics2.setStopTime_ms(4000L); // Resulting stop time
|
|
||||||
statistics.setTotalTime(1500L);
|
|
||||||
statistics2.setTotalTime(1000L); // Result: 1500+1000
|
|
||||||
statistics.setMovingTime(700L);
|
|
||||||
statistics2.setMovingTime(600L); // Result: 700+600
|
|
||||||
statistics.setTotalDistance(750.0);
|
|
||||||
statistics2.setTotalDistance(350.0); // Result: 750+350
|
|
||||||
statistics.setTotalElevationGain(50.0f);
|
|
||||||
statistics2.setTotalElevationGain(850.0f); // Result: 850+50
|
|
||||||
statistics.setMaxSpeed(60.0); // Resulting max speed
|
|
||||||
statistics2.setMaxSpeed(30.0);
|
|
||||||
statistics.setMaxElevation(1250.0);
|
|
||||||
statistics.setMinElevation(1200.0); // Resulting min elevation
|
|
||||||
statistics2.setMaxElevation(3575.0); // Resulting max elevation
|
|
||||||
statistics2.setMinElevation(2800.0);
|
|
||||||
|
|
||||||
|
// when
|
||||||
statistics.merge(statistics2);
|
statistics.merge(statistics2);
|
||||||
|
|
||||||
assertEquals(1000L, statistics.getStartTime_ms());
|
// then
|
||||||
assertEquals(4000L, statistics.getStopTime_ms());
|
assertNull(statistics.getStartTime());
|
||||||
assertEquals(2500L, statistics.getTotalTime());
|
assertNull(statistics.getStopTime());
|
||||||
assertEquals(1300L, statistics.getMovingTime());
|
assertEquals(Duration.ofSeconds(0), statistics.getMovingTime());
|
||||||
assertEquals(1100.0, statistics.getTotalDistance(), 0.001);
|
assertEquals(Duration.ofSeconds(0), statistics.getTotalTime());
|
||||||
assertEquals(900.0, statistics.getTotalElevationGain(), 0.001);
|
|
||||||
assertEquals(statistics.getTotalDistance() / (statistics.getMovingTime() / 1000.0), statistics.getMaxSpeed(), 0.001);
|
assertNull(statistics.getTotalAltitudeGain());
|
||||||
assertEquals(1200.0, statistics.getMinElevation(), 0.001);
|
assertNull(statistics.getTotalAltitudeLoss());
|
||||||
assertEquals(3575.0, statistics.getMaxElevation(), 0.001);
|
assertEquals(Double.NEGATIVE_INFINITY, statistics.getMaxAltitude(), 0.0);
|
||||||
|
assertEquals(Double.POSITIVE_INFINITY, statistics.getMinAltitude(), 0.0);
|
||||||
|
assertEquals(0.0, statistics.getMaxSpeed().toMPS(), 0.0);
|
||||||
|
assertEquals(0.0, statistics.getAverageSpeed().toMPS(), 0.0);
|
||||||
|
assertEquals(0.0, statistics.getAverageMovingSpeed().toMPS(), 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMerge() {
|
||||||
|
// given
|
||||||
|
TrackStatistics statistics2 = new TrackStatistics();
|
||||||
|
statistics.setStartTime(Instant.ofEpochMilli(1000)); // Resulting start time
|
||||||
|
statistics.setStopTime(Instant.ofEpochMilli(2500));
|
||||||
|
statistics2.setStartTime(Instant.ofEpochMilli(3000));
|
||||||
|
statistics2.setStopTime(Instant.ofEpochMilli(4000)); // Resulting stop time
|
||||||
|
statistics.setTotalTime(Duration.ofMillis(1500));
|
||||||
|
statistics2.setTotalTime(Duration.ofMillis(1000)); // Result: 1500+1000
|
||||||
|
statistics.setMovingTime(Duration.ofMillis(700));
|
||||||
|
statistics2.setMovingTime(Duration.ofMillis(600)); // Result: 700+600
|
||||||
|
statistics.setTotalDistance(Distance.of(750.0));
|
||||||
|
statistics2.setTotalDistance(Distance.of(350.0)); // Result: 750+350
|
||||||
|
statistics.setTotalAltitudeGain(50.0f);
|
||||||
|
statistics2.setTotalAltitudeGain(850.0f); // Result: 850+50
|
||||||
|
statistics.setMaxSpeed(Speed.of(60.0)); // Resulting max speed
|
||||||
|
statistics2.setMaxSpeed(Speed.of(30.0));
|
||||||
|
statistics.setMaxAltitude(1250.0);
|
||||||
|
statistics.setMinAltitude(1200.0); // Resulting min altitude
|
||||||
|
statistics2.setMaxAltitude(3575.0); // Resulting max altitude
|
||||||
|
statistics2.setMinAltitude(2800.0);
|
||||||
|
|
||||||
|
// when
|
||||||
|
statistics.merge(statistics2);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals(Instant.ofEpochMilli(1000), statistics.getStartTime());
|
||||||
|
assertEquals(Instant.ofEpochMilli(4000), statistics.getStopTime());
|
||||||
|
assertEquals(Duration.ofMillis(2500), statistics.getTotalTime());
|
||||||
|
assertEquals(Duration.ofMillis(1300), statistics.getMovingTime());
|
||||||
|
assertEquals(1100.0, statistics.getTotalDistance().toM(), 0.001);
|
||||||
|
assertEquals(900.0, statistics.getTotalAltitudeGain(), 0.001);
|
||||||
|
assertEquals(Speed.of(statistics.getTotalDistance(), statistics.getMovingTime()).toMPS(), statistics.getMaxSpeed().toMPS(), 0.001);
|
||||||
|
assertEquals(1200.0, statistics.getMinAltitude(), 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(50000); // in milliseconds
|
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(20000); // in milliseconds
|
statistics.setMovingTime(Duration.ofMillis(20000));
|
||||||
assertEquals(50.0, statistics.getAverageMovingSpeed(), 0.001);
|
assertEquals(50.0, statistics.getAverageMovingSpeed().toMPS(), 0.001);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,167 @@
|
|||||||
|
package de.dennisguse.opentracks.stats;
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
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.Track;
|
||||||
|
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class TrackStatisticsUpdaterTest {
|
||||||
|
|
||||||
|
private static final Distance GPS_DISTANCE = Distance.of(50);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addTrackPoint_TestingTrack() {
|
||||||
|
// given
|
||||||
|
TestDataUtil.TrackData data = TestDataUtil.createTestingTrack(new Track.Id(1));
|
||||||
|
|
||||||
|
// when
|
||||||
|
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
|
||||||
|
data.trackPoints.forEach(it -> subject.addTrackPoint(it, GPS_DISTANCE));
|
||||||
|
|
||||||
|
// then
|
||||||
|
TrackStatistics statistics = subject.getTrackStatistics();
|
||||||
|
assertEquals(99.58, statistics.getTotalDistance().toM(), 0.01);
|
||||||
|
assertEquals(Duration.ofSeconds(14), statistics.getTotalTime());
|
||||||
|
assertEquals(Duration.ofSeconds(7), statistics.getMovingTime());
|
||||||
|
|
||||||
|
assertEquals(2.5, statistics.getMinAltitude(), 0.01);
|
||||||
|
assertEquals(27.5, statistics.getMaxAltitude(), 0.01);
|
||||||
|
assertEquals(36, statistics.getTotalAltitudeGain(), 0.01);
|
||||||
|
assertEquals(36, statistics.getTotalAltitudeLoss(), 0.01);
|
||||||
|
|
||||||
|
assertEquals(14.226, statistics.getMaxSpeed().toMPS(), 0.01);
|
||||||
|
assertEquals(14.226, statistics.getAverageMovingSpeed().toMPS(), 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() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,9 +8,11 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
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;
|
||||||
@@ -25,58 +27,38 @@ public class AnnouncementUtilsTest {
|
|||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getAnnouncement() {
|
public void getAnnouncement_metric() {
|
||||||
TrackStatistics stats = new TrackStatistics();
|
TrackStatistics stats = new TrackStatistics();
|
||||||
stats.setTotalDistance(20000);
|
stats.setTotalDistance(Distance.of(20000));
|
||||||
stats.setTotalTime(600000);
|
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||||
stats.setMovingTime(300000);
|
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||||
stats.setMaxSpeed(100);
|
stats.setMaxSpeed(Speed.of(100));
|
||||||
stats.setTotalElevationGain(6000);
|
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() {
|
public void getAnnouncement_withInterval_metric() {
|
||||||
TrackStatistics stats = new TrackStatistics();
|
TrackStatistics stats = new TrackStatistics();
|
||||||
stats.setTotalDistance(20000);
|
stats.setTotalDistance(Distance.of(20000));
|
||||||
stats.setTotalTime(600000);
|
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||||
stats.setMovingTime(300000);
|
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||||
stats.setMaxSpeed(100);
|
stats.setMaxSpeed(Speed.of(100));
|
||||||
stats.setTotalElevationGain(6000);
|
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);
|
||||||
|
|
||||||
int speedId = R.plurals.voiceSpeedKilometersPerHour;
|
|
||||||
double kmPerHour = lastInterval.getSpeed_ms() * UnitConversions.MPS_TO_KMH;
|
|
||||||
|
|
||||||
String firstPartMsg = "OpenTracks total distance 20.00 kilometers in 5 minutes 0 seconds at 240.0 kilometers per hour";
|
|
||||||
String rateMsg = " Lap speed of " + context.getResources().getQuantityString(speedId, getQuantityCount(kmPerHour), kmPerHour);
|
|
||||||
String msg = firstPartMsg + rateMsg;
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = AnnouncementUtils.getAnnouncement(context, stats, "airplane", lastInterval);
|
String announcement = AnnouncementUtils.getAnnouncement(context, stats, true, true, lastInterval);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(msg, 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);
|
||||||
}
|
|
||||||
|
|
||||||
private int getQuantityCount(double d) {
|
|
||||||
if (d == 0) {
|
|
||||||
return 0;
|
|
||||||
} else if (d == 1) {
|
|
||||||
return 1;
|
|
||||||
} else if (d == 2) {
|
|
||||||
return 2;
|
|
||||||
} else {
|
|
||||||
int count = (int) d;
|
|
||||||
return Math.max(count, 3);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -24,9 +24,13 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
@@ -40,71 +44,47 @@ public class StringUtilsTest {
|
|||||||
|
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link StringUtils#formatDateTimeIso8601(long)}.
|
* Tests {@link StringUtils#formatElapsedTime(Duration)}.
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testFormatDateTimeIso8601() {
|
|
||||||
assertEquals("1970-01-01T00:00:12.345Z", StringUtils.formatDateTimeIso8601(12345));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests {@link StringUtils#formatElapsedTime(long)}.
|
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testformatElapsedTime() {
|
public void testformatElapsedTime() {
|
||||||
// 1 second
|
assertEquals("00:01", StringUtils.formatElapsedTime(Duration.ofMillis(1000)));
|
||||||
assertEquals("00:01", StringUtils.formatElapsedTime(1000));
|
assertEquals("00:10", StringUtils.formatElapsedTime(Duration.ofMillis(10000)));
|
||||||
// 10 seconds
|
assertEquals("01:00", StringUtils.formatElapsedTime(Duration.ofMillis(60000)));
|
||||||
assertEquals("00:10", StringUtils.formatElapsedTime(10000));
|
assertEquals("10:00", StringUtils.formatElapsedTime(Duration.ofMillis(600000)));
|
||||||
// 1 minute
|
assertEquals("1:00:00", StringUtils.formatElapsedTime(Duration.ofMillis(3600000)));
|
||||||
assertEquals("01:00", StringUtils.formatElapsedTime(60000));
|
assertEquals("10:00:00", StringUtils.formatElapsedTime(Duration.ofMillis(36000000)));
|
||||||
// 10 minutes
|
assertEquals("100:00:00", StringUtils.formatElapsedTime(Duration.ofMillis(360000000)));
|
||||||
assertEquals("10:00", StringUtils.formatElapsedTime(600000));
|
|
||||||
// 1 hour
|
|
||||||
assertEquals("1:00:00", StringUtils.formatElapsedTime(3600000));
|
|
||||||
// 10 hours
|
|
||||||
assertEquals("10:00:00", StringUtils.formatElapsedTime(36000000));
|
|
||||||
// 100 hours
|
|
||||||
assertEquals("100:00:00", StringUtils.formatElapsedTime(360000000));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests {@link StringUtils#formatElapsedTimeWithHour(long)}.
|
* Tests {@link StringUtils#formatElapsedTimeWithHour(Duration)}.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testformatElapsedTimeWithHour() {
|
public void testformatElapsedTimeWithHour() {
|
||||||
// 1 second
|
assertEquals("0:00:01", StringUtils.formatElapsedTimeWithHour(Duration.ofMillis(1000)));
|
||||||
assertEquals("0:00:01", StringUtils.formatElapsedTimeWithHour(1000));
|
assertEquals("0:00:10", StringUtils.formatElapsedTimeWithHour(Duration.ofMillis(10000)));
|
||||||
// 10 seconds
|
assertEquals("0:01:00", StringUtils.formatElapsedTimeWithHour(Duration.ofMillis(60000)));
|
||||||
assertEquals("0:00:10", StringUtils.formatElapsedTimeWithHour(10000));
|
assertEquals("0:10:00", StringUtils.formatElapsedTimeWithHour(Duration.ofMillis(600000)));
|
||||||
// 1 minute
|
assertEquals("1:00:00", StringUtils.formatElapsedTimeWithHour(Duration.ofMillis(3600000)));
|
||||||
assertEquals("0:01:00", StringUtils.formatElapsedTimeWithHour(60000));
|
assertEquals("10:00:00", StringUtils.formatElapsedTimeWithHour(Duration.ofMillis(36000000)));
|
||||||
// 10 minutes
|
assertEquals("100:00:00", StringUtils.formatElapsedTimeWithHour(Duration.ofMillis(360000000)));
|
||||||
assertEquals("0:10:00", StringUtils.formatElapsedTimeWithHour(600000));
|
|
||||||
// 1 hour
|
|
||||||
assertEquals("1:00:00", StringUtils.formatElapsedTimeWithHour(3600000));
|
|
||||||
// 10 hours
|
|
||||||
assertEquals("10:00:00", StringUtils.formatElapsedTimeWithHour(36000000));
|
|
||||||
// 100 hours
|
|
||||||
assertEquals("100:00:00", StringUtils.formatElapsedTimeWithHour(360000000));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -116,18 +96,12 @@ public class StringUtilsTest {
|
|||||||
assertEquals("<![CDATA[hello]]]]><![CDATA[>there]]>", StringUtils.formatCData("hello]]>there"));
|
assertEquals("<![CDATA[hello]]]]><![CDATA[>there]]>", StringUtils.formatCData("hello]]>there"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests {@link StringUtils#parseTime(String)} with fractional seconds.
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetTime_fractional() {
|
public void testGetTime_fractional() {
|
||||||
assertGetTime("2010-05-04T03:02:01.352Z", 2010, 5, 4, 3, 2, 1, 352);
|
assertGetTime("2010-05-04T03:02:01.352Z", 2010, 5, 4, 3, 2, 1, 352);
|
||||||
assertGetTime("2010-05-04T03:02:01.3529Z", 2010, 5, 4, 3, 2, 1, 352);
|
assertGetTime("2010-05-04T03:02:01.3529Z", 2010, 5, 4, 3, 2, 1, 352);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests {@link StringUtils#parseTime(String)} with time zone.
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetTime_timezone() {
|
public void testGetTime_timezone() {
|
||||||
assertGetTime("2010-05-04T03:02:01", 2010, 5, 4, 3, 2, 1, 0);
|
assertGetTime("2010-05-04T03:02:01", 2010, 5, 4, 3, 2, 1, 0);
|
||||||
@@ -140,9 +114,6 @@ public class StringUtilsTest {
|
|||||||
assertGetTime("2010-05-04T03:02:01-05:00", 2010, 5, 4, 8, 2, 1, 0);
|
assertGetTime("2010-05-04T03:02:01-05:00", 2010, 5, 4, 8, 2, 1, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests {@link StringUtils#parseTime(String)} with fractional seconds and time zone.
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetTime_fractionalAndTimezone() {
|
public void testGetTime_fractionalAndTimezone() {
|
||||||
assertGetTime("2010-05-04T03:02:01.352Z", 2010, 5, 4, 3, 2, 1, 352);
|
assertGetTime("2010-05-04T03:02:01.352Z", 2010, 5, 4, 3, 2, 1, 352);
|
||||||
@@ -170,29 +141,7 @@ public class StringUtilsTest {
|
|||||||
|
|
||||||
// This comparision tends to be flaky (difference of 1ms)
|
// This comparision tends to be flaky (difference of 1ms)
|
||||||
// Assert.assertEquals(calendar.getTimeInMillis(), StringUtils.parseTime(xmlDateTime));
|
// Assert.assertEquals(calendar.getTimeInMillis(), StringUtils.parseTime(xmlDateTime));
|
||||||
assertTrue(calendar.getTimeInMillis() + " vs. " + StringUtils.parseTime(xmlDateTime), Math.abs(calendar.getTimeInMillis() - StringUtils.parseTime(xmlDateTime)) <= 1);
|
assertTrue(calendar.getTimeInMillis() + " vs. " + StringUtils.parseTime(xmlDateTime), Math.abs(calendar.getTimeInMillis() - StringUtils.parseTime(xmlDateTime).toEpochMilli()) <= 1);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests {@link StringUtils#getTimeParts(long)} with a positive number.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testGetTimeParts_positive() {
|
|
||||||
int[] parts = StringUtils.getTimeParts(61000);
|
|
||||||
assertEquals(1, parts[0]);
|
|
||||||
assertEquals(1, parts[1]);
|
|
||||||
assertEquals(0, parts[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests {@link StringUtils#getTimeParts(long)} with a negative number.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testGetTimeParts_negative() {
|
|
||||||
int[] parts = StringUtils.getTimeParts(-61000);
|
|
||||||
assertEquals(-1, parts[0]);
|
|
||||||
assertEquals(-1, parts[1]);
|
|
||||||
assertEquals(0, parts[2]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -217,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;
|
||||||
@@ -25,7 +26,7 @@ import org.junit.Test;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.time.Instant;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
@@ -42,16 +43,18 @@ import static org.junit.Assert.assertEquals;
|
|||||||
public class TrackNameUtilsTest {
|
public class TrackNameUtilsTest {
|
||||||
|
|
||||||
private static final Track.Id TRACK_ID = new Track.Id(1L);
|
private static final Track.Id TRACK_ID = new Track.Id(1L);
|
||||||
private static final long START_TIME = 1288213406000L;
|
private static final Instant START_TIME = Instant.ofEpochMilli(1288213406000L);
|
||||||
|
|
||||||
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,9 +63,9 @@ 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(new Date(START_TIME)), 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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
package de.dennisguse.opentracks.util;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
public class TrackPointUtilsTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void fixTime_none() {
|
|
||||||
// given
|
|
||||||
long time = System.currentTimeMillis();
|
|
||||||
TrackPoint trackPoint = new TrackPoint();
|
|
||||||
trackPoint.setTime(time);
|
|
||||||
|
|
||||||
// when
|
|
||||||
TrackPointUtils.fixTime(trackPoint);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(time, trackPoint.getTime());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void fixTime_0() {
|
|
||||||
// given
|
|
||||||
long time = System.currentTimeMillis();
|
|
||||||
TrackPoint trackPoint = new TrackPoint();
|
|
||||||
trackPoint.setTime(0);
|
|
||||||
|
|
||||||
// when
|
|
||||||
TrackPointUtils.fixTime(trackPoint);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(time, trackPoint.getTime(), 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void fixTime_gpsWeekRollover() {
|
|
||||||
// given
|
|
||||||
long time = System.currentTimeMillis();
|
|
||||||
TrackPoint trackPoint = new TrackPoint();
|
|
||||||
trackPoint.setTime(time - 1024 * UnitConversions.ONE_WEEK_MS);
|
|
||||||
|
|
||||||
// when
|
|
||||||
TrackPointUtils.fixTime(trackPoint);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(time, trackPoint.getTime());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -8,9 +8,13 @@ import org.junit.Test;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.JUnit4;
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
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;
|
||||||
@@ -23,24 +27,17 @@ public class AggregatedStatisticsTest {
|
|||||||
|
|
||||||
private final Context context = ApplicationProvider.getApplicationContext();
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
|
||||||
/**
|
private static Track createTrack(Context context, Distance totalDistance, Duration totalTime, String category) {
|
||||||
* Create a TrackStatistics object.
|
|
||||||
*
|
|
||||||
* @param totalDistance distance in meters.
|
|
||||||
* @param totalTime total time in milliseconds.
|
|
||||||
* @return TrackStatistics object.
|
|
||||||
*/
|
|
||||||
private static Track createTrack(Context context, long totalDistance, long totalTime, String category) {
|
|
||||||
TrackStatistics statistics = new TrackStatistics();
|
TrackStatistics statistics = new TrackStatistics();
|
||||||
statistics.setStartTime_ms(1000L); // Resulting start time
|
statistics.setStartTime(Instant.ofEpochMilli(1000L)); // Resulting start time
|
||||||
statistics.setStopTime_ms(1000L + totalTime);
|
statistics.setStopTime(statistics.getStartTime().plus(totalTime));
|
||||||
statistics.setTotalTime(totalTime);
|
statistics.setTotalTime(totalTime);
|
||||||
statistics.setMovingTime(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));
|
||||||
@@ -53,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);
|
||||||
|
|
||||||
@@ -67,7 +64,7 @@ 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(totalTime, statistics2.getMovingTime());
|
assertEquals(totalTime, statistics2.getMovingTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,8 +72,8 @@ public class AggregatedStatisticsTest {
|
|||||||
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);
|
||||||
|
|
||||||
@@ -91,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);
|
||||||
|
|
||||||
@@ -107,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));
|
||||||
|
|
||||||
@@ -121,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(totalTime * 2L, 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),
|
||||||
@@ -155,19 +152,19 @@ 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(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(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(totalTime, statistics2.getMovingTime());
|
assertEquals(totalTime, statistics2.getMovingTime());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -176,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);
|
||||||
@@ -218,28 +215,28 @@ 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(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(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(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(totalTime, statistics2.getMovingTime());
|
assertEquals(totalTime, statistics2.getMovingTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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(trackPoints.get(0).getTime());
|
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);
|
||||||
long 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()) * UnitConversions.S_TO_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() * UnitConversions.MS_TO_S, totalTime * UnitConversions.MS_TO_S, 0.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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
This hidden file is here to ensure there is a res folder.
|
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<gpx xmlns="http://www.topografix.com/GPX/1/1" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:gpxtpx="http://www.garmin.com/xmlschemes/TrackPointExtension/v2" xmlns:opentracks="http://opentracksapp.com/xmlschemas/v1" xmlns:topografix="http://www.topografix.com/GPX/Private/TopoGrafix/0/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.1" creator="OpenTracks (Debug)" xsi:schemaLocation="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.garmin.com/xmlschemas/TrackPointExtension/v2 https://www8.garmin.com/xmlschemas/TrackPointExtensionv2.xsd http://opentracksapp.com/xmlschemas/v1 http://opentracksapp.com/xmlschemas/OpenTracks_v1.xsd">
|
||||||
|
<metadata>
|
||||||
|
<name><![CDATA[2021-01-07 22:51]]></name>
|
||||||
|
<desc><![CDATA[the description]]></desc>
|
||||||
|
</metadata>
|
||||||
|
<wpt lat="3" lon="14">
|
||||||
|
<ele>10</ele>
|
||||||
|
<time>2021-01-07T21:51:59.179Z</time>
|
||||||
|
<name><![CDATA[Marker 1]]></name>
|
||||||
|
<desc><![CDATA[Marker 1 desc]]></desc>
|
||||||
|
<type><![CDATA[Marker 1 category]]></type>
|
||||||
|
</wpt>
|
||||||
|
<wpt lat="3" lon="14.002">
|
||||||
|
<ele>10</ele>
|
||||||
|
<time>2021-01-07T21:52:01.010Z</time>
|
||||||
|
<name><![CDATA[Marker 2]]></name>
|
||||||
|
<desc><![CDATA[Marker 2 desc]]></desc>
|
||||||
|
<type><![CDATA[Marker 2 category]]></type>
|
||||||
|
</wpt>
|
||||||
|
<trk>
|
||||||
|
<name><![CDATA[2021-01-07 22:51]]></name>
|
||||||
|
<desc><![CDATA[the description]]></desc>
|
||||||
|
<type><![CDATA[the category]]></type>
|
||||||
|
<extensions>
|
||||||
|
<topografix:color>c0c0c0</topografix:color>
|
||||||
|
<opentracks:trackid>7002101e-4198-4613-8c24-544e01ca3981</opentracks:trackid>
|
||||||
|
</extensions>
|
||||||
|
<trkseg>
|
||||||
|
<trkpt lat="3" lon="14">
|
||||||
|
<ele>10</ele>
|
||||||
|
<time>2021-01-07T21:51:59.179Z</time>
|
||||||
|
</trkpt>
|
||||||
|
<trkpt lat="3" lon="14.001">
|
||||||
|
<ele>10</ele>
|
||||||
|
<time>2021-01-07T21:52:00.653Z</time>
|
||||||
|
</trkpt>
|
||||||
|
<trkpt lat="3" lon="14.002">
|
||||||
|
<ele>10</ele>
|
||||||
|
<time>2021-01-07T21:52:01.010Z</time>
|
||||||
|
</trkpt>
|
||||||
|
<trkpt lat="100" lon="0">
|
||||||
|
<time>2021-01-07T21:52:02.658Z</time>
|
||||||
|
</trkpt>
|
||||||
|
|
||||||
|
<trkpt lat="200" lon="0">
|
||||||
|
<time>2021-01-07T21:52:03.873Z</time>
|
||||||
|
</trkpt>
|
||||||
|
<trkpt lat="3" lon="14.003">
|
||||||
|
<ele>10</ele>
|
||||||
|
<time>2021-01-07T21:52:04.103Z</time>
|
||||||
|
</trkpt>
|
||||||
|
</trkseg>
|
||||||
|
</trk>
|
||||||
|
</gpx>
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<kml xmlns="http://www.opengis.net/kml/2.2"
|
||||||
|
xmlns:gx="http://www.google.com/kml/ext/2.2"
|
||||||
|
xmlns:atom="http://www.w3.org/2005/Atom"
|
||||||
|
xmlns:opentracks="http://opentracksapp.com/xmlschemas/v1">
|
||||||
|
<Document>
|
||||||
|
<open>1</open>
|
||||||
|
<visibility>1</visibility>
|
||||||
|
<name><![CDATA[2020-08-24 20:57]]></name>
|
||||||
|
<atom:generator><![CDATA[OpenTracks]]></atom:generator>
|
||||||
|
<Style id="track">
|
||||||
|
<LineStyle><color>7f0000ff</color><width>4</width></LineStyle>
|
||||||
|
<IconStyle>
|
||||||
|
<scale>1.3</scale>
|
||||||
|
<Icon><href>http://earth.google.com/images/kml-icons/track-directional/track-0.png</href></Icon>
|
||||||
|
</IconStyle>
|
||||||
|
</Style>
|
||||||
|
<Style id="start"><IconStyle>
|
||||||
|
<scale>1.3</scale>
|
||||||
|
<Icon><href>http://maps.google.com/mapfiles/kml/paddle/grn-circle.png</href></Icon>
|
||||||
|
<hotSpot x="32" y="1" xunits="pixels" yunits="pixels"/>
|
||||||
|
</IconStyle></Style>
|
||||||
|
<Style id="end"><IconStyle>
|
||||||
|
<scale>1.3</scale>
|
||||||
|
<Icon><href>http://maps.google.com/mapfiles/kml/paddle/red-circle.png</href></Icon>
|
||||||
|
<hotSpot x="32" y="1" xunits="pixels" yunits="pixels"/>
|
||||||
|
</IconStyle></Style>
|
||||||
|
<Style id="waypoint"><IconStyle>
|
||||||
|
<scale>1.3</scale>
|
||||||
|
<Icon><href>http://maps.google.com/mapfiles/kml/pushpin/blue-pushpin.png</href></Icon>
|
||||||
|
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels"/>
|
||||||
|
</IconStyle></Style>
|
||||||
|
<Schema id="schema">
|
||||||
|
<gx:SimpleArrayField name="speed" type="float">
|
||||||
|
<displayName><![CDATA[Speed (m/s)]]></displayName>
|
||||||
|
</gx:SimpleArrayField>
|
||||||
|
<gx:SimpleArrayField name="power" type="float">
|
||||||
|
<displayName><![CDATA[Power (W)]]></displayName>
|
||||||
|
</gx:SimpleArrayField>
|
||||||
|
<gx:SimpleArrayField name="cadence" type="float">
|
||||||
|
<displayName><![CDATA[Cadence (rpm)]]></displayName>
|
||||||
|
</gx:SimpleArrayField>
|
||||||
|
<gx:SimpleArrayField name="heart_rate" type="float">
|
||||||
|
<displayName><![CDATA[Heart rate (bpm)]]></displayName>
|
||||||
|
</gx:SimpleArrayField>
|
||||||
|
</Schema>
|
||||||
|
<Placemark>
|
||||||
|
<name><![CDATA[2020-08-24 20:57]]></name>
|
||||||
|
<description><![CDATA[]]></description>
|
||||||
|
<icon><![CDATA[UNKNOWN]]></icon>
|
||||||
|
<opentracks:trackid>9316e527-a5b8-47c5-bdbd-954b4be7d80e</opentracks:trackid>
|
||||||
|
<styleUrl>#track</styleUrl>
|
||||||
|
<ExtendedData>
|
||||||
|
<Data name="type"><value><![CDATA[unknown]]></value></Data>
|
||||||
|
</ExtendedData>
|
||||||
|
<gx:MultiTrack>
|
||||||
|
<altitudeMode>absolute</altitudeMode>
|
||||||
|
<gx:interpolate>1</gx:interpolate>
|
||||||
|
</gx:MultiTrack>
|
||||||
|
</Placemark>
|
||||||
|
</Document>
|
||||||
|
</kml>
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:opentracks="http://opentracksapp.com/xmlschemas/v1">
|
||||||
|
<Document>
|
||||||
|
<open>1</open>
|
||||||
|
<visibility>1</visibility>
|
||||||
|
<name><![CDATA[2020-11-28 18:06]]></name>
|
||||||
|
<atom:generator><![CDATA[OpenTracks (Debug)]]></atom:generator>
|
||||||
|
<Style id="track">
|
||||||
|
<LineStyle>
|
||||||
|
<color>7f0000ff</color>
|
||||||
|
<width>4</width>
|
||||||
|
</LineStyle>
|
||||||
|
<IconStyle>
|
||||||
|
<scale>1.3</scale>
|
||||||
|
<Icon>
|
||||||
|
<href>http://earth.google.com/images/kml-icons/track-directional/track-0.png</href>
|
||||||
|
</Icon>
|
||||||
|
</IconStyle>
|
||||||
|
</Style>
|
||||||
|
<Style id="start">
|
||||||
|
<IconStyle>
|
||||||
|
<scale>1.3</scale>
|
||||||
|
<Icon>
|
||||||
|
<href>http://maps.google.com/mapfiles/kml/paddle/grn-circle.png</href>
|
||||||
|
</Icon>
|
||||||
|
<hotSpot x="32" y="1" xunits="pixels" yunits="pixels" />
|
||||||
|
</IconStyle>
|
||||||
|
</Style>
|
||||||
|
<Style id="end">
|
||||||
|
<IconStyle>
|
||||||
|
<scale>1.3</scale>
|
||||||
|
<Icon>
|
||||||
|
<href>http://maps.google.com/mapfiles/kml/paddle/red-circle.png</href>
|
||||||
|
</Icon>
|
||||||
|
<hotSpot x="32" y="1" xunits="pixels" yunits="pixels" />
|
||||||
|
</IconStyle>
|
||||||
|
</Style>
|
||||||
|
<Style id="waypoint">
|
||||||
|
<IconStyle>
|
||||||
|
<scale>1.3</scale>
|
||||||
|
<Icon>
|
||||||
|
<href>http://maps.google.com/mapfiles/kml/pushpin/blue-pushpin.png</href>
|
||||||
|
</Icon>
|
||||||
|
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels" />
|
||||||
|
</IconStyle>
|
||||||
|
</Style>
|
||||||
|
<Schema id="schema">
|
||||||
|
<gx:SimpleArrayField name="speed" type="float">
|
||||||
|
<displayName><![CDATA[Speed (m/s)]]></displayName>
|
||||||
|
</gx:SimpleArrayField>
|
||||||
|
<gx:SimpleArrayField name="power" type="float">
|
||||||
|
<displayName><![CDATA[Power (W)]]></displayName>
|
||||||
|
</gx:SimpleArrayField>
|
||||||
|
<gx:SimpleArrayField name="cadence" type="float">
|
||||||
|
<displayName><![CDATA[Cadence (rpm)]]></displayName>
|
||||||
|
</gx:SimpleArrayField>
|
||||||
|
<gx:SimpleArrayField name="heart_rate" type="float">
|
||||||
|
<displayName><![CDATA[Heart rate (bpm)]]></displayName>
|
||||||
|
</gx:SimpleArrayField>
|
||||||
|
</Schema>
|
||||||
|
<Folder>
|
||||||
|
<name><![CDATA[Sun, 28 Nov 2020 18:06 Markers]]></name>
|
||||||
|
<open>1</open>
|
||||||
|
<Placemark>
|
||||||
|
<name><![CDATA[Split 0]]></name>
|
||||||
|
<description><![CDATA[Total distance: 0.00 km (0.0 mi)
|
||||||
|
Total time: 00:00
|
||||||
|
Moving time: 00:00
|
||||||
|
Average speed: 0.00 km/h (0.0 mi/h)
|
||||||
|
Average moving speed: 0.00 km/h (0.0 mi/h)
|
||||||
|
Max speed: 0.00 km/h (0.0 mi/h)
|
||||||
|
Average pace: 0:00 min/km (0:00 min/mi)
|
||||||
|
Average moving pace: 0:00 min/km (0:00 min/mi)
|
||||||
|
Fastest pace: 0:00 min/km (0:00 min/mi)
|
||||||
|
Max elevation: -9223372036854775808 m (-9223372036854775808 ft)
|
||||||
|
Min elevation: 9223372036854775807 m (9223372036854775807 ft)
|
||||||
|
Elevation gain: 0 m (0 ft)
|
||||||
|
Max grade: 0 %
|
||||||
|
Min grade: 0 %
|
||||||
|
Recorded: 6/23 11:29]]></description>
|
||||||
|
<TimeStamp>
|
||||||
|
<when>1970-01-01T00:00:00Z</when>
|
||||||
|
</TimeStamp>
|
||||||
|
<styleUrl>#waypoint</styleUrl>
|
||||||
|
<Point>
|
||||||
|
<coordinates>180.0,100.0</coordinates>
|
||||||
|
</Point>
|
||||||
|
</Placemark>
|
||||||
|
</Folder>
|
||||||
|
<Placemark>
|
||||||
|
<name><![CDATA[2020-11-28 18:06 (Start)]]></name>
|
||||||
|
<description />
|
||||||
|
<TimeStamp>
|
||||||
|
<when>2020-11-28T17:06:22.401Z</when>
|
||||||
|
</TimeStamp>
|
||||||
|
<styleUrl>#start</styleUrl>
|
||||||
|
<Point>
|
||||||
|
<coordinates>1.234156,12.340097,469.286376953125</coordinates>
|
||||||
|
</Point>
|
||||||
|
</Placemark>
|
||||||
|
<Placemark>
|
||||||
|
<name><![CDATA[2020-11-28 18:06]]></name>
|
||||||
|
<description><![CDATA[Test Track]]></description>
|
||||||
|
<icon><![CDATA[UNKNOWN]]></icon>
|
||||||
|
<opentracks:trackid>2f170875-55d7-445d-b2b8-22a573c81dbc</opentracks:trackid>
|
||||||
|
<styleUrl>#track</styleUrl>
|
||||||
|
<ExtendedData>
|
||||||
|
<Data name="type">
|
||||||
|
<value><![CDATA[unknown]]></value>
|
||||||
|
</Data>
|
||||||
|
</ExtendedData>
|
||||||
|
<gx:MultiTrack>
|
||||||
|
<altitudeMode>absolute</altitudeMode>
|
||||||
|
<gx:interpolate>1</gx:interpolate>
|
||||||
|
<gx:Track>
|
||||||
|
<when>2020-11-28T17:06:22.401Z</when>
|
||||||
|
<gx:coord>1.234156 12.340097 469.286376953125</gx:coord>
|
||||||
|
<when>2020-11-28T17:06:25.448Z</when>
|
||||||
|
<gx:coord>1.23415 12.340036 439.1626281738281</gx:coord>
|
||||||
|
<when>2020-11-28T17:06:47.888Z</when>
|
||||||
|
<gx:coord>1.23405 12.340057 421.8070983886719</gx:coord>
|
||||||
|
<ExtendedData>
|
||||||
|
<SchemaData schemaUrl="#schema">
|
||||||
|
<gx:SimpleArrayData name="speed">
|
||||||
|
<gx:value>0.5391469</gx:value>
|
||||||
|
<gx:value>0.15777911</gx:value>
|
||||||
|
<gx:value>0.0</gx:value>
|
||||||
|
</gx:SimpleArrayData>
|
||||||
|
<gx:SimpleArrayData name="elevation_gain">
|
||||||
|
<gx:value>0.0</gx:value>
|
||||||
|
<gx:value>0.0</gx:value>
|
||||||
|
<gx:value>0.0</gx:value>
|
||||||
|
</gx:SimpleArrayData>
|
||||||
|
</SchemaData>
|
||||||
|
</ExtendedData>
|
||||||
|
</gx:Track>
|
||||||
|
<gx:Track>
|
||||||
|
<when>2020-11-28T17:06:55.861Z</when>
|
||||||
|
<gx:coord>1.23405 12.340057 419.93902587890625</gx:coord>
|
||||||
|
<when>2020-11-28T17:06:56.905Z</when>
|
||||||
|
<gx:coord>1.23405 12.340057 419.9036560058594</gx:coord>
|
||||||
|
<when>2020-11-28T17:07:20.870Z</when>
|
||||||
|
<gx:coord>1.234046 12.340082 417.99432373046875</gx:coord>
|
||||||
|
<ExtendedData>
|
||||||
|
<SchemaData schemaUrl="#schema">
|
||||||
|
<gx:SimpleArrayData name="speed">
|
||||||
|
<gx:value>0.0</gx:value>
|
||||||
|
<gx:value>0.0</gx:value>
|
||||||
|
<gx:value>0.0</gx:value>
|
||||||
|
</gx:SimpleArrayData>
|
||||||
|
<gx:SimpleArrayData name="elevation_gain">
|
||||||
|
<gx:value>0.0</gx:value>
|
||||||
|
<gx:value>0.0</gx:value>
|
||||||
|
<gx:value>0.0</gx:value>
|
||||||
|
<gx:value>0.0</gx:value>
|
||||||
|
<gx:value>0.0</gx:value>
|
||||||
|
<gx:value>0.0</gx:value>
|
||||||
|
</gx:SimpleArrayData>
|
||||||
|
</SchemaData>
|
||||||
|
</ExtendedData>
|
||||||
|
</gx:Track>
|
||||||
|
</gx:MultiTrack>
|
||||||
|
</Placemark>
|
||||||
|
<Placemark>
|
||||||
|
<name><![CDATA[2020-11-28 18:06 (End)]]></name>
|
||||||
|
<description><![CDATA[OpenTracks (Debug)
|
||||||
|
|
||||||
|
Name: 2020-11-28 18:06
|
||||||
|
Activity type: unknown
|
||||||
|
Description: -
|
||||||
|
Total distance: 0.01 km (0.0 mi)
|
||||||
|
Total time: 01:04
|
||||||
|
Moving time: 00:00
|
||||||
|
Average speed: 0.49 km/h (0.3 mi/h)
|
||||||
|
Average moving speed: 0.00 km/h (0.0 mi/h)
|
||||||
|
Max speed: 0.00 km/h (0.0 mi/h)
|
||||||
|
Average pace: 122:30 min/km (197:08 min/mi)
|
||||||
|
Average moving pace: 0:00 min/km (0:00 min/mi)
|
||||||
|
Fastest pace: 0:00 min/km (0:00 min/mi)
|
||||||
|
Max elevation: 469 m (1540 ft)
|
||||||
|
Min elevation: 419 m (1376 ft)
|
||||||
|
Elevation gain: 0 m (0 ft)
|
||||||
|
Recorded: 11/28 18:06]]></description>
|
||||||
|
<TimeStamp>
|
||||||
|
<when>2020-11-28T17:07:20.870Z</when>
|
||||||
|
</TimeStamp>
|
||||||
|
<styleUrl>#end</styleUrl>
|
||||||
|
<Point>
|
||||||
|
<coordinates>1.234046,12.340082,417.99432373046875</coordinates>
|
||||||
|
</Point>
|
||||||
|
</Placemark>
|
||||||
|
</Document>
|
||||||
|
</kml>
|
||||||
@@ -1,173 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<kml xmlns="http://www.opengis.net/kml/2.2"
|
|
||||||
xmlns:gx="http://www.google.com/kml/ext/2.2"
|
|
||||||
xmlns:atom="http://www.w3.org/2005/Atom"
|
|
||||||
xmlns:opentracks="http://opentracksapp.com/xmlschemas/v1">
|
|
||||||
<Document>
|
|
||||||
<open>1</open>
|
|
||||||
<visibility>1</visibility>
|
|
||||||
<name><![CDATA[2020-11-28 18:06]]></name>
|
|
||||||
<atom:generator><![CDATA[OpenTracks (Debug)]]></atom:generator>
|
|
||||||
<Style id="track">
|
|
||||||
<LineStyle><color>7f0000ff</color><width>4</width></LineStyle>
|
|
||||||
<IconStyle>
|
|
||||||
<scale>1.3</scale>
|
|
||||||
<Icon><href>http://earth.google.com/images/kml-icons/track-directional/track-0.png</href></Icon>
|
|
||||||
</IconStyle>
|
|
||||||
</Style>
|
|
||||||
<Style id="start"><IconStyle>
|
|
||||||
<scale>1.3</scale>
|
|
||||||
<Icon><href>http://maps.google.com/mapfiles/kml/paddle/grn-circle.png</href></Icon>
|
|
||||||
<hotSpot x="32" y="1" xunits="pixels" yunits="pixels"/>
|
|
||||||
</IconStyle></Style>
|
|
||||||
<Style id="end"><IconStyle>
|
|
||||||
<scale>1.3</scale>
|
|
||||||
<Icon><href>http://maps.google.com/mapfiles/kml/paddle/red-circle.png</href></Icon>
|
|
||||||
<hotSpot x="32" y="1" xunits="pixels" yunits="pixels"/>
|
|
||||||
</IconStyle></Style>
|
|
||||||
<Style id="waypoint"><IconStyle>
|
|
||||||
<scale>1.3</scale>
|
|
||||||
<Icon><href>http://maps.google.com/mapfiles/kml/pushpin/blue-pushpin.png</href></Icon>
|
|
||||||
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels"/>
|
|
||||||
</IconStyle></Style>
|
|
||||||
<Schema id="schema">
|
|
||||||
<gx:SimpleArrayField name="speed" type="float">
|
|
||||||
<displayName><![CDATA[Speed (m/s)]]></displayName>
|
|
||||||
</gx:SimpleArrayField>
|
|
||||||
<gx:SimpleArrayField name="power" type="float">
|
|
||||||
<displayName><![CDATA[Power (W)]]></displayName>
|
|
||||||
</gx:SimpleArrayField>
|
|
||||||
<gx:SimpleArrayField name="cadence" type="float">
|
|
||||||
<displayName><![CDATA[Cadence (rpm)]]></displayName>
|
|
||||||
</gx:SimpleArrayField>
|
|
||||||
<gx:SimpleArrayField name="heart_rate" type="float">
|
|
||||||
<displayName><![CDATA[Heart rate (bpm)]]></displayName>
|
|
||||||
</gx:SimpleArrayField>
|
|
||||||
</Schema>
|
|
||||||
<Folder>
|
|
||||||
<name><![CDATA[Sun, 28 Nov 2020 18:06 Markers]]></name>
|
|
||||||
<open>1</open>
|
|
||||||
<Placemark>
|
|
||||||
<name><![CDATA[Split 0]]></name>
|
|
||||||
<description><![CDATA[Total distance: 0.00 km (0.0 mi)
|
|
||||||
Total time: 00:00
|
|
||||||
Moving time: 00:00
|
|
||||||
Average speed: 0.00 km/h (0.0 mi/h)
|
|
||||||
Average moving speed: 0.00 km/h (0.0 mi/h)
|
|
||||||
Max speed: 0.00 km/h (0.0 mi/h)
|
|
||||||
Average pace: 0:00 min/km (0:00 min/mi)
|
|
||||||
Average moving pace: 0:00 min/km (0:00 min/mi)
|
|
||||||
Fastest pace: 0:00 min/km (0:00 min/mi)
|
|
||||||
Max elevation: -9223372036854775808 m (-9223372036854775808 ft)
|
|
||||||
Min elevation: 9223372036854775807 m (9223372036854775807 ft)
|
|
||||||
Elevation gain: 0 m (0 ft)
|
|
||||||
Max grade: 0 %
|
|
||||||
Min grade: 0 %
|
|
||||||
Recorded: 6/23 11:29
|
|
||||||
]]></description>
|
|
||||||
<TimeStamp><when>1970-01-01T00:00:00Z</when></TimeStamp>
|
|
||||||
<styleUrl>#waypoint</styleUrl>
|
|
||||||
<Point>
|
|
||||||
<coordinates>180.0,100.0</coordinates>
|
|
||||||
</Point>
|
|
||||||
</Placemark>
|
|
||||||
</Folder>
|
|
||||||
<Placemark>
|
|
||||||
<name><![CDATA[2020-11-28 18:06 (Start)]]></name>
|
|
||||||
<description><![CDATA[]]></description>
|
|
||||||
<TimeStamp><when>2020-11-28T17:06:22.401Z</when></TimeStamp>
|
|
||||||
<styleUrl>#start</styleUrl>
|
|
||||||
<Point>
|
|
||||||
<coordinates>1.234156,12.340097,469.286376953125</coordinates>
|
|
||||||
</Point>
|
|
||||||
</Placemark>
|
|
||||||
<Placemark>
|
|
||||||
<name><![CDATA[2020-11-28 18:06]]></name>
|
|
||||||
<description><![CDATA[Test Track]]></description>
|
|
||||||
<icon><![CDATA[UNKNOWN]]></icon>
|
|
||||||
<opentracks:trackid>2f170875-55d7-445d-b2b8-22a573c81dbc</opentracks:trackid>
|
|
||||||
<styleUrl>#track</styleUrl>
|
|
||||||
<ExtendedData>
|
|
||||||
<Data name="type"><value><![CDATA[unknown]]></value></Data>
|
|
||||||
</ExtendedData>
|
|
||||||
<gx:MultiTrack>
|
|
||||||
<altitudeMode>absolute</altitudeMode>
|
|
||||||
<gx:interpolate>1</gx:interpolate>
|
|
||||||
<gx:Track>
|
|
||||||
<when>2020-11-28T17:06:22.401Z</when>
|
|
||||||
<gx:coord>1.234156 12.340097 469.286376953125</gx:coord>
|
|
||||||
<when>2020-11-28T17:06:25.448Z</when>
|
|
||||||
<gx:coord>1.23415 12.340036 439.1626281738281</gx:coord>
|
|
||||||
<when>2020-11-28T17:06:47.888Z</when>
|
|
||||||
<gx:coord>1.23405 12.340057 421.8070983886719</gx:coord>
|
|
||||||
<ExtendedData>
|
|
||||||
<SchemaData schemaUrl="#schema">
|
|
||||||
<gx:SimpleArrayData name="speed">
|
|
||||||
<gx:value>0.5391469</gx:value>
|
|
||||||
<gx:value>0.15777911</gx:value>
|
|
||||||
<gx:value>0.0</gx:value>
|
|
||||||
</gx:SimpleArrayData>
|
|
||||||
<gx:SimpleArrayData name="elevation_gain">
|
|
||||||
<gx:value>0.0</gx:value>
|
|
||||||
<gx:value>0.0</gx:value>
|
|
||||||
<gx:value>0.0</gx:value>
|
|
||||||
</gx:SimpleArrayData>
|
|
||||||
</SchemaData>
|
|
||||||
</ExtendedData>
|
|
||||||
</gx:Track>
|
|
||||||
<gx:Track>
|
|
||||||
<when>2020-11-28T17:06:55.861Z</when>
|
|
||||||
<gx:coord>1.23405 12.340057 419.93902587890625</gx:coord>
|
|
||||||
<when>2020-11-28T17:06:56.905Z</when>
|
|
||||||
<gx:coord>1.23405 12.340057 419.9036560058594</gx:coord>
|
|
||||||
<when>2020-11-28T17:07:20.870Z</when>
|
|
||||||
<gx:coord>1.234046 12.340082 417.99432373046875</gx:coord>
|
|
||||||
<ExtendedData>
|
|
||||||
<SchemaData schemaUrl="#schema">
|
|
||||||
<gx:SimpleArrayData name="speed">
|
|
||||||
<gx:value>0.0</gx:value>
|
|
||||||
<gx:value>0.0</gx:value>
|
|
||||||
<gx:value>0.0</gx:value>
|
|
||||||
</gx:SimpleArrayData>
|
|
||||||
<gx:SimpleArrayData name="elevation_gain">
|
|
||||||
<gx:value>0.0</gx:value>
|
|
||||||
<gx:value>0.0</gx:value>
|
|
||||||
<gx:value>0.0</gx:value>
|
|
||||||
<gx:value>0.0</gx:value>
|
|
||||||
<gx:value>0.0</gx:value>
|
|
||||||
<gx:value>0.0</gx:value>
|
|
||||||
</gx:SimpleArrayData>
|
|
||||||
</SchemaData>
|
|
||||||
</ExtendedData>
|
|
||||||
</gx:Track>
|
|
||||||
</gx:MultiTrack>
|
|
||||||
</Placemark>
|
|
||||||
<Placemark>
|
|
||||||
<name><![CDATA[2020-11-28 18:06 (End)]]></name>
|
|
||||||
<description><![CDATA[OpenTracks (Debug)
|
|
||||||
|
|
||||||
Name: 2020-11-28 18:06
|
|
||||||
Activity type: unknown
|
|
||||||
Description: -
|
|
||||||
Total distance: 0.01 km (0.0 mi)
|
|
||||||
Total time: 01:04
|
|
||||||
Moving time: 00:00
|
|
||||||
Average speed: 0.49 km/h (0.3 mi/h)
|
|
||||||
Average moving speed: 0.00 km/h (0.0 mi/h)
|
|
||||||
Max speed: 0.00 km/h (0.0 mi/h)
|
|
||||||
Average pace: 122:30 min/km (197:08 min/mi)
|
|
||||||
Average moving pace: 0:00 min/km (0:00 min/mi)
|
|
||||||
Fastest pace: 0:00 min/km (0:00 min/mi)
|
|
||||||
Max elevation: 469 m (1540 ft)
|
|
||||||
Min elevation: 419 m (1376 ft)
|
|
||||||
Elevation gain: 0 m (0 ft)
|
|
||||||
Recorded: 11/28 18:06
|
|
||||||
]]></description>
|
|
||||||
<TimeStamp><when>2020-11-28T17:07:20.870Z</when></TimeStamp>
|
|
||||||
<styleUrl>#end</styleUrl>
|
|
||||||
<Point>
|
|
||||||
<coordinates>1.234046,12.340082,417.99432373046875</coordinates>
|
|
||||||
</Point>
|
|
||||||
</Placemark>
|
|
||||||
</Document>
|
|
||||||
</kml>
|
|
||||||
@@ -14,5 +14,5 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">OpenTracks (Debug)</string>
|
<string name="app_name" translatable="false">OpenTracks (Debug)</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ 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
|
||||||
@@ -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)));
|
||||||
|
|||||||
@@ -84,11 +84,9 @@ public class MarkerEditActivity extends AbstractActivity {
|
|||||||
// Setup UI elements
|
// Setup UI elements
|
||||||
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.marker_types, android.R.layout.simple_dropdown_item_1line);
|
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.marker_types, android.R.layout.simple_dropdown_item_1line);
|
||||||
viewBinding.markerEditMarkerType.setAdapter(adapter);
|
viewBinding.markerEditMarkerType.setAdapter(adapter);
|
||||||
viewBinding.markerEditPhotoDelete.setOnClickListener(v -> {
|
viewBinding.markerEditPhotoDelete.setOnClickListener(v -> viewModel.onPhotoDelete(viewBinding.markerEditName.getText().toString(),
|
||||||
viewModel.onPhotoDelete(viewBinding.markerEditName.getText().toString(),
|
viewBinding.markerEditMarkerType.getText().toString(),
|
||||||
viewBinding.markerEditMarkerType.getText().toString(),
|
viewBinding.markerEditDescription.getText().toString()));
|
||||||
viewBinding.markerEditDescription.getText().toString());
|
|
||||||
});
|
|
||||||
|
|
||||||
viewBinding.markerEditCancel.setOnClickListener(v -> {
|
viewBinding.markerEditCancel.setOnClickListener(v -> {
|
||||||
viewModel.onCancel();
|
viewModel.onCancel();
|
||||||
|
|||||||
@@ -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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,12 +24,13 @@ import android.view.MotionEvent;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.accessibility.AccessibilityEvent;
|
import android.view.accessibility.AccessibilityEvent;
|
||||||
|
|
||||||
|
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.
|
||||||
@@ -40,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();
|
||||||
@@ -52,9 +55,8 @@ public class TrackController implements View.OnTouchListener {
|
|||||||
private final TransitionDrawable transition;
|
private final TransitionDrawable transition;
|
||||||
|
|
||||||
private boolean isRecording;
|
private boolean isRecording;
|
||||||
private boolean isPaused;
|
private boolean isRecordingPaused;
|
||||||
private long totalTime = 0;
|
private Duration totalTime;
|
||||||
private long totalTimeTimestamp = 0;
|
|
||||||
private boolean isResumed = false;
|
private boolean isResumed = false;
|
||||||
|
|
||||||
private final Callback callback;
|
private final Callback callback;
|
||||||
@@ -62,9 +64,10 @@ public class TrackController implements View.OnTouchListener {
|
|||||||
// A runnable to update the total time.
|
// A runnable to update the total time.
|
||||||
private final Runnable updateTotalTimeRunnable = new Runnable() {
|
private final Runnable updateTotalTimeRunnable = new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
if (isResumed && isRecording && !isPaused) {
|
if (isResumed && isRecording && !isRecordingPaused) {
|
||||||
viewBinding.trackControllerTotalTime.setText(StringUtils.formatElapsedTimeWithHour(System.currentTimeMillis() - totalTimeTimestamp + totalTime));
|
updateTotalTime();
|
||||||
handlerUpdateTotalTime.postDelayed(this, UnitConversions.ONE_SECOND_MS);
|
setTotalTime();
|
||||||
|
handlerUpdateTotalTime.postDelayed(this, UI_UPDATE_INTERVAL.toMillis());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -81,7 +84,7 @@ public class TrackController implements View.OnTouchListener {
|
|||||||
|
|
||||||
viewBinding.trackControllerRecord.setOnTouchListener(this);
|
viewBinding.trackControllerRecord.setOnTouchListener(this);
|
||||||
viewBinding.trackControllerRecord.setOnClickListener((view) -> {
|
viewBinding.trackControllerRecord.setOnClickListener((view) -> {
|
||||||
if (buttonDelay != null || (isRecording && !isPaused)) {
|
if (buttonDelay != null || (isRecording && !isRecordingPaused)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +120,7 @@ public class TrackController implements View.OnTouchListener {
|
|||||||
//To pause a recording
|
//To pause a recording
|
||||||
if (viewBinding.trackControllerRecord.equals(view)
|
if (viewBinding.trackControllerRecord.equals(view)
|
||||||
&& event.getAction() == MotionEvent.ACTION_DOWN
|
&& event.getAction() == MotionEvent.ACTION_DOWN
|
||||||
&& isRecording && !isPaused) {
|
&& isRecording && !isRecordingPaused) {
|
||||||
|
|
||||||
transition.startTransition(buttonDelayDuration);
|
transition.startTransition(buttonDelayDuration);
|
||||||
|
|
||||||
@@ -127,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();
|
||||||
|
|
||||||
@@ -164,10 +167,11 @@ public class TrackController implements View.OnTouchListener {
|
|||||||
|
|
||||||
public void update(boolean recording, boolean paused) {
|
public void update(boolean recording, boolean paused) {
|
||||||
if (!isResumed) {
|
if (!isResumed) {
|
||||||
|
handlerUpdateTotalTime.removeCallbacks(updateTotalTimeRunnable);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
isRecording = recording;
|
isRecording = recording;
|
||||||
isPaused = paused;
|
isRecordingPaused = paused;
|
||||||
boolean visible = alwaysShow || isRecording;
|
boolean visible = alwaysShow || isRecording;
|
||||||
viewBinding.trackControllerContainer.setVisibility(visible ? View.VISIBLE : View.GONE);
|
viewBinding.trackControllerContainer.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
@@ -176,26 +180,26 @@ public class TrackController implements View.OnTouchListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
viewBinding.trackControllerRecord.setImageResource(isRecording && !isPaused ? R.drawable.ic_button_pause : R.drawable.button_record);
|
if (isRecording && !isRecordingPaused) {
|
||||||
viewBinding.trackControllerRecord.setContentDescription(activity.getString(isRecording && !isPaused ? R.string.image_pause : R.string.image_record));
|
handlerUpdateTotalTime.postDelayed(updateTotalTimeRunnable, UI_UPDATE_INTERVAL.toMillis());
|
||||||
|
}
|
||||||
|
|
||||||
|
viewBinding.trackControllerRecord.setImageResource(isRecording && !isRecordingPaused ? R.drawable.ic_button_pause : R.drawable.button_record);
|
||||||
|
viewBinding.trackControllerRecord.setContentDescription(activity.getString(isRecording && !isRecordingPaused ? R.string.image_pause : R.string.image_record));
|
||||||
|
|
||||||
viewBinding.trackControllerStop.setEnabled(isRecording);
|
viewBinding.trackControllerStop.setEnabled(isRecording);
|
||||||
|
|
||||||
viewBinding.trackControllerStatus.setVisibility(isRecording ? View.VISIBLE : View.INVISIBLE);
|
viewBinding.trackControllerStatus.setVisibility(isRecording ? View.VISIBLE : View.INVISIBLE);
|
||||||
if (isRecording) {
|
if (isRecording) {
|
||||||
viewBinding.trackControllerStatus.setTextColor(activity.getResources().getColor(isPaused ? android.R.color.white : R.color.recording_text));
|
viewBinding.trackControllerStatus.setTextColor(activity.getResources().getColor(isRecordingPaused ? android.R.color.white : R.color.recording_text));
|
||||||
showStatusSetDefaultText();
|
showStatusSetDefaultText();
|
||||||
}
|
}
|
||||||
|
|
||||||
handlerUpdateTotalTime.removeCallbacks(updateTotalTimeRunnable);
|
|
||||||
viewBinding.trackControllerTotalTime.setVisibility(isRecording ? View.VISIBLE : View.INVISIBLE);
|
viewBinding.trackControllerTotalTime.setVisibility(isRecording ? View.VISIBLE : View.INVISIBLE);
|
||||||
|
|
||||||
if (isRecording) {
|
if (isRecording) {
|
||||||
totalTime = getTotalTime();
|
updateTotalTime();
|
||||||
viewBinding.trackControllerTotalTime.setText(StringUtils.formatElapsedTimeWithHour(totalTime));
|
setTotalTime();
|
||||||
if (!isPaused) {
|
|
||||||
totalTimeTimestamp = System.currentTimeMillis();
|
|
||||||
handlerUpdateTotalTime.postDelayed(updateTotalTimeRunnable, UnitConversions.ONE_SECOND_MS);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,21 +221,26 @@ public class TrackController implements View.OnTouchListener {
|
|||||||
viewBinding.trackControllerContainer.setVisibility(View.VISIBLE);
|
viewBinding.trackControllerContainer.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void updateTotalTime() {
|
||||||
* Gets the total time for the current recording track.
|
TrackRecordingService trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
||||||
*/
|
if (trackRecordingService != null) {
|
||||||
private long getTotalTime() {
|
totalTime = trackRecordingService.getTotalTime();
|
||||||
TrackRecordingServiceInterface trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
}
|
||||||
return trackRecordingService != null ? trackRecordingService.getTotalTime() : 0L;
|
}
|
||||||
|
|
||||||
|
private void setTotalTime() {
|
||||||
|
if (totalTime != null) {
|
||||||
|
viewBinding.trackControllerTotalTime.setText(StringUtils.formatElapsedTimeWithHour(totalTime));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showStatusSetDefaultText() {
|
private void showStatusSetDefaultText() {
|
||||||
viewBinding.trackControllerStatus.setText(isPaused ? R.string.generic_paused : R.string.generic_recording);
|
viewBinding.trackControllerStatus.setText(isRecordingPaused ? R.string.generic_paused : R.string.generic_recording);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface Callback {
|
public interface Callback {
|
||||||
void recordStart();
|
void recordStart();
|
||||||
|
void recordPause();
|
||||||
void recordStop();
|
void recordStop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -44,12 +46,16 @@ import androidx.loader.app.LoaderManager;
|
|||||||
import androidx.loader.content.CursorLoader;
|
import androidx.loader.content.CursorLoader;
|
||||||
import androidx.loader.content.Loader;
|
import androidx.loader.content.Loader;
|
||||||
|
|
||||||
|
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;
|
||||||
@@ -65,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();
|
||||||
|
|
||||||
@@ -82,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() {
|
||||||
@@ -101,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);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,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;
|
||||||
@@ -151,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());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -177,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);
|
||||||
@@ -193,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) {
|
||||||
@@ -224,8 +215,8 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
|||||||
String icon = cursor.getString(iconIndex);
|
String icon = cursor.getString(iconIndex);
|
||||||
int iconId = TrackIconUtils.getIconDrawable(icon);
|
int iconId = TrackIconUtils.getIconDrawable(icon);
|
||||||
String name = cursor.getString(nameIndex);
|
String name = cursor.getString(nameIndex);
|
||||||
String totalTime = StringUtils.formatElapsedTime(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);
|
||||||
@@ -255,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,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
|
||||||
@@ -315,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);
|
||||||
@@ -337,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);
|
||||||
}
|
}
|
||||||
@@ -565,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);
|
||||||
@@ -575,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,12 @@ 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 {
|
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 +72,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 +100,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 +122,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 +161,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 +268,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 +298,14 @@ public class TrackRecordedActivity extends AbstractListActivity implements Confi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void startPostponedEnterTransitionWith(View viewIcon, View viewName) {
|
||||||
|
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();
|
||||||
@@ -92,31 +90,23 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
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 +135,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 +158,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 +170,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 +180,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 +194,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();
|
||||||
@@ -345,22 +333,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 +369,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 +399,22 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTrackRecordingPaused(boolean isPaused) {
|
||||||
|
if (recordingTrackPaused != isPaused) {
|
||||||
|
trackController.update(true, isPaused);
|
||||||
|
}
|
||||||
|
recordingTrackPaused = isPaused;
|
||||||
|
setLockscreenPolicy();
|
||||||
|
setScreenOnPolicy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTrackRecordingId(Track.Id newTrackId) {
|
||||||
|
if (newTrackId != null && !newTrackId.equals(trackId)) {
|
||||||
|
trackId = newTrackId;
|
||||||
|
trackController.update(true, recordingTrackPaused);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ import java.util.concurrent.Future;
|
|||||||
*/
|
*/
|
||||||
class ExecutorListViewService<T> {
|
class ExecutorListViewService<T> {
|
||||||
|
|
||||||
private List<T> enqueueObjects = new ArrayList<>();
|
private final List<T> enqueueObjects = new ArrayList<>();
|
||||||
private ExecutorService executorService;
|
private final ExecutorService executorService;
|
||||||
|
|
||||||
public ExecutorListViewService(int numThreads) {
|
public ExecutorListViewService(int numThreads) {
|
||||||
executorService = Executors.newFixedThreadPool(numThreads);
|
executorService = Executors.newFixedThreadPool(numThreads);
|
||||||
@@ -23,7 +23,6 @@ class ExecutorListViewService<T> {
|
|||||||
|
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
enqueueObjects.clear();
|
enqueueObjects.clear();
|
||||||
enqueueObjects = null;
|
|
||||||
executorService.shutdown();
|
executorService.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ public class MarkerResourceCursorAdapter extends ResourceCursorAdapter implement
|
|||||||
|
|
||||||
private final Activity activity;
|
private final Activity activity;
|
||||||
|
|
||||||
private ExecutorListViewService executorService = new ExecutorListViewService<Long>(LIST_PREFERRED_ITEM_HEIGHT_DEFAULT);
|
//TODO Should be Marker.Id
|
||||||
|
private final ExecutorListViewService<Long> executorService = new ExecutorListViewService<>(LIST_PREFERRED_ITEM_HEIGHT_DEFAULT);
|
||||||
|
|
||||||
private boolean scroll = false;
|
private boolean scroll = false;
|
||||||
|
|
||||||
@@ -111,8 +112,8 @@ public class MarkerResourceCursorAdapter extends ResourceCursorAdapter implement
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
long id = cursor.getLong(cursor.getColumnIndex(MarkerColumns._ID));
|
long id = cursor.getLong(cursor.getColumnIndexOrThrow(MarkerColumns._ID));
|
||||||
String photoUrl = cursor.getString(cursor.getColumnIndex(MarkerColumns.PHOTOURL));
|
String photoUrl = cursor.getString(cursor.getColumnIndexOrThrow(MarkerColumns.PHOTOURL));
|
||||||
|
|
||||||
boolean hasPhoto = photoUrl != null && !photoUrl.equals("");
|
boolean hasPhoto = photoUrl != null && !photoUrl.equals("");
|
||||||
if (hasPhoto) {
|
if (hasPhoto) {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ public class ScrollVisibleViews implements AbsListView.OnScrollListener {
|
|||||||
private int from = -1;
|
private int from = -1;
|
||||||
private int to = -1;
|
private int to = -1;
|
||||||
|
|
||||||
private VisibleViewsListener visibleViewsListener;
|
private final VisibleViewsListener visibleViewsListener;
|
||||||
|
|
||||||
public ScrollVisibleViews(@NonNull VisibleViewsListener visibleViewsListener) {
|
public ScrollVisibleViews(@NonNull VisibleViewsListener visibleViewsListener) {
|
||||||
this.visibleViewsListener = visibleViewsListener;
|
this.visibleViewsListener = visibleViewsListener;
|
||||||
|
|||||||