forked from upstream-mirrors/OpenTracks
Compare commits
76 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 374656aa6b | |||
| c623475c95 | |||
| 316e21a2c4 | |||
| 9dedfb38bc | |||
| eb7552acdb | |||
| 79729cd4a7 | |||
| 12c2c7cae0 | |||
| 504e27a9df | |||
| 5fbc079d96 | |||
| 54fb70053c | |||
| ea7d754482 | |||
| 1647b821e6 | |||
| 5ced5e2c0b | |||
| 31ea1348de | |||
| 3995e2b609 | |||
| eddf6ffc04 | |||
| 88b860af5a | |||
| 1708e09c5a | |||
| 07abf50fca | |||
| afbe94195c | |||
| 8ca208a631 | |||
| 7b6120abf4 | |||
| 2c9726f643 | |||
| b3d610b791 | |||
| 409ddfd31c | |||
| 2d47f1895b | |||
| 1d89d68e59 | |||
| 3ebce4be7c | |||
| a7954382fd | |||
| 1d036a096a | |||
| d952593687 | |||
| 83445cfde0 | |||
| aae1a4d65d | |||
| cb3904ee0b | |||
| 684756c264 | |||
| bc857faa18 | |||
| e9b02df631 | |||
| 648ebadb36 | |||
| fda9ef9dd7 | |||
| 2b08eb3287 | |||
| f4cec0a8b2 | |||
| b5e88b3e05 | |||
| 83da4f8f34 | |||
| 9a50d7bedf | |||
| 348cb16256 | |||
| 7d739e3640 | |||
| 78d2a0722a | |||
| 00debe2eb5 | |||
| 6be9d2477c | |||
| 60fd1cd105 | |||
| b360191055 | |||
| 8a20edb83a | |||
| 165dd401f4 | |||
| 10e65035ee | |||
| f797ce0872 | |||
| 4425fbae90 | |||
| f2c1d08f60 | |||
| 481c77679b | |||
| 1029460e64 | |||
| b250d95e41 | |||
| bd99daddd4 | |||
| b3af0b3b5f | |||
| a151b78da7 | |||
| 3f760c28da | |||
| 50d20f297e | |||
| f643d86772 | |||
| a6f5635cb8 | |||
| bebf89608c | |||
| 0cf02082d3 | |||
| e18017d500 | |||
| 7776b51f05 | |||
| 30c76de136 | |||
| 853469cb28 | |||
| cf9b432224 | |||
| e130039118 | |||
| adb491ee81 |
@@ -8,7 +8,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: actions/setup-java@v3
|
- uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: actions/setup-java@v3
|
- uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
# We have flaky test results due to performance limitations in Github Actions.
|
|
||||||
# Happens too often nowadays; See https://github.com/OpenTracksApp/OpenTracks/issues/1409
|
|
||||||
|
|
||||||
#https://github.com/marketplace/actions/android-emulator-runner
|
#https://github.com/marketplace/actions/android-emulator-runner
|
||||||
name: Test
|
name: Test
|
||||||
on:
|
on:
|
||||||
@@ -11,27 +8,20 @@ jobs:
|
|||||||
test:
|
test:
|
||||||
timeout-minutes: 45
|
timeout-minutes: 45
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
api-level: [ 24, 30 ]
|
|
||||||
target: [ default ]
|
|
||||||
fail-fast: false
|
|
||||||
steps:
|
steps:
|
||||||
- name: checkout
|
- name: checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: actions/setup-java@v3
|
- uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: 'zulu'
|
distribution: 'adopt'
|
||||||
java-version: '11'
|
java-version: '17'
|
||||||
|
|
||||||
- name: run tests
|
- name: run tests
|
||||||
uses: reactivecircus/android-emulator-runner@v2.26.0
|
uses: reactivecircus/android-emulator-runner@v2.28.0
|
||||||
with:
|
with:
|
||||||
api-level: ${{ matrix.api-level }}
|
|
||||||
target: ${{ matrix.target }}
|
|
||||||
arch: x86_64
|
arch: x86_64
|
||||||
profile: Nexus 6
|
api-level: 34
|
||||||
script: ./gradlew connectedCheck
|
script: ./gradlew connectedCheck
|
||||||
|
|
||||||
- name: Archive
|
- name: Archive
|
||||||
@@ -125,14 +125,14 @@ For testing via adb: `adb shell am start -e someParameter someValue -n "package/
|
|||||||
The Public API is disabled by default to protect the user's privacy, but it can easily be enabled in the settings.
|
The Public API is disabled by default to protect the user's privacy, but it can easily be enabled in the settings.
|
||||||
|
|
||||||
## File formats compatibility with open-source software
|
## File formats compatibility with open-source software
|
||||||
|
| | [GPX 1.1](https://www.topografix.com/GPX/1/1/) | [KML 2.3](https://docs.opengeospatial.org/is/12-007r2/12-007r2.html) | [KMZ 2.3](https://docs.opengeospatial.org/is/12-007r2/12-007r2.html) |
|
||||||
| | [GPX 1.1](https://www.topografix.com/GPX/1/1/) | [KML 2.3](https://docs.opengeospatial.org/is/12-007r2/12-007r2.html) | [KMZ 2.3](https://docs.opengeospatial.org/is/12-007r2/12-007r2.html) |
|
|-----------------------------------------------------------|--------------------------------------------------------------------------------|----------------------------------------------------------------------|----------------------------------------------------------------------|
|
||||||
|------------------------------------------------------|------------------------------------------------|----------------------------------------------------------------------|----------------------------------------------------------------------|
|
| [OpenLayers 7.1.0](https://openlayers.org/) | ? | [no](https://github.com/openlayers/openlayers/issues/14104) | [no](https://github.com/openlayers/openlayers/issues/14104) |
|
||||||
| [OpenLayers 7.1.0](https://openlayers.org/) | ? | [no](https://github.com/openlayers/openlayers/issues/14104) | [no](https://github.com/openlayers/openlayers/issues/14104) |
|
| [Golden Cheetah 3.5](https://www.goldencheetah.org/) | ? | [no](https://github.com/GoldenCheetah/GoldenCheetah/issues/4271) | [no](https://github.com/GoldenCheetah/GoldenCheetah/issues/4271) |
|
||||||
| [Golden Cheetah 3.5](https://www.goldencheetah.org/) | ? | [no](https://github.com/GoldenCheetah/GoldenCheetah/issues/4271) | [no](https://github.com/GoldenCheetah/GoldenCheetah/issues/4271) |
|
| [GpxPod](https://apps.nextcloud.com/apps/gpxpod) | ? | ? | ? |
|
||||||
| [GpxPod](https://apps.nextcloud.com/apps/gpxpod) | ? | ? | ? |
|
| [OsmAnd](https://github.com/osmandapp/OsmAnd) | ? | [no](https://github.com/osmandapp/OsmAnd/issues/15271) | [no](https://github.com/osmandapp/OsmAnd/issues/15271) |
|
||||||
| [OsmAnd](https://github.com/osmandapp/OsmAnd) | ? | [no](https://github.com/osmandapp/OsmAnd/issues/15271) | [no](https://github.com/osmandapp/OsmAnd/issues/15271) |
|
| [FitTrackee](https://github.com/SamR1/FitTrackee) | yes | n/a | n/a |
|
||||||
| [FitTrackee](https://github.com/SamR1/FitTrackee) | yes | n/a | n/a |
|
| [SportsTracker](https://github.com/ssaring/sportstracker) | yes, [single tracks only](https://github.com/ssaring/sportstracker/issues/260) | no | no |
|
||||||
|
|
||||||
## Dashboard API (incl. map)
|
## Dashboard API (incl. map)
|
||||||
|
|
||||||
|
|||||||
+5
-5
@@ -6,7 +6,7 @@ buildscript {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:8.2.0-beta01'
|
classpath 'com.android.tools.build:gradle:8.2.0-beta06'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,8 +64,8 @@ android {
|
|||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "de.dennisguse.opentracks"
|
applicationId "de.dennisguse.opentracks"
|
||||||
versionCode 5389
|
versionCode 5465
|
||||||
versionName "v4.8.3"
|
versionName "v4.9.2"
|
||||||
|
|
||||||
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
||||||
|
|
||||||
@@ -131,7 +131,7 @@ dependencies {
|
|||||||
implementation 'androidx.preference:preference:1.2.1'
|
implementation 'androidx.preference:preference:1.2.1'
|
||||||
implementation 'androidx.documentfile:documentfile:1.0.1'
|
implementation 'androidx.documentfile:documentfile:1.0.1'
|
||||||
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
||||||
implementation 'com.google.android.material:material:1.9.0'
|
implementation 'com.google.android.material:material:1.10.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
implementation 'androidx.core:core-splashscreen:1.0.1'
|
implementation 'androidx.core:core-splashscreen:1.0.1'
|
||||||
implementation 'androidx.mediarouter:mediarouter:1.4.0'
|
implementation 'androidx.mediarouter:mediarouter:1.4.0'
|
||||||
@@ -141,7 +141,7 @@ dependencies {
|
|||||||
androidTestImplementation 'androidx.test:rules:1.5.0'
|
androidTestImplementation 'androidx.test:rules:1.5.0'
|
||||||
androidTestImplementation 'androidx.test:runner:1.5.2'
|
androidTestImplementation 'androidx.test:runner:1.5.2'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||||
androidTestImplementation 'org.mockito:mockito-android:5.5.0'
|
androidTestImplementation 'org.mockito:mockito-android:5.6.0'
|
||||||
|
|
||||||
androidTestUtil 'androidx.test:orchestrator:1.4.2'
|
androidTestUtil 'androidx.test:orchestrator:1.4.2'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,483 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg width="22"
|
||||||
|
height="22"
|
||||||
|
viewBox="0 0 5.8208333 5.8208333"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||||
|
sodipodi:docname="ic_activity.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<defs
|
||||||
|
id="defs2">
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="spiro"
|
||||||
|
id="path-effect3162"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1" />
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="fillet_chamfer"
|
||||||
|
id="path-effect3149"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1"
|
||||||
|
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.9999999,0,1 @ F,0,0,1,0,2.0000001,0,1"
|
||||||
|
unit="px"
|
||||||
|
method="auto"
|
||||||
|
mode="F"
|
||||||
|
radius="0"
|
||||||
|
chamfer_steps="1"
|
||||||
|
flexible="false"
|
||||||
|
use_knot_distance="true"
|
||||||
|
apply_no_radius="true"
|
||||||
|
apply_with_radius="true"
|
||||||
|
only_selected="false"
|
||||||
|
hide_knots="false"
|
||||||
|
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.9999999,0,1 @ F,0,0,1,0,2.0000001,0,1" />
|
||||||
|
<inkscape:path-effect
|
||||||
|
hide_knots="false"
|
||||||
|
only_selected="false"
|
||||||
|
apply_with_radius="true"
|
||||||
|
apply_no_radius="true"
|
||||||
|
use_knot_distance="true"
|
||||||
|
flexible="false"
|
||||||
|
chamfer_steps="1"
|
||||||
|
radius="0"
|
||||||
|
mode="F"
|
||||||
|
method="auto"
|
||||||
|
unit="px"
|
||||||
|
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.97014234,0,1 @ F,0,0,1,0,0,0,1"
|
||||||
|
lpeversion="1"
|
||||||
|
is_visible="true"
|
||||||
|
id="path-effect2514"
|
||||||
|
effect="fillet_chamfer"
|
||||||
|
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,4,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.97014234,0,1 @ F,0,0,1,0,0,0,1" />
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="fillet_chamfer"
|
||||||
|
id="path-effect950"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1"
|
||||||
|
satellites_param="F,0,0,1,0,0.51887107,0,1 @ F,0,0,1,0,0.22687835,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,0.51887107,0,1 @ F,0,0,1,0,0.22687835,0,1 @ F,0,0,1,0,0,0,1"
|
||||||
|
unit="px"
|
||||||
|
method="auto"
|
||||||
|
mode="F"
|
||||||
|
radius="0"
|
||||||
|
chamfer_steps="1"
|
||||||
|
flexible="false"
|
||||||
|
use_knot_distance="true"
|
||||||
|
apply_no_radius="true"
|
||||||
|
apply_with_radius="true"
|
||||||
|
only_selected="false"
|
||||||
|
hide_knots="false"
|
||||||
|
nodesatellites_param="F,0,0,1,0,0.51887107,0,1 @ F,0,0,1,0,0.22687835,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,0.51887107,0,1 @ F,0,0,1,0,0.22687835,0,1 @ F,0,0,1,0,0,0,1" />
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="fillet_chamfer"
|
||||||
|
id="path-effect946"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1"
|
||||||
|
satellites_param="F,0,0,1,0,0.34892149,0,1 @ F,0,0,1,0,0.18398697,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.34892149,0,1 @ F,0,0,1,0,0.18398697,0,1 @ F,0,1,1,0,0,0,1"
|
||||||
|
unit="px"
|
||||||
|
method="auto"
|
||||||
|
mode="F"
|
||||||
|
radius="0"
|
||||||
|
chamfer_steps="1"
|
||||||
|
flexible="false"
|
||||||
|
use_knot_distance="true"
|
||||||
|
apply_no_radius="true"
|
||||||
|
apply_with_radius="true"
|
||||||
|
only_selected="false"
|
||||||
|
hide_knots="false"
|
||||||
|
nodesatellites_param="F,0,0,1,0,0.34892149,0,1 @ F,0,0,1,0,0.18398697,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.34892149,0,1 @ F,0,0,1,0,0.18398697,0,1 @ F,0,1,1,0,0,0,1" />
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="fillet_chamfer"
|
||||||
|
id="path-effect918"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1"
|
||||||
|
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,2.00309,0,1 @ F,0,0,1,0,2.00309,0,1"
|
||||||
|
unit="px"
|
||||||
|
method="auto"
|
||||||
|
mode="F"
|
||||||
|
radius="0"
|
||||||
|
chamfer_steps="1"
|
||||||
|
flexible="false"
|
||||||
|
use_knot_distance="true"
|
||||||
|
apply_no_radius="true"
|
||||||
|
apply_with_radius="true"
|
||||||
|
only_selected="false"
|
||||||
|
hide_knots="false"
|
||||||
|
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,1,1,0,2.00309,0,1 @ F,0,0,1,0,2.00309,0,1" />
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="fillet_chamfer"
|
||||||
|
id="path-effect912"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1"
|
||||||
|
satellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,7.0000002,0,1 @ F,0,0,1,0,0,0,1"
|
||||||
|
unit="px"
|
||||||
|
method="auto"
|
||||||
|
mode="F"
|
||||||
|
radius="0"
|
||||||
|
chamfer_steps="1"
|
||||||
|
flexible="false"
|
||||||
|
use_knot_distance="true"
|
||||||
|
apply_no_radius="true"
|
||||||
|
apply_with_radius="true"
|
||||||
|
only_selected="false"
|
||||||
|
hide_knots="false"
|
||||||
|
nodesatellites_param="F,0,0,1,0,0,0,1 @ F,0,0,1,0,7.0000002,0,1 @ F,0,0,1,0,0,0,1" />
|
||||||
|
<inkscape:path-effect
|
||||||
|
effect="fillet_chamfer"
|
||||||
|
id="path-effect894"
|
||||||
|
is_visible="true"
|
||||||
|
lpeversion="1"
|
||||||
|
satellites_param="F,0,0,1,0,0.49832891,0,1 @ F,0,0,1,0,0.49832891,0,1 @ F,0,1,1,0,0.5316463,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.49999976,0,1 @ F,0,0,1,0,0.50000024,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0000001,0,1 @ F,0,0,1,0,0.99999982,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0295214,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5316463,0,1"
|
||||||
|
unit="px"
|
||||||
|
method="auto"
|
||||||
|
mode="F"
|
||||||
|
radius="0"
|
||||||
|
chamfer_steps="1"
|
||||||
|
flexible="false"
|
||||||
|
use_knot_distance="true"
|
||||||
|
apply_no_radius="true"
|
||||||
|
apply_with_radius="true"
|
||||||
|
only_selected="false"
|
||||||
|
hide_knots="false"
|
||||||
|
nodesatellites_param="F,0,0,1,0,0.49832891,0,1 @ F,0,0,1,0,0.49832891,0,1 @ F,0,1,1,0,0.5316463,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.49999976,0,1 @ F,0,0,1,0,0.50000024,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0000001,0,1 @ F,0,0,1,0,0.99999982,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,1.0295214,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0,0,1 @ F,0,0,1,0,0.5316463,0,1" />
|
||||||
|
</defs>
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#232629"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="1"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="22.390336"
|
||||||
|
inkscape:cx="13.4433"
|
||||||
|
inkscape:cy="12.572388"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
inkscape:document-rotation="0"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
inkscape:snap-bbox="true"
|
||||||
|
inkscape:bbox-paths="true"
|
||||||
|
inkscape:bbox-nodes="true"
|
||||||
|
inkscape:snap-bbox-edge-midpoints="true"
|
||||||
|
inkscape:snap-bbox-midpoints="true"
|
||||||
|
inkscape:object-paths="true"
|
||||||
|
inkscape:snap-intersection-paths="true"
|
||||||
|
inkscape:snap-smooth-nodes="true"
|
||||||
|
inkscape:snap-midpoints="true"
|
||||||
|
inkscape:snap-object-midpoints="true"
|
||||||
|
inkscape:snap-center="true"
|
||||||
|
inkscape:snap-text-baseline="true"
|
||||||
|
showguides="false"
|
||||||
|
inkscape:guide-bbox="true"
|
||||||
|
inkscape:window-width="956"
|
||||||
|
inkscape:window-height="1036"
|
||||||
|
inkscape:window-x="960"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="0"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#232629">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid833"
|
||||||
|
empspacing="22"
|
||||||
|
dotted="true" />
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid835"
|
||||||
|
empspacing="1"
|
||||||
|
spacingy="1.0583333"
|
||||||
|
spacingx="1.0583333"
|
||||||
|
originx="0.26458333"
|
||||||
|
originy="0.26458333" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="4.195,4.2333333"
|
||||||
|
orientation="0.96592583,0.25881905"
|
||||||
|
id="guide887"
|
||||||
|
inkscape:label=""
|
||||||
|
inkscape:locked="false"
|
||||||
|
inkscape:color="rgb(0,0,255)" />
|
||||||
|
<sodipodi:guide
|
||||||
|
position="0.26458333,1.9843751"
|
||||||
|
orientation="0,-1"
|
||||||
|
id="guide900"
|
||||||
|
inkscape:locked="false" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1">
|
||||||
|
<path
|
||||||
|
id="path924"
|
||||||
|
style="opacity:1;fill:#fdbc4b;fill-opacity:1;stroke:none;stroke-width:0.264583;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:531.552;stroke-opacity:1;paint-order:markers fill stroke"
|
||||||
|
d="m -1.3252903,1.5032615 -0.4100651,0.6834418 a 0.12845631,0.12845631 60.481872 0 0 0.1101504,0.1945466 l 0.2495783,-10e-8 -0.1100188,0.5495576 a 0.08982544,0.08982544 21.142221 0 0 0.1651025,0.063848 L -0.91047797,2.3112131 A 0.12845634,0.12845634 60.481877 0 0 -1.0206284,2.1166665 H -1.2702067 L -1.1601879,1.567109 a 0.08982541,0.08982541 21.142225 0 0 -0.1651024,-0.063848 z"
|
||||||
|
sodipodi:nodetypes="ccccccc"
|
||||||
|
inkscape:original-d="m -1.0583333,1.0583332 -0.79375,1.3229167 0.4764566,-10e-8 L -1.5875,3.4395832 -0.79375003,2.1166665 H -1.2702067 Z"
|
||||||
|
inkscape:path-effect="#path-effect950" />
|
||||||
|
<g
|
||||||
|
id="g954"
|
||||||
|
transform="matrix(0.38576851,0,0,0.38576851,152.56385,-12.494408)">
|
||||||
|
<rect
|
||||||
|
y="-7.9375"
|
||||||
|
x="-357.1875"
|
||||||
|
height="79.375"
|
||||||
|
width="68.791664"
|
||||||
|
id="rect919"
|
||||||
|
style="fill:#cacaca;fill-opacity:1;stroke-width:14.6855;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
|
||||||
|
<rect
|
||||||
|
y="-5.2916665"
|
||||||
|
x="-354.54169"
|
||||||
|
height="10.583333"
|
||||||
|
width="10.583333"
|
||||||
|
id="rect851"
|
||||||
|
style="fill:#e74c3c;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ed1515;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
|
||||||
|
id="rect853"
|
||||||
|
width="10.583333"
|
||||||
|
height="10.583333"
|
||||||
|
x="-343.95834"
|
||||||
|
y="-5.2916665" />
|
||||||
|
<rect
|
||||||
|
y="-5.2916665"
|
||||||
|
x="-333.375"
|
||||||
|
height="10.583333"
|
||||||
|
width="10.583333"
|
||||||
|
id="rect855"
|
||||||
|
style="fill:#f47750;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
|
||||||
|
<rect
|
||||||
|
style="fill:#fdbc4b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
|
||||||
|
id="rect857"
|
||||||
|
width="10.583333"
|
||||||
|
height="10.583333"
|
||||||
|
x="-322.79169"
|
||||||
|
y="-5.2916665" />
|
||||||
|
<rect
|
||||||
|
y="-5.2916665"
|
||||||
|
x="-312.20834"
|
||||||
|
height="10.583333"
|
||||||
|
width="10.583333"
|
||||||
|
id="rect859"
|
||||||
|
style="fill:#c9ce3b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
|
||||||
|
<rect
|
||||||
|
style="fill:#da4453;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
|
||||||
|
id="rect861"
|
||||||
|
width="10.583333"
|
||||||
|
height="10.583333"
|
||||||
|
x="-354.54169"
|
||||||
|
y="5.291667" />
|
||||||
|
<rect
|
||||||
|
y="5.291667"
|
||||||
|
x="-343.95834"
|
||||||
|
height="10.583333"
|
||||||
|
width="10.583333"
|
||||||
|
id="rect863"
|
||||||
|
style="fill:#c0392b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
|
||||||
|
<rect
|
||||||
|
style="fill:#d35400;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
|
||||||
|
id="rect865"
|
||||||
|
width="10.583333"
|
||||||
|
height="10.583333"
|
||||||
|
x="-333.375"
|
||||||
|
y="5.291667" />
|
||||||
|
<rect
|
||||||
|
y="5.291667"
|
||||||
|
x="-322.79169"
|
||||||
|
height="10.583333"
|
||||||
|
width="10.583333"
|
||||||
|
id="rect867"
|
||||||
|
style="fill:#f67400;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
|
||||||
|
<rect
|
||||||
|
style="fill:#f39c1f;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
|
||||||
|
id="rect869"
|
||||||
|
width="10.583333"
|
||||||
|
height="10.583333"
|
||||||
|
x="-312.20834"
|
||||||
|
y="5.291667" />
|
||||||
|
<rect
|
||||||
|
style="fill:#1cdc9a;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
|
||||||
|
id="rect871"
|
||||||
|
width="10.583333"
|
||||||
|
height="10.583333"
|
||||||
|
x="-354.54169"
|
||||||
|
y="21.166666" />
|
||||||
|
<rect
|
||||||
|
y="21.166666"
|
||||||
|
x="-343.95834"
|
||||||
|
height="10.583333"
|
||||||
|
width="10.583333"
|
||||||
|
id="rect873"
|
||||||
|
style="fill:#2ecc71;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
|
||||||
|
<rect
|
||||||
|
style="fill:#1abc9c;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
|
||||||
|
id="rect875"
|
||||||
|
width="10.583333"
|
||||||
|
height="10.583333"
|
||||||
|
x="-333.375"
|
||||||
|
y="21.166666" />
|
||||||
|
<rect
|
||||||
|
y="21.166666"
|
||||||
|
x="-322.79169"
|
||||||
|
height="10.583333"
|
||||||
|
width="10.583333"
|
||||||
|
id="rect877"
|
||||||
|
style="fill:#3498db;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
|
||||||
|
<rect
|
||||||
|
style="fill:#3daee9;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
|
||||||
|
id="rect879"
|
||||||
|
width="10.583333"
|
||||||
|
height="10.583333"
|
||||||
|
x="-312.20834"
|
||||||
|
y="21.166666" />
|
||||||
|
<rect
|
||||||
|
y="31.75"
|
||||||
|
x="-354.54169"
|
||||||
|
height="10.583333"
|
||||||
|
width="10.583333"
|
||||||
|
id="rect881"
|
||||||
|
style="fill:#10d116;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
|
||||||
|
<rect
|
||||||
|
style="fill:#27ae60;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
|
||||||
|
id="rect883"
|
||||||
|
width="10.583333"
|
||||||
|
height="10.583333"
|
||||||
|
x="-343.95834"
|
||||||
|
y="31.75" />
|
||||||
|
<rect
|
||||||
|
y="31.75"
|
||||||
|
x="-333.375"
|
||||||
|
height="10.583333"
|
||||||
|
width="10.583333"
|
||||||
|
id="rect885"
|
||||||
|
style="fill:#16a085;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
|
||||||
|
<rect
|
||||||
|
style="fill:#2980b9;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
|
||||||
|
id="rect887"
|
||||||
|
width="10.583333"
|
||||||
|
height="10.583333"
|
||||||
|
x="-322.79169"
|
||||||
|
y="31.75" />
|
||||||
|
<rect
|
||||||
|
y="31.75"
|
||||||
|
x="-312.20834"
|
||||||
|
height="10.583333"
|
||||||
|
width="10.583333"
|
||||||
|
id="rect889"
|
||||||
|
style="fill:#1d99f3;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
|
||||||
|
<rect
|
||||||
|
y="21.166666"
|
||||||
|
x="-301.625"
|
||||||
|
height="10.583333"
|
||||||
|
width="10.583333"
|
||||||
|
id="rect891"
|
||||||
|
style="fill:#9b59b6;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
|
||||||
|
<rect
|
||||||
|
style="fill:#8e44ad;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
|
||||||
|
id="rect893"
|
||||||
|
width="10.583333"
|
||||||
|
height="10.583333"
|
||||||
|
x="-301.625"
|
||||||
|
y="31.75" />
|
||||||
|
<rect
|
||||||
|
y="47.625"
|
||||||
|
x="-354.54166"
|
||||||
|
height="10.583333"
|
||||||
|
width="10.583333"
|
||||||
|
id="rect895"
|
||||||
|
style="fill:#34495e;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
|
||||||
|
<rect
|
||||||
|
style="fill:#31363b;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
|
||||||
|
id="rect897"
|
||||||
|
width="10.583333"
|
||||||
|
height="10.583333"
|
||||||
|
x="-343.95831"
|
||||||
|
y="47.625" />
|
||||||
|
<rect
|
||||||
|
y="47.625"
|
||||||
|
x="-333.37497"
|
||||||
|
height="10.583333"
|
||||||
|
width="10.583333"
|
||||||
|
id="rect899"
|
||||||
|
style="fill:#7f8c8d;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
|
||||||
|
<rect
|
||||||
|
style="fill:#bdc3c7;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
|
||||||
|
id="rect901"
|
||||||
|
width="10.583333"
|
||||||
|
height="10.583333"
|
||||||
|
x="-322.79166"
|
||||||
|
y="47.625" />
|
||||||
|
<rect
|
||||||
|
y="47.625"
|
||||||
|
x="-312.20831"
|
||||||
|
height="10.583333"
|
||||||
|
width="10.583333"
|
||||||
|
id="rect903"
|
||||||
|
style="fill:#fcfcfc;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
|
||||||
|
<rect
|
||||||
|
style="fill:#2c3e50;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
|
||||||
|
id="rect905"
|
||||||
|
width="10.583333"
|
||||||
|
height="10.583333"
|
||||||
|
x="-354.54166"
|
||||||
|
y="58.208336" />
|
||||||
|
<rect
|
||||||
|
y="58.208336"
|
||||||
|
x="-343.95831"
|
||||||
|
height="10.583333"
|
||||||
|
width="10.583333"
|
||||||
|
id="rect907"
|
||||||
|
style="fill:#232629;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
|
||||||
|
<rect
|
||||||
|
style="fill:#4d4d4d;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
|
||||||
|
id="rect909"
|
||||||
|
width="10.583333"
|
||||||
|
height="10.583333"
|
||||||
|
x="-333.37497"
|
||||||
|
y="58.208336" />
|
||||||
|
<rect
|
||||||
|
y="58.208336"
|
||||||
|
x="-322.79166"
|
||||||
|
height="10.583333"
|
||||||
|
width="10.583333"
|
||||||
|
id="rect911"
|
||||||
|
style="fill:#95a5a6;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552" />
|
||||||
|
<rect
|
||||||
|
style="fill:#eff0f1;fill-opacity:1;stroke-width:14.955;stroke-linecap:square;stroke-linejoin:round;stroke-dashoffset:531.552"
|
||||||
|
id="rect913"
|
||||||
|
width="10.583333"
|
||||||
|
height="10.583333"
|
||||||
|
x="-312.20831"
|
||||||
|
y="58.208336" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
transform="scale(0.26458333)"
|
||||||
|
id="path887"
|
||||||
|
d="M 13.994141 4.5 A 0.49999949 0.49999949 0 0 0 13.552734 4.7773438 A 0.49999949 0.49999949 0 0 0 13.777344 5.4472656 L 14.908203 6.0136719 C 15.328243 6.2241517 15.636226 6.6066586 15.75 7.0625 L 17.390625 13.623047 C 15.381402 13.909129 13.837817 15.475476 13.601562 17.5 L 8.3984375 17.5 C 8.1378934 15.267332 6.3043435 13.50201 4.0039062 13.5 C 3.982495 13.499838 3.9608645 13.499856 3.9394531 13.5 L 2 13.5 A 0.49999949 0.49999949 0 0 0 1.5 14 A 0.49999949 0.49999949 0 0 0 2 14.5 L 3.9394531 14.5 A 0.50004949 0.50004949 0 0 0 3.9433594 14.5 C 3.9610145 14.499866 3.9784387 14.499866 3.9960938 14.5 A 0.50004949 0.50004949 0 0 0 4 14.5 C 5.9389193 14.5 7.5000003 16.061081 7.5 18 A 0.50004949 0.50004949 0 0 0 8 18.5 L 14 18.5 A 0.50004949 0.50004949 0 0 0 14.5 18 C 14.5 16.061081 16.061081 14.5 18 14.5 A 0.50004949 0.50004949 0 0 0 18.484375 13.878906 L 16.71875 6.8203125 C 16.534472 6.0819921 16.037756 5.460052 15.357422 5.1191406 A 0.50004949 0.50004949 0 0 0 15.355469 5.1191406 L 14.222656 4.5527344 A 0.49999949 0.49999949 0 0 0 13.994141 4.5 z "
|
||||||
|
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00157481;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:531.552;stroke-opacity:1;paint-order:markers fill stroke;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:1" />
|
||||||
|
<path
|
||||||
|
id="path970"
|
||||||
|
d="M 4.7617188 4.1367188 C 4.4265639 4.1367188 4.1523438 4.4109389 4.1523438 4.7460938 C 4.1523438 5.0812486 4.4265639 5.3554687 4.7617188 5.3554688 C 5.0968736 5.3554687 5.3710937 5.0812486 5.3710938 4.7460938 C 5.3710937 4.4109389 5.0968736 4.1367188 4.7617188 4.1367188 z M 4.7617188 4.3378906 C 4.9879388 4.3378906 5.1679687 4.5198737 5.1679688 4.7460938 C 5.1679687 4.9723138 4.9879388 5.1542969 4.7617188 5.1542969 C 4.5354987 5.1542969 4.3535156 4.9723138 4.3535156 4.7460938 C 4.3535156 4.5198737 4.5354987 4.3378906 4.7617188 4.3378906 z "
|
||||||
|
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.201567;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1;opacity:0.997" />
|
||||||
|
<path
|
||||||
|
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.997;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.201567;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||||
|
d="m 1.0221536,4.1367188 c -0.33515491,0 -0.60937501,0.2742201 -0.60937501,0.609375 0,0.3351548 0.2742201,0.6093749 0.60937501,0.609375 0.3351548,-10e-8 0.6093749,-0.2742202 0.609375,-0.609375 -1e-7,-0.3351549 -0.2742202,-0.609375 -0.609375,-0.609375 z m 0,0.2011718 c 0.22622,0 0.4062499,0.1819831 0.40625,0.4082032 -1e-7,0.2262201 -0.18003,0.4082031 -0.40625,0.4082031 -0.22622011,0 -0.40820321,-0.181983 -0.40820321,-0.4082031 0,-0.2262201 0.1819831,-0.4082032 0.40820321,-0.4082032 z"
|
||||||
|
id="path970-7" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 24 KiB |
@@ -0,0 +1 @@
|
|||||||
|
Un amic rastrejador esportiu que respecta la vostra privadesa.
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
v4.8.4: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Show max power for recorded tracks
|
||||||
|
|
||||||
|
Developer:
|
||||||
|
- TrackList now uses RecyclerView
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
v4.9.0: OpenTracks
|
||||||
|
|
||||||
|
NOTE: BREAKING CHANGE
|
||||||
|
|
||||||
|
This release changes how OpenTracks determines moving and not moving.
|
||||||
|
Until now, the speed (GPS or sensor) was used by a applying a threshold (e.g., less than 0.3 m/s).
|
||||||
|
However, this may be very noise.
|
||||||
|
From now on, a timeout duration is used (i.e., no significant change of position in the last X seconds).
|
||||||
|
This duration is user configurable.
|
||||||
|
The events of becoming idle are stored in the database incl. a timestamp, so statistics can be re-computed.
|
||||||
|
|
||||||
|
A downgrade from this version is not implemented.
|
||||||
|
Please backup all before upgrading.
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Idle detection uses "no movement" duration rather than speed too low
|
||||||
|
- Added scooter activity
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- While recording, TrackList showed all tracks as recording
|
||||||
|
|
||||||
|
Developer:
|
||||||
|
- Re-enabled Github action executing tests
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
v4.9.1: OpenTracks
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Crash when doing multi select in track list
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
v4.9.2: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Option to use Android 12's Dynamic Color (Material Design)
|
||||||
|
- Android 14's Predictive Back for some activities
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Settings for total distance voice announcement was accidentially removed
|
||||||
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
v4.7.1: OpenTracks
|
v4.7.1: OpenTracks
|
||||||
|
|
||||||
Bugfixes:
|
Arranxos:
|
||||||
- Android 14: crash on recording start (missing permissions)
|
- Android 14: fallo ao inicio da gravación (por falta de permisos)
|
||||||
- Android 14: crash on track deletion (ForegroundService without reason)
|
- Android 14: fallo ao eliminar unha ruta (sen razóns para ForegroundService)
|
||||||
|
|
||||||
Developer:
|
Desenvolvemento:
|
||||||
- AggregatedStats is now a RecyclerView
|
- agora AggregatedStats é unha RecyclerView
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
v4.8.2: OpenTracks
|
||||||
|
|
||||||
|
Cambios:
|
||||||
|
- Apos exportar as gravacións: mostra mensaxe de aviso se falla
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
v4.8.3: OpenTracks
|
||||||
|
|
||||||
|
|
||||||
|
Arranxos:
|
||||||
|
- Android 14: evitar o peche da app debido a un UUID mal formado (ex. no modelo do nome de ficheiro)
|
||||||
|
|
||||||
|
Desenvolvemento:
|
||||||
|
- A lista Marker agora usa RecyclerView
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
v4.8.4: OpenTracks
|
||||||
|
|
||||||
|
Cambios:
|
||||||
|
- Mostra a potencia máxima nas rutas gravadas
|
||||||
|
|
||||||
|
Desenvolvemento:
|
||||||
|
- agora TrackList usa RecyclerView
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
v4.8.2: OpenTracks
|
||||||
|
|
||||||
|
Veranderingen:
|
||||||
|
- Na export van opname: toon foutmelding indien niet succesvol
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
v4.8.3: OpenTracks
|
||||||
|
|
||||||
|
Bugfix :
|
||||||
|
- Android 14: crash voorkomen t.g.v. misvormde UUID (voorbeeld voor bestandsnaam sjabloon)
|
||||||
|
|
||||||
|
Ontwikkelaar:
|
||||||
|
- Markeringslijst is nu een RecyclerView
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
v4.8.4: OpenTracks
|
||||||
|
|
||||||
|
Veranderingen:
|
||||||
|
- Toon maximale vermogen voor opgenomen tracks
|
||||||
|
|
||||||
|
Ontwikkelaar:
|
||||||
|
- TrackList gebruikt nu RecyclerView
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
v4.9.0: OpenTracks
|
||||||
|
|
||||||
|
OPMERKING: BREAKING CHANGE
|
||||||
|
|
||||||
|
Deze release verandert hoe OpenTracks beweging van geen beweging onderscheidt.
|
||||||
|
Tot nu toe werd de snelheid (GPS of sensor) gebruikt door een drempel toe te passen (bijv., minder dan 0,3 m/s).
|
||||||
|
Dit kan echter erg lawaai zijn.
|
||||||
|
Vanaf nu wordt een time-outduur gebruikt (d.w.z., geen significante positieverandering in de laatste X seconden).
|
||||||
|
Deze duur is door de gebruiker configureerbaar.
|
||||||
|
De gebeurtenissen om inactief te worden, worden opgeslagen in de database incl. een tijdstempel, zodat statistieken opnieuw kunnen worden berekend.
|
||||||
|
|
||||||
|
Een downgrade van deze versie is niet geïmplementeerd.
|
||||||
|
Maak een back-up van alles voordat u gaat upgraden.
|
||||||
|
|
||||||
|
Veranderingen:
|
||||||
|
- Inactieve detectie gebruikt de duur van "geen beweging" in plaats van te laag
|
||||||
|
- Scooteractiviteit toegevoegd
|
||||||
|
|
||||||
|
Bugfix :
|
||||||
|
- Tijdens het opnemen toonde TrackList alle nummers als opname
|
||||||
|
|
||||||
|
Ontwikkelaar:
|
||||||
|
- Github-actie opnieuw ingeschakeld die tests uitvoert
|
||||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
@@ -83,7 +83,8 @@ done
|
|||||||
# This is normally unused
|
# This is normally unused
|
||||||
# shellcheck disable=SC2034
|
# shellcheck disable=SC2034
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
|
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
package de.dennisguse.opentracks;
|
package de.dennisguse.opentracks;
|
||||||
|
|
||||||
|
|
||||||
import static androidx.test.espresso.Espresso.onData;
|
|
||||||
import static androidx.test.espresso.Espresso.onView;
|
import static androidx.test.espresso.Espresso.onView;
|
||||||
import static androidx.test.espresso.action.ViewActions.click;
|
import static androidx.test.espresso.action.ViewActions.click;
|
||||||
import static androidx.test.espresso.action.ViewActions.longClick;
|
import static androidx.test.espresso.action.ViewActions.longClick;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
|
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||||
|
import static androidx.test.espresso.matcher.ViewMatchers.withParent;
|
||||||
|
import static androidx.test.espresso.matcher.ViewMatchers.withParentIndex;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||||
import static org.hamcrest.Matchers.allOf;
|
import static org.hamcrest.Matchers.allOf;
|
||||||
import static org.hamcrest.Matchers.anything;
|
|
||||||
import static de.dennisguse.opentracks.util.EspressoUtils.childAtPosition;
|
import static de.dennisguse.opentracks.util.EspressoUtils.childAtPosition;
|
||||||
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ public class EspressoDeleteTrackTest {
|
|||||||
onView(withId(R.id.finish_button)).perform(click());
|
onView(withId(R.id.finish_button)).perform(click());
|
||||||
|
|
||||||
// select track
|
// select track
|
||||||
onData(anything()).inAdapterView(withId(R.id.track_list)).atPosition(0).perform(longClick());
|
onView(allOf(withParent(withId(R.id.track_list)), withParentIndex(0))).perform(longClick());
|
||||||
|
|
||||||
// open menu and delete selected track
|
// open menu and delete selected track
|
||||||
//TODO openActionBarOverflowOrOptionsMenu(); doesn't work
|
//TODO openActionBarOverflowOrOptionsMenu(); doesn't work
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package de.dennisguse.opentracks;
|
package de.dennisguse.opentracks;
|
||||||
|
|
||||||
import static androidx.test.espresso.Espresso.onData;
|
|
||||||
import static androidx.test.espresso.Espresso.onView;
|
import static androidx.test.espresso.Espresso.onView;
|
||||||
import static androidx.test.espresso.action.ViewActions.click;
|
import static androidx.test.espresso.action.ViewActions.click;
|
||||||
import static androidx.test.espresso.action.ViewActions.longClick;
|
import static androidx.test.espresso.action.ViewActions.longClick;
|
||||||
@@ -8,8 +7,9 @@ import static androidx.test.espresso.assertion.ViewAssertions.matches;
|
|||||||
import static androidx.test.espresso.matcher.ViewMatchers.isClickable;
|
import static androidx.test.espresso.matcher.ViewMatchers.isClickable;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
||||||
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
||||||
|
import static androidx.test.espresso.matcher.ViewMatchers.withParent;
|
||||||
|
import static androidx.test.espresso.matcher.ViewMatchers.withParentIndex;
|
||||||
import static org.hamcrest.Matchers.allOf;
|
import static org.hamcrest.Matchers.allOf;
|
||||||
import static org.hamcrest.Matchers.anything;
|
|
||||||
import static de.dennisguse.opentracks.util.EspressoUtils.selectTabAtIndex;
|
import static de.dennisguse.opentracks.util.EspressoUtils.selectTabAtIndex;
|
||||||
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
||||||
|
|
||||||
@@ -95,6 +95,6 @@ public class EspressoUITest {
|
|||||||
@Test
|
@Test
|
||||||
public void selectAndDeleteTrack() {
|
public void selectAndDeleteTrack() {
|
||||||
onView(withId(R.id.track_list)).check(matches(isDisplayed()));
|
onView(withId(R.id.track_list)).check(matches(isDisplayed()));
|
||||||
onData(anything()).inAdapterView(withId(R.id.track_list)).atPosition(0).perform(longClick());
|
onView(allOf(withParent(withId(R.id.track_list)), withParentIndex(0))).perform(longClick());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,10 +27,10 @@ public class ChartPointTest {
|
|||||||
statistics.setTotalTime(Duration.ofSeconds(1000));
|
statistics.setTotalTime(Duration.ofSeconds(1000));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), false, UnitSystem.IMPERIAL_FEET);
|
ChartPoint point = ChartPoint.create(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), false, UnitSystem.IMPERIAL_FEET);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(1000000, (long) point.getTimeOrDistance());
|
assertEquals(1000000, (long) point.timeOrDistance());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -40,10 +40,10 @@ public class ChartPointTest {
|
|||||||
statistics.setTotalDistance(Distance.of(1000));
|
statistics.setTotalDistance(Distance.of(1000));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), true, UnitSystem.METRIC);
|
ChartPoint point = ChartPoint.create(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), true, UnitSystem.METRIC);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(1, (long) point.getTimeOrDistance());
|
assertEquals(1, (long) point.timeOrDistance());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -54,12 +54,12 @@ public class ChartPointTest {
|
|||||||
.setAltitude(Altitude.EGM2008.of(50));
|
.setAltitude(Altitude.EGM2008.of(50));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
ChartPoint point = new ChartPoint(statistics, trackPoint, Speed.of(10), false, UnitSystem.METRIC);
|
ChartPoint point = ChartPoint.create(statistics, trackPoint, Speed.of(10), false, UnitSystem.METRIC);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(50, point.getAltitude(), 0.01);
|
assertEquals(50, point.altitude(), 0.01);
|
||||||
assertEquals(36, point.getSpeed(), 0.01);
|
assertEquals(36, point.speed(), 0.01);
|
||||||
assertEquals(1.66, point.getPace(), 0.01);
|
assertEquals(1.66, point.pace(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -69,12 +69,12 @@ public class ChartPointTest {
|
|||||||
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint()
|
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint()
|
||||||
.setAltitude(Altitude.EGM2008.of(50));
|
.setAltitude(Altitude.EGM2008.of(50));
|
||||||
// when
|
// when
|
||||||
ChartPoint point = new ChartPoint(statistics, trackPoint, Speed.of(10), false, UnitSystem.METRIC);
|
ChartPoint point = ChartPoint.create(statistics, trackPoint, Speed.of(10), false, UnitSystem.METRIC);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertNull(point.getHeartRate());
|
assertNull(point.heartRate());
|
||||||
assertNull(point.getCadence());
|
assertNull(point.cadence());
|
||||||
assertNull(point.getPower());
|
assertNull(point.power());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -89,11 +89,11 @@ public class ChartPointTest {
|
|||||||
TrackStatistics statistics = new TrackStatistics();
|
TrackStatistics statistics = new TrackStatistics();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
ChartPoint point = new ChartPoint(statistics, trackPoint, Speed.of(10), false, UnitSystem.METRIC);
|
ChartPoint point = ChartPoint.create(statistics, trackPoint, Speed.of(10), false, UnitSystem.METRIC);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(100.0, point.getHeartRate(), 0.01);
|
assertEquals(100.0, point.heartRate(), 0.01);
|
||||||
assertEquals(101.0, point.getCadence(), 0.01);
|
assertEquals(101.0, point.cadence(), 0.01);
|
||||||
assertEquals(102.0, point.getPower(), 0.01);
|
assertEquals(102.0, point.power(), 0.01);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,7 @@ public class ChartValueSeriesTest {
|
|||||||
18) {
|
18) {
|
||||||
@Override
|
@Override
|
||||||
Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
||||||
return chartPoint.getAltitude();
|
return chartPoint.altitude();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -80,9 +80,9 @@ public class ChartValueSeriesTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testVerySmallUpdates() {
|
public void testVerySmallUpdates() {
|
||||||
series.update(new ChartPoint(1f));
|
series.update(withAltitude(1f));
|
||||||
series.update(new ChartPoint(2f));
|
series.update(withAltitude(2f));
|
||||||
series.update(new ChartPoint(3f));
|
series.update(withAltitude(3f));
|
||||||
series.updateDimension();
|
series.updateDimension();
|
||||||
assertEquals(1, series.getInterval());
|
assertEquals(1, series.getInterval());
|
||||||
assertEquals(1, series.getMinMarkerValue());
|
assertEquals(1, series.getMinMarkerValue());
|
||||||
@@ -91,8 +91,8 @@ public class ChartValueSeriesTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSmallUpdates() {
|
public void testSmallUpdates() {
|
||||||
series.update(new ChartPoint(0));
|
series.update(withAltitude(0));
|
||||||
series.update(new ChartPoint(10));
|
series.update(withAltitude(10));
|
||||||
series.updateDimension();
|
series.updateDimension();
|
||||||
assertEquals(100, series.getInterval());
|
assertEquals(100, series.getInterval());
|
||||||
assertEquals(0, series.getMinMarkerValue());
|
assertEquals(0, series.getMinMarkerValue());
|
||||||
@@ -101,8 +101,8 @@ public class ChartValueSeriesTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBigUpdates() {
|
public void testBigUpdates() {
|
||||||
series.update(new ChartPoint(0));
|
series.update(withAltitude(0));
|
||||||
series.update(new ChartPoint(901));
|
series.update(withAltitude(901));
|
||||||
series.updateDimension();
|
series.updateDimension();
|
||||||
assertEquals(1000, series.getInterval());
|
assertEquals(1000, series.getInterval());
|
||||||
assertEquals(0, series.getMinMarkerValue());
|
assertEquals(0, series.getMinMarkerValue());
|
||||||
@@ -111,11 +111,23 @@ public class ChartValueSeriesTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNotZeroBasedUpdates() {
|
public void testNotZeroBasedUpdates() {
|
||||||
series.update(new ChartPoint(220));
|
series.update(withAltitude(220));
|
||||||
series.update(new ChartPoint(250));
|
series.update(withAltitude(250));
|
||||||
series.updateDimension();
|
series.updateDimension();
|
||||||
assertEquals(100, series.getInterval());
|
assertEquals(100, series.getInterval());
|
||||||
assertEquals(200, series.getMinMarkerValue());
|
assertEquals(200, series.getMinMarkerValue());
|
||||||
assertEquals(700, series.getMaxMarkerValue());
|
assertEquals(700, series.getMaxMarkerValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ChartPoint withAltitude(double altitude) {
|
||||||
|
return new ChartPoint(
|
||||||
|
0,
|
||||||
|
altitude,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-30
@@ -986,13 +986,13 @@ public class CustomContentProviderUtilsTest {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
assertTrue(sensorStatistics.hasHeartRate());
|
assertTrue(sensorStatistics.hasHeartRate());
|
||||||
assertEquals(sensorStatistics.getAvgHeartRate().getBPM(), stats.avgHr, 0f);
|
assertEquals(sensorStatistics.avgHeartRate().getBPM(), stats.avgHr, 0f);
|
||||||
assertEquals(sensorStatistics.getMaxHeartRate().getBPM(), stats.maxHr, 0f);
|
assertEquals(sensorStatistics.maxHeartRate().getBPM(), stats.maxHr, 0f);
|
||||||
assertTrue(sensorStatistics.hasCadence());
|
assertTrue(sensorStatistics.hasCadence());
|
||||||
assertEquals(sensorStatistics.getAvgCadence().getRPM(), stats.avgCadence, 0f);
|
assertEquals(sensorStatistics.avgCadence().getRPM(), stats.avgCadence, 0f);
|
||||||
assertEquals(sensorStatistics.getMaxCadence().getRPM(), stats.maxCadence, 0f);
|
assertEquals(sensorStatistics.maxCadence().getRPM(), stats.maxCadence, 0f);
|
||||||
assertTrue(sensorStatistics.hasPower());
|
assertTrue(sensorStatistics.hasPower());
|
||||||
assertEquals(sensorStatistics.getAvgPower().getW(), stats.avgPower, 0f);
|
assertEquals(sensorStatistics.avgPower().getW(), stats.avgPower, 0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -1018,8 +1018,8 @@ public class CustomContentProviderUtilsTest {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
assertTrue(sensorStatistics.hasHeartRate());
|
assertTrue(sensorStatistics.hasHeartRate());
|
||||||
assertEquals(sensorStatistics.getAvgHeartRate().getBPM(), stats.avgHr, 0f);
|
assertEquals(sensorStatistics.avgHeartRate().getBPM(), stats.avgHr, 0f);
|
||||||
assertEquals(sensorStatistics.getMaxHeartRate().getBPM(), stats.maxHr, 0f);
|
assertEquals(sensorStatistics.maxHeartRate().getBPM(), stats.maxHr, 0f);
|
||||||
assertFalse(sensorStatistics.hasCadence());
|
assertFalse(sensorStatistics.hasCadence());
|
||||||
assertFalse(sensorStatistics.hasPower());
|
assertFalse(sensorStatistics.hasPower());
|
||||||
}
|
}
|
||||||
@@ -1048,8 +1048,8 @@ public class CustomContentProviderUtilsTest {
|
|||||||
// then
|
// then
|
||||||
assertFalse(sensorStatistics.hasHeartRate());
|
assertFalse(sensorStatistics.hasHeartRate());
|
||||||
assertTrue(sensorStatistics.hasCadence());
|
assertTrue(sensorStatistics.hasCadence());
|
||||||
assertEquals(sensorStatistics.getAvgCadence().getRPM(), stats.avgCadence, 0f);
|
assertEquals(sensorStatistics.avgCadence().getRPM(), stats.avgCadence, 0f);
|
||||||
assertEquals(sensorStatistics.getMaxCadence().getRPM(), stats.maxCadence, 0f);
|
assertEquals(sensorStatistics.maxCadence().getRPM(), stats.maxCadence, 0f);
|
||||||
assertFalse(sensorStatistics.hasPower());
|
assertFalse(sensorStatistics.hasPower());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1078,7 +1078,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
assertFalse(sensorStatistics.hasHeartRate());
|
assertFalse(sensorStatistics.hasHeartRate());
|
||||||
assertFalse(sensorStatistics.hasCadence());
|
assertFalse(sensorStatistics.hasCadence());
|
||||||
assertTrue(sensorStatistics.hasPower());
|
assertTrue(sensorStatistics.hasPower());
|
||||||
assertEquals(sensorStatistics.getAvgPower().getW(), stats.avgPower, 0f);
|
assertEquals(sensorStatistics.avgPower().getW(), stats.avgPower, 0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -1111,11 +1111,11 @@ public class CustomContentProviderUtilsTest {
|
|||||||
TestSensorDataUtil.SensorDataStats stats = sensorDataUtil.computeStats();
|
TestSensorDataUtil.SensorDataStats stats = sensorDataUtil.computeStats();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(sensorStatistics.getAvgHeartRate().getBPM(), stats.avgHr, 0f);
|
assertEquals(sensorStatistics.avgHeartRate().getBPM(), stats.avgHr, 0f);
|
||||||
assertEquals(sensorStatistics.getMaxHeartRate().getBPM(), stats.maxHr, 0f);
|
assertEquals(sensorStatistics.maxHeartRate().getBPM(), stats.maxHr, 0f);
|
||||||
assertEquals(sensorStatistics.getAvgCadence().getRPM(), stats.avgCadence, 0f);
|
assertEquals(sensorStatistics.avgCadence().getRPM(), stats.avgCadence, 0f);
|
||||||
assertEquals(sensorStatistics.getMaxCadence().getRPM(), stats.maxCadence, 0f);
|
assertEquals(sensorStatistics.maxCadence().getRPM(), stats.maxCadence, 0f);
|
||||||
assertEquals(sensorStatistics.getAvgPower().getW(), stats.avgPower, 0f);
|
assertEquals(sensorStatistics.avgPower().getW(), stats.avgPower, 0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -1156,11 +1156,11 @@ public class CustomContentProviderUtilsTest {
|
|||||||
TestSensorDataUtil.SensorDataStats stats = sensorDataUtil.computeStats();
|
TestSensorDataUtil.SensorDataStats stats = sensorDataUtil.computeStats();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(sensorStatistics.getAvgHeartRate().getBPM(), stats.avgHr, 0f);
|
assertEquals(sensorStatistics.avgHeartRate().getBPM(), stats.avgHr, 0f);
|
||||||
assertEquals(sensorStatistics.getMaxHeartRate().getBPM(), stats.maxHr, 0f);
|
assertEquals(sensorStatistics.maxHeartRate().getBPM(), stats.maxHr, 0f);
|
||||||
assertEquals(sensorStatistics.getAvgCadence().getRPM(), stats.avgCadence, 0f);
|
assertEquals(sensorStatistics.avgCadence().getRPM(), stats.avgCadence, 0f);
|
||||||
assertEquals(sensorStatistics.getMaxCadence().getRPM(), stats.maxCadence, 0f);
|
assertEquals(sensorStatistics.maxCadence().getRPM(), stats.maxCadence, 0f);
|
||||||
assertEquals(sensorStatistics.getAvgPower().getW(), stats.avgPower, 0f);
|
assertEquals(sensorStatistics.avgPower().getW(), stats.avgPower, 0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -1201,11 +1201,11 @@ public class CustomContentProviderUtilsTest {
|
|||||||
TestSensorDataUtil.SensorDataStats stats = sensorDataUtil.computeStats();
|
TestSensorDataUtil.SensorDataStats stats = sensorDataUtil.computeStats();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(sensorStatistics.getAvgHeartRate().getBPM(), stats.avgHr, 0f);
|
assertEquals(sensorStatistics.avgHeartRate().getBPM(), stats.avgHr, 0f);
|
||||||
assertEquals(sensorStatistics.getMaxHeartRate().getBPM(), stats.maxHr, 0f);
|
assertEquals(sensorStatistics.maxHeartRate().getBPM(), stats.maxHr, 0f);
|
||||||
assertEquals(sensorStatistics.getAvgCadence().getRPM(), stats.avgCadence, 0f);
|
assertEquals(sensorStatistics.avgCadence().getRPM(), stats.avgCadence, 0f);
|
||||||
assertEquals(sensorStatistics.getMaxCadence().getRPM(), stats.maxCadence, 0f);
|
assertEquals(sensorStatistics.maxCadence().getRPM(), stats.maxCadence, 0f);
|
||||||
assertEquals(sensorStatistics.getAvgPower().getW(), stats.avgPower, 0f);
|
assertEquals(sensorStatistics.avgPower().getW(), stats.avgPower, 0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testGetSensorStats_randomData(int totalPoints, boolean withStartSegments) {
|
private void testGetSensorStats_randomData(int totalPoints, boolean withStartSegments) {
|
||||||
@@ -1232,11 +1232,11 @@ public class CustomContentProviderUtilsTest {
|
|||||||
TestSensorDataUtil.SensorDataStats stats = sensorDataUtil.computeStats();
|
TestSensorDataUtil.SensorDataStats stats = sensorDataUtil.computeStats();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(sensorStatistics.getAvgHeartRate().getBPM(), stats.avgHr, 0.01f);
|
assertEquals(sensorStatistics.avgHeartRate().getBPM(), stats.avgHr, 0.01f);
|
||||||
assertEquals(sensorStatistics.getMaxHeartRate().getBPM(), stats.maxHr, 0.01f);
|
assertEquals(sensorStatistics.maxHeartRate().getBPM(), stats.maxHr, 0.01f);
|
||||||
assertEquals(sensorStatistics.getAvgCadence().getRPM(), stats.avgCadence, 0.01f);
|
assertEquals(sensorStatistics.avgCadence().getRPM(), stats.avgCadence, 0.01f);
|
||||||
assertEquals(sensorStatistics.getMaxCadence().getRPM(), stats.maxCadence, 0.01f);
|
assertEquals(sensorStatistics.maxCadence().getRPM(), stats.maxCadence, 0.01f);
|
||||||
assertEquals(sensorStatistics.getAvgPower().getW(), stats.avgPower, 0.01f);
|
assertEquals(sensorStatistics.avgPower().getW(), stats.avgPower, 0.01f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ public class TrackSelectionTest extends TestCase {
|
|||||||
SelectionData selection = filter.buildSelection();
|
SelectionData selection = filter.buildSelection();
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
assertNull(selection.getSelection());
|
assertNull(selection.selection());
|
||||||
assertNull(selection.getSelectionArgs());
|
assertNull(selection.selectionArgs());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -37,9 +37,9 @@ public class TrackSelectionTest extends TestCase {
|
|||||||
SelectionData selection = filter.buildSelection();
|
SelectionData selection = filter.buildSelection();
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
assertEquals(selection.getSelection(), "_id IN (?)");
|
assertEquals(selection.selection(), "_id IN (?)");
|
||||||
assertEquals(selection.getSelectionArgs().length, 1);
|
assertEquals(selection.selectionArgs().length, 1);
|
||||||
assertEquals(selection.getSelectionArgs()[0], "1");
|
assertEquals(selection.selectionArgs()[0], "1");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -57,11 +57,11 @@ public class TrackSelectionTest extends TestCase {
|
|||||||
SelectionData selection = filter.buildSelection();
|
SelectionData selection = filter.buildSelection();
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
assertEquals(selection.getSelection(), "_id IN (?,?,?)");
|
assertEquals(selection.selection(), "_id IN (?,?,?)");
|
||||||
assertEquals(selection.getSelectionArgs().length, 3);
|
assertEquals(selection.selectionArgs().length, 3);
|
||||||
assertEquals(selection.getSelectionArgs()[0], "1");
|
assertEquals(selection.selectionArgs()[0], "1");
|
||||||
assertEquals(selection.getSelectionArgs()[1], "2");
|
assertEquals(selection.selectionArgs()[1], "2");
|
||||||
assertEquals(selection.getSelectionArgs()[2], "3");
|
assertEquals(selection.selectionArgs()[2], "3");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -73,9 +73,9 @@ public class TrackSelectionTest extends TestCase {
|
|||||||
SelectionData selection = filter.buildSelection();
|
SelectionData selection = filter.buildSelection();
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
assertEquals(selection.getSelection(), "category IN (?)");
|
assertEquals(selection.selection(), "category IN (?)");
|
||||||
assertEquals(selection.getSelectionArgs().length, 1);
|
assertEquals(selection.selectionArgs().length, 1);
|
||||||
assertEquals(selection.getSelectionArgs()[0], "running");
|
assertEquals(selection.selectionArgs()[0], "running");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -91,12 +91,12 @@ public class TrackSelectionTest extends TestCase {
|
|||||||
SelectionData selection = filter.buildSelection();
|
SelectionData selection = filter.buildSelection();
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
assertEquals(selection.getSelection(), "category IN (?,?,?,?)");
|
assertEquals(selection.selection(), "category IN (?,?,?,?)");
|
||||||
assertEquals(selection.getSelectionArgs().length, 4);
|
assertEquals(selection.selectionArgs().length, 4);
|
||||||
assertEquals(selection.getSelectionArgs()[0], "running");
|
assertEquals(selection.selectionArgs()[0], "running");
|
||||||
assertEquals(selection.getSelectionArgs()[1], "road biking");
|
assertEquals(selection.selectionArgs()[1], "road biking");
|
||||||
assertEquals(selection.getSelectionArgs()[2], "mountain biking");
|
assertEquals(selection.selectionArgs()[2], "mountain biking");
|
||||||
assertEquals(selection.getSelectionArgs()[3], "trail walking");
|
assertEquals(selection.selectionArgs()[3], "trail walking");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -114,16 +114,16 @@ public class TrackSelectionTest extends TestCase {
|
|||||||
SelectionData selectionOk = filterOk.buildSelection();
|
SelectionData selectionOk = filterOk.buildSelection();
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
assertNull(selectionWrong1.getSelection());
|
assertNull(selectionWrong1.selection());
|
||||||
assertNull(selectionWrong1.getSelectionArgs());
|
assertNull(selectionWrong1.selectionArgs());
|
||||||
|
|
||||||
assertNull(selectionWrong2.getSelection());
|
assertNull(selectionWrong2.selection());
|
||||||
assertNull(selectionWrong2.getSelectionArgs());
|
assertNull(selectionWrong2.selectionArgs());
|
||||||
|
|
||||||
assertEquals(selectionOk.getSelection(), "starttime BETWEEN ? AND ?");
|
assertEquals(selectionOk.selection(), "starttime BETWEEN ? AND ?");
|
||||||
assertEquals(selectionOk.getSelectionArgs().length, 2);
|
assertEquals(selectionOk.selectionArgs().length, 2);
|
||||||
assertEquals(selectionOk.getSelectionArgs()[0], Long.toString(instant.toEpochMilli()));
|
assertEquals(selectionOk.selectionArgs()[0], Long.toString(instant.toEpochMilli()));
|
||||||
assertEquals(selectionOk.getSelectionArgs()[1], Long.toString(instant.toEpochMilli() + oneDay));
|
assertEquals(selectionOk.selectionArgs()[1], Long.toString(instant.toEpochMilli() + oneDay));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -143,13 +143,13 @@ public class TrackSelectionTest extends TestCase {
|
|||||||
SelectionData selection = filter.buildSelection();
|
SelectionData selection = filter.buildSelection();
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
assertEquals(selection.getSelection(), "_id IN (?,?,?) AND category IN (?,?)");
|
assertEquals(selection.selection(), "_id IN (?,?,?) AND category IN (?,?)");
|
||||||
assertEquals(selection.getSelectionArgs().length, 5);
|
assertEquals(selection.selectionArgs().length, 5);
|
||||||
assertEquals(selection.getSelectionArgs()[0], "1");
|
assertEquals(selection.selectionArgs()[0], "1");
|
||||||
assertEquals(selection.getSelectionArgs()[1], "2");
|
assertEquals(selection.selectionArgs()[1], "2");
|
||||||
assertEquals(selection.getSelectionArgs()[2], "3");
|
assertEquals(selection.selectionArgs()[2], "3");
|
||||||
assertEquals(selection.getSelectionArgs()[3], "running");
|
assertEquals(selection.selectionArgs()[3], "running");
|
||||||
assertEquals(selection.getSelectionArgs()[4], "road biking");
|
assertEquals(selection.selectionArgs()[4], "road biking");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -172,13 +172,13 @@ public class TrackSelectionTest extends TestCase {
|
|||||||
SelectionData selection = filter.buildSelection();
|
SelectionData selection = filter.buildSelection();
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
assertEquals(selection.getSelection(), "_id IN (?,?,?) AND starttime BETWEEN ? AND ?");
|
assertEquals(selection.selection(), "_id IN (?,?,?) AND starttime BETWEEN ? AND ?");
|
||||||
assertEquals(selection.getSelectionArgs().length, 5);
|
assertEquals(selection.selectionArgs().length, 5);
|
||||||
assertEquals(selection.getSelectionArgs()[0], "1");
|
assertEquals(selection.selectionArgs()[0], "1");
|
||||||
assertEquals(selection.getSelectionArgs()[1], "2");
|
assertEquals(selection.selectionArgs()[1], "2");
|
||||||
assertEquals(selection.getSelectionArgs()[2], "3");
|
assertEquals(selection.selectionArgs()[2], "3");
|
||||||
assertEquals(selection.getSelectionArgs()[3], Long.toString(instant.toEpochMilli()));
|
assertEquals(selection.selectionArgs()[3], Long.toString(instant.toEpochMilli()));
|
||||||
assertEquals(selection.getSelectionArgs()[4], Long.toString(instant.toEpochMilli() + oneDay));
|
assertEquals(selection.selectionArgs()[4], Long.toString(instant.toEpochMilli() + oneDay));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -196,11 +196,11 @@ public class TrackSelectionTest extends TestCase {
|
|||||||
SelectionData selection = filter.buildSelection();
|
SelectionData selection = filter.buildSelection();
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
assertEquals(selection.getSelection(), "category IN (?,?) AND starttime BETWEEN ? AND ?");
|
assertEquals(selection.selection(), "category IN (?,?) AND starttime BETWEEN ? AND ?");
|
||||||
assertEquals(selection.getSelectionArgs().length, 4);
|
assertEquals(selection.selectionArgs().length, 4);
|
||||||
assertEquals(selection.getSelectionArgs()[0], "running");
|
assertEquals(selection.selectionArgs()[0], "running");
|
||||||
assertEquals(selection.getSelectionArgs()[1], "road biking");
|
assertEquals(selection.selectionArgs()[1], "road biking");
|
||||||
assertEquals(selection.getSelectionArgs()[2], Long.toString(instant.toEpochMilli()));
|
assertEquals(selection.selectionArgs()[2], Long.toString(instant.toEpochMilli()));
|
||||||
assertEquals(selection.getSelectionArgs()[3], Long.toString(instant.toEpochMilli() + oneDay));
|
assertEquals(selection.selectionArgs()[3], Long.toString(instant.toEpochMilli() + oneDay));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +1,16 @@
|
|||||||
package de.dennisguse.opentracks.data.model;
|
package de.dennisguse.opentracks.data.model;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.temporal.ChronoUnit;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.data.models.Distance;
|
import de.dennisguse.opentracks.data.models.Distance;
|
||||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||||
|
|
||||||
public class TrackPointTest {
|
public class TrackPointTest {
|
||||||
|
|
||||||
@Test
|
|
||||||
public void isRecent_true() {
|
|
||||||
TrackPoint tp = new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.now());
|
|
||||||
|
|
||||||
assertTrue(tp.isRecent());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void isRecent_false() {
|
|
||||||
TrackPoint tp = new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.now().minus(2, ChronoUnit.MINUTES));
|
|
||||||
|
|
||||||
assertFalse(tp.isRecent());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void distanceToPrevious() {
|
public void distanceToPrevious() {
|
||||||
TrackPoint tp1 = new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochMilli(0))
|
TrackPoint tp1 = new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochMilli(0))
|
||||||
|
|||||||
+2
-2
@@ -5,8 +5,8 @@ import org.junit.runner.RunWith;
|
|||||||
import org.junit.runners.Parameterized;
|
import org.junit.runners.Parameterized;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
@@ -18,7 +18,7 @@ public class TrackFilenameGenerator2Test {
|
|||||||
|
|
||||||
@Parameterized.Parameters
|
@Parameterized.Parameters
|
||||||
public static Collection<String> data() {
|
public static Collection<String> data() {
|
||||||
return Arrays.asList(
|
return List.of(
|
||||||
"{name}_{starime}",
|
"{name}_{starime}",
|
||||||
"{name",
|
"{name",
|
||||||
"name}");
|
"name}");
|
||||||
|
|||||||
-59
@@ -1,59 +0,0 @@
|
|||||||
package de.dennisguse.opentracks.io.file.exporter;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
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 java.time.ZoneOffset;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
|
||||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
|
||||||
|
|
||||||
@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>
|
|
||||||
<coord/>
|
|
||||||
<when>1970-01-01T01:00:00+01:00</when>
|
|
||||||
<coord/>
|
|
||||||
<ExtendedData>
|
|
||||||
<SchemaData schemaUrl="#schema">
|
|
||||||
</SchemaData>
|
|
||||||
</ExtendedData>
|
|
||||||
</Track>
|
|
||||||
""";
|
|
||||||
|
|
||||||
// given
|
|
||||||
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0));
|
|
||||||
|
|
||||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
|
||||||
KMLTrackExporter kmlTrackWriter = (KMLTrackExporter) TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context, null);
|
|
||||||
kmlTrackWriter.prepare(outputStream);
|
|
||||||
|
|
||||||
kmlTrackWriter.writeTrackPoint(ZoneOffset.UTC, trackPoint);
|
|
||||||
kmlTrackWriter.writeTrackPoint(ZoneOffset.ofTotalSeconds(3600), trackPoint);
|
|
||||||
|
|
||||||
// when
|
|
||||||
kmlTrackWriter.writeCloseSegment();
|
|
||||||
kmlTrackWriter.close();
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(expected, outputStream.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+120
-58
@@ -35,6 +35,7 @@ import java.io.InputStreamReader;
|
|||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
@@ -168,9 +169,12 @@ public class ExportImportTest {
|
|||||||
|
|
||||||
sendLocation(trackPointCreator, "2020-02-02T02:03:22Z", 3, 16, 10, 13, 15, 10, 0);
|
sendLocation(trackPointCreator, "2020-02-02T02:03:22Z", 3, 16, 10, 13, 15, 10, 0);
|
||||||
|
|
||||||
sendLocation(trackPointCreator, "2020-02-02T02:03:23Z", 3, 16.001, 10, 27, 15, 10, 0);
|
trackPointCreator.setClock("2020-02-02T02:03:30Z");
|
||||||
|
service.getTrackRecordingManager().onIdle();
|
||||||
|
|
||||||
trackPointCreator.setClock("2020-02-02T02:03:24Z");
|
sendLocation(trackPointCreator, "2020-02-02T02:03:50Z", 3, 16.001, 10, 27, 15, 10, 0);
|
||||||
|
|
||||||
|
trackPointCreator.setClock("2020-02-02T02:04:00Z");
|
||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
@@ -182,8 +186,103 @@ public class ExportImportTest {
|
|||||||
track = contentProviderUtils.getTrack(trackId);
|
track = contentProviderUtils.getTrack(trackId);
|
||||||
trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||||
markers = contentProviderUtils.getMarkers(trackId);
|
markers = contentProviderUtils.getMarkers(trackId);
|
||||||
assertEquals(12, trackPoints.size());
|
}
|
||||||
assertEquals(2, markers.size());
|
|
||||||
|
@LargeTest
|
||||||
|
@Test
|
||||||
|
public void track() throws TimeoutException {
|
||||||
|
setUp();
|
||||||
|
|
||||||
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
|
TrackStatistics trackStatistics = track.getTrackStatistics();
|
||||||
|
|
||||||
|
assertEquals(ZoneOffset.of("+01:00"), track.getZoneOffset());
|
||||||
|
assertEquals(Instant.parse("2020-02-02T02:02:02Z"), trackStatistics.getStartTime());
|
||||||
|
assertEquals(Instant.parse("2020-02-02T02:04:00Z"), trackStatistics.getStopTime());
|
||||||
|
|
||||||
|
assertEquals(Duration.ofSeconds(56), trackStatistics.getTotalTime());
|
||||||
|
assertEquals(Duration.ofSeconds(26), trackStatistics.getMovingTime()); //TODO Likely too low
|
||||||
|
|
||||||
|
// Distance
|
||||||
|
assertEquals(222125.53125, trackStatistics.getTotalDistance().toM(), 0.01); //TODO Too low
|
||||||
|
|
||||||
|
// Speed
|
||||||
|
assertEquals(8543.29, trackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||||
|
assertEquals(3966.52, trackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||||
|
assertEquals(8543.28, trackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
|
||||||
|
|
||||||
|
// Altitude
|
||||||
|
assertEquals(10, trackStatistics.getMinAltitude(), 0.01);
|
||||||
|
assertEquals(10, trackStatistics.getMaxAltitude(), 0.01);
|
||||||
|
|
||||||
|
assertEquals(2, trackStatistics.getTotalAltitudeGain(), 0.01);
|
||||||
|
assertEquals(2, trackStatistics.getTotalAltitudeLoss(), 0.01);
|
||||||
|
|
||||||
|
List<TrackPoint> actual = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||||
|
new TrackPointAssert().assertEquals(List.of(
|
||||||
|
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse("2020-02-02T02:02:02Z")),
|
||||||
|
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:02:03Z"))
|
||||||
|
.setLatitude(3)
|
||||||
|
.setLongitude(14)
|
||||||
|
.setAltitude(10)
|
||||||
|
.setSpeed(Speed.of(15))
|
||||||
|
.setAltitudeLoss(1f)
|
||||||
|
.setAltitudeGain(1f)
|
||||||
|
.setHorizontalAccuracy(Distance.of(10)),
|
||||||
|
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:02:04Z"))
|
||||||
|
.setSensorDistance(Distance.of(10))
|
||||||
|
.setSpeed(Speed.of(15))
|
||||||
|
.setHeartRate(HeartRate.of(66))
|
||||||
|
.setCadence(3)
|
||||||
|
.setPower(50)
|
||||||
|
.setAltitudeLoss(1f)
|
||||||
|
.setAltitudeGain(1f),
|
||||||
|
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:02:15Z"))
|
||||||
|
.setHeartRate(HeartRate.of(68))
|
||||||
|
.setCadence(3)
|
||||||
|
.setPower(50),
|
||||||
|
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:02:17Z"))
|
||||||
|
.setLatitude(3)
|
||||||
|
.setLongitude(14.001)
|
||||||
|
.setAltitude(10)
|
||||||
|
.setSensorDistance(Distance.of(2))
|
||||||
|
.setSpeed(Speed.of(5))
|
||||||
|
.setAltitudeLoss(0f)
|
||||||
|
.setAltitudeGain(0f)
|
||||||
|
.setHorizontalAccuracy(Distance.of(10))
|
||||||
|
.setHeartRate(HeartRate.of(69))
|
||||||
|
.setCadence(3)
|
||||||
|
.setPower(50),
|
||||||
|
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse("2020-02-02T02:02:18Z")),
|
||||||
|
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse("2020-02-02T02:03:20Z")),
|
||||||
|
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:03:21Z"))
|
||||||
|
.setLatitude(3)
|
||||||
|
.setLongitude(14.002)
|
||||||
|
.setAltitude(10)
|
||||||
|
.setSpeed(Speed.of(15))
|
||||||
|
.setAltitudeLoss(0f)
|
||||||
|
.setAltitudeGain(0f)
|
||||||
|
.setHorizontalAccuracy(Distance.of(10)),
|
||||||
|
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse("2020-02-02T02:03:22Z"))
|
||||||
|
.setLatitude(3)
|
||||||
|
.setLongitude(16)
|
||||||
|
.setAltitude(10)
|
||||||
|
.setSpeed(Speed.of(15))
|
||||||
|
.setAltitudeLoss(0f)
|
||||||
|
.setAltitudeGain(0f)
|
||||||
|
.setHorizontalAccuracy(Distance.of(10)),
|
||||||
|
new TrackPoint(TrackPoint.Type.IDLE, Instant.parse("2020-02-02T02:03:30Z")),
|
||||||
|
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:03:50Z"))
|
||||||
|
.setLatitude(3)
|
||||||
|
.setLongitude(16.001)
|
||||||
|
.setAltitude(10)
|
||||||
|
.setSpeed(Speed.of(15))
|
||||||
|
.setAltitudeLoss(0f)
|
||||||
|
.setAltitudeGain(0f)
|
||||||
|
.setHorizontalAccuracy(Distance.of(10)),
|
||||||
|
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse("2020-02-02T02:04:00Z"))
|
||||||
|
|
||||||
|
), actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO Does not test marker images
|
//TODO Does not test marker images
|
||||||
@@ -225,43 +324,7 @@ public class ExportImportTest {
|
|||||||
|
|
||||||
// Time
|
// Time
|
||||||
assertEquals(track.getZoneOffset(), importedTrack.getZoneOffset());
|
assertEquals(track.getZoneOffset(), importedTrack.getZoneOffset());
|
||||||
assertEquals(Instant.parse("2020-02-02T02:02:02Z"), importedTrackStatistics.getStartTime());
|
assertEquals(track.getTrackStatistics(), importedTrackStatistics);
|
||||||
assertEquals(Instant.parse("2020-02-02T02:03:24Z"), importedTrackStatistics.getStopTime());
|
|
||||||
|
|
||||||
TrackStatistics originalTrackStatistics = track.getTrackStatistics();
|
|
||||||
|
|
||||||
assertEquals(originalTrackStatistics.getTotalTime(), importedTrackStatistics.getTotalTime());
|
|
||||||
assertEquals(Duration.ofSeconds(20), importedTrackStatistics.getTotalTime());
|
|
||||||
|
|
||||||
assertEquals(originalTrackStatistics.getMovingTime(), importedTrackStatistics.getMovingTime());
|
|
||||||
assertEquals(Duration.ofSeconds(4), importedTrackStatistics.getMovingTime());
|
|
||||||
|
|
||||||
// Distance
|
|
||||||
assertEquals(originalTrackStatistics.getTotalDistance(), importedTrackStatistics.getTotalDistance());
|
|
||||||
assertEquals(222238.70, importedTrackStatistics.getTotalDistance().toM(), 0.01);
|
|
||||||
|
|
||||||
// Speed
|
|
||||||
assertEquals(originalTrackStatistics.getMaxSpeed(), importedTrackStatistics.getMaxSpeed());
|
|
||||||
assertEquals(55559.67, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
|
||||||
|
|
||||||
assertEquals(originalTrackStatistics.getAverageSpeed(), importedTrackStatistics.getAverageSpeed());
|
|
||||||
assertEquals(11111.93, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
|
|
||||||
|
|
||||||
assertEquals(originalTrackStatistics.getAverageMovingSpeed(), importedTrackStatistics.getAverageMovingSpeed());
|
|
||||||
assertEquals(55559.67, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
|
||||||
|
|
||||||
// Altitude
|
|
||||||
assertEquals(originalTrackStatistics.getMinAltitude(), importedTrackStatistics.getMinAltitude(), 0.01);
|
|
||||||
assertEquals(10, importedTrackStatistics.getMinAltitude(), 0.01);
|
|
||||||
|
|
||||||
assertEquals(originalTrackStatistics.getMaxAltitude(), importedTrackStatistics.getMaxAltitude(), 0.01);
|
|
||||||
assertEquals(10, importedTrackStatistics.getMaxAltitude(), 0.01);
|
|
||||||
|
|
||||||
assertEquals(originalTrackStatistics.getTotalAltitudeGain(), importedTrackStatistics.getTotalAltitudeGain(), 0.01);
|
|
||||||
assertEquals(2, importedTrackStatistics.getTotalAltitudeGain(), 0.01);
|
|
||||||
|
|
||||||
assertEquals(originalTrackStatistics.getTotalAltitudeLoss(), importedTrackStatistics.getTotalAltitudeLoss(), 0.01);
|
|
||||||
assertEquals(2, importedTrackStatistics.getTotalAltitudeLoss(), 0.01);
|
|
||||||
|
|
||||||
// 4. markers
|
// 4. markers
|
||||||
assertMarkers();
|
assertMarkers();
|
||||||
@@ -347,7 +410,7 @@ public class ExportImportTest {
|
|||||||
.setSpeed(Speed.of(5))
|
.setSpeed(Speed.of(5))
|
||||||
.setAltitudeLoss(1f)
|
.setAltitudeLoss(1f)
|
||||||
.setAltitudeGain(1f)
|
.setAltitudeGain(1f)
|
||||||
.setSensorDistance(Distance.of(14))
|
.setSensorDistance(Distance.of(12))
|
||||||
.setHeartRate(69)
|
.setHeartRate(69)
|
||||||
.setPower(50f)
|
.setPower(50f)
|
||||||
.setCadence(3f)
|
.setCadence(3f)
|
||||||
@@ -368,7 +431,7 @@ public class ExportImportTest {
|
|||||||
.setAltitudeGain(0f)
|
.setAltitudeGain(0f)
|
||||||
.setSpeed(Speed.of(15))
|
.setSpeed(Speed.of(15))
|
||||||
.setHorizontalAccuracy(Distance.of(10)),
|
.setHorizontalAccuracy(Distance.of(10)),
|
||||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:03:23Z"))
|
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2020-02-02T02:03:50Z"))
|
||||||
.setLatitude(3)
|
.setLatitude(3)
|
||||||
.setLongitude(16.001)
|
.setLongitude(16.001)
|
||||||
.setAltitude(10)
|
.setAltitude(10)
|
||||||
@@ -379,24 +442,23 @@ public class ExportImportTest {
|
|||||||
), actual);
|
), actual);
|
||||||
|
|
||||||
// 3. trackstatistics
|
// 3. trackstatistics
|
||||||
TrackStatistics trackStatistics = track.getTrackStatistics();
|
|
||||||
TrackStatistics importedTrackStatistics = importedTrack.getTrackStatistics();
|
TrackStatistics importedTrackStatistics = importedTrack.getTrackStatistics();
|
||||||
|
|
||||||
// Time
|
// Time
|
||||||
assertEquals(track.getZoneOffset(), importedTrack.getZoneOffset());
|
assertEquals(track.getZoneOffset(), importedTrack.getZoneOffset());
|
||||||
assertEquals(Instant.parse("2020-02-02T02:02:03Z"), importedTrackStatistics.getStartTime());
|
assertEquals(Instant.parse("2020-02-02T02:02:03Z"), importedTrackStatistics.getStartTime());
|
||||||
assertEquals(Instant.parse("2020-02-02T02:03:23Z"), importedTrackStatistics.getStopTime());
|
assertEquals(Instant.parse("2020-02-02T02:03:50Z"), importedTrackStatistics.getStopTime());
|
||||||
|
|
||||||
assertEquals(Duration.ofSeconds(80), importedTrackStatistics.getTotalTime());
|
assertEquals(Duration.ofSeconds(107), importedTrackStatistics.getTotalTime());
|
||||||
assertEquals(Duration.ofSeconds(80), importedTrackStatistics.getMovingTime());
|
assertEquals(Duration.ofSeconds(107), importedTrackStatistics.getMovingTime());
|
||||||
|
|
||||||
// Distance
|
// Distance
|
||||||
assertEquals(222349.85, importedTrackStatistics.getTotalDistance().toM(), 0.01);
|
assertEquals(222347.85, importedTrackStatistics.getTotalDistance().toM(), 0.01);
|
||||||
|
|
||||||
// Speed
|
// Speed
|
||||||
assertEquals(2779.37, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
assertEquals(2078.01, importedTrackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||||
assertEquals(2779.37, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
|
assertEquals(2078.01, importedTrackStatistics.getAverageSpeed().toMPS(), 0.01);
|
||||||
assertEquals(2779.37, importedTrackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
|
assertEquals(2078.01, importedTrackStatistics.getAverageMovingSpeed().toMPS(), 0.01);
|
||||||
|
|
||||||
// Altitude
|
// Altitude
|
||||||
assertEquals(10, importedTrackStatistics.getMinAltitude(), 0.01);
|
assertEquals(10, importedTrackStatistics.getMinAltitude(), 0.01);
|
||||||
@@ -487,14 +549,14 @@ public class ExportImportTest {
|
|||||||
|
|
||||||
private void mockBLESensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power) {
|
private void mockBLESensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power) {
|
||||||
|
|
||||||
SensorDataSet sensorDataSet = new SensorDataSet();
|
SensorDataSet sensorDataSet = new SensorDataSet();
|
||||||
sensorDataSet.set(new SensorDataCyclingPower("power", "power", Power.of(power)));
|
sensorDataSet.set(new SensorDataCyclingPower("power", "power", Power.of(power)));
|
||||||
sensorDataSet.set(new SensorDataHeartRate("heartRate", "heartRate", HeartRate.of(heartRate)));
|
sensorDataSet.set(new SensorDataHeartRate("heartRate", "heartRate", HeartRate.of(heartRate)));
|
||||||
|
|
||||||
SensorDataCyclingCadence cyclingCadence = Mockito.mock(SensorDataCyclingCadence.class);
|
SensorDataCyclingCadence cyclingCadence = Mockito.mock(SensorDataCyclingCadence.class);
|
||||||
Mockito.when(cyclingCadence.hasValue()).thenReturn(true);
|
Mockito.when(cyclingCadence.hasValue()).thenReturn(true);
|
||||||
Mockito.when(cyclingCadence.getValue()).thenReturn(Cadence.of(cadence));
|
Mockito.when(cyclingCadence.getValue()).thenReturn(Cadence.of(cadence));
|
||||||
sensorDataSet.set(cyclingCadence);
|
sensorDataSet.set(cyclingCadence);
|
||||||
|
|
||||||
if (distance != null && speed != null) {
|
if (distance != null && speed != null) {
|
||||||
SensorDataCyclingDistanceSpeed.Data distanceSpeedData = Mockito.mock(SensorDataCyclingDistanceSpeed.Data.class);
|
SensorDataCyclingDistanceSpeed.Data distanceSpeedData = Mockito.mock(SensorDataCyclingDistanceSpeed.Data.class);
|
||||||
|
|||||||
+3
-3
@@ -141,8 +141,8 @@ public class GPXTrackImporterTest {
|
|||||||
|
|
||||||
// 3. trackstatistics
|
// 3. trackstatistics
|
||||||
TrackStatistics trackStatistics = importedTrack.getTrackStatistics();
|
TrackStatistics trackStatistics = importedTrack.getTrackStatistics();
|
||||||
assertEquals(1.44, trackStatistics.getMaxSpeed().toMPS(), 0.01);
|
assertEquals(0.75, trackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||||
assertEquals(Duration.ofSeconds(53), trackStatistics.getMovingTime());
|
assertEquals(Duration.ofSeconds(101), trackStatistics.getMovingTime());
|
||||||
|
|
||||||
// 4. trackpoints
|
// 4. trackpoints
|
||||||
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
||||||
@@ -189,7 +189,7 @@ public class GPXTrackImporterTest {
|
|||||||
|
|
||||||
// 3. trackstatistics
|
// 3. trackstatistics
|
||||||
TrackStatistics trackStatistics = importedTrack.getTrackStatistics();
|
TrackStatistics trackStatistics = importedTrack.getTrackStatistics();
|
||||||
assertEquals(4.0, trackStatistics.getMaxSpeed().toMPS(), 0.01);
|
assertEquals(5.0, trackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||||
assertEquals(Duration.ofSeconds(101), trackStatistics.getMovingTime());
|
assertEquals(Duration.ofSeconds(101), trackStatistics.getMovingTime());
|
||||||
|
|
||||||
// 4. trackpoints
|
// 4. trackpoints
|
||||||
|
|||||||
+1
@@ -146,6 +146,7 @@ public class TrackRecordingServiceMarkerTest {
|
|||||||
assertEquals(0.0, wpt.getLength().toM(), 0.01);
|
assertEquals(0.0, wpt.getLength().toM(), 0.01);
|
||||||
assertNotNull(wpt.getLocation());
|
assertNotNull(wpt.getLocation());
|
||||||
|
|
||||||
|
trackPointCreator.setClock("2020-02-02T02:02:04Z");
|
||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-231
@@ -88,7 +88,7 @@ public class TrackRecordingServiceRecordingTest {
|
|||||||
contentProviderUtils = new ContentProviderUtils(context);
|
contentProviderUtils = new ContentProviderUtils(context);
|
||||||
|
|
||||||
PreferencesUtils.setString(R.string.recording_distance_interval_key, R.string.recording_distance_interval_default);
|
PreferencesUtils.setString(R.string.recording_distance_interval_key, R.string.recording_distance_interval_default);
|
||||||
PreferencesUtils.setString(R.string.idle_speed_key, R.string.idle_speed_default);
|
PreferencesUtils.setString(R.string.idle_duration_key, R.string.idle_duration_default);
|
||||||
|
|
||||||
service = startService();
|
service = startService();
|
||||||
}
|
}
|
||||||
@@ -122,7 +122,7 @@ public class TrackRecordingServiceRecordingTest {
|
|||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(new TrackStatistics(startTime, stopTime, 0, 1, 0, 0, 0f, 0f)
|
assertEquals(new TrackStatistics(startTime, stopTime, 0, 1, 1, 0, 0f, 0f)
|
||||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||||
|
|
||||||
new TrackPointAssert().assertEquals(List.of(
|
new TrackPointAssert().assertEquals(List.of(
|
||||||
@@ -150,7 +150,7 @@ public class TrackRecordingServiceRecordingTest {
|
|||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(new TrackStatistics(startTime, pauseTime, 0, 1, 0, 0, 0f, 0f)
|
assertEquals(new TrackStatistics(startTime, pauseTime, 0, 1, 1, 0, 0f, 0f)
|
||||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||||
|
|
||||||
new TrackPointAssert().assertEquals(List.of(
|
new TrackPointAssert().assertEquals(List.of(
|
||||||
@@ -166,7 +166,7 @@ public class TrackRecordingServiceRecordingTest {
|
|||||||
service.resumeTrack(trackId);
|
service.resumeTrack(trackId);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(new TrackStatistics(startTime, resumeTime, 0, 1, 0, 0, 0f, 0f)
|
assertEquals(new TrackStatistics(startTime, resumeTime, 0, 1, 1, 0, 0f, 0f)
|
||||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||||
|
|
||||||
new TrackPointAssert().assertEquals(List.of(
|
new TrackPointAssert().assertEquals(List.of(
|
||||||
@@ -299,7 +299,7 @@ public class TrackRecordingServiceRecordingTest {
|
|||||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
|
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(new TrackStatistics(startTime, gps1, 0, 1, 0, 0, 0f, 0f)
|
assertEquals(new TrackStatistics(startTime, gps1, 0, 1, 1, 15, 0f, 0f)
|
||||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@@ -307,7 +307,7 @@ public class TrackRecordingServiceRecordingTest {
|
|||||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.0001, 35.0, 1, 15);
|
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.0001, 35.0, 1, 15);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(new TrackStatistics(startTime, gps2, 11.113178253173828f, 4, 3, 15, 0f, 0f)
|
assertEquals(new TrackStatistics(startTime, gps2, 11.113178253173828f, 4, 4, 15, 0f, 0f)
|
||||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@@ -315,7 +315,7 @@ public class TrackRecordingServiceRecordingTest {
|
|||||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps3, 45.0002, 35.0, 1, 15);
|
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps3, 45.0002, 35.0, 1, 15);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(new TrackStatistics(startTime, gps3, 22.226356506347656, 6, 5, 15, 0f, 0f)
|
assertEquals(new TrackStatistics(startTime, gps3, 22.226356506347656, 6, 6, 15, 0f, 0f)
|
||||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||||
|
|
||||||
|
|
||||||
@@ -325,7 +325,7 @@ public class TrackRecordingServiceRecordingTest {
|
|||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(new TrackStatistics(startTime, stopTime, 22.226356506347656, 10, 5, 15, 0f, 0f)
|
assertEquals(new TrackStatistics(startTime, stopTime, 22.226356506347656, 10, 10, 15, 0f, 0f)
|
||||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||||
|
|
||||||
new TrackPointAssert().assertEquals(List.of(
|
new TrackPointAssert().assertEquals(List.of(
|
||||||
@@ -357,71 +357,6 @@ public class TrackRecordingServiceRecordingTest {
|
|||||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
@Test
|
|
||||||
public void testRecording_gpsOnly_recordingDistance_above_speed_0() {
|
|
||||||
// given
|
|
||||||
String startTime = "2020-02-02T02:02:02Z";
|
|
||||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
|
||||||
trackPointCreator.setClock(startTime);
|
|
||||||
Track.Id trackId = service.startNewTrack();
|
|
||||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
|
||||||
|
|
||||||
// when
|
|
||||||
String gps1 = "2020-02-02T02:02:03Z";
|
|
||||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 0);
|
|
||||||
|
|
||||||
// then
|
|
||||||
TrackStatistics gps1statistics = new TrackStatistics(startTime, gps1, 0, 1, 0, 0, 0f, 0f);
|
|
||||||
assertEquals(gps1statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
|
||||||
|
|
||||||
// when
|
|
||||||
String gps2 = "2020-02-02T02:02:06Z";
|
|
||||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.0001, 35.0, 1, 0);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(gps1statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
|
||||||
|
|
||||||
// when
|
|
||||||
String gps3 = "2020-02-02T02:02:08Z";
|
|
||||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps3, 45.0002, 35.0, 1, 0);
|
|
||||||
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(gps1statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
|
||||||
|
|
||||||
// when
|
|
||||||
String stopTime = "2020-02-02T02:02:12Z";
|
|
||||||
trackPointCreator.setClock(stopTime);
|
|
||||||
service.endCurrentTrack();
|
|
||||||
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(new TrackStatistics(startTime, stopTime, 0, 10, 0, 0, 0f, 0f)
|
|
||||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
|
||||||
|
|
||||||
new TrackPointAssert().assertEquals(List.of(
|
|
||||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
|
||||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps1))
|
|
||||||
.setLatitude(45)
|
|
||||||
.setLongitude(35)
|
|
||||||
.setHorizontalAccuracy(Distance.of(1))
|
|
||||||
.setSpeed(Speed.of(0))
|
|
||||||
.setAltitudeGain(0f)
|
|
||||||
.setAltitudeLoss(0f),
|
|
||||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps3))
|
|
||||||
.setLatitude(45.0001)
|
|
||||||
.setLongitude(35)
|
|
||||||
.setHorizontalAccuracy(Distance.of(1))
|
|
||||||
.setSpeed(Speed.of(0))
|
|
||||||
.setAltitudeGain(0f)
|
|
||||||
.setAltitudeLoss(0f),
|
|
||||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
|
|
||||||
.setAltitudeGain(0f)
|
|
||||||
.setAltitudeLoss(0f)
|
|
||||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testRecording_gpsOnly_recordingDistance_below() {
|
public void testRecording_gpsOnly_recordingDistance_below() {
|
||||||
@@ -437,7 +372,7 @@ public class TrackRecordingServiceRecordingTest {
|
|||||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
|
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
TrackStatistics gps1Statistics = new TrackStatistics(startTime, gps1, 0, 1, 0, 0, 0f, 0f);
|
TrackStatistics gps1Statistics = new TrackStatistics(startTime, gps1, 0, 1, 1, 15, 0f, 0f);
|
||||||
assertEquals(gps1Statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
assertEquals(gps1Statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@@ -461,7 +396,7 @@ public class TrackRecordingServiceRecordingTest {
|
|||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(new TrackStatistics(startTime, stopTime, 2.222635507583618, 10, 5, 15, 0f, 0f)
|
assertEquals(new TrackStatistics(startTime, stopTime, 2.222635507583618, 10, 10, 15, 0f, 0f)
|
||||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||||
|
|
||||||
new TrackPointAssert().assertEquals(List.of(
|
new TrackPointAssert().assertEquals(List.of(
|
||||||
@@ -486,158 +421,6 @@ public class TrackRecordingServiceRecordingTest {
|
|||||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
@Test
|
|
||||||
public void testRecording_gpsOnly_recordingDistance_idle() {
|
|
||||||
// given
|
|
||||||
String startTime = "2020-02-02T02:02:02Z";
|
|
||||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
|
||||||
trackPointCreator.setClock(startTime);
|
|
||||||
Track.Id trackId = service.startNewTrack();
|
|
||||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
|
||||||
|
|
||||||
// when
|
|
||||||
String gps1 = "2020-02-02T02:02:03Z";
|
|
||||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 0);
|
|
||||||
|
|
||||||
// then
|
|
||||||
TrackStatistics gps1Statistics = new TrackStatistics(startTime, gps1, 0, 1, 0, 0, 0f, 0f);
|
|
||||||
assertEquals(gps1Statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
|
||||||
|
|
||||||
// when
|
|
||||||
String gps2 = "2020-02-02T02:02:06Z";
|
|
||||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.0, 35.0, 1, 0);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(gps1Statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
|
||||||
|
|
||||||
// when
|
|
||||||
String gps3 = "2020-02-02T02:02:08Z";
|
|
||||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps3, 45.0, 35.0, 1, 0);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(gps1Statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
|
||||||
|
|
||||||
|
|
||||||
// when
|
|
||||||
String stopTime = "2020-02-02T02:02:12Z";
|
|
||||||
trackPointCreator.setClock(stopTime);
|
|
||||||
service.endCurrentTrack();
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(new TrackStatistics(startTime, stopTime, 0, 10, 0, 0, 0f, 0f)
|
|
||||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
|
||||||
|
|
||||||
new TrackPointAssert().assertEquals(List.of(
|
|
||||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
|
||||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps1))
|
|
||||||
.setLatitude(45)
|
|
||||||
.setLongitude(35)
|
|
||||||
.setHorizontalAccuracy(Distance.of(1))
|
|
||||||
.setSpeed(Speed.of(0))
|
|
||||||
.setAltitudeGain(0f)
|
|
||||||
.setAltitudeLoss(0f),
|
|
||||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps3))
|
|
||||||
.setLatitude(45.00002)
|
|
||||||
.setLongitude(35)
|
|
||||||
.setHorizontalAccuracy(Distance.of(1))
|
|
||||||
.setSpeed(Speed.of(0))
|
|
||||||
.setAltitudeGain(0f)
|
|
||||||
.setAltitudeLoss(0f),
|
|
||||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
|
|
||||||
.setAltitudeGain(0f)
|
|
||||||
.setAltitudeLoss(0f)
|
|
||||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
|
||||||
@Test
|
|
||||||
public void testRecording_gpsOnly_recordingDistance_idle_movement() {
|
|
||||||
// given
|
|
||||||
String startTime = "2020-02-02T02:02:02Z";
|
|
||||||
TrackPointCreator trackPointCreator = service.getTrackPointCreator();
|
|
||||||
trackPointCreator.setClock(startTime);
|
|
||||||
Track.Id trackId = service.startNewTrack();
|
|
||||||
trackPointCreator.getSensorManager().setAltitudeSumManager(altitudeSumManager);
|
|
||||||
|
|
||||||
// when
|
|
||||||
String gps1 = "2020-02-02T02:02:03Z";
|
|
||||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(new TrackStatistics(startTime, gps1, 0, 1, 0, 0, 0f, 0f)
|
|
||||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
|
||||||
|
|
||||||
// when
|
|
||||||
String gps2 = "2020-02-02T02:02:06Z";
|
|
||||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.0, 35.0, 1, 0);
|
|
||||||
|
|
||||||
// then
|
|
||||||
final TrackStatistics gps2statistics = new TrackStatistics(startTime, gps2, 0, 4, 0, 0, 0f, 0f);
|
|
||||||
assertEquals(gps2statistics
|
|
||||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
|
||||||
|
|
||||||
// when
|
|
||||||
String gps3 = "2020-02-02T02:02:08Z";
|
|
||||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps3, 45.0, 35.0, 1, 0);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(gps2statistics, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
|
||||||
|
|
||||||
|
|
||||||
// when
|
|
||||||
String gps4 = "2020-02-02T02:02:10Z";
|
|
||||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps4, 45.0, 35.0, 1, 15);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(new TrackStatistics(startTime, gps4, 0, 8, 0, 0, 0f, 0f)
|
|
||||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
|
||||||
|
|
||||||
// when
|
|
||||||
String stopTime = "2020-02-02T02:02:12Z";
|
|
||||||
trackPointCreator.setClock(stopTime);
|
|
||||||
service.endCurrentTrack();
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(new TrackStatistics(startTime, stopTime, 0, 10, 0, 0, 0f, 0f)
|
|
||||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
|
||||||
|
|
||||||
new TrackPointAssert().assertEquals(List.of(
|
|
||||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.parse(startTime)),
|
|
||||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps1))
|
|
||||||
.setLatitude(45)
|
|
||||||
.setLongitude(35)
|
|
||||||
.setHorizontalAccuracy(Distance.of(1))
|
|
||||||
.setSpeed(Speed.of(15))
|
|
||||||
.setAltitudeGain(0f)
|
|
||||||
.setAltitudeLoss(0f),
|
|
||||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps2))
|
|
||||||
.setLatitude(45)
|
|
||||||
.setLongitude(35)
|
|
||||||
.setHorizontalAccuracy(Distance.of(1))
|
|
||||||
.setSpeed(Speed.of(0))
|
|
||||||
.setAltitudeGain(0f)
|
|
||||||
.setAltitudeLoss(0f),
|
|
||||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps3))
|
|
||||||
.setLatitude(45)
|
|
||||||
.setLongitude(35)
|
|
||||||
.setHorizontalAccuracy(Distance.of(1))
|
|
||||||
.setSpeed(Speed.of(0))
|
|
||||||
.setAltitudeGain(0f)
|
|
||||||
.setAltitudeLoss(0f),
|
|
||||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse(gps4))
|
|
||||||
.setLatitude(45)
|
|
||||||
.setLongitude(35)
|
|
||||||
.setHorizontalAccuracy(Distance.of(1))
|
|
||||||
.setSpeed(Speed.of(15))
|
|
||||||
.setAltitudeGain(0f)
|
|
||||||
.setAltitudeLoss(0f),
|
|
||||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.parse(stopTime))
|
|
||||||
.setAltitudeGain(0f)
|
|
||||||
.setAltitudeLoss(0f)
|
|
||||||
), TestDataUtil.getTrackPoints(contentProviderUtils, trackId));
|
|
||||||
}
|
|
||||||
|
|
||||||
@MediumTest
|
@MediumTest
|
||||||
@Test
|
@Test
|
||||||
public void testRecording_gpsOnly_recordingDistance_movement_non_idle() {
|
public void testRecording_gpsOnly_recordingDistance_movement_non_idle() {
|
||||||
@@ -721,7 +504,7 @@ public class TrackRecordingServiceRecordingTest {
|
|||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(new TrackStatistics(startTime, stopTime, 0, 10, 0, 0, 0f, 0f)
|
assertEquals(new TrackStatistics(startTime, stopTime, 0, 10, 10, 0, 0f, 0f)
|
||||||
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
, contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||||
|
|
||||||
|
|
||||||
@@ -749,14 +532,14 @@ public class TrackRecordingServiceRecordingTest {
|
|||||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
|
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps1, 45.0, 35.0, 1, 15);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(new TrackStatistics(startTime, gps1, 0, 1, 0, 0, 0f, 0f), contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
assertEquals(new TrackStatistics(startTime, gps1, 0, 1, 1, 15, 0f, 0f), contentProviderUtils.getTrack(trackId).getTrackStatistics());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String gps2 = "2020-02-02T02:02:06Z";
|
String gps2 = "2020-02-02T02:02:06Z";
|
||||||
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.1, 35.0, 1, 15);
|
TrackRecordingServiceTestUtils.sendGPSLocation(trackPointCreator, gps2, 45.1, 35.0, 1, 15);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(new TrackStatistics(startTime, gps2, 11113.275390625, 4, 3, 3704.4251302083335f, 0f, 0f).toString(), contentProviderUtils.getTrack(trackId).getTrackStatistics().toString());
|
assertEquals(new TrackStatistics(startTime, gps2, 11113.275390625, 4, 4, 2778.31884765625f, 0f, 0f).toString(), contentProviderUtils.getTrack(trackId).getTrackStatistics().toString());
|
||||||
|
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@@ -765,7 +548,7 @@ public class TrackRecordingServiceRecordingTest {
|
|||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(new TrackStatistics(startTime, stopTime, 11113.275390625, 10, 3, 3704.4251302083335f, 0f, 0f).toString(), contentProviderUtils.getTrack(trackId).getTrackStatistics().toString());
|
assertEquals(new TrackStatistics(startTime, stopTime, 11113.275390625, 10, 10, 1111.3275390625f, 0f, 0f).toString(), contentProviderUtils.getTrack(trackId).getTrackStatistics().toString());
|
||||||
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
|
|||||||
+18
-18
@@ -63,7 +63,7 @@ public class VoiceAnnouncementUtilsTest {
|
|||||||
stats.setTotalAltitudeGain(6000f);
|
stats.setTotalAltitudeGain(6000f);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString();
|
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, null, null).toString();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals("Total distance 20.0 kilometers. 1 hour 5 minutes 10 seconds. Average moving speed 18.4 kilometers per hour.", announcement);
|
assertEquals("Total distance 20.0 kilometers. 1 hour 5 minutes 10 seconds. Average moving speed 18.4 kilometers per hour.", announcement);
|
||||||
@@ -79,7 +79,7 @@ public class VoiceAnnouncementUtilsTest {
|
|||||||
stats.setTotalAltitudeGain(6000f);
|
stats.setTotalAltitudeGain(6000f);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString();
|
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, null, null).toString();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals("Total distance 20.0 kilometers. 1 hour 1 second. Average moving speed 20.0 kilometers per hour.", announcement);
|
assertEquals("Total distance 20.0 kilometers. 1 hour 1 second. Average moving speed 20.0 kilometers per hour.", announcement);
|
||||||
@@ -95,7 +95,7 @@ public class VoiceAnnouncementUtilsTest {
|
|||||||
stats.setTotalAltitudeGain(6000f);
|
stats.setTotalAltitudeGain(6000f);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString();
|
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, null, null).toString();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals("Total distance 20.0 kilometers. 1 hour. Average moving speed 20.0 kilometers per hour.", announcement);
|
assertEquals("Total distance 20.0 kilometers. 1 hour. Average moving speed 20.0 kilometers per hour.", announcement);
|
||||||
@@ -111,7 +111,7 @@ public class VoiceAnnouncementUtilsTest {
|
|||||||
stats.setTotalAltitudeGain(6000f);
|
stats.setTotalAltitudeGain(6000f);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString();
|
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, null, null).toString();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals("Total distance 19.9 kilometers. 1 hour. Average moving speed 19.9 kilometers per hour.", announcement);
|
assertEquals("Total distance 19.9 kilometers. 1 hour. Average moving speed 19.9 kilometers per hour.", announcement);
|
||||||
@@ -133,7 +133,7 @@ public class VoiceAnnouncementUtilsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, lastInterval, null).toString();
|
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, lastInterval, null).toString();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals("Total distance 14.2 kilometers. 16 minutes 39 seconds. Average moving speed 51.2 kilometers per hour. Lap speed 51.2 kilometers per hour.", announcement);
|
assertEquals("Total distance 14.2 kilometers. 16 minutes 39 seconds. Average moving speed 51.2 kilometers per hour. Lap speed 51.2 kilometers per hour.", announcement);
|
||||||
@@ -149,7 +149,7 @@ public class VoiceAnnouncementUtilsTest {
|
|||||||
stats.setTotalAltitudeGain(6000f);
|
stats.setTotalAltitudeGain(6000f);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, false, null, null).toString();
|
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, false, null, null).toString();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals("Total distance 20.0 kilometers. 1 hour 5 minutes 10 seconds. Pace 3 minutes 15 seconds per kilometer.", announcement);
|
assertEquals("Total distance 20.0 kilometers. 1 hour 5 minutes 10 seconds. Pace 3 minutes 15 seconds per kilometer.", announcement);
|
||||||
@@ -171,7 +171,7 @@ public class VoiceAnnouncementUtilsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, false, lastInterval, null).toString();
|
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, false, lastInterval, null).toString();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals("Total distance 14.2 kilometers. 16 minutes 39 seconds. Pace 1 minute 10 seconds per kilometer. Lap time 1 minute 10 seconds per kilometer.", announcement);
|
assertEquals("Total distance 14.2 kilometers. 16 minutes 39 seconds. Pace 1 minute 10 seconds per kilometer. Lap time 1 minute 10 seconds per kilometer.", announcement);
|
||||||
@@ -187,7 +187,7 @@ public class VoiceAnnouncementUtilsTest {
|
|||||||
stats.setTotalAltitudeGain(6000f);
|
stats.setTotalAltitudeGain(6000f);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_FEET, true, null, null).toString();
|
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.IMPERIAL_FEET, true, null, null).toString();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals("Total distance 12.4 miles. 1 hour 5 minutes 10 seconds. Average moving speed 11.4 miles per hour.", announcement);
|
assertEquals("Total distance 12.4 miles. 1 hour 5 minutes 10 seconds. Average moving speed 11.4 miles per hour.", announcement);
|
||||||
@@ -201,7 +201,7 @@ public class VoiceAnnouncementUtilsTest {
|
|||||||
stats.setMovingTime(Duration.ofHours(1));
|
stats.setMovingTime(Duration.ofHours(1));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_FEET, true, null, null).toString();
|
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.IMPERIAL_FEET, true, null, null).toString();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals("Total distance 1.1 miles. 1 hour. Average moving speed 1.1 miles per hour.", announcement);
|
assertEquals("Total distance 1.1 miles. 1 hour. Average moving speed 1.1 miles per hour.", announcement);
|
||||||
@@ -215,7 +215,7 @@ public class VoiceAnnouncementUtilsTest {
|
|||||||
stats.setMovingTime(Duration.ofHours(1));
|
stats.setMovingTime(Duration.ofHours(1));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_METER, true, null, null).toString();
|
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.IMPERIAL_METER, true, null, null).toString();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals("Total distance 1.1 miles. 1 hour. Average moving speed 1.1 miles per hour.", announcement);
|
assertEquals("Total distance 1.1 miles. 1 hour. Average moving speed 1.1 miles per hour.", announcement);
|
||||||
@@ -229,7 +229,7 @@ public class VoiceAnnouncementUtilsTest {
|
|||||||
stats.setMovingTime(Duration.ofHours(1));
|
stats.setMovingTime(Duration.ofHours(1));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, null, null).toString();
|
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, null, null).toString();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals("Total distance 1.1 kilometers. 1 hour. Average moving speed 1.1 kilometers per hour.", announcement);
|
assertEquals("Total distance 1.1 kilometers. 1 hour. Average moving speed 1.1 kilometers per hour.", announcement);
|
||||||
@@ -251,7 +251,7 @@ public class VoiceAnnouncementUtilsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_FEET, true, lastInterval, null).toString();
|
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.IMPERIAL_FEET, true, lastInterval, null).toString();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals("Total distance 8.8 miles. 16 minutes 39 seconds. Average moving speed 31.8 miles per hour. Lap speed 31.8 miles per hour.", announcement);
|
assertEquals("Total distance 8.8 miles. 16 minutes 39 seconds. Average moving speed 31.8 miles per hour. Lap speed 31.8 miles per hour.", announcement);
|
||||||
@@ -267,7 +267,7 @@ public class VoiceAnnouncementUtilsTest {
|
|||||||
stats.setTotalAltitudeGain(6000f);
|
stats.setTotalAltitudeGain(6000f);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_FEET, false, null, null).toString();
|
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.IMPERIAL_FEET, false, null, null).toString();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals("Total distance 12.4 miles. 1 hour 5 minutes 10 seconds. Pace 5 minutes 15 seconds per mile.", announcement);
|
assertEquals("Total distance 12.4 miles. 1 hour 5 minutes 10 seconds. Pace 5 minutes 15 seconds per mile.", announcement);
|
||||||
@@ -289,7 +289,7 @@ public class VoiceAnnouncementUtilsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_FEET, false, lastInterval, null).toString();
|
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.IMPERIAL_FEET, false, lastInterval, null).toString();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals("Total distance 8.8 miles. 16 minutes 39 seconds. Pace 1 minute 53 seconds per mile. Lap time 1 minute 53 seconds per mile.", announcement);
|
assertEquals("Total distance 8.8 miles. 16 minutes 39 seconds. Pace 1 minute 53 seconds per mile. Lap time 1 minute 53 seconds per mile.", announcement);
|
||||||
@@ -312,10 +312,10 @@ public class VoiceAnnouncementUtilsTest {
|
|||||||
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
SensorStatistics sensorStatistics = new SensorStatistics(HeartRate.of(180f), HeartRate.of(180f), null, null, null);
|
SensorStatistics sensorStatistics = new SensorStatistics(HeartRate.of(180f), HeartRate.of(180f), null, null, null, null);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, lastInterval, sensorStatistics).toString();
|
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, lastInterval, sensorStatistics).toString();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals("Total distance 14.2 kilometers. 16 minutes 39 seconds. Average moving speed 51.2 kilometers per hour. Lap speed 51.2 kilometers per hour. Average heart rate 180 bpm. Current heart rate 133 bpm.", announcement);
|
assertEquals("Total distance 14.2 kilometers. 16 minutes 39 seconds. Average moving speed 51.2 kilometers per hour. Lap speed 51.2 kilometers per hour. Average heart rate 180 bpm. Current heart rate 133 bpm.", announcement);
|
||||||
@@ -342,10 +342,10 @@ public class VoiceAnnouncementUtilsTest {
|
|||||||
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
SensorStatistics sensorStatistics = new SensorStatistics(HeartRate.of(180f), HeartRate.of(180f), null, null, null);
|
SensorStatistics sensorStatistics = new SensorStatistics(HeartRate.of(180f), HeartRate.of(180f), null, null, null, null);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.METRIC, true, lastInterval, sensorStatistics).toString();
|
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, lastInterval, sensorStatistics).toString();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(" Current heart rate 133 bpm.", announcement);
|
assertEquals(" Current heart rate 133 bpm.", announcement);
|
||||||
|
|||||||
+62
-88
@@ -83,35 +83,17 @@ public class TrackStatisticsUpdaterTest {
|
|||||||
TrackStatistics statistics = subject.getTrackStatistics();
|
TrackStatistics statistics = subject.getTrackStatistics();
|
||||||
assertEquals(142.26, statistics.getTotalDistance().toM(), 0.01);
|
assertEquals(142.26, statistics.getTotalDistance().toM(), 0.01);
|
||||||
assertEquals(Duration.ofSeconds(12), statistics.getTotalTime());
|
assertEquals(Duration.ofSeconds(12), statistics.getTotalTime());
|
||||||
assertEquals(Duration.ofSeconds(10), statistics.getMovingTime());
|
assertEquals(Duration.ofSeconds(12), statistics.getMovingTime());
|
||||||
|
|
||||||
assertEquals(2.5, statistics.getMinAltitude(), 0.01);
|
assertEquals(2.5, statistics.getMinAltitude(), 0.01);
|
||||||
assertEquals(32.5, statistics.getMaxAltitude(), 0.01);
|
assertEquals(32.5, statistics.getMaxAltitude(), 0.01);
|
||||||
assertEquals(36, statistics.getTotalAltitudeGain(), 0.01);
|
assertEquals(36, statistics.getTotalAltitudeGain(), 0.01);
|
||||||
assertEquals(36, statistics.getTotalAltitudeLoss(), 0.01);
|
assertEquals(36, statistics.getTotalAltitudeLoss(), 0.01);
|
||||||
|
|
||||||
assertEquals(14.226, statistics.getMaxSpeed().toMPS(), 0.01);
|
assertEquals(11.85, statistics.getMaxSpeed().toMPS(), 0.01);
|
||||||
assertEquals(14.226, statistics.getAverageMovingSpeed().toMPS(), 0.01);
|
assertEquals(11.85, statistics.getAverageMovingSpeed().toMPS(), 0.01);
|
||||||
assertEquals(11.85, statistics.getAverageSpeed().toMPS(), 0.01);
|
assertEquals(11.85, statistics.getAverageSpeed().toMPS(), 0.01);
|
||||||
assertEquals(106.64f, statistics.getAverageHeartRate().getBPM(), 0.01);
|
assertEquals(106.834f, statistics.getAverageHeartRate().getBPM(), 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, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(2000));
|
|
||||||
TrackPoint tp3 = new TrackPoint(0.00001, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(3000));
|
|
||||||
|
|
||||||
// when
|
|
||||||
subject.addTrackPoint(tp1);
|
|
||||||
subject.addTrackPoint(tp2);
|
|
||||||
subject.addTrackPoint(tp3);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(0, subject.getTrackStatistics().getTotalDistance().toM(), 0.01);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -165,35 +147,6 @@ public class TrackStatisticsUpdaterTest {
|
|||||||
assertEquals(125.57, subject.getTrackStatistics().getTotalDistance().toM(), 0.01);
|
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, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(2000));
|
|
||||||
TrackPoint tp3 = new TrackPoint(0.00001, 0, Altitude.WGS84.of(5.0), Instant.ofEpochMilli(3000));
|
|
||||||
TrackPoint tp4 = new TrackPoint(0.00001, 0, Altitude.WGS84.of(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);
|
|
||||||
subject.addTrackPoint(tp2);
|
|
||||||
subject.addTrackPoint(tp3);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(0, subject.getTrackStatistics().getTotalDistance().toM(), 0.01);
|
|
||||||
|
|
||||||
// when
|
|
||||||
subject.addTrackPoint(tp4);
|
|
||||||
subject.addTrackPoint(tp5);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertEquals(15, subject.getTrackStatistics().getTotalDistance().toM(), 0.01);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void addTrackPoint_distance_from_GPS_moving_and_sensor_disconnecting() {
|
public void addTrackPoint_distance_from_GPS_moving_and_sensor_disconnecting() {
|
||||||
// given
|
// given
|
||||||
@@ -225,42 +178,6 @@ public class TrackStatisticsUpdaterTest {
|
|||||||
assertEquals(59.18, subject.getTrackStatistics().getTotalDistance().toM(), 0.01);
|
assertEquals(59.18, subject.getTrackStatistics().getTotalDistance().toM(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addTrackPoint_maxSpeed_ignore_above_acceleration() {
|
|
||||||
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
|
|
||||||
assertEquals(Speed.of(0f), subject.getTrackStatistics().getMaxSpeed());
|
|
||||||
|
|
||||||
subject.addTrackPoint(new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0)));
|
|
||||||
assertEquals(Speed.of(0f), subject.getTrackStatistics().getMaxSpeed());
|
|
||||||
|
|
||||||
// Ignore as we set max speed if two consecutive trackpoints were considered moving
|
|
||||||
subject.addTrackPoint(new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(1))
|
|
||||||
.setSpeed(Speed.of(1f)));
|
|
||||||
assertEquals(Speed.of(0f), subject.getTrackStatistics().getMaxSpeed());
|
|
||||||
|
|
||||||
// Update max speed
|
|
||||||
subject.addTrackPoint(new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(2))
|
|
||||||
.setSpeed(Speed.of(1f)));
|
|
||||||
assertEquals(Speed.of(1f), subject.getTrackStatistics().getMaxSpeed());
|
|
||||||
|
|
||||||
// Update max speed
|
|
||||||
subject.addTrackPoint(new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(12))
|
|
||||||
.setSpeed(Speed.of(50f)));
|
|
||||||
assertEquals(Speed.of(50f), subject.getTrackStatistics().getMaxSpeed());
|
|
||||||
|
|
||||||
// Ignore; we were getting slower
|
|
||||||
subject.addTrackPoint(new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(13))
|
|
||||||
.setSpeed(Speed.of(5f)));
|
|
||||||
assertEquals(Speed.of(50f), subject.getTrackStatistics().getMaxSpeed());
|
|
||||||
|
|
||||||
// Ignore acceleration above 2g
|
|
||||||
subject.addTrackPoint(new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(14))
|
|
||||||
.setSpeed(Speed.of(500f)));
|
|
||||||
assertEquals(Speed.of(50f), subject.getTrackStatistics().getMaxSpeed());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void addTrackPoint_maxSpeed_multiple_segments() {
|
public void addTrackPoint_maxSpeed_multiple_segments() {
|
||||||
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
|
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
|
||||||
@@ -290,6 +207,64 @@ public class TrackStatisticsUpdaterTest {
|
|||||||
assertEquals(Speed.of(2f), subject.getTrackStatistics().getMaxSpeed());
|
assertEquals(Speed.of(2f), subject.getTrackStatistics().getMaxSpeed());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addTrackPoint_idle_withoutDistance() {
|
||||||
|
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
|
||||||
|
|
||||||
|
// when
|
||||||
|
subject.addTrackPoints(List.of(
|
||||||
|
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0)),
|
||||||
|
new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(1))
|
||||||
|
.setSpeed(Speed.of(2f)),
|
||||||
|
new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(2))
|
||||||
|
.setSpeed(Speed.of(2f)),
|
||||||
|
|
||||||
|
new TrackPoint(TrackPoint.Type.IDLE, Instant.ofEpochSecond(30)),
|
||||||
|
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochSecond(40))
|
||||||
|
.setHeartRate(50),
|
||||||
|
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochSecond(45))
|
||||||
|
.setHeartRate(50),
|
||||||
|
new TrackPoint(0, 1, Altitude.WGS84.of(0), Instant.ofEpochSecond(50)),
|
||||||
|
new TrackPoint(0, 2, Altitude.WGS84.of(0), Instant.ofEpochSecond(55)),
|
||||||
|
|
||||||
|
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.ofEpochSecond(60))
|
||||||
|
));
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals(Duration.ofSeconds(40), subject.getTrackStatistics().getMovingTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addTrackPoint_idle_withDistance() {
|
||||||
|
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
|
||||||
|
|
||||||
|
// when
|
||||||
|
subject.addTrackPoints(List.of(
|
||||||
|
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0)),
|
||||||
|
new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(1))
|
||||||
|
.setSensorDistance(Distance.of(10)),
|
||||||
|
new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(2))
|
||||||
|
.setSensorDistance(Distance.of(10)),
|
||||||
|
|
||||||
|
new TrackPoint(TrackPoint.Type.IDLE, Instant.ofEpochSecond(30))
|
||||||
|
.setSensorDistance(Distance.ofKilometer(1)),
|
||||||
|
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochSecond(40))
|
||||||
|
.setHeartRate(50),
|
||||||
|
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.ofEpochSecond(45))
|
||||||
|
.setHeartRate(50),
|
||||||
|
new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(50))
|
||||||
|
.setSensorDistance(Distance.of(10)),
|
||||||
|
new TrackPoint(0, 0, Altitude.WGS84.of(0), Instant.ofEpochSecond(55))
|
||||||
|
.setSensorDistance(Distance.of(10)),
|
||||||
|
|
||||||
|
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, Instant.ofEpochSecond(60))
|
||||||
|
));
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals(Duration.ofSeconds(45), subject.getTrackStatistics().getMovingTime());
|
||||||
|
assertEquals(Distance.of(1040), subject.getTrackStatistics().getTotalDistance());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void copy_constructor() {
|
public void copy_constructor() {
|
||||||
// given
|
// given
|
||||||
@@ -314,7 +289,6 @@ public class TrackStatisticsUpdaterTest {
|
|||||||
subject.addTrackPoint(tp5);
|
subject.addTrackPoint(tp5);
|
||||||
copy.addTrackPoint(tp5);
|
copy.addTrackPoint(tp5);
|
||||||
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(55.287, subject.getTrackStatistics().getTotalDistance().toM(), 0.01);
|
assertEquals(55.287, subject.getTrackStatistics().getTotalDistance().toM(), 0.01);
|
||||||
assertEquals(55.287, copy.getTrackStatistics().getTotalDistance().toM(), 0.01);
|
assertEquals(55.287, copy.getTrackStatistics().getTotalDistance().toM(), 0.01);
|
||||||
|
|||||||
@@ -57,12 +57,12 @@ public class StatisticsUtilsTest extends TestCase {
|
|||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(resultTrue.getFields().size(), 3);
|
assertEquals(resultTrue.getFields().size(), 3);
|
||||||
assertTrue(resultTrue.getFields().stream().anyMatch(f -> f.getKey().equals("key1")));
|
assertTrue(resultTrue.getFields().stream().anyMatch(f -> "key1".equals(f.getKey())));
|
||||||
assertTrue(resultTrue.getFields().stream().anyMatch(f -> f.getKey().equals("key3")));
|
assertTrue(resultTrue.getFields().stream().anyMatch(f -> "key3".equals(f.getKey())));
|
||||||
assertTrue(resultTrue.getFields().stream().anyMatch(f -> f.getKey().equals("key5")));
|
assertTrue(resultTrue.getFields().stream().anyMatch(f -> "key5".equals(f.getKey())));
|
||||||
|
|
||||||
assertEquals(resultFalse.getFields().size(), 2);
|
assertEquals(resultFalse.getFields().size(), 2);
|
||||||
assertTrue(resultFalse.getFields().stream().anyMatch(f -> f.getKey().equals("key2")));
|
assertTrue(resultFalse.getFields().stream().anyMatch(f -> "key2".equals(f.getKey())));
|
||||||
assertTrue(resultFalse.getFields().stream().anyMatch(f -> f.getKey().equals("key4")));
|
assertTrue(resultFalse.getFields().stream().anyMatch(f -> "key4".equals(f.getKey())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
"2020-02-02T03:02:03+01:00","TRACKPOINT",3,14,10,10,,54,1,1,,,,
|
"2020-02-02T03:02:03+01:00","TRACKPOINT",3,14,10,10,,54,1,1,,,,
|
||||||
"2020-02-02T03:02:04+01:00","TRACKPOINT",,,,,,54,1,1,10,66,3,50
|
"2020-02-02T03:02:04+01:00","TRACKPOINT",,,,,,54,1,1,10,66,3,50
|
||||||
"2020-02-02T03:02:15+01:00","TRACKPOINT",,,,,,,,,,68,3,50
|
"2020-02-02T03:02:15+01:00","TRACKPOINT",,,,,,,,,,68,3,50
|
||||||
"2020-02-02T03:02:16+01:00","TRACKPOINT",,,,,,18,,,2,69,3,50
|
|
||||||
"2020-02-02T03:02:17+01:00","TRACKPOINT",3,14.001,10,10,,18,0,0,2,69,3,50
|
"2020-02-02T03:02:17+01:00","TRACKPOINT",3,14.001,10,10,,18,0,0,2,69,3,50
|
||||||
"2020-02-02T03:02:18+01:00","SEGMENT_END_MANUAL",,,,,,,,,,,,
|
"2020-02-02T03:02:18+01:00","SEGMENT_END_MANUAL",,,,,,,,,,,,
|
||||||
"2020-02-02T03:03:20+01:00","SEGMENT_START_MANUAL",,,,,,,,,,,,
|
"2020-02-02T03:03:20+01:00","SEGMENT_START_MANUAL",,,,,,,,,,,,
|
||||||
"2020-02-02T03:03:21+01:00","TRACKPOINT",3,14.002,10,10,,54,0,0,,,,
|
"2020-02-02T03:03:21+01:00","TRACKPOINT",3,14.002,10,10,,54,0,0,,,,
|
||||||
"2020-02-02T03:03:22+01:00","SEGMENT_START_AUTOMATIC",3,16,10,10,,54,0,0,,,,
|
"2020-02-02T03:03:22+01:00","SEGMENT_START_AUTOMATIC",3,16,10,10,,54,0,0,,,,
|
||||||
"2020-02-02T03:03:23+01:00","TRACKPOINT",3,16.001,10,10,,54,0,0,,,,
|
"2020-02-02T03:03:30+01:00","IDLE",,,,,,,,,,,,
|
||||||
"2020-02-02T03:03:24+01:00","SEGMENT_END_MANUAL",,,,,,,,,,,,
|
"2020-02-02T03:03:50+01:00","TRACKPOINT",3,16.001,10,10,,54,0,0,,,,
|
||||||
|
"2020-02-02T03:04:00+01:00","SEGMENT_END_MANUAL",,,,,,,,,,,,
|
||||||
|
@@ -69,6 +69,7 @@ limitations under the License.
|
|||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:fullBackupContent="@xml/backup_rules_30_lower"
|
android:fullBackupContent="@xml/backup_rules_30_lower"
|
||||||
android:dataExtractionRules="@xml/backup_rules_31_higher"
|
android:dataExtractionRules="@xml/backup_rules_31_higher"
|
||||||
|
android:enableOnBackInvokedCallback="true"
|
||||||
android:hardwareAccelerated="true"
|
android:hardwareAccelerated="true"
|
||||||
android:hasFragileUserData="false"
|
android:hasFragileUserData="false"
|
||||||
android:icon="@drawable/ic_launcher"
|
android:icon="@drawable/ic_launcher"
|
||||||
@@ -333,7 +334,8 @@ limitations under the License.
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".TrackListActivity"
|
android:name=".TrackListActivity"
|
||||||
android:exported="true">
|
android:exported="true"
|
||||||
|
android:launchMode="singleTop">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEARCH" />
|
<action android:name="android.intent.action.SEARCH" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import android.view.View;
|
|||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.services.announcement.VoiceAnnouncement;
|
import de.dennisguse.opentracks.services.announcement.TTSManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Jimmy Shih
|
* @author Jimmy Shih
|
||||||
@@ -33,14 +33,14 @@ public abstract class AbstractActivity extends AppCompatActivity {
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
// Set volume control stream for text to speech
|
// Set volume control stream for text to speech
|
||||||
setVolumeControlStream(VoiceAnnouncement.AUDIO_STREAM);
|
setVolumeControlStream(TTSManager.AUDIO_STREAM);
|
||||||
|
|
||||||
setContentView(getRootView());
|
setContentView(getRootView());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onSupportNavigateUp() {
|
public boolean onSupportNavigateUp() {
|
||||||
onBackPressed();
|
getOnBackPressedDispatcher().onBackPressed();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ public abstract class AbstractTrackDeleteActivity extends AbstractActivity imple
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onConfirmDeleteDone(Track.Id... trackIds) {
|
public void onConfirmDeleteDone(Track.Id... trackIds) {
|
||||||
ArrayList<Track.Id> trackIdList = new ArrayList<>(Arrays.asList(trackIds))
|
ArrayList<Track.Id> trackIdList = Arrays.stream(trackIds)
|
||||||
.stream().filter(trackId -> !trackId.equals(getRecordingTrackId())).collect(Collectors.toCollection(ArrayList::new));
|
.filter(trackId -> !trackId.equals(getRecordingTrackId())).collect(Collectors.toCollection(ArrayList::new));
|
||||||
|
|
||||||
onDeleteConfirmed();
|
onDeleteConfirmed();
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import android.os.Build;
|
|||||||
import android.os.StrictMode;
|
import android.os.StrictMode;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.google.android.material.color.DynamicColors;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||||
@@ -28,6 +30,11 @@ public class Startup extends Application {
|
|||||||
|
|
||||||
// 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);
|
||||||
|
// In debug builds: show thread and VM warnings.
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
Log.d(TAG, "Enabling strict mode");
|
||||||
|
StrictMode.enableDefaults();
|
||||||
|
}
|
||||||
|
|
||||||
PreferencesUtils.initPreferences(this, getResources());
|
PreferencesUtils.initPreferences(this, getResources());
|
||||||
// Set default values of preferences on first start.
|
// Set default values of preferences on first start.
|
||||||
@@ -35,11 +42,8 @@ public class Startup extends Application {
|
|||||||
PreferencesUtils.applyDefaultUnit();
|
PreferencesUtils.applyDefaultUnit();
|
||||||
PreferencesUtils.applyNightMode();
|
PreferencesUtils.applyNightMode();
|
||||||
|
|
||||||
|
if (PreferencesUtils.shouldUseDynamicColors()) {
|
||||||
// In debug builds: show thread and VM warnings.
|
DynamicColors.applyToActivitiesIfAvailable(this);
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
Log.d(TAG, "Enabling strict mode");
|
|
||||||
StrictMode.enableDefaults();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package de.dennisguse.opentracks;
|
package de.dennisguse.opentracks;
|
||||||
|
|
||||||
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;
|
||||||
@@ -26,37 +25,26 @@ import android.graphics.drawable.AnimatedVectorDrawable;
|
|||||||
import android.location.LocationManager;
|
import android.location.LocationManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
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;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.content.res.AppCompatResources;
|
import androidx.appcompat.content.res.AppCompatResources;
|
||||||
import androidx.appcompat.widget.SearchView;
|
import androidx.appcompat.widget.SearchView;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.cursoradapter.widget.ResourceCursorAdapter;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.loader.app.LoaderManager;
|
|
||||||
import androidx.loader.content.CursorLoader;
|
|
||||||
import androidx.loader.content.Loader;
|
|
||||||
|
|
||||||
import com.google.android.material.button.MaterialButton;
|
import com.google.android.material.button.MaterialButton;
|
||||||
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.time.OffsetDateTime;
|
|
||||||
import java.time.ZoneOffset;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.data.models.ActivityType;
|
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.data.models.Distance;
|
|
||||||
import de.dennisguse.opentracks.data.models.DistanceFormatter;
|
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
import de.dennisguse.opentracks.data.tables.TracksColumns;
|
|
||||||
import de.dennisguse.opentracks.databinding.TrackListBinding;
|
import de.dennisguse.opentracks.databinding.TrackListBinding;
|
||||||
import de.dennisguse.opentracks.services.RecordingStatus;
|
import de.dennisguse.opentracks.services.RecordingStatus;
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||||
@@ -66,15 +54,14 @@ import de.dennisguse.opentracks.settings.PreferencesUtils;
|
|||||||
import de.dennisguse.opentracks.settings.SettingsActivity;
|
import de.dennisguse.opentracks.settings.SettingsActivity;
|
||||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||||
import de.dennisguse.opentracks.share.ShareUtils;
|
import de.dennisguse.opentracks.share.ShareUtils;
|
||||||
|
import de.dennisguse.opentracks.ui.TrackListAdapter;
|
||||||
import de.dennisguse.opentracks.ui.aggregatedStatistics.AggregatedStatisticsActivity;
|
import de.dennisguse.opentracks.ui.aggregatedStatistics.AggregatedStatisticsActivity;
|
||||||
import de.dennisguse.opentracks.ui.aggregatedStatistics.ConfirmDeleteDialogFragment;
|
import de.dennisguse.opentracks.ui.aggregatedStatistics.ConfirmDeleteDialogFragment;
|
||||||
import de.dennisguse.opentracks.ui.markers.MarkerListActivity;
|
import de.dennisguse.opentracks.ui.markers.MarkerListActivity;
|
||||||
import de.dennisguse.opentracks.ui.util.ActivityUtils;
|
import de.dennisguse.opentracks.ui.util.ActivityUtils;
|
||||||
import de.dennisguse.opentracks.ui.util.ListItemUtils;
|
|
||||||
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.PermissionRequester;
|
import de.dennisguse.opentracks.util.PermissionRequester;
|
||||||
import de.dennisguse.opentracks.util.StringUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An activity displaying a list of tracks.
|
* An activity displaying a list of tracks.
|
||||||
@@ -87,12 +74,10 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
|
|
||||||
// The following are set in onCreate
|
// The following are set in onCreate
|
||||||
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||||
private ResourceCursorAdapter resourceCursorAdapter;
|
private TrackListAdapter adapter;
|
||||||
|
|
||||||
private TrackListBinding viewBinding;
|
private TrackListBinding viewBinding;
|
||||||
|
|
||||||
private final TrackLoaderCallBack loaderCallbacks = new TrackLoaderCallBack();
|
|
||||||
|
|
||||||
// Preferences
|
// Preferences
|
||||||
private UnitSystem unitSystem = UnitSystem.defaultUnitSystem();
|
private UnitSystem unitSystem = UnitSystem.defaultUnitSystem();
|
||||||
|
|
||||||
@@ -128,11 +113,14 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
|
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
|
||||||
if (PreferencesUtils.isKey(R.string.stats_units_key, key)) {
|
if (PreferencesUtils.isKey(R.string.stats_units_key, key)) {
|
||||||
unitSystem = PreferencesUtils.getUnitSystem();
|
unitSystem = PreferencesUtils.getUnitSystem();
|
||||||
|
if (adapter != null) {
|
||||||
|
adapter.updateUnitSystem(unitSystem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (key != null) {
|
if (key != null) {
|
||||||
runOnUiThread(() -> {
|
runOnUiThread(() -> {
|
||||||
TrackListActivity.this.invalidateOptionsMenu();
|
TrackListActivity.this.invalidateOptionsMenu();
|
||||||
loaderCallbacks.restart();
|
loadData();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -140,6 +128,8 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
// Menu items
|
// Menu items
|
||||||
private MenuItem searchMenuItem;
|
private MenuItem searchMenuItem;
|
||||||
|
|
||||||
|
private String searchQuery;
|
||||||
|
|
||||||
private final TrackRecordingServiceConnection.Callback bindChangedCallback = (service, unused) -> {
|
private final TrackRecordingServiceConnection.Callback bindChangedCallback = (service, unused) -> {
|
||||||
service.getRecordingStatusObservable()
|
service.getRecordingStatusObservable()
|
||||||
.observe(TrackListActivity.this, this::onRecordingStatusChanged);
|
.observe(TrackListActivity.this, this::onRecordingStatusChanged);
|
||||||
@@ -180,63 +170,10 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
viewBinding.trackList.setEmptyView(viewBinding.trackListEmptyView);
|
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
|
||||||
viewBinding.trackList.setOnItemClickListener((parent, view, position, trackIdId) -> {
|
adapter = new TrackListAdapter(this, viewBinding.trackList, recordingStatus, unitSystem);
|
||||||
Track.Id trackId = new Track.Id(trackIdId);
|
viewBinding.trackList.setLayoutManager(layoutManager);
|
||||||
if (recordingStatus.isRecording() && trackId.equals(recordingStatus.getTrackId())) {
|
viewBinding.trackList.setAdapter(adapter);
|
||||||
// Is recording -> open record activity.
|
|
||||||
Intent newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordingActivity.class)
|
|
||||||
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, trackId);
|
|
||||||
startActivity(newIntent);
|
|
||||||
} else {
|
|
||||||
// Not recording -> open detail activity.
|
|
||||||
Intent newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordedActivity.class)
|
|
||||||
.putExtra(TrackRecordedActivity.EXTRA_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());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
resourceCursorAdapter = new ResourceCursorAdapter(this, R.layout.list_item, null, 0) {
|
|
||||||
@Override
|
|
||||||
public void bindView(View view, Context context, Cursor cursor) {
|
|
||||||
int idIndex = cursor.getColumnIndexOrThrow(TracksColumns._ID);
|
|
||||||
int iconIndex = cursor.getColumnIndexOrThrow(TracksColumns.ICON);
|
|
||||||
int nameIndex = cursor.getColumnIndexOrThrow(TracksColumns.NAME);
|
|
||||||
int totalTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.TOTALTIME);
|
|
||||||
int totalDistanceIndex = cursor.getColumnIndexOrThrow(TracksColumns.TOTALDISTANCE);
|
|
||||||
int startTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME);
|
|
||||||
int startTimeOffsetIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME_OFFSET);
|
|
||||||
int activityTypeIndex = cursor.getColumnIndexOrThrow(TracksColumns.ACTIVITY_TYPE_LOCALIZED);
|
|
||||||
int descriptionIndex = cursor.getColumnIndexOrThrow(TracksColumns.DESCRIPTION);
|
|
||||||
int markerCountIndex = cursor.getColumnIndexOrThrow(TracksColumns.MARKER_COUNT);
|
|
||||||
|
|
||||||
Track.Id trackId = new Track.Id(cursor.getLong(idIndex));
|
|
||||||
boolean isRecording = trackId.equals(recordingStatus.getTrackId());
|
|
||||||
String icon = cursor.getString(iconIndex);
|
|
||||||
int iconId = ActivityType.findBy(icon)
|
|
||||||
.getIconDrawableId();
|
|
||||||
String name = cursor.getString(nameIndex);
|
|
||||||
String totalTime = StringUtils.formatElapsedTime(Duration.ofMillis(cursor.getLong(totalTimeIndex)));
|
|
||||||
String totalDistance = DistanceFormatter.Builder()
|
|
||||||
.setUnit(unitSystem)
|
|
||||||
.build(TrackListActivity.this).formatDistance(Distance.of(cursor.getDouble(totalDistanceIndex)));
|
|
||||||
int markerCount = cursor.getInt(markerCountIndex);
|
|
||||||
long startTime = cursor.getLong(startTimeIndex);
|
|
||||||
int startTimeOffset = cursor.getInt(startTimeOffsetIndex);
|
|
||||||
String activityType = icon != null && !icon.equals("") ? null : cursor.getString(activityTypeIndex);
|
|
||||||
String description = cursor.getString(descriptionIndex);
|
|
||||||
|
|
||||||
ListItemUtils.setListItem(TrackListActivity.this, view, isRecording,
|
|
||||||
iconId, R.string.image_track, name, totalTime, totalDistance, markerCount,
|
|
||||||
OffsetDateTime.ofInstant(Instant.ofEpochMilli(startTime), ZoneOffset.ofTotalSeconds(startTimeOffset)),
|
|
||||||
activityType, description, false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
viewBinding.trackList.setAdapter(resourceCursorAdapter);
|
|
||||||
ActivityUtils.configureListViewContextualMenu(viewBinding.trackList, contextualActionModeCallback);
|
|
||||||
|
|
||||||
viewBinding.trackListFabAction.setOnClickListener((view) -> {
|
viewBinding.trackListFabAction.setOnClickListener((view) -> {
|
||||||
if (recordingStatus.isRecording()) {
|
if (recordingStatus.isRecording()) {
|
||||||
@@ -271,8 +208,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
});
|
});
|
||||||
|
|
||||||
setSupportActionBar(viewBinding.trackListToolbar);
|
setSupportActionBar(viewBinding.trackListToolbar);
|
||||||
|
adapter.setActionModeCallback(contextualActionModeCallback);
|
||||||
loadData(getIntent());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void requestRequiredPermissions() {
|
private void requestRequiredPermissions() {
|
||||||
@@ -293,7 +229,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
|
|
||||||
// Update UI
|
// Update UI
|
||||||
this.invalidateOptionsMenu();
|
this.invalidateOptionsMenu();
|
||||||
LoaderManager.getInstance(this).restartLoader(0, null, loaderCallbacks);
|
loadData();
|
||||||
|
|
||||||
// Float button
|
// Float button
|
||||||
setFloatButton();
|
setFloatButton();
|
||||||
@@ -312,6 +248,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
viewBinding = null;
|
viewBinding = null;
|
||||||
trackRecordingServiceConnection = null;
|
trackRecordingServiceConnection = null;
|
||||||
|
adapter = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -333,10 +270,6 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
@Override
|
@Override
|
||||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||||
updateGpsMenuItem(gpsStatusValue.isGpsStarted(), recordingStatus.isRecording());
|
updateGpsMenuItem(gpsStatusValue.isGpsStarted(), recordingStatus.isRecording());
|
||||||
|
|
||||||
SearchView searchView = (SearchView) searchMenuItem.getActionView();
|
|
||||||
searchView.setQuery("", false);
|
|
||||||
|
|
||||||
return super.onPrepareOptionsMenu(menu);
|
return super.onPrepareOptionsMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,11 +308,6 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
return super.onKeyUp(keyCode, event);
|
return super.onKeyUp(keyCode, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void overridePendingTransition(int enterAnim, int exitAnim) {
|
|
||||||
//Disable animations as it is weird going into searchMode; looks okay for SplashScreen.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
SearchView searchView = (SearchView) searchMenuItem.getActionView();
|
SearchView searchView = (SearchView) searchMenuItem.getActionView();
|
||||||
@@ -387,8 +315,9 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
searchView.setIconified(true);
|
searchView.setIconified(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loaderCallbacks.getSearchQuery() != null) {
|
if (searchQuery != null) {
|
||||||
loaderCallbacks.setSearch(null);
|
searchQuery = null;
|
||||||
|
loadData();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,19 +325,30 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNewIntent(Intent intent) {
|
protected void onNewIntent(Intent intent) {
|
||||||
super.onNewIntent(intent);
|
super.onNewIntent(intent);
|
||||||
setIntent(intent);
|
|
||||||
loadData(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadData(Intent intent) {
|
|
||||||
String searchQuery = null;
|
|
||||||
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
|
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
|
||||||
searchQuery = intent.getStringExtra(SearchManager.QUERY);
|
searchQuery = intent.getStringExtra(SearchManager.QUERY);
|
||||||
|
} else {
|
||||||
|
searchQuery = null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
loaderCallbacks.setSearch(searchQuery);
|
private void loadData() {
|
||||||
|
viewBinding.trackListToolbar.setTitle(Objects.requireNonNullElseGet(searchQuery, () -> getString(R.string.app_name)));
|
||||||
|
|
||||||
|
Cursor tracks = new ContentProviderUtils(this).searchTracks(searchQuery);
|
||||||
|
|
||||||
|
adapter.swapData(tracks);
|
||||||
|
|
||||||
|
if (tracks.getCount() == 0) {
|
||||||
|
viewBinding.trackListEmptyView.setVisibility(View.VISIBLE);
|
||||||
|
viewBinding.trackList.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
viewBinding.trackListEmptyView.setVisibility(View.GONE);
|
||||||
|
viewBinding.trackList.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -424,7 +364,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
protected Track.Id getRecordingTrackId() {
|
protected Track.Id getRecordingTrackId() {
|
||||||
return recordingStatus.getTrackId();
|
return recordingStatus.trackId();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -490,64 +430,13 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (itemId == R.id.list_context_menu_select_all) {
|
if (itemId == R.id.list_context_menu_select_all) {
|
||||||
for (int i = 0; i < viewBinding.trackList.getCount(); i++) {
|
adapter.setAllSelected(true);
|
||||||
viewBinding.trackList.setItemChecked(i, true);
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TrackLoaderCallBack implements LoaderManager.LoaderCallbacks<Cursor> {
|
|
||||||
|
|
||||||
private String searchQuery = null;
|
|
||||||
|
|
||||||
public String getSearchQuery() {
|
|
||||||
return searchQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSearch(String searchQuery) {
|
|
||||||
this.searchQuery = searchQuery;
|
|
||||||
restart();
|
|
||||||
viewBinding.trackListToolbar.setTitle(searchQuery == null ? getString(R.string.app_name) : searchQuery);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void restart() {
|
|
||||||
LoaderManager.getInstance(TrackListActivity.this).restartLoader(0, null, loaderCallbacks);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
|
|
||||||
final String[] PROJECTION = new String[]{TracksColumns._ID, TracksColumns.NAME,
|
|
||||||
TracksColumns.DESCRIPTION, TracksColumns.ACTIVITY_TYPE_LOCALIZED, TracksColumns.STARTTIME, TracksColumns.STARTTIME_OFFSET,
|
|
||||||
TracksColumns.TOTALDISTANCE, TracksColumns.TOTALTIME, TracksColumns.ICON, TracksColumns.MARKER_COUNT};
|
|
||||||
|
|
||||||
final String sortOrder = TracksColumns.STARTTIME + " DESC";
|
|
||||||
|
|
||||||
if (searchQuery == null) {
|
|
||||||
return new CursorLoader(TrackListActivity.this, TracksColumns.CONTENT_URI, PROJECTION, null, null, sortOrder);
|
|
||||||
} else {
|
|
||||||
final String SEARCH_QUERY = TracksColumns.NAME + " LIKE ? OR " +
|
|
||||||
TracksColumns.DESCRIPTION + " LIKE ? OR " +
|
|
||||||
TracksColumns.ACTIVITY_TYPE_LOCALIZED + " LIKE ?";
|
|
||||||
final String[] selectionArgs = new String[]{"%" + searchQuery + "%", "%" + searchQuery + "%", "%" + searchQuery + "%"};
|
|
||||||
return new CursorLoader(TrackListActivity.this, TracksColumns.CONTENT_URI, PROJECTION, SEARCH_QUERY, selectionArgs, sortOrder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoadFinished(@NonNull Loader<Cursor> loader, Cursor cursor) {
|
|
||||||
resourceCursorAdapter.swapCursor(cursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLoaderReset(@NonNull Loader<Cursor> loader) {
|
|
||||||
resourceCursorAdapter.swapCursor(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onGpsStatusChanged(GpsStatusValue newStatus) {
|
public void onGpsStatusChanged(GpsStatusValue newStatus) {
|
||||||
gpsStatusValue = newStatus;
|
gpsStatusValue = newStatus;
|
||||||
updateGpsMenuItem(true, recordingStatus.isRecording());
|
updateGpsMenuItem(true, recordingStatus.isRecording());
|
||||||
@@ -561,5 +450,6 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
private void onRecordingStatusChanged(RecordingStatus status) {
|
private void onRecordingStatusChanged(RecordingStatus status) {
|
||||||
recordingStatus = status;
|
recordingStatus = status;
|
||||||
setFloatButton();
|
setFloatButton();
|
||||||
|
adapter.updateRecordingStatus(recordingStatus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -226,7 +226,7 @@ public class TrackRecordedActivity extends AbstractTrackDeleteActivity implement
|
|||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
protected Track.Id getRecordingTrackId() {
|
protected Track.Id getRecordingTrackId() {
|
||||||
return recordingStatus.getTrackId();
|
return recordingStatus.trackId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ public class ChartFragment extends Fragment implements TrackDataHub.Listener {
|
|||||||
|
|
||||||
public void onSampledInTrackPoint(@NonNull TrackPoint trackPoint, @NonNull TrackStatistics trackStatistics) {
|
public void onSampledInTrackPoint(@NonNull TrackPoint trackPoint, @NonNull TrackStatistics trackStatistics) {
|
||||||
if (isResumed()) {
|
if (isResumed()) {
|
||||||
ChartPoint point = new ChartPoint(trackStatistics, trackPoint, trackPoint.getSpeed(), chartByDistance, viewBinding.chartView.getUnitSystem());
|
ChartPoint point = ChartPoint.create(trackStatistics, trackPoint, trackPoint.getSpeed(), chartByDistance, viewBinding.chartView.getUnitSystem());
|
||||||
pendingPoints.add(point);
|
pendingPoints.add(point);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package de.dennisguse.opentracks.chart;
|
package de.dennisguse.opentracks.chart;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.VisibleForTesting;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.data.models.Distance;
|
import de.dennisguse.opentracks.data.models.Distance;
|
||||||
import de.dennisguse.opentracks.data.models.Speed;
|
import de.dennisguse.opentracks.data.models.Speed;
|
||||||
@@ -9,81 +8,43 @@ import de.dennisguse.opentracks.data.models.TrackPoint;
|
|||||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||||
|
|
||||||
public class ChartPoint {
|
public record ChartPoint(
|
||||||
//X-axis
|
//X-axis
|
||||||
private double timeOrDistance;
|
double timeOrDistance,
|
||||||
|
|
||||||
//Y-axis
|
//Y-axis
|
||||||
private Double altitude;
|
Double altitude,
|
||||||
private Double speed;
|
Double speed,
|
||||||
private Double pace;
|
Double pace,
|
||||||
private Double heartRate;
|
Double heartRate,
|
||||||
private Double cadence;
|
Double cadence,
|
||||||
private Double power;
|
Double power
|
||||||
|
) {
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
@VisibleForTesting
|
|
||||||
ChartPoint(double altitude) {
|
|
||||||
this.altitude = altitude;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ChartPoint(@NonNull TrackStatistics trackStatistics, @NonNull TrackPoint trackPoint, Speed smoothedSpeed, boolean chartByDistance, UnitSystem unitSystem) {
|
public static ChartPoint create(@NonNull TrackStatistics trackStatistics, @NonNull TrackPoint trackPoint, Speed smoothedSpeed, boolean chartByDistance, UnitSystem unitSystem) {
|
||||||
if (chartByDistance) {
|
return new ChartPoint(
|
||||||
timeOrDistance = trackStatistics.getTotalDistance().toKM_Miles(unitSystem);
|
chartByDistance
|
||||||
} else {
|
? trackStatistics.getTotalDistance().toKM_Miles(unitSystem)
|
||||||
timeOrDistance = trackStatistics.getTotalTime().toMillis();
|
: trackStatistics.getTotalTime().toMillis(),
|
||||||
}
|
trackPoint.hasAltitude()
|
||||||
|
? Distance.of(trackPoint.getAltitude().toM()).toM_FT(unitSystem)
|
||||||
if (trackPoint.hasAltitude()) {
|
: null,
|
||||||
altitude = Distance.of(trackPoint.getAltitude().toM()).toM_FT(unitSystem);
|
smoothedSpeed != null
|
||||||
}
|
? smoothedSpeed.to(unitSystem)
|
||||||
|
: null,
|
||||||
if (smoothedSpeed != null) {
|
smoothedSpeed != null
|
||||||
speed = smoothedSpeed.to(unitSystem);
|
? smoothedSpeed.toPace(unitSystem).toSeconds() / 60d
|
||||||
pace = smoothedSpeed.toPace(unitSystem).toSeconds() / 60d;
|
: null,
|
||||||
}
|
trackPoint.hasHeartRate()
|
||||||
if (trackPoint.hasHeartRate()) {
|
? (double) trackPoint.getHeartRate().getBPM()
|
||||||
heartRate = (double) trackPoint.getHeartRate().getBPM();
|
: null,
|
||||||
}
|
trackPoint.hasCadence()
|
||||||
if (trackPoint.hasCadence()) {
|
? (double) trackPoint.getCadence().getRPM()
|
||||||
cadence = (double) trackPoint.getCadence().getRPM();
|
: null,
|
||||||
}
|
trackPoint.hasPower()
|
||||||
if (trackPoint.hasPower()) {
|
? (double) trackPoint.getPower().getW()
|
||||||
power = (double) trackPoint.getPower().getW();
|
: null
|
||||||
}
|
);
|
||||||
}
|
|
||||||
|
|
||||||
public double getTimeOrDistance() {
|
|
||||||
return timeOrDistance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Double getAltitude() {
|
|
||||||
return altitude;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Double getSpeed() {
|
|
||||||
return speed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Double getPace() {
|
|
||||||
return pace;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Double getHeartRate() {
|
|
||||||
return heartRate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Double getCadence() {
|
|
||||||
return cadence;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Double getPower() {
|
|
||||||
return power;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "ChartPoint{" + "timeOrDistance=" + timeOrDistance + '}';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ public class ChartView extends View {
|
|||||||
fontSizeMedium) {
|
fontSizeMedium) {
|
||||||
@Override
|
@Override
|
||||||
protected Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
protected Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
||||||
return chartPoint.getAltitude();
|
return chartPoint.altitude();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -232,7 +232,7 @@ public class ChartView extends View {
|
|||||||
fontSizeMedium) {
|
fontSizeMedium) {
|
||||||
@Override
|
@Override
|
||||||
protected Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
protected Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
||||||
return chartPoint.getSpeed();
|
return chartPoint.speed();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -255,7 +255,7 @@ public class ChartView extends View {
|
|||||||
fontSizeMedium) {
|
fontSizeMedium) {
|
||||||
@Override
|
@Override
|
||||||
protected Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
protected Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
||||||
return chartPoint.getPace();
|
return chartPoint.pace();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -278,7 +278,7 @@ public class ChartView extends View {
|
|||||||
fontSizeMedium) {
|
fontSizeMedium) {
|
||||||
@Override
|
@Override
|
||||||
protected Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
protected Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
||||||
return chartPoint.getHeartRate();
|
return chartPoint.heartRate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -300,7 +300,7 @@ public class ChartView extends View {
|
|||||||
fontSizeMedium) {
|
fontSizeMedium) {
|
||||||
@Override
|
@Override
|
||||||
protected Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
protected Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
||||||
return chartPoint.getCadence();
|
return chartPoint.cadence();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -321,7 +321,7 @@ public class ChartView extends View {
|
|||||||
fontSizeMedium) {
|
fontSizeMedium) {
|
||||||
@Override
|
@Override
|
||||||
protected Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
protected Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
|
||||||
return chartPoint.getPower();
|
return chartPoint.power();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -425,7 +425,7 @@ public class ChartView extends View {
|
|||||||
synchronized (chartPoints) {
|
synchronized (chartPoints) {
|
||||||
chartPoints.addAll(dataPoints);
|
chartPoints.addAll(dataPoints);
|
||||||
for (ChartPoint dataPoint : dataPoints) {
|
for (ChartPoint dataPoint : dataPoints) {
|
||||||
xExtremityMonitor.update(dataPoint.getTimeOrDistance());
|
xExtremityMonitor.update(dataPoint.timeOrDistance());
|
||||||
for (ChartValueSeries i : seriesList) {
|
for (ChartValueSeries i : seriesList) {
|
||||||
i.update(dataPoint);
|
i.update(dataPoint);
|
||||||
}
|
}
|
||||||
@@ -887,7 +887,7 @@ public class ChartView extends View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
double value = series.extractDataFromChartPoint(point);
|
double value = series.extractDataFromChartPoint(point);
|
||||||
int x = getX(point.getTimeOrDistance());
|
int x = getX(point.timeOrDistance());
|
||||||
int y = getY(series, value);
|
int y = getY(series, value);
|
||||||
|
|
||||||
// start from lower left corner
|
// start from lower left corner
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ public class ContentProviderUtils {
|
|||||||
public List<Track> getTracks(ContentProviderSelectionInterface selection) {
|
public List<Track> getTracks(ContentProviderSelectionInterface selection) {
|
||||||
SelectionData selectionData = selection.buildSelection();
|
SelectionData selectionData = selection.buildSelection();
|
||||||
ArrayList<Track> tracks = new ArrayList<>();
|
ArrayList<Track> tracks = new ArrayList<>();
|
||||||
try (Cursor cursor = getTrackCursor(selectionData.getSelection(), selectionData.getSelectionArgs(), TracksColumns._ID)) {
|
try (Cursor cursor = getTrackCursor(selectionData.selection(), selectionData.selectionArgs(), TracksColumns._ID)) {
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
tracks.ensureCapacity(cursor.getCount());
|
tracks.ensureCapacity(cursor.getCount());
|
||||||
do {
|
do {
|
||||||
@@ -228,6 +228,31 @@ public class ContentProviderUtils {
|
|||||||
return tracks;
|
return tracks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Cursor searchTracks(String searchQuery) {
|
||||||
|
// Needed, because MARKER_COUNT is a virtual column and has to be explicitly requested.
|
||||||
|
final String[] PROJECTION = new String[]{TracksColumns._ID, TracksColumns.UUID, TracksColumns.NAME,
|
||||||
|
TracksColumns.DESCRIPTION, TracksColumns.ACTIVITY_TYPE_LOCALIZED, TracksColumns.STARTTIME,
|
||||||
|
TracksColumns.STARTTIME_OFFSET, TracksColumns.STOPTIME, TracksColumns.MARKER_COUNT,
|
||||||
|
TracksColumns.TOTALDISTANCE, TracksColumns.TOTALTIME, TracksColumns.MOVINGTIME,
|
||||||
|
TracksColumns.AVGSPEED, TracksColumns.AVGMOVINGSPEED, TracksColumns.MAXSPEED,
|
||||||
|
TracksColumns.MIN_ALTITUDE, TracksColumns.MAX_ALTITUDE, TracksColumns.ALTITUDE_GAIN,
|
||||||
|
TracksColumns.ALTITUDE_LOSS, TracksColumns.ICON
|
||||||
|
};
|
||||||
|
|
||||||
|
String selection = null;
|
||||||
|
String[] selectionArgs = null;
|
||||||
|
final String sortOrder = TracksColumns.STARTTIME + " DESC";
|
||||||
|
|
||||||
|
if (searchQuery != null) {
|
||||||
|
selection = TracksColumns.NAME + " LIKE ? OR " +
|
||||||
|
TracksColumns.DESCRIPTION + " LIKE ? OR " +
|
||||||
|
TracksColumns.ACTIVITY_TYPE_LOCALIZED + " LIKE ?";
|
||||||
|
selectionArgs = new String[]{"%" + searchQuery + "%", "%" + searchQuery + "%", "%" + searchQuery + "%"};
|
||||||
|
}
|
||||||
|
|
||||||
|
return contentResolver.query(TracksColumns.CONTENT_URI, PROJECTION, selection, selectionArgs, sortOrder);
|
||||||
|
}
|
||||||
|
|
||||||
public Track getTrack(@NonNull Track.Id trackId) {
|
public Track getTrack(@NonNull Track.Id trackId) {
|
||||||
try (Cursor cursor = getTrackCursor(TracksColumns._ID + "=?", new String[]{Long.toString(trackId.id())}, null)) {
|
try (Cursor cursor = getTrackCursor(TracksColumns._ID + "=?", new String[]{Long.toString(trackId.id())}, null)) {
|
||||||
if (cursor != null && cursor.moveToNext()) {
|
if (cursor != null && cursor.moveToNext()) {
|
||||||
@@ -869,12 +894,14 @@ public class ContentProviderUtils {
|
|||||||
final int AVG_HR_INDEX = cursor.getColumnIndexOrThrow(TrackPointsColumns.ALIAS_AVG_HR);
|
final int AVG_HR_INDEX = cursor.getColumnIndexOrThrow(TrackPointsColumns.ALIAS_AVG_HR);
|
||||||
final int MAX_CADENCE_INDEX = cursor.getColumnIndexOrThrow(TrackPointsColumns.ALIAS_MAX_CADENCE);
|
final int MAX_CADENCE_INDEX = cursor.getColumnIndexOrThrow(TrackPointsColumns.ALIAS_MAX_CADENCE);
|
||||||
final int AVG_CADENCE_INDEX = cursor.getColumnIndexOrThrow(TrackPointsColumns.ALIAS_AVG_CADENCE);
|
final int AVG_CADENCE_INDEX = cursor.getColumnIndexOrThrow(TrackPointsColumns.ALIAS_AVG_CADENCE);
|
||||||
|
final int MAX_POWER_INDEX = cursor.getColumnIndexOrThrow(TrackPointsColumns.ALIAS_MAX_POWER);
|
||||||
final int AVG_POWER_INDEX = cursor.getColumnIndexOrThrow(TrackPointsColumns.ALIAS_AVG_POWER);
|
final int AVG_POWER_INDEX = cursor.getColumnIndexOrThrow(TrackPointsColumns.ALIAS_AVG_POWER);
|
||||||
sensorStatistics = new SensorStatistics(
|
sensorStatistics = new SensorStatistics(
|
||||||
!cursor.isNull(MAX_HR_INDEX) ? HeartRate.of(cursor.getFloat(MAX_HR_INDEX)) : null,
|
!cursor.isNull(MAX_HR_INDEX) ? HeartRate.of(cursor.getFloat(MAX_HR_INDEX)) : null,
|
||||||
!cursor.isNull(AVG_HR_INDEX) ? HeartRate.of(cursor.getFloat(AVG_HR_INDEX)) : null,
|
!cursor.isNull(AVG_HR_INDEX) ? HeartRate.of(cursor.getFloat(AVG_HR_INDEX)) : null,
|
||||||
!cursor.isNull(MAX_CADENCE_INDEX) ? Cadence.of(cursor.getFloat(MAX_CADENCE_INDEX)) : null,
|
!cursor.isNull(MAX_CADENCE_INDEX) ? Cadence.of(cursor.getFloat(MAX_CADENCE_INDEX)) : null,
|
||||||
!cursor.isNull(AVG_CADENCE_INDEX) ? Cadence.of(cursor.getFloat(AVG_CADENCE_INDEX)) : null,
|
!cursor.isNull(AVG_CADENCE_INDEX) ? Cadence.of(cursor.getFloat(AVG_CADENCE_INDEX)) : null,
|
||||||
|
!cursor.isNull(MAX_POWER_INDEX) ? Power.of(cursor.getFloat(MAX_POWER_INDEX)) : null,
|
||||||
!cursor.isNull(AVG_POWER_INDEX) ? Power.of(cursor.getFloat(AVG_POWER_INDEX)) : null
|
!cursor.isNull(AVG_POWER_INDEX) ? Power.of(cursor.getFloat(AVG_POWER_INDEX)) : null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ public class CustomContentProvider extends ContentProvider {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The string representing the query that compute sensor stats from trackpoints table.
|
* The string representing the query that compute sensor stats from trackpoints table.
|
||||||
* It computes the average for heart rate, cadence, and power (duration-based average) and the maximum for heart rate and cadence.
|
* It computes the average for heart rate, cadence and power (duration-based average) and the maximum for heart rate, cadence and power.
|
||||||
* Finally, it ignores manual pause (SEGMENT_START_MANUAL).
|
* Finally, it ignores manual pause (SEGMENT_START_MANUAL).
|
||||||
*/
|
*/
|
||||||
private final String SENSOR_STATS_QUERY =
|
private final String SENSOR_STATS_QUERY =
|
||||||
@@ -86,7 +86,9 @@ public class CustomContentProvider extends ContentProvider {
|
|||||||
|
|
||||||
"SUM(t." + TrackPointsColumns.SENSOR_POWER + " * (COALESCE(MAX(t." + TrackPointsColumns.TIME + ", (SELECT time_value FROM time_select)), t." + TrackPointsColumns.TIME + ") - t." + TrackPointsColumns.TIME + ")) " +
|
"SUM(t." + TrackPointsColumns.SENSOR_POWER + " * (COALESCE(MAX(t." + TrackPointsColumns.TIME + ", (SELECT time_value FROM time_select)), t." + TrackPointsColumns.TIME + ") - t." + TrackPointsColumns.TIME + ")) " +
|
||||||
"/ " +
|
"/ " +
|
||||||
"SUM(COALESCE(MAX(t." + TrackPointsColumns.TIME + ", (SELECT time_value FROM time_select)), t." + TrackPointsColumns.TIME + ") - t." + TrackPointsColumns.TIME + ") " + TrackPointsColumns.ALIAS_AVG_POWER + " " +
|
"SUM(COALESCE(MAX(t." + TrackPointsColumns.TIME + ", (SELECT time_value FROM time_select)), t." + TrackPointsColumns.TIME + ") - t." + TrackPointsColumns.TIME + ") " + TrackPointsColumns.ALIAS_AVG_POWER + ", " +
|
||||||
|
|
||||||
|
"MAX(t." + TrackPointsColumns.SENSOR_POWER + ") " + TrackPointsColumns.ALIAS_MAX_POWER + " " +
|
||||||
|
|
||||||
"FROM " + TrackPointsColumns.TABLE_NAME + " t " +
|
"FROM " + TrackPointsColumns.TABLE_NAME + " t " +
|
||||||
"WHERE t." + TrackPointsColumns.TRACKID + " = ? " +
|
"WHERE t." + TrackPointsColumns.TRACKID + " = ? " +
|
||||||
|
|||||||
@@ -1,24 +1,11 @@
|
|||||||
package de.dennisguse.opentracks.data;
|
package de.dennisguse.opentracks.data;
|
||||||
|
|
||||||
public class SelectionData {
|
public record SelectionData(
|
||||||
private final String selection;
|
String selection,
|
||||||
private final String[] selectionArgs;
|
String[] selectionArgs
|
||||||
|
) {
|
||||||
|
|
||||||
public SelectionData() {
|
public SelectionData() {
|
||||||
selection = null;
|
this(null, null);
|
||||||
selectionArgs = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SelectionData(String selection, String[] selectionArgs) {
|
|
||||||
this.selection = selection;
|
|
||||||
this.selectionArgs = selectionArgs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSelection() {
|
|
||||||
return selection;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String[] getSelectionArgs() {
|
|
||||||
return selectionArgs;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ public class TrackDataHub {
|
|||||||
* Returns true if the selected track is recording.
|
* Returns true if the selected track is recording.
|
||||||
*/
|
*/
|
||||||
public boolean isSelectedTrackRecording() {
|
public boolean isSelectedTrackRecording() {
|
||||||
return selectedTrackId != null && selectedTrackId.equals(recordingStatus.getTrackId());
|
return selectedTrackId != null && selectedTrackId.equals(recordingStatus.trackId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ public enum ActivityIcon {
|
|||||||
UNKNOWN("UNKNOWN", R.drawable.ic_logo_24dp),
|
UNKNOWN("UNKNOWN", R.drawable.ic_logo_24dp),
|
||||||
WALK("WALK", R.drawable.ic_activity_walk_24dp),
|
WALK("WALK", R.drawable.ic_activity_walk_24dp),
|
||||||
ESCOOTER("ESCOOTER", R.drawable.ic_activity_escooter_24dp),
|
ESCOOTER("ESCOOTER", R.drawable.ic_activity_escooter_24dp),
|
||||||
|
KICKSCOOTER("KICKSCOOTER", R.drawable.ic_activity_scooter_24dp),
|
||||||
INLINE_SKATING("INLINES_SKATING", R.drawable.ic_activity_inline_skating_24dp),
|
INLINE_SKATING("INLINES_SKATING", R.drawable.ic_activity_inline_skating_24dp),
|
||||||
SKATE_BOARDING("SKATE_BOARDING", R.drawable.ic_activity_skateboarding_24dp),
|
SKATE_BOARDING("SKATE_BOARDING", R.drawable.ic_activity_skateboarding_24dp),
|
||||||
CLIMBING("CLIMBING", R.drawable.ic_activity_climbing_24dp),
|
CLIMBING("CLIMBING", R.drawable.ic_activity_climbing_24dp),
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ public enum ActivityType {
|
|||||||
ROWING("rowing", R.string.activity_type_rowing, ActivityIcon.UNKNOWN, true),
|
ROWING("rowing", R.string.activity_type_rowing, ActivityIcon.UNKNOWN, true),
|
||||||
RUNNING("running", R.string.activity_type_running, ActivityIcon.RUN, false),
|
RUNNING("running", R.string.activity_type_running, ActivityIcon.RUN, false),
|
||||||
SAILING("sailing", R.string.activity_type_sailing, ActivityIcon.SAILING, true),
|
SAILING("sailing", R.string.activity_type_sailing, ActivityIcon.SAILING, true),
|
||||||
|
KICKSCOOTER("kickscooter", R.string.activity_type_kickscooter, ActivityIcon.KICKSCOOTER, true),
|
||||||
SEAPLANE("seaplane", R.string.activity_type_seaplane, ActivityIcon.AIRPLANE, true),
|
SEAPLANE("seaplane", R.string.activity_type_seaplane, ActivityIcon.AIRPLANE, true),
|
||||||
SKATE_BOARDING("skate boarding", R.string.activity_type_skate_boarding, ActivityIcon.SKATE_BOARDING, true),
|
SKATE_BOARDING("skate boarding", R.string.activity_type_skate_boarding, ActivityIcon.SKATE_BOARDING, true),
|
||||||
SKATING("skating", R.string.activity_type_skating, ActivityIcon.UNKNOWN, true),
|
SKATING("skating", R.string.activity_type_skating, ActivityIcon.UNKNOWN, true),
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package de.dennisguse.opentracks.data.models;
|
|||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
|
||||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||||
|
|
||||||
public record Speed(double speed_mps) {
|
public record Speed(double speed_mps) {
|
||||||
@@ -56,10 +55,6 @@ public record Speed(double speed_mps) {
|
|||||||
return Double.isNaN(speed_mps) || Double.isInfinite(speed_mps);
|
return Double.isNaN(speed_mps) || Double.isInfinite(speed_mps);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isMoving() {
|
|
||||||
return !isInvalid() && greaterThan(PreferencesUtils.getIdleSpeed());
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean lessThan(Speed speed) {
|
public boolean lessThan(Speed speed) {
|
||||||
return !greaterThan(speed);
|
return !greaterThan(speed);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ public class TrackPoint {
|
|||||||
|
|
||||||
public final int type_db;
|
public final int type_db;
|
||||||
|
|
||||||
|
|
||||||
Type(int type_db) {
|
Type(int type_db) {
|
||||||
this.type_db = type_db;
|
this.type_db = type_db;
|
||||||
}
|
}
|
||||||
@@ -287,11 +288,6 @@ public class TrackPoint {
|
|||||||
this.speed = speed;
|
this.speed = speed;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isMoving() {
|
|
||||||
return hasSpeed() && getSpeed().isMoving();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasBearing() {
|
public boolean hasBearing() {
|
||||||
return bearing != null;
|
return bearing != null;
|
||||||
}
|
}
|
||||||
@@ -352,9 +348,8 @@ public class TrackPoint {
|
|||||||
return hasHorizontalAccuracy() && horizontalAccuracy.lessThan(thresholdHorizontalAccuracy);
|
return hasHorizontalAccuracy() && horizontalAccuracy.lessThan(thresholdHorizontalAccuracy);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO Bearing requires a location; what do we do if we don't have any?
|
|
||||||
public float bearingTo(@NonNull TrackPoint dest) {
|
public float bearingTo(@NonNull TrackPoint dest) {
|
||||||
return getLocation().bearingTo(dest.getLocation());
|
return bearingTo(dest.getLocation());
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO Bearing requires a location; what do we do if we don't have any?
|
//TODO Bearing requires a location; what do we do if we don't have any?
|
||||||
@@ -449,13 +444,14 @@ public class TrackPoint {
|
|||||||
", latitude=" + latitude +
|
", latitude=" + latitude +
|
||||||
", longitude=" + longitude +
|
", longitude=" + longitude +
|
||||||
", horizontalAccuracy=" + horizontalAccuracy +
|
", horizontalAccuracy=" + horizontalAccuracy +
|
||||||
|
", verticalAccuracy=" + verticalAccuracy +
|
||||||
", altitude=" + altitude +
|
", altitude=" + altitude +
|
||||||
", speed=" + speed +
|
", speed=" + speed +
|
||||||
", bearing=" + bearing +
|
", bearing=" + bearing +
|
||||||
", sensorDistance=" + sensorDistance +
|
", sensorDistance=" + sensorDistance +
|
||||||
", type=" + type +
|
", type=" + type +
|
||||||
", heartRate_bpm=" + heartRate +
|
", heartRate=" + heartRate +
|
||||||
", cadence_rpm=" + cadence +
|
", cadence=" + cadence +
|
||||||
", power=" + power +
|
", power=" + power +
|
||||||
", altitudeGain_m=" + altitudeGain_m +
|
", altitudeGain_m=" + altitudeGain_m +
|
||||||
", altitudeLoss_m=" + altitudeLoss_m +
|
", altitudeLoss_m=" + altitudeLoss_m +
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ public interface TrackPointsColumns extends BaseColumns {
|
|||||||
String ALIAS_AVG_CADENCE = "avg_cadence";
|
String ALIAS_AVG_CADENCE = "avg_cadence";
|
||||||
String ALIAS_MAX_CADENCE = "max_cadence";
|
String ALIAS_MAX_CADENCE = "max_cadence";
|
||||||
String ALIAS_AVG_POWER = "avg_power";
|
String ALIAS_AVG_POWER = "avg_power";
|
||||||
|
String ALIAS_MAX_POWER = "max_power";
|
||||||
|
|
||||||
String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " ("
|
String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " ("
|
||||||
+ _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
+ _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ public class ChooseActivityTypeDialogFragment extends DialogFragment implements
|
|||||||
ActivityType.SNOW_BOARDING,
|
ActivityType.SNOW_BOARDING,
|
||||||
ActivityType.SKIING,
|
ActivityType.SKIING,
|
||||||
ActivityType.WALKING,
|
ActivityType.WALKING,
|
||||||
|
ActivityType.KICKSCOOTER,
|
||||||
ActivityType.ESCOOTER,
|
ActivityType.ESCOOTER,
|
||||||
ActivityType.INLINE_SKATING,
|
ActivityType.INLINE_SKATING,
|
||||||
ActivityType.SKATE_BOARDING,
|
ActivityType.SKATE_BOARDING,
|
||||||
|
|||||||
@@ -254,26 +254,28 @@ public class StatisticsRecordedFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sensorStatistics.hasHeartRate()) {
|
if (sensorStatistics.hasHeartRate()) {
|
||||||
String maxBPM = String.valueOf(Math.round(sensorStatistics.getMaxHeartRate().getBPM()));
|
String maxBPM = String.valueOf(Math.round(sensorStatistics.maxHeartRate().getBPM()));
|
||||||
String avgBPM = String.valueOf(Math.round(sensorStatistics.getAvgHeartRate().getBPM()));
|
String avgBPM = String.valueOf(Math.round(sensorStatistics.avgHeartRate().getBPM()));
|
||||||
|
|
||||||
viewBinding.statsHeartRateGroup.setVisibility(View.VISIBLE);
|
viewBinding.statsHeartRateGroup.setVisibility(View.VISIBLE);
|
||||||
viewBinding.statsMaxHeartRateValue.setText(maxBPM);
|
viewBinding.statsMaxHeartRateValue.setText(maxBPM);
|
||||||
viewBinding.statsAvgHeartRateValue.setText(avgBPM);
|
viewBinding.statsAvgHeartRateValue.setText(avgBPM);
|
||||||
}
|
}
|
||||||
if (sensorStatistics.hasCadence()) {
|
if (sensorStatistics.hasCadence()) {
|
||||||
String maxRPM = String.valueOf(Math.round(sensorStatistics.getMaxCadence().getRPM()));
|
String maxRPM = String.valueOf(Math.round(sensorStatistics.maxCadence().getRPM()));
|
||||||
String avgRPM = String.valueOf(Math.round(sensorStatistics.getAvgCadence().getRPM()));
|
String avgRPM = String.valueOf(Math.round(sensorStatistics.avgCadence().getRPM()));
|
||||||
|
|
||||||
viewBinding.statsCadenceGroup.setVisibility(View.VISIBLE);
|
viewBinding.statsCadenceGroup.setVisibility(View.VISIBLE);
|
||||||
viewBinding.statsMaxCadenceValue.setText(maxRPM);
|
viewBinding.statsMaxCadenceValue.setText(maxRPM);
|
||||||
viewBinding.statsAvgCadenceValue.setText(avgRPM);
|
viewBinding.statsAvgCadenceValue.setText(avgRPM);
|
||||||
}
|
}
|
||||||
if (sensorStatistics.hasPower()) {
|
if (sensorStatistics.hasPower()) {
|
||||||
String W = String.valueOf(Math.round(sensorStatistics.getAvgPower().getW()));
|
String maxW = String.valueOf(Math.round(sensorStatistics.maxPower().getW()));
|
||||||
|
String avgW = String.valueOf(Math.round(sensorStatistics.avgPower().getW()));
|
||||||
|
|
||||||
viewBinding.statsPowerGroup.setVisibility(View.VISIBLE);
|
viewBinding.statsPowerGroup.setVisibility(View.VISIBLE);
|
||||||
viewBinding.statsPowerValue.setText(W);
|
viewBinding.statsMaxPowerValue.setText(maxW);
|
||||||
|
viewBinding.statsAvgPowerValue.setText(avgW);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,6 +171,9 @@ public class GPXTrackExporter implements TrackExporter {
|
|||||||
sensorPoints.add(trackPoint);
|
sensorPoints.add(trackPoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case IDLE -> {
|
||||||
|
// Not supported as IDLE-TrackPoints have no location.
|
||||||
|
}
|
||||||
default ->
|
default ->
|
||||||
throw new RuntimeException("Exporting this TrackPoint type is not implemented: " + trackPoint.getType());
|
throw new RuntimeException("Exporting this TrackPoint type is not implemented: " + trackPoint.getType());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
|
|
||||||
public static final String EXTENDED_DATA_TYPE_ACTIVITYTYPE = "type";
|
public static final String EXTENDED_DATA_TYPE_ACTIVITYTYPE = "type";
|
||||||
|
|
||||||
|
public static final String EXTENDED_DATA_TYPE_TRACKPOINT = "trackpoint_type";
|
||||||
public static final String EXTENDED_DATA_TYPE_SPEED = "speed";
|
public static final String EXTENDED_DATA_TYPE_SPEED = "speed";
|
||||||
public static final String EXTENDED_DATA_TYPE_DISTANCE = "distance";
|
public static final String EXTENDED_DATA_TYPE_DISTANCE = "distance";
|
||||||
public static final String EXTENDED_DATA_TYPE_CADENCE = "cadence";
|
public static final String EXTENDED_DATA_TYPE_CADENCE = "cadence";
|
||||||
@@ -83,6 +84,9 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
private final ContentProviderUtils contentProviderUtils;
|
private final ContentProviderUtils contentProviderUtils;
|
||||||
|
|
||||||
private PrintWriter printWriter;
|
private PrintWriter printWriter;
|
||||||
|
|
||||||
|
private final ArrayList<TrackPoint.Type> trackpointTypeList = new ArrayList<>();
|
||||||
|
|
||||||
private final List<Float> speedList = new ArrayList<>();
|
private final List<Float> speedList = new ArrayList<>();
|
||||||
private final List<Float> distanceList = new ArrayList<>();
|
private final List<Float> distanceList = new ArrayList<>();
|
||||||
private final List<Float> powerList = new ArrayList<>();
|
private final List<Float> powerList = new ArrayList<>();
|
||||||
@@ -177,12 +181,13 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
writeCloseSegment();
|
writeCloseSegment();
|
||||||
wroteSegment = false;
|
wroteSegment = false;
|
||||||
}
|
}
|
||||||
case TRACKPOINT -> {
|
case TRACKPOINT, IDLE -> {
|
||||||
if (!wroteSegment) {
|
if (!wroteSegment) {
|
||||||
// Might happen for older data (pre v3.15.0)
|
// Might happen for older data (pre v3.15.0)
|
||||||
writeOpenSegment();
|
writeOpenSegment();
|
||||||
wroteSegment = true;
|
wroteSegment = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
writeTrackPoint(track.getZoneOffset(), trackPoint);
|
writeTrackPoint(track.getZoneOffset(), trackPoint);
|
||||||
}
|
}
|
||||||
default ->
|
default ->
|
||||||
@@ -309,6 +314,7 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
void writeOpenSegment() {
|
void writeOpenSegment() {
|
||||||
printWriter.println("<Track>");
|
printWriter.println("<Track>");
|
||||||
|
trackpointTypeList.clear();
|
||||||
speedList.clear();
|
speedList.clear();
|
||||||
distanceList.clear();
|
distanceList.clear();
|
||||||
powerList.clear();
|
powerList.clear();
|
||||||
@@ -324,32 +330,35 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
void writeCloseSegment() {
|
void writeCloseSegment() {
|
||||||
printWriter.println("<ExtendedData>");
|
printWriter.println("<ExtendedData>");
|
||||||
printWriter.println("<SchemaData schemaUrl=\"#" + SCHEMA_ID + "\">");
|
printWriter.println("<SchemaData schemaUrl=\"#" + SCHEMA_ID + "\">");
|
||||||
|
|
||||||
|
writeTrackPointType(trackpointTypeList);
|
||||||
|
|
||||||
if (speedList.stream().anyMatch(Objects::nonNull)) {
|
if (speedList.stream().anyMatch(Objects::nonNull)) {
|
||||||
writeSimpleArrayData(speedList, EXTENDED_DATA_TYPE_SPEED);
|
writeSimpleArraySensorData(speedList, EXTENDED_DATA_TYPE_SPEED);
|
||||||
}
|
}
|
||||||
if (distanceList.stream().anyMatch(Objects::nonNull)) {
|
if (distanceList.stream().anyMatch(Objects::nonNull)) {
|
||||||
writeSimpleArrayData(distanceList, EXTENDED_DATA_TYPE_DISTANCE);
|
writeSimpleArraySensorData(distanceList, EXTENDED_DATA_TYPE_DISTANCE);
|
||||||
}
|
}
|
||||||
if (powerList.stream().anyMatch(Objects::nonNull)) {
|
if (powerList.stream().anyMatch(Objects::nonNull)) {
|
||||||
writeSimpleArrayData(powerList, EXTENDED_DATA_TYPE_POWER);
|
writeSimpleArraySensorData(powerList, EXTENDED_DATA_TYPE_POWER);
|
||||||
}
|
}
|
||||||
if (cadenceList.stream().anyMatch(Objects::nonNull)) {
|
if (cadenceList.stream().anyMatch(Objects::nonNull)) {
|
||||||
writeSimpleArrayData(cadenceList, EXTENDED_DATA_TYPE_CADENCE);
|
writeSimpleArraySensorData(cadenceList, EXTENDED_DATA_TYPE_CADENCE);
|
||||||
}
|
}
|
||||||
if (heartRateList.stream().anyMatch(Objects::nonNull)) {
|
if (heartRateList.stream().anyMatch(Objects::nonNull)) {
|
||||||
writeSimpleArrayData(heartRateList, EXTENDED_DATA_TYPE_HEART_RATE);
|
writeSimpleArraySensorData(heartRateList, EXTENDED_DATA_TYPE_HEART_RATE);
|
||||||
}
|
}
|
||||||
if (altitudeGainList.stream().anyMatch(Objects::nonNull)) {
|
if (altitudeGainList.stream().anyMatch(Objects::nonNull)) {
|
||||||
writeSimpleArrayData(altitudeGainList, EXTENDED_DATA_TYPE_ALTITUDE_GAIN);
|
writeSimpleArraySensorData(altitudeGainList, EXTENDED_DATA_TYPE_ALTITUDE_GAIN);
|
||||||
}
|
}
|
||||||
if (altitudeLossList.stream().anyMatch(Objects::nonNull)) {
|
if (altitudeLossList.stream().anyMatch(Objects::nonNull)) {
|
||||||
writeSimpleArrayData(altitudeLossList, EXTENDED_DATA_TYPE_ALTITUDE_LOSS);
|
writeSimpleArraySensorData(altitudeLossList, EXTENDED_DATA_TYPE_ALTITUDE_LOSS);
|
||||||
}
|
}
|
||||||
if (accuracyHorizontal.stream().anyMatch(Objects::nonNull)) {
|
if (accuracyHorizontal.stream().anyMatch(Objects::nonNull)) {
|
||||||
writeSimpleArrayData(accuracyHorizontal, EXTENDED_DATA_TYPE_ACCURACY_HORIZONTAL);
|
writeSimpleArraySensorData(accuracyHorizontal, EXTENDED_DATA_TYPE_ACCURACY_HORIZONTAL);
|
||||||
}
|
}
|
||||||
if (accuracyVertical.stream().anyMatch(Objects::nonNull)) {
|
if (accuracyVertical.stream().anyMatch(Objects::nonNull)) {
|
||||||
writeSimpleArrayData(accuracyVertical, EXTENDED_DATA_TYPE_ACCURACY_VERTICAL);
|
writeSimpleArraySensorData(accuracyVertical, EXTENDED_DATA_TYPE_ACCURACY_VERTICAL);
|
||||||
}
|
}
|
||||||
printWriter.println("</SchemaData>");
|
printWriter.println("</SchemaData>");
|
||||||
printWriter.println("</ExtendedData>");
|
printWriter.println("</ExtendedData>");
|
||||||
@@ -360,6 +369,8 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
void writeTrackPoint(ZoneOffset zoneOffset, TrackPoint trackPoint) {
|
void writeTrackPoint(ZoneOffset zoneOffset, TrackPoint trackPoint) {
|
||||||
printWriter.println("<when>" + getTime(zoneOffset, trackPoint.getLocation()) + "</when>");
|
printWriter.println("<when>" + getTime(zoneOffset, trackPoint.getLocation()) + "</when>");
|
||||||
|
|
||||||
|
trackpointTypeList.add(trackPoint.getType());
|
||||||
|
|
||||||
if (trackPoint.hasLocation()) {
|
if (trackPoint.hasLocation()) {
|
||||||
printWriter.println("<coord>" + getCoordinates(trackPoint.getLocation(), " ") + "</coord>");
|
printWriter.println("<coord>" + getCoordinates(trackPoint.getLocation(), " ") + "</coord>");
|
||||||
} else {
|
} else {
|
||||||
@@ -378,13 +389,7 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
accuracyVertical.add(trackPoint.hasVerticalAccuracy() ? (float) trackPoint.getVerticalAccuracy().toM() : null);
|
accuracyVertical.add(trackPoint.hasVerticalAccuracy() ? (float) trackPoint.getVerticalAccuracy().toM() : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void writeSimpleArraySensorData(List<Float> list, String name) {
|
||||||
* Writes the simple array data.
|
|
||||||
*
|
|
||||||
* @param list a list of simple array data
|
|
||||||
* @param name the name of the simple array data
|
|
||||||
*/
|
|
||||||
private void writeSimpleArrayData(List<Float> list, String name) {
|
|
||||||
printWriter.println("<SimpleArrayData name=\"" + name + "\">");
|
printWriter.println("<SimpleArrayData name=\"" + name + "\">");
|
||||||
for (int i = 0; i < list.size(); i++) {
|
for (int i = 0; i < list.size(); i++) {
|
||||||
Float value = list.get(i);
|
Float value = list.get(i);
|
||||||
@@ -397,14 +402,14 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
printWriter.println("</SimpleArrayData>");
|
printWriter.println("</SimpleArrayData>");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void writeTrackPointType(List<TrackPoint.Type> list) {
|
||||||
* Writes a placemark.
|
printWriter.println("<SimpleArrayData name=\"" + EXTENDED_DATA_TYPE_TRACKPOINT + "\">");
|
||||||
*
|
for (TrackPoint.Type value : list) {
|
||||||
* @param name the name
|
printWriter.println("<value>" + value.name() + "</value>");
|
||||||
* @param activityType the activityType
|
}
|
||||||
* @param description the description
|
printWriter.println("</SimpleArrayData>");
|
||||||
* @param location the location
|
}
|
||||||
*/
|
|
||||||
private void writePlacemark(String name, String activityType, String description, Location location, ZoneOffset zoneOffset) {
|
private void writePlacemark(String name, String activityType, String description, Location location, ZoneOffset zoneOffset) {
|
||||||
if (location != null) {
|
if (location != null) {
|
||||||
printWriter.println("<Placemark>");
|
printWriter.println("<Placemark>");
|
||||||
@@ -489,7 +494,7 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void writeActivityType(String activityTypeLocalized) {
|
private void writeActivityType(String activityTypeLocalized) {
|
||||||
if (activityTypeLocalized == null || activityTypeLocalized.equals("")) {
|
if (activityTypeLocalized == null || "".equals(activityTypeLocalized)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
printWriter.println("<ExtendedData>");
|
printWriter.println("<ExtendedData>");
|
||||||
|
|||||||
@@ -94,7 +94,9 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
|||||||
private final ArrayList<Instant> whenList = new ArrayList<>();
|
private final ArrayList<Instant> whenList = new ArrayList<>();
|
||||||
private final ArrayList<Location> locationList = new ArrayList<>();
|
private final ArrayList<Location> locationList = new ArrayList<>();
|
||||||
|
|
||||||
private String dataType;
|
private String dataType; //Could be converted to an ENUM
|
||||||
|
|
||||||
|
private final ArrayList<String> trackpointTypeList = new ArrayList<>();
|
||||||
private final ArrayList<Float> sensorSpeedList = new ArrayList<>();
|
private final ArrayList<Float> sensorSpeedList = new ArrayList<>();
|
||||||
private final ArrayList<Float> sensorDistanceList = new ArrayList<>();
|
private final ArrayList<Float> sensorDistanceList = new ArrayList<>();
|
||||||
private final ArrayList<Float> sensorCadenceList = new ArrayList<>();
|
private final ArrayList<Float> sensorCadenceList = new ArrayList<>();
|
||||||
@@ -287,6 +289,7 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
|||||||
locationList.clear();
|
locationList.clear();
|
||||||
whenList.clear();
|
whenList.clear();
|
||||||
|
|
||||||
|
trackpointTypeList.clear();
|
||||||
sensorSpeedList.clear();
|
sensorSpeedList.clear();
|
||||||
sensorDistanceList.clear();
|
sensorDistanceList.clear();
|
||||||
sensorHeartRateList.clear();
|
sensorHeartRateList.clear();
|
||||||
@@ -313,6 +316,12 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
|||||||
trackPoint.setLocation(location);
|
trackPoint.setLocation(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (i < trackpointTypeList.size() && trackpointTypeList.get(i) != null) {
|
||||||
|
|
||||||
|
TrackPoint.Type type = TrackPoint.Type.valueOf(trackpointTypeList.get(i));
|
||||||
|
trackPoint.setType(type);
|
||||||
|
}
|
||||||
|
|
||||||
if (i < sensorSpeedList.size() && sensorSpeedList.get(i) != null) {
|
if (i < sensorSpeedList.size() && sensorSpeedList.get(i) != null) {
|
||||||
trackPoint.setSpeed(Speed.of(sensorSpeedList.get(i)));
|
trackPoint.setSpeed(Speed.of(sensorSpeedList.get(i)));
|
||||||
}
|
}
|
||||||
@@ -399,10 +408,14 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onExtendedDataValueEnd() throws SAXException {
|
private void onExtendedDataValueEnd() throws SAXException {
|
||||||
|
if (dataType.equals(KMLTrackExporter.EXTENDED_DATA_TYPE_TRACKPOINT)) {
|
||||||
|
trackpointTypeList.add(content != null ? content.trim() : null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
Float value = null;
|
Float value = null;
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
content = content.trim();
|
content = content.trim();
|
||||||
if (!content.equals("")) {
|
if (!"".equals(content)) {
|
||||||
try {
|
try {
|
||||||
value = Float.parseFloat(content);
|
value = Float.parseFloat(content);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import java.io.FilterInputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
@@ -49,7 +48,7 @@ public class KmzTrackImporter {
|
|||||||
|
|
||||||
private static final String TAG = KmzTrackImporter.class.getSimpleName();
|
private static final String TAG = KmzTrackImporter.class.getSimpleName();
|
||||||
|
|
||||||
private static final List<String> KMZ_IMAGES_EXT = Arrays.asList("jpeg", "jpg", "png");
|
private static final List<String> KMZ_IMAGES_EXT = List.of("jpeg", "jpg", "png");
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final TrackImporter trackImporter;
|
private final TrackImporter trackImporter;
|
||||||
@@ -231,7 +230,7 @@ public class KmzTrackImporter {
|
|||||||
*/
|
*/
|
||||||
@Deprecated //TODO Use JDK9's inputStream.transferTo() instead of manual buffer
|
@Deprecated //TODO Use JDK9's inputStream.transferTo() instead of manual buffer
|
||||||
private void readAndSaveImageFile(ZipInputStream zipInputStream, Track.Id trackId, String fileName) throws IOException {
|
private void readAndSaveImageFile(ZipInputStream zipInputStream, Track.Id trackId, String fileName) throws IOException {
|
||||||
if (trackId == null || fileName.equals("")) {
|
if (trackId == null || "".equals(fileName)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
package de.dennisguse.opentracks.services;
|
package de.dennisguse.opentracks.services;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
|
|
||||||
public record RecordingStatus(Track.Id trackId) {
|
public record RecordingStatus(@Nullable Track.Id trackId) {
|
||||||
|
|
||||||
public Track.Id getTrackId() {
|
static RecordingStatus record(@NonNull Track.Id trackId) {
|
||||||
return trackId;
|
return new RecordingStatus(trackId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRecording() {
|
public boolean isRecording() {
|
||||||
@@ -18,10 +19,6 @@ public record RecordingStatus(Track.Id trackId) {
|
|||||||
return new RecordingStatus(null);
|
return new RecordingStatus(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
static RecordingStatus record(@NonNull Track.Id trackId) {
|
|
||||||
return new RecordingStatus(trackId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public RecordingStatus stop() {
|
public RecordingStatus stop() {
|
||||||
return TrackRecordingService.STATUS_DEFAULT;
|
return TrackRecordingService.STATUS_DEFAULT;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,12 @@ import android.content.Context;
|
|||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.database.sqlite.SQLiteException;
|
import android.database.sqlite.SQLiteException;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Handler;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.ZoneOffset;
|
import java.time.ZoneOffset;
|
||||||
@@ -28,19 +30,25 @@ import de.dennisguse.opentracks.stats.TrackStatistics;
|
|||||||
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
|
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
|
||||||
import de.dennisguse.opentracks.util.TrackNameUtils;
|
import de.dennisguse.opentracks.util.TrackNameUtils;
|
||||||
|
|
||||||
class TrackRecordingManager implements SharedPreferences.OnSharedPreferenceChangeListener {
|
public class TrackRecordingManager implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
|
|
||||||
private static final String TAG = TrackRecordingManager.class.getSimpleName();
|
private static final String TAG = TrackRecordingManager.class.getSimpleName();
|
||||||
|
|
||||||
private static final AltitudeCorrectionManager ALTITUDE_CORRECTION_MANAGER = new AltitudeCorrectionManager();
|
private static final AltitudeCorrectionManager ALTITUDE_CORRECTION_MANAGER = new AltitudeCorrectionManager();
|
||||||
|
|
||||||
|
private final Runnable ON_IDLE = this::onIdle;
|
||||||
|
|
||||||
private final ContentProviderUtils contentProviderUtils;
|
private final ContentProviderUtils contentProviderUtils;
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
private final IdleObserver idleObserver;
|
||||||
|
|
||||||
|
private final Handler handler;
|
||||||
|
|
||||||
private final TrackPointCreator trackPointCreator;
|
private final TrackPointCreator trackPointCreator;
|
||||||
|
|
||||||
private Distance recordingDistanceInterval;
|
private Distance recordingDistanceInterval;
|
||||||
private Distance maxRecordingDistance;
|
private Distance maxRecordingDistance;
|
||||||
|
private Duration idleDuration;
|
||||||
|
|
||||||
private Track.Id trackId;
|
private Track.Id trackId;
|
||||||
private TrackStatisticsUpdater trackStatisticsUpdater;
|
private TrackStatisticsUpdater trackStatisticsUpdater;
|
||||||
@@ -52,15 +60,17 @@ class TrackRecordingManager implements SharedPreferences.OnSharedPreferenceChang
|
|||||||
private TrackPoint lastStoredTrackPoint;
|
private TrackPoint lastStoredTrackPoint;
|
||||||
private TrackPoint lastStoredTrackPointWithLocation;
|
private TrackPoint lastStoredTrackPointWithLocation;
|
||||||
|
|
||||||
TrackRecordingManager(Context context, TrackPointCreator trackPointCreator) {
|
TrackRecordingManager(Context context, TrackPointCreator trackPointCreator, IdleObserver idleObserver, Handler handler) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
this.idleObserver = idleObserver;
|
||||||
this.trackPointCreator = trackPointCreator;
|
this.trackPointCreator = trackPointCreator;
|
||||||
|
this.handler = handler;
|
||||||
contentProviderUtils = new ContentProviderUtils(context);
|
contentProviderUtils = new ContentProviderUtils(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
Track.Id startNewTrack() {
|
Track.Id startNewTrack() {
|
||||||
TrackPoint segmentStartTrackPoint = trackPointCreator.createSegmentStartManual();
|
TrackPoint segmentStartTrackPoint = trackPointCreator.createSegmentStartManual();
|
||||||
// Create new track
|
|
||||||
ZoneOffset zoneOffset = ZoneOffset.systemDefault().getRules().getOffset(segmentStartTrackPoint.getTime());
|
ZoneOffset zoneOffset = ZoneOffset.systemDefault().getRules().getOffset(segmentStartTrackPoint.getTime());
|
||||||
Track track = new Track(zoneOffset);
|
Track track = new Track(zoneOffset);
|
||||||
trackId = contentProviderUtils.insertTrack(track);
|
trackId = contentProviderUtils.insertTrack(track);
|
||||||
@@ -74,7 +84,6 @@ class TrackRecordingManager implements SharedPreferences.OnSharedPreferenceChang
|
|||||||
track.setActivityTypeLocalized(activityTypeLocalized);
|
track.setActivityTypeLocalized(activityTypeLocalized);
|
||||||
track.setActivityType(ActivityType.findByLocalizedString(context, activityTypeLocalized));
|
track.setActivityType(ActivityType.findByLocalizedString(context, activityTypeLocalized));
|
||||||
track.setTrackStatistics(trackStatisticsUpdater.getTrackStatistics());
|
track.setTrackStatistics(trackStatisticsUpdater.getTrackStatistics());
|
||||||
//TODO Pass TrackPoint
|
|
||||||
track.setName(TrackNameUtils.getTrackName(context, trackId, track.getStartTime()));
|
track.setName(TrackNameUtils.getTrackName(context, trackId, track.getStartTime()));
|
||||||
contentProviderUtils.updateTrack(track);
|
contentProviderUtils.updateTrack(track);
|
||||||
|
|
||||||
@@ -100,7 +109,7 @@ class TrackRecordingManager implements SharedPreferences.OnSharedPreferenceChang
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void end() {
|
void endCurrentTrack() {
|
||||||
TrackPoint segmentEnd = trackPointCreator.createSegmentEnd();
|
TrackPoint segmentEnd = trackPointCreator.createSegmentEnd();
|
||||||
insertTrackPoint(segmentEnd, true);
|
insertTrackPoint(segmentEnd, true);
|
||||||
|
|
||||||
@@ -154,16 +163,30 @@ class TrackRecordingManager implements SharedPreferences.OnSharedPreferenceChang
|
|||||||
return new Marker.Id(ContentUris.parseId(uri));
|
return new Marker.Id(ContentUris.parseId(uri));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
|
||||||
|
public void onIdle() {
|
||||||
|
Log.d(TAG, "Becoming idle");
|
||||||
|
onNewTrackPoint(trackPointCreator.createIdle());
|
||||||
|
|
||||||
|
idleObserver.onIdle();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return TrackPoint was stored?
|
* @return TrackPoint was stored?
|
||||||
*/
|
*/
|
||||||
boolean onNewTrackPoint(@NonNull TrackPoint trackPoint) {
|
synchronized boolean onNewTrackPoint(@NonNull TrackPoint trackPoint) {
|
||||||
if (trackPoint.hasSpeed()) {
|
if (trackPoint.hasSpeed()) {
|
||||||
lastTrackPointUIWithSpeed = trackPoint;
|
lastTrackPointUIWithSpeed = trackPoint;
|
||||||
}
|
}
|
||||||
if (trackPoint.hasAltitude()) {
|
if (trackPoint.hasAltitude()) {
|
||||||
lastTrackPointUIWithAltitude = trackPoint;
|
lastTrackPointUIWithAltitude = trackPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (trackPoint.getType() == TrackPoint.Type.IDLE) {
|
||||||
|
insertTrackPoint(trackPoint, true);
|
||||||
|
handler.removeCallbacks(ON_IDLE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
//Storing trackPoint
|
//Storing trackPoint
|
||||||
|
|
||||||
// Always insert the first segment location
|
// Always insert the first segment location
|
||||||
@@ -184,10 +207,10 @@ class TrackRecordingManager implements SharedPreferences.OnSharedPreferenceChang
|
|||||||
if (!shouldStore) {
|
if (!shouldStore) {
|
||||||
Log.d(TAG, "Ignoring TrackPoint as it has no distance (and sensor data is not new enough).");
|
Log.d(TAG, "Ignoring TrackPoint as it has no distance (and sensor data is not new enough).");
|
||||||
return false;
|
return false;
|
||||||
} else {
|
|
||||||
insertTrackPoint(trackPoint, true);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
insertTrackPoint(trackPoint, true);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Distance distanceToLastStoredTrackPoint;
|
Distance distanceToLastStoredTrackPoint;
|
||||||
@@ -200,18 +223,17 @@ class TrackRecordingManager implements SharedPreferences.OnSharedPreferenceChang
|
|||||||
if (distanceToLastStoredTrackPoint.greaterThan(maxRecordingDistance)) {
|
if (distanceToLastStoredTrackPoint.greaterThan(maxRecordingDistance)) {
|
||||||
trackPoint.setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
trackPoint.setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||||
insertTrackPoint(trackPoint, true);
|
insertTrackPoint(trackPoint, true);
|
||||||
|
|
||||||
|
handler.removeCallbacks(ON_IDLE);
|
||||||
|
handler.postDelayed(ON_IDLE, idleDuration.toMillis());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (distanceToLastStoredTrackPoint.greaterOrEqualThan(recordingDistanceInterval)
|
if (distanceToLastStoredTrackPoint.greaterOrEqualThan(recordingDistanceInterval)) {
|
||||||
&& trackPoint.isMoving()) {
|
|
||||||
insertTrackPoint(trackPoint, false);
|
insertTrackPoint(trackPoint, false);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trackPoint.isMoving() != lastStoredTrackPoint.isMoving()) {
|
handler.removeCallbacks(ON_IDLE);
|
||||||
// Moving from non-moving to moving or vice versa; required to compute moving time correctly.
|
handler.postDelayed(ON_IDLE, idleDuration.toMillis());
|
||||||
insertTrackPoint(trackPoint, true);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,6 +274,7 @@ class TrackRecordingManager implements SharedPreferences.OnSharedPreferenceChang
|
|||||||
lastStoredTrackPointWithLocation = lastStoredTrackPoint;
|
lastStoredTrackPointWithLocation = lastStoredTrackPoint;
|
||||||
}
|
}
|
||||||
} catch (SQLiteException e) {
|
} catch (SQLiteException e) {
|
||||||
|
// TODO Remove; if this is a problem; use a synchronized method.
|
||||||
/*
|
/*
|
||||||
* Insert failed, most likely because of SqlLite error code 5 (SQLite_BUSY).
|
* Insert failed, most likely because of SqlLite error code 5 (SQLite_BUSY).
|
||||||
* This is expected to happen extremely rarely (if our listener gets invoked twice at about the same time).
|
* This is expected to happen extremely rarely (if our listener gets invoked twice at about the same time).
|
||||||
@@ -277,5 +300,12 @@ class TrackRecordingManager implements SharedPreferences.OnSharedPreferenceChang
|
|||||||
if (PreferencesUtils.isKey(R.string.max_recording_distance_key, key)) {
|
if (PreferencesUtils.isKey(R.string.max_recording_distance_key, key)) {
|
||||||
maxRecordingDistance = PreferencesUtils.getMaxRecordingDistance();
|
maxRecordingDistance = PreferencesUtils.getMaxRecordingDistance();
|
||||||
}
|
}
|
||||||
|
if (PreferencesUtils.isKey(R.string.idle_duration_key, key)) {
|
||||||
|
idleDuration = PreferencesUtils.getIdleDurationTimeout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IdleObserver {
|
||||||
|
void onIdle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
|||||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||||
import de.dennisguse.opentracks.util.SystemUtils;
|
import de.dennisguse.opentracks.util.SystemUtils;
|
||||||
|
|
||||||
public class TrackRecordingService extends Service implements TrackPointCreator.Callback, SharedPreferences.OnSharedPreferenceChangeListener {
|
public class TrackRecordingService extends Service implements TrackPointCreator.Callback, SharedPreferences.OnSharedPreferenceChangeListener, TrackRecordingManager.IdleObserver {
|
||||||
|
|
||||||
private static final String TAG = TrackRecordingService.class.getSimpleName();
|
private static final String TAG = TrackRecordingService.class.getSimpleName();
|
||||||
|
|
||||||
@@ -107,8 +107,8 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
|||||||
gpsStatusObservable = new MutableLiveData<>(STATUS_GPS_DEFAULT);
|
gpsStatusObservable = new MutableLiveData<>(STATUS_GPS_DEFAULT);
|
||||||
recordingDataObservable = new MutableLiveData<>(NOT_RECORDING);
|
recordingDataObservable = new MutableLiveData<>(NOT_RECORDING);
|
||||||
|
|
||||||
trackPointCreator = new TrackPointCreator(this, this, handler);
|
trackPointCreator = new TrackPointCreator(this);
|
||||||
trackRecordingManager = new TrackRecordingManager(this, trackPointCreator);
|
trackRecordingManager = new TrackRecordingManager(this, trackPointCreator, this , handler);
|
||||||
|
|
||||||
voiceAnnouncementManager = new VoiceAnnouncementManager(this);
|
voiceAnnouncementManager = new VoiceAnnouncementManager(this);
|
||||||
notificationManager = new TrackRecordingServiceNotificationManager(this);
|
notificationManager = new TrackRecordingServiceNotificationManager(this);
|
||||||
@@ -214,7 +214,7 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
|||||||
// Set recording status
|
// Set recording status
|
||||||
updateRecordingStatus(STATUS_DEFAULT);
|
updateRecordingStatus(STATUS_DEFAULT);
|
||||||
|
|
||||||
trackRecordingManager.end();
|
trackRecordingManager.endCurrentTrack();
|
||||||
|
|
||||||
stopUpdateRecordingData();
|
stopUpdateRecordingData();
|
||||||
|
|
||||||
@@ -249,7 +249,7 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
|||||||
if (notificationManager == null) {
|
if (notificationManager == null) {
|
||||||
|
|
||||||
StringWriter writer = new StringWriter();
|
StringWriter writer = new StringWriter();
|
||||||
Exception e = new RuntimeException("TrackRecording.newGpsStatus() called after onDestroy(); objectID: " + this + " with thread: " + Thread.currentThread().toString());
|
Exception e = new RuntimeException("TrackRecording.newGpsStatus() called after onDestroy(); objectID: " + this + " with thread: " + Thread.currentThread());
|
||||||
e.printStackTrace(new PrintWriter(writer));
|
e.printStackTrace(new PrintWriter(writer));
|
||||||
|
|
||||||
Log.e(TAG, e.getMessage() + " " + writer);
|
Log.e(TAG, e.getMessage() + " " + writer);
|
||||||
@@ -296,11 +296,15 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
|||||||
// Compute temporary track statistics using sensorData and update time.
|
// Compute temporary track statistics using sensorData and update time.
|
||||||
Pair<Track, Pair<TrackPoint, SensorDataSet>> data = trackRecordingManager.getDataForUI();
|
Pair<Track, Pair<TrackPoint, SensorDataSet>> data = trackRecordingManager.getDataForUI();
|
||||||
|
|
||||||
voiceAnnouncementManager.update(this, data.first);
|
voiceAnnouncementManager.announceStatisticsIfNeeded(data.first);
|
||||||
|
|
||||||
recordingDataObservable.postValue(new RecordingData(data.first, data.second.first, data.second.second));
|
recordingDataObservable.postValue(new RecordingData(data.first, data.second.first, data.second.second));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onIdle() {
|
||||||
|
voiceAnnouncementManager.announceIdle();
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public void stopUpdateRecordingData() {
|
public void stopUpdateRecordingData() {
|
||||||
handler.removeCallbacks(updateRecordingData);
|
handler.removeCallbacks(updateRecordingData);
|
||||||
|
|||||||
+6
-45
@@ -27,42 +27,23 @@ import android.util.Log;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
|
||||||
import de.dennisguse.opentracks.data.TrackPointIterator;
|
|
||||||
import de.dennisguse.opentracks.data.models.Distance;
|
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
|
||||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
|
||||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||||
import de.dennisguse.opentracks.stats.SensorStatistics;
|
|
||||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
|
||||||
import de.dennisguse.opentracks.ui.intervals.IntervalStatistics;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class will announce the user's {@link TrackStatistics}.
|
public class TTSManager {
|
||||||
*
|
|
||||||
* @author Sandor Dornbush
|
|
||||||
*/
|
|
||||||
public class VoiceAnnouncement {
|
|
||||||
|
|
||||||
public final static int AUDIO_STREAM = TextToSpeech.Engine.DEFAULT_STREAM;
|
public final static int AUDIO_STREAM = TextToSpeech.Engine.DEFAULT_STREAM;
|
||||||
|
|
||||||
private static final String TAG = VoiceAnnouncement.class.getSimpleName();
|
private static final String TAG = TTSManager.class.getSimpleName();
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
|
||||||
private final AudioManager audioManager;
|
private final AudioManager audioManager;
|
||||||
|
|
||||||
private final ContentProviderUtils contentProviderUtils;
|
|
||||||
private TrackPoint.Id startTrackPointId = null;
|
|
||||||
|
|
||||||
private IntervalStatistics intervalStatistics;
|
|
||||||
private Distance intervalDistance;
|
|
||||||
|
|
||||||
private final AudioManager.OnAudioFocusChangeListener audioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() {
|
private final AudioManager.OnAudioFocusChangeListener audioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onAudioFocusChange(int focusChange) {
|
public void onAudioFocusChange(int focusChange) {
|
||||||
@@ -109,12 +90,9 @@ public class VoiceAnnouncement {
|
|||||||
|
|
||||||
private MediaPlayer ttsFallback;
|
private MediaPlayer ttsFallback;
|
||||||
|
|
||||||
VoiceAnnouncement(Context context) {
|
TTSManager(Context context) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||||
contentProviderUtils = new ContentProviderUtils(context);
|
|
||||||
intervalDistance = PreferencesUtils.getVoiceAnnouncementDistance();
|
|
||||||
intervalStatistics = new IntervalStatistics(intervalDistance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
@@ -140,7 +118,7 @@ public class VoiceAnnouncement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void announce(@NonNull Track track) {
|
public void announce(@NonNull Spannable announcement) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (!ttsReady) {
|
if (!ttsReady) {
|
||||||
ttsReady = ttsInitStatus == TextToSpeech.SUCCESS;
|
ttsReady = ttsInitStatus == TextToSpeech.SUCCESS;
|
||||||
@@ -150,7 +128,7 @@ public class VoiceAnnouncement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Arrays.asList(AudioManager.MODE_IN_CALL, AudioManager.MODE_IN_COMMUNICATION)
|
if (List.of(AudioManager.MODE_IN_CALL, AudioManager.MODE_IN_COMMUNICATION)
|
||||||
.contains(audioManager.getMode())) {
|
.contains(audioManager.getMode())) {
|
||||||
Log.i(TAG, "Announcement is not allowed at this time.");
|
Log.i(TAG, "Announcement is not allowed at this time.");
|
||||||
return;
|
return;
|
||||||
@@ -167,23 +145,6 @@ public class VoiceAnnouncement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Distance currentIntervalDistance = PreferencesUtils.getVoiceAnnouncementDistance();
|
|
||||||
if (currentIntervalDistance != intervalDistance) {
|
|
||||||
intervalStatistics = new IntervalStatistics(currentIntervalDistance);
|
|
||||||
intervalDistance = currentIntervalDistance;
|
|
||||||
startTrackPointId = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
TrackPointIterator trackPointIterator = new TrackPointIterator(contentProviderUtils, track.getId(), startTrackPointId);
|
|
||||||
startTrackPointId = intervalStatistics.addTrackPoints(trackPointIterator);
|
|
||||||
IntervalStatistics.Interval lastInterval = intervalStatistics.getLastInterval();
|
|
||||||
SensorStatistics sensorStatistics = null;
|
|
||||||
if (track.getId() != null) {
|
|
||||||
sensorStatistics = contentProviderUtils.getSensorStats(track.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
Spannable announcement = VoiceAnnouncementUtils.getAnnouncement(context, track.getTrackStatistics(), PreferencesUtils.getUnitSystem(), PreferencesUtils.isReportSpeed(track), lastInterval, sensorStatistics);
|
|
||||||
|
|
||||||
if (announcement.length() > 0) {
|
if (announcement.length() > 0) {
|
||||||
// We don't care about the utterance id. It is supplied here to force onUtteranceCompleted to be called.
|
// We don't care about the utterance id. It is supplied here to force onUtteranceCompleted to be called.
|
||||||
tts.speak(announcement, TextToSpeech.QUEUE_FLUSH, null, "not used");
|
tts.speak(announcement, TextToSpeech.QUEUE_FLUSH, null, "not used");
|
||||||
+63
-9
@@ -17,6 +17,7 @@ package de.dennisguse.opentracks.services.announcement;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.text.Spannable;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@@ -27,11 +28,15 @@ import androidx.mediarouter.media.MediaRouter;
|
|||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
|
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||||
|
import de.dennisguse.opentracks.data.TrackPointIterator;
|
||||||
import de.dennisguse.opentracks.data.models.Distance;
|
import de.dennisguse.opentracks.data.models.Distance;
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||||
|
import de.dennisguse.opentracks.stats.SensorStatistics;
|
||||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||||
|
import de.dennisguse.opentracks.ui.intervals.IntervalStatistics;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute a periodic task on a time or distance schedule.
|
* Execute a periodic task on a time or distance schedule.
|
||||||
@@ -42,9 +47,9 @@ public class VoiceAnnouncementManager implements SharedPreferences.OnSharedPrefe
|
|||||||
|
|
||||||
private static final String TAG = VoiceAnnouncementManager.class.getSimpleName();
|
private static final String TAG = VoiceAnnouncementManager.class.getSimpleName();
|
||||||
|
|
||||||
private final TrackRecordingService trackRecordingService;
|
private final Context context;
|
||||||
|
|
||||||
private VoiceAnnouncement voiceAnnouncement;
|
private TTSManager voiceAnnouncement;
|
||||||
|
|
||||||
private TrackStatistics trackStatistics;
|
private TrackStatistics trackStatistics;
|
||||||
|
|
||||||
@@ -58,12 +63,22 @@ public class VoiceAnnouncementManager implements SharedPreferences.OnSharedPrefe
|
|||||||
@NonNull
|
@NonNull
|
||||||
private Duration nextTotalTime = TOTALTIME_OFF;
|
private Duration nextTotalTime = TOTALTIME_OFF;
|
||||||
|
|
||||||
public VoiceAnnouncementManager(@NonNull TrackRecordingService trackRecordingService) {
|
|
||||||
this.trackRecordingService = trackRecordingService;
|
private final ContentProviderUtils contentProviderUtils;
|
||||||
|
private TrackPoint.Id startTrackPointId = null;
|
||||||
|
|
||||||
|
private IntervalStatistics intervalStatistics;
|
||||||
|
private Distance intervalDistance;
|
||||||
|
|
||||||
|
public VoiceAnnouncementManager(@NonNull Context context) {
|
||||||
|
this.context = context;
|
||||||
|
contentProviderUtils = new ContentProviderUtils(context);
|
||||||
|
intervalDistance = PreferencesUtils.getVoiceAnnouncementDistance();
|
||||||
|
intervalStatistics = new IntervalStatistics(intervalDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start(@Nullable TrackStatistics trackStatistics) {
|
public void start(@Nullable TrackStatistics trackStatistics) {
|
||||||
voiceAnnouncement = new VoiceAnnouncement(trackRecordingService);
|
voiceAnnouncement = new TTSManager(context);
|
||||||
voiceAnnouncement.start();
|
voiceAnnouncement.start();
|
||||||
update(trackStatistics);
|
update(trackStatistics);
|
||||||
}
|
}
|
||||||
@@ -74,10 +89,10 @@ public class VoiceAnnouncementManager implements SharedPreferences.OnSharedPrefe
|
|||||||
updateNextTaskDistance();
|
updateNextTaskDistance();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update(@NonNull Context context, @NonNull Track track) {
|
private boolean shouldNotAnnounce() {
|
||||||
if (voiceAnnouncement == null) {
|
if (voiceAnnouncement == null) {
|
||||||
Log.e(TAG, "Cannot update when in status shutdown.");
|
Log.e(TAG, "Cannot update when in status shutdown.");
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!PreferencesUtils.shouldVoiceAnnouncementOnDeviceSpeaker()
|
if (!PreferencesUtils.shouldVoiceAnnouncementOnDeviceSpeaker()
|
||||||
@@ -85,6 +100,26 @@ public class VoiceAnnouncementManager implements SharedPreferences.OnSharedPrefe
|
|||||||
.getSelectedRoute()
|
.getSelectedRoute()
|
||||||
.isDeviceSpeaker()) {
|
.isDeviceSpeaker()) {
|
||||||
Log.i(TAG, "No voice announcement on device speaker.");
|
Log.i(TAG, "No voice announcement on device speaker.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void announceIdle() {
|
||||||
|
if (shouldNotAnnounce()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PreferencesUtils.shouldVoiceAnnouncementIdle()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
voiceAnnouncement.announce(VoiceAnnouncementUtils.createIdle(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void announceStatisticsIfNeeded(@NonNull Track track) {
|
||||||
|
if (shouldNotAnnounce()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,10 +135,29 @@ public class VoiceAnnouncementManager implements SharedPreferences.OnSharedPrefe
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (announce) {
|
if (announce) {
|
||||||
voiceAnnouncement.announce(track);
|
voiceAnnouncement.announce(createAnnouncement(track));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Spannable createAnnouncement(Track track) {
|
||||||
|
Distance currentIntervalDistance = PreferencesUtils.getVoiceAnnouncementDistance();
|
||||||
|
if (currentIntervalDistance != intervalDistance) {
|
||||||
|
intervalStatistics = new IntervalStatistics(currentIntervalDistance);
|
||||||
|
intervalDistance = currentIntervalDistance;
|
||||||
|
startTrackPointId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
TrackPointIterator trackPointIterator = new TrackPointIterator(contentProviderUtils, track.getId(), startTrackPointId);
|
||||||
|
startTrackPointId = intervalStatistics.addTrackPoints(trackPointIterator);
|
||||||
|
IntervalStatistics.Interval lastInterval = intervalStatistics.getLastInterval();
|
||||||
|
SensorStatistics sensorStatistics = null;
|
||||||
|
if (track.getId() != null) {
|
||||||
|
sensorStatistics = contentProviderUtils.getSensorStats(track.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
return VoiceAnnouncementUtils.createStatistics(context, track.getTrackStatistics(), PreferencesUtils.getUnitSystem(), PreferencesUtils.isReportSpeed(track), lastInterval, sensorStatistics);
|
||||||
|
}
|
||||||
|
|
||||||
public void stop() {
|
public void stop() {
|
||||||
if (voiceAnnouncement != null) {
|
if (voiceAnnouncement != null) {
|
||||||
voiceAnnouncement.stop();
|
voiceAnnouncement.stop();
|
||||||
|
|||||||
+7
-2
@@ -33,7 +33,12 @@ class VoiceAnnouncementUtils {
|
|||||||
private VoiceAnnouncementUtils() {
|
private VoiceAnnouncementUtils() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static Spannable getAnnouncement(Context context, TrackStatistics trackStatistics, UnitSystem unitSystem, boolean isReportSpeed, @Nullable IntervalStatistics.Interval currentInterval, @Nullable SensorStatistics sensorStatistics) {
|
static Spannable createIdle(Context context) {
|
||||||
|
return new SpannableStringBuilder()
|
||||||
|
.append(context.getString(R.string.voiceIdle));
|
||||||
|
}
|
||||||
|
|
||||||
|
static Spannable createStatistics(Context context, TrackStatistics trackStatistics, UnitSystem unitSystem, boolean isReportSpeed, @Nullable IntervalStatistics.Interval currentInterval, @Nullable SensorStatistics sensorStatistics) {
|
||||||
SpannableStringBuilder builder = new SpannableStringBuilder();
|
SpannableStringBuilder builder = new SpannableStringBuilder();
|
||||||
Distance totalDistance = trackStatistics.getTotalDistance();
|
Distance totalDistance = trackStatistics.getTotalDistance();
|
||||||
Speed averageMovingSpeed = trackStatistics.getAverageMovingSpeed();
|
Speed averageMovingSpeed = trackStatistics.getAverageMovingSpeed();
|
||||||
@@ -133,7 +138,7 @@ class VoiceAnnouncementUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (shouldVoiceAnnounceAverageHeartRate() && sensorStatistics != null && sensorStatistics.hasHeartRate()) {
|
if (shouldVoiceAnnounceAverageHeartRate() && sensorStatistics != null && sensorStatistics.hasHeartRate()) {
|
||||||
int averageHeartRate = Math.round(sensorStatistics.getAvgHeartRate().getBPM());
|
int averageHeartRate = Math.round(sensorStatistics.avgHeartRate().getBPM());
|
||||||
|
|
||||||
builder.append(" ")
|
builder.append(" ")
|
||||||
.append(context.getString(R.string.average_heart_rate));
|
.append(context.getString(R.string.average_heart_rate));
|
||||||
|
|||||||
@@ -36,16 +36,11 @@ public class TrackPointCreator implements SharedPreferences.OnSharedPreferenceCh
|
|||||||
private Clock clock = new MonotonicClock();
|
private Clock clock = new MonotonicClock();
|
||||||
private SensorManager sensorManager;
|
private SensorManager sensorManager;
|
||||||
|
|
||||||
public TrackPointCreator(Callback service, Context context, Handler handler) {
|
public TrackPointCreator(Callback service) {
|
||||||
this.service = service;
|
this.service = service;
|
||||||
this.sensorManager = new SensorManager(this);
|
this.sensorManager = new SensorManager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
TrackPointCreator(Callback service) {
|
|
||||||
this.service = service;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void start(@NonNull Context context, @NonNull Handler handler) {
|
public synchronized void start(@NonNull Context context, @NonNull Handler handler) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
|
||||||
@@ -106,6 +101,13 @@ public class TrackPointCreator implements SharedPreferences.OnSharedPreferenceCh
|
|||||||
return segmentEnd;
|
return segmentEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized TrackPoint createIdle() {
|
||||||
|
TrackPoint idle = new TrackPoint(TrackPoint.Type.IDLE, createNow());
|
||||||
|
addSensorData(idle);
|
||||||
|
reset();
|
||||||
|
return idle;
|
||||||
|
}
|
||||||
|
|
||||||
public Pair<TrackPoint, SensorDataSet> createCurrentTrackPoint(@Nullable TrackPoint lastTrackPointUISpeed, @Nullable TrackPoint lastTrackPointUIAltitude, @Nullable TrackPoint lastStoredTrackPointWithLocation) {
|
public Pair<TrackPoint, SensorDataSet> createCurrentTrackPoint(@Nullable TrackPoint lastTrackPointUISpeed, @Nullable TrackPoint lastTrackPointUIAltitude, @Nullable TrackPoint lastStoredTrackPointWithLocation) {
|
||||||
TrackPoint currentTrackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT, createNow());
|
TrackPoint currentTrackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT, createNow());
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public class AnnouncementsSettingsFragment extends PreferenceFragmentCompat {
|
|||||||
@Override
|
@Override
|
||||||
public void onStart() {
|
public void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_announcements_title);
|
((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_announcements_statistics_title);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -71,8 +71,10 @@ public class DefaultsSettingsFragment extends PreferenceFragmentCompat implement
|
|||||||
|
|
||||||
int entriesId = switch (unitSystem) {
|
int entriesId = switch (unitSystem) {
|
||||||
case METRIC -> R.array.stats_rate_metric_options;
|
case METRIC -> R.array.stats_rate_metric_options;
|
||||||
case IMPERIAL_FEET, IMPERIAL_METER, NAUTICAL_IMPERIAL ->
|
case IMPERIAL_FEET, IMPERIAL_METER ->
|
||||||
R.array.stats_rate_imperial_options;
|
R.array.stats_rate_imperial_options;
|
||||||
|
case NAUTICAL_IMPERIAL ->
|
||||||
|
R.array.stats_rate_nautical_options;
|
||||||
};
|
};
|
||||||
|
|
||||||
String[] entries = getResources().getStringArray(entriesId);
|
String[] entries = getResources().getStringArray(entriesId);
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ public class GpsSettingsFragment extends PreferenceFragmentCompat {
|
|||||||
ListPreference recordingGpsAccuracy = findPreference(getString(R.string.recording_gps_accuracy_key));
|
ListPreference recordingGpsAccuracy = findPreference(getString(R.string.recording_gps_accuracy_key));
|
||||||
recordingGpsAccuracy.setEntries(PreferencesUtils.getThresholdHorizontalAccuracyEntries());
|
recordingGpsAccuracy.setEntries(PreferencesUtils.getThresholdHorizontalAccuracyEntries());
|
||||||
|
|
||||||
ListPreference idleSpeed = findPreference(getString(R.string.idle_speed_key));
|
ListPreference idleDuration = findPreference(getString(R.string.idle_duration_key));
|
||||||
idleSpeed.setEntries(PreferencesUtils.getIdleSpeedEntries());
|
idleDuration.setEntries(PreferencesUtils.getIdleDurationEntries());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ import de.dennisguse.opentracks.data.models.Distance;
|
|||||||
import de.dennisguse.opentracks.data.models.DistanceFormatter;
|
import de.dennisguse.opentracks.data.models.DistanceFormatter;
|
||||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||||
import de.dennisguse.opentracks.data.models.HeartRateZones;
|
import de.dennisguse.opentracks.data.models.HeartRateZones;
|
||||||
import de.dennisguse.opentracks.data.models.Speed;
|
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||||
import de.dennisguse.opentracks.io.file.TrackFilenameGenerator;
|
import de.dennisguse.opentracks.io.file.TrackFilenameGenerator;
|
||||||
@@ -308,11 +307,20 @@ public class PreferencesUtils {
|
|||||||
return getBoolean(R.string.stats_fullscreen_while_recording_key, DEFAULT);
|
return getBoolean(R.string.stats_fullscreen_while_recording_key, DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean shouldUseDynamicColors() {
|
||||||
|
final boolean DEFAULT = resources.getBoolean(R.bool.settings_ui_dynamic_colors_default);
|
||||||
|
return getBoolean(R.string.settings_ui_dynamic_colors_key, DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean shouldVoiceAnnouncementOnDeviceSpeaker() {
|
public static boolean shouldVoiceAnnouncementOnDeviceSpeaker() {
|
||||||
final boolean DEFAULT = resources.getBoolean(R.bool.voice_on_device_speaker_default);
|
final boolean DEFAULT = resources.getBoolean(R.bool.voice_on_device_speaker_default);
|
||||||
return getBoolean(R.string.voice_on_device_speaker_key, DEFAULT);
|
return getBoolean(R.string.voice_on_device_speaker_key, DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean shouldVoiceAnnouncementIdle() {
|
||||||
|
return getBoolean(R.string.voice_announce_idle_key, true);
|
||||||
|
}
|
||||||
|
|
||||||
public static Duration getVoiceAnnouncementFrequency() {
|
public static Duration getVoiceAnnouncementFrequency() {
|
||||||
final int DEFAULT = Integer.parseInt(resources.getString(R.string.voice_announcement_frequency_default));
|
final int DEFAULT = Integer.parseInt(resources.getString(R.string.voice_announcement_frequency_default));
|
||||||
int value = getInt(R.string.voice_announcement_frequency_key, DEFAULT);
|
int value = getInt(R.string.voice_announcement_frequency_key, DEFAULT);
|
||||||
@@ -530,11 +538,12 @@ public class PreferencesUtils {
|
|||||||
|
|
||||||
static String[] getMinRecordingIntervalEntries() {
|
static String[] getMinRecordingIntervalEntries() {
|
||||||
String[] entryValues = resources.getStringArray(R.array.min_recording_interval_values);
|
String[] entryValues = resources.getStringArray(R.array.min_recording_interval_values);
|
||||||
|
long recommended = PreferencesUtils.getMinRecordingIntervalDefault().getSeconds();
|
||||||
String[] entries = new String[entryValues.length];
|
String[] entries = new String[entryValues.length];
|
||||||
for (int i = 0; i < entryValues.length; i++) {
|
for (int i = 0; i < entryValues.length; i++) {
|
||||||
int value = Integer.parseInt(entryValues[i]);
|
int value = Integer.parseInt(entryValues[i]);
|
||||||
|
|
||||||
if (value == PreferencesUtils.getMinRecordingIntervalDefault().getSeconds()) {
|
if (value == recommended) {
|
||||||
entries[i] = resources.getString(R.string.value_smallest_recommended);
|
entries[i] = resources.getString(R.string.value_smallest_recommended);
|
||||||
} else {
|
} else {
|
||||||
entries[i] = value < 60 ? resources.getString(R.string.value_integer_second, value) : resources.getString(R.string.value_integer_minute, value / 60);
|
entries[i] = value < 60 ? resources.getString(R.string.value_integer_second, value) : resources.getString(R.string.value_integer_minute, value / 60);
|
||||||
@@ -598,49 +607,25 @@ public class PreferencesUtils {
|
|||||||
return entries;
|
return entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Duration getIdleDurationTimeout() {
|
||||||
public static Speed getIdleSpeed() {
|
final int DEFAULT = Integer.parseInt(resources.getString(R.string.idle_duration_default));
|
||||||
final float DEFAULT = Float.parseFloat(resources.getString(R.string.idle_speed_default));
|
int value = getInt(R.string.idle_duration_key, DEFAULT);
|
||||||
float value = getFloat(R.string.idle_speed_key, DEFAULT);
|
return Duration.ofSeconds(value);
|
||||||
return Speed.ofKMH(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static String[] getIdleSpeedEntries() {
|
static String[] getIdleDurationEntries() {
|
||||||
String[] entryValues = resources.getStringArray(R.array.idle_speed_values);
|
String[] entryValues = resources.getStringArray(R.array.idle_duration_values);
|
||||||
String[] entries = new String[entryValues.length];
|
String[] entries = new String[entryValues.length];
|
||||||
|
|
||||||
final float idleSpeedDefault = Float.parseFloat(resources.getString(R.string.idle_speed_default));
|
final int idleDurationDefault = Integer.parseInt(resources.getString(R.string.idle_duration_default));
|
||||||
|
|
||||||
UnitSystem unitSystem = getUnitSystem();
|
|
||||||
|
|
||||||
for (int i = 0; i < entryValues.length; i++) {
|
for (int i = 0; i < entryValues.length; i++) {
|
||||||
float value = Float.parseFloat(entryValues[i]);
|
int value = Integer.parseInt(entryValues[i]);
|
||||||
|
|
||||||
switch (unitSystem) {
|
if (value == idleDurationDefault) {
|
||||||
case METRIC -> {
|
entries[i] = resources.getString(R.string.value_int_seconds, value);
|
||||||
if (value == idleSpeedDefault) {
|
} else {
|
||||||
entries[i] = resources.getString(R.string.value_float_kilometer_hour_recommended, value);
|
entries[i] = value < 60 ? resources.getString(R.string.value_integer_second, value) : resources.getString(R.string.value_integer_minute, value / 60);
|
||||||
} else {
|
|
||||||
entries[i] = resources.getString(R.string.value_float_kilometer_hour, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case IMPERIAL_FEET, IMPERIAL_METER -> {
|
|
||||||
double valueMPH = Speed.ofKMH(value).toMPH();
|
|
||||||
if (value == idleSpeedDefault) {
|
|
||||||
entries[i] = resources.getString(R.string.value_float_mile_hour_recommended, valueMPH);
|
|
||||||
} else {
|
|
||||||
entries[i] = resources.getString(R.string.value_float_mile_hour, valueMPH);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case NAUTICAL_IMPERIAL -> {
|
|
||||||
double valueKnots = Speed.ofKMH(value).toKnots();
|
|
||||||
if (value == idleSpeedDefault) {
|
|
||||||
entries[i] = resources.getString(R.string.value_float_knots_recommended, valueKnots);
|
|
||||||
} else {
|
|
||||||
entries[i] = resources.getString(R.string.value_float_knots, valueKnots);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default -> throw new RuntimeException("Not implemented");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package de.dennisguse.opentracks.settings;
|
|||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
import androidx.fragment.app.DialogFragment;
|
import androidx.fragment.app.DialogFragment;
|
||||||
@@ -34,6 +35,9 @@ public class UserInterfaceSettingsFragment extends PreferenceFragmentCompat {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Preference dynamicColors = findPreference(getString(R.string.settings_ui_dynamic_colors_key));
|
||||||
|
dynamicColors.setEnabled(Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU);
|
||||||
|
|
||||||
setShowOnMapFormatOptions();
|
setShowOnMapFormatOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,50 +4,24 @@ import de.dennisguse.opentracks.data.models.Cadence;
|
|||||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||||
import de.dennisguse.opentracks.data.models.Power;
|
import de.dennisguse.opentracks.data.models.Power;
|
||||||
|
|
||||||
public class SensorStatistics {
|
public record SensorStatistics(
|
||||||
private final HeartRate maxHr;
|
HeartRate maxHeartRate,
|
||||||
private final HeartRate avgHr;
|
HeartRate avgHeartRate,
|
||||||
private final Cadence maxCadence;
|
Cadence maxCadence,
|
||||||
private final Cadence avgCadence;
|
Cadence avgCadence,
|
||||||
private final Power avgPower;
|
Power maxPower,
|
||||||
|
Power avgPower
|
||||||
public SensorStatistics(HeartRate maxHr, HeartRate avgHr, Cadence maxCadence, Cadence avgCadence, Power avgPower) {
|
) {
|
||||||
this.maxHr = maxHr;
|
|
||||||
this.avgHr = avgHr;
|
|
||||||
this.maxCadence = maxCadence;
|
|
||||||
this.avgCadence = avgCadence;
|
|
||||||
this.avgPower = avgPower;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasHeartRate() {
|
public boolean hasHeartRate() {
|
||||||
return avgHr != null && maxHr != null;
|
return avgHeartRate != null && maxHeartRate != null;
|
||||||
}
|
|
||||||
|
|
||||||
public HeartRate getMaxHeartRate() {
|
|
||||||
return maxHr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HeartRate getAvgHeartRate() {
|
|
||||||
return avgHr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasCadence() {
|
public boolean hasCadence() {
|
||||||
return avgCadence != null && maxCadence != null;
|
return avgCadence != null && maxCadence != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cadence getMaxCadence() {
|
|
||||||
return maxCadence;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Cadence getAvgCadence() {
|
|
||||||
return avgCadence;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasPower() {
|
public boolean hasPower() {
|
||||||
return avgPower != null;
|
return avgPower != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Power getAvgPower() {
|
|
||||||
return avgPower;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ public class TrackStatistics {
|
|||||||
// The average heart rate seen on this track
|
// The average heart rate seen on this track
|
||||||
private HeartRate avgHeartRate = null;
|
private HeartRate avgHeartRate = null;
|
||||||
|
|
||||||
|
private boolean isIdle;
|
||||||
|
|
||||||
public TrackStatistics() {
|
public TrackStatistics() {
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
@@ -81,6 +83,7 @@ public class TrackStatistics {
|
|||||||
totalAltitudeGain_m = other.totalAltitudeGain_m;
|
totalAltitudeGain_m = other.totalAltitudeGain_m;
|
||||||
totalAltitudeLoss_m = other.totalAltitudeLoss_m;
|
totalAltitudeLoss_m = other.totalAltitudeLoss_m;
|
||||||
avgHeartRate = other.avgHeartRate;
|
avgHeartRate = other.avgHeartRate;
|
||||||
|
isIdle = other.isIdle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -168,6 +171,8 @@ public class TrackStatistics {
|
|||||||
setMaxSpeed(Speed.zero());
|
setMaxSpeed(Speed.zero());
|
||||||
setTotalAltitudeGain(null);
|
setTotalAltitudeGain(null);
|
||||||
setTotalAltitudeLoss(null);
|
setTotalAltitudeLoss(null);
|
||||||
|
|
||||||
|
isIdle = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reset(Instant startTime) {
|
public void reset(Instant startTime) {
|
||||||
@@ -248,6 +253,14 @@ public class TrackStatistics {
|
|||||||
return totalTime.minus(movingTime);
|
return totalTime.minus(movingTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isIdle() {
|
||||||
|
return isIdle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIdle(boolean idle) {
|
||||||
|
isIdle = idle;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasAverageHeartRate() {
|
public boolean hasAverageHeartRate() {
|
||||||
return avgHeartRate != null;
|
return avgHeartRate != null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
package de.dennisguse.opentracks.stats;
|
package de.dennisguse.opentracks.stats;
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
@@ -40,11 +38,6 @@ import de.dennisguse.opentracks.data.models.TrackPoint;
|
|||||||
public class TrackStatisticsUpdater {
|
public class TrackStatisticsUpdater {
|
||||||
|
|
||||||
private static final String TAG = TrackStatisticsUpdater.class.getSimpleName();
|
private static final String TAG = TrackStatisticsUpdater.class.getSimpleName();
|
||||||
/**
|
|
||||||
* Ignore any acceleration faster than this.
|
|
||||||
* Will ignore any speeds that imply acceleration greater than 2g's
|
|
||||||
*/
|
|
||||||
private static final double SPEED_MAX_ACCELERATION = 2 * 9.81;
|
|
||||||
|
|
||||||
private final TrackStatistics trackStatistics;
|
private final TrackStatistics trackStatistics;
|
||||||
|
|
||||||
@@ -132,26 +125,35 @@ public class TrackStatisticsUpdater {
|
|||||||
currentSegment.setAverageHeartRate(HeartRate.of(averageHeartRateBPM));
|
currentSegment.setAverageHeartRate(HeartRate.of(averageHeartRateBPM));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update total distance
|
{
|
||||||
if (trackPoint.hasSensorDistance()) {
|
// Update total distance
|
||||||
// Sensor-based distance/speed
|
Distance movingDistance = null;
|
||||||
currentSegment.addTotalDistance(trackPoint.getSensorDistance());
|
if (trackPoint.hasSensorDistance()) {
|
||||||
} else if (lastTrackPoint != null
|
movingDistance = trackPoint.getSensorDistance();
|
||||||
&& lastTrackPoint.hasLocation()
|
} else if (lastTrackPoint != null
|
||||||
&& trackPoint.hasLocation() && trackPoint.isMoving()) {
|
&& lastTrackPoint.hasLocation()
|
||||||
// GPS-based distance/speed
|
&& trackPoint.hasLocation()) {
|
||||||
// Assumption: we ignore TrackPoints that are not moving as those are likely imprecise GPS measurements
|
// GPS-based distance/speed
|
||||||
Distance movingDistance = trackPoint.distanceToPrevious(lastTrackPoint);
|
movingDistance = trackPoint.distanceToPrevious(lastTrackPoint);
|
||||||
currentSegment.addTotalDistance(movingDistance);
|
}
|
||||||
}
|
if (movingDistance != null) {
|
||||||
|
currentSegment.setIdle(false);
|
||||||
|
currentSegment.addTotalDistance(movingDistance);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentSegment.isIdle() && !trackPoint.isSegmentManualStart()) {
|
||||||
|
if (lastTrackPoint != null) {
|
||||||
|
currentSegment.addMovingTime(trackPoint, lastTrackPoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update moving time
|
if (trackPoint.getType() == TrackPoint.Type.IDLE) {
|
||||||
if (trackPoint.isMoving() && lastTrackPoint != null && lastTrackPoint.isMoving()) {
|
currentSegment.setIdle(true);
|
||||||
currentSegment.addMovingTime(trackPoint, lastTrackPoint);
|
}
|
||||||
|
|
||||||
// Update max speed
|
if (trackPoint.hasSpeed()) {
|
||||||
updateSpeed(trackPoint, lastTrackPoint);
|
updateSpeed(trackPoint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trackPoint.isSegmentManualEnd()) {
|
if (trackPoint.isSegmentManualEnd()) {
|
||||||
@@ -180,27 +182,13 @@ public class TrackStatisticsUpdater {
|
|||||||
/**
|
/**
|
||||||
* Updates a speed reading while assuming the user is moving.
|
* Updates a speed reading while assuming the user is moving.
|
||||||
*/
|
*/
|
||||||
private void updateSpeed(@NonNull TrackPoint trackPoint, @NonNull TrackPoint lastTrackPoint) {
|
private void updateSpeed(@NonNull TrackPoint trackPoint) {
|
||||||
if (isValidSpeed(trackPoint, lastTrackPoint)) {
|
Speed currentSpeed = trackPoint.getSpeed();
|
||||||
Speed currentSpeed = trackPoint.getSpeed();
|
if (currentSpeed.greaterThan(currentSegment.getMaxSpeed())) {
|
||||||
if (currentSpeed.greaterThan(currentSegment.getMaxSpeed())) {
|
currentSegment.setMaxSpeed(currentSpeed);
|
||||||
currentSegment.setMaxSpeed(currentSpeed);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.d(TAG, "Invalid speed. speed: " + trackPoint.getSpeed() + " lastLocationSpeed: " + lastTrackPoint.getSpeed());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isValidSpeed(@NonNull TrackPoint trackPoint, @NonNull TrackPoint lastTrackPoint) {
|
|
||||||
// See if the speed seems physically likely. Ignore any speeds that imply acceleration greater than 2g.
|
|
||||||
Duration timeDifference = Duration.between(lastTrackPoint.getTime(), trackPoint.getTime());
|
|
||||||
Speed maxSpeedDifference = Speed.of(Distance.of(SPEED_MAX_ACCELERATION), Duration.ofMillis(1000))
|
|
||||||
.mul(timeDifference.toSeconds());
|
|
||||||
|
|
||||||
Speed speedDifference = Speed.absDiff(lastTrackPoint.getSpeed(), trackPoint.getSpeed());
|
|
||||||
return speedDifference.lessThan(maxSpeedDifference);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
|||||||
@@ -0,0 +1,296 @@
|
|||||||
|
package de.dennisguse.opentracks.ui;
|
||||||
|
|
||||||
|
import android.app.ActivityOptions;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.util.Pair;
|
||||||
|
import android.util.SparseBooleanArray;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.appcompat.view.ActionMode;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.R;
|
||||||
|
import de.dennisguse.opentracks.TrackRecordedActivity;
|
||||||
|
import de.dennisguse.opentracks.TrackRecordingActivity;
|
||||||
|
import de.dennisguse.opentracks.data.models.ActivityType;
|
||||||
|
import de.dennisguse.opentracks.data.models.Distance;
|
||||||
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
|
import de.dennisguse.opentracks.data.tables.TracksColumns;
|
||||||
|
import de.dennisguse.opentracks.databinding.TrackListItemBinding;
|
||||||
|
import de.dennisguse.opentracks.services.RecordingStatus;
|
||||||
|
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||||
|
import de.dennisguse.opentracks.ui.util.ActivityUtils;
|
||||||
|
import de.dennisguse.opentracks.ui.util.ListItemUtils;
|
||||||
|
import de.dennisguse.opentracks.util.IntentUtils;
|
||||||
|
import de.dennisguse.opentracks.util.StringUtils;
|
||||||
|
|
||||||
|
public class TrackListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements ActionMode.Callback {
|
||||||
|
|
||||||
|
private static final String TAG = TrackListAdapter.class.getSimpleName();
|
||||||
|
|
||||||
|
private final AppCompatActivity context;
|
||||||
|
private final RecyclerView recyclerView;
|
||||||
|
private final SparseBooleanArray selection = new SparseBooleanArray();
|
||||||
|
private RecordingStatus recordingStatus;
|
||||||
|
private UnitSystem unitSystem;
|
||||||
|
private Cursor tracks;
|
||||||
|
private boolean selectionMode = false;
|
||||||
|
private ActivityUtils.ContextualActionModeCallback actionModeCallback;
|
||||||
|
private ActionMode actionMode;
|
||||||
|
|
||||||
|
public TrackListAdapter(AppCompatActivity context, RecyclerView recyclerView, RecordingStatus recordingStatus, UnitSystem unitSystem) {
|
||||||
|
this.context = context;
|
||||||
|
this.recyclerView = recyclerView;
|
||||||
|
this.recordingStatus = recordingStatus;
|
||||||
|
this.unitSystem = unitSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActionModeCallback(ActivityUtils.ContextualActionModeCallback actionModeCallback) {
|
||||||
|
this.actionModeCallback = actionModeCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.track_list_item, parent, false);
|
||||||
|
return new ViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||||
|
ViewHolder viewHolder = (ViewHolder) holder;
|
||||||
|
|
||||||
|
tracks.moveToPosition(position);
|
||||||
|
viewHolder.bind(tracks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
if (tracks == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return tracks.getCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void swapData(Cursor tracks) {
|
||||||
|
this.tracks = tracks;
|
||||||
|
this.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateRecordingStatus(RecordingStatus recordingStatus) {
|
||||||
|
this.recordingStatus = recordingStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateUnitSystem(UnitSystem unitSystem) {
|
||||||
|
this.unitSystem = unitSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||||
|
mode.getMenuInflater().inflate(R.menu.list_context_menu, menu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
||||||
|
selectionMode = true;
|
||||||
|
|
||||||
|
actionModeCallback.onPrepare(menu, null, getCheckedIds(), true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||||
|
if (actionModeCallback.onClick(item.getItemId(), null, getCheckedIds())) {
|
||||||
|
mode.finish();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyActionMode(ActionMode mode) {
|
||||||
|
selectionMode = false;
|
||||||
|
|
||||||
|
setAllSelected(false);
|
||||||
|
|
||||||
|
actionModeCallback.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAllSelected(boolean isSelected) {
|
||||||
|
if (isSelected) {
|
||||||
|
final int idIndex = tracks.getColumnIndexOrThrow(TracksColumns._ID);
|
||||||
|
|
||||||
|
tracks.moveToFirst();
|
||||||
|
do {
|
||||||
|
selection.put((int) tracks.getLong(idIndex), true);
|
||||||
|
} while (tracks.moveToNext());
|
||||||
|
} else {
|
||||||
|
selection.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < recyclerView.getChildCount(); i++) {
|
||||||
|
ViewHolder holder = (ViewHolder) recyclerView.getChildViewHolder(recyclerView.getChildAt(i));
|
||||||
|
holder.setSelected(isSelected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private long[] getCheckedIds() {
|
||||||
|
List<Long> ids = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < selection.size(); i++) {
|
||||||
|
if (selection.valueAt(i)) {
|
||||||
|
ids.add((long) selection.keyAt(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ids.stream().mapToLong(i -> i).toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
|
||||||
|
|
||||||
|
private final TrackListItemBinding viewBinding;
|
||||||
|
private final View view;
|
||||||
|
|
||||||
|
private ActivityType activityType;
|
||||||
|
private String name;
|
||||||
|
private int markerCount;
|
||||||
|
private Duration totalTime;
|
||||||
|
private Distance totalDistance;
|
||||||
|
private Instant startTime;
|
||||||
|
private ZoneOffset zoneOffset;
|
||||||
|
private String activityTypeLocalized;
|
||||||
|
private String description;
|
||||||
|
private Track.Id trackId;
|
||||||
|
|
||||||
|
public ViewHolder(@NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
|
||||||
|
viewBinding = TrackListItemBinding.bind(itemView);
|
||||||
|
view = itemView;
|
||||||
|
|
||||||
|
view.setOnClickListener(this);
|
||||||
|
view.setOnLongClickListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getData(Cursor track) {
|
||||||
|
final int idIndex = track.getColumnIndexOrThrow(TracksColumns._ID);
|
||||||
|
final int nameIndex = track.getColumnIndexOrThrow(TracksColumns.NAME);
|
||||||
|
final int descriptionIndex = track.getColumnIndexOrThrow(TracksColumns.DESCRIPTION);
|
||||||
|
final int activityTypeLocalizedIndex = track.getColumnIndexOrThrow(TracksColumns.ACTIVITY_TYPE_LOCALIZED);
|
||||||
|
final int startTimeIndex = track.getColumnIndexOrThrow(TracksColumns.STARTTIME);
|
||||||
|
final int startTimeOffsetIndex = track.getColumnIndexOrThrow(TracksColumns.STARTTIME_OFFSET);
|
||||||
|
final int totalDistanceIndex = track.getColumnIndexOrThrow(TracksColumns.TOTALDISTANCE);
|
||||||
|
final int totalTimeIndex = track.getColumnIndexOrThrow(TracksColumns.TOTALTIME);
|
||||||
|
final int iconIndex = track.getColumnIndexOrThrow(TracksColumns.ICON);
|
||||||
|
final int markerCountIndex = track.getColumnIndexOrThrow(TracksColumns.MARKER_COUNT);
|
||||||
|
|
||||||
|
activityType = ActivityType.findBy(track.getString(iconIndex));
|
||||||
|
name = track.getString(nameIndex);
|
||||||
|
markerCount = track.getInt(markerCountIndex);
|
||||||
|
totalTime = Duration.ofMillis(track.getLong(totalTimeIndex));
|
||||||
|
totalDistance = Distance.of(track.getFloat(totalDistanceIndex));
|
||||||
|
startTime = Instant.ofEpochMilli(track.getLong(startTimeIndex));
|
||||||
|
zoneOffset = ZoneOffset.ofTotalSeconds(track.getInt(startTimeOffsetIndex));
|
||||||
|
activityTypeLocalized = track.getString(activityTypeLocalizedIndex);
|
||||||
|
description = track.getString(descriptionIndex);
|
||||||
|
trackId = new Track.Id(track.getLong(idIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void bind(Cursor track) {
|
||||||
|
|
||||||
|
getData(track);
|
||||||
|
|
||||||
|
int iconId = activityType.getIconDrawableId();
|
||||||
|
int iconDesc = R.string.image_track;
|
||||||
|
|
||||||
|
boolean isRecordingThisTrackRecording = trackId.equals(recordingStatus.trackId());
|
||||||
|
if (isRecordingThisTrackRecording) {
|
||||||
|
iconId = R.drawable.ic_track_recording;
|
||||||
|
iconDesc = R.string.image_record;
|
||||||
|
}
|
||||||
|
|
||||||
|
viewBinding.trackListItemIcon.setImageResource(iconId);
|
||||||
|
viewBinding.trackListItemIcon.setContentDescription(context.getString(iconDesc));
|
||||||
|
|
||||||
|
viewBinding.trackListItemName.setText(name);
|
||||||
|
|
||||||
|
String timeDistanceText = ListItemUtils.getTimeDistanceText(context, unitSystem, isRecordingThisTrackRecording, totalTime, totalDistance, markerCount);
|
||||||
|
viewBinding.trackListItemTimeDistance.setText(timeDistanceText);
|
||||||
|
|
||||||
|
viewBinding.trackListItemMarkerCountIcon.setVisibility(markerCount > 0 ? View.VISIBLE : View.GONE);
|
||||||
|
viewBinding.trackListItemMarkerCount.setText(markerCount > 0 ? Integer.toString(markerCount) : null);
|
||||||
|
|
||||||
|
if (!recordingStatus.isRecording()) {
|
||||||
|
ListItemUtils.setDateAndTime(context, viewBinding.trackListItemDate, viewBinding.trackListItemTime, startTime, zoneOffset);
|
||||||
|
} else {
|
||||||
|
viewBinding.trackListItemDate.setText(null);
|
||||||
|
viewBinding.trackListItemTime.setText(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
String category = activityType == null ? activityTypeLocalized : null;
|
||||||
|
String categoryDescription = StringUtils.getCategoryDescription(category, description);
|
||||||
|
viewBinding.trackListItemCategoryDescription.setText(categoryDescription);
|
||||||
|
viewBinding.trackListItemCategoryDescription.setVisibility("".equals(categoryDescription) ? View.GONE : View.VISIBLE);
|
||||||
|
|
||||||
|
setSelected(selection.get((int) getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSelected(boolean isSelected) {
|
||||||
|
selection.put((int) getId(), isSelected);
|
||||||
|
view.setActivated(isSelected);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getId() {
|
||||||
|
return trackId.id();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
if (selectionMode) {
|
||||||
|
setSelected(!view.isActivated());
|
||||||
|
actionMode.invalidate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recordingStatus.isRecording() && trackId.equals(recordingStatus.trackId())) {
|
||||||
|
// Is recording -> open record activity.
|
||||||
|
Intent newIntent = IntentUtils.newIntent(context, TrackRecordingActivity.class)
|
||||||
|
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, trackId);
|
||||||
|
context.startActivity(newIntent);
|
||||||
|
} else {
|
||||||
|
// Not recording -> open detail activity.
|
||||||
|
Intent newIntent = IntentUtils.newIntent(context, TrackRecordedActivity.class)
|
||||||
|
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, trackId);
|
||||||
|
ActivityOptions activityOptions = ActivityOptions.makeSceneTransitionAnimation(
|
||||||
|
context,
|
||||||
|
new Pair<>(view.findViewById(R.id.track_list_item_icon), TrackRecordedActivity.VIEW_TRACK_ICON));
|
||||||
|
context.startActivity(newIntent, activityOptions.toBundle());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onLongClick(View v) {
|
||||||
|
setSelected(!view.isActivated());
|
||||||
|
if (!selectionMode) {
|
||||||
|
actionMode = context.startSupportActionMode(TrackListAdapter.this);
|
||||||
|
} else {
|
||||||
|
actionMode.invalidate();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-2
@@ -44,8 +44,8 @@ public class RecordingLayoutIO {
|
|||||||
private static DataField fromCSV(String[] fieldParts, @NonNull Resources resources) {
|
private static DataField fromCSV(String[] fieldParts, @NonNull Resources resources) {
|
||||||
return new DataField(
|
return new DataField(
|
||||||
fieldParts[0],
|
fieldParts[0],
|
||||||
fieldParts[1].equals(YES_VALUE),
|
YES_VALUE.equals(fieldParts[1]),
|
||||||
fieldParts[2].equals(YES_VALUE),
|
YES_VALUE.equals(fieldParts[2]),
|
||||||
fieldParts[0].equals(resources.getString(R.string.stats_custom_layout_coordinates_key)));
|
fieldParts[0].equals(resources.getString(R.string.stats_custom_layout_coordinates_key)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ public class MarkerListActivity extends AbstractActivity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||||
insertMarkerMenuItem.setVisible(trackId != null && trackId.equals(recordingStatus.getTrackId()));
|
insertMarkerMenuItem.setVisible(trackId != null && trackId.equals(recordingStatus.trackId()));
|
||||||
return super.onPrepareOptionsMenu(menu);
|
return super.onPrepareOptionsMenu(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import android.view.MenuItem;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
@@ -23,9 +22,7 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.time.OffsetDateTime;
|
|
||||||
import java.time.ZoneOffset;
|
import java.time.ZoneOffset;
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -116,13 +113,13 @@ public class MarkerListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
|
|||||||
@Override
|
@Override
|
||||||
public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
|
public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
|
||||||
selectionMode = true;
|
selectionMode = true;
|
||||||
actionModeCallback.onPrepare(menu, getCheckedPositions(), getCheckedIds(), true);
|
actionModeCallback.onPrepare(menu, null, getCheckedIds(), true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
|
public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
|
||||||
if (actionModeCallback.onClick(menuItem.getItemId(), getCheckedPositions(), getCheckedIds())) {
|
if (actionModeCallback.onClick(menuItem.getItemId(), null, getCheckedIds())) {
|
||||||
actionMode.finish();
|
actionMode.finish();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -133,37 +130,31 @@ public class MarkerListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
|
|||||||
selectionMode = false;
|
selectionMode = false;
|
||||||
|
|
||||||
setAllSelected(false);
|
setAllSelected(false);
|
||||||
selection.clear();
|
|
||||||
|
|
||||||
actionModeCallback.onDestroy();
|
actionModeCallback.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAllSelected(boolean isSelected) {
|
public void setAllSelected(boolean isSelected) {
|
||||||
for (int i = 0; i < getItemCount(); i++) {
|
if (isSelected) {
|
||||||
ViewHolder holder = (ViewHolder) recyclerView.findViewHolderForLayoutPosition(i);
|
for (Marker marker : markers) {
|
||||||
|
selection.put((int) marker.getId().id(), true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
selection.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < recyclerView.getChildCount(); i++) {
|
||||||
|
ViewHolder holder = (ViewHolder) recyclerView.getChildViewHolder(recyclerView.getChildAt(i));
|
||||||
holder.setSelected(isSelected);
|
holder.setSelected(isSelected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int[] getCheckedPositions() {
|
|
||||||
List<Integer> positions = new ArrayList<>();
|
|
||||||
|
|
||||||
for (int i = 0; i < selection.size(); i++) {
|
|
||||||
if (selection.valueAt(i)) {
|
|
||||||
positions.add(selection.keyAt(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return positions.stream().mapToInt(i -> i).toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
private long[] getCheckedIds() {
|
private long[] getCheckedIds() {
|
||||||
List<Long> ids = new ArrayList<>();
|
List<Long> ids = new ArrayList<>();
|
||||||
|
|
||||||
for (int i = 0; i < selection.size(); i++) {
|
for (int i = 0; i < selection.size(); i++) {
|
||||||
if (selection.valueAt(i)) {
|
if (selection.valueAt(i)) {
|
||||||
ViewHolder holder = (ViewHolder) recyclerView.findViewHolderForLayoutPosition(selection.keyAt(i));
|
ids.add((long) selection.keyAt(i));
|
||||||
ids.add(holder.getId());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,11 +203,11 @@ public class MarkerListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
|
|||||||
String categoryDescription = StringUtils.getCategoryDescription(marker.getCategory(), marker.getDescription());
|
String categoryDescription = StringUtils.getCategoryDescription(marker.getCategory(), marker.getDescription());
|
||||||
viewBinding.markerListItemTimeDistance.setText(categoryDescription);
|
viewBinding.markerListItemTimeDistance.setText(categoryDescription);
|
||||||
|
|
||||||
setSelected(selection.get(getLayoutPosition()));
|
setSelected(selection.get((int) getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSelected(boolean isSelected) {
|
public void setSelected(boolean isSelected) {
|
||||||
selection.put(getLayoutPosition(), isSelected);
|
selection.put((int) getId(), isSelected);
|
||||||
view.setActivated(isSelected);
|
view.setActivated(isSelected);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,8 +229,12 @@ public class MarkerListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onLongClick(View view) {
|
public boolean onLongClick(View view) {
|
||||||
actionMode = context.startSupportActionMode(MarkerListAdapter.this);
|
setSelected(!view.isActivated());
|
||||||
setSelected(true);
|
if (!selectionMode) {
|
||||||
|
actionMode = context.startSupportActionMode(MarkerListAdapter.this);
|
||||||
|
} else {
|
||||||
|
actionMode.invalidate();
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,77 +9,16 @@ import android.os.Build;
|
|||||||
import android.os.VibrationEffect;
|
import android.os.VibrationEffect;
|
||||||
import android.os.Vibrator;
|
import android.os.Vibrator;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.SparseBooleanArray;
|
|
||||||
import android.view.ActionMode;
|
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.widget.AbsListView;
|
|
||||||
import android.widget.ListView;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.widget.SearchView;
|
import androidx.appcompat.widget.SearchView;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
|
||||||
|
|
||||||
public class ActivityUtils {
|
public class ActivityUtils {
|
||||||
|
|
||||||
private static final String TAG = ActivityUtils.class.getSimpleName();
|
private static final String TAG = ActivityUtils.class.getSimpleName();
|
||||||
|
|
||||||
public static void configureListViewContextualMenu(final ListView listView, final ContextualActionModeCallback contextualActionModeCallback) {
|
|
||||||
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
|
|
||||||
listView.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
|
||||||
mode.getMenuInflater().inflate(R.menu.list_context_menu, menu);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
|
||||||
contextualActionModeCallback.onPrepare(menu, getCheckedPositions(listView), listView.getCheckedItemIds(), true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyActionMode(ActionMode mode) {
|
|
||||||
contextualActionModeCallback.onDestroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
|
|
||||||
mode.invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
|
||||||
if (contextualActionModeCallback.onClick(item.getItemId(), getCheckedPositions(listView), listView.getCheckedItemIds())) {
|
|
||||||
mode.finish();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the checked positions in a list view.
|
|
||||||
*
|
|
||||||
* @param list the list view
|
|
||||||
*/
|
|
||||||
private int[] getCheckedPositions(ListView list) {
|
|
||||||
SparseBooleanArray positions = list.getCheckedItemPositions();
|
|
||||||
ArrayList<Integer> arrayList = new ArrayList<>();
|
|
||||||
for (int i = 0; i < positions.size(); i++) {
|
|
||||||
int key = positions.keyAt(i);
|
|
||||||
if (positions.valueAt(i)) {
|
|
||||||
arrayList.add(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return arrayList.stream().mapToInt(i -> i).toArray();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SearchView configureSearchWidget(Activity activity, final MenuItem menuItem) {
|
public static SearchView configureSearchWidget(Activity activity, final MenuItem menuItem) {
|
||||||
final SearchView searchView = (SearchView) menuItem.getActionView();
|
final SearchView searchView = (SearchView) menuItem.getActionView();
|
||||||
SearchManager searchManager = (SearchManager) activity.getSystemService(Context.SEARCH_SERVICE);
|
SearchManager searchManager = (SearchManager) activity.getSystemService(Context.SEARCH_SERVICE);
|
||||||
|
|||||||
@@ -18,18 +18,20 @@ package de.dennisguse.opentracks.ui.util;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup.LayoutParams;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.time.ZoneOffset;
|
import java.time.ZoneOffset;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
|
import de.dennisguse.opentracks.data.models.Distance;
|
||||||
|
import de.dennisguse.opentracks.data.models.DistanceFormatter;
|
||||||
|
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||||
import de.dennisguse.opentracks.util.StringUtils;
|
import de.dennisguse.opentracks.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,101 +44,6 @@ public class ListItemUtils {
|
|||||||
private ListItemUtils() {
|
private ListItemUtils() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets a list item.
|
|
||||||
*
|
|
||||||
* @param context the context
|
|
||||||
* @param view the list item view
|
|
||||||
* @param isRecording true if recording
|
|
||||||
* @param iconId the icon id
|
|
||||||
* @param iconContentDescriptionId the icon content description id
|
|
||||||
* @param name the name value
|
|
||||||
* @param totalTime the total time value
|
|
||||||
* @param totalDistance the total distance value
|
|
||||||
* @param markerCount the marker count
|
|
||||||
* @param offsetDateTime the start time with offset
|
|
||||||
* @param category the category value
|
|
||||||
* @param description the description value
|
|
||||||
* @param hasPhoto true if this list item has photo
|
|
||||||
*/
|
|
||||||
public static void setListItem(Context context, View view, boolean isRecording, int iconId, int iconContentDescriptionId, String name, String totalTime, String totalDistance, int markerCount, OffsetDateTime offsetDateTime, String category, String description, boolean hasPhoto) {
|
|
||||||
// Set icon
|
|
||||||
if (isRecording) {
|
|
||||||
iconId = R.drawable.ic_track_recording;
|
|
||||||
iconContentDescriptionId = R.string.image_record;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageView iconImageView = view.findViewById(R.id.list_item_icon);
|
|
||||||
iconImageView.setImageResource(iconId);
|
|
||||||
iconImageView.setContentDescription(context.getString(iconContentDescriptionId));
|
|
||||||
|
|
||||||
// Set name
|
|
||||||
TextView nameTextView = view.findViewById(R.id.list_item_name);
|
|
||||||
setTextView(context, nameTextView, name, hasPhoto);
|
|
||||||
|
|
||||||
// Set totalTime/totalDistance
|
|
||||||
TextView timeDistanceTextView = view.findViewById(R.id.list_item_time_distance);
|
|
||||||
String timeDistanceText;
|
|
||||||
if (isRecording) {
|
|
||||||
timeDistanceText = context.getString(R.string.generic_recording);
|
|
||||||
} else {
|
|
||||||
// Match list_item_time_distance in list_item.xml
|
|
||||||
timeDistanceText = getTimeDistance(totalTime, totalDistance);
|
|
||||||
if (markerCount > 0) {
|
|
||||||
timeDistanceText += " ‧";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setTextView(context, timeDistanceTextView, timeDistanceText, hasPhoto);
|
|
||||||
|
|
||||||
// Set markerCount
|
|
||||||
ImageView markerCountIcon = view.findViewById(R.id.list_item_marker_count_icon);
|
|
||||||
TextView markerCountTextView = view.findViewById(R.id.list_item_marker_count);
|
|
||||||
boolean hasMarker = markerCount > 0;
|
|
||||||
markerCountIcon.setVisibility(hasMarker ? View.VISIBLE : View.GONE);
|
|
||||||
String markerCountValue = hasMarker ? String.valueOf(markerCount) : null;
|
|
||||||
if (hasMarker) {
|
|
||||||
// Scale markerCountIcon
|
|
||||||
int lineHeight = markerCountTextView.getLineHeight();
|
|
||||||
LayoutParams layoutParams = markerCountIcon.getLayoutParams();
|
|
||||||
layoutParams.width = lineHeight;
|
|
||||||
layoutParams.height = lineHeight;
|
|
||||||
}
|
|
||||||
setTextView(context, markerCountTextView, markerCountValue, hasPhoto);
|
|
||||||
|
|
||||||
// Set date/time
|
|
||||||
TextView dateTextView = view.findViewById(R.id.list_item_date);
|
|
||||||
TextView timeTextView = view.findViewById(R.id.list_item_time);
|
|
||||||
String dateValue = null;
|
|
||||||
String timeValue = null;
|
|
||||||
if (!isRecording) {
|
|
||||||
dateValue = StringUtils.formatDateTodayRelative(context, offsetDateTime);
|
|
||||||
String pattern = "HH:mm";
|
|
||||||
if (!offsetDateTime.getOffset().equals(OffsetDateTime.now().getOffset())) {
|
|
||||||
pattern = "HH:mm x";
|
|
||||||
}
|
|
||||||
timeValue = offsetDateTime.format(DateTimeFormatter.ofPattern(pattern));
|
|
||||||
}
|
|
||||||
setTextView(context, dateTextView, dateValue, hasPhoto);
|
|
||||||
setTextView(context, timeTextView, timeValue, hasPhoto);
|
|
||||||
|
|
||||||
// Set category and description
|
|
||||||
TextView categoryDescriptionTextView = view.findViewById(R.id.list_item_category_description);
|
|
||||||
String categoryDescription = isRecording ? null : StringUtils.getCategoryDescription(category, description);
|
|
||||||
|
|
||||||
// Place categoryDescription in either ownerTimeDistanceTextView or categoryDescriptionTextView
|
|
||||||
if (timeDistanceTextView.getVisibility() == View.GONE && markerCountIcon.getVisibility() == View.GONE) {
|
|
||||||
setTextView(context, categoryDescriptionTextView, null, hasPhoto);
|
|
||||||
// Match list_item_category_description in list_item.xml
|
|
||||||
timeDistanceTextView.setSingleLine(false);
|
|
||||||
timeDistanceTextView.setMaxLines(2);
|
|
||||||
setTextView(context, timeDistanceTextView, categoryDescription, hasPhoto);
|
|
||||||
} else {
|
|
||||||
// Match list_item_time_distance in list_item.xml
|
|
||||||
timeDistanceTextView.setSingleLine(true);
|
|
||||||
setTextView(context, categoryDescriptionTextView, categoryDescription, hasPhoto);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a string for share owner, total time, and total distance.
|
* Gets a string for share owner, total time, and total distance.
|
||||||
*
|
*
|
||||||
@@ -160,6 +67,29 @@ public class ListItemUtils {
|
|||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getTimeDistanceText(Context context, UnitSystem unitSystem, boolean isRecording, Duration totalTime, Distance totalDistance, int markerCount) {
|
||||||
|
String timeDistanceText;
|
||||||
|
if (isRecording) {
|
||||||
|
timeDistanceText = context.getString(R.string.generic_recording);
|
||||||
|
} else {
|
||||||
|
// Match list_item_time_distance in list_item.xml
|
||||||
|
|
||||||
|
String time = StringUtils.formatElapsedTime(totalTime);
|
||||||
|
// TODO unit system
|
||||||
|
String distance = DistanceFormatter.Builder()
|
||||||
|
.setUnit(unitSystem)
|
||||||
|
.build(context)
|
||||||
|
.formatDistance(totalDistance);
|
||||||
|
|
||||||
|
timeDistanceText = getTimeDistance(time, distance);
|
||||||
|
if (markerCount > 0) {
|
||||||
|
timeDistanceText += " ‧";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return timeDistanceText;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a text view.
|
* Sets a text view.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -0,0 +1,239 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="5.8208"
|
||||||
|
android:viewportHeight="5.8208">
|
||||||
|
<path
|
||||||
|
android:pathData="m-1.3253,1.5033 l-0.4101,0.6834a0.1285,0.1285 60.4819,0 0,0.1102 0.1945l0.2496,-0 -0.11,0.5496a0.0898,0.0898 21.1422,0 0,0.1651 0.0638L-0.9105,2.3112A0.1285,0.1285 60.4819,0 0,-1.0206 2.1167H-1.2702L-1.1602,1.5671a0.0898,0.0898 21.1422,0 0,-0.1651 -0.0638z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="0.264583"
|
||||||
|
android:fillColor="#fdbc4b"
|
||||||
|
android:strokeColor="#00000000"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M14.7722,-15.5564l26.5377,0l0,30.6204l-26.5377,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.6652"
|
||||||
|
android:fillColor="#cacaca"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M15.7928,-14.5358l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#e74c3c"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M19.8755,-14.5358l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#ed1515"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M23.9583,-14.5358l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#f47750"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M28.041,-14.5358l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#fdbc4b"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M32.1237,-14.5358l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#c9ce3b"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M15.7928,-10.453l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#da4453"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M19.8755,-10.453l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#c0392b"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M23.9583,-10.453l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#d35400"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M28.041,-10.453l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#f67400"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M32.1237,-10.453l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#f39c1f"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M15.7928,-4.329l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#1cdc9a"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M19.8755,-4.329l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#2ecc71"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M23.9583,-4.329l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#1abc9c"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M28.041,-4.329l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#3498db"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M32.1237,-4.329l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#3daee9"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M15.7928,-0.2463l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#10d116"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M19.8755,-0.2463l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#27ae60"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M23.9583,-0.2463l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#16a085"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M28.041,-0.2463l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#2980b9"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M32.1237,-0.2463l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#1d99f3"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M36.2064,-4.329l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#9b59b6"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M36.2064,-0.2463l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#8e44ad"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M15.7928,5.8778l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#34495e"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M19.8756,5.8778l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#31363b"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M23.9583,5.8778l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#7f8c8d"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M28.041,5.8778l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#bdc3c7"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M32.1237,5.8778l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#fcfcfc"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M15.7928,9.9605l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#2c3e50"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M19.8756,9.9605l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#232629"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M23.9583,9.9605l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#4d4d4d"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M28.041,9.9605l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#95a5a6"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M32.1237,9.9605l4.0827,0l0,4.0827l-4.0827,0z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="5.7692"
|
||||||
|
android:fillColor="#eff0f1"
|
||||||
|
android:strokeLineCap="square"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M3.7026,1.1906A0.1323,0.1323 0,0 0,3.5858 1.264A0.1323,0.1323 0,0 0,3.6453 1.4413L3.9445,1.5911C4.0556,1.6468 4.1371,1.748 4.1672,1.8686L4.6013,3.6044C4.0697,3.6801 3.6613,4.0946 3.5987,4.6302L2.2221,4.6302C2.1532,4.0395 1.668,3.5724 1.0594,3.5719C1.0537,3.5718 1.048,3.5718 1.0423,3.5719L0.5292,3.5719A0.1323,0.1323 0,0 0,0.3969 3.7042A0.1323,0.1323 0,0 0,0.5292 3.8365L1.0423,3.8365A0.1323,0.1323 0,0 0,1.0433 3.8365C1.048,3.8364 1.0526,3.8364 1.0573,3.8365A0.1323,0.1323 0,0 0,1.0583 3.8365C1.5713,3.8365 1.9844,4.2495 1.9844,4.7625A0.1323,0.1323 0,0 0,2.1167 4.8948L3.7042,4.8948A0.1323,0.1323 0,0 0,3.8365 4.7625C3.8365,4.2495 4.2495,3.8365 4.7625,3.8365A0.1323,0.1323 0,0 0,4.8907 3.6721L4.4235,1.8045C4.3747,1.6092 4.2433,1.4446 4.0633,1.3544A0.1323,0.1323 0,0 0,4.0628 1.3544L3.7631,1.2046A0.1323,0.1323 0,0 0,3.7026 1.1906z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="0.265"
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:strokeColor="#00000000"
|
||||||
|
android:fillType="nonZero"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M4.7617,4.1367C4.4266,4.1367 4.1523,4.4109 4.1523,4.7461C4.1523,5.0812 4.4266,5.3555 4.7617,5.3555C5.0969,5.3555 5.3711,5.0812 5.3711,4.7461C5.3711,4.4109 5.0969,4.1367 4.7617,4.1367zM4.7617,4.3379C4.9879,4.3379 5.168,4.5199 5.168,4.7461C5.168,4.9723 4.9879,5.1543 4.7617,5.1543C4.5355,5.1543 4.3535,4.9723 4.3535,4.7461C4.3535,4.5199 4.5355,4.3379 4.7617,4.3379z"
|
||||||
|
android:strokeAlpha="0.997"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="0.201567"
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:strokeColor="#00000000"
|
||||||
|
android:fillType="nonZero"
|
||||||
|
android:fillAlpha="0.997"
|
||||||
|
android:strokeLineCap="butt"/>
|
||||||
|
<path
|
||||||
|
android:pathData="m1.0222,4.1367c-0.3352,0 -0.6094,0.2742 -0.6094,0.6094 0,0.3352 0.2742,0.6094 0.6094,0.6094 0.3352,-0 0.6094,-0.2742 0.6094,-0.6094 -0,-0.3352 -0.2742,-0.6094 -0.6094,-0.6094zM1.0222,4.3379c0.2262,0 0.4062,0.182 0.4063,0.4082 -0,0.2262 -0.18,0.4082 -0.4063,0.4082 -0.2262,0 -0.4082,-0.182 -0.4082,-0.4082 0,-0.2262 0.182,-0.4082 0.4082,-0.4082z"
|
||||||
|
android:strokeAlpha="0.997"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="0.201567"
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:strokeColor="#00000000"
|
||||||
|
android:fillType="nonZero"
|
||||||
|
android:fillAlpha="0.997"
|
||||||
|
android:strokeLineCap="butt"/>
|
||||||
|
</vector>
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:background="@drawable/activated_background"
|
|
||||||
android:paddingStart="8dp"
|
|
||||||
android:paddingEnd="8dp"
|
|
||||||
android:paddingTop="12dp"
|
|
||||||
android:paddingBottom="12dp">
|
|
||||||
|
|
||||||
<!-- Photo -->
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/list_item_photo"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
android:layout_marginBottom="4dp"
|
|
||||||
android:contentDescription="@string/image_photo_marker"
|
|
||||||
android:scaleType="centerCrop" />
|
|
||||||
|
|
||||||
<!-- A gradient on top of the text -->
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/list_item_text_gradient"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
android:alpha=".6"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:visibility="visible"
|
|
||||||
android:contentDescription="@string/image_photo_marker"
|
|
||||||
android:src="@drawable/text_gradient" />
|
|
||||||
|
|
||||||
<!-- Icon -->
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/list_item_icon"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
android:contentDescription="@string/image_track"
|
|
||||||
tools:src="@drawable/ic_activity_bike_24dp"/>
|
|
||||||
|
|
||||||
<!-- Name -->
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/list_item_name"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="?attr/textAppearanceBody1"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/list_item_icon"
|
|
||||||
app:layout_constraintEnd_toStartOf="@id/list_item_date"
|
|
||||||
tools:text="Main text" />
|
|
||||||
|
|
||||||
<!-- Date -->
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/list_item_date"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="?attr/textAppearanceBodySmall"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/list_item_name"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
android:gravity="end"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
tools:text="2022-02-17" />
|
|
||||||
|
|
||||||
<!-- Total time and distance -->
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/list_item_time_distance"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="?attr/textAppearanceBodySmall"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/list_item_name"
|
|
||||||
app:layout_constraintStart_toStartOf="@id/list_item_name"
|
|
||||||
tools:text="50:43 (50.00km)" />
|
|
||||||
|
|
||||||
<!-- Marker count icon -->
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/list_item_marker_count_icon"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/list_item_name"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/list_item_time_distance"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@id/list_item_time_distance"
|
|
||||||
android:contentDescription="@string/image_marker_count"
|
|
||||||
android:scaleType="centerInside"
|
|
||||||
android:src="@drawable/ic_marker_show_24dp" />
|
|
||||||
|
|
||||||
<!-- Marker count text -->
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/list_item_marker_count"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="?attr/textAppearanceBodySmall"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/list_item_marker_count_icon"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@id/list_item_marker_count_icon"
|
|
||||||
android:singleLine="true"
|
|
||||||
tools:text="2" />
|
|
||||||
|
|
||||||
<!-- Time -->
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/list_item_time"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="?attr/textAppearanceBodySmall"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/list_item_name"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/list_item_marker_count"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
android:gravity="end"
|
|
||||||
android:layout_gravity="bottom"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
tools:text="10:10:10"/>
|
|
||||||
|
|
||||||
<!-- Second row barrier -->
|
|
||||||
<androidx.constraintlayout.widget.Barrier
|
|
||||||
android:id="@+id/list_item_second_row_barrier"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:barrierDirection="bottom"
|
|
||||||
app:constraint_referenced_ids="list_item_time_distance,list_item_marker_count_icon,list_item_marker_count,list_item_time" />
|
|
||||||
|
|
||||||
<!-- Category and description -->
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/list_item_category_description"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="?attr/textAppearanceBodySmall"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/list_item_second_row_barrier"
|
|
||||||
app:layout_constraintStart_toStartOf="@id/list_item_name"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
android:maxLines="2"
|
|
||||||
android:ellipsize="end"
|
|
||||||
tools:text="Description" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -584,53 +584,85 @@
|
|||||||
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
app:layout_constraintEnd_toStartOf="@id/guideline3"
|
||||||
app:layout_constraintTop_toBottomOf="@id/stats_cadence_barrier" />
|
app:layout_constraintTop_toBottomOf="@id/stats_cadence_barrier" />
|
||||||
|
|
||||||
<!-- Sensors data -->
|
|
||||||
<androidx.constraintlayout.widget.Group
|
<androidx.constraintlayout.widget.Group
|
||||||
android:id="@+id/stats_power_group"
|
android:id="@+id/stats_power_group"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:constraint_referenced_ids="stats_power_horizontal_line,stats_power_label,stats_power_value,stats_power_unit" />
|
app:constraint_referenced_ids="stats_power_horizontal_line,stats_max_power_label,stats_max_power_value,stats_max_power_unit,stats_avg_power_label,stats_avg_power_value,stats_avg_power_unit" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/stats_power_label"
|
android:id="@+id/stats_max_power_label"
|
||||||
style="@style/TextAppearance.OpenTracks.PrimaryHeader"
|
style="@style/TextAppearance.OpenTracks.PrimaryHeader"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/sensor_state_power_avg"
|
android:text="@string/sensor_state_power_max"
|
||||||
app:layout_constrainedWidth="true"
|
app:layout_constrainedWidth="true"
|
||||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||||
app:layout_constraintEnd_toStartOf="@id/guideline2"
|
app:layout_constraintEnd_toStartOf="@id/guideline2"
|
||||||
app:layout_constraintTop_toBottomOf="@id/stats_power_horizontal_line" />
|
app:layout_constraintTop_toBottomOf="@id/stats_power_horizontal_line" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/stats_power_value"
|
android:id="@+id/stats_max_power_value"
|
||||||
style="@style/TextAppearance.OpenTracks.PrimaryValue"
|
style="@style/TextAppearance.OpenTracks.PrimaryValue"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:value="@string/value_unknown"
|
android:value="@string/value_unknown"
|
||||||
app:layout_constraintHorizontal_chainStyle="packed"
|
app:layout_constraintHorizontal_chainStyle="packed"
|
||||||
app:layout_constraintStart_toEndOf="@id/guideline"
|
app:layout_constraintStart_toEndOf="@id/guideline"
|
||||||
app:layout_constraintEnd_toStartOf="@id/stats_power_unit"
|
app:layout_constraintEnd_toStartOf="@id/stats_max_power_unit"
|
||||||
app:layout_constraintTop_toBottomOf="@id/stats_power_label"
|
app:layout_constraintTop_toBottomOf="@id/stats_max_power_label"
|
||||||
tools:text="100" />
|
tools:text="100" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/stats_power_unit"
|
android:id="@+id/stats_max_power_unit"
|
||||||
style="@style/TextAppearance.OpenTracks.PrimaryUnit"
|
style="@style/TextAppearance.OpenTracks.PrimaryUnit"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/sensor_state_power_unit"
|
android:text="@string/sensor_state_power_unit"
|
||||||
app:layout_constraintBottom_toBottomOf="@id/stats_power_value"
|
app:layout_constraintBottom_toBottomOf="@id/stats_max_power_value"
|
||||||
app:layout_constraintStart_toEndOf="@id/stats_power_value"
|
app:layout_constraintStart_toEndOf="@id/stats_max_power_value"
|
||||||
app:layout_constraintEnd_toStartOf="@id/guideline2" />
|
app:layout_constraintEnd_toStartOf="@id/guideline2" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/stats_avg_power_label"
|
||||||
|
style="@style/TextAppearance.OpenTracks.PrimaryHeader"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/sensor_state_power_avg"
|
||||||
|
app:layout_constrainedWidth="true"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/guideline3"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/stats_power_horizontal_line" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/stats_avg_power_value"
|
||||||
|
style="@style/TextAppearance.OpenTracks.PrimaryValue"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:value="@string/value_unknown"
|
||||||
|
app:layout_constraintHorizontal_chainStyle="packed"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/guideline2"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/stats_avg_power_unit"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/stats_avg_power_label"
|
||||||
|
tools:text="100" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/stats_avg_power_unit"
|
||||||
|
style="@style/TextAppearance.OpenTracks.PrimaryUnit"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/sensor_state_power_unit"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/stats_avg_power_value"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/stats_avg_power_value"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/guideline3" />
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Barrier
|
<androidx.constraintlayout.widget.Barrier
|
||||||
android:id="@+id/stats_power_barrier"
|
android:id="@+id/stats_power_barrier"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:barrierDirection="bottom"
|
app:barrierDirection="bottom"
|
||||||
app:constraint_referenced_ids="stats_power_value" />
|
app:constraint_referenced_ids="stats_max_power_value,stats_avg_power_value" />
|
||||||
|
|
||||||
<!-- Horizontal Line -->
|
<!-- Horizontal Line -->
|
||||||
<View
|
<View
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="16dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/stats_description_main"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="@style/TextAppearance.OpenTracks.PrimaryHeader"
|
|
||||||
tools:text="@string/sensor_state_heart_rate"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/stats_value"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="@style/TextAppearance.OpenTracks.PrimaryValue"
|
|
||||||
android:value="@string/value_unknown"
|
|
||||||
app:layout_constraintHorizontal_chainStyle="packed"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/stats_description_main"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@id/stats_unit"
|
|
||||||
tools:text="90" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/stats_unit"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="@style/TextAppearance.OpenTracks.PrimaryUnit"
|
|
||||||
tools:text="@string/sensor_unit_beats_per_minute"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/stats_value"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@id/stats_value" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/stats_description_secondary"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
style="@style/TextAppearance.OpenTracks.SecondaryHeader"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/stats_value"
|
|
||||||
tools:visibility="visible"
|
|
||||||
tools:text="90" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -39,7 +39,7 @@ limitations under the License.
|
|||||||
android:paddingBottom="?attr/actionBarSize"
|
android:paddingBottom="?attr/actionBarSize"
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
|
|
||||||
<ListView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/track_list"
|
android:id="@+id/track_list"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
|
|||||||
@@ -0,0 +1,117 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/activated_background"
|
||||||
|
android:padding="8dp">
|
||||||
|
|
||||||
|
<!-- Icon -->
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/track_list_item_icon"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:contentDescription="@string/image_track"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:src="@drawable/ic_activity_bike_24dp" />
|
||||||
|
|
||||||
|
<!-- Name -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/track_list_item_name"
|
||||||
|
style="?attr/textAppearanceBody1"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:singleLine="true"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/track_list_item_icon"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="Main Text" />
|
||||||
|
|
||||||
|
<!-- Date -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/track_list_item_date"
|
||||||
|
style="?attr/textAppearanceBodySmall"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="end"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="2022-02-17" />
|
||||||
|
|
||||||
|
<!-- Total time and distance -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/track_list_item_time_distance"
|
||||||
|
style="?attr/textAppearanceBodySmall"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/track_list_item_name"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/track_list_item_name"
|
||||||
|
tools:text="50:43 (50.00km)" />
|
||||||
|
|
||||||
|
<!-- Marker Count Icon -->
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/track_list_item_marker_count_icon"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:contentDescription="@string/image_marker_count"
|
||||||
|
android:scaleType="centerInside"
|
||||||
|
android:src="@drawable/ic_marker_show_24dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/track_list_item_time_distance"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/track_list_item_time_distance"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/track_list_item_name" />
|
||||||
|
|
||||||
|
<!-- Marker count -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/track_list_item_marker_count"
|
||||||
|
style="?attr/textAppearanceBodySmall"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:singleLine="true"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/track_list_item_marker_count_icon"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/track_list_item_marker_count_icon"
|
||||||
|
tools:text="2" />
|
||||||
|
|
||||||
|
<!-- Time -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/track_list_item_time"
|
||||||
|
style="?attr/textAppearanceBodySmall"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="end"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/track_list_item_name"
|
||||||
|
tools:text="10:10:10" />
|
||||||
|
|
||||||
|
<!-- Second row barrier -->
|
||||||
|
<androidx.constraintlayout.widget.Barrier
|
||||||
|
android:id="@+id/track_list_item_barrier"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:barrierDirection="bottom"
|
||||||
|
app:constraint_referenced_ids="track_list_item_time_distance,track_list_item_marker_count_icon,track_list_item_marker_count,track_list_item_time" />
|
||||||
|
|
||||||
|
<!-- Category and description -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/track_list_item_category_description"
|
||||||
|
style="?attr/textAppearanceBodySmall"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="2"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/track_list_item_name"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/track_list_item_barrier"
|
||||||
|
tools:text="Description" />
|
||||||
|
|
||||||
|
<!-- Bottom Line -->
|
||||||
|
<View
|
||||||
|
android:id="@+id/marker_list_item_bottom_line"
|
||||||
|
style="@style/HorizontalLine"
|
||||||
|
android:layout_marginBottom="0dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/track_list_item_category_description" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -126,7 +126,6 @@ limitations under the License.
|
|||||||
<string name="gps_wait_for_signal">في انتظار إشارة نظام تحديد المواقع العالمي (GPS)</string>
|
<string name="gps_wait_for_signal">في انتظار إشارة نظام تحديد المواقع العالمي (GPS)</string>
|
||||||
<!-- Image -->
|
<!-- Image -->
|
||||||
<string name="image_arrow">سهم</string>
|
<string name="image_arrow">سهم</string>
|
||||||
<string name="image_marker">علامة</string>
|
|
||||||
<string name="image_marker_count">عدد العلامات</string>
|
<string name="image_marker_count">عدد العلامات</string>
|
||||||
<string name="image_photo_marker">علامة الصورة</string>
|
<string name="image_photo_marker">علامة الصورة</string>
|
||||||
<string name="image_record">تسجيل</string>
|
<string name="image_record">تسجيل</string>
|
||||||
@@ -163,8 +162,6 @@ limitations under the License.
|
|||||||
<string name="menu_settings">الإعدادات</string>
|
<string name="menu_settings">الإعدادات</string>
|
||||||
<string name="menu_share">مشاركة</string>
|
<string name="menu_share">مشاركة</string>
|
||||||
<string name="menu_show_on_map">إظهار على الخريطة</string>
|
<string name="menu_show_on_map">إظهار على الخريطة</string>
|
||||||
<string name="menu_start_gps">بدء GPS</string>
|
|
||||||
<string name="menu_stop_gps">إيقاف GPS</string>
|
|
||||||
<!-- OpenTracks -->
|
<!-- OpenTracks -->
|
||||||
<string name="recording_service">خدمة التسجيل</string>
|
<string name="recording_service">خدمة التسجيل</string>
|
||||||
<!-- Search -->
|
<!-- Search -->
|
||||||
@@ -193,7 +190,6 @@ limitations under the License.
|
|||||||
<string name="settings_reset_confirm_title">إعادة التعيين الآن؟</string>
|
<string name="settings_reset_confirm_title">إعادة التعيين الآن؟</string>
|
||||||
<string name="settings_reset_done">لقد تمت إعادة جميع الإعدادات إلى القيم الافتراضية</string>
|
<string name="settings_reset_done">لقد تمت إعادة جميع الإعدادات إلى القيم الافتراضية</string>
|
||||||
<!-- Settings Sensor -->
|
<!-- Settings Sensor -->
|
||||||
<string name="settings_sensor_bluetooth">البلوتوث</string>
|
|
||||||
<!-- Settings Stats -->
|
<!-- Settings Stats -->
|
||||||
<string name="settings_stats_rate_title">المعدل المفضل</string>
|
<string name="settings_stats_rate_title">المعدل المفضل</string>
|
||||||
<string name="settings_stats_units_imperial">وحدات القياس البريطانية (ميل، قدم)</string>
|
<string name="settings_stats_units_imperial">وحدات القياس البريطانية (ميل، قدم)</string>
|
||||||
|
|||||||
@@ -104,7 +104,6 @@
|
|||||||
<string name="gps_wait_for_better_signal">Waiting for a better GPS signal</string>
|
<string name="gps_wait_for_better_signal">Waiting for a better GPS signal</string>
|
||||||
<string name="gps_wait_for_signal">Waiting for GPS signal</string>
|
<string name="gps_wait_for_signal">Waiting for GPS signal</string>
|
||||||
<string name="image_arrow">Arrow</string>
|
<string name="image_arrow">Arrow</string>
|
||||||
<string name="image_marker">Marker</string>
|
|
||||||
<string name="image_marker_count">Marker count</string>
|
<string name="image_marker_count">Marker count</string>
|
||||||
<string name="image_photo_marker">Photo marker</string>
|
<string name="image_photo_marker">Photo marker</string>
|
||||||
<string name="image_record">Record</string>
|
<string name="image_record">Record</string>
|
||||||
@@ -135,8 +134,6 @@
|
|||||||
<string name="menu_settings">Settings</string>
|
<string name="menu_settings">Settings</string>
|
||||||
<string name="menu_share">Share</string>
|
<string name="menu_share">Share</string>
|
||||||
<string name="menu_show_on_map">Show on map</string>
|
<string name="menu_show_on_map">Show on map</string>
|
||||||
<string name="menu_start_gps">Start GPS</string>
|
|
||||||
<string name="menu_stop_gps">Stop GPS</string>
|
|
||||||
<string name="recording_service">Recording service</string>
|
<string name="recording_service">Recording service</string>
|
||||||
<string name="sensor_state_heart_rate">Heart rate</string>
|
<string name="sensor_state_heart_rate">Heart rate</string>
|
||||||
<string name="sensor_state_heart_rate_value">%1$d bpm</string>
|
<string name="sensor_state_heart_rate_value">%1$d bpm</string>
|
||||||
@@ -158,7 +155,6 @@
|
|||||||
<string name="settings_reset_confirm_message">All settings will be reverted to default values.</string>
|
<string name="settings_reset_confirm_message">All settings will be reverted to default values.</string>
|
||||||
<string name="settings_reset_confirm_title">Reset now?</string>
|
<string name="settings_reset_confirm_title">Reset now?</string>
|
||||||
<string name="settings_reset_done">All settings have been reverted to default values</string>
|
<string name="settings_reset_done">All settings have been reverted to default values</string>
|
||||||
<string name="settings_sensor_bluetooth">Bluetooth</string>
|
|
||||||
<string name="settings_stats_rate_title">Preferred rate</string>
|
<string name="settings_stats_rate_title">Preferred rate</string>
|
||||||
<string name="settings_stats_units_imperial">Imperial (miles, ft)</string>
|
<string name="settings_stats_units_imperial">Imperial (miles, ft)</string>
|
||||||
<string name="settings_stats_units_metric">Metric (km, m)</string>
|
<string name="settings_stats_units_metric">Metric (km, m)</string>
|
||||||
|
|||||||
@@ -104,7 +104,6 @@
|
|||||||
<string name="gps_wait_for_better_signal">Esperando una mejor señal de GPS</string>
|
<string name="gps_wait_for_better_signal">Esperando una mejor señal de GPS</string>
|
||||||
<string name="gps_wait_for_signal">Esperando señal de GPS</string>
|
<string name="gps_wait_for_signal">Esperando señal de GPS</string>
|
||||||
<string name="image_arrow">Flecha</string>
|
<string name="image_arrow">Flecha</string>
|
||||||
<string name="image_marker">Marcador</string>
|
|
||||||
<string name="image_marker_count">Recuento de marcadores</string>
|
<string name="image_marker_count">Recuento de marcadores</string>
|
||||||
<string name="image_photo_marker">Marcador de foto</string>
|
<string name="image_photo_marker">Marcador de foto</string>
|
||||||
<string name="image_record">Grabar</string>
|
<string name="image_record">Grabar</string>
|
||||||
@@ -136,8 +135,6 @@
|
|||||||
<string name="menu_settings">Configuración</string>
|
<string name="menu_settings">Configuración</string>
|
||||||
<string name="menu_share">Compartir</string>
|
<string name="menu_share">Compartir</string>
|
||||||
<string name="menu_show_on_map">Mostrar en el mapa</string>
|
<string name="menu_show_on_map">Mostrar en el mapa</string>
|
||||||
<string name="menu_start_gps">Iniciar GPS</string>
|
|
||||||
<string name="menu_stop_gps">Detener GPS</string>
|
|
||||||
<string name="menu_resume_track">Reanudar recorrido</string>
|
<string name="menu_resume_track">Reanudar recorrido</string>
|
||||||
<string name="recording_service">Servicio de grabación</string>
|
<string name="recording_service">Servicio de grabación</string>
|
||||||
<string name="sensor_state_heart_rate">Frecuencia cardíaca</string>
|
<string name="sensor_state_heart_rate">Frecuencia cardíaca</string>
|
||||||
@@ -160,7 +157,6 @@
|
|||||||
<string name="settings_reset_confirm_message">Todos las configuraciones se restablecerán a los valores predeterminados. Esto no eliminará ningún recorrido en el dispositivo.</string>
|
<string name="settings_reset_confirm_message">Todos las configuraciones se restablecerán a los valores predeterminados. Esto no eliminará ningún recorrido en el dispositivo.</string>
|
||||||
<string name="settings_reset_confirm_title">¿Restablecer ahora?</string>
|
<string name="settings_reset_confirm_title">¿Restablecer ahora?</string>
|
||||||
<string name="settings_reset_done">Se restablecieron los valores predeterminados de todas las configuraciones</string>
|
<string name="settings_reset_done">Se restablecieron los valores predeterminados de todas las configuraciones</string>
|
||||||
<string name="settings_sensor_bluetooth">Bluetooth</string>
|
|
||||||
<string name="settings_stats_rate_title">Tasa preferida</string>
|
<string name="settings_stats_rate_title">Tasa preferida</string>
|
||||||
<string name="settings_stats_units_imperial">Imperiales (millas, pies)</string>
|
<string name="settings_stats_units_imperial">Imperiales (millas, pies)</string>
|
||||||
<string name="settings_stats_units_metric">Métricas (km, m)</string>
|
<string name="settings_stats_units_metric">Métricas (km, m)</string>
|
||||||
@@ -331,7 +327,6 @@
|
|||||||
\na) (actualizaciones en vivo) Instale <a href="https://github.com/OpenTracksApp/OSMDashboard"> OSMDashboard </a>. Proporciona mapas en línea o fuera de línea a través de <a href="https://www.openstreetmap.org"> OpenStreetMap </a>.
|
\na) (actualizaciones en vivo) Instale <a href="https://github.com/OpenTracksApp/OSMDashboard"> OSMDashboard </a>. Proporciona mapas en línea o fuera de línea a través de <a href="https://www.openstreetmap.org"> OpenStreetMap </a>.
|
||||||
\nEn realidad, OSMDashboard es una aplicación separada que solo puede acceder (temporalmente) a los datos de los recorridos que se mostrarán.
|
\nEn realidad, OSMDashboard es una aplicación separada que solo puede acceder (temporalmente) a los datos de los recorridos que se mostrarán.
|
||||||
\nb) A través de una aplicación de mapas normal (p. ej., <a href="http://osmand.net/"> OsmAnd </a> y <a href="https://maps.me/"> MAPS.ME </a>). Estas aplicaciones deben admitir el formato de archivo KMZ (solo ubicaciones y marcas de tiempo). De hecho, una aplicación debe admitir <i> ACTION_VIEW </i> con MIME <i> application / vnd.google-earth.kmz</i>. </string>
|
\nb) A través de una aplicación de mapas normal (p. ej., <a href="http://osmand.net/"> OsmAnd </a> y <a href="https://maps.me/"> MAPS.ME </a>). Estas aplicaciones deben admitir el formato de archivo KMZ (solo ubicaciones y marcas de tiempo). De hecho, una aplicación debe admitir <i> ACTION_VIEW </i> con MIME <i> application / vnd.google-earth.kmz</i>. </string>
|
||||||
<string name="delete_service">Borrar servicio</string>
|
|
||||||
<string name="about_third_party_data">Datos de terceros</string>
|
<string name="about_third_party_data">Datos de terceros</string>
|
||||||
<string name="about_libraries">Bibliotecas</string>
|
<string name="about_libraries">Bibliotecas</string>
|
||||||
<string name="help_feature_title">OpenTracks tiene las siguientes características:</string>
|
<string name="help_feature_title">OpenTracks tiene las siguientes características:</string>
|
||||||
@@ -352,8 +347,6 @@
|
|||||||
<string name="help_data_storage_title">¿Cómo almacena OpenTracks los datos de los sensores\?</string>
|
<string name="help_data_storage_title">¿Cómo almacena OpenTracks los datos de los sensores\?</string>
|
||||||
<string name="help_gps_troubleshooting_title">¿OpenTracks no registra ubicaciones\?</string>
|
<string name="help_gps_troubleshooting_title">¿OpenTracks no registra ubicaciones\?</string>
|
||||||
<string name="help_support_title">¿Cómo puedo ayudar a OpenTracks\?</string>
|
<string name="help_support_title">¿Cómo puedo ayudar a OpenTracks\?</string>
|
||||||
<string name="open_track_as_KML">Abrir como KML</string>
|
|
||||||
<string name="open_track_as_GPX">Abrir como GPX</string>
|
|
||||||
<string name="open_track_as_trackfileformat">Abrir como %1$s</string>
|
<string name="open_track_as_trackfileformat">Abrir como %1$s</string>
|
||||||
<string name="activity_type_escooter">moto</string>
|
<string name="activity_type_escooter">moto</string>
|
||||||
<string name="description_speed_ms">Velocidad (m/s)</string>
|
<string name="description_speed_ms">Velocidad (m/s)</string>
|
||||||
@@ -425,11 +418,10 @@
|
|||||||
<string name="generic_filter">Filtrar</string>
|
<string name="generic_filter">Filtrar</string>
|
||||||
<string name="menu_select_layout">Seleccionar diseño</string>
|
<string name="menu_select_layout">Seleccionar diseño</string>
|
||||||
<string name="settings_import_export_title">Importar/Exportar</string>
|
<string name="settings_import_export_title">Importar/Exportar</string>
|
||||||
<string name="settings_announcements_title">Avisos de voz</string>
|
<string name="settings_announcements_statistics_title">Avisos de voz</string>
|
||||||
<string name="settings_announcements_summary">Tiempo, Distancia, Velocidad de la voz</string>
|
<string name="settings_announcements_summary">Tiempo, Distancia, Velocidad de la voz</string>
|
||||||
<string name="settings_import_export_summary">Importar, Exportar, Autoexportar</string>
|
<string name="settings_import_export_summary">Importar, Exportar, Autoexportar</string>
|
||||||
<string name="settings_units_category">Unidades</string>
|
<string name="settings_units_category">Unidades</string>
|
||||||
<string name="settings_recording_idle_speed_title">Umbral de velocidad de ralentí</string>
|
|
||||||
<string name="custom_layout_select_layout">Seleccionar diseño</string>
|
<string name="custom_layout_select_layout">Seleccionar diseño</string>
|
||||||
<string name="stats_custom_layout_default_layout">Diseño predeterminado</string>
|
<string name="stats_custom_layout_default_layout">Diseño predeterminado</string>
|
||||||
<string name="settings_generic_sensor">Genérico</string>
|
<string name="settings_generic_sensor">Genérico</string>
|
||||||
|
|||||||
@@ -126,7 +126,6 @@ limitations under the License.
|
|||||||
<string name="gps_wait_for_signal">Изчаква се сигнал за GPS</string>
|
<string name="gps_wait_for_signal">Изчаква се сигнал за GPS</string>
|
||||||
<!-- Image -->
|
<!-- Image -->
|
||||||
<string name="image_arrow">Стрелка</string>
|
<string name="image_arrow">Стрелка</string>
|
||||||
<string name="image_marker">Маркер</string>
|
|
||||||
<string name="image_marker_count">Брой маркери</string>
|
<string name="image_marker_count">Брой маркери</string>
|
||||||
<string name="image_photo_marker">Маркер за снимки</string>
|
<string name="image_photo_marker">Маркер за снимки</string>
|
||||||
<string name="image_record">Запис</string>
|
<string name="image_record">Запис</string>
|
||||||
@@ -163,8 +162,6 @@ limitations under the License.
|
|||||||
<string name="menu_settings">Настройки</string>
|
<string name="menu_settings">Настройки</string>
|
||||||
<string name="menu_share">Споделяне</string>
|
<string name="menu_share">Споделяне</string>
|
||||||
<string name="menu_show_on_map">Показване на картата</string>
|
<string name="menu_show_on_map">Показване на картата</string>
|
||||||
<string name="menu_start_gps">Стартиране на GPS</string>
|
|
||||||
<string name="menu_stop_gps">Спиране на GPS</string>
|
|
||||||
<!-- OpenTracks -->
|
<!-- OpenTracks -->
|
||||||
<string name="recording_service">Услуга за запис</string>
|
<string name="recording_service">Услуга за запис</string>
|
||||||
<!-- Search -->
|
<!-- Search -->
|
||||||
@@ -193,7 +190,6 @@ limitations under the License.
|
|||||||
<string name="settings_reset_confirm_title">Да се зададе ли повторно сега?</string>
|
<string name="settings_reset_confirm_title">Да се зададе ли повторно сега?</string>
|
||||||
<string name="settings_reset_done">Всички настройки са възстановени до стойностите по подразбиране</string>
|
<string name="settings_reset_done">Всички настройки са възстановени до стойностите по подразбиране</string>
|
||||||
<!-- Settings Sensor -->
|
<!-- Settings Sensor -->
|
||||||
<string name="settings_sensor_bluetooth">Bluetooth</string>
|
|
||||||
<!-- Settings Stats -->
|
<!-- Settings Stats -->
|
||||||
<string name="settings_stats_rate_title">Предпочитана мярка</string>
|
<string name="settings_stats_rate_title">Предпочитана мярка</string>
|
||||||
<string name="settings_stats_units_imperial">Имперски (мил, фт)</string>
|
<string name="settings_stats_units_imperial">Имперски (мил, фт)</string>
|
||||||
@@ -312,7 +308,6 @@ limitations under the License.
|
|||||||
<string name="waypoint_type_water_fountain">чешма</string>
|
<string name="waypoint_type_water_fountain">чешма</string>
|
||||||
<string name="waypoint_type_waypoint">пътна точка</string>
|
<string name="waypoint_type_waypoint">пътна точка</string>
|
||||||
<string name="generic_completed">Завършен</string>
|
<string name="generic_completed">Завършен</string>
|
||||||
<string name="open_track_as_GPX">Отваряне като GPX</string>
|
|
||||||
<string name="sensor_state_heart_rate_max">Максимален пулс</string>
|
<string name="sensor_state_heart_rate_max">Максимален пулс</string>
|
||||||
<string name="help_gps_troubleshooting_title">OpenTracks не записва локации\?</string>
|
<string name="help_gps_troubleshooting_title">OpenTracks не записва локации\?</string>
|
||||||
<string name="track_pace_notification">Темп: %1$s</string>
|
<string name="track_pace_notification">Темп: %1$s</string>
|
||||||
@@ -352,7 +347,6 @@ limitations under the License.
|
|||||||
\n б) Чрез нормално приложение за карти (напр. чрез <a href="http://osmand.net/">OsmAnd</a> и <a href="https://maps.me/">MAPS.ME</a>). Тези приложения трябва да поддържат файловия формат KMZ (само местоположения и времеви марки). Всъщност едно приложение трябва да поддържа <i>ACTION_VIEW</i> с MIME <i>application/vnd.google-earth.kmz</i>. </string>
|
\n б) Чрез нормално приложение за карти (напр. чрез <a href="http://osmand.net/">OsmAnd</a> и <a href="https://maps.me/">MAPS.ME</a>). Тези приложения трябва да поддържат файловия формат KMZ (само местоположения и времеви марки). Всъщност едно приложение трябва да поддържа <i>ACTION_VIEW</i> с MIME <i>application/vnd.google-earth.kmz</i>. </string>
|
||||||
<string name="help_data_storage_title">Как OpenTracks съхранява данните от сензорите\?</string>
|
<string name="help_data_storage_title">Как OpenTracks съхранява данните от сензорите\?</string>
|
||||||
<string name="help_data_storage_content"> Данните от сензора се съхраняват за всяко записано място. По отношение на сензора за сърдечен ритъм това означава, че само <i>едно</i> измерване на сърдечния ритъм се съхранява за всяко местоположение. Ако стоите неподвижно или се намирате на закрито, няма да бъдат записани данни от сензора (но те ще бъдат </string>
|
<string name="help_data_storage_content"> Данните от сензора се съхраняват за всяко записано място. По отношение на сензора за сърдечен ритъм това означава, че само <i>едно</i> измерване на сърдечния ритъм се съхранява за всяко местоположение. Ако стоите неподвижно или се намирате на закрито, няма да бъдат записани данни от сензора (но те ще бъдат </string>
|
||||||
<string name="open_track_as_KML">Отваряне като KML</string>
|
|
||||||
<string name="open_track_as_trackfileformat">Отворете като %1$s</string>
|
<string name="open_track_as_trackfileformat">Отворете като %1$s</string>
|
||||||
<string name="description_altitude_loss">Загуба на височина: %1$d m (%2$d ft)</string>
|
<string name="description_altitude_loss">Загуба на височина: %1$d m (%2$d ft)</string>
|
||||||
<string name="help_recording_content">OpenTracks използва GPS сензора на устройството, за да записва непрекъснато местоположението. Ако GPS устройството съобщи неточни данни (напр. местоположение, скорост, надморска височина), OpenTracks не може да направи нищо по въпроса.</string>
|
<string name="help_recording_content">OpenTracks използва GPS сензора на устройството, за да записва непрекъснато местоположението. Ако GPS устройството съобщи неточни данни (напр. местоположение, скорост, надморска височина), OpenTracks не може да направи нищо по въпроса.</string>
|
||||||
@@ -400,7 +394,6 @@ limitations under the License.
|
|||||||
<string name="permission_gps_failed">OpenTracks изисква разрешение за използване на GPS.</string>
|
<string name="permission_gps_failed">OpenTracks изисква разрешение за използване на GPS.</string>
|
||||||
<string name="sensor_state_power_avg">Средна мощност</string>
|
<string name="sensor_state_power_avg">Средна мощност</string>
|
||||||
<string name="help_feature_title">OpenTracks има следните функции:</string>
|
<string name="help_feature_title">OpenTracks има следните функции:</string>
|
||||||
<string name="delete_service">Услуга за изтриване</string>
|
|
||||||
<string name="settings_night_mode_option_system">Система</string>
|
<string name="settings_night_mode_option_system">Система</string>
|
||||||
<string name="settings_prevent_reimport_tracks_title">Забрана за повторно импортиране на песни</string>
|
<string name="settings_prevent_reimport_tracks_title">Забрана за повторно импортиране на песни</string>
|
||||||
<string name="export_progress_summary_skip_msg">Пропуснати писти</string>
|
<string name="export_progress_summary_skip_msg">Пропуснати писти</string>
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ limitations under the License.
|
|||||||
<string name="activity_type_driving_bus">conducció d\'autobús</string>
|
<string name="activity_type_driving_bus">conducció d\'autobús</string>
|
||||||
<string name="activity_type_driving_car">conducció de cotxe</string>
|
<string name="activity_type_driving_car">conducció de cotxe</string>
|
||||||
<string name="activity_type_ferry">ferri</string>
|
<string name="activity_type_ferry">ferri</string>
|
||||||
<string name="activity_type_frisbee">frisbee</string>
|
<string name="activity_type_frisbee">el frisbee</string>
|
||||||
<string name="activity_type_gliding">ala delta</string>
|
<string name="activity_type_gliding">ala delta</string>
|
||||||
<string name="activity_type_hang_gliding">vol amb ala delta</string>
|
<string name="activity_type_hang_gliding">vol amb ala delta</string>
|
||||||
<string name="activity_type_helicopter">helicòpter</string>
|
<string name="activity_type_helicopter">helicòpter</string>
|
||||||
@@ -75,7 +75,7 @@ limitations under the License.
|
|||||||
<string name="activity_type_trail_hiking">ruta de senderisme</string>
|
<string name="activity_type_trail_hiking">ruta de senderisme</string>
|
||||||
<string name="activity_type_trail_running">cursa de muntanya</string>
|
<string name="activity_type_trail_running">cursa de muntanya</string>
|
||||||
<string name="activity_type_train">tren</string>
|
<string name="activity_type_train">tren</string>
|
||||||
<string name="activity_type_ultimate_frisbee">ultimate frisbee</string>
|
<string name="activity_type_ultimate_frisbee">frisbee definitiu</string>
|
||||||
<string name="activity_type_unknown">desconegut</string>
|
<string name="activity_type_unknown">desconegut</string>
|
||||||
<string name="activity_type_wakeboarding">esquí-surf</string>
|
<string name="activity_type_wakeboarding">esquí-surf</string>
|
||||||
<string name="activity_type_walking">caminar</string>
|
<string name="activity_type_walking">caminar</string>
|
||||||
@@ -121,12 +121,11 @@ limitations under the License.
|
|||||||
<string name="generic_save">Desa</string>
|
<string name="generic_save">Desa</string>
|
||||||
<string name="generic_tracks">Rutes</string>
|
<string name="generic_tracks">Rutes</string>
|
||||||
<!-- Gps -->
|
<!-- Gps -->
|
||||||
<string name="gps_starting">El GPS s\'està iniciant.</string>
|
<string name="gps_starting">S \' està iniciant EL GPS</string>
|
||||||
<string name="gps_wait_for_better_signal">S\'està esperant un senyal GPS de millor qualitat.</string>
|
<string name="gps_wait_for_better_signal">Esperant un millor senyal GPS</string>
|
||||||
<string name="gps_wait_for_signal">S\'està esperant el senyal GPS.</string>
|
<string name="gps_wait_for_signal">Esperant el senyal GPS</string>
|
||||||
<!-- Image -->
|
<!-- Image -->
|
||||||
<string name="image_arrow">Fletxa</string>
|
<string name="image_arrow">Fletxa</string>
|
||||||
<string name="image_marker">Marcador</string>
|
|
||||||
<string name="image_marker_count">Recompte de marcadors</string>
|
<string name="image_marker_count">Recompte de marcadors</string>
|
||||||
<string name="image_photo_marker">Marcador de fotos</string>
|
<string name="image_photo_marker">Marcador de fotos</string>
|
||||||
<string name="image_record">Enregistra</string>
|
<string name="image_record">Enregistra</string>
|
||||||
@@ -136,23 +135,23 @@ limitations under the License.
|
|||||||
<string name="import_progress_message">S\'està important des de %1$s…</string>
|
<string name="import_progress_message">S\'està important des de %1$s…</string>
|
||||||
<!-- Marker -->
|
<!-- Marker -->
|
||||||
<string name="marker_add_error">No es pot inserir un marcador. No hi ha senyal del GPS. Torneu-ho a provar.</string>
|
<string name="marker_add_error">No es pot inserir un marcador. No hi ha senyal del GPS. Torneu-ho a provar.</string>
|
||||||
<string name="marker_add_success">S\'ha inserit un marcador.</string>
|
<string name="marker_add_success">S\'ha inserit un marcador</string>
|
||||||
<string name="marker_add_canceled">L\'acció s\'ha cancel·lat. No s\'ha afegit cap marcador.</string>
|
<string name="marker_add_canceled">Cancelat, no s \' ha afegit cap marcador</string>
|
||||||
<string name="marker_delete_one_confirm_message">El marcador se suprimirà definitivament del dispositiu.</string>
|
<string name="marker_delete_one_confirm_message">El marcador se suprimirà permanentment del dispositiu</string>
|
||||||
<string name="marker_delete_one_confirm_title">Voleu suprimir el marcador?</string>
|
<string name="marker_delete_one_confirm_title">Voleu suprimir el marcador?</string>
|
||||||
<string name="marker_delete_multiple_confirm_message">Els marcadors seleccionats se suprimiran definitivament del dispositiu.</string>
|
<string name="marker_delete_multiple_confirm_message">Els marcadors seleccionats se suprimiran permanentment del dispositiu</string>
|
||||||
<string name="marker_name_format">Marcador %1$d</string>
|
<string name="marker_name_format">Marcador %1$d</string>
|
||||||
<string name="marker_title">%1$d de %2$d</string>
|
<string name="marker_title">%1$d de %2$d</string>
|
||||||
<!-- Marker Detail -->
|
<!-- Marker Detail -->
|
||||||
<!-- Marker Edit -->
|
<!-- Marker Edit -->
|
||||||
<string name="marker_edit_marker_type">Tipus de marcador (p. ex. cascada)</string>
|
<string name="marker_edit_marker_type">Tipus de marcador (p. ex. cascada)</string>
|
||||||
<!-- Marker List -->
|
<!-- Marker List -->
|
||||||
<string name="marker_list_empty_message">No teniu cap marcador.</string>
|
<string name="marker_list_empty_message">No teniu cap marcador</string>
|
||||||
<!-- Menu -->
|
<!-- Menu -->
|
||||||
<string name="menu_aggregated_statistics">Estadístiques globals</string>
|
<string name="menu_aggregated_statistics">Estadístiques globals</string>
|
||||||
<string name="menu_delete">Suprimeix</string>
|
<string name="menu_delete">Suprimeix</string>
|
||||||
<string name="menu_edit">Edita</string>
|
<string name="menu_edit">Edita</string>
|
||||||
<string name="menu_export_all">Exporta-ho tot</string>
|
<string name="menu_export_all">Exporta-ho tot (un fitxer per pista)</string>
|
||||||
<string name="menu_help">ajuda</string>
|
<string name="menu_help">ajuda</string>
|
||||||
<string name="menu_import_all">Importa-ho tot</string>
|
<string name="menu_import_all">Importa-ho tot</string>
|
||||||
<string name="menu_insert_marker">Insereix un marcador</string>
|
<string name="menu_insert_marker">Insereix un marcador</string>
|
||||||
@@ -163,8 +162,6 @@ limitations under the License.
|
|||||||
<string name="menu_settings">Configuració</string>
|
<string name="menu_settings">Configuració</string>
|
||||||
<string name="menu_share">Comparteix</string>
|
<string name="menu_share">Comparteix</string>
|
||||||
<string name="menu_show_on_map">Mostra-ho al mapa</string>
|
<string name="menu_show_on_map">Mostra-ho al mapa</string>
|
||||||
<string name="menu_start_gps">Activa el GPS</string>
|
|
||||||
<string name="menu_stop_gps">Desactiva el GPS</string>
|
|
||||||
<!-- OpenTracks -->
|
<!-- OpenTracks -->
|
||||||
<string name="recording_service">Servei d\'enregistrament</string>
|
<string name="recording_service">Servei d\'enregistrament</string>
|
||||||
<!-- Search -->
|
<!-- Search -->
|
||||||
@@ -179,7 +176,7 @@ limitations under the License.
|
|||||||
<string name="settings_chart_by_time">Per temps</string>
|
<string name="settings_chart_by_time">Per temps</string>
|
||||||
<string name="settings_recording_default_activity_title">Activitat predeterminada</string>
|
<string name="settings_recording_default_activity_title">Activitat predeterminada</string>
|
||||||
<string name="settings_recording_location_frequency_summary">%1$s entre ubicacions registrades</string>
|
<string name="settings_recording_location_frequency_summary">%1$s entre ubicacions registrades</string>
|
||||||
<string name="settings_recording_max_recording_distance_summary">Es crea un tram si la distància entre dues ubicacions és superior a %1$s.</string>
|
<string name="settings_recording_max_recording_distance_summary">Es crea un segment nou si la distància entre dues ubicacions és més gran que %1$s</string>
|
||||||
<string name="settings_recording_max_recording_distance_title">Distància màxima</string>
|
<string name="settings_recording_max_recording_distance_title">Distància màxima</string>
|
||||||
<string name="settings_recording_min_recording_distance_title">Interval de distàncies</string>
|
<string name="settings_recording_min_recording_distance_title">Interval de distàncies</string>
|
||||||
<string name="settings_recording_min_recording_interval_title">Interval de temps</string>
|
<string name="settings_recording_min_recording_interval_title">Interval de temps</string>
|
||||||
@@ -191,16 +188,17 @@ limitations under the License.
|
|||||||
<!-- Settings Reset -->
|
<!-- Settings Reset -->
|
||||||
<string name="settings_reset_confirm_message">Es tornaran a definir els valors predeterminats de la configuració.</string>
|
<string name="settings_reset_confirm_message">Es tornaran a definir els valors predeterminats de la configuració.</string>
|
||||||
<string name="settings_reset_confirm_title">Voleu restablir-ho ara?</string>
|
<string name="settings_reset_confirm_title">Voleu restablir-ho ara?</string>
|
||||||
<string name="settings_reset_done">S\'ha recuperat la configuració predeterminada.</string>
|
<string name="settings_reset_done">Tots els paràmetres s\'han revertit als valors predeterminats</string>
|
||||||
<!-- Settings Sensor -->
|
<!-- Settings Sensor -->
|
||||||
<string name="settings_sensor_bluetooth">Bluetooth</string>
|
|
||||||
<!-- Settings Stats -->
|
<!-- Settings Stats -->
|
||||||
<string name="settings_stats_rate_title">Mesura preferida</string>
|
<string name="settings_stats_rate_title">Mesura preferida</string>
|
||||||
<string name="settings_stats_units_imperial">Sistema anglosaxó (milles, peus)</string>
|
<string name="settings_stats_units_imperial">Sistema anglosaxó (milles, peus)</string>
|
||||||
<string name="settings_stats_units_metric">Sistema mètric (km, m)</string>
|
<string name="settings_stats_units_metric">Sistema mètric (km, m)</string>
|
||||||
<string name="settings_stats_units_title">Unitats preferides</string>
|
<string name="settings_stats_units_title">Unitats preferides</string>
|
||||||
<!-- Share Track -->
|
<!-- Share Track -->
|
||||||
<string name="share_track_share_file_body">Crec que aquesta ruta et podria interessar.\n\nLa pots veure amb Google Earth o qualsevol altre programari de mapatge.\n\n%1$s</string>
|
<string name="share_track_share_file_body">Crec que aquesta ruta et podria interessar.
|
||||||
|
\n
|
||||||
|
\n%1$s</string>
|
||||||
<string name="share_track_subject">Vull compartir una ruta amb tu</string>
|
<string name="share_track_subject">Vull compartir una ruta amb tu</string>
|
||||||
<!-- Stats -->
|
<!-- Stats -->
|
||||||
<string name="stats_average_moving_pace">Ritme de moviment mitjà</string>
|
<string name="stats_average_moving_pace">Ritme de moviment mitjà</string>
|
||||||
@@ -232,7 +230,7 @@ limitations under the License.
|
|||||||
<string name="track_edit_activity_type">Tipus d\'activitat (p. ex. senderisme)</string>
|
<string name="track_edit_activity_type">Tipus d\'activitat (p. ex. senderisme)</string>
|
||||||
<string name="track_edit_activity_type_hint">Tipus d\'activitat</string>
|
<string name="track_edit_activity_type_hint">Tipus d\'activitat</string>
|
||||||
<!-- Track List -->
|
<!-- Track List -->
|
||||||
<string name="track_list_empty_message">Comenceu a enregistrar aquí la vostra pròxima aventura.</string>
|
<string name="track_list_empty_message">Comença a gravar la teva propera aventura aquí</string>
|
||||||
<!-- Track Widget -->
|
<!-- Track Widget -->
|
||||||
<!-- Tracks -->
|
<!-- Tracks -->
|
||||||
<!-- Unit -->
|
<!-- Unit -->
|
||||||
@@ -312,4 +310,18 @@ limitations under the License.
|
|||||||
<!-- Share image -->
|
<!-- Share image -->
|
||||||
<string name="share_image_subject">Vull compartir una imatge amb tu</string>
|
<string name="share_image_subject">Vull compartir una imatge amb tu</string>
|
||||||
<string name="share_image_body">Crec que aquesta imatge et podria interessar.</string>
|
<string name="share_image_body">Crec que aquesta imatge et podria interessar.</string>
|
||||||
|
<string name="about_libraries">Biblioteques</string>
|
||||||
|
<string name="help_feature_content">* crear pistes GPS
|
||||||
|
\n * utilitzeu un sensor de freqüència cardíaca (Només Bluetooth le)
|
||||||
|
\n * anuncis de veu (per temps o distància)
|
||||||
|
\n * creeu marcadors (incl. imatges)
|
||||||
|
\n * funciona completament fora de línia
|
||||||
|
\n * mostra les pistes en un mapa (vegeu més avall)</string>
|
||||||
|
<string name="about_version_name">Nom de la versió: %1$s</string>
|
||||||
|
<string name="about_preference_title">Sobre OpenTracks</string>
|
||||||
|
<string name="help_track_export_title">Com puc exportar/compartir pistes gravades\?</string>
|
||||||
|
<string name="help_feature_title">OpenTracks té les següents característiques:</string>
|
||||||
|
<string name="help_track_map_title">Com mostrar una pista en un mapa\?</string>
|
||||||
|
<string name="about_version_code">Codi de versió: %1$d</string>
|
||||||
|
<string name="about_third_party_data">Dades de tercers</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -175,7 +175,6 @@ limitations under the License.
|
|||||||
<string name="gps_wait_for_signal">Čekání na signál GPS</string>
|
<string name="gps_wait_for_signal">Čekání na signál GPS</string>
|
||||||
<!-- Image -->
|
<!-- Image -->
|
||||||
<string name="image_arrow">Šipka</string>
|
<string name="image_arrow">Šipka</string>
|
||||||
<string name="image_marker">Značka</string>
|
|
||||||
<string name="image_marker_count">Počet značek</string>
|
<string name="image_marker_count">Počet značek</string>
|
||||||
<string name="image_photo_marker">Fotografická značka</string>
|
<string name="image_photo_marker">Fotografická značka</string>
|
||||||
<string name="image_record">Zaznamenat</string>
|
<string name="image_record">Zaznamenat</string>
|
||||||
@@ -214,8 +213,6 @@ limitations under the License.
|
|||||||
<string name="menu_settings">Nastavení</string>
|
<string name="menu_settings">Nastavení</string>
|
||||||
<string name="menu_share">Sdílet</string>
|
<string name="menu_share">Sdílet</string>
|
||||||
<string name="menu_show_on_map">Zobrazit na mapě</string>
|
<string name="menu_show_on_map">Zobrazit na mapě</string>
|
||||||
<string name="menu_start_gps">Spustit GPS</string>
|
|
||||||
<string name="menu_stop_gps">Ukončit GPS</string>
|
|
||||||
<string name="menu_resume_track">Obnovit měření</string>
|
<string name="menu_resume_track">Obnovit měření</string>
|
||||||
<!-- OpenTracks -->
|
<!-- OpenTracks -->
|
||||||
<string name="recording_service">Služba pro záznam</string>
|
<string name="recording_service">Služba pro záznam</string>
|
||||||
@@ -225,7 +222,7 @@ limitations under the License.
|
|||||||
<!-- Sensor State -->
|
<!-- Sensor State -->
|
||||||
<string name="sensor_state_heart_rate">Srdeční frekvence</string>
|
<string name="sensor_state_heart_rate">Srdeční frekvence</string>
|
||||||
<string name="sensor_state_heart_rate_value">%1$d úderů/min</string>
|
<string name="sensor_state_heart_rate_value">%1$d úderů/min</string>
|
||||||
<string name="sensor_state_power">Napájení</string>
|
<string name="sensor_state_power">Výkon</string>
|
||||||
<string name="sensor_could_not_scan">Nelze vyhledat zařízení Bluetooth (chyba %1$i).</string>
|
<string name="sensor_could_not_scan">Nelze vyhledat zařízení Bluetooth (chyba %1$i).</string>
|
||||||
<string name="bluetooth_disabled">Aktivujte prosím Bluetooth.</string>
|
<string name="bluetooth_disabled">Aktivujte prosím Bluetooth.</string>
|
||||||
<!-- Settings Advanced -->
|
<!-- Settings Advanced -->
|
||||||
@@ -254,7 +251,6 @@ limitations under the License.
|
|||||||
<string name="settings_reset_confirm_title">Resetovat nyní?</string>
|
<string name="settings_reset_confirm_title">Resetovat nyní?</string>
|
||||||
<string name="settings_reset_done">Všechna nastavení byla obnovena na výchozí hodnoty</string>
|
<string name="settings_reset_done">Všechna nastavení byla obnovena na výchozí hodnoty</string>
|
||||||
<!-- Settings Sensor -->
|
<!-- Settings Sensor -->
|
||||||
<string name="settings_sensor_bluetooth">Bluetooth</string>
|
|
||||||
<!-- Settings Stats -->
|
<!-- Settings Stats -->
|
||||||
<string name="settings_stats_rate_title">Preferovaná rychlost</string>
|
<string name="settings_stats_rate_title">Preferovaná rychlost</string>
|
||||||
<string name="settings_stats_units_imperial">Imperiální jednotky (míle, stopy)</string>
|
<string name="settings_stats_units_imperial">Imperiální jednotky (míle, stopy)</string>
|
||||||
@@ -376,8 +372,6 @@ limitations under the License.
|
|||||||
<!-- Share image -->
|
<!-- Share image -->
|
||||||
<string name="share_image_subject">Chtěl bych s vámi sdílet obrázek</string>
|
<string name="share_image_subject">Chtěl bych s vámi sdílet obrázek</string>
|
||||||
<string name="share_image_body">Myslím, že by vás mohl tento obrázek zajímat.</string>
|
<string name="share_image_body">Myslím, že by vás mohl tento obrázek zajímat.</string>
|
||||||
<string name="open_track_as_KML">Otevřít jako KML</string>
|
|
||||||
<string name="open_track_as_GPX">Otevřít jako GPX</string>
|
|
||||||
<string name="open_track_as_trackfileformat">Otevřít jako %1$s</string>
|
<string name="open_track_as_trackfileformat">Otevřít jako %1$s</string>
|
||||||
<string name="export_all_do_it_for_all">provést pro všechny konflikty</string>
|
<string name="export_all_do_it_for_all">provést pro všechny konflikty</string>
|
||||||
<string name="export_track_errors">Soubory nebyly exportovány</string>
|
<string name="export_track_errors">Soubory nebyly exportovány</string>
|
||||||
@@ -433,7 +427,7 @@ limitations under the License.
|
|||||||
<string name="import_progress_summary_exists_msg">Existovat</string>
|
<string name="import_progress_summary_exists_msg">Existovat</string>
|
||||||
<string name="import_progress_summary_ok_msg">Importované soubory</string>
|
<string name="import_progress_summary_ok_msg">Importované soubory</string>
|
||||||
<string name="generic_open_track">Otevřená trať</string>
|
<string name="generic_open_track">Otevřená trať</string>
|
||||||
<string name="sensor_state_power_avg">Výkon Avg</string>
|
<string name="sensor_state_power_avg">Prům. výkon</string>
|
||||||
<string name="sensor_state_heart_rate_max">Maximální srdeční frekvence</string>
|
<string name="sensor_state_heart_rate_max">Maximální srdeční frekvence</string>
|
||||||
<string name="sensor_state_heart_rate_avg">Avg srdeční frekvence</string>
|
<string name="sensor_state_heart_rate_avg">Avg srdeční frekvence</string>
|
||||||
<string name="sensor_state_cadence_max">Max kadence</string>
|
<string name="sensor_state_cadence_max">Max kadence</string>
|
||||||
@@ -442,7 +436,6 @@ limitations under the License.
|
|||||||
<string name="track_speed_notification">Rychlost: %1$s</string>
|
<string name="track_speed_notification">Rychlost: %1$s</string>
|
||||||
<string name="track_pace_notification">Tempo: %1$s</string>
|
<string name="track_pace_notification">Tempo: %1$s</string>
|
||||||
<string name="about_third_party_data">Údaje třetích stran</string>
|
<string name="about_third_party_data">Údaje třetích stran</string>
|
||||||
<string name="delete_service">Služba mazání</string>
|
|
||||||
<string name="menu_voice_rate">Rychlost hlasových oznámení</string>
|
<string name="menu_voice_rate">Rychlost hlasových oznámení</string>
|
||||||
<string name="track_delete_not_recording">Není možno vymazat aktuálně zaznamenávanou trasu.</string>
|
<string name="track_delete_not_recording">Není možno vymazat aktuálně zaznamenávanou trasu.</string>
|
||||||
<string name="settings_sensor_cycling_distance_speed">Vzdálenost a rychlost</string>
|
<string name="settings_sensor_cycling_distance_speed">Vzdálenost a rychlost</string>
|
||||||
@@ -477,7 +470,6 @@ limitations under the License.
|
|||||||
<string name="permission_bluetooth_failed_rejected">V nastavení systému je třeba ručně povolit aplikaci OpenTracks přístup k blízkým zařízením.</string>
|
<string name="permission_bluetooth_failed_rejected">V nastavení systému je třeba ručně povolit aplikaci OpenTracks přístup k blízkým zařízením.</string>
|
||||||
<string name="value_float_mile_hour">%1$.1f mi/h</string>
|
<string name="value_float_mile_hour">%1$.1f mi/h</string>
|
||||||
<string name="value_float_mile_hour_recommended">%1$.1f mil/h (doporučeno)</string>
|
<string name="value_float_mile_hour_recommended">%1$.1f mil/h (doporučeno)</string>
|
||||||
<string name="settings_recording_idle_speed_title">Rychlost pro autopausu</string>
|
|
||||||
<string name="value_float_kilometer_hour">%1$.1f km/h</string>
|
<string name="value_float_kilometer_hour">%1$.1f km/h</string>
|
||||||
<string name="value_float_kilometer_hour_recommended">%1$.1f km/h (doporučeno)</string>
|
<string name="value_float_kilometer_hour_recommended">%1$.1f km/h (doporučeno)</string>
|
||||||
<string name="stats_clock">Hodiny</string>
|
<string name="stats_clock">Hodiny</string>
|
||||||
@@ -489,7 +481,7 @@ limitations under the License.
|
|||||||
<string name="settings_import_export_summary">Import, export, automatický export</string>
|
<string name="settings_import_export_summary">Import, export, automatický export</string>
|
||||||
<string name="settings_import_export_title">Import/Export</string>
|
<string name="settings_import_export_title">Import/Export</string>
|
||||||
<string name="settings_announcements_summary">Čas, vzdálenost, rychlost hlasu</string>
|
<string name="settings_announcements_summary">Čas, vzdálenost, rychlost hlasu</string>
|
||||||
<string name="settings_announcements_title">Hlasová oznámení</string>
|
<string name="settings_announcements_statistics_title">Hlasová oznámení</string>
|
||||||
<string name="settings_sensors_title">Bluetooth senzory</string>
|
<string name="settings_sensors_title">Bluetooth senzory</string>
|
||||||
<string name="settings_gps_summary">Časový interval, Interval vzdálenosti, Maximální vzdálenost, Přesnost</string>
|
<string name="settings_gps_summary">Časový interval, Interval vzdálenosti, Maximální vzdálenost, Přesnost</string>
|
||||||
<string name="settings_ui_summary">Vzhled, Rozložení záznamu, Téma</string>
|
<string name="settings_ui_summary">Vzhled, Rozložení záznamu, Téma</string>
|
||||||
@@ -591,4 +583,11 @@ limitations under the License.
|
|||||||
<string name="voiceSpeedKilometersPerHourPlural">{n, plural, =1 {1 kilometr za hodinu} few {{n,number,#.0} kilometry za hodinu} other {{n,number,#.0} kilometrů za hodinu} }</string>
|
<string name="voiceSpeedKilometersPerHourPlural">{n, plural, =1 {1 kilometr za hodinu} few {{n,number,#.0} kilometry za hodinu} other {{n,number,#.0} kilometrů za hodinu} }</string>
|
||||||
<string name="voiceSpeedMilesPerHourPlural">{n, plural, =1 {1 míle za hodinu} few {{n,number,#.0} míle za hodinu} other {{n,number,#.0} mil za hodinu} }</string>
|
<string name="voiceSpeedMilesPerHourPlural">{n, plural, =1 {1 míle za hodinu} few {{n,number,#.0} míle za hodinu} other {{n,number,#.0} mil za hodinu} }</string>
|
||||||
<string name="voiceDistanceKilometersPlural">{n, plural, =1 {1 kilometr} few {{n,number,#.0} kilometry} other {{n,number,#.0} kilometrů} }</string>
|
<string name="voiceDistanceKilometersPlural">{n, plural, =1 {1 kilometr} few {{n,number,#.0} kilometry} other {{n,number,#.0} kilometrů} }</string>
|
||||||
|
<string name="sensor_state_power_max">Maximální výkon</string>
|
||||||
|
<string name="settings_announcements_idle_category_title">Oznámení o nečinnosti</string>
|
||||||
|
<string name="activity_type_kickscooter">skútr</string>
|
||||||
|
<string name="voiceIdle">Vstupujete do režimu nečinnosti.</string>
|
||||||
|
<string name="settings_announcements_idle_title">Nečinnost</string>
|
||||||
|
<string name="settings_recording_idle_timeout_title">Hranice nečinnosti</string>
|
||||||
|
<string name="value_int_seconds">%1$d s (doporučeno)</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -63,8 +63,6 @@ Hvis GPS-enheden rapporterer unøjagtige data (f.eks. Placering, hastighed, høj
|
|||||||
\n e) køb i <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore"> Google Play Store </a>,
|
\n e) køb i <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore"> Google Play Store </a>,
|
||||||
\n f) donere, og
|
\n f) donere, og
|
||||||
\n g) gør hvad du synes hjælper OpenTracks. </string>
|
\n g) gør hvad du synes hjælper OpenTracks. </string>
|
||||||
<string name="open_track_as_KML">Åben som KML</string>
|
|
||||||
<string name="open_track_as_GPX">Åben som GPX</string>
|
|
||||||
<!-- Activity Type -->
|
<!-- Activity Type -->
|
||||||
<string name="activity_type_airplane">fly</string>
|
<string name="activity_type_airplane">fly</string>
|
||||||
<string name="activity_type_atv">Terrængående køretøj</string>
|
<string name="activity_type_atv">Terrængående køretøj</string>
|
||||||
@@ -180,7 +178,6 @@ Hvis GPS-enheden rapporterer unøjagtige data (f.eks. Placering, hastighed, høj
|
|||||||
<string name="gps_wait_for_signal">Venter på GPS-signal</string>
|
<string name="gps_wait_for_signal">Venter på GPS-signal</string>
|
||||||
<!-- Image -->
|
<!-- Image -->
|
||||||
<string name="image_arrow">Pil</string>
|
<string name="image_arrow">Pil</string>
|
||||||
<string name="image_marker">Markør</string>
|
|
||||||
<string name="image_marker_count">Antal markører</string>
|
<string name="image_marker_count">Antal markører</string>
|
||||||
<string name="image_photo_marker">Billedmarkør</string>
|
<string name="image_photo_marker">Billedmarkør</string>
|
||||||
<string name="image_record">Registrer</string>
|
<string name="image_record">Registrer</string>
|
||||||
@@ -219,8 +216,6 @@ Hvis GPS-enheden rapporterer unøjagtige data (f.eks. Placering, hastighed, høj
|
|||||||
<string name="menu_settings">Indstillinger</string>
|
<string name="menu_settings">Indstillinger</string>
|
||||||
<string name="menu_share">Del</string>
|
<string name="menu_share">Del</string>
|
||||||
<string name="menu_show_on_map">Vis på kort</string>
|
<string name="menu_show_on_map">Vis på kort</string>
|
||||||
<string name="menu_start_gps">Start GPS</string>
|
|
||||||
<string name="menu_stop_gps">Stop GPS</string>
|
|
||||||
<string name="menu_resume_track">Genoptag sporet</string>
|
<string name="menu_resume_track">Genoptag sporet</string>
|
||||||
<!-- OpenTracks -->
|
<!-- OpenTracks -->
|
||||||
<string name="recording_service">Registreringstjeneste</string>
|
<string name="recording_service">Registreringstjeneste</string>
|
||||||
@@ -259,7 +254,6 @@ Hvis GPS-enheden rapporterer unøjagtige data (f.eks. Placering, hastighed, høj
|
|||||||
<string name="settings_reset_confirm_title">Vil du nulstille nu?</string>
|
<string name="settings_reset_confirm_title">Vil du nulstille nu?</string>
|
||||||
<string name="settings_reset_done">Alle indstillinger er blevet gendannet til standardværdierne</string>
|
<string name="settings_reset_done">Alle indstillinger er blevet gendannet til standardværdierne</string>
|
||||||
<!-- Settings Sensor -->
|
<!-- Settings Sensor -->
|
||||||
<string name="settings_sensor_bluetooth">Bluetooth</string>
|
|
||||||
<!-- Settings Stats -->
|
<!-- Settings Stats -->
|
||||||
<string name="settings_stats_rate_title">Foretrukkenfartenhed</string>
|
<string name="settings_stats_rate_title">Foretrukkenfartenhed</string>
|
||||||
<string name="settings_stats_units_imperial">Britisk (mil, ft)</string>
|
<string name="settings_stats_units_imperial">Britisk (mil, ft)</string>
|
||||||
@@ -444,7 +438,6 @@ Hvis GPS-enheden rapporterer unøjagtige data (f.eks. Placering, hastighed, høj
|
|||||||
<string name="track_distance_notification">Afstand: %1$s</string>
|
<string name="track_distance_notification">Afstand: %1$s</string>
|
||||||
<string name="track_speed_notification">Hastighed: %1$s</string>
|
<string name="track_speed_notification">Hastighed: %1$s</string>
|
||||||
<string name="about_third_party_data">Data fra tredjeparter</string>
|
<string name="about_third_party_data">Data fra tredjeparter</string>
|
||||||
<string name="delete_service">Tjeneste til sletning</string>
|
|
||||||
<string name="menu_voice_rate">Stemmehastighed</string>
|
<string name="menu_voice_rate">Stemmehastighed</string>
|
||||||
<string name="track_delete_not_recording">Dette spor kan ikke slettes, da det er optaget i øjeblikket.</string>
|
<string name="track_delete_not_recording">Dette spor kan ikke slettes, da det er optaget i øjeblikket.</string>
|
||||||
<string name="settings_sensor_cycling_distance_speed">Afstand og hastighed</string>
|
<string name="settings_sensor_cycling_distance_speed">Afstand og hastighed</string>
|
||||||
@@ -458,7 +451,7 @@ Hvis GPS-enheden rapporterer unøjagtige data (f.eks. Placering, hastighed, høj
|
|||||||
<string name="settings_defaults_summary">Foretrukne enheder, standardaktivitet, spor standardnavn</string>
|
<string name="settings_defaults_summary">Foretrukne enheder, standardaktivitet, spor standardnavn</string>
|
||||||
<string name="settings_gps_summary">Tidsinterval, Distanceinterval, Maksimal afstand, Nøjagtighed</string>
|
<string name="settings_gps_summary">Tidsinterval, Distanceinterval, Maksimal afstand, Nøjagtighed</string>
|
||||||
<string name="settings_sensors_summary">Cykelsensorer, Hjulomkreds, Løbesensorer</string>
|
<string name="settings_sensors_summary">Cykelsensorer, Hjulomkreds, Løbesensorer</string>
|
||||||
<string name="settings_announcements_title">Stemmemeddelelser</string>
|
<string name="settings_announcements_statistics_title">Stemmemeddelelser</string>
|
||||||
<string name="settings_announcements_summary">Tid, afstand, stemmehastighed</string>
|
<string name="settings_announcements_summary">Tid, afstand, stemmehastighed</string>
|
||||||
<string name="settings_import_export_title">Import/eksport</string>
|
<string name="settings_import_export_title">Import/eksport</string>
|
||||||
<string name="settings_import_export_summary">Import, eksport, automatisk eksport</string>
|
<string name="settings_import_export_summary">Import, eksport, automatisk eksport</string>
|
||||||
@@ -487,7 +480,6 @@ Hvis GPS-enheden rapporterer unøjagtige data (f.eks. Placering, hastighed, høj
|
|||||||
<string name="settings_import_section">Importere</string>
|
<string name="settings_import_section">Importere</string>
|
||||||
<string name="settings_export_section">Eksport</string>
|
<string name="settings_export_section">Eksport</string>
|
||||||
<string name="stats_clock">Klokken</string>
|
<string name="stats_clock">Klokken</string>
|
||||||
<string name="settings_recording_idle_speed_title">Tærskel for tomgangshastighed</string>
|
|
||||||
<string name="value_float_mile_hour_recommended">%1$.1f mi/h (anbefalet)</string>
|
<string name="value_float_mile_hour_recommended">%1$.1f mi/h (anbefalet)</string>
|
||||||
<string name="value_float_mile_hour">%1$.1f mi/h</string>
|
<string name="value_float_mile_hour">%1$.1f mi/h</string>
|
||||||
<string name="generic_to">Til</string>
|
<string name="generic_to">Til</string>
|
||||||
|
|||||||
@@ -63,8 +63,6 @@ Meldet das GPS-Gerät ungenaue Daten (z.B. Standort, Geschwindigkeit, Höhe), ka
|
|||||||
\n e) kaufen in <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Google Play Store</a>,
|
\n e) kaufen in <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Google Play Store</a>,
|
||||||
\n f) spenden, und
|
\n f) spenden, und
|
||||||
\n g) das tun, was Deiner Meinung nach OpenTracks hilft </string>
|
\n g) das tun, was Deiner Meinung nach OpenTracks hilft </string>
|
||||||
<string name="open_track_as_KML">Als KML öffnen</string>
|
|
||||||
<string name="open_track_as_GPX">Als GPX öffnen</string>
|
|
||||||
<string name="open_track_as_trackfileformat">Als %1$s öffnen</string>
|
<string name="open_track_as_trackfileformat">Als %1$s öffnen</string>
|
||||||
<!-- Activity Type -->
|
<!-- Activity Type -->
|
||||||
<string name="activity_type_airplane">Flugzeug</string>
|
<string name="activity_type_airplane">Flugzeug</string>
|
||||||
@@ -181,7 +179,6 @@ Meldet das GPS-Gerät ungenaue Daten (z.B. Standort, Geschwindigkeit, Höhe), ka
|
|||||||
<string name="gps_wait_for_signal">Warten auf GPS-Signal</string>
|
<string name="gps_wait_for_signal">Warten auf GPS-Signal</string>
|
||||||
<!-- Image -->
|
<!-- Image -->
|
||||||
<string name="image_arrow">Pfeil</string>
|
<string name="image_arrow">Pfeil</string>
|
||||||
<string name="image_marker">Markierung</string>
|
|
||||||
<string name="image_marker_count">Anzahl der Markierungen</string>
|
<string name="image_marker_count">Anzahl der Markierungen</string>
|
||||||
<string name="image_photo_marker">Fotomarkierung</string>
|
<string name="image_photo_marker">Fotomarkierung</string>
|
||||||
<string name="image_record">Aufzeichnen</string>
|
<string name="image_record">Aufzeichnen</string>
|
||||||
@@ -220,8 +217,6 @@ Meldet das GPS-Gerät ungenaue Daten (z.B. Standort, Geschwindigkeit, Höhe), ka
|
|||||||
<string name="menu_settings">Einstellungen</string>
|
<string name="menu_settings">Einstellungen</string>
|
||||||
<string name="menu_share">Teilen</string>
|
<string name="menu_share">Teilen</string>
|
||||||
<string name="menu_show_on_map">Auf Karte anzeigen</string>
|
<string name="menu_show_on_map">Auf Karte anzeigen</string>
|
||||||
<string name="menu_start_gps">GPS aktivieren</string>
|
|
||||||
<string name="menu_stop_gps">GPS deaktivieren</string>
|
|
||||||
<string name="menu_resume_track">Track fortsetzen</string>
|
<string name="menu_resume_track">Track fortsetzen</string>
|
||||||
<!-- OpenTracks -->
|
<!-- OpenTracks -->
|
||||||
<string name="recording_service">Dienst für Aufzeichnung</string>
|
<string name="recording_service">Dienst für Aufzeichnung</string>
|
||||||
@@ -263,7 +258,6 @@ Meldet das GPS-Gerät ungenaue Daten (z.B. Standort, Geschwindigkeit, Höhe), ka
|
|||||||
<string name="settings_reset_confirm_title">Jetzt zurücksetzen?</string>
|
<string name="settings_reset_confirm_title">Jetzt zurücksetzen?</string>
|
||||||
<string name="settings_reset_done">Alle Einstellungen wurden auf ihre Standardwerte zurückgesetzt</string>
|
<string name="settings_reset_done">Alle Einstellungen wurden auf ihre Standardwerte zurückgesetzt</string>
|
||||||
<!-- Settings Sensor -->
|
<!-- Settings Sensor -->
|
||||||
<string name="settings_sensor_bluetooth">Bluetooth</string>
|
|
||||||
<!-- Settings Stats -->
|
<!-- Settings Stats -->
|
||||||
<string name="settings_stats_rate_title">Bevorzugtes Maß</string>
|
<string name="settings_stats_rate_title">Bevorzugtes Maß</string>
|
||||||
<string name="settings_stats_units_imperial">Imperiales Maß (mi, ft)</string>
|
<string name="settings_stats_units_imperial">Imperiales Maß (mi, ft)</string>
|
||||||
@@ -480,7 +474,6 @@ Meldet das GPS-Gerät ungenaue Daten (z.B. Standort, Geschwindigkeit, Höhe), ka
|
|||||||
<string name="import_no_kml_file_found">Keine doc.kml Datei im KMZ gefunden</string>
|
<string name="import_no_kml_file_found">Keine doc.kml Datei im KMZ gefunden</string>
|
||||||
<string name="import_thread_interrupted">Thread unterbrochen</string>
|
<string name="import_thread_interrupted">Thread unterbrochen</string>
|
||||||
<string name="import_unable_to_import_file">Importieren der Datei gescheitert: %1$s</string>
|
<string name="import_unable_to_import_file">Importieren der Datei gescheitert: %1$s</string>
|
||||||
<string name="delete_service">Löschdienst</string>
|
|
||||||
<string name="settings_sensor_cycling_cadence">Trittfrequenz</string>
|
<string name="settings_sensor_cycling_cadence">Trittfrequenz</string>
|
||||||
<string name="settings_sensor_cycling_distance_speed">Entfernung und Geschwindigkeit</string>
|
<string name="settings_sensor_cycling_distance_speed">Entfernung und Geschwindigkeit</string>
|
||||||
<string name="generic_filter">Filter</string>
|
<string name="generic_filter">Filter</string>
|
||||||
@@ -496,12 +489,11 @@ Meldet das GPS-Gerät ungenaue Daten (z.B. Standort, Geschwindigkeit, Höhe), ka
|
|||||||
<string name="description_speed_source_gps">GPS</string>
|
<string name="description_speed_source_gps">GPS</string>
|
||||||
<string name="menu_select_layout">Wähle Layout</string>
|
<string name="menu_select_layout">Wähle Layout</string>
|
||||||
<string name="generic_choose_an_option">Wähle eine Option</string>
|
<string name="generic_choose_an_option">Wähle eine Option</string>
|
||||||
<string name="settings_recording_idle_speed_title">Inaktivitätsgeschwindigkeit</string>
|
|
||||||
<string name="settings_gps_title">GPS</string>
|
<string name="settings_gps_title">GPS</string>
|
||||||
<string name="settings_import_export_title">Import/Export</string>
|
<string name="settings_import_export_title">Import/Export</string>
|
||||||
<string name="settings_sensors_title">Bluetooth Sensoren</string>
|
<string name="settings_sensors_title">Bluetooth Sensoren</string>
|
||||||
<string name="settings_sensors_summary">Fahrradsensoren, Radumfang und Lauf-Sensoren</string>
|
<string name="settings_sensors_summary">Fahrradsensoren, Radumfang und Lauf-Sensoren</string>
|
||||||
<string name="settings_announcements_title">Sprachansagen</string>
|
<string name="settings_announcements_statistics_title">Sprachansagen</string>
|
||||||
<string name="generic_undo">Rückgängig</string>
|
<string name="generic_undo">Rückgängig</string>
|
||||||
<string name="generic_today">Heute</string>
|
<string name="generic_today">Heute</string>
|
||||||
<string name="generic_yesterday">Gestern</string>
|
<string name="generic_yesterday">Gestern</string>
|
||||||
|
|||||||
@@ -126,7 +126,6 @@ limitations under the License.
|
|||||||
<string name="gps_wait_for_signal">Αναμονή για σήμα GPS</string>
|
<string name="gps_wait_for_signal">Αναμονή για σήμα GPS</string>
|
||||||
<!-- Image -->
|
<!-- Image -->
|
||||||
<string name="image_arrow">Βέλος</string>
|
<string name="image_arrow">Βέλος</string>
|
||||||
<string name="image_marker">Δείκτης</string>
|
|
||||||
<string name="image_marker_count">Αριθμός δείκτη</string>
|
<string name="image_marker_count">Αριθμός δείκτη</string>
|
||||||
<string name="image_photo_marker">Δείκτης φωτογραφίας</string>
|
<string name="image_photo_marker">Δείκτης φωτογραφίας</string>
|
||||||
<string name="image_record">Εγγραφή</string>
|
<string name="image_record">Εγγραφή</string>
|
||||||
@@ -163,8 +162,6 @@ limitations under the License.
|
|||||||
<string name="menu_settings">Ρυθμίσεις</string>
|
<string name="menu_settings">Ρυθμίσεις</string>
|
||||||
<string name="menu_share">Κοινή χρήση</string>
|
<string name="menu_share">Κοινή χρήση</string>
|
||||||
<string name="menu_show_on_map">Εμφάνιση στον χάρτη</string>
|
<string name="menu_show_on_map">Εμφάνιση στον χάρτη</string>
|
||||||
<string name="menu_start_gps">Έναρξη GPS</string>
|
|
||||||
<string name="menu_stop_gps">Διακοπή GPS</string>
|
|
||||||
<!-- OpenTracks -->
|
<!-- OpenTracks -->
|
||||||
<string name="recording_service">Υπηρεσία καταγραφής</string>
|
<string name="recording_service">Υπηρεσία καταγραφής</string>
|
||||||
<!-- Search -->
|
<!-- Search -->
|
||||||
@@ -193,7 +190,6 @@ limitations under the License.
|
|||||||
<string name="settings_reset_confirm_title">Επαναφορά τώρα;</string>
|
<string name="settings_reset_confirm_title">Επαναφορά τώρα;</string>
|
||||||
<string name="settings_reset_done">Όλες οι ρυθμίσεις έχουν επανέλθει στις προεπιλεγμένες τιμές</string>
|
<string name="settings_reset_done">Όλες οι ρυθμίσεις έχουν επανέλθει στις προεπιλεγμένες τιμές</string>
|
||||||
<!-- Settings Sensor -->
|
<!-- Settings Sensor -->
|
||||||
<string name="settings_sensor_bluetooth">Bluetooth</string>
|
|
||||||
<!-- Settings Stats -->
|
<!-- Settings Stats -->
|
||||||
<string name="settings_stats_rate_title">Προτιμώμενος ρυθμός</string>
|
<string name="settings_stats_rate_title">Προτιμώμενος ρυθμός</string>
|
||||||
<string name="settings_stats_units_imperial">Αγγλοσαξονικό (μίλια, πόδια)</string>
|
<string name="settings_stats_units_imperial">Αγγλοσαξονικό (μίλια, πόδια)</string>
|
||||||
|
|||||||
@@ -175,7 +175,6 @@ limitations under the License.
|
|||||||
<string name="gps_wait_for_signal">Esperando señal de GPS</string>
|
<string name="gps_wait_for_signal">Esperando señal de GPS</string>
|
||||||
<!-- Image -->
|
<!-- Image -->
|
||||||
<string name="image_arrow">Flecha</string>
|
<string name="image_arrow">Flecha</string>
|
||||||
<string name="image_marker">Marcador</string>
|
|
||||||
<string name="image_marker_count">Recuento de marcadores</string>
|
<string name="image_marker_count">Recuento de marcadores</string>
|
||||||
<string name="image_photo_marker">Marcador de foto</string>
|
<string name="image_photo_marker">Marcador de foto</string>
|
||||||
<string name="image_record">Grabar</string>
|
<string name="image_record">Grabar</string>
|
||||||
@@ -214,8 +213,6 @@ limitations under the License.
|
|||||||
<string name="menu_settings">Configuración</string>
|
<string name="menu_settings">Configuración</string>
|
||||||
<string name="menu_share">Compartir</string>
|
<string name="menu_share">Compartir</string>
|
||||||
<string name="menu_show_on_map">Mostrar en el mapa</string>
|
<string name="menu_show_on_map">Mostrar en el mapa</string>
|
||||||
<string name="menu_start_gps">Iniciar GPS</string>
|
|
||||||
<string name="menu_stop_gps">Detener GPS</string>
|
|
||||||
<string name="menu_resume_track">Reanudar recorrido</string>
|
<string name="menu_resume_track">Reanudar recorrido</string>
|
||||||
<!-- OpenTracks -->
|
<!-- OpenTracks -->
|
||||||
<string name="recording_service">Servicio de grabación</string>
|
<string name="recording_service">Servicio de grabación</string>
|
||||||
@@ -254,7 +251,6 @@ limitations under the License.
|
|||||||
<string name="settings_reset_confirm_title">¿Restablecer ahora?</string>
|
<string name="settings_reset_confirm_title">¿Restablecer ahora?</string>
|
||||||
<string name="settings_reset_done">Se restablecieron los valores predeterminados de todas las configuraciones</string>
|
<string name="settings_reset_done">Se restablecieron los valores predeterminados de todas las configuraciones</string>
|
||||||
<!-- Settings Sensor -->
|
<!-- Settings Sensor -->
|
||||||
<string name="settings_sensor_bluetooth">Bluetooth</string>
|
|
||||||
<!-- Settings Stats -->
|
<!-- Settings Stats -->
|
||||||
<string name="settings_stats_rate_title">Tasa preferida</string>
|
<string name="settings_stats_rate_title">Tasa preferida</string>
|
||||||
<string name="settings_stats_units_imperial">Imperiales (millas, pies)</string>
|
<string name="settings_stats_units_imperial">Imperiales (millas, pies)</string>
|
||||||
@@ -412,8 +408,6 @@ limitations under the License.
|
|||||||
<!-- Share image -->
|
<!-- Share image -->
|
||||||
<string name="share_image_subject">Me gustaría compartir una imagen contigo</string>
|
<string name="share_image_subject">Me gustaría compartir una imagen contigo</string>
|
||||||
<string name="share_image_body">Creo que esta imagen podría interesarte.</string>
|
<string name="share_image_body">Creo que esta imagen podría interesarte.</string>
|
||||||
<string name="open_track_as_KML">Abrir como KML</string>
|
|
||||||
<string name="open_track_as_GPX">Abrir como GPX</string>
|
|
||||||
<string name="open_track_as_trackfileformat">Abrir como %1$s</string>
|
<string name="open_track_as_trackfileformat">Abrir como %1$s</string>
|
||||||
<string name="activity_type_escooter">moto</string>
|
<string name="activity_type_escooter">moto</string>
|
||||||
<string name="export_all_do_it_for_all">hacerlo para todos los conflictos</string>
|
<string name="export_all_do_it_for_all">hacerlo para todos los conflictos</string>
|
||||||
@@ -478,7 +472,6 @@ limitations under the License.
|
|||||||
<string name="track_speed_notification">Velocidad: %1$s</string>
|
<string name="track_speed_notification">Velocidad: %1$s</string>
|
||||||
<string name="track_pace_notification">Ritmo: %1$s</string>
|
<string name="track_pace_notification">Ritmo: %1$s</string>
|
||||||
<string name="about_third_party_data">Datos de terceros</string>
|
<string name="about_third_party_data">Datos de terceros</string>
|
||||||
<string name="delete_service">Borrar servicio</string>
|
|
||||||
<string name="menu_voice_rate">Velocidad de la voz</string>
|
<string name="menu_voice_rate">Velocidad de la voz</string>
|
||||||
<string name="track_delete_not_recording">Este recorrido no puede ser eliminado porque se está grabando actualmente.</string>
|
<string name="track_delete_not_recording">Este recorrido no puede ser eliminado porque se está grabando actualmente.</string>
|
||||||
<string name="settings_sensor_running_speed_and_cadence">Distancia, velocidad y cadencia</string>
|
<string name="settings_sensor_running_speed_and_cadence">Distancia, velocidad y cadencia</string>
|
||||||
@@ -507,7 +500,6 @@ limitations under the License.
|
|||||||
<string name="value_float_kilometer_hour">%1$.1f km/h</string>
|
<string name="value_float_kilometer_hour">%1$.1f km/h</string>
|
||||||
<string name="value_float_mile_hour">%1$.1f mi/h</string>
|
<string name="value_float_mile_hour">%1$.1f mi/h</string>
|
||||||
<string name="value_float_mile_hour_recommended">%1$.1f mi/h (recomendado)</string>
|
<string name="value_float_mile_hour_recommended">%1$.1f mi/h (recomendado)</string>
|
||||||
<string name="settings_recording_idle_speed_title">Umbral de velocidad de ralentí</string>
|
|
||||||
<string name="stats_clock">Reloj</string>
|
<string name="stats_clock">Reloj</string>
|
||||||
<string name="value_float_kilometer_hour_recommended">%1$.1f km/h (recomendado)</string>
|
<string name="value_float_kilometer_hour_recommended">%1$.1f km/h (recomendado)</string>
|
||||||
<string name="settings_generic_sensor">Genérico</string>
|
<string name="settings_generic_sensor">Genérico</string>
|
||||||
@@ -526,7 +518,7 @@ limitations under the License.
|
|||||||
<string name="settings_ui_title">Interfaz de usuario</string>
|
<string name="settings_ui_title">Interfaz de usuario</string>
|
||||||
<string name="settings_ui_summary">Apariencia, Diseño de grabación, Tema</string>
|
<string name="settings_ui_summary">Apariencia, Diseño de grabación, Tema</string>
|
||||||
<string name="settings_gps_title">GPS</string>
|
<string name="settings_gps_title">GPS</string>
|
||||||
<string name="settings_announcements_title">Avisos de voz</string>
|
<string name="settings_announcements_statistics_title">Avisos de voz</string>
|
||||||
<string name="settings_units_category">Unidades</string>
|
<string name="settings_units_category">Unidades</string>
|
||||||
<string name="settings_import_export_title">Importar/Exportar</string>
|
<string name="settings_import_export_title">Importar/Exportar</string>
|
||||||
<string name="stats_custom_layout_default_layout">Diseño predeterminado</string>
|
<string name="stats_custom_layout_default_layout">Diseño predeterminado</string>
|
||||||
@@ -646,4 +638,11 @@ limitations under the License.
|
|||||||
<string name="settings_sensor_bluetooth_service_filter_off">Mostrar todos los dispositivos Bluetooth detectados</string>
|
<string name="settings_sensor_bluetooth_service_filter_off">Mostrar todos los dispositivos Bluetooth detectados</string>
|
||||||
<string name="voice_on_device_speaker_title">Utiliza el altavoz del dispositivo</string>
|
<string name="voice_on_device_speaker_title">Utiliza el altavoz del dispositivo</string>
|
||||||
<string name="menu_export_all_one_file">Exportar todo (un archivo para todas las pistas)</string>
|
<string name="menu_export_all_one_file">Exportar todo (un archivo para todas las pistas)</string>
|
||||||
|
<string name="settings_announcements_idle_category_title">Anuncios inactivos</string>
|
||||||
|
<string name="sensor_state_power_max">Potencia máxima</string>
|
||||||
|
<string name="activity_type_kickscooter">escúter</string>
|
||||||
|
<string name="voiceIdle">Volverlo inactivo.</string>
|
||||||
|
<string name="settings_announcements_idle_title">Inactivo</string>
|
||||||
|
<string name="settings_recording_idle_timeout_title">Umbral de inactividad</string>
|
||||||
|
<string name="value_int_seconds">%1$d s (recomendado)</string>
|
||||||
</resources>
|
</resources>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user