forked from upstream-mirrors/OpenTracks
Compare commits
96 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| 721b54b06f | |||
| b65230bc5d | |||
| 7c8fae66ef | |||
| ef73f89fc9 | |||
| c4579eb91f | |||
| a015b87cc3 | |||
| 27e38410d7 | |||
| 714f1e715a | |||
| c8183949e2 | |||
| 4313b51e1e | |||
| e4eebb92d3 | |||
| 61f496926a | |||
| ba3c8f6a2d | |||
| a5ba6fb0dc | |||
| e0a28923fe | |||
| 4f05b65f32 | |||
| bec9be8402 | |||
| a46de8f872 | |||
| 993c2a1617 | |||
| cddfbf8b80 | |||
| cba55eab74 | |||
| 0b4a18deb4 | |||
| 7cd054f591 | |||
| 2cc4218dc6 | |||
| d9de56be33 | |||
| fa1496d130 | |||
| 89059caec2 | |||
| 8f1d8e2881 | |||
| 54e456046e | |||
| 1775c52291 | |||
| b50965f0e8 | |||
| d9bba7852c | |||
| 5ff91a98f4 | |||
| 79f62bd3db | |||
| c387729301 | |||
| 1094059fb8 | |||
| affb4e1053 | |||
| 0547a14fbd | |||
| 39d6c9a298 | |||
| 9c51a31b01 | |||
| f7f58141de | |||
| e46d3cd06c | |||
| a40b87efb6 | |||
| e98dff55ae | |||
| f0b7de4c7c | |||
| f0f571480b | |||
| 072fdb12ed | |||
| fda2fae02b | |||
| a09fa93887 | |||
| 8f0ed91f80 | |||
| e5a60a8141 | |||
| c7d3c19919 |
@@ -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
|
||||
|
||||
@@ -7,18 +7,27 @@ on:
|
||||
jobs:
|
||||
test:
|
||||
timeout-minutes: 45
|
||||
runs-on: macos-latest
|
||||
runs-on: ubuntu-latest
|
||||
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:
|
||||
distribution: 'adopt'
|
||||
java-version: '17'
|
||||
|
||||
- name: Enable KVM
|
||||
run: |
|
||||
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger --name-match=kvm
|
||||
|
||||
- name: run tests
|
||||
uses: reactivecircus/android-emulator-runner@v2.32.0
|
||||
uses: reactivecircus/android-emulator-runner@v2.33.0
|
||||
with:
|
||||
arch: x86_64
|
||||
api-level: 34
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -66,6 +66,7 @@ We do not support type of movement (i.e., walking vs running).
|
||||
### 0x2A6D: Environmental Sensing Service
|
||||
Sadly, Bluetooth sensors providing _air pressure_ via Environmental Sensing Service seem to be very rare.
|
||||
|
||||
* [Flybeeper Pressure Sensor](https://market.flybeeper.com/device/ps1)
|
||||
* NoTrustIssues: https://github.com/OpenTracksApp/NoTrustIssues
|
||||
An Android app that exposes the internal barometer via Environmental Sensing Service.
|
||||
|
||||
|
||||
+58
-34
@@ -6,7 +6,7 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.7.0'
|
||||
classpath 'com.android.tools.build:gradle:8.7.3'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,25 @@ android {
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "de.dennisguse.opentracks"
|
||||
versionCode 5889
|
||||
versionName "v4.14.0"
|
||||
applicationId 'de.dennisguse.opentracks'
|
||||
versionCode 5985
|
||||
versionName "v4.17.4"
|
||||
|
||||
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')
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,60 +90,84 @@ 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'
|
||||
}
|
||||
}
|
||||
|
||||
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 == 'reproducible') {
|
||||
outputFileName = "${applicationId}_${variant.versionName}.apk"
|
||||
} else {
|
||||
outputFileName = "${applicationId}_${variant.versionCode}.apk"
|
||||
}
|
||||
|
||||
if (variant.flavorName== 'nightly') {
|
||||
setVersionCodeOverride(getVersionCode())
|
||||
setVersionNameOverride(getVersionName())
|
||||
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.2'
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.4'
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||
implementation 'androidx.preference:preference:1.2.1'
|
||||
implementation 'androidx.documentfile:documentfile:1.0.1'
|
||||
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
||||
implementation 'com.google.android.material:material:1.12.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
implementation 'androidx.core:core:1.13.1'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.2.0'
|
||||
implementation 'androidx.core:core:1.15.0'
|
||||
implementation 'androidx.core:core-splashscreen:1.0.1'
|
||||
implementation 'androidx.mediarouter:mediarouter:1.7.0'
|
||||
implementation 'androidx.core:core-location-altitude:1.0.0-alpha02'
|
||||
implementation 'androidx.core:core-location-altitude:1.0.0-alpha03'
|
||||
|
||||
androidTestImplementation 'androidx.test:core:1.6.1'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
|
||||
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.1'
|
||||
androidTestImplementation 'org.mockito:mockito-android:5.14.2'
|
||||
|
||||
androidTestUtil 'androidx.test:orchestrator:1.5.0'
|
||||
androidTestUtil 'androidx.test:orchestrator:1.5.1'
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
height="188"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
viewBox="0 0 78 188"
|
||||
width="78"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<defs id="defs6" />
|
||||
<g id="layer1" />
|
||||
<g
|
||||
style="display:inline"
|
||||
id="layer2" />
|
||||
<metadata id="metadata8">
|
||||
<rdf:RDF>
|
||||
<cc:Work rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:title />
|
||||
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<path
|
||||
style="fill:#cccccc;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:0.7019608"
|
||||
d="m 1,150 38,37 39,-37 H 50 V 97 L 29,81 v 69 z"
|
||||
id="path31161" />
|
||||
<path
|
||||
style="fill:#cccccc;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:0.7019608"
|
||||
d="M 29,51 V 70 L 50,86 V 67 Z"
|
||||
id="path31169" />
|
||||
<path
|
||||
style="fill:#cccccc;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:0.7019608"
|
||||
d="M 29,23 V 40 L 50,56 V 39 Z"
|
||||
id="path31192" />
|
||||
<path
|
||||
style="fill:#cccccc;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:0.7019608"
|
||||
d="M 29,0 V 14 L 50,29 V 16 Z"
|
||||
id="path31194" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,7 @@
|
||||
v4.14.0: OpenTracks
|
||||
|
||||
Změny:
|
||||
- Použití dat Androidx EGM2008 (namísto přibalení našeho vlastního řešení).
|
||||
|
||||
Vývojář:
|
||||
- Cíl Android 15 / SDK 35
|
||||
@@ -0,0 +1,7 @@
|
||||
v4.14.1: OpenTracks
|
||||
|
||||
Změny:
|
||||
- Detekce zastavení může být zakázána
|
||||
|
||||
Oprava chyby:
|
||||
- Rozhraní přidávání obrázků ke značkám bylo skryto
|
||||
@@ -0,0 +1,4 @@
|
||||
v4.14.2: OpenTracks
|
||||
|
||||
Oprava chyby:
|
||||
- Android 15: rozhraní již nepřetéká (edge-to-edge)
|
||||
@@ -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,8 @@
|
||||
v4.14.1: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Idle detection can be disabled
|
||||
|
||||
Bugfix:
|
||||
- UI for adding pictures to markers was hidden
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
v4.14.2: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- Android 15: UI is not overflowing anymore (edge-to-edge)
|
||||
@@ -0,0 +1,8 @@
|
||||
v4.15.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- In-app language selector (Android 14 or later)
|
||||
- Show-on-map-feature only supports OSM Dashboard (removed KML/KMZ or GPX)
|
||||
|
||||
Developer:
|
||||
- Github actions now uses Ubuntu
|
||||
@@ -0,0 +1,5 @@
|
||||
v4.16.0: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- PublicAPI: starting a recording didn't record no data
|
||||
|
||||
@@ -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,7 @@
|
||||
v4.14.1: OpenTracks
|
||||
|
||||
Cambios:
|
||||
- Detección de inactividad se puede desactivar
|
||||
|
||||
Correcciones de errores:
|
||||
- UI para añadir imágenes a los marcadores estaba oculto
|
||||
@@ -0,0 +1,15 @@
|
||||
v3.0.0 : OpenTracks
|
||||
|
||||
Première édition de OpenTracks (c'est-à-dire bifurcation du dépôt depuis MyTracks)
|
||||
|
||||
Changements:
|
||||
- Renommé de MyTracks à OpenTracks
|
||||
- Capteur de fréquence cardiaque par Bluetooth LE ajouté
|
||||
- Toutes les fonctionnalités nécessitant une connexion Internet ont été enlevées
|
||||
- Intégration Google Maps enlevée
|
||||
- Toutes les utilisations des services Google Play ont été enlevées
|
||||
- Capteurs ANT+ enlevés
|
||||
- Capteurs Bluetooth classiques enlevés
|
||||
- Protobuf enlevé
|
||||
- Estimations de calories et d'activité enlevés
|
||||
- API Service MyTracks enlevée (c'est-à-dire, pas d'intégration avec les autres applications)
|
||||
@@ -0,0 +1,17 @@
|
||||
v3.0.1 : OpenTracks
|
||||
|
||||
Changements :
|
||||
− Partage des marqueurs
|
||||
− Restructuration des paramètres
|
||||
− Suppression du bouton retour de la barre d'action
|
||||
− Exportation : suppression de l'option de paramétrage de la taille de photo
|
||||
− Exportation : KML est désormais sélectionnable en tant que format
|
||||
|
||||
Correction de bogues :
|
||||
− Le répertoire d'export est désormais /OpenTracks/
|
||||
− Le mode paysage ne plante plus
|
||||
− Correction de l'export KML/KMZ
|
||||
|
||||
Développeurs :
|
||||
− Les tests sont exécutables (mais beaucoup d'échecs)
|
||||
− Activation de l'intégration continue avec TravisCI (pas de tests)
|
||||
@@ -0,0 +1,14 @@
|
||||
v3.1.0 : OpenTracks
|
||||
|
||||
Changements :
|
||||
− Ajout de l'option pour montrer les traces sur une application de carte (testé avec OsmAnd et Maps.ME)
|
||||
− Ajout des statistiques de la trace sans débloquer l'appareil, durant le tracé
|
||||
− Suppression du mode paysage pour la vue détaillée de la trace (était bogué)
|
||||
− Suppression des suggestions pour l'historique de recherche
|
||||
|
||||
Correction de bogue :
|
||||
− Demande des permissions de stockage pour tout importer / tout exporter
|
||||
|
||||
Développeurs :
|
||||
− Mise à niveau vers Gradle 5.6
|
||||
− Ne pas minifier les compilations release
|
||||
@@ -0,0 +1,16 @@
|
||||
v3.2.0 : OpenTracks
|
||||
|
||||
Changements :
|
||||
− Suppression Widget
|
||||
− Introduction des icons material design
|
||||
− Suppression du snowboard et du ski (besoin de nouveaux icons)
|
||||
|
||||
Correction de bogue :
|
||||
− Correction de la fonctionnalité de recherche
|
||||
|
||||
Développeurs :
|
||||
− Correction de (quelques) cas de test
|
||||
− Suppression de fonctionnalités parcelable
|
||||
− Nettoyage majeur et reformatage
|
||||
− Ne pas utiliser la diffusion pour la communication app widget
|
||||
− stats_content.xml ne contient pas d'autres fichiers
|
||||
@@ -0,0 +1,7 @@
|
||||
v3.2.1 : OpenTracks
|
||||
|
||||
Changements :
|
||||
− Les paramètres sont désormais une page unique
|
||||
|
||||
Développeurs :
|
||||
− Paramètres passé à Androidx compat (utilisation de fragments)
|
||||
@@ -0,0 +1,13 @@
|
||||
v3.2.2 : OpenTracks
|
||||
|
||||
Changements :
|
||||
− Suppression des statistiques agrégées
|
||||
− Migré vers Material Design
|
||||
|
||||
Correction de bogues :
|
||||
− Correction de l'export de données de capteurs
|
||||
− Correction de l'import de données de capteurs incomplètes
|
||||
|
||||
Développeurs :
|
||||
− Remplacement de la barre d'action par une barre d'outil
|
||||
− Migré vers androidx appcompat (incluant Material Design)
|
||||
@@ -0,0 +1,14 @@
|
||||
v3.2.3 : OpenTracks
|
||||
|
||||
Changements :
|
||||
− Partage : utilise Android Sharesheet
|
||||
− Partage : partage de plusieurs tracés en tant que fichier individuel
|
||||
− Utilisation d'Android ShareSheet
|
||||
− Capteur de fréquence cardiaque (Bluetooth LE) : reconnexion automatique
|
||||
|
||||
Correction de bogues :
|
||||
− Capteur de fréquence cardiaque (Bluetooth LE) : déconnecte correctement
|
||||
− La fréquence cardiaque n'était pas toujours montrée lors de l'enregistrement
|
||||
|
||||
Développeurs :
|
||||
− Activation des tests d'instrumentation de TravisCI
|
||||
@@ -0,0 +1,5 @@
|
||||
v3.2.4 : OpenTracks
|
||||
|
||||
Correction de bogue :
|
||||
− Plantage activité paramètres
|
||||
Suppression de la possibilité de change la voix / la fréquence de division depuis le menu contextuel
|
||||
@@ -0,0 +1,15 @@
|
||||
v3.2.5 : OpenTracks
|
||||
|
||||
Changements :
|
||||
− Désactivation du changement d'activité
|
||||
− Ajout des permissions pour Android 10 : enregistrement des données de localisation
|
||||
|
||||
Correction de bogue :
|
||||
− Correction des paramètres de réinitialisation
|
||||
|
||||
Développeurs :
|
||||
− Préférences
|
||||
− Si les paramètres ne peuvent être chargés, remise à zéro de toutes les préférences
|
||||
− Déplacement de l'accès des préférences vers PreferencesUtil
|
||||
− Déplacement paramètres défaut préférences de code à XML
|
||||
− Utilise SummaryProvider
|
||||
@@ -0,0 +1,8 @@
|
||||
v3.2.6 : OpenTracks
|
||||
|
||||
Changements :
|
||||
− Moniteur de fréquence cardiaque : scanne les appareils disponibles
|
||||
− Suppression du raccourci vers les paramètres Bluetooth
|
||||
|
||||
Développeurs :
|
||||
− Préférences : défaut de préférences déplacé de code à XML
|
||||
@@ -0,0 +1,10 @@
|
||||
v3.2.7 : OpenTracks
|
||||
|
||||
Changements :
|
||||
− Notification montre la précision GPS
|
||||
− Montre une notification d'avertissement si la précision GPS descend en dessous du seuil
|
||||
− Montre toujours les graphiques en fonction du temps, ainsi que par la distance
|
||||
− Ajout de l'aide (dans les paramètres)
|
||||
|
||||
Correction de bogue :
|
||||
- TrackDetailActivity : désactivation du mode paysage non fonctionnel (temporaire)
|
||||
@@ -0,0 +1,10 @@
|
||||
v3.2.8 : OpenTracks
|
||||
|
||||
Changements :
|
||||
− Graphique : zoom en effectuant des gestes
|
||||
|
||||
Correction de bogues :
|
||||
− L'export via KMZ n'exportait pas le nom des images correctement
|
||||
− Le partage de plusieurs tracés n'incluait pas les fichiers KMZ
|
||||
− L'IU de MarkerDetailActivity n'était pas montré
|
||||
− Les paramètres plantaient si les préférences pour le rythme cardiaque n'étaient pas initialisées
|
||||
@@ -0,0 +1,10 @@
|
||||
v3.2.9 : OpenTracks
|
||||
|
||||
Changements :
|
||||
− Option pour garder l'écran allumé lors de l'enregistrement
|
||||
− Marqueurs : suppression des marqueurs divisés
|
||||
− L'annonceur vocal fonctionne désormais sans la permission READ_PHONE_STATE
|
||||
|
||||
Correction de bogues :
|
||||
− Statistiques : le rythme cardiaque n'était pas montré
|
||||
− Statistiques : le temps était brièvement remis à 0s
|
||||
@@ -0,0 +1,11 @@
|
||||
v3.2.10 : OpenTracks
|
||||
|
||||
Changements :
|
||||
− ChartView n'est désormais plus configurable
|
||||
− Après redémarrage : suppression du redémarrage automatique d'un enregistrement incomplet
|
||||
|
||||
Correction de bogues :
|
||||
− L'annonceur vocal utilise MAY_DUCK (à nouveau)
|
||||
− ChartView montre les marqueurs (à nouveau)
|
||||
− Correction du paysage de recordingLayout pour TrackListActivity
|
||||
− Demande du comportement de l'API des fichiers obsolètes (nécessaire pour Android 10)
|
||||
@@ -0,0 +1,12 @@
|
||||
v3.2.11 : OpenTracks
|
||||
|
||||
Bogues :
|
||||
− La vitesse maximale est égale à la vitesse moyenne
|
||||
|
||||
Correction de bogue :
|
||||
− Plantage lors de la sélection du capteur de rythme cardiaque
|
||||
|
||||
Développeurs :
|
||||
− Remplacement de TabHost par TabsLayout
|
||||
− Suppression premier point de cheminement spécial
|
||||
− Suppression des traductions dupliquées
|
||||
@@ -0,0 +1,12 @@
|
||||
v3.3.1 : OpenTracks
|
||||
|
||||
Changements :
|
||||
− Android 10 : lors de la désinstallation, permet la conservation des données de l'app
|
||||
− Introduction de l'API du tableau de bord : l'icon de la carte peut être utilisé pour ouvrir le tracé actuel dans une app externe
|
||||
(nécessite l'installation d'une app comme OSMDashboard)
|
||||
|
||||
Correction de bogue :
|
||||
− Le label de l'app était cassé (introduit en v3.3.0)
|
||||
|
||||
Développeurs :
|
||||
− La documentation pour l'API du tableau de bord se situe dans README_DEVELOPER.md
|
||||
@@ -0,0 +1,11 @@
|
||||
v3.3.2 : OpenTracks
|
||||
|
||||
Changements :
|
||||
− Export : écrase désormais les fichiers
|
||||
|
||||
Correction de bogue :
|
||||
− Supprime toujours la notification d'enregistrement
|
||||
|
||||
Développeurs :
|
||||
− Sur l'API 26+, java.time est utilisé pour analyser et écrire les dates
|
||||
− Réusinage de TrackRecordingService
|
||||
@@ -0,0 +1,12 @@
|
||||
v3.4.0 : OpenTracks
|
||||
|
||||
Changements :
|
||||
− API tableau de bord : plusieurs tracés
|
||||
− Mise à jour de l'aide
|
||||
|
||||
Correction de bogues :
|
||||
− Correction du plantage lors de la recréation de fragment
|
||||
− Lors d'une pause : empêche l'app de s'arrêter
|
||||
|
||||
Développeurs :
|
||||
− Activation de tests via CI (Github Actions et TravisCI)
|
||||
@@ -0,0 +1,14 @@
|
||||
v3.5.0 : OpenTracks
|
||||
|
||||
Changements :
|
||||
− Marqueur : sélection d'une image depuis la galerie
|
||||
− Marqueur : partager une image
|
||||
− Utilisation du nom de la trace lors du partage
|
||||
− Suppression de l’élévation min et max (était imprécise)
|
||||
|
||||
Correction de bogue :
|
||||
− Les graphiques utilisent les unités et quantités préférées
|
||||
|
||||
Développeurs :
|
||||
− Base de données : suppression des colonnes inutilisées
|
||||
− Base de données : Ajout d'index
|
||||
@@ -0,0 +1,10 @@
|
||||
v3.6.0 : OpenTracks
|
||||
|
||||
Changements :
|
||||
− Les traces arrêtées peuvent être reprises
|
||||
|
||||
Correction de bogue :
|
||||
− L'import de KMZ ne traitait pas les URL de photos correctement
|
||||
|
||||
Développeurs :
|
||||
− Simplification du traitement des données
|
||||
@@ -0,0 +1,7 @@
|
||||
v4.14.0 : OpenTracks
|
||||
|
||||
Changements :
|
||||
− Utilisation des données EGM2008 d'Androidx (au lieu d'empaqueter notre propre solution).
|
||||
|
||||
Développeurs :
|
||||
− Cible Android15/ SDK35
|
||||
@@ -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,4 @@
|
||||
v4.14.2 : OpenTracks
|
||||
|
||||
Correction de bogue :
|
||||
− Android 15 : l’interface utilisateur ne débordera plus (bord-à-bord / edge-to-edge)
|
||||
@@ -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,10 @@
|
||||
(versionName) : OpenTracks
|
||||
|
||||
Changements :
|
||||
−
|
||||
|
||||
Correction de bogue(s) :
|
||||
−
|
||||
|
||||
Développeurs :
|
||||
−
|
||||
@@ -0,0 +1,7 @@
|
||||
v4.14.0: OpenTracks
|
||||
|
||||
Athruithe:
|
||||
- Úsáid sonraí Androidx EGM2008 (in ionad ár réiteach saincheaptha a chuachadh).
|
||||
|
||||
Forbróir:
|
||||
- Dírigh ar Android15/ SDK35
|
||||
@@ -0,0 +1,7 @@
|
||||
v4.14.1: OpenTracks
|
||||
|
||||
Athruithe:
|
||||
- Is féidir brath díomhaoin a dhíchumasú
|
||||
|
||||
Ceartúchán Bug:
|
||||
- Bhí Chomhéadain chun pictiúir a chur le marcóirí i bhfolach
|
||||
@@ -0,0 +1,4 @@
|
||||
v4.14.2: OpenTracks
|
||||
|
||||
Ceartúchán Bug:
|
||||
- Android 15: Níl an Chomhéadain ag cur thar maoil níos mó (ó imeall go himeall)
|
||||
@@ -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.14.0: OpenTracks
|
||||
|
||||
Veranderingen:
|
||||
- Gebruik de EGM2008-gegevens van Androidx (in plaats van onze aangepaste oplossing te bundelen).
|
||||
|
||||
Ontwikkelaar:
|
||||
- Target Android15 / SDK35
|
||||
@@ -0,0 +1,7 @@
|
||||
v4.14.1: OpenTracks
|
||||
|
||||
Veranderingen:
|
||||
- Detectie van inactiviteit kan worden uitgeschakeld
|
||||
|
||||
Opgeloste probleemoplossing:
|
||||
- Gebruikersinterface voor het toevoegen van afbeeldingen aan markeringen is verborgen
|
||||
@@ -0,0 +1,4 @@
|
||||
v4.14.2: OpenTracks
|
||||
|
||||
Bugfix:
|
||||
- Android 15: gebruikersinterface loopt niet meer over (rand tot rand)
|
||||
@@ -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)
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
@@ -84,7 +86,8 @@ done
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||
' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
Vendored
+2
@@ -13,6 +13,8 @@
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
|
||||
+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());
|
||||
|
||||
@@ -31,13 +31,7 @@ public class PublicApiTest {
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
@Test
|
||||
public void StartTest() {
|
||||
PreferencesUtils.setBoolean(R.string.publicapi_enabled_key, true);
|
||||
|
||||
context.startActivity(IntentUtils.newIntent(context, StartRecording.class));
|
||||
}
|
||||
|
||||
//NOTE: this doesn't check if the TrackRecordingService was started in foreground.
|
||||
@Test
|
||||
public void StartStopTest() throws InterruptedException {
|
||||
PreferencesUtils.setBoolean(R.string.publicapi_enabled_key, true);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -286,6 +286,11 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
return true;
|
||||
}
|
||||
|
||||
if (item.getItemId() == R.id.track_list_about) {
|
||||
startActivity(IntentUtils.newIntent(this, AboutActivity.class));
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -33,11 +33,15 @@ public abstract class AbstractAPIActivity extends AppCompatActivity {
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
SplashScreen splashScreen = SplashScreen.installSplashScreen(this);
|
||||
super.onCreate(savedInstanceState);
|
||||
splashScreen.setKeepOnScreenCondition(() -> true );
|
||||
splashScreen.setKeepOnScreenCondition(() -> true);
|
||||
|
||||
if (PreferencesUtils.isPublicAPIenabled()) {
|
||||
Log.i(TAG, "Received and trying to execute requested action.");
|
||||
TrackRecordingServiceConnection.execute(this, serviceConnectedCallback);
|
||||
if (requiresForeground()) {
|
||||
TrackRecordingServiceConnection.executeForeground(this, serviceConnectedCallback);
|
||||
} else {
|
||||
TrackRecordingServiceConnection.execute(this, serviceConnectedCallback);
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this, getString(R.string.settings_public_api_disabled_toast), Toast.LENGTH_LONG).show();
|
||||
Log.w(TAG, "Public API is disabled; ignoring request.");
|
||||
@@ -48,4 +52,8 @@ public abstract class AbstractAPIActivity extends AppCompatActivity {
|
||||
protected abstract void execute(TrackRecordingService service);
|
||||
|
||||
protected abstract boolean isPostExecuteStopService();
|
||||
|
||||
protected boolean requiresForeground() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -62,4 +62,9 @@ public class StartRecording extends AbstractAPIActivity {
|
||||
protected boolean isPostExecuteStopService() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean requiresForeground() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,9 @@ public abstract class Aggregator<Input, Output> {
|
||||
/**
|
||||
* Reset long term aggregated values (more than derived from previous SensorData). e.g. overall distance.
|
||||
*/
|
||||
public void reset() {}
|
||||
public void reset() {
|
||||
value = getNoneValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the data recent considering the current time.
|
||||
|
||||
@@ -37,10 +37,5 @@ public class AggregatorBarometer extends Aggregator<AtmosphericPressure, Altitud
|
||||
return new AltitudeGainLoss(0f, 0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
value = getNoneValue();
|
||||
}
|
||||
|
||||
public record Data(Altitude gain, Altitude loss) {}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,6 @@ public class AggregatorGPS extends Aggregator<Position, Position> {
|
||||
value = current.value();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
value = null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected Position getNoneValue() {
|
||||
|
||||
@@ -15,6 +15,11 @@ public class AggregatorHeartRate extends Aggregator<HeartRate, HeartRate> {
|
||||
this.value = current.value();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
// We don't need to reset the heart rate as this value is valid for a certain amount of time: and it is not an aggregate.
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected HeartRate getNoneValue() {
|
||||
|
||||
@@ -170,8 +170,7 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
||||
if (trackPoint.hasLocation() && lastStoredTrackPointWithLocation == null) {
|
||||
insertTrackPoint(trackPoint, true);
|
||||
|
||||
handler.removeCallbacks(ON_IDLE);
|
||||
handler.postDelayed(ON_IDLE, idleDuration.toMillis());
|
||||
scheduleNewIdleTimeout();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -199,16 +198,14 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
||||
trackPoint.setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||
insertTrackPoint(trackPoint, true);
|
||||
|
||||
handler.removeCallbacks(ON_IDLE);
|
||||
handler.postDelayed(ON_IDLE, idleDuration.toMillis());
|
||||
scheduleNewIdleTimeout();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (distanceToLastStoredTrackPoint.greaterOrEqualThan(recordingDistanceInterval)) {
|
||||
insertTrackPoint(trackPoint, false);
|
||||
|
||||
handler.removeCallbacks(ON_IDLE);
|
||||
handler.postDelayed(ON_IDLE, idleDuration.toMillis());
|
||||
scheduleNewIdleTimeout();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -218,6 +215,15 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
||||
return false;
|
||||
}
|
||||
|
||||
private void scheduleNewIdleTimeout() {
|
||||
if (idleDuration.isZero()) {
|
||||
Log.d(TAG, "idle functionality is disabled");
|
||||
return;
|
||||
}
|
||||
handler.removeCallbacks(ON_IDLE);
|
||||
handler.postDelayed(ON_IDLE, idleDuration.toMillis());
|
||||
}
|
||||
|
||||
TrackStatistics getTrackStatistics() {
|
||||
return trackStatisticsUpdater == null ? null : trackStatisticsUpdater.getTrackStatistics();
|
||||
}
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
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);
|
||||
}
|
||||
|
||||
public LocalePreference(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public LocalePreference(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public LocalePreference(@NonNull Context context) {
|
||||
super(context);
|
||||
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);
|
||||
|
||||
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 = getLocaleListCompat();
|
||||
|
||||
ArrayList<LocaleItem> localeItemsSorting = new ArrayList<>();
|
||||
for (int i = 0; i < supportedLocales.size(); i++) {
|
||||
Locale current = supportedLocales.get(i);
|
||||
localeItemsSorting.add(new LocaleItem(current.toLanguageTag(), current.getDisplayName()));
|
||||
}
|
||||
|
||||
localeItemsSorting.removeIf(current -> current.languageTag.equals(currentLocale.languageTag));
|
||||
localeItemsSorting.sort(Comparator.comparing(o -> o.displayName));
|
||||
|
||||
ArrayList<LocaleItem> localeItemList = new ArrayList<>();
|
||||
localeItemList.add(systemDefaultLocale);
|
||||
localeItemList.add(currentLocale);
|
||||
localeItemList.addAll(localeItemsSorting);
|
||||
|
||||
ArrayList<String> entries = new ArrayList<>();
|
||||
ArrayList<String> entryValues = new ArrayList<>();
|
||||
|
||||
for (LocaleItem current : localeItemList) {
|
||||
entries.add(current.displayName);
|
||||
entryValues.add(current.languageTag);
|
||||
}
|
||||
|
||||
setEntries(entries.toArray(new String[]{}));
|
||||
setEntryValues(entryValues.toArray(new String[]{}));
|
||||
|
||||
if (AppCompatDelegate.getApplicationLocales().equals(LocaleListCompat.getEmptyLocaleList())) {
|
||||
setValue(systemDefaultLocale.languageTag);
|
||||
} else {
|
||||
setValue(currentLocale.languageTag);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnPreferenceChangeListener(@Nullable OnPreferenceChangeListener onPreferenceChangeListener) {
|
||||
super.setOnPreferenceChangeListener(onPreferenceChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean callChangeListener(Object newValue) {
|
||||
LocaleListCompat newLocale = LocaleListCompat.getEmptyLocaleList();
|
||||
if (!newValue.equals("")) {
|
||||
newLocale = LocaleListCompat.forLanguageTags((String) newValue);
|
||||
}
|
||||
AppCompatDelegate.setApplicationLocales(newLocale);
|
||||
return super.callChangeListener(newValue);
|
||||
}
|
||||
|
||||
record LocaleItem(
|
||||
String languageTag,
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,6 @@ import de.dennisguse.opentracks.sensors.SensorType;
|
||||
import de.dennisguse.opentracks.ui.customRecordingLayout.CsvLayoutUtils;
|
||||
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayout;
|
||||
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayoutIO;
|
||||
import de.dennisguse.opentracks.util.IntentDashboardUtils;
|
||||
|
||||
/**
|
||||
* Utilities to access preferences stored in {@link SharedPreferences}.
|
||||
@@ -344,17 +343,17 @@ public class PreferencesUtils {
|
||||
}
|
||||
|
||||
static String[] getVoiceAnnouncementFrequencyEntries() {
|
||||
String[] values = resources.getStringArray(R.array.voice_announcement_frequency_values);
|
||||
String[] options = new String[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (resources.getString(R.string.announcement_off).equals(values[i])) {
|
||||
options[i] = resources.getString(R.string.value_off);
|
||||
String[] entryValues = resources.getStringArray(R.array.voice_announcement_frequency_values);
|
||||
String[] entries = new String[entryValues.length];
|
||||
for (int i = 0; i < entryValues.length; i++) {
|
||||
if (resources.getString(R.string.announcement_off).equals(entryValues[i])) {
|
||||
entries[i] = resources.getString(R.string.value_off);
|
||||
} else {
|
||||
int value = Integer.parseInt(values[i]);
|
||||
options[i] = resources.getString(R.string.value_integer_minute, Duration.ofSeconds(value).toMinutes());
|
||||
int value = Integer.parseInt(entryValues[i]);
|
||||
entries[i] = resources.getString(R.string.value_integer_minute, Duration.ofSeconds(value).toMinutes());
|
||||
}
|
||||
}
|
||||
return options;
|
||||
return entries;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -370,23 +369,23 @@ public class PreferencesUtils {
|
||||
* @return Result depends on getUnitSystem
|
||||
*/
|
||||
static String[] getVoiceAnnouncementDistanceEntries() {
|
||||
String[] values = resources.getStringArray(R.array.voice_announcement_distance_values);
|
||||
String[] options = new String[values.length];
|
||||
String[] entryValues = resources.getStringArray(R.array.voice_announcement_distance_values);
|
||||
String[] entries = new String[entryValues.length];
|
||||
UnitSystem unitSystem = getUnitSystem();
|
||||
|
||||
DistanceFormatter formatter = DistanceFormatter.Builder()
|
||||
.setDecimalCount(0)
|
||||
.setUnit(unitSystem)
|
||||
.build(resources);
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (resources.getString(R.string.announcement_off).equals(values[i])) {
|
||||
options[i] = resources.getString(R.string.value_off);
|
||||
for (int i = 0; i < entryValues.length; i++) {
|
||||
if (resources.getString(R.string.announcement_off).equals(entryValues[i])) {
|
||||
entries[i] = resources.getString(R.string.value_off);
|
||||
} else {
|
||||
Distance distance = Distance.one(unitSystem).multipliedBy(Double.parseDouble(values[i]));
|
||||
options[i] = formatter.formatDistance(distance);
|
||||
Distance distance = Distance.one(unitSystem).multipliedBy(Double.parseDouble(entryValues[i]));
|
||||
entries[i] = formatter.formatDistance(distance);
|
||||
}
|
||||
}
|
||||
return options;
|
||||
return entries;
|
||||
}
|
||||
|
||||
public static float getVoiceSpeedRate() {
|
||||
@@ -642,7 +641,9 @@ public class PreferencesUtils {
|
||||
for (int i = 0; i < entryValues.length; i++) {
|
||||
int value = Integer.parseInt(entryValues[i]);
|
||||
|
||||
if (value == idleDurationDefault) {
|
||||
if (resources.getString(R.string.announcement_off).equals(entryValues[i])) {
|
||||
entries[i] = resources.getString(R.string.value_off);
|
||||
} else if (value == idleDurationDefault) {
|
||||
entries[i] = resources.getString(R.string.value_int_seconds, value);
|
||||
} else {
|
||||
entries[i] = value < 60 ? resources.getString(R.string.value_integer_second, value) : resources.getString(R.string.value_integer_minute, value / 60);
|
||||
@@ -858,14 +859,6 @@ public class PreferencesUtils {
|
||||
sharedPreferences.edit().clear().commit();
|
||||
}
|
||||
|
||||
public static void setShowOnMapFormat(final String showOnMapFormat) {
|
||||
setString(R.string.show_on_map_format_key, showOnMapFormat);
|
||||
}
|
||||
|
||||
public static String getShowOnMapFormat() {
|
||||
return getString(R.string.show_on_map_format_key, IntentDashboardUtils.PREFERENCE_ID_ASK);
|
||||
}
|
||||
|
||||
public static int getTotalRowsDeleted() {
|
||||
return getInt(R.string.total_rows_deleted_key, 0);
|
||||
}
|
||||
|
||||
@@ -7,15 +7,10 @@ import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.util.IntentDashboardUtils;
|
||||
|
||||
public class UserInterfaceSettingsFragment extends PreferenceFragmentCompat {
|
||||
|
||||
@@ -41,8 +36,6 @@ public class UserInterfaceSettingsFragment extends PreferenceFragmentCompat {
|
||||
|
||||
Preference dynamicColors = findPreference(getString(R.string.settings_ui_dynamic_colors_key));
|
||||
dynamicColors.setEnabled(Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU);
|
||||
|
||||
setShowOnMapFormatOptions();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -79,14 +72,4 @@ public class UserInterfaceSettingsFragment extends PreferenceFragmentCompat {
|
||||
|
||||
super.onDisplayPreferenceDialog(preference);
|
||||
}
|
||||
|
||||
private void setShowOnMapFormatOptions() {
|
||||
Map<String, String> options = TrackFileFormat.toPreferenceIdLabelMap(getResources(), IntentDashboardUtils.SHOW_ON_MAP_TRACK_FILE_FORMATS);
|
||||
options.put(IntentDashboardUtils.PREFERENCE_ID_DASHBOARD, getString(R.string.show_on_dashboard));
|
||||
options.put(IntentDashboardUtils.PREFERENCE_ID_ASK, getString(R.string.show_on_map_format_ask));
|
||||
ListPreference listPreference = findPreference(getString(R.string.show_on_map_format_key));
|
||||
listPreference.setEntries(options.values().toArray(new String[0]));
|
||||
listPreference.setEntryValues(options.keySet().toArray(new String[0]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -164,6 +164,8 @@ public class MarkerEditActivity extends AbstractActivity {
|
||||
} else {
|
||||
loadMarkerData(markerId);
|
||||
}
|
||||
|
||||
setSupportActionBar(viewBinding.bottomAppBarLayout.bottomAppBar);
|
||||
}
|
||||
|
||||
private LiveData<Marker> createNewMarker() {
|
||||
|
||||
@@ -6,25 +6,18 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.data.ShareContentProvider;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.data.tables.MarkerColumns;
|
||||
import de.dennisguse.opentracks.data.tables.TrackPointsColumns;
|
||||
import de.dennisguse.opentracks.data.tables.TracksColumns;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
/**
|
||||
@@ -39,14 +32,6 @@ public class IntentDashboardUtils {
|
||||
|
||||
private static final String ACTION_DASHBOARD_PAYLOAD = ACTION_DASHBOARD + ".Payload";
|
||||
|
||||
public static final TrackFileFormat[] SHOW_ON_MAP_TRACK_FILE_FORMATS = new TrackFileFormat[] {
|
||||
TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES,
|
||||
TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA,
|
||||
TrackFileFormat.GPX};
|
||||
|
||||
public static final String PREFERENCE_ID_DASHBOARD = ACTION_DASHBOARD;
|
||||
public static final String PREFERENCE_ID_ASK = "ASK";
|
||||
|
||||
/**
|
||||
* Assume "v1" if not present.
|
||||
*/
|
||||
@@ -73,59 +58,13 @@ public class IntentDashboardUtils {
|
||||
|
||||
/**
|
||||
* Send intent to show tracks on a map (needs an another app) as resource URIs.
|
||||
* Shows an AlertDialog with different format option if none is defined as preference.
|
||||
*
|
||||
* @param context the context
|
||||
* @param isRecording are we currently recording?
|
||||
* @param trackIds the track ids
|
||||
*/
|
||||
public static void showTrackOnMap(Context context, boolean isRecording, Track.Id... trackIds) {
|
||||
Map<String, String> options = TrackFileFormat.toPreferenceIdLabelMap(context.getResources(), IntentDashboardUtils.SHOW_ON_MAP_TRACK_FILE_FORMATS);
|
||||
options.put(IntentDashboardUtils.PREFERENCE_ID_DASHBOARD, context.getString(R.string.show_on_dashboard));
|
||||
final String[] optionLabels = options.values().toArray(new String[0]);
|
||||
final String[] optionValues = options.keySet().toArray(new String[0]);
|
||||
final AtomicInteger checkedItem = new AtomicInteger(NONE_SELECTED);
|
||||
String preferenceValue = PreferencesUtils.getShowOnMapFormat();
|
||||
for (int i = 0; i < optionValues.length; i++) {
|
||||
if (optionValues[i].equals(preferenceValue)) {
|
||||
checkedItem.set(i);
|
||||
}
|
||||
}
|
||||
if (checkedItem.get() == NONE_SELECTED) {
|
||||
checkedItem.set(0); // set first option as default
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
||||
builder.setTitle(R.string.select_show_on_map_behavior);
|
||||
builder.setSingleChoiceItems(optionLabels, checkedItem.get(), (dialog, which) -> checkedItem.set(which));
|
||||
builder.setPositiveButton(android.R.string.ok, (dialog, which) -> onFormatSelected(context, isRecording, optionValues[checkedItem.get()], trackIds, false));
|
||||
builder.setNeutralButton(R.string.always, ((dialog, which) -> onFormatSelected(context, isRecording, optionValues[checkedItem.get()], trackIds, true)));
|
||||
builder.setNegativeButton(android.R.string.cancel, null);
|
||||
|
||||
AlertDialog dialog = builder.create();
|
||||
dialog.show();
|
||||
} else {
|
||||
onFormatSelected(context, isRecording, preferenceValue, trackIds, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A format was selected, remember if <code>always</code> is true and start the necessary action
|
||||
*
|
||||
* @param context the context
|
||||
* @param isRecording are we currently recording?
|
||||
* @param selectedValue the chosen format
|
||||
* @param trackIds the track ids
|
||||
* @param always set the selectedValue as default preference
|
||||
*/
|
||||
private static void onFormatSelected(final Context context, final boolean isRecording, final String selectedValue, final Track.Id[] trackIds, final boolean always) {
|
||||
if (always) {
|
||||
PreferencesUtils.setShowOnMapFormat(selectedValue);
|
||||
}
|
||||
TrackFileFormat trackFileFormat = TrackFileFormat.valueOfPreferenceId(selectedValue);
|
||||
if (trackFileFormat != null) {
|
||||
showTrackOnMapWithFileFormat(context, trackFileFormat, Set.of(trackIds));
|
||||
} else {
|
||||
startDashboard(context, isRecording, null, null, trackIds);
|
||||
}
|
||||
startDashboard(context, isRecording, null, null, trackIds);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,28 +122,4 @@ public class IntentDashboardUtils {
|
||||
Toast.makeText(context, R.string.show_on_dashboard_not_installed, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send intent to show tracks on a map (needs an another app) to support specific trackFileFormat.
|
||||
*
|
||||
* @param context the context
|
||||
* @param trackFileFormat the track file format
|
||||
* @param trackIds the track ids
|
||||
*/
|
||||
private static void showTrackOnMapWithFileFormat(Context context, TrackFileFormat trackFileFormat, Set<Track.Id> trackIds) {
|
||||
if (trackIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(android.content.Intent.ACTION_VIEW);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
Pair<Uri, String> uriAndMime = ShareContentProvider.createURI(trackIds, "SharingTrack", trackFileFormat);
|
||||
intent.setDataAndType(uriAndMime.first, uriAndMime.second);
|
||||
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
|
||||
context.startActivity(Intent.createChooser(intent, context.getString(R.string.open_track_as_trackfileformat, trackFileFormat.getExtension())));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="188dp"
|
||||
android:height="78dp"
|
||||
android:viewportWidth="188"
|
||||
android:viewportHeight="78">
|
||||
<path
|
||||
android:fillAlpha="0.70196078"
|
||||
android:fillColor="#cccccc"
|
||||
android:pathData="m37.804,0.731 l-37,38 37,39L37.804,49.731L90.804,49.731L106.804,28.731l-69,0z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeAlpha="1"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeLineCap="butt"
|
||||
android:strokeLineJoin="miter" />
|
||||
<path
|
||||
android:fillAlpha="0.70196078"
|
||||
android:fillColor="#cccccc"
|
||||
android:pathData="M136.804,28.731L117.804,28.731L101.804,49.731L120.804,49.731Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeAlpha="1"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeLineCap="butt"
|
||||
android:strokeLineJoin="miter" />
|
||||
<path
|
||||
android:fillAlpha="0.70196078"
|
||||
android:fillColor="#cccccc"
|
||||
android:pathData="M164.804,28.731L147.804,28.731L131.804,49.731L148.804,49.731Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeAlpha="1"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeLineCap="butt"
|
||||
android:strokeLineJoin="miter" />
|
||||
<path
|
||||
android:fillAlpha="0.70196078"
|
||||
android:fillColor="#cccccc"
|
||||
android:pathData="M187.804,28.731L173.804,28.731L158.804,49.731L171.804,49.731Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeAlpha="1"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeLineCap="butt"
|
||||
android:strokeLineJoin="miter" />
|
||||
</vector>
|
||||
@@ -1,42 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="78dp"
|
||||
android:height="188dp"
|
||||
android:viewportWidth="78"
|
||||
android:viewportHeight="188">
|
||||
<path
|
||||
android:fillAlpha="0.7019608"
|
||||
android:fillColor="#cccccc"
|
||||
android:pathData="m1,150 l38,37 39,-37H50V97L29,81v69z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeAlpha="1"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeLineCap="butt"
|
||||
android:strokeLineJoin="miter" />
|
||||
<path
|
||||
android:fillAlpha="0.7019608"
|
||||
android:fillColor="#cccccc"
|
||||
android:pathData="M29,51V70L50,86V67Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeAlpha="1"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeLineCap="butt"
|
||||
android:strokeLineJoin="miter" />
|
||||
<path
|
||||
android:fillAlpha="0.7019608"
|
||||
android:fillColor="#cccccc"
|
||||
android:pathData="M29,23V40L50,56V39Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeAlpha="1"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeLineCap="butt"
|
||||
android:strokeLineJoin="miter" />
|
||||
<path
|
||||
android:fillAlpha="0.7019608"
|
||||
android:fillColor="#cccccc"
|
||||
android:pathData="M29,0V14L50,29V16Z"
|
||||
android:strokeWidth="1"
|
||||
android:strokeAlpha="1"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeLineCap="butt"
|
||||
android:strokeLineJoin="miter" />
|
||||
</vector>
|
||||
@@ -7,7 +7,8 @@
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/track_list_toolbar"
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/export_activity_toolbar"
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/show_error_toolbar"
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/export_activity_toolbar"
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/import_activity_toolbar"
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextAppearance.OpenTracks.PrimaryHeader"
|
||||
android:text="@string/stats_altitude" />
|
||||
android:text="@string/stats_gain_loss" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
@@ -20,7 +20,8 @@ limitations under the License.
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/marker_detail_toolbar"
|
||||
|
||||
@@ -20,7 +20,8 @@ limitations under the License.
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/marker_edit_toolbar"
|
||||
@@ -34,6 +35,7 @@ limitations under the License.
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginBottom="102dp"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<ScrollView style="@style/ScrollView.WithBottomButtons">
|
||||
@@ -150,4 +152,8 @@ limitations under the License.
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<include
|
||||
android:id="@+id/bottom_app_bar_layout"
|
||||
layout="@layout/bottomappbar" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
@@ -20,7 +20,8 @@ limitations under the License.
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<com.google.android.material.search.SearchBar
|
||||
android:id="@+id/marker_list_toolbar"
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/top_app_bar"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user