mirror of
https://codeberg.org/OpenTracksApp/OpenTracks.git
synced 2026-10-24 03:05:17 +02:00
Compare commits
81 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4bbdd55254 | |||
| 7df625ff0c | |||
| 7d01048a98 | |||
| 70fdb0e877 | |||
| ec61383ffd | |||
| 28e24615b8 | |||
| 4c6abcfecd | |||
| 8fb294ea9e | |||
| 52d52ddcab | |||
| 1769bc26a2 | |||
| e3fab57c43 | |||
| f7030cd3eb | |||
| b5ba8a9f67 | |||
| 6ae7cb49f4 | |||
| 16899004b5 | |||
| 69fe5e8127 | |||
| 65913a1ba2 | |||
| 0b1e72cf58 | |||
| f89a81cb22 | |||
| ece9f3f0b2 | |||
| 3011198b2c | |||
| 77d56a864f | |||
| 0bb468e31d | |||
| 627d7c9288 | |||
| f428096164 | |||
| dfaaf4b022 | |||
| b576ccc4e2 | |||
| 0e0958c5e8 | |||
| 43e1cbcdc6 | |||
| 073b3d2052 | |||
| 01f4359cdf | |||
| 333bc6f31b | |||
| a2a9132658 | |||
| 5857c0032e | |||
| d086aa3c06 | |||
| fba09bcc3a | |||
| 3ce0e2cede | |||
| df0d3ce600 | |||
| c05cbc2fa6 | |||
| 2f07208d9d | |||
| ded98eddb5 | |||
| 45732735d4 | |||
| b43dfd05ef | |||
| c011fd4e60 | |||
| a03825e10e | |||
| e230fb42b8 | |||
| 82c0f3dcc5 | |||
| a5296c4cd9 | |||
| 522d9ebe2e | |||
| 26c660ec5b | |||
| fd9646bb1f | |||
| 4966ba8868 | |||
| 6a8861b41b | |||
| 8dc20c48b7 | |||
| f2b8dc2e07 | |||
| 41ce088a0e | |||
| 782cefb4ff | |||
| 35a02a2f81 | |||
| 8c35bd6f4f | |||
| ad26ce795a | |||
| ed1e47213d | |||
| cbb88fd1ae | |||
| a6b808d5cf | |||
| 9718377307 | |||
| dbf0c3bfc9 | |||
| 8dff211a26 | |||
| 319079162c | |||
| 731c182a12 | |||
| 6c4bb8d536 | |||
| a42aefa752 | |||
| 2f489d73b0 | |||
| babd59f0ea | |||
| 37b7277f1a | |||
| ecb421248c | |||
| 42f53199a3 | |||
| 530b906f73 | |||
| e4ebf97c27 | |||
| e3cdc952a2 | |||
| aaf78e0b74 | |||
| c3a2c0d6d0 | |||
| 05fbfcb914 |
@@ -9,6 +9,9 @@ jobs:
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-tags: true
|
||||
fetch-depth: 0 #TODO See https://github.com/actions/checkout/issues/1471
|
||||
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
@@ -19,7 +22,7 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
echo ${NIGHTLY_STORE_FILE} | base64 -d > KEY_NIGHTLY.jks
|
||||
./gradlew assembleNightly -Dnightly_store_file=KEY_NIGHTLY.jks -Dnightly_store_password="${NIGHTLY_STORE_PASSWORD}" -Dnightly_key_alias="${NIGHTLY_KEY_ALIAS}" -Dnightly_key_password="${NIGHTLY_STORE_PASSWORD}"
|
||||
./gradlew assembleNightlyRelease -Dnightly_store_file=KEY_NIGHTLY.jks -Dnightly_store_password="${NIGHTLY_STORE_PASSWORD}" -Dnightly_key_alias="${NIGHTLY_KEY_ALIAS}" -Dnightly_key_password="${NIGHTLY_STORE_PASSWORD}"
|
||||
env:
|
||||
NIGHTLY_STORE_FILE: ${{ secrets.NIGHTLY_STORE_FILE }}
|
||||
NIGHTLY_STORE_PASSWORD: ${{ secrets.NIGHTLY_STORE_PASSWORD }}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
name: Linting
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
@@ -10,12 +9,15 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-tags: true
|
||||
fetch-depth: 0 #TODO See https://github.com/actions/checkout/issues/1471
|
||||
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
distribution: 'adopt'
|
||||
java-version: '17'
|
||||
|
||||
- run: ./gradlew lint
|
||||
|
||||
@@ -11,6 +11,9 @@ jobs:
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-tags: true
|
||||
fetch-depth: 0 #TODO See https://github.com/actions/checkout/issues/1471
|
||||
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
|
||||
@@ -12,7 +12,7 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://f-droid.org/packages/de.dennisguse.opentracks">
|
||||
<a href="https://f-droid.org/packages/de.dennisguse.opentracks.playstore">
|
||||
<img alt="Get it on F-Droid" src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" height="60" align="middle">
|
||||
</a>
|
||||
</td>
|
||||
@@ -29,7 +29,7 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<img alt="OpenTracks version published on F-Droid" src="https://img.shields.io/f-droid/v/de.dennisguse.opentracks.svg" align="middle" >
|
||||
<img alt="OpenTracks version published on F-Droid" src="https://img.shields.io/f-droid/v/de.dennisguse.opentracks.playstore.svg" align="middle" >
|
||||
</td>
|
||||
<td></td>
|
||||
<td align="center">
|
||||
@@ -109,19 +109,20 @@ The API can be invoked by sending an explicit Intent to start an activity.
|
||||
* Debug: `de.dennisguse.opentracks.debug`
|
||||
* Nightly: `de.dennisguse.opentracks.nightly`
|
||||
|
||||
`Class`:
|
||||
|
||||
`Classes`:
|
||||
* **Start a recording:** `de.dennisguse.opentracks.publicapi.StartRecording`
|
||||
* Set track data: `TRACK_NAME`, `TRACK_DESCRIPTION`, `TRACK_CATEGORY`, and `TRACK_ICON` (
|
||||
non-localized identifier
|
||||
see [/src/main/java/de/dennisguse/opentracks/util/TrackIconUtils.java#L38](/src/main/java/de/dennisguse/opentracks/util/TrackIconUtils.java#L38)).
|
||||
NOTE: if `TRACK_ICON` is not present, `TRACK_CATEGORY` will be used to determine the icon (
|
||||
localized).
|
||||
* Send recorded data to another application via _Dashboard API_: `STATS_TARGET_PACKAGE` and
|
||||
`STATS_TARGET_CLASS`
|
||||
* **Stop a recording:** `de.dennisguse.opentracks.publicapi.StopRecording`
|
||||
* **Create a marker:** `de.dennisguse.opentracks.publicapi.CreateMarker`
|
||||
|
||||
For testing via adb: `adb shell am start -e someParameter someValue -n "package/class"`
|
||||
|
||||
`StartRecording` supports the following parameters:
|
||||
|
||||
* Set track data: `TRACK_NAME`, `TRACK_DESCRIPTION`, `TRACK_CATEGORY`, and `TRACK_ICON` (non-localized identifier see [/src/main/java/de/dennisguse/opentracks/util/TrackIconUtils.java#L38](/src/main/java/de/dennisguse/opentracks/util/TrackIconUtils.java#L38)).
|
||||
NOTE: if `TRACK_ICON` is not present, `TRACK_CATEGORY` will be used to determine the icon (localized).
|
||||
* Send recorded data to another application via _Dashboard API_: `STATS_TARGET_PACKAGE` and `STATS_TARGET_CLASS`
|
||||
|
||||
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
|
||||
|
||||
+19
-27
@@ -2,39 +2,31 @@
|
||||
|
||||
## Craft new releases
|
||||
|
||||
1. Get next _version code_ (main): `git rev-list HEAD --count main`
|
||||
2. Decide on _version name_ (semantic versioning)
|
||||
3. Manually update _version code_ and _version name_ in `build.gradle`
|
||||
4. Create changelog (`_version code_.txt`)
|
||||
5. Create commit with all changes
|
||||
6. Tag newly create commit with _version name_ (e.g., v3.2.1)
|
||||
7. Push commits and tags to public repository
|
||||
8. Add changelog to tag (Github releases)
|
||||
1. Execute use `RELEASE.sh`.
|
||||
2. Push to Github
|
||||
3. Create release on Github (incl. changelog)
|
||||
This actually creates a tag that is later used by F-Droid
|
||||
4. Pull from Github
|
||||
5. Build reproducible release and attach to release
|
||||
6. ... wait until reproducibility is verified by F-Droid
|
||||
|
||||
## F-Droid
|
||||
Application id: de.dennisguse.opentracks
|
||||
Two releases will be created:
|
||||
* Application id: `de.dennisguse.opentracks` (irreproducible)
|
||||
* Application id: `de.dennisguse.opentracks.playstore` (reproducible)
|
||||
|
||||
It if it properly tagged F-Droid takes care of building the app and publishing.
|
||||
Usually, it takes two days before the update is published (build, signed, and published).
|
||||
For F-Droid the build status can be found [here](https://monitor.f-droid.org/builds/build).
|
||||
|
||||
The recent build activity can be found [here](https://monitor.f-droid.org/builds/build)
|
||||
## Reproducible builds
|
||||
|
||||
## Google Play Store
|
||||
Application id: `de.dennisguse.opentracks.playstore`
|
||||
A lot of detailed information can be found [here](https://f-droid.org/docs/Reproducible_Builds/)
|
||||
|
||||
Requirement: `gem install fastlane`
|
||||
|
||||
1. Build signed apk
|
||||
(requires keystore)
|
||||
2. Upload signed apk to [Google Play Console](https://play.google.com/apps/publish)
|
||||
3. Update store meta data (e.g., description and screenshots)
|
||||
`fastlane metadata`
|
||||
(requires API key configured in `Appfile`)
|
||||
|
||||
Supported languages: https://support.google.com/googleplay/android-developer/table/4419860
|
||||
Technical requirements:
|
||||
* Must be build using OpenJDK17
|
||||
|
||||
## Translations (localization)
|
||||
|
||||
Translations of the OpenTracks are handled in
|
||||
Weblate: [https://hosted.weblate.org/projects/opentracks/](https://hosted.weblate.org/projects/opentracks)
|
||||
.
|
||||
Weblate: [https://hosted.weblate.org/projects/opentracks/](https://hosted.weblate.org/projects/opentracks).
|
||||
|
||||
## Google Play Store
|
||||
Supported languages: https://support.google.com/googleplay/android-developer/table/4419860
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64/
|
||||
|
||||
JAVA_VERSION=`java --version`
|
||||
if [[ $JAVA_VERSION != "openjdk 17."* ]]; then
|
||||
echo "OpenJDK version should be 17.X"
|
||||
exit -1;
|
||||
fi
|
||||
|
||||
RELEASE_STORE_PASSWORD=
|
||||
read -s -p "Enter store password: " RELEASE_STORE_PASSWORD
|
||||
echo ""
|
||||
read -s -p "Enter key alias: " RELEASE_KEY_ALIAS
|
||||
echo ""
|
||||
read -s -p "Enter key password: " RELEASE_KEY_PASSWORD
|
||||
echo ""
|
||||
|
||||
./gradlew --no-configuration-cache \
|
||||
clean \
|
||||
assembleReproducibleRelease \
|
||||
-Drelease_store_file=`ls ../*.jks` \
|
||||
-Drelease_store_file="../keystore_opentracks.jks" \
|
||||
-Drelease_store_password="$RELEASE_STORE_PASSWORD" \
|
||||
-Drelease_key_alias="$RELEASE_KEY_ALIAS" \
|
||||
-Drelease_key_password="$RELEASE_KEY_PASSWORD"
|
||||
+62
-29
@@ -6,7 +6,7 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.7.3'
|
||||
classpath 'com.android.tools.build:gradle:8.8.0'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ def getVersionName = { ->
|
||||
}
|
||||
return stdout.toString().trim()
|
||||
} catch (ignored) {
|
||||
return null
|
||||
throw new GradleException('VersionName could not be read:' + ignored.message)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ def getVersionCode = { ->
|
||||
}
|
||||
return Integer.valueOf(stdout.toString().trim())
|
||||
} catch (ignored) {
|
||||
return null
|
||||
throw new GradleException('VersionCode could not be read:' + ignored.message)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,25 +63,33 @@ android {
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "de.dennisguse.opentracks"
|
||||
versionCode 5941
|
||||
versionName "v4.16.0"
|
||||
applicationId 'de.dennisguse.opentracks'
|
||||
versionCode 6022
|
||||
versionName "v4.17.6"
|
||||
|
||||
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
||||
|
||||
minSdk 26
|
||||
targetSdk 35
|
||||
|
||||
testInstrumentationRunner "de.dennisguse.opentracks.TestRunner"
|
||||
testInstrumentationRunner 'de.dennisguse.opentracks.TestRunner'
|
||||
testInstrumentationRunnerArguments clearPackageData: 'true'
|
||||
}
|
||||
signingConfigs {
|
||||
nightly {
|
||||
if (System.getProperty("nightly_store_file") != null) {
|
||||
storeFile file(System.getProperty("nightly_store_file"))
|
||||
storePassword System.getProperty("nightly_store_password")
|
||||
keyAlias System.getProperty("nightly_key_alias")
|
||||
keyPassword System.getProperty("nightly_key_password")
|
||||
if (System.getProperty('nightly_store_file') != null) {
|
||||
storeFile file(System.getProperty('nightly_store_file'))
|
||||
storePassword System.getProperty('nightly_store_password')
|
||||
keyAlias System.getProperty('nightly_key_alias')
|
||||
keyPassword System.getProperty('nightly_key_password')
|
||||
}
|
||||
}
|
||||
release {
|
||||
if (System.getProperty("release_store_file") != null) {
|
||||
storeFile file(System.getProperty("release_store_file"))
|
||||
storePassword System.getProperty("release_store_password")
|
||||
keyAlias System.getProperty("release_key_alias")
|
||||
keyPassword System.getProperty("release_key_password")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,42 +98,67 @@ android {
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
applicationIdSuffix ".debug"
|
||||
versionNameSuffix "-debug"
|
||||
applicationIdSuffix '.debug'
|
||||
versionNameSuffix '-debug'
|
||||
}
|
||||
|
||||
nightly {
|
||||
signingConfig signingConfigs.nightly
|
||||
applicationIdSuffix ".nightly"
|
||||
}
|
||||
|
||||
release {
|
||||
crunchPngs false
|
||||
minifyEnabled false
|
||||
}
|
||||
}
|
||||
|
||||
releasePlayStore {
|
||||
applicationIdSuffix ".playstore"
|
||||
versionNameSuffix "-PlayStore"
|
||||
signingConfig signingConfigs.debug
|
||||
|
||||
flavorDimensions 'version'
|
||||
productFlavors {
|
||||
nightly {
|
||||
dimension 'version'
|
||||
applicationId 'de.dennisguse.opentracks.nightly'
|
||||
signingConfig signingConfigs.nightly
|
||||
}
|
||||
irreproducible {
|
||||
// Non-reproducible: https://f-droid.org/de/packages/de.dennisguse.opentracks/
|
||||
dimension 'version'
|
||||
applicationId 'de.dennisguse.opentracks'
|
||||
versionNameSuffix 'irreproducible'
|
||||
}
|
||||
reproducible {
|
||||
// Developer Binaries: https://github.com/OpenTracksApp/OSMDashboard/releases/download/v%v/de.dennisguse.opentracks.playstore_%v.apk
|
||||
// FDroid: https://f-droid.org/de/packages/de.dennisguse.opentracks.playstore
|
||||
// PlayStore: https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore
|
||||
dimension 'version'
|
||||
applicationId 'de.dennisguse.opentracks.playstore'
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
}
|
||||
|
||||
applicationVariants.configureEach { variant ->
|
||||
variant.resValue "string", "applicationId", variant.applicationId
|
||||
variant.resValue 'string', 'applicationId', variant.applicationId
|
||||
|
||||
if (variant.buildType.name == 'nightly') {
|
||||
variant.outputs.configureEach {
|
||||
variant.outputs.configureEach {
|
||||
if (variant.flavorName == 'nightly') {
|
||||
setVersionCodeOverride(getVersionCode())
|
||||
setVersionNameOverride(getVersionName())
|
||||
}
|
||||
|
||||
if (variant.flavorName == 'reproducible') {
|
||||
outputFileName = "${applicationId}_${variant.versionName}.apk"
|
||||
} else {
|
||||
outputFileName = "${applicationId}_${variant.versionCode}.apk"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependenciesInfo {
|
||||
// Disables dependency metadata when building APKs.
|
||||
includeInApk = false
|
||||
// Disables dependency metadata when building Android App Bundles.
|
||||
includeInBundle = false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dependencies {
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.3'
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.4'
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||
implementation 'androidx.preference:preference:1.2.1'
|
||||
@@ -143,7 +176,7 @@ dependencies {
|
||||
androidTestImplementation 'androidx.test:rules:1.6.1'
|
||||
androidTestImplementation 'androidx.test:runner:1.6.2'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
|
||||
androidTestImplementation 'org.mockito:mockito-android:5.14.2'
|
||||
androidTestImplementation 'org.mockito:mockito-android:5.15.2'
|
||||
|
||||
androidTestUtil 'androidx.test:orchestrator:1.5.1'
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.15.0: OpenTracks
|
||||
|
||||
Změny:
|
||||
- Výběr jazyka v aplikaci (Android 14 nebo novější)
|
||||
- Funkce zobrazit na mapě podporuje pouze aplikaci OSM Dashboard (odstraněno KML/KMZ nebo GPX)
|
||||
|
||||
Vývojář:
|
||||
- GitHub Actions nyní používají Ubuntu
|
||||
@@ -0,0 +1,4 @@
|
||||
v4.16.0: OpenTracks
|
||||
|
||||
Oprava chyby:
|
||||
- PublicAPI: spuštění záznamu nezaznamenalo žádná data
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.16.1: OpenTracks
|
||||
|
||||
Změny:
|
||||
- Výběr jazyka v aplikaci je nyní k dispozici pro všechny verze systému Android
|
||||
(na základě velké poptávky)
|
||||
|
||||
Vývojář:
|
||||
- GitHub Actions: při checkoutu se nestahovaly značky (noční aktualizace)
|
||||
@@ -0,0 +1,7 @@
|
||||
v4.17.0: OpenTracks
|
||||
|
||||
Změny:
|
||||
- Veřejné rozhraní API pro vytváření značek (během záznamu)
|
||||
|
||||
Oprava chyby:
|
||||
- Senzory: při nečinnosti nastavena data pohybových senzorů na 0
|
||||
@@ -0,0 +1,5 @@
|
||||
v4.17.1: OpenTracks
|
||||
|
||||
Změny:
|
||||
- Reprodukovatelné sestavení: uvidíme, jestli to bude fungovat.
|
||||
Varianta pro Obchod Play bude odeslána F-Droidu, který ji ověří a zveřejní.
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.17.2: OpenTracks
|
||||
|
||||
Změny:
|
||||
- Reprodukovatelné sestavení: uvidíme, zda to bude fungovat
|
||||
Varianta pro Obchod Play bude odeslána do F-Droidu, kde bude ověřena a zveřejněna.
|
||||
|
||||
Vývojář:
|
||||
- F-Droid vyžaduje použití Product Flavors (namísto Build Types)
|
||||
@@ -0,0 +1,7 @@
|
||||
v4.17.2: OpenTracks
|
||||
|
||||
Změny:
|
||||
- Reprodukovatelné sestavení: uvidíme, zda to bude fungovat.
|
||||
|
||||
Vývojář:
|
||||
- Pokus o opravu názvu souboru APK...
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.17.4: OpenTracks
|
||||
|
||||
|
||||
Změny:
|
||||
- Reprodukovatelné sestavení: uvidíme, jestli to bude fungovat.
|
||||
|
||||
Vývojář:
|
||||
- Odstranění DependencyInfo.
|
||||
@@ -0,0 +1,10 @@
|
||||
v4.17.5: OpenTracks
|
||||
|
||||
Změny:
|
||||
- Reprodukovatelné sestavení: uvidíme, jestli to bude fungovat.
|
||||
|
||||
Oprava chyby:
|
||||
- Reprodukovatelné sestavení používá ikonu z Obchodu Play.
|
||||
|
||||
Vývojář:
|
||||
- Přidán soubor RELEASE_BUILD.sh
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.16.1: OpenTracks
|
||||
|
||||
Changes:
|
||||
- In-app language selector now available for all Android versions
|
||||
(due to some popular demand)
|
||||
|
||||
Developer:
|
||||
- Github actions: checkout was not fetching tags (nightly)
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.17.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Public API to create markers (while recording)
|
||||
|
||||
Bugfix:
|
||||
- Sensors: on idle, set movement-based sensor data to 0
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
v4.17.1: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Reproducible build: let's see if it works.
|
||||
The PlayStore variant will be submitted to F-Droid, verified and published them.
|
||||
@@ -0,0 +1,9 @@
|
||||
v4.17.2: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Reproducible build: let's see if it works.
|
||||
The PlayStore variant will be submitted to F-Droid, verified and published them.
|
||||
|
||||
Developer:
|
||||
- F-Droid requires to use Product Flavors (instead of Build Types)
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
v4.17.3: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Reproducible build: let's see if it works.
|
||||
|
||||
Developer:
|
||||
- Just trying to get the APK filename, right...
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.17.4: OpenTracks
|
||||
|
||||
|
||||
Changes:
|
||||
- Reproducible build: let's see if it works.
|
||||
|
||||
Developer:
|
||||
- Removing DependencyInfo.
|
||||
@@ -0,0 +1,10 @@
|
||||
v4.17.5: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Reproducible build: let's see if it works.
|
||||
|
||||
Bugfix:
|
||||
- Reproducible build uses PlayStore icon.
|
||||
|
||||
Developer:
|
||||
- Added RELEASE_BUILD.sh
|
||||
@@ -0,0 +1,5 @@
|
||||
v4.17.6: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- UI: GPS data may be not shown regularly (does not affect recorded data)
|
||||
Introduced in v4.17.0
|
||||
@@ -0,0 +1,7 @@
|
||||
v4.14.1 : OpenTracks
|
||||
|
||||
Changements :
|
||||
− La détection d’immobilité peut être désactivée
|
||||
|
||||
Correction de bogue :
|
||||
− L’interface utilisateur pour ajouter des images aux marqueurs était cachée
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.15.0 : OpenTracks
|
||||
|
||||
Changements :
|
||||
− Sélection de la langue sur l’application (Android 14 ou plus)
|
||||
− La fonctionnalité « montrer sur la carte » prend en charge uniquement OSM Dashboard (suppression de KML/KMZ ou GPX)
|
||||
|
||||
Développeurs :
|
||||
− Les actions Github utilisent désormais Ubuntu
|
||||
@@ -0,0 +1,7 @@
|
||||
v4.17.0 : OpenTracks
|
||||
|
||||
Changements :
|
||||
− IPA (API) publique pour créer des marqueurs (lors de l’enregistrement)
|
||||
|
||||
Correction de bogue :
|
||||
− Capteurs : quand immobile, les données des capteurs de mouvements sont mises à 0
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.15.0: OpenTracks
|
||||
|
||||
Athruithe:
|
||||
- Roghnóir teanga in-app (Android 14 nó níos déanaí)
|
||||
- Ní thacaíonn an ghné taispeáin-ar-mapa ach le Painéal OSM (bainte KML/KMZ nó GPX)
|
||||
|
||||
Forbróir:
|
||||
- Úsáideann gníomhartha Github anois Ubuntu
|
||||
@@ -0,0 +1,4 @@
|
||||
v4.16.0: OpenTracks
|
||||
|
||||
Ceartúchán Bug:
|
||||
- PublicAPI: níor taifeadadh aon sonraí le tosú taifeadta
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.16.1: OpenTracks
|
||||
|
||||
Athruithe:
|
||||
- Roghnóir teanga in-app ar fáil anois do gach leagan Android
|
||||
(mar gheall ar roinnt éileamh coitianta)
|
||||
|
||||
Forbróir:
|
||||
- Gníomhartha Github: ní raibh an tseiceáil amach ag fáil clibeanna (oíche)
|
||||
@@ -0,0 +1,7 @@
|
||||
v4.17.0: OpenTracks
|
||||
|
||||
Athruithe:
|
||||
- API poiblí chun marcóirí a chruthú (agus tú ag taifeadadh)
|
||||
|
||||
Ceartúchán Bug:
|
||||
- Braiteoirí: i gcás díomhaoin, socraigh sonraí braiteora bunaithe ar ghluaiseacht go 0
|
||||
@@ -0,0 +1,5 @@
|
||||
v4.17.1: OpenTracks
|
||||
|
||||
Athruithe:
|
||||
- Tógáil atáirgthe: féachaimis an n-oibríonn sé.
|
||||
Cuirfear an leagan PlayStore faoi bhráid F-Droid, fíorófar iad agus foilseofar iad.
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.17.2: OpenTracks
|
||||
|
||||
Athruithe:
|
||||
- Tógáil atáirgthe: féachaimis an n-oibríonn sé.
|
||||
Cuirfear an leagan PlayStore faoi bhráid F-Droid, fíorófar iad agus foilseofar iad.
|
||||
|
||||
Forbróir:
|
||||
- Éilíonn F-Droid Blasanna Táirge a úsáid (in ionad Cineálacha Tógála)
|
||||
@@ -0,0 +1,7 @@
|
||||
v4.17.3: OpenTracks
|
||||
|
||||
Athruithe:
|
||||
- Tógáil atáirgthe: féachaimis an n-oibríonn sé.
|
||||
|
||||
Forbróir:
|
||||
- Díreach ag iarraidh ainm an chomhaid APK a fháil, ceart ...
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.17.4: OpenTracks
|
||||
|
||||
|
||||
Athruithe:
|
||||
- Tógáil atáirgthe: féachaimis an n-oibríonn sé.
|
||||
|
||||
Forbróir:
|
||||
- DependencyInfo a bhaint.
|
||||
@@ -0,0 +1,10 @@
|
||||
v4.17.5: OpenTracks
|
||||
|
||||
Athruithe:
|
||||
- Tógáil atáirgthe: féachaimis an n-oibríonn sé.
|
||||
|
||||
Ceartúchán Bug:
|
||||
- Úsáideann tógáil atáirgthe íocón PlayStore.
|
||||
|
||||
Forbróir:
|
||||
- Curtha leis RELEASE_BUILD.sh
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.17.2: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Compilación reproducible: comprobemos se funciona ben.
|
||||
Enviaremos a F-Droid a variante da Play Store, para que a verifiquen e publiquen.
|
||||
|
||||
Desenvolvemento:
|
||||
- F-Droid require que usemos Product Flavors (no lugar de Build Types)
|
||||
@@ -0,0 +1,7 @@
|
||||
v4.17.3: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Compilación reproducible: agardemos que funcione ben.
|
||||
|
||||
Desenvolvemento:
|
||||
- Intento de que o nome do ficheiro APK sai a ben, vexamos...
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.15.0: OpenTracks
|
||||
|
||||
Wijzigingen:
|
||||
- In-app taalkiezer (Android 14 of hoger)
|
||||
- Show-on-map-functie ondersteunt alleen OSM Dashboard (verwijderd KML/KMZ of GPX)
|
||||
|
||||
Ontwikkelaar:
|
||||
- Github-acties gebruiken nu Ubuntu
|
||||
@@ -0,0 +1,4 @@
|
||||
v4.16.0: OpenTracks
|
||||
|
||||
Opgelost probleem:
|
||||
- PublicAPI: bij het starten van een opname zijn geen gegevens vastgelegd
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.16.1: OpenTracks
|
||||
|
||||
Veranderingen:
|
||||
- In-app taalkiezer nu beschikbaar voor alle Android-versies
|
||||
(op veler verzoek)
|
||||
|
||||
Ontwikkelaar:
|
||||
- Github-acties: checkout was not fetching tags (nightly)
|
||||
@@ -0,0 +1,9 @@
|
||||
v4.17.0: OpenTracks
|
||||
|
||||
Aanpassing:
|
||||
|
||||
- Openbare API om markeringen te maken (tijdens opname)
|
||||
|
||||
Opgelost:
|
||||
|
||||
- Sensoren: bij rust de op beweging gebaseerde sensorgegevens op 0 zetten
|
||||
@@ -0,0 +1,5 @@
|
||||
v4.17.1: OpenTracks
|
||||
|
||||
Veranderingen:
|
||||
- Reproduceerbare build: laten we eens kijken of het werkt.
|
||||
De PlayStore-variant wordt ingediend bij F-Droid, geverifieerd en gepubliceerd.
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.17.2: OpenTracks
|
||||
|
||||
Veranderingen:
|
||||
- Reproduceerbare build: laten we eens kijken of het werkt.
|
||||
De PlayStore-variant wordt ingediend bij F-Droid, geverifieerd en gepubliceerd.
|
||||
|
||||
Ontwikkelaar:
|
||||
- F-Droid vereist het gebruik van productsmaken (in plaats van buildtypes)
|
||||
@@ -0,0 +1,7 @@
|
||||
v4.17.3: OpenTracks
|
||||
|
||||
Veranderingen:
|
||||
- Reproduceerbare build: laten we eens kijken of het werkt.
|
||||
|
||||
Ontwikkelaar:
|
||||
- Ik probeer gewoon de APK-bestandsnaam te krijgen, toch...
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.17.4: OpenTracks
|
||||
|
||||
|
||||
Veranderingen:
|
||||
- Reproduceerbare build: laten we eens kijken of het werkt.
|
||||
|
||||
Ontwikkelaar:
|
||||
- DependencyInfo verwijderen.
|
||||
@@ -0,0 +1,10 @@
|
||||
v4.17.5: OpenTracks
|
||||
|
||||
Veranderingen:
|
||||
- Reproduceerbare build: laten we eens kijken of het werkt.
|
||||
|
||||
Opgelost probleem:
|
||||
- Reproduceerbare build maakt gebruik van het PlayStore-pictogram.
|
||||
|
||||
Ontwikkelaar:
|
||||
- Toegevoegd RELEASE_BUILD.sh
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
+9
-9
@@ -482,7 +482,7 @@ public class CustomContentProviderUtilsTest {
|
||||
assertEquals(contentProviderUtils.getMarkers(trackId).size(), 1);
|
||||
|
||||
// Get marker id that needs to delete.
|
||||
Marker.Id marker1Id = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(marker1)));
|
||||
Marker.Id marker1Id = contentProviderUtils.insertMarker(marker1);
|
||||
|
||||
// Delete
|
||||
contentProviderUtils.deleteMarker(context, marker1Id);
|
||||
@@ -509,7 +509,7 @@ public class CustomContentProviderUtilsTest {
|
||||
assertEquals(contentProviderUtils.getMarkers(trackId).size(), 1);
|
||||
|
||||
// Get marker id that needs to delete.
|
||||
Marker.Id marker1Id = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(marker1)));
|
||||
Marker.Id marker1Id = contentProviderUtils.insertMarker(marker1);
|
||||
|
||||
// Check marker has photo and it's in the external storage.
|
||||
assertTrue(marker1.hasPhoto());
|
||||
@@ -553,11 +553,11 @@ public class CustomContentProviderUtilsTest {
|
||||
// Insert at first.
|
||||
Marker marker1 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||
marker1.setDescription(MOCK_DESC);
|
||||
Marker.Id marker1Id = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(marker1)));
|
||||
Marker.Id marker1Id = contentProviderUtils.insertMarker(marker1);
|
||||
|
||||
Marker marker2 = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||
marker2.setDescription(MOCK_DESC);
|
||||
Marker.Id marker2Id = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(marker2)));
|
||||
Marker.Id marker2Id = contentProviderUtils.insertMarker(marker2);
|
||||
|
||||
// Delete
|
||||
assertNotNull(contentProviderUtils.getMarker(marker1Id));
|
||||
@@ -598,7 +598,7 @@ public class CustomContentProviderUtilsTest {
|
||||
|
||||
Marker marker = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||
marker.setDescription(TEST_DESC);
|
||||
Marker.Id markerId = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(marker)));
|
||||
Marker.Id markerId = contentProviderUtils.insertMarker(marker);
|
||||
|
||||
assertEquals(TEST_DESC, contentProviderUtils.getMarker(markerId).getDescription());
|
||||
}
|
||||
@@ -614,7 +614,7 @@ public class CustomContentProviderUtilsTest {
|
||||
// Insert at first.
|
||||
Marker marker = new Marker(trackId, contentProviderUtils.getLastValidTrackPoint(trackId));
|
||||
marker.setDescription(TEST_DESC);
|
||||
Marker.Id markerId = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(marker)));
|
||||
Marker.Id markerId = contentProviderUtils.insertMarker(marker);
|
||||
|
||||
// Update
|
||||
marker = contentProviderUtils.getMarker(markerId);
|
||||
@@ -638,7 +638,7 @@ public class CustomContentProviderUtilsTest {
|
||||
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
||||
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
|
||||
marker.setDescription(TEST_DESC);
|
||||
Marker.Id markerId = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(marker)));
|
||||
Marker.Id markerId = contentProviderUtils.insertMarker(marker);
|
||||
|
||||
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.id());
|
||||
assertTrue(dir.exists());
|
||||
@@ -673,7 +673,7 @@ public class CustomContentProviderUtilsTest {
|
||||
TrackPoint trackPoint = contentProviderUtils.getLastValidTrackPoint(trackId);
|
||||
Marker marker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
|
||||
marker.setDescription(TEST_DESC);
|
||||
Marker.Id markerId = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(marker)));
|
||||
Marker.Id markerId = contentProviderUtils.insertMarker(marker);
|
||||
|
||||
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.id());
|
||||
assertTrue(dir.exists());
|
||||
@@ -709,7 +709,7 @@ public class CustomContentProviderUtilsTest {
|
||||
marker.setDescription(TEST_DESC);
|
||||
Marker otherMarker = TestDataUtil.createMarkerWithPhoto(context, trackId, trackPoint);
|
||||
otherMarker.setDescription(TEST_DESC);
|
||||
Marker.Id markerId = new Marker.Id(ContentUris.parseId(contentProviderUtils.insertMarker(marker)));
|
||||
Marker.Id markerId = contentProviderUtils.insertMarker(marker);
|
||||
contentProviderUtils.insertMarker(otherMarker);
|
||||
|
||||
File dir = new File(FileUtils.getPhotoDir(context), "" + trackId.id());
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.junit.Test;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import de.dennisguse.opentracks.BuildConfig;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
|
||||
@@ -34,6 +35,7 @@ public class ShareContentProviderTest {
|
||||
public void testCreateURIescapeFilename() {
|
||||
Pair<Uri, String> shareURIandMIME = ShareContentProvider.createURI(Set.of(new Track.Id(1)), "../../&1=1", TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA);
|
||||
|
||||
assertEquals(Uri.parse("content://de.dennisguse.opentracks.debug.content/tracks/KML_WITH_TRACKDETAIL_AND_SENSORDATA/1/..%2F..%2F%261%3D1"), shareURIandMIME.first);
|
||||
|
||||
assertEquals(Uri.parse("content://" + BuildConfig.APPLICATION_ID + ".content/tracks/KML_WITH_TRACKDETAIL_AND_SENSORDATA/1/..%2F..%2F%261%3D1"), shareURIandMIME.first);
|
||||
}
|
||||
}
|
||||
+9
-9
@@ -157,7 +157,7 @@ public class ExportImportTest {
|
||||
contentProviderUtils.insertMarker(new Marker("Marker 2", "Marker 2 desc", "Marker 2 category", null, trackId, service.getLastStoredTrackPointWithLocation(), ""));
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:02:18Z");
|
||||
trackPointCreator.getSensorManager().sensorDataSet = new SensorDataSet();
|
||||
trackPointCreator.getSensorManager().sensorDataSet = new SensorDataSet(trackPointCreator);
|
||||
service.endCurrentTrack();
|
||||
|
||||
trackPointCreator.setClock("2020-02-02T02:03:20Z");
|
||||
@@ -172,7 +172,7 @@ public class ExportImportTest {
|
||||
|
||||
sendLocation(trackPointCreator, "2020-02-02T02:03:50Z", 3, 16.001, 10, 27, 15, 10, 0f);
|
||||
|
||||
trackPointCreator.getSensorManager().sensorDataSet = new SensorDataSet();
|
||||
trackPointCreator.getSensorManager().sensorDataSet = new SensorDataSet(trackPointCreator);
|
||||
trackPointCreator.setClock("2020-02-02T02:04:00Z");
|
||||
service.endCurrentTrack();
|
||||
|
||||
@@ -550,22 +550,22 @@ public class ExportImportTest {
|
||||
|
||||
AggregatorCyclingPower cyclingPower = Mockito.mock(AggregatorCyclingPower.class);
|
||||
Mockito.when(cyclingPower.hasValue()).thenReturn(true);
|
||||
Mockito.when(cyclingPower.getValue()).thenReturn(Power.of(power));
|
||||
Mockito.when(cyclingPower.getValue(Mockito.any())).thenReturn(Power.of(power));
|
||||
sensorDataSet.add(cyclingPower);
|
||||
|
||||
AggregatorHeartRate agheartRate = Mockito.mock(AggregatorHeartRate.class);
|
||||
Mockito.when(agheartRate.getValue()).thenReturn(HeartRate.of(heartRate));
|
||||
sensorDataSet.add(agheartRate);
|
||||
AggregatorHeartRate avgHeartRate = Mockito.mock(AggregatorHeartRate.class);
|
||||
Mockito.when(avgHeartRate.getValue(Mockito.any())).thenReturn(HeartRate.of(heartRate));
|
||||
sensorDataSet.add(avgHeartRate);
|
||||
|
||||
AggregatorCyclingCadence cyclingCadence = Mockito.mock(AggregatorCyclingCadence.class);
|
||||
Mockito.when(cyclingCadence.hasValue()).thenReturn(true);
|
||||
Mockito.when(cyclingCadence.getValue()).thenReturn(Cadence.of(cadence));
|
||||
Mockito.when(cyclingCadence.getValue(Mockito.any())).thenReturn(Cadence.of(cadence));
|
||||
sensorDataSet.add(cyclingCadence);
|
||||
|
||||
if (distance != null && speed != null) {
|
||||
AggregatorCyclingDistanceSpeed distanceSpeed = Mockito.mock(AggregatorCyclingDistanceSpeed.class);
|
||||
Mockito.when(distanceSpeed.hasValue()).thenReturn(true);
|
||||
Mockito.when(distanceSpeed.getValue()).thenReturn(new AggregatorCyclingDistanceSpeed.Data(null, distance, Speed.of(speed)));
|
||||
Mockito.when(distanceSpeed.getValue(Mockito.any())).thenReturn(new AggregatorCyclingDistanceSpeed.Data(null, distance, Speed.of(speed)));
|
||||
sensorDataSet.add(distanceSpeed);
|
||||
} else {
|
||||
sensorDataSet.add(new AggregatorCyclingDistanceSpeed("", ""));
|
||||
@@ -582,7 +582,7 @@ public class ExportImportTest {
|
||||
if (altitudeGain != null) {
|
||||
AggregatorBarometer barometer = Mockito.mock(AggregatorBarometer.class);
|
||||
Mockito.when(barometer.hasValue()).thenReturn(true);
|
||||
Mockito.when(barometer.getValue()).thenReturn(new AltitudeGainLoss(altitudeGain, altitudeGain));
|
||||
Mockito.when(barometer.getValue(Mockito.any())).thenReturn(new AltitudeGainLoss(altitudeGain, altitudeGain));
|
||||
sensorDataSet.add(barometer);
|
||||
} else {
|
||||
sensorDataSet.add(new AggregatorBarometer("test", null));
|
||||
|
||||
+3
-1
@@ -3,13 +3,15 @@ package de.dennisguse.opentracks.sensors.sensorData;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.AtmosphericPressure;
|
||||
|
||||
public class AggregatorBarometerTest {
|
||||
|
||||
private static void addSensorValue(AggregatorBarometer aggregatorBarometer, float[] values) {
|
||||
for (float f : values) {
|
||||
aggregatorBarometer.add(new Raw<>(AtmosphericPressure.ofHPA(f)));
|
||||
aggregatorBarometer.add(new Raw<>(Instant.MIN, AtmosphericPressure.ofHPA(f)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+26
-24
@@ -9,6 +9,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Cadence;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingCadence;
|
||||
@@ -23,11 +25,11 @@ public class SensorDataCyclingTest {
|
||||
AggregatorCyclingCadence current = new AggregatorCyclingCadence("", "");
|
||||
|
||||
// when
|
||||
current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(1, 1024)));
|
||||
current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(2, 2048)));
|
||||
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(1, 1024)));
|
||||
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(2, 2048)));
|
||||
|
||||
// then
|
||||
assertEquals(60, current.getValue().getRPM(), 0.01);
|
||||
assertEquals(60, current.getValue(Instant.MIN).getRPM(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -35,11 +37,11 @@ public class SensorDataCyclingTest {
|
||||
AggregatorCyclingCadence current = new AggregatorCyclingCadence("", "");
|
||||
|
||||
// when
|
||||
current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(1, 6184)));
|
||||
current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(2, 8016)));
|
||||
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(1, 6184)));
|
||||
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(2, 8016)));
|
||||
|
||||
// then
|
||||
assertEquals(33.53, current.getValue().getRPM(), 0.01);
|
||||
assertEquals(33.53, current.getValue(Instant.MIN).getRPM(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -47,11 +49,11 @@ public class SensorDataCyclingTest {
|
||||
AggregatorCyclingCadence current = new AggregatorCyclingCadence("", "");
|
||||
|
||||
// when
|
||||
current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(1, 1024)));
|
||||
current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(1, 2048)));
|
||||
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(1, 1024)));
|
||||
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(1, 2048)));
|
||||
|
||||
// then
|
||||
assertEquals(Cadence.of(0), current.getValue());
|
||||
assertEquals(Cadence.of(0), current.getValue(Instant.MIN));
|
||||
}
|
||||
|
||||
|
||||
@@ -60,8 +62,8 @@ public class SensorDataCyclingTest {
|
||||
AggregatorCyclingCadence current = new AggregatorCyclingCadence("", "");
|
||||
|
||||
// when
|
||||
current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(1, 1024)));
|
||||
current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(2, 1024)));
|
||||
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(1, 1024)));
|
||||
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(2, 1024)));
|
||||
|
||||
// then
|
||||
assertFalse(current.hasValue()); //TODO Cadence should be 0?
|
||||
@@ -72,11 +74,11 @@ public class SensorDataCyclingTest {
|
||||
AggregatorCyclingCadence current = new AggregatorCyclingCadence("", "");
|
||||
|
||||
// when
|
||||
current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(1, UintUtils.UINT16_MAX - 1024)));
|
||||
current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(2, 0)));
|
||||
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(1, UintUtils.UINT16_MAX - 1024)));
|
||||
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(2, 0)));
|
||||
|
||||
// then
|
||||
assertEquals(60, current.getValue().getRPM(), 0.01);
|
||||
assertEquals(60, current.getValue(Instant.MIN).getRPM(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -85,13 +87,13 @@ public class SensorDataCyclingTest {
|
||||
AggregatorCyclingCadence current = new AggregatorCyclingCadence("", "");
|
||||
|
||||
// when
|
||||
current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(UintUtils.UINT32_MAX - 1, 1024)));
|
||||
current.add(new Raw<>(new BluetoothHandlerCyclingCadence.CrankData(0, 2048)));
|
||||
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(UintUtils.UINT32_MAX - 1, 1024)));
|
||||
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingCadence.CrankData(0, 2048)));
|
||||
|
||||
// then
|
||||
// TODO See #953
|
||||
// assertEquals(60, current.getValue().getRPM(), 0.01);
|
||||
assertNull(current.getValue());
|
||||
assertNull(current.getValue(Instant.MIN));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -100,12 +102,12 @@ public class SensorDataCyclingTest {
|
||||
current.setWheelCircumference(Distance.ofMM(2150));
|
||||
|
||||
// when
|
||||
current.add(new Raw<>(new BluetoothHandlerCyclingDistanceSpeed.WheelData(1, 6184)));
|
||||
current.add(new Raw<>(new BluetoothHandlerCyclingDistanceSpeed.WheelData(2, 8016)));
|
||||
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingDistanceSpeed.WheelData(1, 6184)));
|
||||
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingDistanceSpeed.WheelData(2, 8016)));
|
||||
|
||||
// then
|
||||
assertEquals(2.15, current.getValue().distance().toM(), 0.01);
|
||||
assertEquals(1.20, current.getValue().speed().toMPS(), 0.01);
|
||||
assertEquals(2.15, current.getValue(Instant.MIN).distance().toM(), 0.01);
|
||||
assertEquals(1.20, current.getValue(Instant.MIN).speed().toMPS(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -115,14 +117,14 @@ public class SensorDataCyclingTest {
|
||||
current.setWheelCircumference(Distance.ofMM(2000));
|
||||
|
||||
// when
|
||||
current.add(new Raw<>(new BluetoothHandlerCyclingDistanceSpeed.WheelData(UintUtils.UINT32_MAX - 1, 1024)));
|
||||
current.add(new Raw<>(new BluetoothHandlerCyclingDistanceSpeed.WheelData(0, 2048)));
|
||||
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingDistanceSpeed.WheelData(UintUtils.UINT32_MAX - 1, 1024)));
|
||||
current.add(new Raw<>(Instant.MIN, new BluetoothHandlerCyclingDistanceSpeed.WheelData(0, 2048)));
|
||||
|
||||
|
||||
// then
|
||||
// TODO See #953
|
||||
// assertEquals(2, current.getValue().getDistance().toM(), 0.01);
|
||||
// assertEquals(2, current.getValue().getSpeed().toMPS(), 0.01);
|
||||
assertNull(current.getValue());
|
||||
assertNull(current.getValue(Instant.MIN));
|
||||
}
|
||||
}
|
||||
+8
-8
@@ -304,11 +304,11 @@ public class TrackRecordingServiceRecordingTest {
|
||||
String sensor1 = "2020-02-02T02:02:03Z";
|
||||
trackPointCreator.setClock(sensor1);
|
||||
|
||||
sensorManager.onChanged(new Raw<>(HeartRate.of(5))); //Should be ignored
|
||||
sensorManager.onChanged(new Raw<>(sensor1, HeartRate.of(5))); //Should be ignored
|
||||
|
||||
String sensor3 = "2020-02-02T02:02:13Z";
|
||||
trackPointCreator.setClock(sensor3);
|
||||
sensorManager.onChanged(new Raw<>(HeartRate.of(7)));
|
||||
sensorManager.onChanged(new Raw<>(sensor3, HeartRate.of(7)));
|
||||
|
||||
String stopTime = "2020-02-02T02:02:15Z";
|
||||
trackPointCreator.setClock(stopTime);
|
||||
@@ -646,12 +646,12 @@ public class TrackRecordingServiceRecordingTest {
|
||||
// when
|
||||
String sensor1 = "2020-02-02T02:02:03Z";
|
||||
trackPointCreator.setClock(sensor1);
|
||||
sensorManager.onChanged(new Raw<>(new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(0)))); //Should be ignored
|
||||
sensorManager.onChanged(new Raw<>(sensor1, new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(0)))); //Should be ignored
|
||||
|
||||
// when
|
||||
String sensor2 = "2020-02-02T02:02:04Z";
|
||||
trackPointCreator.setClock(sensor2);
|
||||
sensorManager.onChanged(new Raw<>(new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(2))));
|
||||
sensorManager.onChanged(new Raw<>(sensor2, new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(2))));
|
||||
|
||||
// when
|
||||
String gps1 = "2020-02-02T02:02:05Z";
|
||||
@@ -660,12 +660,12 @@ public class TrackRecordingServiceRecordingTest {
|
||||
// when
|
||||
String sensor3 = "2020-02-02T02:02:06Z";
|
||||
trackPointCreator.setClock(sensor3);
|
||||
sensorManager.onChanged(new Raw<>(new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(12))));
|
||||
sensorManager.onChanged(new Raw<>(sensor3, new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(12))));
|
||||
|
||||
// when
|
||||
String sensor4 = "2020-02-02T02:02:07Z";
|
||||
trackPointCreator.setClock(sensor4);
|
||||
sensorManager.onChanged(new Raw<>(new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(14)))); //Should be ignored
|
||||
sensorManager.onChanged(new Raw<>(sensor4, new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(14)))); //Should be ignored
|
||||
|
||||
// when
|
||||
String gps2 = "2020-02-02T02:02:08Z";
|
||||
@@ -674,7 +674,7 @@ public class TrackRecordingServiceRecordingTest {
|
||||
// when
|
||||
String sensor5 = "2020-02-02T02:02:10Z";
|
||||
trackPointCreator.setClock(sensor5);
|
||||
sensorManager.onChanged(new Raw<>(new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(16)))); //Should be ignored
|
||||
sensorManager.onChanged(new Raw<>(sensor5, new BluetoothHandlerRunningSpeedAndCadence.Data(Speed.of(5), null, Distance.of(16)))); //Should be ignored
|
||||
|
||||
// when
|
||||
String gps3 = "2020-02-02T02:02:12Z";
|
||||
@@ -730,7 +730,7 @@ public class TrackRecordingServiceRecordingTest {
|
||||
private void mockAltitudeChange(TrackPointCreator trackPointCreator, float altitudeGain) {
|
||||
AggregatorBarometer barometer = Mockito.mock(AggregatorBarometer.class);
|
||||
Mockito.when(barometer.hasValue()).thenReturn(true);
|
||||
Mockito.when(barometer.getValue()).thenReturn(new AltitudeGainLoss(altitudeGain, altitudeGain));
|
||||
Mockito.when(barometer.getValue(Mockito.any())).thenReturn(new AltitudeGainLoss(altitudeGain, altitudeGain));
|
||||
|
||||
trackPointCreator.getSensorManager().sensorDataSet.barometer = barometer;
|
||||
}
|
||||
|
||||
+153
-34
@@ -1,9 +1,11 @@
|
||||
package de.dennisguse.opentracks.services.announcement;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
import android.content.Context;
|
||||
import android.icu.text.MessageFormat;
|
||||
import android.os.Build;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
@@ -13,8 +15,10 @@ import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -26,6 +30,7 @@ import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||
import de.dennisguse.opentracks.stats.SensorStatistics;
|
||||
@@ -45,6 +50,7 @@ public class VoiceAnnouncementUtilsTest {
|
||||
public void setUp() {
|
||||
contentProviderUtils = new ContentProviderUtils(context);
|
||||
|
||||
PreferencesUtils.setVoiceAnnounceHeartRateCurrent(false);
|
||||
PreferencesUtils.setVoiceAnnounceLapHeartRate(false);
|
||||
PreferencesUtils.setVoiceAnnounceAverageHeartRate(false);
|
||||
PreferencesUtils.setVoiceAnnounceTotalDistance(true);
|
||||
@@ -53,68 +59,101 @@ public class VoiceAnnouncementUtilsTest {
|
||||
PreferencesUtils.setVoiceAnnounceLapSpeedPace(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sdkRequirement() {
|
||||
assertFalse("Due to DateTimeFormatter using NBSP, these tests only work in SDK34+", Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_metric_speed() {
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setStartTime(Instant.EPOCH);
|
||||
stats.setTotalDistance(Distance.of(20000));
|
||||
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMaxSpeed(Speed.of(100));
|
||||
stats.setTotalAltitudeGain(6000f);
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, null, null).toString();
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.METRIC, true, null, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 20.0 kilometers. 1 hour 5 minutes 10 seconds. Average moving speed 18.4 kilometers per hour.", announcement);
|
||||
assertEquals("12:00 AM. Total distance 20.0 kilometers. 1 hour 5 minutes 10 seconds. Average moving speed 18.4 kilometers per hour.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_metric_speed_rounding_check() {
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setStartTime(Instant.EPOCH);
|
||||
stats.setTotalDistance(Distance.of(20000));
|
||||
stats.setTotalTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMovingTime(Duration.ofHours(1).plusSeconds(1));
|
||||
stats.setMaxSpeed(Speed.of(100));
|
||||
stats.setTotalAltitudeGain(6000f);
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, null, null).toString();
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.METRIC, true, null, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 20.0 kilometers. 1 hour 1 second. Average moving speed 20.0 kilometers per hour.", announcement);
|
||||
assertEquals("12:00 AM. Total distance 20.0 kilometers. 1 hour 1 second. Average moving speed 20.0 kilometers per hour.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_metric_distance_rounding_check() {
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setStartTime(Instant.EPOCH);
|
||||
stats.setTotalDistance(Distance.of(19999));
|
||||
stats.setTotalTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMovingTime(Duration.ofHours(1));
|
||||
stats.setMaxSpeed(Speed.of(100));
|
||||
stats.setTotalAltitudeGain(6000f);
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, null, null).toString();
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.METRIC, true, null, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 20.0 kilometers. 1 hour. Average moving speed 20.0 kilometers per hour.", announcement);
|
||||
assertEquals("12:00 AM. Total distance 20.0 kilometers. 1 hour. Average moving speed 20.0 kilometers per hour.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_metric_distance_rounding_check_two() {
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setStartTime(Instant.EPOCH);
|
||||
stats.setTotalDistance(Distance.of(19900));
|
||||
stats.setTotalTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMovingTime(Duration.ofHours(1));
|
||||
stats.setMaxSpeed(Speed.of(100));
|
||||
stats.setTotalAltitudeGain(6000f);
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, null, null).toString();
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.METRIC, true, null, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 19.9 kilometers. 1 hour. Average moving speed 19.9 kilometers per hour.", announcement);
|
||||
assertEquals("12:00 AM. Total distance 19.9 kilometers. 1 hour. Average moving speed 19.9 kilometers per hour.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -132,27 +171,40 @@ public class VoiceAnnouncementUtilsTest {
|
||||
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||
}
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, lastInterval, null).toString();
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.METRIC, true, lastInterval, null).toString();
|
||||
|
||||
// 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("12:16 AM. 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);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_metric_pace() {
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setStartTime(Instant.EPOCH);
|
||||
stats.setTotalDistance(Distance.of(20000));
|
||||
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMaxSpeed(Speed.of(100));
|
||||
stats.setTotalAltitudeGain(6000f);
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, false, null, null).toString();
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.METRIC, false, null, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 20.0 kilometers. 1 hour 5 minutes 10 seconds. Pace 3 minutes 15 seconds per kilometer.", announcement);
|
||||
assertEquals("12:00 AM. Total distance 20.0 kilometers. 1 hour 5 minutes 10 seconds. Pace 3 minutes 15 seconds per kilometer.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -170,69 +222,103 @@ public class VoiceAnnouncementUtilsTest {
|
||||
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||
}
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, false, lastInterval, null).toString();
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.METRIC, false, lastInterval, null).toString();
|
||||
|
||||
// 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("12:16 AM. 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);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_imperial_speed() {
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setStartTime(Instant.EPOCH);
|
||||
stats.setTotalDistance(Distance.of(20000));
|
||||
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMaxSpeed(Speed.of(100));
|
||||
stats.setTotalAltitudeGain(6000f);
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.IMPERIAL_FEET, true, null, null).toString();
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.IMPERIAL_FEET, true, null, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 12.4 miles. 1 hour 5 minutes 10 seconds. Average moving speed 11.4 miles per hour.", announcement);
|
||||
assertEquals("12:00 AM. Total distance 12.4 miles. 1 hour 5 minutes 10 seconds. Average moving speed 11.4 miles per hour.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_imperial_speed_1() {
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setStartTime(Instant.EPOCH);
|
||||
stats.setTotalDistance(Distance.ofMile(1.1));
|
||||
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMovingTime(Duration.ofHours(1));
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.IMPERIAL_FEET, true, null, null).toString();
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.IMPERIAL_FEET, true, null, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 1.1 miles. 1 hour. Average moving speed 1.1 miles per hour.", announcement);
|
||||
assertEquals("12:00 AM. Total distance 1.1 miles. 1 hour. Average moving speed 1.1 miles per hour.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_imperial_meter_speed_1() {
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setStartTime(Instant.EPOCH);
|
||||
stats.setTotalDistance(Distance.ofMile(1.1));
|
||||
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMovingTime(Duration.ofHours(1));
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.IMPERIAL_METER, true, null, null).toString();
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.IMPERIAL_METER, true, null, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 1.1 miles. 1 hour. Average moving speed 1.1 miles per hour.", announcement);
|
||||
assertEquals("12:00 AM. Total distance 1.1 miles. 1 hour. Average moving speed 1.1 miles per hour.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_metric_speed_1() {
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setStartTime(Instant.EPOCH);
|
||||
stats.setTotalDistance(Distance.ofKilometer(1.1));
|
||||
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMovingTime(Duration.ofHours(1));
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, null, null).toString();
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.METRIC, true, null, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 1.1 kilometers. 1 hour. Average moving speed 1.1 kilometers per hour.", announcement);
|
||||
assertEquals("12:00 AM. Total distance 1.1 kilometers. 1 hour. Average moving speed 1.1 kilometers per hour.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -250,27 +336,40 @@ public class VoiceAnnouncementUtilsTest {
|
||||
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||
}
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.IMPERIAL_FEET, true, lastInterval, null).toString();
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.IMPERIAL_FEET, true, lastInterval, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 8.8 miles. 16 minutes 39 seconds. Average moving speed 31.8 miles per hour. Lap speed 31.8 miles per hour.", announcement);
|
||||
assertEquals("12:16 AM. 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);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_imperial_pace() {
|
||||
TrackStatistics stats = new TrackStatistics();
|
||||
stats.setStartTime(Instant.EPOCH);
|
||||
stats.setTotalDistance(Distance.of(20000));
|
||||
stats.setTotalTime(Duration.ofHours(2).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMovingTime(Duration.ofHours(1).plusMinutes(5).plusSeconds(10));
|
||||
stats.setMaxSpeed(Speed.of(100));
|
||||
stats.setTotalAltitudeGain(6000f);
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.IMPERIAL_FEET, false, null, null).toString();
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.IMPERIAL_FEET, false, null, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 12.4 miles. 1 hour 5 minutes 10 seconds. Pace 5 minutes 15 seconds per mile.", announcement);
|
||||
assertEquals("12:00 AM. Total distance 12.4 miles. 1 hour 5 minutes 10 seconds. Pace 5 minutes 15 seconds per mile.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -288,11 +387,17 @@ public class VoiceAnnouncementUtilsTest {
|
||||
lastInterval = intervalStatistics.getIntervalList().get(intervalStatistics.getIntervalList().size() - 1);
|
||||
}
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.IMPERIAL_FEET, false, lastInterval, null).toString();
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.IMPERIAL_FEET, false, lastInterval, null).toString();
|
||||
|
||||
// then
|
||||
assertEquals("Total distance 8.8 miles. 16 minutes 39 seconds. Pace 1 minute 53 seconds per mile. Lap time 1 minute 53 seconds per mile.", announcement);
|
||||
assertEquals("12:16 AM. 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);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -314,17 +419,25 @@ public class VoiceAnnouncementUtilsTest {
|
||||
|
||||
SensorStatistics sensorStatistics = new SensorStatistics(HeartRate.of(180f), HeartRate.of(180f), null, null, null, null);
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, lastInterval, sensorStatistics).toString();
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.METRIC, true, lastInterval, sensorStatistics).toString();
|
||||
|
||||
// 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("12:16 AM. 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. Lap heart rate 133 bpm.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAnnouncement_only_lap_heart_rate() {
|
||||
public void getAnnouncement_heart_rate() {
|
||||
PreferencesUtils.setVoiceAnnounceHeartRateCurrent(true);
|
||||
PreferencesUtils.setVoiceAnnounceLapHeartRate(true);
|
||||
PreferencesUtils.setVoiceAnnounceAverageHeartRate(false);
|
||||
PreferencesUtils.setVoiceAnnounceAverageHeartRate(true);
|
||||
|
||||
PreferencesUtils.setVoiceAnnounceTotalDistance(false);
|
||||
PreferencesUtils.setVoiceAnnounceMovingTime(false);
|
||||
PreferencesUtils.setVoiceAnnounceAverageSpeedPace(false);
|
||||
@@ -344,11 +457,17 @@ public class VoiceAnnouncementUtilsTest {
|
||||
|
||||
SensorStatistics sensorStatistics = new SensorStatistics(HeartRate.of(180f), HeartRate.of(180f), null, null, null, null);
|
||||
|
||||
Track track = new Track();
|
||||
track.setTrackStatistics(stats);
|
||||
|
||||
SensorDataSet dataSet = Mockito.mock(SensorDataSet.class);
|
||||
Mockito.when(dataSet.getHeartRate()).thenReturn(new Pair<>(HeartRate.of(60), "unused"));
|
||||
|
||||
// when
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, stats, UnitSystem.METRIC, true, lastInterval, sensorStatistics).toString();
|
||||
String announcement = VoiceAnnouncementUtils.createStatistics(context, track, dataSet, UnitSystem.METRIC, true, lastInterval, sensorStatistics).toString();
|
||||
|
||||
// then
|
||||
assertEquals(" Current heart rate 133 bpm.", announcement);
|
||||
assertEquals("12:16 AM. Current heart rate 60 bpm. Average heart rate 180 bpm. Lap heart rate 133 bpm.", announcement);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -98,6 +98,14 @@ limitations under the License.
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".publicapi.CreateMarker"
|
||||
android:exported="true"
|
||||
android:theme="@style/SplashTheme">
|
||||
<intent-filter>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- NOTE: the name of the MAIN/LAUNCHER is used by icon packs; if this is changed, icon packs break.-->
|
||||
<activity
|
||||
@@ -401,6 +409,16 @@ limitations under the License.
|
||||
android:name=".services.TrackDeleteService"
|
||||
android:exported="false"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"/>
|
||||
|
||||
<!--Necessary for SDK < 33 to (re-)store per app locale information-->
|
||||
<service
|
||||
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
|
||||
android:enabled="false"
|
||||
android:exported="false">
|
||||
<meta-data
|
||||
android:name="autoStoreLocales"
|
||||
android:value="true" />
|
||||
</service>
|
||||
</application>
|
||||
|
||||
<queries>
|
||||
|
||||
@@ -487,12 +487,11 @@ public class ContentProviderUtils {
|
||||
return markers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the content provider URI of the inserted marker.
|
||||
*/
|
||||
public Uri insertMarker(@NonNull Marker marker) {
|
||||
// TODO Merge with updateMarker
|
||||
public Marker.Id insertMarker(@NonNull Marker marker) {
|
||||
marker.setId(null);
|
||||
return contentResolver.insert(MarkerColumns.CONTENT_URI, createContentValues(marker));
|
||||
Uri uri = contentResolver.insert(MarkerColumns.CONTENT_URI, createContentValues(marker));
|
||||
return new Marker.Id(ContentUris.parseId(uri));
|
||||
}
|
||||
|
||||
private void deleteMarkerPhoto(Context context, Marker marker) {
|
||||
|
||||
@@ -118,7 +118,13 @@ public class Track {
|
||||
}
|
||||
|
||||
public OffsetDateTime getStartTime() {
|
||||
return trackStatistics.getStartTime().atOffset(zoneOffset);
|
||||
return trackStatistics
|
||||
.getStartTime().atOffset(zoneOffset);
|
||||
}
|
||||
|
||||
public OffsetDateTime getStopTime() {
|
||||
return trackStatistics
|
||||
.getStopTime().atOffset(zoneOffset);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
|
||||
@@ -13,7 +13,7 @@ import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
public abstract class AbstractAPIActivity extends AppCompatActivity {
|
||||
abstract class AbstractAPIActivity extends AppCompatActivity {
|
||||
|
||||
private final String TAG = AbstractAPIActivity.class.getSimpleName();
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package de.dennisguse.opentracks.publicapi;
|
||||
|
||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
|
||||
public class CreateMarker extends AbstractAPIActivity {
|
||||
|
||||
@Override
|
||||
protected void execute(TrackRecordingService service) {
|
||||
service.createMarker();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isPostExecuteStopService() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import de.dennisguse.opentracks.ui.markers.MarkerEditActivity;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
|
||||
/**
|
||||
* Public API to creates a Marker for a given track with a given location
|
||||
* INTERNAL: only meant for clients of OSMDashboard API.
|
||||
*/
|
||||
public class CreateMarkerActivity extends AppCompatActivity {
|
||||
|
||||
@@ -23,8 +23,11 @@ public class CreateMarkerActivity extends AppCompatActivity {
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Track.Id trackId = new Track.Id(getIntent().getLongExtra(EXTRA_TRACK_ID, 0L));
|
||||
Track.Id trackId = new Track.Id(getIntent().getLongExtra(EXTRA_TRACK_ID, -1L));
|
||||
Location location = getIntent().getParcelableExtra(EXTRA_LOCATION);
|
||||
if (!getIntent().hasExtra(EXTRA_TRACK_ID) || location == null) {
|
||||
throw new IllegalStateException("Parameter 'track_id' and/or 'location' missing or invalid.");
|
||||
}
|
||||
|
||||
TrackRecordingServiceConnection.execute(this, (service, self) -> {
|
||||
Intent intent = IntentUtils
|
||||
|
||||
@@ -1,34 +1,19 @@
|
||||
package de.dennisguse.opentracks.publicapi;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentStatePagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.AbstractActivity;
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.models.Marker;
|
||||
import de.dennisguse.opentracks.databinding.MarkerDetailActivityBinding;
|
||||
import de.dennisguse.opentracks.ui.markers.DeleteMarkerDialogFragment.DeleteMarkerCaller;
|
||||
import de.dennisguse.opentracks.ui.markers.MarkerDetailActivity;
|
||||
import de.dennisguse.opentracks.ui.markers.MarkerDetailFragment;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
|
||||
/**
|
||||
* Public api to show an existing marker
|
||||
* INTERNAL: only meant for clients of OSMDashboard API.
|
||||
*/
|
||||
public class ShowMarkerActivity extends AppCompatActivity {
|
||||
|
||||
@@ -44,10 +29,11 @@ public class ShowMarkerActivity extends AppCompatActivity {
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
|
||||
Marker.Id markerId = new Marker.Id(getIntent().getLongExtra(EXTRA_MARKER_ID, 0));
|
||||
if (markerId.id() == 0) {
|
||||
throw new IllegalStateException("No valid markerId provided");
|
||||
if (!getIntent().hasExtra(EXTRA_MARKER_ID)) {
|
||||
throw new IllegalStateException("Parameter 'markerId' missing");
|
||||
}
|
||||
|
||||
Marker.Id markerId = new Marker.Id(getIntent().getLongExtra(EXTRA_MARKER_ID, -1));
|
||||
Intent intent = IntentUtils.newIntent(this, MarkerDetailActivity.class)
|
||||
.putExtra(MarkerDetailActivity.EXTRA_MARKER_ID, markerId);
|
||||
startActivity(intent);
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ public class BluetoothHandlerBarometricPressure implements SensorHandlerInterfac
|
||||
AtmosphericPressure value = parseEnvironmentalSensing(characteristic);
|
||||
if (value == null) return;
|
||||
|
||||
observer.onChange(new Raw<>(value));
|
||||
observer.onChange(new Raw<>(observer.getNow(), value));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,7 +33,7 @@ public class BluetoothHandlerCyclingCadence implements SensorHandlerInterface {
|
||||
if (serviceMeasurementUUID.equals(BluetoothHandlerManagerCyclingPower.CYCLING_POWER)) {
|
||||
BluetoothHandlerManagerCyclingPower.Data data = BluetoothHandlerManagerCyclingPower.parseCyclingPower(characteristic);
|
||||
if (data != null && data.crank() != null) {
|
||||
observer.onChange(new Raw<>(data.crank()));
|
||||
observer.onChange(new Raw<>(observer.getNow(), data.crank()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -42,7 +42,7 @@ public class BluetoothHandlerCyclingCadence implements SensorHandlerInterface {
|
||||
Pair<BluetoothHandlerCyclingDistanceSpeed.WheelData, CrankData> data = BluetoothHandlerCyclingDistanceSpeed.parseCyclingCrankAndWheel(address, sensorName, characteristic);
|
||||
|
||||
if (data != null && data.second != null) {
|
||||
observer.onChange(new Raw<>(data.second));
|
||||
observer.onChange(new Raw<>(observer.getNow(), data.second));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ public class BluetoothHandlerCyclingDistanceSpeed implements SensorHandlerInterf
|
||||
public void handlePayload(SensorManager.SensorDataChangedObserver observer, ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) {
|
||||
Pair<WheelData, BluetoothHandlerCyclingCadence.CrankData> data = parseCyclingCrankAndWheel(address, sensorName, characteristic);
|
||||
if (data.first != null) {
|
||||
observer.onChange(new Raw<>(data.first));
|
||||
observer.onChange(new Raw<>(observer.getNow(), data.first));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ public class BluetoothHandlerManagerCyclingPower implements SensorHandlerInterfa
|
||||
Data cyclingPower = parseCyclingPower(characteristic);
|
||||
|
||||
if (cyclingPower != null) {
|
||||
observer.onChange(new Raw<>(cyclingPower));
|
||||
observer.onChange(new Raw<>(observer.getNow(), cyclingPower));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ public class BluetoothHandlerManagerHeartRate implements SensorHandlerInterface
|
||||
HeartRate heartRate = parseHeartRate(characteristic);
|
||||
|
||||
if (heartRate != null) {
|
||||
observer.onChange(new Raw<>(heartRate));
|
||||
observer.onChange(new Raw<>(observer.getNow(), heartRate));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ public class BluetoothHandlerRunningSpeedAndCadence implements SensorHandlerInte
|
||||
@Override
|
||||
public void handlePayload(SensorManager.SensorDataChangedObserver observer, @NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) {
|
||||
Data data = parseRunningSpeedAndCadence(sensorName, characteristic);
|
||||
observer.onChange(new Raw<>(data));
|
||||
observer.onChange(new Raw<>(observer.getNow(), data));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
||||
@@ -8,6 +8,8 @@ import android.util.Log;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.Aggregator;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.Raw;
|
||||
@@ -21,7 +23,7 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
|
||||
|
||||
//TODO Should be final and not be visible for testing
|
||||
@VisibleForTesting
|
||||
public SensorDataSet sensorDataSet = new SensorDataSet();
|
||||
public SensorDataSet sensorDataSet;
|
||||
|
||||
private final TrackPointCreator observer;
|
||||
|
||||
@@ -47,6 +49,11 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
|
||||
public void onRemove(Aggregator<?, ?> aggregator) {
|
||||
sensorDataSet.remove(aggregator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Instant getNow() {
|
||||
return observer.createNow();
|
||||
}
|
||||
};
|
||||
|
||||
private BluetoothRemoteSensorManager bluetoothSensorManager;
|
||||
@@ -57,6 +64,7 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
|
||||
|
||||
public SensorManager(TrackPointCreator observer) {
|
||||
this.observer = observer;
|
||||
this.sensorDataSet = new SensorDataSet(observer);
|
||||
}
|
||||
|
||||
public void start(Context context, Handler handler) {
|
||||
@@ -138,5 +146,7 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
|
||||
void onDisconnect(Aggregator<?, ?> sensorData);
|
||||
|
||||
void onRemove(Aggregator<?, ?> sensorData);
|
||||
|
||||
Instant getNow();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ public class BarometerInternal implements Driver {
|
||||
|
||||
private static final int SAMPLING_PERIOD = (int) TimeUnit.SECONDS.toMicros(5);
|
||||
|
||||
private final SensorManager.SensorDataChangedObserver listener;
|
||||
private final SensorManager.SensorDataChangedObserver observer;
|
||||
|
||||
private Context context;
|
||||
|
||||
@@ -34,7 +34,7 @@ public class BarometerInternal implements Driver {
|
||||
return;
|
||||
}
|
||||
|
||||
listener.onChange(new Raw<>(AtmosphericPressure.ofHPA(event.values[0])));
|
||||
observer.onChange(new Raw<>(observer.getNow(), AtmosphericPressure.ofHPA(event.values[0])));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -43,8 +43,8 @@ public class BarometerInternal implements Driver {
|
||||
}
|
||||
};
|
||||
|
||||
public BarometerInternal(@NonNull SensorManager.SensorDataChangedObserver listener) {
|
||||
this.listener = listener;
|
||||
public BarometerInternal(@NonNull SensorManager.SensorDataChangedObserver observer) {
|
||||
this.observer = observer;
|
||||
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ public class BarometerInternal implements Driver {
|
||||
|
||||
if (sensorManager.registerListener(sensorEventListener, pressureSensor, SAMPLING_PERIOD, handler)) {
|
||||
this.context = context;
|
||||
listener.onConnect(new AggregatorBarometer("internal", null));
|
||||
observer.onConnect(new AggregatorBarometer("internal", null));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,11 +42,11 @@ public abstract class Aggregator<Input, Output> {
|
||||
@NonNull
|
||||
protected abstract Output getNoneValue();
|
||||
|
||||
public Output getValue() {
|
||||
public Output getValue(Instant now) {
|
||||
if (!hasValue()) {
|
||||
return null; //TODO Check if this is a good idea!
|
||||
}
|
||||
if (isRecent()) {
|
||||
if (isRecent(now)) {
|
||||
return value;
|
||||
}
|
||||
return getNoneValue();
|
||||
@@ -60,12 +60,12 @@ public abstract class Aggregator<Input, Output> {
|
||||
/**
|
||||
* Is the data recent considering the current time.
|
||||
*/
|
||||
private boolean isRecent() {
|
||||
private boolean isRecent(Instant now) {
|
||||
if (previous == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Instant.now()
|
||||
return now
|
||||
.isBefore(previous.time().plus(BluetoothRemoteSensorManager.MAX_SENSOR_DATE_SET_AGE));
|
||||
}
|
||||
|
||||
|
||||
@@ -2,15 +2,21 @@ package de.dennisguse.opentracks.sensors.sensorData;
|
||||
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.time.Instant;
|
||||
|
||||
public record Raw<T>(
|
||||
@NonNull T value,
|
||||
|
||||
@NonNull Instant time
|
||||
@NonNull Instant time,
|
||||
@NonNull T value
|
||||
) {
|
||||
public Raw(@NonNull T value) {
|
||||
this(value, Instant.now()); //TODO We should be using the MonotonicClock
|
||||
public Raw(@NonNull Clock clock, @NonNull T value) {
|
||||
this(clock.instant(), value);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public Raw(@NonNull String time, @NonNull T value) {
|
||||
this(Instant.parse(time), value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,10 @@ import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingCadence;
|
||||
import de.dennisguse.opentracks.sensors.BluetoothHandlerCyclingDistanceSpeed;
|
||||
import de.dennisguse.opentracks.sensors.BluetoothHandlerManagerCyclingPower;
|
||||
import de.dennisguse.opentracks.sensors.BluetoothHandlerRunningSpeedAndCadence;
|
||||
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
public final class SensorDataSet {
|
||||
public class SensorDataSet {
|
||||
|
||||
private static final String TAG = SensorDataSet.class.getSimpleName();
|
||||
|
||||
@@ -43,11 +44,15 @@ public final class SensorDataSet {
|
||||
|
||||
public AggregatorGPS gps;
|
||||
|
||||
public SensorDataSet() {
|
||||
private TrackPointCreator trackPointCreator;
|
||||
|
||||
public SensorDataSet(TrackPointCreator trackPointCreator) {
|
||||
this.trackPointCreator = trackPointCreator;
|
||||
}
|
||||
|
||||
@Deprecated //TODO This is not a copy constructor anymore, but it should be - aggregators are no value objects; best guess: can be removed
|
||||
public SensorDataSet(SensorDataSet toCopy) {
|
||||
//TODO This is not a copy constructor anymore, but it should be - aggregators are no value objects
|
||||
this.trackPointCreator = toCopy.trackPointCreator;
|
||||
this.heartRate = toCopy.heartRate;
|
||||
this.cyclingCadence = toCopy.cyclingCadence;
|
||||
this.cyclingDistanceSpeed = toCopy.cyclingDistanceSpeed;
|
||||
@@ -59,7 +64,7 @@ public final class SensorDataSet {
|
||||
|
||||
public Pair<HeartRate, String> getHeartRate() {
|
||||
if (heartRate != null) {
|
||||
return new Pair<>(heartRate.getValue(), heartRate.getSensorNameOrAddress());
|
||||
return new Pair<>(heartRate.getValue(trackPointCreator.createNow()), heartRate.getSensorNameOrAddress());
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -67,7 +72,7 @@ public final class SensorDataSet {
|
||||
|
||||
public Pair<Cadence, String> getCadence() {
|
||||
if (cyclingCadence != null) {
|
||||
return new Pair<>(cyclingCadence.getValue(), cyclingCadence.getSensorNameOrAddress());
|
||||
return new Pair<>(cyclingCadence.getValue(trackPointCreator.createNow()), cyclingCadence.getSensorNameOrAddress());
|
||||
}
|
||||
|
||||
if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasValue() && runningDistanceSpeedCadence.value.cadence() != null) {
|
||||
@@ -78,11 +83,11 @@ public final class SensorDataSet {
|
||||
}
|
||||
|
||||
public Pair<Speed, String> getSpeed() {
|
||||
if (cyclingDistanceSpeed != null && cyclingDistanceSpeed.hasValue() && cyclingDistanceSpeed.getValue().speed() != null) {
|
||||
return new Pair<>(cyclingDistanceSpeed.getValue().speed(), cyclingDistanceSpeed.getSensorNameOrAddress());
|
||||
if (cyclingDistanceSpeed != null && cyclingDistanceSpeed.hasValue() && cyclingDistanceSpeed.getValue(trackPointCreator.createNow()).speed() != null) {
|
||||
return new Pair<>(cyclingDistanceSpeed.getValue(trackPointCreator.createNow()).speed(), cyclingDistanceSpeed.getSensorNameOrAddress());
|
||||
}
|
||||
|
||||
if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasValue() && runningDistanceSpeedCadence.getValue().speed() != null) {
|
||||
if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasValue() && runningDistanceSpeedCadence.getValue(trackPointCreator.createNow()).speed() != null) {
|
||||
return new Pair<>(runningDistanceSpeedCadence.value.speed(), runningDistanceSpeedCadence.getSensorNameOrAddress());
|
||||
}
|
||||
|
||||
@@ -152,7 +157,7 @@ public final class SensorDataSet {
|
||||
|
||||
public void fillTrackPoint(TrackPoint trackPoint) {
|
||||
if (gps != null && gps.hasValue()) {
|
||||
trackPoint.setPosition(gps.getValue());
|
||||
trackPoint.setPosition(gps.getValue(trackPointCreator.createNow()));
|
||||
}
|
||||
|
||||
if (getHeartRate() != null) {
|
||||
@@ -168,20 +173,20 @@ public final class SensorDataSet {
|
||||
}
|
||||
|
||||
if (cyclingDistanceSpeed != null && cyclingDistanceSpeed.hasValue()) {
|
||||
trackPoint.setSensorDistance(cyclingDistanceSpeed.getValue().distanceOverall());
|
||||
trackPoint.setSensorDistance(cyclingDistanceSpeed.getValue(trackPointCreator.createNow()).distanceOverall());
|
||||
}
|
||||
|
||||
if (cyclingPower != null && cyclingPower.hasValue()) {
|
||||
trackPoint.setPower(cyclingPower.getValue());
|
||||
trackPoint.setPower(cyclingPower.getValue(trackPointCreator.createNow()));
|
||||
}
|
||||
|
||||
if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasValue()) {
|
||||
trackPoint.setSensorDistance(runningDistanceSpeedCadence.getValue().distance());
|
||||
trackPoint.setSensorDistance(runningDistanceSpeedCadence.getValue(trackPointCreator.createNow()).distance());
|
||||
}
|
||||
|
||||
if (barometer != null && barometer.hasValue()) {
|
||||
trackPoint.setAltitudeGain(barometer.getValue().gain_m());
|
||||
trackPoint.setAltitudeLoss(barometer.getValue().loss_m());
|
||||
trackPoint.setAltitudeGain(barometer.getValue(trackPointCreator.createNow()).gain_m());
|
||||
trackPoint.setAltitudeLoss(barometer.getValue(trackPointCreator.createNow()).loss_m());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,9 @@ import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.time.Duration;
|
||||
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.Marker;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
|
||||
@@ -243,6 +245,20 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
||||
gpsStatusObservable.postValue(STATUS_GPS_DEFAULT);
|
||||
}
|
||||
|
||||
public Marker.Id createMarker() {
|
||||
if (!isRecording()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//TODO This contains some duplication to TrackRecodingActivity's Marker creation
|
||||
TrackPoint trackPoint = trackRecordingManager.getLastStoredTrackPointWithLocation();
|
||||
if (trackPoint == null) {
|
||||
return null;
|
||||
}
|
||||
Marker marker = new Marker(recordingStatus.trackId(), trackPoint);
|
||||
return new ContentProviderUtils(this).insertMarker(marker);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean newTrackPoint(TrackPoint trackPoint, Distance thresholdHorizontalAccuracy) {
|
||||
if (!isRecording()) {
|
||||
@@ -301,7 +317,7 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
||||
// Compute temporary track statistics using sensorData and update time.
|
||||
Pair<Track, Pair<TrackPoint, SensorDataSet>> data = trackRecordingManager.getDataForUI();
|
||||
|
||||
voiceAnnouncementManager.announceStatisticsIfNeeded(data.first);
|
||||
voiceAnnouncementManager.announceStatisticsIfNeeded(data.first, data.second.second);
|
||||
|
||||
recordingDataObservable.postValue(new RecordingData(data.first, data.second.first, data.second.second));
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.speech.tts.TextToSpeech;
|
||||
import android.speech.tts.UtteranceProgressListener;
|
||||
import android.text.Spannable;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -118,7 +117,7 @@ public class TTSManager {
|
||||
}
|
||||
}
|
||||
|
||||
public void announce(@NonNull Spannable announcement) {
|
||||
public void speak(@NonNull CharSequence announcement) {
|
||||
synchronized (this) {
|
||||
if (!ttsReady) {
|
||||
ttsReady = ttsInitStatus == TextToSpeech.SUCCESS;
|
||||
|
||||
+6
-5
@@ -33,6 +33,7 @@ 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.sensors.sensorData.SensorDataSet;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.stats.SensorStatistics;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
@@ -115,10 +116,10 @@ public class VoiceAnnouncementManager implements SharedPreferences.OnSharedPrefe
|
||||
return;
|
||||
}
|
||||
|
||||
voiceAnnouncement.announce(VoiceAnnouncementUtils.createIdle(context));
|
||||
voiceAnnouncement.speak(VoiceAnnouncementUtils.createIdle(context));
|
||||
}
|
||||
|
||||
public void announceStatisticsIfNeeded(@NonNull Track track) {
|
||||
public void announceStatisticsIfNeeded(@NonNull Track track, @NonNull SensorDataSet sensorDataSet) {
|
||||
if (shouldNotAnnounce()) {
|
||||
return;
|
||||
}
|
||||
@@ -135,11 +136,11 @@ public class VoiceAnnouncementManager implements SharedPreferences.OnSharedPrefe
|
||||
}
|
||||
|
||||
if (announce) {
|
||||
voiceAnnouncement.announce(createAnnouncement(track));
|
||||
voiceAnnouncement.speak(createAnnouncement(track, sensorDataSet));
|
||||
}
|
||||
}
|
||||
|
||||
private Spannable createAnnouncement(Track track) {
|
||||
private Spannable createAnnouncement(Track track, SensorDataSet sensorDataSet) {
|
||||
Distance currentIntervalDistance = PreferencesUtils.getVoiceAnnouncementDistance();
|
||||
if (currentIntervalDistance != intervalDistance) {
|
||||
intervalStatistics = new IntervalStatistics(currentIntervalDistance);
|
||||
@@ -155,7 +156,7 @@ public class VoiceAnnouncementManager implements SharedPreferences.OnSharedPrefe
|
||||
sensorStatistics = contentProviderUtils.getSensorStats(track.getId());
|
||||
}
|
||||
|
||||
return VoiceAnnouncementUtils.createStatistics(context, track.getTrackStatistics(), PreferencesUtils.getUnitSystem(), PreferencesUtils.isReportSpeed(track), lastInterval, sensorStatistics);
|
||||
return VoiceAnnouncementUtils.createStatistics(context, track, sensorDataSet, PreferencesUtils.getUnitSystem(), PreferencesUtils.isReportSpeed(track), lastInterval, sensorStatistics);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
|
||||
+101
-60
@@ -3,29 +3,38 @@ package de.dennisguse.opentracks.services.announcement;
|
||||
import static android.text.Spanned.SPAN_INCLUSIVE_EXCLUSIVE;
|
||||
import static de.dennisguse.opentracks.settings.PreferencesUtils.shouldVoiceAnnounceAverageHeartRate;
|
||||
import static de.dennisguse.opentracks.settings.PreferencesUtils.shouldVoiceAnnounceAverageSpeedPace;
|
||||
import static de.dennisguse.opentracks.settings.PreferencesUtils.shouldVoiceAnnounceHeartRateCurrent;
|
||||
import static de.dennisguse.opentracks.settings.PreferencesUtils.shouldVoiceAnnounceLapHeartRate;
|
||||
import static de.dennisguse.opentracks.settings.PreferencesUtils.shouldVoiceAnnounceLapPower;
|
||||
import static de.dennisguse.opentracks.settings.PreferencesUtils.shouldVoiceAnnounceLapSpeedPace;
|
||||
import static de.dennisguse.opentracks.settings.PreferencesUtils.shouldVoiceAnnounceMovingTime;
|
||||
import static de.dennisguse.opentracks.settings.PreferencesUtils.shouldVoiceAnnounceTime;
|
||||
import static de.dennisguse.opentracks.settings.PreferencesUtils.shouldVoiceAnnounceTotalDistance;
|
||||
import static de.dennisguse.opentracks.settings.PreferencesUtils.shouldVoiceAnnounceUnit;
|
||||
|
||||
import android.content.Context;
|
||||
import android.icu.text.MessageFormat;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.style.TtsSpan;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.FormatStyle;
|
||||
import java.util.Map;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.Power;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
|
||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||
import de.dennisguse.opentracks.stats.SensorStatistics;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
@@ -42,8 +51,15 @@ class VoiceAnnouncementUtils {
|
||||
.append(context.getString(R.string.voiceIdle));
|
||||
}
|
||||
|
||||
static Spannable createStatistics(Context context, TrackStatistics trackStatistics, UnitSystem unitSystem, boolean isReportSpeed, @Nullable IntervalStatistics.Interval currentInterval, @Nullable SensorStatistics sensorStatistics) {
|
||||
static Spannable createStatistics(Context context, Track track, SensorDataSet sensorDataSet, UnitSystem unitSystem, boolean isReportSpeed, @Nullable IntervalStatistics.Interval currentInterval, @Nullable SensorStatistics sensorStatistics) {
|
||||
TrackStatistics trackStatistics = track.getTrackStatistics();
|
||||
|
||||
SpannableStringBuilder builder = new SpannableStringBuilder();
|
||||
|
||||
if (shouldVoiceAnnounceTime()) {
|
||||
appendTime(builder, track.getStopTime());
|
||||
}
|
||||
|
||||
Distance totalDistance = trackStatistics.getTotalDistance();
|
||||
Speed averageMovingSpeed = trackStatistics.getAverageMovingSpeed();
|
||||
Speed currentDistancePerTime = currentInterval != null ? currentInterval.getSpeed() : null;
|
||||
@@ -92,88 +108,99 @@ class VoiceAnnouncementUtils {
|
||||
// Punctuation helps introduce natural pauses in TTS
|
||||
builder.append(".");
|
||||
}
|
||||
if (totalDistance.isZero()) {
|
||||
return builder;
|
||||
}
|
||||
|
||||
boolean hasTravelledDistance = !totalDistance.isZero();
|
||||
|
||||
// Announce time
|
||||
Duration movingTime = trackStatistics.getMovingTime();
|
||||
if (shouldVoiceAnnounceMovingTime() && !movingTime.isZero()) {
|
||||
if (shouldVoiceAnnounceMovingTime() && hasTravelledDistance && !movingTime.isZero()) {
|
||||
appendDuration(context, builder, movingTime);
|
||||
builder.append(".");
|
||||
}
|
||||
|
||||
if (isReportSpeed) {
|
||||
if (shouldVoiceAnnounceAverageSpeedPace()) {
|
||||
double speedInUnit = averageMovingSpeed.to(unitSystem);
|
||||
builder.append(" ")
|
||||
.append(context.getString(R.string.speed));
|
||||
String template = context.getResources().getString(speedId);
|
||||
appendDecimalUnit(builder, MessageFormat.format(template, Map.of("n", speedInUnit)), speedInUnit, 1, unitSpeedTTS);
|
||||
builder.append(".");
|
||||
}
|
||||
if (shouldVoiceAnnounceLapSpeedPace() && currentDistancePerTime != null) {
|
||||
double currentDistancePerTimeInUnit = currentDistancePerTime.to(unitSystem);
|
||||
if (currentDistancePerTimeInUnit > 0) {
|
||||
if (hasTravelledDistance) {
|
||||
if (isReportSpeed) {
|
||||
if (shouldVoiceAnnounceAverageSpeedPace()) {
|
||||
double speedInUnit = averageMovingSpeed.to(unitSystem);
|
||||
builder.append(" ")
|
||||
.append(context.getString(R.string.lap_speed));
|
||||
.append(context.getString(R.string.speed));
|
||||
String template = context.getResources().getString(speedId);
|
||||
appendDecimalUnit(builder, MessageFormat.format(template, Map.of("n", currentDistancePerTimeInUnit)), currentDistancePerTimeInUnit, 1, unitSpeedTTS);
|
||||
appendDecimalUnit(builder, MessageFormat.format(template, Map.of("n", speedInUnit)), speedInUnit, 1, unitSpeedTTS);
|
||||
builder.append(".");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (shouldVoiceAnnounceAverageSpeedPace()) {
|
||||
Duration time = averageMovingSpeed.toPace(unitSystem);
|
||||
builder.append(" ")
|
||||
.append(context.getString(R.string.pace));
|
||||
appendDuration(context, builder, time);
|
||||
builder.append(" ")
|
||||
.append(context.getString(perUnitStringId))
|
||||
.append(".");
|
||||
}
|
||||
if (shouldVoiceAnnounceLapSpeedPace() && currentDistancePerTime != null) {
|
||||
double currentDistancePerTimeInUnit = currentDistancePerTime.to(unitSystem);
|
||||
if (currentDistancePerTimeInUnit > 0) {
|
||||
builder.append(" ")
|
||||
.append(context.getString(R.string.lap_speed));
|
||||
String template = context.getResources().getString(speedId);
|
||||
appendDecimalUnit(builder, MessageFormat.format(template, Map.of("n", currentDistancePerTimeInUnit)), currentDistancePerTimeInUnit, 1, unitSpeedTTS);
|
||||
builder.append(".");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (shouldVoiceAnnounceAverageSpeedPace()) {
|
||||
Duration time = averageMovingSpeed.toPace(unitSystem);
|
||||
builder.append(" ")
|
||||
.append(context.getString(R.string.pace));
|
||||
appendDuration(context, builder, time);
|
||||
builder.append(" ")
|
||||
.append(context.getString(perUnitStringId))
|
||||
.append(".");
|
||||
}
|
||||
|
||||
if (shouldVoiceAnnounceLapSpeedPace() && currentDistancePerTime != null) {
|
||||
Duration currentTime = currentDistancePerTime.toPace(unitSystem);
|
||||
builder.append(" ")
|
||||
.append(context.getString(R.string.lap_time));
|
||||
appendDuration(context, builder, currentTime);
|
||||
builder.append(" ")
|
||||
.append(context.getString(perUnitStringId))
|
||||
.append(".");
|
||||
if (shouldVoiceAnnounceLapSpeedPace() && currentDistancePerTime != null) {
|
||||
Duration currentTime = currentDistancePerTime.toPace(unitSystem);
|
||||
builder.append(" ")
|
||||
.append(context.getString(R.string.lap_time));
|
||||
appendDuration(context, builder, currentTime);
|
||||
builder.append(" ")
|
||||
.append(context.getString(perUnitStringId))
|
||||
.append(".");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldVoiceAnnounceAverageHeartRate() && sensorStatistics != null && sensorStatistics.hasHeartRate()) {
|
||||
int averageHeartRate = Math.round(sensorStatistics.avgHeartRate().getBPM());
|
||||
|
||||
builder.append(" ")
|
||||
.append(context.getString(R.string.average_heart_rate));
|
||||
appendCardinal(builder, context.getString(R.string.sensor_state_heart_rate_value, averageHeartRate), averageHeartRate);
|
||||
builder.append(".");
|
||||
}
|
||||
if (shouldVoiceAnnounceLapHeartRate() && currentInterval != null && currentInterval.hasAverageHeartRate()) {
|
||||
int currentHeartRate = Math.round(currentInterval.getAverageHeartRate().getBPM());
|
||||
Pair<HeartRate, String> heartrate = sensorDataSet.getHeartRate();
|
||||
if (shouldVoiceAnnounceHeartRateCurrent() && heartrate != null && heartrate.first != null) { //TODO Check has an announcable value?
|
||||
int averageHeartRate = Math.round(heartrate.first.getBPM());
|
||||
|
||||
builder.append(" ")
|
||||
.append(context.getString(R.string.current_heart_rate));
|
||||
appendCardinal(builder, context.getString(R.string.sensor_state_heart_rate_value, currentHeartRate), currentHeartRate);
|
||||
appendCardinal(builder, context.getString(R.string.sensor_state_heart_rate_value, averageHeartRate), averageHeartRate);
|
||||
builder.append(".");
|
||||
}
|
||||
if (hasTravelledDistance) {
|
||||
if (shouldVoiceAnnounceAverageHeartRate() && sensorStatistics != null && sensorStatistics.hasHeartRate()) {
|
||||
int averageHeartRate = Math.round(sensorStatistics.avgHeartRate().getBPM());
|
||||
|
||||
if (shouldVoiceAnnounceLapPower() && currentInterval != null && currentInterval.hasAveragePower()) {
|
||||
int currentPower = Math.round(currentInterval.getAveragePower().getW());
|
||||
if(shouldVoiceAnnounceUnit()) {
|
||||
String template = context.getResources().getString(R.string.power_x_watt);
|
||||
builder.append(" ")
|
||||
.append(MessageFormat.format(template, Map.of("x", currentPower)));
|
||||
} else {
|
||||
builder.append(" ").append(context.getString(R.string.power)).append(" ");
|
||||
builder.append(String.valueOf(currentPower));
|
||||
.append(context.getString(R.string.average_heart_rate));
|
||||
appendCardinal(builder, context.getString(R.string.sensor_state_heart_rate_value, averageHeartRate), averageHeartRate);
|
||||
builder.append(".");
|
||||
}
|
||||
builder.append(".");
|
||||
}
|
||||
if (shouldVoiceAnnounceLapHeartRate() && currentInterval != null && currentInterval.hasAverageHeartRate()) {
|
||||
int currentHeartRate = Math.round(currentInterval.getAverageHeartRate().getBPM());
|
||||
|
||||
builder.append(" ")
|
||||
.append(context.getString(R.string.lap_heart_rate));
|
||||
appendCardinal(builder, context.getString(R.string.sensor_state_heart_rate_value, currentHeartRate), currentHeartRate);
|
||||
builder.append(".");
|
||||
}
|
||||
|
||||
if (shouldVoiceAnnounceLapPower() && currentInterval != null && currentInterval.hasAveragePower()) {
|
||||
int currentPower = Math.round(currentInterval.getAveragePower().getW());
|
||||
if (shouldVoiceAnnounceUnit()) {
|
||||
String template = context.getResources().getString(R.string.power_x_watt);
|
||||
builder.append(" ")
|
||||
.append(MessageFormat.format(template, Map.of("x", currentPower)));
|
||||
} else {
|
||||
builder.append(" ").append(context.getString(R.string.power)).append(" ");
|
||||
builder.append(String.valueOf(currentPower));
|
||||
}
|
||||
builder.append(".");
|
||||
}
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
@@ -196,6 +223,20 @@ class VoiceAnnouncementUtils {
|
||||
}
|
||||
}
|
||||
|
||||
private static void appendTime(@NonNull SpannableStringBuilder builder, OffsetDateTime now) {
|
||||
TtsSpan.TimeBuilder ttsSpan = new TtsSpan.TimeBuilder();
|
||||
ttsSpan.setHours(now.getHour());
|
||||
ttsSpan.setMinutes(now.getMinute());
|
||||
|
||||
String formattedTime = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT)
|
||||
.format(now);
|
||||
|
||||
Spannable spannable = new SpannableString(formattedTime + ".");
|
||||
spannable.setSpan(ttsSpan.build(), 0, spannable.length(), SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
builder.append(spannable);
|
||||
builder.append(" ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Speaks as: 98.14 [UNIT] - ninety eight point one four [UNIT with correct plural form]
|
||||
*
|
||||
|
||||
@@ -37,7 +37,7 @@ public class GPSManager implements SensorConnector, LocationListenerCompat, GpsS
|
||||
|
||||
private TrackPointCreator trackPointCreator;
|
||||
|
||||
private SensorManager.SensorDataChangedObserver listener;
|
||||
private SensorManager.SensorDataChangedObserver observer;
|
||||
private Context context;
|
||||
private Handler handler;
|
||||
|
||||
@@ -46,9 +46,9 @@ public class GPSManager implements SensorConnector, LocationListenerCompat, GpsS
|
||||
private Duration gpsInterval;
|
||||
private Distance thresholdHorizontalAccuracy;
|
||||
|
||||
public GPSManager(TrackPointCreator trackPointCreator, SensorManager.SensorDataChangedObserver listener) {
|
||||
public GPSManager(TrackPointCreator trackPointCreator, SensorManager.SensorDataChangedObserver observer) {
|
||||
this.trackPointCreator = trackPointCreator;
|
||||
this.listener = listener;
|
||||
this.observer = observer;
|
||||
}
|
||||
|
||||
public void start(@NonNull Context context, @NonNull Handler handler) {
|
||||
@@ -60,7 +60,7 @@ public class GPSManager implements SensorConnector, LocationListenerCompat, GpsS
|
||||
gpsStatusManager = new GpsStatusManager(context, this, handler);
|
||||
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
|
||||
|
||||
listener.onConnect(new AggregatorGPS("internal"));
|
||||
observer.onConnect(new AggregatorGPS("internal"));
|
||||
|
||||
registerLocationListener();
|
||||
gpsStatusManager.start();
|
||||
@@ -82,8 +82,8 @@ public class GPSManager implements SensorConnector, LocationListenerCompat, GpsS
|
||||
gpsStatusManager.stop();
|
||||
gpsStatusManager = null;
|
||||
|
||||
listener.onDisconnect(new AggregatorGPS("internal"));
|
||||
listener = null;
|
||||
observer.onDisconnect(new AggregatorGPS("internal"));
|
||||
observer = null;
|
||||
|
||||
trackPointCreator = null;
|
||||
}
|
||||
@@ -139,7 +139,7 @@ public class GPSManager implements SensorConnector, LocationListenerCompat, GpsS
|
||||
return;
|
||||
}
|
||||
|
||||
listener.onChange(new Raw<>(Position.of(location)));
|
||||
observer.onChange(new Raw<>(observer.getNow(), Position.of(location)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -124,8 +124,7 @@ public class TrackPointCreator implements SharedPreferences.OnSharedPreferenceCh
|
||||
return new Pair<>(currentTrackPoint, sensorDataSet);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
Instant createNow() {
|
||||
public Instant createNow() {
|
||||
return Instant.now(clock);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +1,36 @@
|
||||
package de.dennisguse.opentracks.settings;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.LocaleConfig;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.LocaleList;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.core.app.LocaleManagerCompat;
|
||||
import androidx.core.os.LocaleListCompat;
|
||||
import androidx.preference.ListPreference;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
public class LocalePreference extends ListPreference {
|
||||
|
||||
private String TAG = LocalePreference.class.getSimpleName();
|
||||
|
||||
public LocalePreference(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
init(context);
|
||||
@@ -40,18 +51,17 @@ public class LocalePreference extends ListPreference {
|
||||
init(context);
|
||||
}
|
||||
|
||||
// /data/user/0/de.dennisguse.opentracks.debug/files/androidx.appcompat.app.AppCompatDelegate.application_locales_record_file: open failed: ENOENT (No such file or directory)
|
||||
private void init(Context context) {
|
||||
setPersistent(false);
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
setEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
LocaleItem systemDefaultLocale = new LocaleItem("", context.getString(R.string.settings_locale_system_default));
|
||||
|
||||
LocaleItem currentLocale = new LocaleItem(Locale.getDefault().toLanguageTag(), Locale.getDefault().getDisplayName());
|
||||
|
||||
// All available options
|
||||
LocaleList supportedLocales = LocaleConfig.fromContextIgnoringOverride(context).getSupportedLocales();
|
||||
LocaleList supportedLocales = getLocaleListCompat();
|
||||
|
||||
ArrayList<LocaleItem> localeItemsSorting = new ArrayList<>();
|
||||
for (int i = 0; i < supportedLocales.size(); i++) {
|
||||
Locale current = supportedLocales.get(i);
|
||||
@@ -104,4 +114,36 @@ public class LocalePreference extends ListPreference {
|
||||
String displayName
|
||||
) {
|
||||
}
|
||||
|
||||
// TODO Get this functionality from any Androidx compat library: should be in LocaleListCompat or LocaleManagerCompat
|
||||
// 2024-12-12: on Android 14-: LocaleManagerCompat.getApplicationLocales(getContext()) returned "[]"
|
||||
// See: https://stackoverflow.com/questions/78116375/per-app-language-preferences-get-list-of-apps-available-language-programmatic
|
||||
@Deprecated
|
||||
private LocaleList getLocaleListCompat() {
|
||||
var a = LocaleManagerCompat.getApplicationLocales(getContext());
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
return LocaleConfig.fromContextIgnoringOverride(getContext()).getSupportedLocales();
|
||||
}
|
||||
|
||||
@SuppressLint("DiscouragedApi") int localesConfigId = getContext().getResources().getIdentifier("_generated_res_locale_config", "xml", getContext().getPackageName());
|
||||
|
||||
List<String> localeList = new ArrayList<>();
|
||||
XmlPullParser xpp = getContext().getResources().getXml(localesConfigId);
|
||||
try {
|
||||
while (xpp.getEventType() != XmlPullParser.END_DOCUMENT) {
|
||||
if (xpp.getEventType() == XmlPullParser.START_TAG) {
|
||||
if ("locale".equals(xpp.getName()) && xpp.getAttributeCount() > 0 && xpp.getAttributeName(0).equals("name")) {
|
||||
localeList.add(xpp.getAttributeValue(0));
|
||||
}
|
||||
}
|
||||
xpp.next();
|
||||
}
|
||||
} catch (IOException | XmlPullParserException e) {
|
||||
Log.e(TAG, "Could not load locales: " + e.getMessage());
|
||||
}
|
||||
Optional<String> locales = localeList.stream().reduce((s1, s2) -> s1 + "," + s2);
|
||||
return locales
|
||||
.map(LocaleList::forLanguageTags)
|
||||
.orElseGet(LocaleList::getEmptyLocaleList);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,6 +332,10 @@ public class PreferencesUtils {
|
||||
return getBoolean(R.string.voice_announce_idle_key, true);
|
||||
}
|
||||
|
||||
public static boolean shouldVoiceAnnounceTime() {
|
||||
return getBoolean(R.string.voice_announce_unit_key, true);
|
||||
}
|
||||
|
||||
public static boolean shouldVoiceAnnounceUnit() {
|
||||
return getBoolean(R.string.voice_announce_unit_key, true);
|
||||
}
|
||||
@@ -429,14 +433,23 @@ public class PreferencesUtils {
|
||||
setBoolean(R.string.voice_announce_lap_speed_pace_key, value);
|
||||
}
|
||||
|
||||
public static boolean shouldVoiceAnnounceLapHeartRate() {
|
||||
return getBoolean(R.string.voice_announce_lap_heart_rate_key, false);
|
||||
}
|
||||
|
||||
public static boolean shouldVoiceAnnounceLapPower() {
|
||||
return getBoolean(R.string.voice_announce_lap_power_key, false);
|
||||
}
|
||||
|
||||
public static boolean shouldVoiceAnnounceHeartRateCurrent() {
|
||||
return getBoolean(R.string.voice_announce_heart_rate_current_key, false);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static void setVoiceAnnounceHeartRateCurrent(boolean value) {
|
||||
setBoolean(R.string.voice_announce_heart_rate_current_key, value);
|
||||
}
|
||||
|
||||
public static boolean shouldVoiceAnnounceLapHeartRate() {
|
||||
return getBoolean(R.string.voice_announce_lap_heart_rate_key, false);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static void setVoiceAnnounceLapHeartRate(boolean value) {
|
||||
setBoolean(R.string.voice_announce_lap_heart_rate_key, value);
|
||||
|
||||
@@ -46,9 +46,9 @@ public class TrackStatistics {
|
||||
private final ExtremityMonitor altitudeExtremities = new ExtremityMonitor();
|
||||
|
||||
// The track start time.
|
||||
private Instant startTime;
|
||||
private Instant startTime; //TODO Should never be null!
|
||||
// The track stop time.
|
||||
private Instant stopTime;
|
||||
private Instant stopTime; //TODO Should never be null!
|
||||
|
||||
private Distance totalDistance;
|
||||
// Updated when new points are received, may be stale.
|
||||
|
||||
@@ -3,6 +3,8 @@ package de.dennisguse.opentracks.viewmodels;
|
||||
import android.util.Pair;
|
||||
import android.view.LayoutInflater;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.data.models.HeartRateZones;
|
||||
import de.dennisguse.opentracks.databinding.StatsSensorItemBinding;
|
||||
@@ -92,7 +94,7 @@ public abstract class SensorStatisticsViewHolder extends StatisticViewHolder<Sta
|
||||
|
||||
Pair<String, String> valueAndUnit;
|
||||
if (sensorDataSet != null && sensorDataSet.getCyclingPower() != null) {
|
||||
valueAndUnit = StringUtils.getPowerParts(getContext(), sensorDataSet.getCyclingPower().getValue());
|
||||
valueAndUnit = StringUtils.getPowerParts(getContext(), sensorDataSet.getCyclingPower().getValue(Instant.now())); //TODO Use MonotonicClock
|
||||
sensorName = sensorDataSet.getCyclingPower().getSensorNameOrAddress();
|
||||
} else {
|
||||
valueAndUnit = StringUtils.getCadenceParts(getContext(), null);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="about_preference_title">Apprupositu d’OpenTracks</string>
|
||||
<string name="about_preference_title">Apprupositu</string>
|
||||
<string name="about_description"><i>OpenTracks</i> hè un’appiecazione di tracciatu spurtivu chì rispetta cumpletamente a vostra vita privata.</string>
|
||||
<string name="about_url">Pagina web di u prughjettu : <a href="%1$s">%1$s</a></string>
|
||||
<string name="about_version_code">Codice di a versione :%1$d</string>
|
||||
@@ -85,7 +85,7 @@
|
||||
<string name="description_pace_nautical">Andatura (min/NM)</string>
|
||||
<string name="description_recorded_time">Data d’arregistramentu : %1$s</string>
|
||||
<string name="description_speed_ms">Vitezza (m/s)</string>
|
||||
<string name="description_sensor_cadence">Cadenza (rpm)</string>
|
||||
<string name="description_sensor_cadence">Cadenza (g/min)</string>
|
||||
<string name="description_sensor_power">Putenza (W)</string>
|
||||
<string name="description_default_speed_or_pace">Da tipu d’attività</string>
|
||||
<string name="description_speed_imperial">Vitezza (mi/ora)</string>
|
||||
@@ -165,29 +165,13 @@
|
||||
<string name="about_history">OpenTracks hè una scansa di <a href="https://code.google.com/archive/p/mytracks/"><i>MyTracks da Google</i></a> (abbandunatu in u 2016).</string>
|
||||
<string name="help_track_export_title">Cumu possu espurtà o scumparte i tracciati arregistrati ?</string>
|
||||
<string name="help_track_export_content"> I tracciati ponu esse espurtati da i Parametri à u furmatu KMZ, KML, o GPX. KMZ o KML per tutti i dati ; GPX solu per e pusizioni. I tracciati scumparti via l’<i>icona di spartera</i> sò à u furmatu KMZ. </string>
|
||||
<string name="help_track_map_content"> \u0020Impiegate a chjuca icona in forma di carta.
|
||||
\n \u0020a) \u0020(messe à livellu in vivu) Installate <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>. \u0020Furnisce carte in linea o fora di linea via <a href="https://www.openstreetmap.org">OpenStreetMap</a>.
|
||||
\n \u0020In fatti, OSMDashboard hè un’altra appiecazione chì pò solu accede (timpurariamente) à i dati di i tracciati chì anu da esse affissati). \u0020
|
||||
\n \u0020b) \u0020Via un’appiecazione classica di cartugrafia (p.i. <a href="http://osmand.net/">OsmAnd</a> è <a href="https://maps.me/">MAPS.ME</a>). \u0020St’appiecazioni devenu piglià in carica u furmatu di schedariu KMZ (solu e pusizioni è e stampiglie). \u0020In fatti, l’appiecazione deve incaricassi di <i>ACTION_VIEW</i> cù u tipu MIME <i>application/vnd.google-earth.kmz</i>. \u0020</string>
|
||||
<string name="help_track_map_content">Impiegate a chjuca icona in forma di carta.\n \u0020a) (messe à livellu in vivu) Installate <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>. Furnisce carte in linea o fora di linea via <a href="https://www.openstreetmap.org">OpenStreetMap</a>.\n \u0020In fatti, OSMDashboard hè un’altra appiecazione chì pò solu accede (timpurariamente) à i dati di i tracciati chì anu da esse affissati).\n \u0020b) Via un’appiecazione classica di cartugrafia (p.i. <a href="http://osmand.net/">OsmAnd</a> è <a href="https://maps.me/">MAPS.ME</a>). \u0020St’appiecazioni devenu piglià in carica u furmatu di schedariu KMZ (solu e pusizioni è e stampiglie). \u0020In fatti, l’appiecazione deve incaricassi di <i>ACTION_VIEW</i> cù u tipu MIME <i>application/vnd.google-earth.kmz</i>.</string>
|
||||
<string name="help_recording_content">OpenTracks impiegheghja u cattore GPS di l’apparechju per arregistrà di cuntinuu a pusizione. S’è l’apparechju GPS signaleghja dati falsi (per indettu, pusizione, vitezza o altezza), OpenTracks ùn pò fà nunda.</string>
|
||||
<string name="help_data_storage_title">Cumu OpenTracks arregistreghja i dati di i cattori ?</string>
|
||||
<string name="activity_type_driving_car">vittura</string>
|
||||
<string name="help_data_storage_content"> I dati di i cattori sò piazzati à a pusizione arregistrata. In casu d’un cattore di frequenza cardiaca, vole si dì chì solu <i>una</i> misura di frequenza cardiaca hè arregistrata à a pusizione. S’è vo state immobile o s’è vo site dentru, nisunu datu di cattore ùn serà arregistratu (ma serà affissatu). </string>
|
||||
<string name="help_gps_troubleshooting_content"> \u0020Ciò ch’ella si pò fà :
|
||||
\n \u0020a) verificate chì u GPS hè attivatu,
|
||||
\n \u0020b) amendà a ricezzione (p.i. andate fora),
|
||||
\n \u0020c) cuntrollate cù l’appiecazione <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/">GPSTest</a>, è
|
||||
\n \u0020d) disattivate l’ecunumia d’energia.
|
||||
\n
|
||||
\n \u0020S’è tuttu què ùn aiuta micca, ci vole à signalà u prublema. \u0020</string>
|
||||
<string name="help_support_content"> \u0020Pudete :
|
||||
\n \u0020a) fà cunnosce OpenTracks,
|
||||
\n \u0020b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">prupone funzioni</a>,
|
||||
\n \u0020c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">signalà i prublemi</a>,
|
||||
\n \u0020d) aiutà à u sviluppu di u prugramma,
|
||||
\n \u0020e) cumprallu in u <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Google Play Store</a>,
|
||||
\n \u0020f) fà una dunazione, è
|
||||
\n \u0020g) fà tuttu ciò ch’ella vi pare ghjuvevule per OpenTracks. \u0020</string>
|
||||
<string name="help_gps_troubleshooting_content">Ciò ch’ella si pò fà :\n a) verificate chì u GPS hè attivatu,\n b) amendà a ricezzione (p.i. andate fora),\n c) cuntrollate cù l’appiecazione <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/">GPSTest</a>, è\n d) disattivate l’ecunumia d’energia.\n\n S’è tuttu què ùn aiuta micca, ci vole à signalà u prublema.</string>
|
||||
<string name="help_support_content">Pudete :\n a) fà cunnosce OpenTracks,\n b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">prupone funzioni</a>,\n c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">signalà i prublemi</a>,\n d) aiutà à u sviluppu di u prugramma,\n e) cumprallu in u <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Google Play Store</a>,\n f) fà una dunazione, è\n g) fà tuttu ciò ch’ella vi pare ghjuvevule per OpenTracks.</string>
|
||||
<string name="activity_type_boat">battellu</string>
|
||||
<string name="activity_type_cross_country_skiing">schì di fondu</string>
|
||||
<string name="help_feature_title">OpenTracks pussede quelle funzioni :</string>
|
||||
@@ -278,7 +262,7 @@
|
||||
<string name="settings_export_filename_title">Furmatu di u nome di schedariu</string>
|
||||
<string name="settings_chart_by_distance">Secondu à a distanza</string>
|
||||
<string name="settings_chart_by_time">Secondu à a durata</string>
|
||||
<string name="settings_ui_dynamic_colors_summary">Impiegà i culori dinamichi definiti da l’utilizatore (solu Android 12è più ; richiede un rilanciu manuale)</string>
|
||||
<string name="settings_ui_dynamic_colors_summary">Impiegà i culori dinamichi definiti da l’utilizatore (solu Android 12 è più ; richiede un rilanciu manuale)</string>
|
||||
<string name="settings_recording_customize_layout_title">Persunalizà u vostru accunciamentu d’arregistramentu</string>
|
||||
<string name="settings_recording_customize_layout_select_columns">Campi di dati à a linea</string>
|
||||
<string name="settings_recording_keep_screen_on_while_recording_title">Cunservà u screnu accesu</string>
|
||||
@@ -423,7 +407,7 @@
|
||||
<string name="power_x_watt">Power {x, plural, =1 {1 vattu} other {# vatti} }</string>
|
||||
<string name="permission_recording_failed">OpenTracks richiede u permessu d’accede à a vostra pusizione ma dinù à l’apparechji vicini.</string>
|
||||
<string name="permission_bluetooth_failed_rejected">Duvete cunsente manualmente à OpenTracks d’accede à l’apparechji vicini in i parametri di u sistema.</string>
|
||||
<string name="sensor_state_cadence_unit">rpm</string>
|
||||
<string name="sensor_state_cadence_unit">g/min</string>
|
||||
<string name="sensor_state_heart_rate_value">%1$d palpiti à u minutu</string>
|
||||
<string name="sensor_state_power_unit">W</string>
|
||||
<string name="settings_defaults_summary">Unità preferite, attività predefinita, nome predefinitu di u tracciatu</string>
|
||||
@@ -553,4 +537,9 @@
|
||||
<string name="settings_recording_min_sampling_interval_title">Intervallu di durata di campiunariu</string>
|
||||
<string name="settings_announcements_idle_title">Inattività</string>
|
||||
<string name="voiceIdle">Diventà inattivu.</string>
|
||||
<string name="settings_locale_title">Lingua</string>
|
||||
<string name="settings_locale_system_default">Predefinizione di u sistema</string>
|
||||
<string name="stats_gain_loss">Guadagnu è perdita</string>
|
||||
<string name="voice_announce_lap_power_key">AnnunziamentuVucalePutenzaÀUGiru</string>
|
||||
<string name="lap_heart_rate">Frequenza cardiaca à u giru</string>
|
||||
</resources>
|
||||
@@ -14,7 +14,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
--><resources>
|
||||
<string name="about_preference_title">O OpenTracks</string>
|
||||
<string name="about_preference_title">O aplikaci</string>
|
||||
<string name="about_description"><i> OpenTracks </i> je aplikace pro záznam vašich sportů, která zcela respektuje vaše soukromí.</string>
|
||||
<string name="about_url">Webová stránka projektu: <a href="%1$s">%1$s</a></string>
|
||||
<string name="about_version_name">Název verze: %1$s</string>
|
||||
@@ -30,10 +30,7 @@ limitations under the License.
|
||||
\n* funguje zcela offline
|
||||
\n* zobrazit trasy na mapě (viz níže)</string>
|
||||
<string name="help_track_map_title">Jak ukázat trasu na mapě\?</string>
|
||||
<string name="help_track_map_content"> \u0020Použijte malou ikonu mapy.
|
||||
\n \u0020a) \u0020(živé aktualizace) Nainstalujte <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>. \u0020Poskytuje online nebo offline mapy prostřednictvím <a href="https://www.openstreetmap.org">OpenStreetMap</a>.
|
||||
\n \u0020Ve skutečnosti je OSMDashboard samostatná aplikace, která má přístup (dočasně) pouze k datům stop, které se budou zobrazovat. \u0020
|
||||
\n \u0020b) \u0020Prostřednictvím normální mapové aplikace (např. <a href="http://osmand.net/">OsmAnd</a> a <a href="https://maps.me/">MAPS.ME</a>). \u0020Tyto aplikace musí podporovat formát souboru KMZ (pouze umístění a časová razítka). \u0020Ve skutečnosti musí aplikace podporovat <i>ACTION_VIEW</i> pomocí MIME <i>application/vnd.google-earth.kmz</i>. \u0020</string>
|
||||
<string name="help_track_map_content">Použijte malou ikonu mapy.\n a) (živé aktualizace) Nainstalujte si aplikaci <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>. Poskytuje online i offline mapy prostřednictvím <a href="https://www.openstreetmap.org">OpenStreetMap</a>.\n OSMDashboard je samostatná aplikace, která má (dočasný) přístup pouze k datům stop, které se budou zobrazovat. \n b) Prostřednictvím normální mapové aplikace (např. <a href="http://osmand.net/">OsmAnd</a> a <a href="https://maps.me/">MAPS.ME</a>). Tyto aplikace musí podporovat formát souboru KMZ (pouze umístění a časová razítka). Technicky vzato musí aplikace podporovat <i>ACTION_VIEW</i> s MIME <i>application/vnd.google-earth.kmz</i>.</string>
|
||||
<string name="help_storage_pictures_title">Jak ukládá OpenTracks obrázky pořízené jako značky na cestě\?</string>
|
||||
<string name="help_storage_pictures_content">Jsou uloženy v interním úložišti aplikace (tj. přístupné pouze v rámci OpenTracks).</string>
|
||||
<string name="help_track_export_title">Jak mohu exportovat/sdílet zaznamenané stopy\?</string>
|
||||
@@ -44,22 +41,9 @@ limitations under the License.
|
||||
<string name="help_data_storage_title">Jak OpenTracks ukládá data ze senzorů\?</string>
|
||||
<string name="help_data_storage_content"> Data senzorů se ukládají na zaznamenané místo. Pokud jde o snímač srdeční frekvence, znamená to, že na každé místo je uloženo pouze <i>jedno</i> měření srdeční frekvence. Pokud stojíte v klidu nebo jste uvnitř, nebudou zaznamenána žádná data ze senzorů (ale budou zobrazena). </string>
|
||||
<string name="help_gps_troubleshooting_title">OpenTracks nezaznamenává místa?</string>
|
||||
<string name="help_gps_troubleshooting_content"> \u0020Co můžete udělat:
|
||||
\n \u0020a) zkontrolujte zda je zapnuta GPS,
|
||||
\n \u0020b) zkuste zlepšit příjem GPS (např. vyjít ven),
|
||||
\n \u0020c) zkontrolujte, zda používáte aplikaci <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/">GPSTest </a> a
|
||||
\n \u0020d) deaktivujte úsporu energie.
|
||||
\n \u0020
|
||||
\n \u0020Pokud nic nepomůže, nahlaste to jako chybu. \u0020</string>
|
||||
<string name="help_gps_troubleshooting_content">Co můžete udělat:\n a) zkontrolujte zda je zapnuta poloha,\n b) zkuste zlepšit příjem GPS (např. vyjít ven),\n c) zkontrolujte funkčnost polohy s aplikací <a href="https://f-droid.org/cs/packages/com.android.gpstest.osmdroid/">GPSTest </a> a\n d) vypněte spořič baterie.\n \n Pokud nic nepomůže, nahlaste to jako chybu.</string>
|
||||
<string name="help_support_title">Jak mohu podpořit OpenTracks\?</string>
|
||||
<string name="help_support_content"> \u0020Můžete:
|
||||
\n \u0020a) dát vědět ostatním o OpenTracks
|
||||
\n \u0020b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">navrhnout nové funkce</a>,
|
||||
\n \u0020c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">nahlásit chybu</a>,
|
||||
\n \u0020d) pomoci při vývoji aplikace,
|
||||
\n \u0020e) zakoupit v <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Obchodě Google Play</a>,
|
||||
\n \u0020f) zaslat dar či
|
||||
\n \u0020g) udělat něco jiného, co si myslíte že pomůže OpenTracks. \u0020</string>
|
||||
<string name="help_support_content">Můžete:\n a) dát vědět ostatním o OpenTracks\n b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">navrhnout nové funkce</a>,\n c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">nahlásit chybu</a>,\n d) pomoci při vývoji aplikace,\n e) koupit si aplikaci v <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Obchodě Google Play</a>,\n f) zaslat dar nebo\n g) udělat něco jiného, co si myslíte že pomůže OpenTracks.</string>
|
||||
<!-- Activity Type -->
|
||||
<string name="activity_type_airplane">letadlo</string>
|
||||
<string name="activity_type_atv">terénní auta</string>
|
||||
@@ -607,4 +591,8 @@ limitations under the License.
|
||||
<string name="power_x_watt">Výkon {x, plural, =1 {1 watt} few {# watty} other {# wattů} }</string>
|
||||
<string name="create_marker_error">Nepodařilo se vytvořit značku. Zkuste to znovu.</string>
|
||||
<string name="empty">" "</string>
|
||||
<string name="settings_locale_title">Jazyk</string>
|
||||
<string name="settings_locale_system_default">Podle systému</string>
|
||||
<string name="stats_gain_loss">Stoupání a klesání</string>
|
||||
<string name="lap_heart_rate">Tepová frekvence za kolo</string>
|
||||
</resources>
|
||||
@@ -14,7 +14,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
--><resources>
|
||||
<string name="about_preference_title">Acerca de OpenTracks</string>
|
||||
<string name="about_preference_title">Acerca de</string>
|
||||
<string name="about_description"><i> OpenTracks </i> es una aplicación de seguimiento deportivo que respeta completamente su privacidad.</string>
|
||||
<string name="about_url">Página web del proyecto: <a href="%1$s">%1$s</a></string>
|
||||
<string name="about_version_name">Nombre de la versión: %1$s</string>
|
||||
@@ -661,4 +661,7 @@ limitations under the License.
|
||||
<string name="create_marker_error">No se ha podido crear un marcador. Inténtelo de nuevo.</string>
|
||||
<string name="voice_announce_lap_power_key">voiceAnnounceLapPower</string>
|
||||
<string name="power">Potencia</string>
|
||||
<string name="settings_locale_title">Idioma</string>
|
||||
<string name="settings_locale_system_default">Predeterminado del sistema</string>
|
||||
<string name="stats_gain_loss">Ganancia y pérdida</string>
|
||||
</resources>
|
||||
@@ -662,4 +662,8 @@ Si le dispositif GPS signale des données inexactes (par exemple, la localisatio
|
||||
<string name="activity_type_kickscooter">trottinette</string>
|
||||
<string name="voiceSpeed">{n, plural, =1 {1} autres {{n,number,#.0}} }</string>
|
||||
<string name="voiceDistance">{n, plural, =1 {1 } autres {{n,number,#.0}} }</string>
|
||||
<string name="settings_locale_system_default">Paramètres par défaut du système</string>
|
||||
<string name="settings_locale_title">Langue</string>
|
||||
<string name="stats_gain_loss">Gain et perte</string>
|
||||
<string name="voice_announce_lap_power_key">voiceAnnounceLapPower</string>
|
||||
</resources>
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="about_libraries">Leabharlanna</string>
|
||||
<string name="about_preference_title">Maidir Le OpenTracks</string>
|
||||
<string name="about_preference_title">Maidir</string>
|
||||
<string name="about_url">Leathanach Gréasáin an tionscadail: <a href="%1$s">%1$s</a></string>
|
||||
<string name="about_version_name">Ainm an leagain: %1$s</string>
|
||||
<string name="about_version_code">Cód an leagain: %1$d</string>
|
||||
@@ -542,4 +542,8 @@
|
||||
<string name="settings_announcements_lap_power">Cumhacht glúine</string>
|
||||
<string name="power_x_watt">Cumhacht {x, iolra, = 1 {1 vata} eile {# vata} }</string>
|
||||
<string name="empty">" "</string>
|
||||
<string name="settings_locale_system_default">Réamhshocrú córais</string>
|
||||
<string name="settings_locale_title">Teanga</string>
|
||||
<string name="stats_gain_loss">Gnóthachan & Caillteanas</string>
|
||||
<string name="lap_heart_rate">Ráta croí glúine</string>
|
||||
</resources>
|
||||
@@ -14,7 +14,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
--><resources>
|
||||
<string name="about_preference_title">Sobre OpenTracks</string>
|
||||
<string name="about_preference_title">Sobre</string>
|
||||
<string name="about_description"><i>OpenTracks</i> é unha aplicación para rexistrar actividades deportivas que respecta a túa privacidade.</string>
|
||||
<string name="about_url">Web do proxecto: <a href="%1$s">%1$s</a></string>
|
||||
<string name="about_version_name">Nome da versión: %1$s</string>
|
||||
@@ -30,10 +30,7 @@ limitations under the License.
|
||||
\n* funciona sen precisar conexión á rede
|
||||
\n* mostra as rutas nun mapa (ver abaixo)</string>
|
||||
<string name="help_track_map_title">Como mostrar a ruta no mapa\?</string>
|
||||
<string name="help_track_map_content"> \u0020Usa a icona do mapa pequeno.
|
||||
\n a) (actualizacións en directo) Instala <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>. Proporciona mapas en liña ou sen conexión grazas a <a href="https://www.openstreetmap.org">OpenStreetMap</a>.
|
||||
\n En realidade, OSMDashboard é unha aplicación independente que só pode acceder (temporalmente) aos datos das pistas que se amosarán.
|
||||
\n b) A través dunha aplicación de mapa normal (por exemplo, <a href="http://osmand.net/">OsmAnd</a> e <a href="https://maps.me/">MAPS.ME</a>). Estas aplicacións precisan admitir o formato de ficheiro KMZ (só localizacións e marcas de tempo). De feito, unha aplicación ten que admitir <i>ACTION_VIEW</i> con MIME <i>application/vnd.google-earth.kmz</i>. \u0020</string>
|
||||
<string name="help_track_map_content">Usa a icona do mapa pequeno. \n a) (actualizacións en directo) Instala <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>. Proporciona mapas en liña ou sen conexión grazas a <a href="https://www.openstreetmap.org">OpenStreetMap</a>. \n En realidade, OSMDashboard é unha aplicación independente que só pode acceder (temporalmente) aos datos das pistas que se amosarán. \n b) A través dunha aplicación de mapa normal (por exemplo, <a href="http://osmand.net/">OsmAnd</a> e <a href="https://maps.me/">MAPS.ME</a>). Estas aplicacións precisan admitir o formato de ficheiro KMZ (só localizacións e marcas de tempo). De feito, unha aplicación ten que admitir <i>ACTION_VIEW</i> con MIME <i>application/vnd.google-earth.kmz</i>.</string>
|
||||
<string name="help_storage_pictures_title">De que xeito garda OpenTracks as fotos tomadas como marcadores\?</string>
|
||||
<string name="help_storage_pictures_content">Gárdanse na almacenaxe interna da app (só accesible desde OpenTracks).</string>
|
||||
<string name="help_track_export_title">De que xeito exporto/importo as rutas gravadas\?</string>
|
||||
@@ -44,24 +41,11 @@ limitations under the License.
|
||||
OpenTracks usa o sensor GPS do dispositivo para gravar a localización de xeito contínuo.
|
||||
Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, elevación), OpenTracks non pode facer nada ao respecto.</string>
|
||||
<string name="help_data_storage_title">De que xeito garda OpenTracks os datos dos sensores\?</string>
|
||||
<string name="help_data_storage_content"> Os datos dos sensores gárdanse asociados á localización. Respecto do ritmo cardíaco, significa que só se garda <i>unha medida</i> asociada a unha localización. \u0020Se te detés ou estás en interiores, non se gardarán datos do sensor (mais serán mostrados). </string>
|
||||
<string name="help_data_storage_content">Os datos dos sensores gárdanse asociados á localización. Respecto do ritmo cardíaco, significa que só se garda <i>unha medida</i> asociada a unha localización. Se te detés ou estás en interiores, non se gardarán datos do sensor (mais serán mostrados).</string>
|
||||
<string name="help_gps_troubleshooting_title">OpenTracks non grava as localizacións\?</string>
|
||||
<string name="help_gps_troubleshooting_content"> \u0020Podes intentar:
|
||||
\n a) mira que o GPS esté activado,
|
||||
\n b) mellora a recepción GPS (ex. sae ao exterior),
|
||||
\n c) comprobaa coa app <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/">GPSTest</a>, e
|
||||
\n d) desactiva o aforro de enerxía.
|
||||
\n
|
||||
\n Se isto non axuda, infórmanos do problema. \u0020</string>
|
||||
<string name="help_gps_troubleshooting_content">Podes intentar: \n a) mira que o GPS esté activado, \n b) mellora a recepción GPS (ex. sae ao exterior), \n c) comprobaa coa app <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/">GPSTest</a>, e \n d) desactiva o aforro de enerxía. \n \n Se isto non axuda, infórmanos do problema.</string>
|
||||
<string name="help_support_title">Como podo axudar a OpenTracks\?</string>
|
||||
<string name="help_support_content"> \u0020Podes:
|
||||
\n a) falar de OpenTracks coas túas amizades,
|
||||
\n b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">propoñer novas funcións</a>,
|
||||
\n c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">informar de fallos</a>,
|
||||
\n d) axudar no desenvolvemento,
|
||||
\n e) mercar a app na <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Google Play Store</a>,
|
||||
\n f) facer unha doazón, e
|
||||
\n g) calquera cousa que estimes axuda a OpenTracks. \u0020</string>
|
||||
<string name="help_support_content">Podes: \n a) falar de OpenTracks coas túas amizades, \n b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">propoñer novas funcións</a>, \n c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">informar de fallos</a>, \n d) axudar no desenvolvemento, \n e) mercar a app na <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Google Play Store</a>, \n f) facer unha doazón, e \n g) calquera cousa que estimes axuda a OpenTracks.</string>
|
||||
<string name="open_track_as_trackfileformat">Abrir como %1$s</string>
|
||||
<!-- Activity Type -->
|
||||
<string name="activity_type_airplane">avión</string>
|
||||
@@ -103,7 +87,7 @@ Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, ele
|
||||
<string name="activity_type_rc_airplane">Avión de radiocontrol</string>
|
||||
<string name="activity_type_rc_boat">Barco de radiocontrol</string>
|
||||
<string name="activity_type_rc_helicopter">Helicóptero de radiocontrol</string>
|
||||
<string name="activity_type_riding">ciclismo</string>
|
||||
<string name="activity_type_riding">equitación</string>
|
||||
<string name="activity_type_road_biking">ciclismo de estrada</string>
|
||||
<string name="activity_type_roller_skiing">esquí sobre rodas</string>
|
||||
<string name="activity_type_rowing">remo</string>
|
||||
@@ -607,4 +591,7 @@ Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, ele
|
||||
<string name="power">Potencia</string>
|
||||
<string name="power_x_watt">Potencia {x, plural, =1 {1 watio} other {# watios} }</string>
|
||||
<string name="create_marker_error">Non se creou o marcador. Inténtao outra vez.</string>
|
||||
<string name="settings_locale_title">Idioma</string>
|
||||
<string name="settings_locale_system_default">Predeterminado no sistema</string>
|
||||
<string name="stats_gain_loss">Ganancia e Perda</string>
|
||||
</resources>
|
||||
@@ -313,7 +313,7 @@ limitations under the License.
|
||||
<string name="about_url">A projekt weboldala: <a href="%1$s">%1$s</a></string>
|
||||
<string name="about_version_name">Verzió neve: %1$s</string>
|
||||
<string name="about_version_code">Verziókód: %1$d</string>
|
||||
<string name="about_preference_title">Az OpenTracks névjegye</string>
|
||||
<string name="about_preference_title">Névjegy</string>
|
||||
<string name="help_data_google_play_services_title">Az OpenTracks igényli/használja a Google Play szolgáltatást?</string>
|
||||
<string name="help_track_export_title">Hogyan tudom exportálni/megosztani a felvett nyomvonalakat?</string>
|
||||
<string name="help_storage_pictures_content">Ezeket az alkalmazás belső tárhelyén tárolja (azaz csak az OpenTracks-en belül érhető el).</string>
|
||||
@@ -351,13 +351,7 @@ limitations under the License.
|
||||
<string name="import_unsupported_format">Nem támogatott fájlformátum</string>
|
||||
<string name="description_default_speed_or_pace">Tevékenységtípus szerint</string>
|
||||
<string name="export_without_photos">fotók nélkül</string>
|
||||
<string name="help_gps_troubleshooting_content"> \u0020Mit tehet:
|
||||
\n a) ellenőrizze, hogy a GPS engedélyezve van-e,
|
||||
\n b) javítsa a GPS-vételt (pl. menjen a szabadba),
|
||||
\n c) ellenőrizze a <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/">GPSTest</a> alkalmazással, és
|
||||
\n d) tiltsa le az energiatakarékosságot.
|
||||
\n
|
||||
\n Ha mindez nem segít, kérjük, jelentse a hibát. \u0020</string>
|
||||
<string name="help_gps_troubleshooting_content">Mit tehet: \n a) ellenőrizze, hogy a GPS engedélyezve van-e, \n b) javítsa a GPS-vételt (pl. menjen a szabadba), \n c) ellenőrizze a <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/">GPSTest</a> alkalmazással, és \n d) tiltsa le az energiatakarékosságot. \n \n Ha mindez nem segít, kérjük, jelentse a hibát.</string>
|
||||
<string name="export_progress_summary_new_msg">Új fájlok exportálva</string>
|
||||
<string name="generic_completed">Befejezve</string>
|
||||
<string name="sensor_state_heart_rate_avg">Átl. pulzusszám</string>
|
||||
@@ -394,14 +388,7 @@ limitations under the License.
|
||||
<string name="menu_resume_track">Nyomvonal folytatása</string>
|
||||
<string name="import_parser_error">Értelmezőhiba: %1$s</string>
|
||||
<string name="help_data_storage_content"> Az érzékelőadatok rögzített helyenként vannak tárolva. A pulzusszám-érzékelőre vonatkoztatva ez azt jelenti, hogy csak <i>egy</i> pulzusszámmérés kerül tárolásra helyenként. Ha Ön mozdulatlanul áll vagy beltérben tartózkodik, akkor nem kerülnek rögzítésre az érzékelőadatai (de megjelennek). </string>
|
||||
<string name="help_support_content"> \u0020Ezeket teheti meg:
|
||||
\n \u0020a) terjesztheti az OpenTracks hírét,
|
||||
\n \u0020b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">funkciókat javasolhat</a>,
|
||||
\n \u0020c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">hibákat jelenthet be</a>,
|
||||
\n \u0020d) segíthet a szoftverfejlesztésben,
|
||||
\n \u0020e) megveheti a <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Google Play Store</a>-ban,
|
||||
\n \u0020f) adományozhat, és
|
||||
\n \u0020g) tehet bármit, amiről úgy gondolja, hogy segít az OpenTracks-nek. \u0020</string>
|
||||
<string name="help_support_content">Ezeket teheti meg: \n a) terjesztheti az OpenTracks hírét, \n b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">funkciókat javasolhat</a>, \n c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">hibákat jelenthet be</a>, \n d) segíthet a szoftverfejlesztésben, \n e) megveheti a <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Google Play Store</a>-ban, \n f) adományozhat, és \n g) tehet bármit, amiről úgy gondolja, hogy segít az OpenTracks-nek.</string>
|
||||
<plurals name="generic_completed_with_errors">
|
||||
<item quantity="one">Egy hibával befejezve</item>
|
||||
<item quantity="other">%1$d hibával befejezve</item>
|
||||
@@ -418,10 +405,7 @@ limitations under the License.
|
||||
<string name="share_image_body">Azt hiszem, ez a kép érdekelhet.</string>
|
||||
<string name="help_recording_title">Hogyan veszi fel az OpenTracks az adatokat?</string>
|
||||
<string name="help_track_export_content"> A nyomvonalak KMZ, KML vagy GPX formátumban exportálhatók a Beállításokból. KMZ/KML minden adat; GPX csak a helyszínek. A <i>megosztás ikonon</i> keresztül megosztott nyomvonalak KMZ formátumban kerülnek megosztásra. </string>
|
||||
<string name="help_track_map_content"> \u0020Használja a kis térkép ikont.
|
||||
\n \u0020a) \u0020(élő frissítések) Telepítse a <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>. \u0020Ez online vagy offline térképeket biztosít a <a href="https://www.openstreetmap.org">OpenStreetMap</a> segítségével.
|
||||
\n \u0020Valójában az OSMDashboard egy különálló alkalmazás, amely csak a megjelenítendő nyomvonal(ak) adataihoz fér hozzá (ideiglenesen). \u0020
|
||||
\n \u0020b) \u0020Egy normál térképalkalmazáson keresztül (pl. <a href="http://osmand.net/">OsmAnd</a> és <a href="https://maps.me/">MAPS.ME</a>). \u0020Ezeknek az alkalmazásoknak támogatniuk kell a KMZ fájlformátumot (csak a helyeket és az időbélyegeket). \u0020Valójában egy alkalmazásnak támogatnia kell a <i>ACTION_VIEW</i> MIME <i>application/vnd.google-earth.kmz</i> formátumot. \u0020</string>
|
||||
<string name="help_track_map_content">Használja a kis térkép ikont.\na) (élő frissítések) Telepítse a <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>. Ez online vagy offline térképeket biztosít a <a href="https://www.openstreetmap.org">OpenStreetMap</a> segítségével.\nValójában az OSMDashboard egy különálló alkalmazás, amely csak a megjelenítendő nyomvonal(ak) adataihoz fér hozzá (ideiglenesen).\nb) Egy normál térképalkalmazáson keresztül (pl. <a href="http://osmand.net/">OsmAnd</a> és <a href="https://maps.me/">MAPS.ME</a>). Ezeknek az alkalmazásoknak támogatniuk kell a KMZ fájlformátumot (csak a helyeket és az időbélyegeket). Valójában egy alkalmazásnak támogatnia kell a <i>ACTION_VIEW</i> MIME <i>application/vnd.google-earth.kmz</i> formátumot.</string>
|
||||
<string name="about_description">Az <i>OpenTracks</i> egy sportkövető alkalmazás, amely teljes mértékben tiszteletben tartja az Ön adatvédelmét.</string>
|
||||
<string name="about_history">Az OpenTracks a <a href="https://code.google.com/archive/p/mytracks/"><i>Google MyTracks</i></a> (2016-ban megszűnt) elágazása.</string>
|
||||
<string name="help_feature_content">* GPS nyomvonalak létrehozása
|
||||
@@ -517,7 +501,7 @@ limitations under the License.
|
||||
<string name="show_on_dashboard_not_installed">Nincs OpenTracks Dashboard API-t támogató alkalmazás telepítve.</string>
|
||||
<string name="value_float_mile_hour">%1$.1f mérföld/óra</string>
|
||||
<string name="bluetooth_sensor_summary">%1$s (%2$s)</string>
|
||||
<string name="settings_announcements_lap_heart_rate">Kör pulzusszám</string>
|
||||
<string name="settings_announcements_lap_heart_rate">Körönkénti pulzusszám</string>
|
||||
<string name="settings_sensor_bluetooth_service_filter_title">Bluetooth-eszközök szűrése</string>
|
||||
<string name="activity_type_kickscooter">roller</string>
|
||||
<string name="gps_recording_without_signal">Az OpenTracks GPS-adatok nélkül rögzít.</string>
|
||||
@@ -601,4 +585,8 @@ limitations under the License.
|
||||
<string name="voiceDistance">{n, plural, =1 {1 } other {{n,number,#.0}} }</string>
|
||||
<string name="voice_announce_lap_power_key">voiceAnnounceLapPower</string>
|
||||
<string name="power_x_watt">Teljesítmény {x, plural, =1 {1 watt} other {# watts} }</string>
|
||||
<string name="settings_locale_title">Nyelv</string>
|
||||
<string name="settings_locale_system_default">Rendszer alapértelmezett</string>
|
||||
<string name="stats_gain_loss">Növekedés és veszteség</string>
|
||||
<string name="lap_heart_rate">Körönkénti pulzusszám</string>
|
||||
</resources>
|
||||
@@ -14,7 +14,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
--><resources>
|
||||
<string name="about_preference_title">A proposito di OpenTracks</string>
|
||||
<string name="about_preference_title">Informazioni su OpenTracks</string>
|
||||
<string name="about_description"><i>OpenTracks</i>è un\'applicazione di tracciamento sportivo che rispetta completamente la tua privacy.</string>
|
||||
<string name="about_url">Pagina web del progetto: <a href="%1$s">%1$s</a></string>
|
||||
<string name="about_version_name">Nome versione: %1$s</string>
|
||||
@@ -189,7 +189,7 @@ Infatti, un\'applicazione deve supportare <i>ACTION_VIEW</i> con MIME <i>applica
|
||||
<string name="menu_aggregated_statistics">Statistiche aggregate</string>
|
||||
<string name="menu_delete">Elimina</string>
|
||||
<string name="menu_edit">Modifica</string>
|
||||
<string name="menu_export_all">Esporta tutto</string>
|
||||
<string name="menu_export_all">Esporta tutto (un file per traccia)</string>
|
||||
<string name="menu_help">Aiuto</string>
|
||||
<string name="menu_import_all">Importa tutto</string>
|
||||
<string name="menu_insert_marker">Inserisci indicatore</string>
|
||||
@@ -213,7 +213,7 @@ Infatti, un\'applicazione deve supportare <i>ACTION_VIEW</i> con MIME <i>applica
|
||||
<string name="settings_chart_by_distance">Per distanza</string>
|
||||
<string name="settings_chart_by_time">Per durata</string>
|
||||
<string name="settings_recording_default_activity_title">Tipo di attività predefinita</string>
|
||||
<string name="settings_recording_location_frequency_summary">%1$s tra posizioni registrate</string>
|
||||
<string name="settings_recording_location_frequency_summary">%1$s tra posizioni marcate</string>
|
||||
<string name="settings_recording_max_recording_distance_summary">Viene creato un nuovo segmento se la distanza tra due punti è maggiore di %1$s</string>
|
||||
<string name="settings_recording_max_recording_distance_title">Distanza massima</string>
|
||||
<string name="settings_recording_min_recording_distance_title">Intervallo distanza tra registrazioni</string>
|
||||
@@ -371,7 +371,7 @@ Infatti, un\'applicazione deve supportare <i>ACTION_VIEW</i> con MIME <i>applica
|
||||
<string name="activity_type_escooter">Scooter elettrico</string>
|
||||
<string name="export_with_photos">con foto</string>
|
||||
<string name="export_without_photos">senza foto</string>
|
||||
<string name="export_track_already_exists_msg">Il percorso %1$s esiste già nella cartella di salvataggio.</string>
|
||||
<string name="export_track_already_exists_msg">Il file %1$s esiste già nella cartella di salvataggio.</string>
|
||||
<plurals name="generic_completed_with_errors">
|
||||
<item quantity="one">Completato con un errore</item>
|
||||
<item quantity="other">Completato con %1$d errori</item>
|
||||
@@ -384,7 +384,7 @@ Infatti, un\'applicazione deve supportare <i>ACTION_VIEW</i> con MIME <i>applica
|
||||
<string name="settings_recording_show_on_lockscreen_while_recording_summary">Durante la registrazione del percorso, mostra le statistiche senza sbloccare il dispositivo.</string>
|
||||
<string name="description_default_speed_or_pace">Per tipo di attività</string>
|
||||
<string name="export_all_do_it_for_all">applica lo stesso per tutti i conflitti</string>
|
||||
<string name="export_track_errors">Percorsi non esportati</string>
|
||||
<string name="export_track_errors">Files non esportati</string>
|
||||
<string name="generic_overwrite">Sovrascrivere</string>
|
||||
<string name="generic_skip">Ignora</string>
|
||||
<string name="generic_separator_done_total">/</string>
|
||||
@@ -422,9 +422,9 @@ Infatti, un\'applicazione deve supportare <i>ACTION_VIEW</i> con MIME <i>applica
|
||||
<string name="export_error_post_workout">Esportazione post-allenamento non riuscita, controllare la cartella di esportazione.</string>
|
||||
<string name="settings_prevent_reimport_tracks_title">Impedire la reimportazione delle tracce</string>
|
||||
<string name="export_progress_summary_errors_msg">Errori</string>
|
||||
<string name="export_progress_summary_skip_msg">Tracce saltate</string>
|
||||
<string name="export_progress_summary_overwrite_msg">Tracce sovrascritte</string>
|
||||
<string name="export_progress_summary_new_msg">Nuove tracce esportate</string>
|
||||
<string name="export_progress_summary_skip_msg">Files saltati</string>
|
||||
<string name="export_progress_summary_overwrite_msg">Files sovrascritti</string>
|
||||
<string name="export_progress_summary_new_msg">Nuovi files esportati</string>
|
||||
<string name="import_no_kml_file_found">Nessun file kml trovato in KMZ</string>
|
||||
<string name="import_progress_summary_ok_msg">File importati</string>
|
||||
<string name="import_progress_summary_exists_msg">Esiste già</string>
|
||||
@@ -448,7 +448,7 @@ Infatti, un\'applicazione deve supportare <i>ACTION_VIEW</i> con MIME <i>applica
|
||||
<string name="settings_sensor_running_speed_and_cadence">Distanza, Velocità e Cadenza</string>
|
||||
<string name="stats_clock">Orologio</string>
|
||||
<string name="settings_announcements_totaltime_frequency">Intervallo di tempo</string>
|
||||
<string name="settings_announcements_distance_frequency">Intervallo di distanza</string>
|
||||
<string name="settings_announcements_distance_frequency">Lunghezza giro</string>
|
||||
<string name="location_latitude_longitude">%1$s, %2$s</string>
|
||||
<string name="location_coordinate">%1$s°</string>
|
||||
<string name="value_float_kilometer_hour_recommended">%1$.1f km/h (consigliato)</string>
|
||||
@@ -482,7 +482,7 @@ Infatti, un\'applicazione deve supportare <i>ACTION_VIEW</i> con MIME <i>applica
|
||||
<string name="settings_sensors_title">Sensori</string>
|
||||
<string name="settings_announcements_statistics_title">Annunci Vocali</string>
|
||||
<string name="settings_announcements_summary">Tempo, Distanza, Velocità della Voce</string>
|
||||
<string name="settings_import_export_title">Importa/Esporta</string>
|
||||
<string name="settings_import_export_title">Importa ed Esporta</string>
|
||||
<string name="settings_import_export_summary">Importa, Esporta, Auto Esporta</string>
|
||||
<string name="settings_units_category">Unità</string>
|
||||
<string name="settings_activity_defaults_category">Predefiniti dell\'Attività</string>
|
||||
@@ -491,7 +491,7 @@ Infatti, un\'applicazione deve supportare <i>ACTION_VIEW</i> con MIME <i>applica
|
||||
<string name="settings_layout_reset_confirm_message">Tutte le impostazioni di layout saranno reimpostate ai valori predefiniti. Questo non eliminerà alcuna traccia dal dispositivo.</string>
|
||||
<string name="settings_layout_reset_confirm_title">Rempostare i tuoi layout\?</string>
|
||||
<string name="settings_all_reset">Reimposta</string>
|
||||
<string name="stats_sensors_heart_rate">Frequenza Cardiaca</string>
|
||||
<string name="stats_sensors_heart_rate">Frequenza cardiaca</string>
|
||||
<string name="stats_sensors_power">Potenza</string>
|
||||
<string name="custom_layout_list_title">Disegni</string>
|
||||
<string name="custom_layout_list_edit_name">Digitare il nome del layout da aggiungere</string>
|
||||
@@ -513,4 +513,104 @@ Infatti, un\'applicazione deve supportare <i>ACTION_VIEW</i> con MIME <i>applica
|
||||
<string name="export_cannot_write_to_dir">Impossibile scrivere nella directory di esportazione.</string>
|
||||
<string name="export_dir_not_writable">" (non scrivibile!)"</string>
|
||||
<string name="no_compatible_file_manager_installed">Nessun file manager compatibile installato</string>
|
||||
<string name="value_integer_knots">%1$d nodi</string>
|
||||
<string name="bluetooth_sensor_summary">%1$s (%2$s)</string>
|
||||
<string name="total_distance">Distanza totale</string>
|
||||
<string name="average_heart_rate">Frequenza cardiaca media</string>
|
||||
<string name="error_crash_title">%1$s si è interrotto</string>
|
||||
<string name="description_pace_nautical">Ritmo (min/NM)</string>
|
||||
<string name="description_speed_nautical">Velocità (nodi)</string>
|
||||
<string name="gps_recording_without_signal">OpenTracks sta registrando senza segnale GPS.</string>
|
||||
<string name="image_discard">Cancella</string>
|
||||
<string name="menu_export_all_one_file">Esporta tutto (un file con tutte le tracce)</string>
|
||||
<string name="permission_recording_failed">OpenTracks richiede il permesso di accedere alla tua posizione e ai dispositivi vicini.</string>
|
||||
<string name="sensor_state_heart_rate_unit">bpm</string>
|
||||
<string name="sensor_state_cadence_unit">rpm (rotazioni per minuto)</string>
|
||||
<string name="sensor_state_pressure">Barometro</string>
|
||||
<string name="sensor_state_power_max">Potenza Massima</string>
|
||||
<string name="sensor_state_power_unit">W</string>
|
||||
<string name="settings_night_mode_option_night_oled">Notte (schermi OLED)</string>
|
||||
<string name="settings_recording_idle_timeout_title">Soglia di inattività</string>
|
||||
<string name="settings_announcements_lap_speed_pace">Velocità/ritmo del giro</string>
|
||||
<string name="settings_api_title">API pubblica</string>
|
||||
<string name="settings_public_api_disabled_toast">L\'API pubblica di OpenTracks è disabilitata: può essere riabilitata nelle impostazioni.</string>
|
||||
<string name="settings_public_api_dashboard_enabled_title">Trasferimento dati automatico</string>
|
||||
<string name="voice_per_kilometer">per kilometro</string>
|
||||
<string name="share_crash_report">Condividi il report d\'errore</string>
|
||||
<string name="report_bug">Segnala un problema</string>
|
||||
<string name="show_on_dashboard">OpenTracks Dashboard API</string>
|
||||
<string name="introduction_osm_dashboard">OpenTracks non fornisce mappe. Installa OSMDashboard per visualizzare le tue registrazioni su una mappa.</string>
|
||||
<string name="show_on_dashboard_not_installed">Nessuna delle applicazioni installate supporta l\'API di OpenTracks Dashboard.</string>
|
||||
<string name="settings_locale_title">Lingua</string>
|
||||
<string name="settings_locale_system_default">Default di sistema</string>
|
||||
<string name="settings_announcements_lap_heart_rate">Frequenza cardiaca del giro</string>
|
||||
<string name="settings_announcements_average_speed_pace">Media velocità/ritmo</string>
|
||||
<string name="settings_announcements_unit">Unità di misura della distanza/velocità</string>
|
||||
<string name="settings_sensor_bluetooth_service_filter_title">Filtra dispositivi Bluetooth</string>
|
||||
<string name="settings_sensor_bluetooth_service_filter_off">Mostra tutti i dispositivi Bluetooth che possono essere associati</string>
|
||||
<string name="settings_stats_units_imperial_meter">Imperiale (mi,m)</string>
|
||||
<string name="settings_stats_units_nautical">Nautiche (NM, ft)</string>
|
||||
<string name="settings_public_api_enabled_title">API pubblica</string>
|
||||
<string name="settings_public_api_enabled_summary_on">Altre applicazioni installate possono far partire o fermare le registrazioni.</string>
|
||||
<string name="settings_api_summary">API per controllo remoto e statistiche con altre applicazioni</string>
|
||||
<string name="settings_public_api_dashboard_enabled_summary_off">I dati registrati non saranno condivisi in automatico con altre applicazioni.</string>
|
||||
<string name="track_delete_number_of_tracks">%1$d tracce</string>
|
||||
<string name="voiceDistanceMilesPlural">{n, plural, =1 {un miglio} other {{n,number,#.0} miglia} }</string>
|
||||
<string name="no_compatible_gallery_installed">Nessuna delle applicazioni galleria (foto) installate è compatibile</string>
|
||||
<string name="no_compatible_camera_installed">Nessuna delle applicazioni telecamera installate è compatibile</string>
|
||||
<string name="voice_announce_lap_power_key">voiceAnnounceLapPower</string>
|
||||
<string name="settings_announcements_idle_category_title">Annunci di inattività</string>
|
||||
<string name="settings_announcements_idle_title">Inattività</string>
|
||||
<string name="settings_recording_title">Registrazione</string>
|
||||
<string name="settings_theme_switch_restart">Cambiamenti al tema potrebbero richiedere un riavvio manuale dell\'applicazione.</string>
|
||||
<string name="settings_ui_dynamic_colors_title">Colori Dinamici</string>
|
||||
<string name="settings_ui_dynamic_colors_summary">Utilizza Colori Dinamici definiti dall\'utente (si applica solo ad Android 12+; richiede un riavvio manuale dell\'applicazione)</string>
|
||||
<string name="settings_recording_min_required_accuracy_summary">La posizione viene ignorata se l\'accuratezza del segnale è maggiore di %1$s</string>
|
||||
<string name="voice_on_device_speaker_title">Utilizza l\'altoparlante dello smartphone</string>
|
||||
<string name="voice_per_nautical_mile">per miglio nautico</string>
|
||||
<string name="voiceDistanceNauticalMilesPlural">{n, plural, =1 {un miglio nautico} other {{n,number,#.0} miglia nautiche} }</string>
|
||||
<string name="current_heart_rate">Frequenza cardiaca istantanea</string>
|
||||
<string name="stats_gain_loss">Guadagno & Perdita</string>
|
||||
<string name="track_discarding">Traccia da cancellare</string>
|
||||
<string name="unit_knots">kn</string>
|
||||
<string name="value_float_knots">%1$.1f nodi</string>
|
||||
<string name="value_float_knots_recommended">%1$.1f nodi (consigliato)</string>
|
||||
<string name="value_internal_sensor">Sensore interno</string>
|
||||
<string name="value_int_seconds">%1$d s (consigliato)</string>
|
||||
<string name="voiceHoursPlural">{n, plural, =1 {un\'ora} other {{n,number} ore} }</string>
|
||||
<string name="voiceSecondsPlural">{n, plural, =1 {un secondo} other {{n,number} secondi} }</string>
|
||||
<string name="voiceSpeedKilometersPerHourPlural">{n, plural, =1 {un kilometro all\'ora} other {{n,number,#.0} kilometri all\'ora} }</string>
|
||||
<string name="voiceSpeedMilesPerHourPlural">{n, plural, =1 {un miglio all\'ora} other {{n,number,#.0} miglia all\'ora} }</string>
|
||||
<string name="voiceSpeedMKnotsPlural">{n, plural, =1 {1 nodo} other {{n,number,#.0} nodi} }</string>
|
||||
<string name="lap_time">Tempo del giro</string>
|
||||
<string name="speed">Velocità media in movimento</string>
|
||||
<string name="voiceDistanceKilometersPlural">{n, plural, =1 {un kilometro} other {{n,number,#.0} kilometri} }</string>
|
||||
<string name="settings_announcements_lap_power">Potenza del giro</string>
|
||||
<string name="power">Potenza</string>
|
||||
<string name="settings_recording_location_distance_summary">%1$s tra posizioni registrate</string>
|
||||
<string name="power_x_watt">Potenza {x, plural, =1 {un watt} other {# watt} }</string>
|
||||
<string name="voice_per_mile">per miglio</string>
|
||||
<string name="unit_nautical_mile">MN</string>
|
||||
<string name="help_data_google_play_services_content">No</string>
|
||||
<string name="generic_dismiss">Ignora</string>
|
||||
<string name="always">Sempre</string>
|
||||
<string name="activity_type_swimming">nuoto</string>
|
||||
<string name="gps_recording_status">%1$s: %2$s</string>
|
||||
<string name="activity_type_swimming_open">nuoto in mare</string>
|
||||
<string name="settings_public_api_enabled_summary_off">Solo OpenTracks può far partire o fermare le registrazioni.</string>
|
||||
<string name="pace">Ritmo</string>
|
||||
<string name="lap_speed">Velocità del giro</string>
|
||||
<string name="unit_minute_per_nautical_mile">min/NM</string>
|
||||
<string name="voiceMinutesPlural">{n, plural, =1 {un minuto} other {{n,number} minuti} }</string>
|
||||
<string name="settings_announcements_average_heart_rate">Frequenza cardiaca media</string>
|
||||
<string name="activity_type_kickscooter">monopattino</string>
|
||||
<string name="settings_recording_min_sampling_interval_title">Intervallo temporale di campionamento</string>
|
||||
<string name="create_marker_error">Impossibile marcare la posizione. Riprovare.</string>
|
||||
<string name="settings_export_filename_title">Formato del nome del file</string>
|
||||
<string name="settings_sensor_heart_rate_max">Zone: frequenza cardiaca massima (bpm)</string>
|
||||
<string name="settings_public_api_package_title">Nome del pacchetto per API pubblica</string>
|
||||
<string name="settings_public_api_dashboard_enabled_summary_on">Un\'applicazione che ha fatto partire una registrazione può anche accedere ai dati registrati.</string>
|
||||
<string name="voiceSpeed">{n, plural, =1 {1} other {{n,number,#.0}} }</string>
|
||||
<string name="voiceDistance">{n, plural, =1 {1 } other {{n,number,#.0}} }</string>
|
||||
<string name="lap_heart_rate">Frequenza cardiaca del giro</string>
|
||||
</resources>
|
||||
@@ -196,9 +196,7 @@ limitations under the License.
|
||||
<string name="settings_stats_units_metric">Metriske (km, m)</string>
|
||||
<string name="settings_stats_units_title">Foretrukne enheter</string>
|
||||
<!-- Share Track -->
|
||||
<string name="share_track_share_file_body">Jeg tror du kan være interessert i dette sporet.
|
||||
\n
|
||||
\n%1$s</string>
|
||||
<string name="share_track_share_file_body">Jeg tror du kan være interessert i dette sporet.\n\n%1$s</string>
|
||||
<string name="share_track_subject">Jeg vil dele et spor med deg</string>
|
||||
<!-- Stats -->
|
||||
<string name="stats_average_moving_pace">Gjen. forflytningstempo</string>
|
||||
@@ -359,12 +357,7 @@ limitations under the License.
|
||||
<string name="sensor_could_not_scan">Kunne ikke se etter Blåtannsenheter (feil %1$i).</string>
|
||||
<string name="help_data_google_play_services_title">Krever/bruker OpenTracks Google Play-tjenesten\?</string>
|
||||
<string name="help_feature_title">OpenTracks har følgende funksjoner:</string>
|
||||
<string name="help_feature_content">* Opprett GPS-spor
|
||||
\n * Bruk av pulssensor (kun Blåtann-LE)
|
||||
\n * Talemeldinger (etter tid eller distanse)
|
||||
\n * Opprett markører (inkl. bilder)
|
||||
\n * Fungerer uten Internett-forbindelse
|
||||
\n * Vis spor på et kart (se nedenfor)</string>
|
||||
<string name="help_feature_content">* Opprett GPS-spor \n* Bruk av pulssensor (kun Blåtann-LE) \n* Talemeldinger (etter tid eller distanse) \n* Opprett markører (inkl. bilder) \n* Fungerer uten Internett-forbindelse \n* Vis spor på et kart (se nedenfor)</string>
|
||||
<string name="help_track_map_title">Hvordan vise et spor på et kart\?</string>
|
||||
<string name="about_description"><i>OpenTracks</i> er et sportssporingsprogram som fullt ut respekterer ditt personvern.</string>
|
||||
<string name="about_url">Prosjektnettside: <a href="%1$s">%1$s</a></string>
|
||||
@@ -392,21 +385,8 @@ limitations under the License.
|
||||
<string name="help_data_storage_title">Hvordan lagrer OpenTracks sensordata\?</string>
|
||||
<string name="help_data_storage_content"> Sensordata lagres per posisjon som er tatt opp. Når det gjelder pulssensorer betyr dette at kun <i>én</i> pulsmåling lagres per posisjon. Hvis du står stille eller er innendørs, blir ingen sensordata tatt opp (men de vil bli vist). </string>
|
||||
<string name="help_gps_troubleshooting_title">OpenTracks tar ikke opp posisjoner\?</string>
|
||||
<string name="help_gps_troubleshooting_content"> Hva du kan gjøre:
|
||||
\n a) sjekk om GPS er slått på,
|
||||
\n b) forbedre GPS-mottak (f.eks. gå utendørs),
|
||||
\n c) sjekk med programmet <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/">GPSTest</a>, og
|
||||
\n d) slå av strømsparing.
|
||||
\n
|
||||
\n Hvis alt dette ikke hjelper, kan du rapportere en feil. </string>
|
||||
<string name="help_support_content"> Du kan:
|
||||
\n a) spre info om OpenTracks,
|
||||
\n b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">foreslå funksjoner</a>,
|
||||
\n c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">rapportere feil</a>,
|
||||
\n d) hjelpe til med programvareutvikling,
|
||||
\n e) kjøpe programmet i <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Google Play Store</a>,
|
||||
\n f) donere, og
|
||||
\n g) gjøre noe du tror hjelper OpenTracks. </string>
|
||||
<string name="help_gps_troubleshooting_content">Hva du kan gjøre:\n a) sjekk om GPS er slått på,\n b) forbedre GPS-mottak (f.eks. gå utendørs),\n c) sjekk med programmet <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/">GPSTest</a>, og\n d) slå av strømsparing.\n \n Hvis alt dette ikke hjelper, kan du rapportere en feil.</string>
|
||||
<string name="help_support_content">Du kan:\n a) spre info om OpenTracks,\n b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">foreslå funksjoner</a>,\n c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">rapportere feil</a>,\n d) hjelpe til med programvareutvikling,\n e) kjøpe programmet i <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Google Play Store</a>,\n f) donere, og\n g) gjøre noe du tror hjelper OpenTracks.</string>
|
||||
<string name="description_altitude_loss">Total nedstigning: %1$d m (%2$d ft)</string>
|
||||
<string name="stats_loss">Total nedstigning</string>
|
||||
<string name="import_prevent_reimport">Du har stilt inn forhindring av at spor importeres om igjen</string>
|
||||
@@ -547,7 +527,7 @@ limitations under the License.
|
||||
<string name="value_float_knots">%1$.1f knop</string>
|
||||
<string name="value_float_knots_recommended">%1$.1f knop (anbefales)</string>
|
||||
<string name="voice_per_nautical_mile">per nautisk mil</string>
|
||||
<string name="show_on_dashboard_not_installed">Ingen app som støtter OpenTracks-API for oversikten er installert.</string>
|
||||
<string name="show_on_dashboard_not_installed">Ingen app som støtter OpenTracks Dashboard-API er installert.</string>
|
||||
<string name="track_delete_number_of_tracks">%1$d spor</string>
|
||||
<string name="settings_public_api_package_title">Pakkenavn for offentlig API</string>
|
||||
<string name="introduction_osm_dashboard">OpenTracks sørger ikke selv for kart. Installer OSMDashboard for å vise opptakene dine på et kart.</string>
|
||||
@@ -595,4 +575,7 @@ limitations under the License.
|
||||
<string name="settings_sensor_bluetooth_service_filter_off">Vis alle Bluetooth-enheter som kan oppdages</string>
|
||||
<string name="permission_recording_failed">OpenTracks krever tillatelse for å få tilgang til posisjonen din samt enheter i nærheten.</string>
|
||||
<string name="create_marker_error">Klarte ikke å opprette en markør. Prøv igjen.</string>
|
||||
<string name="settings_locale_title">Språk</string>
|
||||
<string name="settings_locale_system_default">Systemstandard</string>
|
||||
<string name="stats_gain_loss">Total stigning og nedstigning</string>
|
||||
</resources>
|
||||
@@ -14,7 +14,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
--><resources>
|
||||
<string name="about_preference_title">Over OpenTracks</string>
|
||||
<string name="about_preference_title">Over</string>
|
||||
<string name="about_description"><i>OpenTracks</i> is een sport tracker applicatie met respect voor privacy.</string>
|
||||
<string name="about_url">Project webpagina: <a href="%1$s">%1$s</a></string>
|
||||
<string name="about_version_name">Versienaam: %1$s</string>
|
||||
@@ -30,38 +30,22 @@ limitations under the License.
|
||||
\n* werkt volledig offline
|
||||
\n* toon tracks op een kaart (zie hieronder)</string>
|
||||
<string name="help_track_map_title">Hoe wordt een spoor op een kaart weergegeven\?</string>
|
||||
<string name="help_track_map_content"> \u0020Gebruik het kleine kaartpictogram.
|
||||
\n \u0020a) \u0020(live updates) Installeer <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>. Het biedt online of offline kaarten via <a href="https://www.openstreetmap.org">OpenStreetMap</a>.
|
||||
\n \u0020Eigenlijk is OSMDashboard een aparte app die alleen (tijdelijk) toegang heeft tot de gegevens van de track(s) die getoond gaan worden.
|
||||
\n \u0020b) \u0020Via een normale kaartapplicatie (bijv. <a href="http://osmand.net/">OsmAnd</a> en <a href="https://maps.me/">MAPS.ME</a>). Deze apps moeten het KMZ-bestandsformaat ondersteunen (alleen locaties en tijdstempels). In feite moet een app <i>ACTION_VIEW</i> ondersteunen met MIME <i>application/vnd.google-earth.kmz</i>. \u0020</string>
|
||||
<string name="help_track_map_content">Gebruik het kleine kaartpictogram. \n a) (live updates) Installeer <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>. Het biedt online of offline kaarten via <a href="https://www.openstreetmap.org">OpenStreetMap</a>. \n Eigenlijk is OSMDashboard een aparte app die alleen (tijdelijk) toegang heeft tot de gegevens van de track(s) die getoond gaan worden. \n b) Via een normale kaartapplicatie (bijv. <a href="http://osmand.net/">OsmAnd</a> en <a href="https://maps.me/">MAPS.ME</a>). Deze apps moeten het KMZ-bestandsformaat ondersteunen (alleen locaties en tijdstempels). In feite moet een app <i>ACTION_VIEW</i> ondersteunen met MIME <i>application/vnd.google-earth.kmz</i>.</string>
|
||||
<string name="help_storage_pictures_title">Hoe slaat OpenTracks foto\'s op die als markeringen zijn genomen\?</string>
|
||||
<string name="help_storage_pictures_content">Deze worden opgeslagen in de interne opslag van de app (d.w.z. alleen toegankelijk binnen OpenTracks).</string>
|
||||
<string name="help_track_export_title">Hoe kan ik opgenomen tracks exporteren/delen?</string>
|
||||
<string name="help_track_export_content"> Tracks kunnen worden geëxporteerd vanuit de instellingen als KMZ, KML of GPX. KMZ/KML alle gegevens; GPX alleen locaties. Tracks die worden gedeeld via het <i>share-icoontje</i> worden gedeeld als KMZ </string>
|
||||
<string name="help_track_export_content">Tracks kunnen worden geëxporteerd vanuit de instellingen als KMZ, KML of GPX. KMZ/KML alle gegevens; GPX alleen locaties. Tracks die worden gedeeld via het <i>share-icoontje</i> worden gedeeld als KMZ.</string>
|
||||
<string name="help_data_google_play_services_title">Vereist/gebruikt OpenTracks Google Play Service\?</string>
|
||||
<string name="help_recording_title">Hoe legt OpenTracks gegevens vast?</string>
|
||||
<string name="help_recording_content">
|
||||
OpenTracks gebruikt de GPS-sensor van het apparaat om de locatie continu te registreren.
|
||||
Als het GPS-apparaat onnauwkeurige gegevens rapporteert (bijv. locatie, snelheid, hoogte), kan OpenTracks daar niets aan doen.</string>
|
||||
<string name="help_data_storage_title">Hoe slaat OpenTracks sensorgegevens op?</string>
|
||||
<string name="help_data_storage_content"> De sensorgegevens worden per opgenomen locatie opgeslagen. In termen van een hartslagsensor betekent dit dat slechts <i>een</i> hartslagmeting per locatie wordt opgeslagen. Als u stilstaat of binnenshuis bent, worden er geen sensorgegevens geregistreerd (maar wel getoond) </string>
|
||||
<string name="help_data_storage_content">De sensorgegevens worden per opgenomen locatie opgeslagen. In termen van een hartslagsensor betekent dit dat slechts <i>een</i> hartslagmeting per locatie wordt opgeslagen. Als u stilstaat of binnenshuis bent, worden er geen sensorgegevens geregistreerd (maar wel getoond).</string>
|
||||
<string name="help_gps_troubleshooting_title">OpenTracks neemt geen locaties op?</string>
|
||||
<string name="help_gps_troubleshooting_content"> \u0020Wat u kunt doen:
|
||||
\n \u0020a) Controleer of GPS is ingeschakeld,
|
||||
\n \u0020b) verbeter de GPS-ontvangst (ga bijvoorbeeld naar buiten),
|
||||
\n \u0020c) controleren met <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/">GPSTest</a> app, en
|
||||
\n \u0020d) schakel energiebesparing uit.
|
||||
\n
|
||||
\n \u0020Als alles niet helpt, rapporteer dan een mogelijke fout. \u0020</string>
|
||||
<string name="help_gps_troubleshooting_content">Wat u kunt doen:\n a) Controleer of GPS is ingeschakeld,\n b) verbeter de GPS-ontvangst (ga bijvoorbeeld naar buiten),\n c) controleren met <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/">GPSTest</a> app, en\n d) schakel energiebesparing uit.\n \n Als alles niet helpt, rapporteer dan een mogelijke fout.</string>
|
||||
<string name="help_support_title">Hoe kan ik OpenTracks ondersteunen?</string>
|
||||
<string name="help_support_content"> \u0020U kunt:
|
||||
\n \u0020a) anderen vertellen over OpenTracks,
|
||||
\n \u0020b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">functies voorstellen</a>,
|
||||
\n \u0020c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">bugs melden</a>,
|
||||
\n \u0020d) helpen bij de ontwikkeling van software,
|
||||
\n \u0020e) aanschaffen in <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Google Play Store</a>,
|
||||
\n \u0020f) doneren, en
|
||||
\n \u0020g) wat u ook denkt dat OpenTracks helpt. \u0020</string>
|
||||
<string name="help_support_content">U kunt:\n a) anderen vertellen over OpenTracks,\n b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">functies voorstellen</a>,\n c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">bugs melden</a>,\n d) helpen bij de ontwikkeling van software,\n e) aanschaffen in <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Google Play Store</a>,\n f) doneren, en\n g) wat u ook denkt dat OpenTracks helpt.</string>
|
||||
<!-- Activity Type -->
|
||||
<string name="activity_type_airplane">vliegtuig</string>
|
||||
<string name="activity_type_atv">quad</string>
|
||||
@@ -536,7 +520,7 @@ Als het GPS-apparaat onnauwkeurige gegevens rapporteert (bijv. locatie, snelheid
|
||||
<string name="report_bug">Bug rapporteren</string>
|
||||
<string name="share_crash_report">Deel het crash rapport</string>
|
||||
<string name="settings_announcements_average_heart_rate">Gemiddelde hartslag</string>
|
||||
<string name="settings_announcements_lap_heart_rate">Hartslag per ronde</string>
|
||||
<string name="settings_announcements_lap_heart_rate">Ronde-hartslag</string>
|
||||
<string name="image_discard">Weggooien</string>
|
||||
<string name="settings_announcements_average_speed_pace">Gemiddelde snelheid/tred</string>
|
||||
<string name="track_discarding">Track weggooien</string>
|
||||
@@ -607,4 +591,8 @@ Als het GPS-apparaat onnauwkeurige gegevens rapporteert (bijv. locatie, snelheid
|
||||
<string name="settings_announcements_lap_power">Lap power</string>
|
||||
<string name="power_x_watt">Power {x, plural, =1 {1 watt} other {# watt} }</string>
|
||||
<string name="create_marker_error">Kan geen markering maken. Probeer het opnieuw.</string>
|
||||
<string name="settings_locale_title">Taal</string>
|
||||
<string name="settings_locale_system_default">Systeemstandaard</string>
|
||||
<string name="stats_gain_loss">Winst & verlies</string>
|
||||
<string name="lap_heart_rate">Ronde-hartslag</string>
|
||||
</resources>
|
||||
@@ -250,9 +250,13 @@
|
||||
<bool name="voice_announce_average_speed_pace_default" translatable="false">true</bool>
|
||||
<string name="voice_announce_lap_speed_pace_key" translatable="false">voiceAnnounceLapSpeedPace</string>
|
||||
<bool name="voice_announce_lap_speed_pace_default" translatable="false">true</bool>
|
||||
<string name="voice_announce_time_key" translatable="false">voiceAnnounceTime</string>
|
||||
<bool name="voice_announce_time_default" translatable="false">true</bool>
|
||||
<string name="voice_announce_unit_key" translatable="false">voiceAnnounceUnit</string>
|
||||
<bool name="voice_announce_unit_default" translatable="false">true</bool>
|
||||
|
||||
<string name="voice_announce_heart_rate_current_key" translatable="false">voiceAnnounceHeartRateCurrent</string>
|
||||
<bool name="voice_announce_heart_rate_current_default" translatable="false">false</bool>
|
||||
<string name="voice_announce_average_heart_rate_key" translatable="false">voiceAnnounceAverageHeartRate</string>
|
||||
<bool name="voice_announce_average_heart_rate_default" translatable="false">false</bool>
|
||||
<string name="voice_announce_lap_heart_rate_key" translatable="false">voiceAnnounceLapHeartRate</string>
|
||||
|
||||
@@ -609,6 +609,7 @@ limitations under the License.
|
||||
</string>
|
||||
<string name="average_heart_rate">Average heart rate</string>
|
||||
<string name="current_heart_rate">Current heart rate</string>
|
||||
<string name="lap_heart_rate">Lap heart rate</string>
|
||||
<!-- Waypoint Type -->
|
||||
<string name="waypoint_type_atm">ATM</string>
|
||||
<string name="waypoint_type_bank">bank</string>
|
||||
|
||||
@@ -39,6 +39,11 @@
|
||||
android:title="@string/settings_announcements_totaltime_frequency"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="@bool/voice_announce_time_default"
|
||||
android:key="@string/voice_announce_time_key"
|
||||
android:title="@string/description_time" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="@bool/voice_announce_total_distance_default"
|
||||
android:key="@string/voice_announce_total_distance_key"
|
||||
@@ -54,6 +59,11 @@
|
||||
android:key="@string/voice_announce_average_speed_pace_key"
|
||||
android:title="@string/settings_announcements_average_speed_pace" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="@bool/voice_announce_heart_rate_current_default"
|
||||
android:key="@string/voice_announce_heart_rate_current_key"
|
||||
android:title="@string/current_heart_rate" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="@bool/voice_announce_average_heart_rate_default"
|
||||
android:key="@string/voice_announce_average_heart_rate_key"
|
||||
|
||||
Reference in New Issue
Block a user