diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml
index 37fcb95ea..e5726f2d2 100644
--- a/.github/workflows/android.yml
+++ b/.github/workflows/android.yml
@@ -27,8 +27,8 @@ jobs:
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- - name: Build with Gradle # skip tests, coverage, etc.
- run: ./gradlew assembleDebug --stacktrace -x test
+ - name: Build with Gradle
+ run: ./gradlew assembleDebug --stacktrace
- name: Rename output APK
run: |
@@ -41,3 +41,25 @@ jobs:
with:
name: debug-${{ env.ARTIFACT_DATE }}
path: app/build/outputs/apk/debug/OSMTracker-debug-*.apk
+
+ - name: Run unit tests and jacoco coverage
+ run: ./gradlew testDebugUnitTest jacocoTestReport --stacktrace
+
+ - 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 connected tests
+ uses: ReactiveCircus/android-emulator-runner@v2
+ with:
+ api-level: 26
+ script: |
+ adb logcat &
+ ./gradlew connectedCheck --no-parallel
+
+ - name: Coveralls GitHub Action
+ uses: coverallsapp/github-action@v2
+ with:
+ format: jacoco
diff --git a/README.md b/README.md
index 47bf24e02..335238300 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,28 @@
+# OSMTracker for Android™
+
+
+[](https://coveralls.io/github/labexp/osmtracker-android?branch=develop)
+
+**OSMTracker for Android™** is a mobile app designed for OpenStreetMap mappers and outdoor adventurers. It lets you log a GPS track to document your journey. Its customizable buttons let you simply add POIs as track points directly inside your GPX track.
+
+It also supports voice recording, picture taking, and note-taking. This is the perfect app to survey a place or a path whether you are hiking, cycling, or exploring new areas.
+
+
+
+Here is a screenshot of the main screen with its default buttons. You can [customize](https://github.com/labexp/osmtracker-android/wiki/Custom-buttons-layouts) these buttons to your liking.
+
+## Get the App 📲
+
[](https://play.google.com/store/apps/details?id=net.osmtracker)
[](https://f-droid.org/app/net.osmtracker)
-OSMTracker for Android™ official source code repository is [https://github.com/labexp/osmtracker-android](https://github.com/labexp/osmtracker-android).
+## More Info ℹ️
-[](https://travis-ci.org/labexp/osmtracker-android)
+- Find more information in the [documentation](https://github.com/labexp/osmtracker-android/wiki)
+- Submit bug reports in the [issue tracker](https://github.com/labexp/osmtracker-android/issues)
+- Contributions are welcome, please visit our [contributor guide](https://github.com/labexp/osmtracker-android/blob/master/CONTRIBUTING.md)
+- Translations can be done on [Transifex](https://explore.transifex.com/labexp/osmtracker-android/)
-For more information about the project, documentation and bug reports please visit https://github.com/labexp/osmtracker-android/wiki
+## Note 📝
-If you are interested in contribute to this project, please visit https://github.com/labexp/osmtracker-android/blob/master/CONTRIBUTING.md to know the way you could do it.
-
-To help translate OSMTracker, please visit https://www.transifex.com/projects/p/osmtracker-android/
+OSMTracker for Android™ official source code repository is [https://github.com/labexp/osmtracker-android](https://github.com/labexp/osmtracker-android).
diff --git a/app/build.gradle b/app/build.gradle
index 2087fcec9..7f0d40d6a 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,25 +1,47 @@
-apply plugin: 'com.android.application'
-apply plugin: 'kotlin-android'
-apply plugin: 'kotlin-android-extensions'
+plugins {
+ id 'com.android.application'
+ id 'kotlin-android'
+ id 'jacoco'
+}
android {
-
- useLibrary 'org.apache.http.legacy'
- compileSdk 33
+ namespace 'net.osmtracker'
+ compileSdk 35
defaultConfig {
applicationId "net.osmtracker"
- minSdkVersion 25
- targetSdkVersion 33
+ minSdk 25
+ targetSdk 35
multiDexEnabled true
+ // Version code should be increased after each release
+ versionCode 65
+ versionName new Date().format('yyyy.MM.dd')
+
testApplicationId "net.osmtracker.test"
- testInstrumentationRunner "android.test.InstrumentationTestRunner"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ signingConfig signingConfigs.findByName('release')
+ }
+ debug {
+ versionNameSuffix "-dev"
+ }
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_17
+ targetCompatibility JavaVersion.VERSION_17
+ }
+ kotlinOptions {
+ jvmTarget = JavaVersion.VERSION_17
}
signingConfigs {
- // Create a file $HOME/.gradle/gradle.properties
- // containing the values signing.storeFile=..., etc.
if (project.hasProperty('signing.storeFile')) {
release {
storeFile file(project.property('signing.storeFile'))
@@ -30,92 +52,72 @@ android {
}
}
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
- if (signingConfigs.hasProperty('release')) {
- signingConfig signingConfigs.release
- }
-
-
- }
- }
-
packagingOptions {
- exclude 'META-INF/DEPENDENCIES'
- exclude 'META-INF/LICENSE.txt'
- exclude 'META-INF/NOTICE.txt'
- }
- android {
- lintOptions {
- abortOnError false
- }
+ resources.excludes += [
+ 'META-INF/DEPENDENCIES',
+ 'META-INF/LICENSE.txt',
+ 'META-INF/NOTICE.txt'
+ ]
}
-
- defaultConfig {
- testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
- }
-
testOptions {
unitTests.returnDefaultValues = true
+ unitTests.all {
+ it.jvmArgs = [
+ '--add-opens', 'java.base/java.io=ALL-UNNAMED',
+ '--add-opens', 'java.base/java.lang=ALL-UNNAMED',
+ '--add-opens', 'java.base/java.lang.reflect=ALL-UNNAMED',
+ '--add-opens', 'java.base/java.util=ALL-UNNAMED'
+ ]
+ }
animationsDisabled = true
}
- namespace 'net.osmtracker'
-
}
dependencies {
-
- //implementation 'org.apache.james:apache-mime4j-core:0.8.3'
-
- //implementation 'org.apache.httpcomponents:httpmime:4.5.6'
-
- implementation 'org.osmdroid:osmdroid-android:6.1.5'
- //implementation 'org.apache.httpcomponents:httpcore:4.4.13'
-
- //implementation 'oauth.signpost:signpost-commonshttp4:1.2.1.2'
- implementation 'org.slf4j:slf4j-android:1.7.30'
- implementation 'androidx.core:core:1.6.0'
- //implementation 'org.apache.commons:commons-io:1.3.2'
-
- // For upload traces to osm server
+ // Lib to show OSM map as background
+ implementation 'org.osmdroid:osmdroid-android:6.1.20'
+ // OAuth
implementation 'net.openid:appauth:0.11.1'
- implementation 'de.westnordost:osmapi-traces:3.1'
-
- // Required for local unit tests (JUnit 4 framework)
- testImplementation 'junit:junit:4.12'
- testImplementation 'org.mockito:mockito-core:2.4.0'
+ // For upload traces to osm server
+ implementation('de.westnordost:osmapi-traces:3.1') {
+ // Already included in Android
+ exclude group: 'net.sf.kxml', module: 'kxml2'
+ exclude group: 'xmlpull', module: 'xmlpull'
+ }
+ // App intro
+ implementation 'com.github.AppIntro:AppIntro:6.3.1'
- testImplementation 'org.powermock:powermock-core:1.7.0RC2'
- testImplementation 'org.powermock:powermock-module-junit4:1.7.0RC2'
- testImplementation 'org.powermock:powermock-api-mockito2:1.7.0RC2'
+ implementation 'com.google.android.material:material:1.12.0'
+ implementation 'org.slf4j:slf4j-android:1.7.30'
+ implementation 'org.apache.commons:commons-io:1.3.2'
+ implementation 'androidx.core:core:1.15.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.2.0'
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ implementation 'androidx.appcompat:appcompat:1.7.0'
+
+ // Required -- JUnit 4 framework
+ testImplementation 'junit:junit:4.13.2'
+ // Robolectric environment
+ testImplementation "androidx.test:core:1.6.1"
+ // Mockito framework
+ testImplementation "org.mockito:mockito-core:3.12.4"
+
+ testImplementation 'org.powermock:powermock-core:2.0.9'
+ testImplementation 'org.powermock:powermock-api-mockito2:2.0.9'
+ testImplementation 'org.powermock:powermock-module-junit4:2.0.9'
+ // Required for local unit tests. Prevent null in JSONObject, JSONArray, etc.
+ testImplementation 'org.json:json:20240303'
// Required for instrumented tests
- androidTestImplementation 'androidx.test.ext:junit:1.1.3'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
- androidTestImplementation 'androidx.test:rules:1.4.0'
-
- implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
- implementation 'androidx.appcompat:appcompat:1.3.0'
-
- //compileOnly 'org.jbundle.util.osgi.wrapped:org.jbundle.util.osgi.wrapped.org.apache.http.client:4.1.2'
-
- // AndroidX Capable version
- implementation 'com.github.AppIntro:AppIntro:6.0.0'
- implementation 'com.google.android.material:material:1.4.0'
-
-
-}
-
-configurations.all {
- // it's already included in Android and not need from osmapi
- exclude group:'net.sf.kxml', module:'kxml2'
- exclude group: 'xmlpull', module: 'xmlpull'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
+ androidTestImplementation 'androidx.test.ext:junit:1.2.1'
+ androidTestImplementation 'androidx.test:rules:1.6.1'
}
repositories {
+ google()
mavenCentral()
maven { url "https://jitpack.io" }
}
+
+apply from: "jacoco.gradle"
diff --git a/app/jacoco.gradle b/app/jacoco.gradle
new file mode 100644
index 000000000..8bcded24b
--- /dev/null
+++ b/app/jacoco.gradle
@@ -0,0 +1,25 @@
+jacoco {
+ toolVersion = jacocoVersion
+}
+
+tasks.withType(Test).configureEach {
+ jacoco.includeNoLocationClasses = true
+ jacoco.excludes = ['jdk.internal.*']
+}
+
+tasks.register("jacocoTestReport", JacocoReport) {
+ dependsOn testDebugUnitTest
+
+ reports {
+ xml.required = true
+ }
+
+ def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*']
+ def mainSrc = "${project.projectDir}/src/main/java"
+
+ sourceDirectories.setFrom(files([mainSrc]))
+ classDirectories.setFrom(fileTree(dir: layout.buildDirectory.dir("intermediates/javac/debug").get().asFile, excludes: fileFilter))
+ executionData.setFrom(fileTree(dir: layout.buildDirectory.get(), includes: [
+ 'jacoco/testDebugUnitTest.exec', 'outputs/unit_test_code_coverage/debugUnitTest/testDebugUnitTest.exec'
+ ]))
+}
\ No newline at end of file
diff --git a/app/src/androidTest/java/net/osmtracker/layouts/DeleteLayoutTest.java b/app/src/androidTest/java/net/osmtracker/layouts/DeleteLayoutTest.java
index 59b16cc2d..63fd0a464 100644
--- a/app/src/androidTest/java/net/osmtracker/layouts/DeleteLayoutTest.java
+++ b/app/src/androidTest/java/net/osmtracker/layouts/DeleteLayoutTest.java
@@ -8,6 +8,7 @@
import net.osmtracker.activity.ButtonsPresets;
import net.osmtracker.activity.Preferences;
import net.osmtracker.util.CustomLayoutsUtils;
+import net.osmtracker.util.TestUtils;
import org.junit.Rule;
import org.junit.Test;
@@ -32,6 +33,9 @@
public class DeleteLayoutTest {
+ @Rule
+ public GrantPermissionRule storagePermission = GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE);
+
@Rule
public ActivityTestRule mRule = new ActivityTestRule(ButtonsPresets.class) {
@Override
@@ -47,10 +51,6 @@ protected void beforeActivityLaunched() {
}
};
- // Storage permissions are required
- @Rule
- public GrantPermissionRule writePermission = GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE);
-
private static String layoutName = "mock";
private static String ISOLanguageCode = "es";
diff --git a/app/src/androidTest/java/net/osmtracker/layouts/DownloadLayoutTest.java b/app/src/androidTest/java/net/osmtracker/layouts/DownloadLayoutTest.java
index 57040fe94..1c20d9c7e 100644
--- a/app/src/androidTest/java/net/osmtracker/layouts/DownloadLayoutTest.java
+++ b/app/src/androidTest/java/net/osmtracker/layouts/DownloadLayoutTest.java
@@ -1,11 +1,27 @@
package net.osmtracker.layouts;
+import static androidx.test.espresso.Espresso.onData;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.scrollTo;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.PreferenceMatchers.withTitleText;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static junit.framework.TestCase.fail;
+import static net.osmtracker.util.WaitForView.waitForView;
+
+import android.Manifest;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import androidx.test.espresso.Espresso;
-import androidx.test.espresso.assertion.ViewAssertions;
import androidx.test.rule.ActivityTestRule;
+import androidx.test.rule.GrantPermissionRule;
import net.osmtracker.OSMTracker;
import net.osmtracker.R;
@@ -18,19 +34,17 @@
import java.util.Locale;
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-import static androidx.test.espresso.Espresso.onData;
-import static androidx.test.espresso.Espresso.onView;
-import static androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu;
-import static androidx.test.espresso.action.ViewActions.click;
-import static androidx.test.espresso.action.ViewActions.scrollTo;
-import static androidx.test.espresso.matcher.PreferenceMatchers.withTitleText;
-import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static androidx.test.espresso.matcher.ViewMatchers.withId;
-import static androidx.test.espresso.matcher.ViewMatchers.withText;
-import static junit.framework.TestCase.fail;
-
public class DownloadLayoutTest {
+
+ private final int WAIT_VIEW_TIMEOUT = 5000;
+
+ @Rule
+ public GrantPermissionRule fineLocationPermission = GrantPermissionRule.grant(Manifest.permission.ACCESS_FINE_LOCATION);
+ @Rule
+ public GrantPermissionRule coarseLocationPermission = GrantPermissionRule.grant(Manifest.permission.ACCESS_COARSE_LOCATION);
+ @Rule
+ public GrantPermissionRule writeStoragePermission = GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE);
+
@Rule
public ActivityTestRule mRule = new ActivityTestRule(TrackManager.class) {
@Override
@@ -71,13 +85,16 @@ public void deleteLayoutsDirectory(){
/**
* Assuming being in TrackManager
*/
- public void navigateToAvailableLayouts(){
+ public void navigateToAvailableLayouts() {
+ // Open options menu in the Action Bar
openActionBarOverflowOrOptionsMenu(getInstrumentation().getTargetContext());
-
+ // Click on "Settings" in this menu
onView(withText(TestUtils.getStringResource(R.string.menu_settings))).perform(click());
-
+ // Click on "Buttons presets" settings
onData(withTitleText(TestUtils.getStringResource(R.string.prefs_ui_buttons_layout))).perform(scrollTo(), click());
-
+ // Wait for "+" to be visible
+ onView(isRoot()).perform(waitForView(R.id.launch_available, WAIT_VIEW_TIMEOUT));
+ // Perform a click action on the "+" button
onView(withId(R.id.launch_available)).perform(click());
}
@@ -91,7 +108,7 @@ private void makePostDownloadAssertions(String layoutName) {
Espresso.pressBack();
// Check the layout appears as a new option in AvailableLayouts
- onView(withText(layoutName.toLowerCase())).check(ViewAssertions.matches(isDisplayed()));
+ onView(withText(layoutName.toLowerCase())).check(matches(isDisplayed()));
// Select the layout
onView(withText(layoutName.toLowerCase())).perform(click());
@@ -104,7 +121,7 @@ private void makePostDownloadAssertions(String layoutName) {
// Check the buttons are loaded correctly
String expectedButtonsLabels[] = new String[]{"A", "B", "C"};
for(String label : expectedButtonsLabels)
- onView(withText(label)).check(ViewAssertions.matches(isDisplayed()));
+ onView(withText(label)).check(matches(isDisplayed()));
}
diff --git a/app/src/androidTest/java/net/osmtracker/layouts/RepositorySettingsDialogTest.java b/app/src/androidTest/java/net/osmtracker/layouts/RepositorySettingsDialogTest.java
index 4fc9c8729..607587fc0 100644
--- a/app/src/androidTest/java/net/osmtracker/layouts/RepositorySettingsDialogTest.java
+++ b/app/src/androidTest/java/net/osmtracker/layouts/RepositorySettingsDialogTest.java
@@ -1,16 +1,5 @@
package net.osmtracker.layouts;
-import androidx.test.espresso.Espresso;
-import androidx.test.espresso.ViewAssertion;
-import androidx.test.rule.ActivityTestRule;
-
-import net.osmtracker.OSMTracker;
-import net.osmtracker.R;
-import net.osmtracker.activity.AvailableLayouts;
-
-import org.hamcrest.Matcher;
-import org.junit.Rule;
-import org.junit.Test;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.clearText;
import static androidx.test.espresso.action.ViewActions.click;
@@ -27,6 +16,17 @@
import static net.osmtracker.util.TestUtils.getStringResource;
import static org.hamcrest.core.IsNot.not;
+import androidx.test.espresso.ViewAssertion;
+import androidx.test.rule.ActivityTestRule;
+
+import net.osmtracker.OSMTracker;
+import net.osmtracker.R;
+import net.osmtracker.activity.AvailableLayouts;
+
+import org.hamcrest.Matcher;
+import org.junit.Rule;
+import org.junit.Test;
+
public class RepositorySettingsDialogTest {
@Rule
@@ -66,7 +66,7 @@ public void checkStateAfterToggle(int expectedActiveId, int expectedInactiveId){
onView(withId(expectedInactiveId)).check(matches(isEnabled()));
}
- public void checkRepositoryValidity(String user, String repo, String branch, boolean isValid){
+ public void checkRepositoryValidity(String user, String repo, String branch, boolean isValid) {
onView(withId(R.id.github_config)).perform(click());
onView(withId(R.id.custom_server)).perform(click(), closeSoftKeyboard());
@@ -84,7 +84,6 @@ public void checkRepositoryValidity(String user, String repo, String branch, boo
ViewAssertion expectedDialogState = (isValid) ? doesNotExist() : matches(isDisplayed());
checkDialogState(expectedDialogState);
-
}
/**
diff --git a/app/src/androidTest/java/net/osmtracker/util/LogcatHelper.java b/app/src/androidTest/java/net/osmtracker/util/LogcatHelper.java
new file mode 100644
index 000000000..8049c6dbd
--- /dev/null
+++ b/app/src/androidTest/java/net/osmtracker/util/LogcatHelper.java
@@ -0,0 +1,29 @@
+package net.osmtracker.util;
+
+import android.util.Log;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.regex.Pattern;
+
+public class LogcatHelper {
+
+ public static boolean checkLogForMessage(String tag, String message) {
+ try {
+ Process process = Runtime.getRuntime().exec("logcat -d " + tag + ":I *:S");
+ BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
+
+ String line;
+ Pattern pattern = Pattern.compile(".*\\b" + Pattern.quote(message) + "\\b.*");
+
+ while ((line = bufferedReader.readLine()) != null) {
+ if (pattern.matcher(line).matches()) {
+ return true;
+ }
+ }
+ } catch (Exception e) {
+ Log.e("LogcatHelper", "Error reading logcat output", e);
+ }
+
+ return false;
+ }
+}
diff --git a/app/src/androidTest/java/net/osmtracker/util/TestUtils.java b/app/src/androidTest/java/net/osmtracker/util/TestUtils.java
index 15b621190..2c733c75f 100644
--- a/app/src/androidTest/java/net/osmtracker/util/TestUtils.java
+++ b/app/src/androidTest/java/net/osmtracker/util/TestUtils.java
@@ -1,9 +1,11 @@
package net.osmtracker.util;
+import static net.osmtracker.util.LogcatHelper.checkLogForMessage;
+
import android.content.Context;
import android.content.SharedPreferences;
-import android.os.Environment;
import android.preference.PreferenceManager;
+
import androidx.test.platform.app.InstrumentationRegistry;
import net.osmtracker.OSMTracker;
@@ -14,11 +16,6 @@
import java.io.FileWriter;
import java.util.ArrayList;
-import static androidx.test.espresso.Espresso.onView;
-import static androidx.test.espresso.assertion.ViewAssertions.matches;
-import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static androidx.test.espresso.matcher.ViewMatchers.withText;
-
/**
* Contains common and reusable static methods used for tests
*/
@@ -118,8 +115,10 @@ public static File getLayoutsDirectory(){
}
public static void checkToastIsShownWith(String text){
- onView(withText(text)).inRoot(new ToastMatcher())
- .check(matches(isDisplayed()));
+ // Espresso can not check Toast for android >= 11
+ // https://github.com/android/android-test/issues/803
+ //onView(withText(text)).inRoot(new ToastMatcher()).check(matches(isDisplayed()));
+ checkLogForMessage("TOAST", text);
}
public static String getStringResource(int resourceId){
diff --git a/app/src/androidTest/java/net/osmtracker/util/ToastMatcher.java b/app/src/androidTest/java/net/osmtracker/util/ToastMatcher.java
index 0a1f1433a..f5e3c19f6 100644
--- a/app/src/androidTest/java/net/osmtracker/util/ToastMatcher.java
+++ b/app/src/androidTest/java/net/osmtracker/util/ToastMatcher.java
@@ -9,7 +9,7 @@
/**
* Used to search for Toasts in the UI
- * This class was taken from https://stackoverflow.com/questions/28390574/checking-toast-message-in-android-espresso
+ * This class was taken from stackoverflow
*/
public class ToastMatcher extends TypeSafeMatcher {
diff --git a/app/src/androidTest/java/net/osmtracker/util/WaitForView.java b/app/src/androidTest/java/net/osmtracker/util/WaitForView.java
new file mode 100644
index 000000000..2cbc9aed6
--- /dev/null
+++ b/app/src/androidTest/java/net/osmtracker/util/WaitForView.java
@@ -0,0 +1,65 @@
+package net.osmtracker.util;
+
+import android.view.View;
+import androidx.test.espresso.PerformException;
+import androidx.test.espresso.UiController;
+import androidx.test.espresso.ViewAction;
+import androidx.test.espresso.matcher.ViewMatchers;
+import androidx.test.espresso.util.HumanReadables;
+import androidx.test.espresso.util.TreeIterables;
+
+import org.hamcrest.Matcher;
+
+import java.util.concurrent.TimeoutException;
+
+public class WaitForView implements ViewAction {
+ private final int viewId;
+ private final long timeout;
+
+ /**
+ * This ViewAction tells espresso to wait till a certain view is found in the view hierarchy.
+ * @param viewId The id of the view to wait for.
+ * @param timeout The maximum time which espresso will wait for the view to show up (in milliseconds)
+ */
+ public WaitForView(int viewId, long timeout) {
+ this.viewId = viewId;
+ this.timeout = timeout;
+ }
+
+ @Override
+ public Matcher getConstraints() {
+ return ViewMatchers.isRoot();
+ }
+
+ @Override
+ public String getDescription() {
+ return "wait for a specific view with id " + viewId + " during " + timeout + " millis.";
+ }
+
+ @Override
+ public void perform(UiController uiController, View rootView) {
+ uiController.loopMainThreadUntilIdle();
+ long startTime = System.currentTimeMillis();
+ long endTime = startTime + timeout;
+ Matcher viewMatcher = ViewMatchers.withId(viewId);
+
+ do {
+ for (View child : TreeIterables.breadthFirstViewTraversal(rootView)) {
+ if (viewMatcher.matches(child)) {
+ return;
+ }
+ }
+ uiController.loopMainThreadForAtLeast(100);
+ } while (System.currentTimeMillis() < endTime);
+
+ throw new PerformException.Builder()
+ .withCause(new TimeoutException())
+ .withActionDescription(this.getDescription())
+ .withViewDescription(HumanReadables.describe(rootView))
+ .build();
+ }
+
+ public static ViewAction waitForView(final int viewId, final long timeout) {
+ return new WaitForView(viewId, timeout);
+ }
+}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index a2b63e2b8..053700c60 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,22 +1,21 @@
+ android:installLocation="auto">
+
-
+
@@ -127,4 +126,4 @@
-
\ No newline at end of file
+
diff --git a/app/src/main/java/net/osmtracker/OSMTracker.java b/app/src/main/java/net/osmtracker/OSMTracker.java
index f8c18b75d..046a4d23a 100644
--- a/app/src/main/java/net/osmtracker/OSMTracker.java
+++ b/app/src/main/java/net/osmtracker/OSMTracker.java
@@ -25,6 +25,7 @@ public static final class Preferences {
public final static String KEY_GPS_LOGGING_MIN_DISTANCE = "gps.logging.min_distance";
public final static String KEY_USE_BAROMETER = "gpx.use_barometer";
public final static String KEY_OUTPUT_FILENAME = "gpx.filename";
+ public final static String KEY_OUTPUT_FILENAME_LABEL = "gpx.filename.label";
public final static String KEY_OUTPUT_ACCURACY = "gpx.accuracy";
public final static String KEY_OUTPUT_GPX_HDOP_APPROXIMATION = "gpx.hdop.approximation";
public final static String KEY_OUTPUT_DIR_PER_TRACK = "gpx.directory_per_track";
@@ -62,8 +63,10 @@ public static final class Preferences {
public final static String VAL_OUTPUT_FILENAME_NAME = "name";
public final static String VAL_OUTPUT_FILENAME_NAME_DATE = "name_date";
+ public final static String VAL_OUTPUT_FILENAME_DATE_NAME = "date_name";
public final static String VAL_OUTPUT_FILENAME_DATE = "date";
public final static String VAL_OUTPUT_FILENAME = VAL_OUTPUT_FILENAME_NAME_DATE;
+ public final static String VAL_OUTPUT_FILENAME_LABEL = "";
public final static String VAL_OUTPUT_ACCURACY_NONE = "none";
public final static String VAL_OUTPUT_ACCURACY_WPT_NAME = "wpt_name";
diff --git a/app/src/main/java/net/osmtracker/activity/About.java b/app/src/main/java/net/osmtracker/activity/About.java
index 8386ef4f9..84aa7804c 100644
--- a/app/src/main/java/net/osmtracker/activity/About.java
+++ b/app/src/main/java/net/osmtracker/activity/About.java
@@ -10,6 +10,7 @@
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
+import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
@@ -47,7 +48,7 @@ protected void onCreate(Bundle savedInstanceState) {
} catch (NameNotFoundException nnfe) {
// Should not occur
}
-
+
findViewById(R.id.about_debug_info_button).setOnClickListener(
new OnClickListener() {
@@ -76,7 +77,7 @@ public void onClick(View view) {
File dbFile = getDatabasePath(DatabaseHelper.DB_NAME);
File targetFolder = new File(
- view.getContext().getExternalFilesDir(null),
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS),
//Environment.getExternalStorageDirectory(),
PreferenceManager.getDefaultSharedPreferences(About.this).getString(
OSMTracker.Preferences.KEY_STORAGE_DIR,
@@ -128,10 +129,17 @@ public ProgressDialog getExportDbProgressDialog() {
private String getDebugInfo() {
File externalStorageDir = this.getExternalFilesDir(null);
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
+ String exportDirectoryNameInPreferences = preferences.getString(
+ OSMTracker.Preferences.KEY_STORAGE_DIR, OSMTracker.Preferences.VAL_STORAGE_DIR);
+ File baseExportDirectory = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS),
+ exportDirectoryNameInPreferences);
return "External Storage Directory: '" + externalStorageDir + "'\n"
+ "External Storage State: '" + Environment.getExternalStorageState() + "'\n"
+ "Can write to external storage: "
- + Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) + "\n";
+ + Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) + "\n"
+ + "Export External Public Storage Directory: '"
+ + baseExportDirectory + "'\n";
}
}
diff --git a/app/src/main/java/net/osmtracker/activity/AvailableLayouts.java b/app/src/main/java/net/osmtracker/activity/AvailableLayouts.java
index 30553259f..eb215767c 100644
--- a/app/src/main/java/net/osmtracker/activity/AvailableLayouts.java
+++ b/app/src/main/java/net/osmtracker/activity/AvailableLayouts.java
@@ -151,12 +151,13 @@ public void setAvailableLayouts(List options) {
Button layoutButton = new Button(this);
layoutButton.setHeight(150);
layoutButton.setText(CustomLayoutsUtils.convertFileName(option));
- layoutButton.setTextSize((float)22 );
+ layoutButton.setTextSize(16f);
layoutButton.setTextColor(Color.WHITE);
+ layoutButton.setSingleLine(false);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
- layoutParams.setMargins(110, 0, 110, 0);
+ layoutParams.setMargins(50, 10, 50, 10);
layoutButton.setLayoutParams(layoutParams);
- layoutButton.setPadding(10,20,10,20);
+ layoutButton.setPadding(40, 30, 40, 30);
layoutButton.setOnClickListener(listener);
rootLayout.addView(layoutButton,AT_START);
}
@@ -247,7 +248,9 @@ public void onClick(DialogInterface dialog, int which) {
protected void onPostExecute(Boolean result){
//validating the github repository
if(result){
- Toast.makeText(AvailableLayouts.this, getResources().getString(R.string.github_repository_settings_valid_server), Toast.LENGTH_SHORT).show();
+ String message = getResources().getString(R.string.github_repository_settings_valid_server);
+ Log.i("TOAST", message);
+ Toast.makeText(AvailableLayouts.this, message, Toast.LENGTH_SHORT).show();
//save the entered options into the shared preferences file
editor.putString(OSMTracker.Preferences.KEY_GITHUB_USERNAME, repositoryCustomOptions[0]);
editor.putString(OSMTracker.Preferences.KEY_REPOSITORY_NAME, repositoryCustomOptions[1]);
@@ -257,7 +260,9 @@ protected void onPostExecute(Boolean result){
tmpSharedPref.edit().putBoolean("isCallBack", false).commit();
retrieveAvailableLayouts();
}else{
- Toast.makeText(AvailableLayouts.this, getResources().getString(R.string.github_repository_settings_invalid_server), Toast.LENGTH_SHORT).show();
+ String message = getResources().getString(R.string.github_repository_settings_invalid_server);
+ Log.e("TOAST", message);
+ Toast.makeText(AvailableLayouts.this, message, Toast.LENGTH_SHORT).show();
tmpSharedPref.edit().putString(OSMTracker.Preferences.KEY_GITHUB_USERNAME, repositoryCustomOptions[0]).commit();
tmpSharedPref.edit().putString(OSMTracker.Preferences.KEY_REPOSITORY_NAME, repositoryCustomOptions[1]).commit();
tmpSharedPref.edit().putString(OSMTracker.Preferences.KEY_BRANCH_NAME, repositoryCustomOptions[2]).commit();
@@ -321,8 +326,8 @@ private void toggleRepositoryOptions(boolean status){
//setting the custom options into text fields
else{
etxGithubUsername.setText(sharedPrefs.getString(OSMTracker.Preferences.KEY_GITHUB_USERNAME, ""));
- etxRepositoryName.setText(sharedPrefs.getString(OSMTracker.Preferences.KEY_REPOSITORY_NAME,""));
- etxBranchName.setText(sharedPrefs.getString(OSMTracker.Preferences.KEY_BRANCH_NAME, ""));
+ etxRepositoryName.setText(sharedPrefs.getString(OSMTracker.Preferences.KEY_REPOSITORY_NAME, OSMTracker.Preferences.VAL_REPOSITORY_NAME));
+ etxBranchName.setText(sharedPrefs.getString(OSMTracker.Preferences.KEY_BRANCH_NAME, OSMTracker.Preferences.VAL_BRANCH_NAME));
}
}
@@ -492,9 +497,11 @@ protected void onPostExecute(Boolean status){
String message="";
if (status) {
message = getResources().getString(R.string.available_layouts_successful_download);
+ Log.i("TOAST", message);
}
else {
message = getResources().getString(R.string.available_layouts_unsuccessful_download);
+ Log.e("TOAST", message);
}
Toast.makeText(getApplicationContext(),message,Toast.LENGTH_LONG).show();
dialog.dismiss();
diff --git a/app/src/main/java/net/osmtracker/activity/ButtonsPresets.java b/app/src/main/java/net/osmtracker/activity/ButtonsPresets.java
index ed6a9fb46..2d5f864dd 100644
--- a/app/src/main/java/net/osmtracker/activity/ButtonsPresets.java
+++ b/app/src/main/java/net/osmtracker/activity/ButtonsPresets.java
@@ -285,8 +285,11 @@ public void onClick(DialogInterface dialog, int which) {
int messageToShowId = (successfulDeletion) ? R.string.buttons_presets_successful_delete :
R.string.buttons_presets_unsuccessful_delete;
+ String message = getResources().getString(messageToShowId);
- Toast.makeText(getApplicationContext(), getResources().getString(messageToShowId), Toast.LENGTH_SHORT).show();
+ Log.println(successfulDeletion ? Log.INFO : Log.ERROR, "TOAST", message);
+
+ Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
//reload the activity
refreshActivity();
diff --git a/app/src/main/java/net/osmtracker/activity/DisplayTrackMap.java b/app/src/main/java/net/osmtracker/activity/DisplayTrackMap.java
index c0e8f3d64..707fefbc2 100644
--- a/app/src/main/java/net/osmtracker/activity/DisplayTrackMap.java
+++ b/app/src/main/java/net/osmtracker/activity/DisplayTrackMap.java
@@ -1,24 +1,5 @@
package net.osmtracker.activity;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.List;
-
-import net.osmtracker.OSMTracker;
-import net.osmtracker.R;
-import net.osmtracker.db.TrackContentProvider;
-import net.osmtracker.overlay.WayPointsOverlay;
-
-import org.osmdroid.api.IMapController;
-import org.osmdroid.config.Configuration;
-import org.osmdroid.tileprovider.tilesource.ITileSource;
-import org.osmdroid.tileprovider.tilesource.TileSourceFactory;
-import org.osmdroid.util.GeoPoint;
-import org.osmdroid.views.MapView;
-import org.osmdroid.views.overlay.PathOverlay;
-import org.osmdroid.views.overlay.mylocation.SimpleLocationOverlay;
-import org.osmdroid.views.overlay.ScaleBarOverlay;
-
import android.app.Activity;
import android.content.ContentUris;
import android.content.Intent;
@@ -26,6 +7,7 @@
import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.Color;
+import android.graphics.Paint;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
@@ -35,49 +17,62 @@
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.OnClickListener;
+
+import net.osmtracker.OSMTracker;
+import net.osmtracker.R;
+import net.osmtracker.db.TrackContentProvider;
+import net.osmtracker.overlay.WayPointsOverlay;
+
+import org.osmdroid.api.IMapController;
+import org.osmdroid.config.Configuration;
+import org.osmdroid.tileprovider.tilesource.TileSourceFactory;
+import org.osmdroid.util.GeoPoint;
+import org.osmdroid.views.CustomZoomButtonsController;
+import org.osmdroid.views.MapView;
+import org.osmdroid.views.overlay.Polyline;
+import org.osmdroid.views.overlay.ScaleBarOverlay;
+import org.osmdroid.views.overlay.mylocation.SimpleLocationOverlay;
+
+import java.util.ArrayList;
+import java.util.List;
/**
* Display current track over an OSM map.
- * Based on osmdroid code http://osmdroid.googlecode.com/
+ * Based on osmdroid code
*
* Used only if {@link OSMTracker.Preferences#KEY_UI_DISPLAYTRACK_OSM} is set.
* Otherwise {@link DisplayTrack} is used (track only, no OSM background tiles).
- *
+ *
* @author Viesturs Zarins
*
*/
public class DisplayTrackMap extends Activity {
private static final String TAG = DisplayTrackMap.class.getSimpleName();
-
+
/**
* Key for keeping the zoom level in the saved instance bundle
*/
private static final String CURRENT_ZOOM = "currentZoom";
/**
- * Key for keeping scrolled left position of OSM view activity re-creation
- *
+ * Key for keeping scrolled left position of OSM view activity re-creation
*/
private static final String CURRENT_SCROLL_X = "currentScrollX";
- /**
+ /**
* Key for keeping scrolled top position of OSM view across activity re-creation
- *
- */
+ */
private static final String CURRENT_SCROLL_Y = "currentScrollY";
/**
- * Key for keeping whether the map display should be centered to the gps location
- *
+ * Key for keeping whether the map display should be centered to the gps location
*/
private static final String CURRENT_CENTER_TO_GPS_POS = "currentCenterToGpsPos";
/**
- * Key for keeping whether the map display was zoomed and centered
- * on an old track id loaded from the database (boolean {@link #zoomedToTrackAlready})
+ * Key for keeping whether the map display was zoomed and centered
+ * on an old track id loaded from the database (boolean {@link #zoomedToTrackAlready})
*/
private static final String CURRENT_ZOOMED_TO_TRACK = "currentZoomedToTrack";
@@ -89,30 +84,40 @@ public class DisplayTrackMap extends Activity {
/**
* Default zoom level
*/
- private static final int DEFAULT_ZOOM = 16;
+ private static final int DEFAULT_ZOOM = 16;
+
+ /**
+ * Default zoom level for center with zoom
+ */
+ private static final double CENTER_DEFAULT_ZOOM_LEVEL = 18;
+
+ /**
+ * Animation duration in milliseconds for center with zoom
+ */
+ private static final long ANIMATION_DURATION_MS = 1000;
/**
* Main OSM view
*/
private MapView osmView;
-
+
/**
* Controller to interact with view
*/
private IMapController osmViewController;
-
+
/**
* OSM view overlay that displays current location
*/
private SimpleLocationOverlay myLocationOverlay;
-
+
/**
* OSM view overlay that displays current path
*/
- private PathOverlay pathOverlay;
+ private Polyline polyline;
/**
- * OSM view overlay that displays waypoints
+ * OSM view overlay that displays waypoints
*/
private WayPointsOverlay wayPointsOverlay;
@@ -120,23 +125,23 @@ public class DisplayTrackMap extends Activity {
* OSM view overlay for the map scale bar
*/
private ScaleBarOverlay scaleBarOverlay;
-
+
/**
* Current track id
*/
private long currentTrackId;
-
+
/**
- * whether the map display should be centered to the gps location
+ * whether the map display should be centered to the gps location
*/
private boolean centerToGpsPos = true;
-
+
/**
* whether the map display was already zoomed and centered
* on an old track loaded from the database (should be done only once).
*/
private boolean zoomedToTrackAlready = false;
-
+
/**
* the last position we know
*/
@@ -145,12 +150,12 @@ public class DisplayTrackMap extends Activity {
/**
* The row id of the last location read from the database that has been added to the
* list of layout points. Using this we to reduce DB load by only reading new points.
- * Initially null, to indicate that no data has yet been read.
+ * Initially null, to indicate that no data has yet been read.
*/
private Integer lastTrackPointIdProcessed = null;
-
+
/**
- * Observes changes on trackpoints
+ * Observes changes on track points
*/
private ContentObserver trackpointContentObserver;
@@ -173,14 +178,17 @@ public void onCreate(Bundle savedInstanceState) {
// Initialize OSM view
Configuration.getInstance().load(this, prefs);
- osmView = (MapView) findViewById(R.id.displaytrackmap_osmView);
- osmView.setMultiTouchControls(true); // pinch to zoom
+
+ osmView = findViewById(R.id.displaytrackmap_osmView);
+ // pinch to zoom
+ osmView.setMultiTouchControls(true);
+ osmView.getZoomController().setVisibility(CustomZoomButtonsController.Visibility.NEVER);
// we'll use osmView to define if the screen is always on or not
osmView.setKeepScreenOn(prefs.getBoolean(OSMTracker.Preferences.KEY_UI_DISPLAY_KEEP_ON, OSMTracker.Preferences.VAL_UI_DISPLAY_KEEP_ON));
osmViewController = osmView.getController();
// Check if there is a saved zoom level
- if(savedInstanceState != null) {
+ if (savedInstanceState != null) {
osmViewController.setZoom(savedInstanceState.getInt(CURRENT_ZOOM, DEFAULT_ZOOM));
osmView.scrollTo(savedInstanceState.getInt(CURRENT_SCROLL_X, 0),
savedInstanceState.getInt(CURRENT_SCROLL_Y, 0));
@@ -194,11 +202,11 @@ public void onCreate(Bundle savedInstanceState) {
selectTileSource();
- setTileDpiScaling();
+ setTileDpiScaling();
createOverlays();
- // Create content observer for trackpoints
+ // Create content observer for track points
trackpointContentObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
@@ -207,16 +215,12 @@ public void onChange(boolean selfChange) {
};
// Register listeners for zoom buttons
- findViewById(R.id.displaytrackmap_imgZoomIn).setOnClickListener( new OnClickListener() {
- @Override
- public void onClick(View v) {
- osmViewController.zoomIn();
- }
- });
- findViewById(R.id.displaytrackmap_imgZoomOut).setOnClickListener( new OnClickListener() {
- @Override
- public void onClick(View v) {
- osmViewController.zoomOut();
+ findViewById(R.id.displaytrackmap_imgZoomIn).setOnClickListener(v -> osmViewController.zoomIn());
+ findViewById(R.id.displaytrackmap_imgZoomOut).setOnClickListener(v -> osmViewController.zoomOut());
+ findViewById(R.id.displaytrackmap_imgZoomCenter).setOnClickListener(view -> {
+ centerToGpsPos = true;
+ if (currentPosition != null) {
+ osmViewController.animateTo(currentPosition,CENTER_DEFAULT_ZOOM_LEVEL, ANIMATION_DURATION_MS);
}
});
}
@@ -231,14 +235,14 @@ public void selectTileSource() {
osmView.setTileSource(TileSourceFactory.DEFAULT_TILE_SOURCE);
}
- /**
- * Make text on map better readable on high DPI displays
- */
- public void setTileDpiScaling () {
- osmView.setTilesScaledToDpi(true);
- }
+ /**
+ * Make text on map better readable on high DPI displays
+ */
+ public void setTileDpiScaling() {
+ osmView.setTilesScaledToDpi(true);
+ }
+
-
// /**
// * Returns a ITileSource for the map according to the selected mapTile
// * String. The default is mapnik.
@@ -268,20 +272,17 @@ protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
-
@Override
protected void onResume() {
-
super.onResume();
resumeActivity();
-
}
- private void resumeActivity(){
+ private void resumeActivity() {
// setKeepScreenOn depending on user's preferences
osmView.setKeepScreenOn(prefs.getBoolean(OSMTracker.Preferences.KEY_UI_DISPLAY_KEEP_ON, OSMTracker.Preferences.VAL_UI_DISPLAY_KEEP_ON));
- // Register content observer for any trackpoint changes
+ // Register content observer for any track point changes
getContentResolver().registerContentObserver(
TrackContentProvider.trackPointsUri(currentTrackId),
true, trackpointContentObserver);
@@ -297,19 +298,18 @@ private void resumeActivity(){
selectTileSource();
setTileDpiScaling();
-
+
// Refresh way points
wayPointsOverlay.refresh();
-
}
@Override
protected void onPause() {
// Unregister content observer
getContentResolver().unregisterContentObserver(trackpointContentObserver);
-
+
// Clear the points list.
- pathOverlay.clearPath();
+ polyline.setPoints(new ArrayList<>());
super.onPause();
}
@@ -322,46 +322,43 @@ protected void onStop() {
SharedPreferences settings = getPreferences(MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putInt(LAST_ZOOM, osmView.getZoomLevel());
- editor.commit();
+ editor.apply();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.displaytrackmap_menu, menu);
+ inflater.inflate(R.menu.displaytrackmap_menu, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
- menu.findItem(R.id.displaytrackmap_menu_center_to_gps).setEnabled( (!centerToGpsPos && currentPosition != null ) );
+ menu.findItem(R.id.displaytrackmap_menu_center_to_gps).setEnabled((!centerToGpsPos && currentPosition != null));
return super.onPrepareOptionsMenu(menu);
}
-
-
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- switch(item.getItemId()){
- case R.id.displaytrackmap_menu_center_to_gps:
- centerToGpsPos = true;
- if(currentPosition != null){
- osmViewController.animateTo(currentPosition);
- }
- break;
- case R.id.displaytrackmap_menu_settings:
- // Start settings activity
- startActivity(new Intent(this, Preferences.class));
- break;
+ switch (item.getItemId()) {
+ case R.id.displaytrackmap_menu_center_to_gps:
+ centerToGpsPos = true;
+ if (currentPosition != null) {
+ osmViewController.animateTo(currentPosition);
+ }
+ break;
+ case R.id.displaytrackmap_menu_settings:
+ // Start settings activity
+ startActivity(new Intent(this, Preferences.class));
+ break;
}
return super.onOptionsItemSelected(item);
}
-
@Override
public boolean onTouchEvent(MotionEvent event) {
- switch(event.getAction()){
+ switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
if (currentPosition != null)
centerToGpsPos = false;
@@ -370,7 +367,6 @@ public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
-
/**
* Creates overlays over the OSM view
*/
@@ -379,20 +375,22 @@ private void createOverlays() {
this.getWindowManager().getDefaultDisplay().getMetrics(metrics);
// set with to hopefully DPI independent 0.5mm
- pathOverlay = new PathOverlay(Color.BLUE, (float)(metrics.densityDpi / 25.4 / 2),this);
+ polyline = new Polyline();
+ Paint paint = polyline.getOutlinePaint();
+ paint.setColor(Color.BLUE);
+ paint.setStrokeWidth((float) (metrics.densityDpi / 25.4 / 2));
+ osmView.getOverlayManager().add(polyline);
- osmView.getOverlays().add(pathOverlay);
-
myLocationOverlay = new SimpleLocationOverlay(this);
osmView.getOverlays().add(myLocationOverlay);
-
+
wayPointsOverlay = new WayPointsOverlay(this, currentTrackId);
osmView.getOverlays().add(wayPointsOverlay);
scaleBarOverlay = new ScaleBarOverlay(osmView);
osmView.getOverlays().add(scaleBarOverlay);
}
-
+
/**
* On track path changed, update the two overlays and repaint view.
* If {@link #lastTrackPointIdProcessed} is null, this is the initial call
@@ -404,7 +402,7 @@ private void pathChanged() {
if (isFinishing()) {
return;
}
-
+
// See if the track is active.
// If not, we'll calculate initial track bounds
// while retrieving from the database.
@@ -412,18 +410,21 @@ private void pathChanged() {
boolean doInitialBoundsCalc = false;
double minLat = 91.0, minLon = 181.0;
double maxLat = -91.0, maxLon = -181.0;
- if ((! zoomedToTrackAlready) && (lastTrackPointIdProcessed == null)) {
+ if ((!zoomedToTrackAlready) && (lastTrackPointIdProcessed == null)) {
final String[] proj_active = {TrackContentProvider.Schema.COL_ACTIVE};
Cursor cursor = getContentResolver().query(
- ContentUris.withAppendedId(TrackContentProvider.CONTENT_URI_TRACK, currentTrackId),
- proj_active, null, null, null);
- if (cursor.moveToFirst()) {
- doInitialBoundsCalc =
- (cursor.getInt(cursor.getColumnIndex(TrackContentProvider.Schema.COL_ACTIVE)) == TrackContentProvider.Schema.VAL_TRACK_INACTIVE);
+ ContentUris.withAppendedId(TrackContentProvider.CONTENT_URI_TRACK, currentTrackId),
+ proj_active, null, null, null);
+ if (cursor != null && cursor.moveToFirst()) {
+ int colIndex = cursor.getColumnIndex(TrackContentProvider.Schema.COL_ACTIVE);
+ if (colIndex != -1) {
+ doInitialBoundsCalc =
+ (cursor.getInt(colIndex) == TrackContentProvider.Schema.VAL_TRACK_INACTIVE);
+ }
+ cursor.close();
}
- cursor.close();
}
-
+
// Projection: The columns to retrieve. Here, we want the latitude,
// longitude and primary key only
String[] projection = {TrackContentProvider.Schema.COL_LATITUDE, TrackContentProvider.Schema.COL_LONGITUDE, TrackContentProvider.Schema.COL_ID};
@@ -431,71 +432,69 @@ private void pathChanged() {
String selection = null;
// SelectionArgs: The parameter replacements to use for the '?' in the selection
String[] selectionArgs = null;
-
- // Only request the track points that we have not seen yet
+
+ // Only request the track points that we have not seen yet
// If we have processed any track points in this session then
// lastTrackPointIdProcessed will not be null. We only want
// to see data from rows with a primary key greater than lastTrackPointIdProcessed
if (lastTrackPointIdProcessed != null) {
selection = TrackContentProvider.Schema.COL_ID + " > ?";
- List selectionArgsList = new ArrayList();
+ List selectionArgsList = new ArrayList<>();
selectionArgsList.add(lastTrackPointIdProcessed.toString());
-
- selectionArgs = selectionArgsList.toArray(new String[1]);
+ selectionArgs = selectionArgsList.toArray(new String[1]);
}
// Retrieve any points we have not yet seen
Cursor c = getContentResolver().query(
- TrackContentProvider.trackPointsUri(currentTrackId),
- projection, selection, selectionArgs, TrackContentProvider.Schema.COL_ID + " asc");
-
- int numberOfPointsRetrieved = c.getCount();
- if (numberOfPointsRetrieved > 0 ) {
- c.moveToFirst();
- double lastLat = 0;
- double lastLon = 0;
- int primaryKeyColumnIndex = c.getColumnIndex(TrackContentProvider.Schema.COL_ID);
- int latitudeColumnIndex = c.getColumnIndex(TrackContentProvider.Schema.COL_LATITUDE);
- int longitudeColumnIndex = c.getColumnIndex(TrackContentProvider.Schema.COL_LONGITUDE);
-
- // Add each new point to the track
- while(!c.isAfterLast()) {
- lastLat = c.getDouble(latitudeColumnIndex);
- lastLon = c.getDouble(longitudeColumnIndex);
- lastTrackPointIdProcessed = c.getInt(primaryKeyColumnIndex);
- pathOverlay.addPoint((int)(lastLat * 1e6), (int)(lastLon * 1e6));
- if (doInitialBoundsCalc) {
- if (lastLat < minLat) minLat = lastLat;
- if (lastLon < minLon) minLon = lastLon;
- if (lastLat > maxLat) maxLat = lastLat;
- if (lastLon > maxLon) maxLon = lastLon;
+ TrackContentProvider.trackPointsUri(currentTrackId),
+ projection, selection, selectionArgs, TrackContentProvider.Schema.COL_ID + " asc");
+
+ if (c != null) {
+ int numberOfPointsRetrieved = c.getCount();
+ if (numberOfPointsRetrieved > 0) {
+ c.moveToFirst();
+ double lastLat = 0;
+ double lastLon = 0;
+ int primaryKeyColumnIndex = c.getColumnIndex(TrackContentProvider.Schema.COL_ID);
+ int latitudeColumnIndex = c.getColumnIndex(TrackContentProvider.Schema.COL_LATITUDE);
+ int longitudeColumnIndex = c.getColumnIndex(TrackContentProvider.Schema.COL_LONGITUDE);
+
+ // Add each new point to the track
+ while (!c.isAfterLast()) {
+ lastLat = c.getDouble(latitudeColumnIndex);
+ lastLon = c.getDouble(longitudeColumnIndex);
+ lastTrackPointIdProcessed = c.getInt(primaryKeyColumnIndex);
+ polyline.addPoint(new GeoPoint(lastLat, lastLon));
+ if (doInitialBoundsCalc) {
+ if (lastLat < minLat) minLat = lastLat;
+ if (lastLon < minLon) minLon = lastLon;
+ if (lastLat > maxLat) maxLat = lastLat;
+ if (lastLon > maxLon) maxLon = lastLon;
+ }
+ c.moveToNext();
}
- c.moveToNext();
- }
-
- // Last point is current position.
- currentPosition = new GeoPoint(lastLat, lastLon);
- myLocationOverlay.setLocation(currentPosition);
- if(centerToGpsPos) {
- osmViewController.setCenter(currentPosition);
- }
-
- // Repaint
- osmView.invalidate();
- if (doInitialBoundsCalc && (numberOfPointsRetrieved > 1)) {
- // osmdroid-3.0.8 hangs if we directly call zoomToSpan during initial onResume,
- // so post a Runnable instead for after it's done initializing.
- final double north = maxLat, east = maxLon, south = minLat, west = minLon;
- osmView.post(new Runnable() {
- @Override
- public void run() {
- osmViewController.zoomToSpan((int) (north-south), (int) (east-west));
+
+ // Last point is current position.
+ currentPosition = new GeoPoint(lastLat, lastLon);
+ myLocationOverlay.setLocation(currentPosition);
+ if (centerToGpsPos) {
+ osmViewController.setCenter(currentPosition);
+ }
+
+ // Repaint
+ osmView.invalidate();
+ if (doInitialBoundsCalc && (numberOfPointsRetrieved > 1)) {
+ // osmdroid-3.0.8 hangs if we directly call zoomToSpan during initial onResume,
+ // so post a Runnable instead for after it's done initializing.
+ final double north = maxLat, east = maxLon, south = minLat, west = minLon;
+ osmView.post(() -> {
+ osmViewController.zoomToSpan((int) (north - south), (int) (east - west));
osmViewController.setCenter(new GeoPoint((north + south) / 2, (east + west) / 2));
zoomedToTrackAlready = true;
- }
- });
+ });
+ }
}
+ c.close();
}
- c.close();
}
}
diff --git a/app/src/main/java/net/osmtracker/activity/Intro.kt b/app/src/main/java/net/osmtracker/activity/Intro.kt
index 013f88914..073ac2f6a 100644
--- a/app/src/main/java/net/osmtracker/activity/Intro.kt
+++ b/app/src/main/java/net/osmtracker/activity/Intro.kt
@@ -15,16 +15,18 @@ class Intro : AppIntro() {
// Call addSlide passing your Fragments.
// You can use AppIntroFragment to use a pre-built fragment
- addSlide(AppIntroFragment.newInstance(
+ addSlide(AppIntroFragment.createInstance(
title = getString(R.string.app_intro_slide1_title),
imageDrawable = R.drawable.icon_100x100,
+ backgroundColorRes = R.color.appintro_background_color,
description = getString(R.string.app_intro_slide1_description)
))
//TODO: change the image of slide number 2.
- addSlide(AppIntroFragment.newInstance(
+ addSlide(AppIntroFragment.createInstance(
title = getString(R.string.app_intro_slide2_title),
imageDrawable = R.drawable.icon_100x100,
+ backgroundColorRes = R.color.appintro_background_color,
description = getString(R.string.app_intro_slide2_description)
))
}
diff --git a/app/src/main/java/net/osmtracker/activity/Preferences.java b/app/src/main/java/net/osmtracker/activity/Preferences.java
index b32133503..6ccb65aed 100644
--- a/app/src/main/java/net/osmtracker/activity/Preferences.java
+++ b/app/src/main/java/net/osmtracker/activity/Preferences.java
@@ -27,6 +27,7 @@
import android.text.TextWatcher;
import android.widget.Button;
import android.widget.EditText;
+import android.widget.ListView;
/**
@@ -63,11 +64,25 @@ public class Preferences extends PreferenceActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
+ ListView listView = getListView();
+ listView.setFitsSystemWindows(true);
+ listView.setClipToPadding(false);
+ listView.setPadding(0, 48, 0, 0);
// Set summary of some preferences to their actual values
// and register a change listener to set again the summary in case of change
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
+ // Explicit execution of buttons presets window
+ Preference buttonLayoutPref = findPreference("prefs_ui_buttons_layout");
+ if (buttonLayoutPref != null) {
+ buttonLayoutPref.setOnPreferenceClickListener(preference -> {
+ Intent intent = new Intent(this, ButtonsPresets.class);
+ startActivity(intent);
+ return true;
+ });
+ }
+
// External storage directory
EditTextPreference storageDirPref = (EditTextPreference) findPreference(OSMTracker.Preferences.KEY_STORAGE_DIR);
storageDirPref.setSummary(prefs.getString(OSMTracker.Preferences.KEY_STORAGE_DIR, OSMTracker.Preferences.VAL_STORAGE_DIR));
diff --git a/app/src/main/java/net/osmtracker/activity/TrackDetail.java b/app/src/main/java/net/osmtracker/activity/TrackDetail.java
index 5f4eb6d95..5cb640f0c 100644
--- a/app/src/main/java/net/osmtracker/activity/TrackDetail.java
+++ b/app/src/main/java/net/osmtracker/activity/TrackDetail.java
@@ -15,12 +15,14 @@
import net.osmtracker.util.MercatorProjection;
import android.Manifest;
+import android.app.AlertDialog;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Paint;
+import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import androidx.core.app.ActivityCompat;
@@ -181,7 +183,7 @@ protected void onResume() {
if (cursor.isNull(cursor.getColumnIndex(TrackContentProvider.Schema.COL_OSM_UPLOAD_DATE))) {
map.put(ITEM_VALUE, getResources().getString(R.string.trackdetail_osm_upload_notyet));
} else {
- map.put(ITEM_VALUE, DateFormat.getDateTimeInstance().format(new Date(cursor.getLong(cursor.getColumnIndex(TrackContentProvider.Schema.COL_EXPORT_DATE)))));
+ map.put(ITEM_VALUE, DateFormat.getDateTimeInstance().format(new Date(cursor.getLong(cursor.getColumnIndex(TrackContentProvider.Schema.COL_OSM_UPLOAD_DATE)))));
}
data.add(map);
@@ -237,32 +239,10 @@ public boolean onOptionsItemSelected(MenuItem item) {
startActivity(i);
break;
case R.id.trackdetail_menu_export:
- if (ContextCompat.checkSelfPermission(this,
- Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
-
- // Should we show an explanation?
- if (ActivityCompat.shouldShowRequestPermissionRationale(this,
- Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
-
- // Show an expanation to the user *asynchronously* -- don't block
- // this thread waiting for the user's response! After the user
- // sees the explanation, try again to request the permission.
- // TODO: explain why we need permission.
- Log.w(TAG, "we should explain why we need write permission");
-
- } else {
-
- // No explanation needed, we can request the permission.
- ActivityCompat.requestPermissions(this,
- new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
- RC_WRITE_PERMISSIONS);
- break;
- }
-
- } else {
+ if (writeExternalStoragePermissionGranted()) {
exportTrack();
- break;
}
+ break;
case R.id.trackdetail_menu_osm_upload:
i = new Intent(this, OpenStreetMapUpload.class);
i.putExtra(TrackContentProvider.Schema.COL_TRACK_ID, trackId);
@@ -272,6 +252,51 @@ public boolean onOptionsItemSelected(MenuItem item) {
return super.onOptionsItemSelected(item);
}
+ /**
+ * Checks if the external storage write permission is granted.
+ * If not, it requests the permission and may display a rationale dialog explaining why it is needed.
+ *
+ *
For devices running Android R (API level 30) and above, this permission is not required,
+ * so the method will return {@code true} immediately.
+ *
+ * @return {@code true} if the write permission is already granted or not required (Android R+),
+ * {@code false} otherwise.
+ */
+ private boolean writeExternalStoragePermissionGranted() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ return true;
+ }
+ else if (ContextCompat.checkSelfPermission(this,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
+ // Should we show an explanation?
+ if (ActivityCompat.shouldShowRequestPermissionRationale(this,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
+ // Show an expanation to the user *asynchronously* -- don't block
+ // this thread waiting for the user's response! After the user
+ // sees the explanation, try again to request the permission.
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.permission_required)
+ .setMessage(R.string.storage_permission_for_export_GPX)
+ .setPositiveButton(R.string.acccept, (dialog, which) -> {
+ // Request the permission again
+ ActivityCompat.requestPermissions(this,
+ new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
+ RC_WRITE_PERMISSIONS);
+ })
+ .setNegativeButton(R.string.menu_cancel, (dialog, which) -> dialog.dismiss())
+ .show();
+ } else {
+ // No explanation needed, we can request the permission.
+ ActivityCompat.requestPermissions(this,
+ new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
+ RC_WRITE_PERMISSIONS);
+ }
+ }
+
+ return ContextCompat.checkSelfPermission(this,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
+ }
+
/**
* Invoke the export track task after external write permissions request.
diff --git a/app/src/main/java/net/osmtracker/activity/TrackLogger.java b/app/src/main/java/net/osmtracker/activity/TrackLogger.java
index f384d9d05..0cbb5f405 100644
--- a/app/src/main/java/net/osmtracker/activity/TrackLogger.java
+++ b/app/src/main/java/net/osmtracker/activity/TrackLogger.java
@@ -1,36 +1,12 @@
package net.osmtracker.activity;
-import java.io.File;
-
-import java.util.Date;
-import java.util.HashSet;
-
-import net.osmtracker.OSMTracker;
-import net.osmtracker.R;
-import net.osmtracker.db.DataHelper;
-import net.osmtracker.layout.GpsStatusRecord;
-import net.osmtracker.layout.UserDefinedLayout;
-import net.osmtracker.listener.SensorListener;
-import net.osmtracker.listener.PressureListener;
-import net.osmtracker.receiver.MediaButtonReceiver;
-import net.osmtracker.service.gps.GPSLogger;
-import net.osmtracker.service.gps.GPSLoggerServiceConnection;
-import net.osmtracker.util.CustomLayoutsUtils;
-import net.osmtracker.util.FileSystemUtils;
-import net.osmtracker.util.ThemeValidator;
-import net.osmtracker.view.TextNoteDialog;
-import net.osmtracker.view.VoiceRecDialog;
-import net.osmtracker.db.TrackContentProvider;
-
import android.Manifest;
-
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.ComponentName;
import android.content.ContentUris;
import android.content.ContentValues;
-
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -42,17 +18,11 @@
import android.location.LocationManager;
import android.media.AudioManager;
import android.net.Uri;
-
import android.os.Bundle;
-import android.os.Environment;
import android.os.StrictMode;
import android.preference.PreferenceManager;
import android.provider.MediaStore;
-import android.provider.MediaStore.Images.ImageColumns;
import android.provider.Settings;
-import androidx.annotation.NonNull;
-import androidx.core.app.ActivityCompat;
-import androidx.core.content.ContextCompat;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
@@ -62,6 +32,33 @@
import android.view.ViewGroup;
import android.widget.Toast;
+import androidx.annotation.NonNull;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+import androidx.core.content.FileProvider;
+
+import net.osmtracker.OSMTracker;
+import net.osmtracker.R;
+import net.osmtracker.db.DataHelper;
+import net.osmtracker.db.TrackContentProvider;
+import net.osmtracker.layout.GpsStatusRecord;
+import net.osmtracker.layout.UserDefinedLayout;
+import net.osmtracker.listener.PressureListener;
+import net.osmtracker.listener.SensorListener;
+import net.osmtracker.receiver.MediaButtonReceiver;
+import net.osmtracker.service.gps.GPSLogger;
+import net.osmtracker.service.gps.GPSLoggerServiceConnection;
+import net.osmtracker.util.CustomLayoutsUtils;
+import net.osmtracker.util.FileSystemUtils;
+import net.osmtracker.util.ThemeValidator;
+import net.osmtracker.view.TextNoteDialog;
+import net.osmtracker.view.VoiceRecDialog;
+
+import java.io.File;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.UUID;
+
/**
* Main track logger activity. Communicate with the GPS service to display GPS
@@ -75,7 +72,6 @@ public class TrackLogger extends Activity {
private static final String TAG = TrackLogger.class.getSimpleName();
final private int RC_STORAGE_AUDIO_PERMISSIONS = 1;
- final private int RC_STORAGE_CAMERA_PERMISSIONS = 2;
/**
* Request code for callback after the camera application had taken a
@@ -124,11 +120,11 @@ public class TrackLogger extends Activity {
* goes to settings/about/other screen.
*/
private boolean checkGPSFlag = true;
-
+
/**
* Keeps track of the image file when taking a picture.
*/
- private File currentImageFile;
+ private File currentPhotoFile;
/**
* Keeps track of the current track id.
@@ -180,6 +176,10 @@ public class TrackLogger extends Activity {
*/
private HashSet layoutNameTags = new HashSet();
+ public boolean getButtonsEnabled() {
+ return buttonsEnabled;
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -476,6 +476,7 @@ public boolean onOptionsItemSelected(MenuItem item) {
saveTagsForTrack();
Intent intent = new Intent(OSMTracker.INTENT_STOP_TRACKING);
+ intent.setPackage(getPackageName());
sendBroadcast(intent);
((GpsStatusRecord) findViewById(R.id.gpsStatus)).manageRecordingIndicator(false);
finish();
@@ -526,34 +527,7 @@ public boolean onKeyDown(int keyCode, KeyEvent event) {
case KeyEvent.KEYCODE_CAMERA:
Log.d(TAG, "click on camera button");
if (gpsLogger.isTracking()) {
- if (ContextCompat.checkSelfPermission(this,
- Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
- Log.d(TAG, "camera permission isn't granted, will request");
- // Should we show an explanation?
- if ( (ActivityCompat.shouldShowRequestPermissionRationale(this,
- Manifest.permission.CAMERA)) ) {
-
- // Show an expanation to the user *asynchronously* -- don't block
- // this thread waiting for the user's response! After the user
- // sees the explanation, try again to request the permission.
- // TODO: explain why we need permission.
- Log.w(TAG, "we should explain why we need write and record audio permission");
-
- } else {
-
- // No explanation needed, we can request the permission.
- ActivityCompat.requestPermissions(this,
- new String[]{
- Manifest.permission.CAMERA},
- RC_STORAGE_CAMERA_PERMISSIONS);
- break;
- }
-
- } else {
- requestStillImage();
- //return true;
- }
-
+ requestStillImage();
}
break;
case KeyEvent.KEYCODE_DPAD_CENTER:
@@ -580,92 +554,101 @@ public boolean onKeyDown(int keyCode, KeyEvent event) {
*/
public void requestStillImage() {
if (gpsLogger.isTracking()) {
- final File imageFile = pushImageFile();
- if (null != imageFile) {
-
-
- final String pictureSource = prefs.getString(OSMTracker.Preferences.KEY_UI_PICTURE_SOURCE,
- OSMTracker.Preferences.VAL_UI_PICTURE_SOURCE);
- if (OSMTracker.Preferences.VAL_UI_PICTURE_SOURCE_CAMERA.equals(pictureSource)) {
- startCamera(imageFile);
- } else if (OSMTracker.Preferences.VAL_UI_PICTURE_SOURCE_GALLERY.equals(pictureSource)) {
- startGallery();
- } else {
- // Let the user choose between using the camera
- // or selecting a picture from the gallery
-
- AlertDialog.Builder getImageFrom = new AlertDialog.Builder(TrackLogger.this);
- getImageFrom.setTitle("Select:");
- final CharSequence[] opsChars = { getString(R.string.tracklogger_camera), getString(R.string.tracklogger_gallery) };
- getImageFrom.setItems(opsChars, new android.content.DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (which == 0) {
- startCamera(imageFile);
- } else if (which == 1) {
- startGallery();
- }
- dialog.dismiss();
- }
- });
-
- getImageFrom.show();
- }
+ final String pictureSource = prefs.getString(OSMTracker.Preferences.KEY_UI_PICTURE_SOURCE,
+ OSMTracker.Preferences.VAL_UI_PICTURE_SOURCE);
+ if (OSMTracker.Preferences.VAL_UI_PICTURE_SOURCE_CAMERA.equals(pictureSource)) {
+ startCamera();
+ } else if (OSMTracker.Preferences.VAL_UI_PICTURE_SOURCE_GALLERY.equals(pictureSource)) {
+ startGallery();
} else {
- Toast.makeText(getBaseContext(),
- getResources().getString(R.string.error_externalstorage_not_writable),
- Toast.LENGTH_SHORT).show();
+ // Let the user choose between using the camera
+ // or selecting a picture from the gallery
+
+ AlertDialog.Builder getImageFrom = new AlertDialog.Builder(TrackLogger.this);
+ getImageFrom.setTitle("Select:");
+ final CharSequence[] opsChars = { getString(R.string.tracklogger_camera), getString(R.string.tracklogger_gallery) };
+ getImageFrom.setItems(opsChars, new android.content.DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == 0) {
+ startCamera();
+ } else if (which == 1) {
+ startGallery();
+ }
+ dialog.dismiss();
+ }
+ });
+
+ getImageFrom.show();
}
+ } else {
+ Toast.makeText(getBaseContext(),
+ getResources().getString(R.string.error_externalstorage_not_writable),
+ Toast.LENGTH_SHORT).show();
}
}
+ /**
+ * Get file path from android media URI
+ *
+ * @param contentUri the android media URI
+ * @return the filepath of the file
+ */
+ public String getRealPathFromURI(Uri contentUri) {
+ Cursor cursor = getContentResolver().query(contentUri, null, null, null, null);
+ int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns.DATA);
+ cursor.moveToFirst();
+ return cursor.getString(column_index);
+ }
+
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.v(TAG, "Activity result: " + requestCode + ", resultCode=" + resultCode + ", Intent=" + data);
switch (requestCode) {
case REQCODE_IMAGE_CAPTURE:
if (resultCode == RESULT_OK) {
- // A still image has been captured, track the corresponding waypoint
- // Send an intent to inform service to track the waypoint.
- File imageFile = popImageFile();
- if (imageFile != null) {
+ if (currentPhotoFile != null && currentPhotoFile.exists()) {
Intent intent = new Intent(OSMTracker.INTENT_TRACK_WP);
+ intent.putExtra(OSMTracker.INTENT_KEY_UUID, UUID.randomUUID().toString());
intent.putExtra(TrackContentProvider.Schema.COL_TRACK_ID, currentTrackId);
intent.putExtra(OSMTracker.INTENT_KEY_NAME, getResources().getString(R.string.wpt_stillimage));
- intent.putExtra(OSMTracker.INTENT_KEY_LINK, imageFile.getName());
+ intent.putExtra(OSMTracker.INTENT_KEY_LINK, currentPhotoFile.getName());
+ intent.setPackage(this.getPackageName());
sendBroadcast(intent);
+ } else {
+ Log.e(TAG, "Cannot get image path from camera intent");
}
}
break;
case REQCODE_GALLERY_CHOSEN:
if (resultCode == RESULT_OK) {
- // An image has been selected from the gallery, track the corresponding waypoint
- File imageFile = popImageFile();
- if (imageFile != null) {
+ // Get imagePath from Gallery Uri
+ String imagePath = getRealPathFromURI(data.getData());
+ File imageFile = new File(imagePath != null ? imagePath : "");
+ if (imageFile.exists()) {
// Copy the file from the gallery
- Cursor c = getContentResolver().query(data.getData(), null, null, null, null);
- c.moveToFirst();
- String f = c.getString(c.getColumnIndex(ImageColumns.DATA));
- c.close();
- Log.d(TAG, "Copying gallery file '"+f+"' into '"+imageFile+"'");
- FileSystemUtils.copyFile(imageFile.getParentFile(), new File(f), imageFile.getName());
+ File destFile = createImageFile();
+ Log.d(TAG, "Copying gallery file '" + imagePath + "' into '" + destFile.getAbsolutePath() + "'");
+ FileSystemUtils.copyFile(destFile.getParentFile(), new File(imagePath), destFile.getName());
// Send an intent to inform service to track the waypoint.
Intent intent = new Intent(OSMTracker.INTENT_TRACK_WP);
+ intent.putExtra(OSMTracker.INTENT_KEY_UUID, UUID.randomUUID().toString());
intent.putExtra(TrackContentProvider.Schema.COL_TRACK_ID, currentTrackId);
intent.putExtra(OSMTracker.INTENT_KEY_NAME, getResources().getString(R.string.wpt_stillimage));
- intent.putExtra(OSMTracker.INTENT_KEY_LINK, imageFile.getName());
+ intent.putExtra(OSMTracker.INTENT_KEY_LINK, destFile.getName());
+ intent.setPackage(this.getPackageName());
sendBroadcast(intent);
+ } else {
+ Log.e(TAG, "Cannot get image path from gallery intent");
}
}
}
super.onActivityResult(requestCode, resultCode, data);
}
-
-
-
+
/**
* Getter for gpsLogger
*
@@ -684,45 +667,25 @@ public GPSLogger getGpsLogger() {
public void setGpsLogger(GPSLogger l) {
this.gpsLogger = l;
}
-
+
/**
- * Gets a File for storing an image in the current track dir
- * and stores it in a class variable.
- *
- * @return A File pointing to an image file inside the current track directory
+ * Create Image file according to DataHelper format and location
+ *
+ * @return a File
*/
- public File pushImageFile() {
- currentImageFile = null;
-
- // Query for current track directory
+ public File createImageFile() {
File trackDir = DataHelper.getTrackDirectory(currentTrackId, this);
-
- // Create the track storage directory if it does not yet exist
- if (!trackDir.exists()) {
- if ( !trackDir.mkdirs() ) {
- Log.w(TAG, "Directory [" + trackDir.getAbsolutePath() + "] does not exist and cannot be created");
- }
+ if (!trackDir.exists() && !trackDir.mkdirs()) {
+ Log.w(TAG, "Directory [" + trackDir.getAbsolutePath() + "] does not exist and cannot be created");
+ return null;
}
-
- // Ensure that this location can be written to
if (trackDir.exists() && trackDir.canWrite()) {
- currentImageFile = new File(trackDir,
- DataHelper.FILENAME_FORMATTER.format(new Date()) + DataHelper.EXTENSION_JPG);
- } else {
- Log.w(TAG, "The directory [" + trackDir.getAbsolutePath() + "] will not allow files to be created");
+ File imageFile = new File(trackDir, DataHelper.FILENAME_FORMATTER.format(new Date()) + DataHelper.EXTENSION_JPG);
+ Log.d(TAG, "New Image File: " + imageFile);
+ return imageFile;
}
-
- Log.d(TAG, "currentImage File: " + currentImageFile);
- return currentImageFile;
- }
-
- /**
- * @return The current image file, and clear the internal variable.
- */
- public File popImageFile() {
- File imageFile = currentImageFile;
- currentImageFile = null;
- return imageFile;
+ Log.w(TAG, "The directory [" + trackDir.getAbsolutePath() + "] will not allow files to be created");
+ return null;
}
@Override
@@ -795,13 +758,21 @@ public long getCurrentTrackId() {
/**
* Starts the camera app. to take a picture
- * @param imageFile File to save the picture to
*/
- private void startCamera(File imageFile) {
+ private void startCamera() {
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
+
+ currentPhotoFile = createImageFile();
+ if (currentPhotoFile == null) {
+ Log.e(TAG, "imageFile is NULL in startCamera");
+ return;
+ }
+ Uri imageUriContent = FileProvider.getUriForFile(this, DataHelper.FILE_PROVIDER_AUTHORITY, currentPhotoFile);
+
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
- cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(imageFile));
+ cameraIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUriContent);
startActivityForResult(cameraIntent, REQCODE_IMAGE_CAPTURE);
}
@@ -809,10 +780,10 @@ private void startCamera(File imageFile) {
* Starts the gallery app. to choose a picture
*/
private void startGallery() {
- Intent intent = new Intent();
- intent.setType("image/*");
- intent.setAction(Intent.ACTION_GET_CONTENT);
- startActivityForResult(Intent.createChooser(intent, getString(R.string.tracklogger_choose_gallery_camera)), REQCODE_GALLERY_CHOSEN);
+ Intent galleryIntent = new Intent();
+ galleryIntent.setType(DataHelper.MIME_TYPE_IMAGE);
+ galleryIntent.setAction(Intent.ACTION_GET_CONTENT);
+ startActivityForResult(galleryIntent, REQCODE_GALLERY_CHOSEN);
}
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@@ -837,27 +808,6 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis
}
return;
}
-
- case RC_STORAGE_CAMERA_PERMISSIONS: {
- Log.d(TAG, "camera case");
- // If request is cancelled, the result arrays are empty.
- if (grantResults.length > 1) {
- // TODO: fix permission management
- //&& grantResults[0] == PackageManager.PERMISSION_GRANTED
- //&& grantResults[1] == PackageManager.PERMISSION_GRANTED) {
-
- // permission was granted, yay!
- requestStillImage();
-
- } else {
-
- // permission denied, boo! Disable the
- // functionality that depends on this permission.
- //TODO: add an informative message.
- Log.v(TAG, "Camera permission is denied.");
- }
- return;
- }
}
}
diff --git a/app/src/main/java/net/osmtracker/activity/TrackManager.java b/app/src/main/java/net/osmtracker/activity/TrackManager.java
index 337485293..57e317444 100644
--- a/app/src/main/java/net/osmtracker/activity/TrackManager.java
+++ b/app/src/main/java/net/osmtracker/activity/TrackManager.java
@@ -40,6 +40,7 @@
import net.osmtracker.exception.CreateTrackException;
import net.osmtracker.gpx.ExportToStorageTask;
import net.osmtracker.gpx.ExportToTempFileTask;
+import net.osmtracker.gpx.ZipHelper;
import net.osmtracker.util.FileSystemUtils;
import java.io.File;
@@ -84,6 +85,9 @@ public class TrackManager extends AppCompatActivity
private TrackListRVAdapter recyclerViewAdapter;
+ // To check if the RecyclerView already has a DividerItemDecoration added
+ private boolean hasDivider;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -112,6 +116,14 @@ public void onClick(View view) {
Intent intro = new Intent(this, Intro.class);
startActivity(intro);
}
+ RecyclerView recyclerView = findViewById(R.id.recyclerview);
+ recyclerView.setLayoutManager(new LinearLayoutManager(this));
+
+ // Adding a horizontal divider
+ DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(), DividerItemDecoration.VERTICAL);
+ dividerItemDecoration.setDrawable(ContextCompat.getDrawable(this, R.drawable.divider)); // Using a custom drawable
+
+ recyclerView.addItemDecoration(dividerItemDecoration);
}
@Override
@@ -139,7 +151,7 @@ protected void onResume() {
/**
- *
+ * Configures and initializes the RecyclerView for displaying the list of tracks.
*/
private void setRecyclerView() {
RecyclerView recyclerView = findViewById(R.id.recyclerview);
@@ -147,11 +159,13 @@ private void setRecyclerView() {
LinearLayoutManager layoutManager = new LinearLayoutManager(this,
LinearLayoutManager.VERTICAL, false);
recyclerView.setLayoutManager(layoutManager);
-
- DividerItemDecoration did = new DividerItemDecoration(recyclerView.getContext(),
- layoutManager.getOrientation());
- recyclerView.addItemDecoration(did);
-
+ // adds a divider decoration if not already present
+ if (!hasDivider) {
+ DividerItemDecoration did = new DividerItemDecoration(recyclerView.getContext(),
+ layoutManager.getOrientation());
+ recyclerView.addItemDecoration(did);
+ hasDivider = true;
+ }
recyclerView.setHasFixedSize(true);
Cursor cursor = getContentResolver().query(
TrackContentProvider.CONTENT_URI_TRACK, null, null, null,
@@ -281,7 +295,7 @@ private void tryStartTrackLogger(Intent intent){
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.ACCESS_FINE_LOCATION)) {
Log.i(TAG,"Should explain");
- Toast.makeText(this, "Can't continue without GPS permission",
+ Toast.makeText(this, R.string.gps_perms_required,
Toast.LENGTH_LONG).show();
}
@@ -396,10 +410,13 @@ public boolean onContextItemSelected(MenuItem item) {
// stop the active track
stopActiveTrack();
break;
-
case R.id.trackmgr_contextmenu_resume:
- // let's activate the track and start the TrackLogger activity
- setActiveTrack(contextMenuSelectedTrackid);
+ // Activate the selected track if it is different from the currently active one
+ // (or if no track is currently active)
+ if (currentTrackId != contextMenuSelectedTrackid) {
+ setActiveTrack(contextMenuSelectedTrackid);
+ }
+ // Start the TrackLogger activity to begin logging the selected track
i = new Intent(this, TrackLogger.class);
i.putExtra(TrackContentProvider.Schema.COL_TRACK_ID, contextMenuSelectedTrackid);
tryStartTrackLogger(i);
@@ -565,7 +582,9 @@ private static void prepareAndShareTrack(final long trackId, Context context) {
new ExportToTempFileTask(context, trackId){
@Override
protected void executionCompleted(){
- shareFile(this.getTmpFile(), context);
+ // Creates a zip file with the trace and its multimedia files
+ File zipFile = ZipHelper.zipCacheFiles(context, trackId, this.getTmpFile());
+ shareFile(zipFile, context);
}
@Override
@@ -688,6 +707,7 @@ private void stopActiveTrack(){
if(currentTrackId != TRACK_ID_NO_TRACK){
// we send a broadcast to inform all registered services to stop tracking
Intent intent = new Intent(OSMTracker.INTENT_STOP_TRACKING);
+ intent.setPackage(this.getPackageName());
sendBroadcast(intent);
// need to get sure, that the database is up to date
@@ -720,7 +740,7 @@ public void onRequestPermissionsResult(int requestCode, String permissions[],
// functionality that depends on this permission.
//TODO: add an informative message.
Log.w(TAG, "we should explain why we need write permission_EXPORT_ALL");
- Toast.makeText(this, "To export the GPX trace we need to write on the storage.", Toast.LENGTH_LONG).show();
+ Toast.makeText(this, R.string.storage_permission_for_export_GPX, Toast.LENGTH_LONG).show();
}
break;
}
@@ -738,7 +758,7 @@ public void onRequestPermissionsResult(int requestCode, String permissions[],
// functionality that depends on this permission.
//TODO: add an informative message.
Log.w(TAG, "we should explain why we need write permission_EXPORT_ONE");
- Toast.makeText(this, "To export the GPX trace we need to write on the storage.", Toast.LENGTH_LONG).show();
+ Toast.makeText(this, R.string.storage_permission_for_export_GPX, Toast.LENGTH_LONG).show();
}
break;
}
@@ -755,7 +775,7 @@ public void onRequestPermissionsResult(int requestCode, String permissions[],
// functionality that depends on this permission.
//TODO: add an informative message.
Log.w(TAG, "Permission not granted");
- Toast.makeText(this, "To display the track properly we need access to the storage.", Toast.LENGTH_LONG).show();
+ Toast.makeText(this, R.string.storage_permission_for_display_track, Toast.LENGTH_LONG).show();
}
break;
}
@@ -773,7 +793,7 @@ public void onRequestPermissionsResult(int requestCode, String permissions[],
// functionality that depends on this permission.
//TODO: add an informative message.
Log.w(TAG, "Permission not granted");
- Toast.makeText(this, "To share the track properly we need access to the storage.", Toast.LENGTH_LONG).show();
+ Toast.makeText(this, R.string.storage_permission_for_share_track, Toast.LENGTH_LONG).show();
}
break;
}
@@ -790,7 +810,7 @@ public void onRequestPermissionsResult(int requestCode, String permissions[],
// functionality that depends on this permission.
//TODO: add an informative message.
Log.w(TAG, "Permission not granted");
- Toast.makeText(this, "To upload the track to OSM we need access to the storage.", Toast.LENGTH_LONG).show();
+ Toast.makeText(this, R.string.storage_permission_for_upload_to_OSM, Toast.LENGTH_LONG).show();
}
break;
}
diff --git a/app/src/main/java/net/osmtracker/activity/WaypointList.java b/app/src/main/java/net/osmtracker/activity/WaypointList.java
index f337e7c51..0c046c7ee 100644
--- a/app/src/main/java/net/osmtracker/activity/WaypointList.java
+++ b/app/src/main/java/net/osmtracker/activity/WaypointList.java
@@ -1,11 +1,28 @@
package net.osmtracker.activity;
-import net.osmtracker.db.TrackContentProvider;
-import net.osmtracker.db.WaypointListAdapter;
-
+import android.app.AlertDialog;
import android.app.ListActivity;
+import android.content.DialogInterface;
+import android.content.Intent;
import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Button;
import android.widget.CursorAdapter;
+import android.widget.EditText;
+import android.widget.ListView;
+import androidx.core.content.FileProvider;
+import net.osmtracker.R;
+import net.osmtracker.db.DataHelper;
+import net.osmtracker.db.TrackContentProvider;
+import net.osmtracker.db.WaypointListAdapter;
+import net.osmtracker.listener.EditWaypointDialogOnClickListener;
+
+import java.io.File;
/**
* Activity that lists the previous waypoints tracked by the user.
@@ -15,6 +32,17 @@
*/
public class WaypointList extends ListActivity {
+ private static final String TAG = WaypointList.class.getSimpleName();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ ListView listView = getListView();
+ listView.setFitsSystemWindows(true);
+ listView.setClipToPadding(false);
+ listView.setPadding(0, 48, 0, 0);
+ }
+
@Override
protected void onResume() {
Long trackId = getIntent().getExtras().getLong(TrackContentProvider.Schema.COL_TRACK_ID);
@@ -41,4 +69,152 @@ protected void onPause() {
super.onPause();
}
+ /**
+ * Handles the selection of a waypoint from the list and opens an edit dialog.
+ * This dialog allows the user to update the waypoint's name and preview attached files (images or audio).
+ *
+ * @param l The ListView where the item was clicked.
+ * @param v The view that was clicked.
+ * @param position The position of the clicked item.
+ * @param id The ID of the clicked waypoint.
+ */
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ final Cursor cursor = ((CursorAdapter) getListAdapter()).getCursor();
+ final DataHelper dataHelper = new DataHelper(l.getContext());
+ LayoutInflater inflater = this.getLayoutInflater();
+
+ // Inflate the waypoint edit dialog layout
+ final View editWaypointDialog = inflater.inflate(R.layout.edit_waypoint_dialog, null);
+ final EditText editWaypointName = editWaypointDialog.findViewById(R.id.edit_waypoint_et_name);
+
+ Button buttonPreview = editWaypointDialog.findViewById(R.id.edit_waypoint_button_preview);
+ Button buttonUpdate = editWaypointDialog.findViewById(R.id.edit_waypoint_button_update);
+ Button buttonDelete = editWaypointDialog.findViewById(R.id.edit_waypoint_button_delete);
+ Button buttonCancel = editWaypointDialog.findViewById(R.id.edit_waypoint_button_cancel);
+
+ // Retrieve existing waypoint name
+ String oldName = cursor.getString(cursor.getColumnIndex(TrackContentProvider.Schema.COL_NAME));
+ editWaypointName.setText(oldName);
+ editWaypointName.setSelection(oldName.length());
+
+ // Retrieve waypoint details
+ final long trackId = cursor.getLong(cursor.getColumnIndex(TrackContentProvider.Schema.COL_TRACK_ID));
+ final String uuid = cursor.getString(cursor.getColumnIndex(TrackContentProvider.Schema.COL_UUID));
+ final String link = cursor.getString(cursor.getColumnIndex(TrackContentProvider.Schema.COL_LINK));
+
+ final String filePath = (link != null) ? DataHelper.getTrackDirectory(trackId, l.getContext()) + "/" + link : null;
+ File file = (filePath != null) ? new File(filePath) : null;
+
+ if (file != null && file.exists()) {
+ try {
+ if (isImageFile(filePath) || isAudioFile(filePath)) {
+ buttonPreview.setVisibility(View.VISIBLE);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error handling file: " + filePath, e);
+ }
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setCancelable(true);
+ AlertDialog alert = builder.create();
+
+ // Preview button
+ buttonPreview.setOnClickListener(new EditWaypointDialogOnClickListener(alert, null) {
+ @Override
+ public void onClick(View view) {
+ if (filePath != null) {
+ File file = new File(filePath);
+ Uri fileUri = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) ?
+ FileProvider.getUriForFile(getApplicationContext(), DataHelper.FILE_PROVIDER_AUTHORITY, file) :
+ Uri.fromFile(file);
+
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ if (isImageFile(filePath)) {
+ intent.setDataAndType(fileUri, DataHelper.MIME_TYPE_IMAGE);
+ } else if (isAudioFile(filePath)) {
+ intent.setDataAndType(fileUri, DataHelper.MIME_TYPE_AUDIO);
+ }
+
+ if (intent.resolveActivity(getPackageManager()) != null) {
+ startActivity(intent);
+ }
+ }
+ alert.dismiss();
+ }
+ });
+
+ // Update waypoint name
+ buttonUpdate.setOnClickListener(new EditWaypointDialogOnClickListener(alert, null) {
+ @Override
+ public void onClick(View view) {
+ String newName = editWaypointName.getText().toString();
+ dataHelper.updateWayPoint(trackId, uuid, newName, link);
+ alert.dismiss();
+ }
+ });
+
+ // Delete waypoint
+ buttonDelete.setOnClickListener(new EditWaypointDialogOnClickListener(alert, cursor) {
+ @Override
+ public void onClick(View view) {
+ new AlertDialog.Builder(WaypointList.this)
+ .setTitle(getString(R.string.delete_waypoint_confirm_dialog_title))
+ .setMessage(getString(R.string.delete_waypoint_confirm_dialog_msg))
+ .setPositiveButton(getString(R.string.delete_waypoint_confirm_bt_ok), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dataHelper.deleteWayPoint(uuid, filePath);
+ cursor.requery();
+ alert.dismiss();
+ dialog.dismiss();
+ }
+ })
+ .setNegativeButton(getString(R.string.delete_waypoint_confirm_bt_cancel), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ })
+ .show();
+ }
+ });
+
+ // Cancel button
+ buttonCancel.setOnClickListener(new EditWaypointDialogOnClickListener(alert, null) {
+ @Override
+ public void onClick(View view) {
+ alert.dismiss();
+ }
+ });
+
+ alert.setView(editWaypointDialog);
+ alert.show();
+
+ super.onListItemClick(l, v, position, id);
+ }
+
+ /**
+ * Checks if a given file path corresponds to an image.
+ *
+ * @param path The file path.
+ * @return True if the file is an image, false otherwise.
+ */
+ private boolean isImageFile(String path) {
+ return path.endsWith(DataHelper.EXTENSION_JPG);
+ }
+
+ /**
+ * Checks if a given file path corresponds to an audio file.
+ *
+ * @param path The file path.
+ * @return True if the file is an audio file, false otherwise.
+ */
+ private boolean isAudioFile(String path) {
+ return path.endsWith(DataHelper.EXTENSION_3GPP);
+ }
+
}
diff --git a/app/src/main/java/net/osmtracker/db/DataHelper.java b/app/src/main/java/net/osmtracker/db/DataHelper.java
index 3dd174563..aafdbb751 100644
--- a/app/src/main/java/net/osmtracker/db/DataHelper.java
+++ b/app/src/main/java/net/osmtracker/db/DataHelper.java
@@ -48,11 +48,26 @@ public class DataHelper {
*/
public static final String EXTENSION_JPG = ".jpg";
+ /**
+ * ZIP file extension
+ */
+ public static final String EXTENSION_ZIP = ".zip";
+
/**
* GPX Files MIME standard for sharing
*/
public static final String MIME_TYPE_GPX = "application/gpx+xml";
+ /**
+ * Audio Files MIME
+ */
+ public static final String MIME_TYPE_AUDIO = "audio/*";
+
+ /**
+ * Image Files MIME
+ */
+ public static final String MIME_TYPE_IMAGE = "image/*";
+
/**
* APP sign plus FileProvider = authority
*/
@@ -253,16 +268,25 @@ public void updateWayPoint(long trackId, String uuid, String name, String link)
}
/**
- * Deletes a waypoint
+ * Deletes a waypoint and its file associated (if exists)
*
* @param uuid
* Unique ID of the target waypoint
+ *
+ * @param filepath
+ * file attached to the waypoint
*/
- public void deleteWayPoint(String uuid) {
+ public void deleteWayPoint(String uuid, String filepath) {
Log.v(TAG, "Deleting waypoint with uuid '" + uuid);
if (uuid != null) {
contentResolver.delete(Uri.withAppendedPath(TrackContentProvider.CONTENT_URI_WAYPOINT_UUID, uuid), null, null);
}
+
+ // delete file if exists
+ File file = (filepath != null) ? new File(filepath) : null;
+ if (file != null && file.exists() && file.delete()) {
+ Log.v(TAG, "File deleted: " + filepath);
+ }
}
@@ -396,33 +420,12 @@ public static File getTrackDirectory(long trackId, Context context) {
File _return = null;
String trackStorageDirectory = context.getExternalFilesDir(null)
- + "/osmtracker/track" + trackId;
+ + OSMTracker.Preferences.VAL_STORAGE_DIR + File.separator + "track" + trackId;
_return = new File(trackStorageDirectory);
return _return;
}
- /* method not in use. TODO: delete code.
- public static File getGPXTrackFile(long trackId, ContentResolver contentResolver, Context context) {
-
- String trackName = getTrackNameInDB(trackId, contentResolver);
-
- File sdRoot = Environment.getExternalStorageDirectory();
-
- // The location where the user has specified gpx files and associated content to be written
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- String userGPXExportDirectoryName = prefs.getString(
- OSMTracker.Preferences.KEY_STORAGE_DIR, OSMTracker.Preferences.VAL_STORAGE_DIR);
-
- // Build storage track path for file creation
- String completeGPXTrackPath = sdRoot + userGPXExportDirectoryName.trim() +
- File.separator + trackName.trim() + File.separator +
- trackName.trim() + DataHelper.EXTENSION_GPX;
-
- return new File(completeGPXTrackPath);
- }
- */
-
public static String getTrackNameInDB(long trackId, ContentResolver contentResolver) {
String trackName = "";
Uri trackUri = ContentUris.withAppendedId(TrackContentProvider.CONTENT_URI_TRACK, trackId);
diff --git a/app/src/main/java/net/osmtracker/db/TrackContentProvider.java b/app/src/main/java/net/osmtracker/db/TrackContentProvider.java
index ffb551ed0..38f344221 100644
--- a/app/src/main/java/net/osmtracker/db/TrackContentProvider.java
+++ b/app/src/main/java/net/osmtracker/db/TrackContentProvider.java
@@ -471,7 +471,6 @@ public static final class Schema {
public static final String TBL_TRACKPOINT = "trackpoint";
public static final String TBL_WAYPOINT = "waypoint";
public static final String TBL_TRACK = "track";
-
public static final String COL_ID = "_id";
public static final String COL_TRACK_ID = "track_id";
public static final String COL_UUID = "uuid";
diff --git a/app/src/main/java/net/osmtracker/db/WaypointListAdapter.java b/app/src/main/java/net/osmtracker/db/WaypointListAdapter.java
index b8f847a45..2f8ebc75c 100644
--- a/app/src/main/java/net/osmtracker/db/WaypointListAdapter.java
+++ b/app/src/main/java/net/osmtracker/db/WaypointListAdapter.java
@@ -13,6 +13,7 @@
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.RelativeLayout;
+import android.widget.TableLayout;
import android.widget.TextView;
/**
@@ -45,15 +46,15 @@ public WaypointListAdapter(Context context, Cursor c) {
@Override
public void bindView(View view, Context context, Cursor cursor) {
- RelativeLayout rl = (RelativeLayout) view;
- bind(cursor, rl, context);
+ TableLayout tl = (TableLayout) view;
+ bind(cursor, tl, context);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup vg) {
- RelativeLayout rl = (RelativeLayout) LayoutInflater.from(vg.getContext()).inflate(R.layout.waypointlist_item,
+ TableLayout tl = (TableLayout) LayoutInflater.from(vg.getContext()).inflate(R.layout.waypointlist_item,
vg, false);
- return bind(cursor, rl, context);
+ return bind(cursor, tl, context);
}
/**
@@ -61,16 +62,16 @@ public View newView(Context context, Cursor cursor, ViewGroup vg) {
*
* @param cursor
* Cursor to pull data
- * @param rl
+ * @param tl
* RelativeView representing one item
* @param context
* Context, to get resources
* @return The relative view with data bound.
*/
- private View bind(Cursor cursor, RelativeLayout rl, Context context) {
- TextView vName = (TextView) rl.findViewById(R.id.wplist_item_name);
- TextView vLocation = (TextView) rl.findViewById(R.id.wplist_item_location);
- TextView vTimestamp = (TextView) rl.findViewById(R.id.wplist_item_timestamp);
+ private View bind(Cursor cursor, TableLayout tl, Context context) {
+ TextView vName = (TextView) tl.findViewById(R.id.wplist_item_name);
+ TextView vLocation = (TextView) tl.findViewById(R.id.wplist_item_location);
+ TextView vTimestamp = (TextView) tl.findViewById(R.id.wplist_item_timestamp);
// Bind name
String name = cursor.getString(cursor.getColumnIndex(TrackContentProvider.Schema.COL_NAME));
@@ -103,8 +104,7 @@ private View bind(Cursor cursor, RelativeLayout rl, Context context) {
// Bind timestamp
Date ts = new Date(cursor.getLong(cursor.getColumnIndex(TrackContentProvider.Schema.COL_TIMESTAMP)));
vTimestamp.setText(DATE_FORMATTER.format(ts));
-
- return rl;
+ return tl;
}
}
diff --git a/app/src/main/java/net/osmtracker/gpx/ExportToStorageTask.java b/app/src/main/java/net/osmtracker/gpx/ExportToStorageTask.java
index 32d97718d..855929f85 100644
--- a/app/src/main/java/net/osmtracker/gpx/ExportToStorageTask.java
+++ b/app/src/main/java/net/osmtracker/gpx/ExportToStorageTask.java
@@ -1,5 +1,7 @@
package net.osmtracker.gpx;
+import static net.osmtracker.util.FileSystemUtils.getUniqueChildNameFor;
+
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Environment;
@@ -15,34 +17,57 @@
import java.io.File;
import java.util.Date;
-import static net.osmtracker.util.FileSystemUtils.getUniqueChildNameFor;
-
-import androidx.core.content.ContextCompat;
-
/**
- * Exports to the external storage / SD card
- * in a folder defined by the user.
+ * ExportToStorageTask is responsible for exporting track data to the device's storage.
+ * It extends the ExportTrackTask class and provides specific implementations for
+ * exporting track data to a directory on the external storage.
*/
public class ExportToStorageTask extends ExportTrackTask {
private static final String TAG = ExportToStorageTask.class.getSimpleName();
- private String ERROR_MESSAGE;
-
-
+ private final String ERROR_MESSAGE;
+ private final DataHelper dataHelper;
+ private final SharedPreferences sharedPreferences;
+
+ /**
+ * Constructor for ExportToStorageTask.
+ *
+ * @param context the context of the application
+ * @param trackId the IDs of the tracks to be exported
+ */
public ExportToStorageTask(Context context, long... trackId) {
+ this(context, new DataHelper(context), trackId);
+ }
+
+ /**
+ * Constructor for ExportToStorageTask with a DataHelper instance.
+ *
+ * @param context the context of the application
+ * @param dataHelper the DataHelper instance for accessing track data
+ * @param trackId the IDs of the tracks to be exported
+ */
+ public ExportToStorageTask(Context context, DataHelper dataHelper, long... trackId) {
super(context, trackId);
+ this.dataHelper = dataHelper;
+ this.sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
ERROR_MESSAGE = context.getString(R.string.error_create_track_dir);
}
+ /**
+ * Gets the directory where the track data will be exported.
+ *
+ * @param startDate the start date of the track
+ * @return the directory where the track data will be exported
+ * @throws ExportTrackException if the directory cannot be created
+ */
@Override
protected File getExportDirectory(Date startDate) throws ExportTrackException {
- SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
-
String trackName = getSanitizedTrackNameByStartDate(startDate);
- boolean shouldCreateDirectoryPerTrack = shouldCreateDirectoryPerTrack(preferences);
- File finalExportDirectory = getBaseExportDirectory(preferences);
+ boolean shouldCreateDirectoryPerTrack = shouldCreateDirectoryPerTrack();
+ File finalExportDirectory = getBaseExportDirectory();
+ Log.d(TAG, "absolute dir: " + finalExportDirectory.getAbsolutePath());
- if( shouldCreateDirectoryPerTrack && trackName.length() >= 1){
+ if( shouldCreateDirectoryPerTrack && !trackName.isEmpty()){
String uniqueFolderName = getUniqueChildNameFor(finalExportDirectory, trackName, "");
finalExportDirectory = new File(finalExportDirectory, uniqueFolderName);
finalExportDirectory.mkdirs();
@@ -53,65 +78,76 @@ protected File getExportDirectory(Date startDate) throws ExportTrackException {
return finalExportDirectory;
}
+ /**
+ * Gets a sanitized track name based on the start date.
+ *
+ * @param startDate the start date of the track
+ * @return the sanitized track name
+ */
+ public String getSanitizedTrackNameByStartDate(Date startDate) {
+ Track track = dataHelper.getTrackByStartDate(startDate);
+
+ String trackName = "";
+ if (track != null) {
+ trackName = track.getName();
+ }
+ if (trackName != null && !trackName.isEmpty()) {
+ trackName = trackName.replace("/", "_").trim();
+ }
+ return trackName;
+ }
- /**
- *
- * @param startDate
- * @return
- */
- public String getSanitizedTrackNameByStartDate(Date startDate){
-
- DataHelper dh = new DataHelper(context);
- Track track = dh.getTrackByStartDate(startDate);
-
- String trackName = "";
- if(track != null){
- trackName = track.getName();
- }
- if(trackName != null && trackName.length() >= 1) {
- trackName = trackName.replace("/", "_").trim();
- }
- return trackName;
- }
-
- public boolean shouldCreateDirectoryPerTrack(SharedPreferences prefs){
- return prefs.getBoolean(OSMTracker.Preferences.KEY_OUTPUT_DIR_PER_TRACK,
+ /**
+ * Determines whether a separate directory should be created for each track.
+ *
+ * @return true if a separate directory should be created for each track, false otherwise
+ */
+ public boolean shouldCreateDirectoryPerTrack(){
+ return sharedPreferences.getBoolean(OSMTracker.Preferences.KEY_OUTPUT_DIR_PER_TRACK,
OSMTracker.Preferences.VAL_OUTPUT_GPX_OUTPUT_DIR_PER_TRACK);
-
}
- // Checks if a volume containing external storage is available for read and write.
+ /**
+ * Checks if external storage is writable.
+ *
+ * @return true if external storage is writable, false otherwise
+ */
private boolean isExternalStorageWritable() {
return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
}
- // Create before returning if not exists
- public File getBaseExportDirectory(SharedPreferences prefs) throws ExportTrackException {
+ /**
+ * Gets the base directory where the track data will be exported.
+ * Creates the directory if it does not exist.
+ *
+ * @return the base directory where the track data will be exported
+ * @throws ExportTrackException if the directory cannot be created or is not writable
+ */
+ public File getBaseExportDirectory() throws ExportTrackException {
if (!isExternalStorageWritable()) {
throw new ExportTrackException(
context.getResources().getString(R.string.error_externalstorage_not_writable));
}
-
- String exportDirectoryNameInPreferences = prefs.getString(
+ String exportDirectoryNameInPreferences = sharedPreferences.getString(
OSMTracker.Preferences.KEY_STORAGE_DIR, OSMTracker.Preferences.VAL_STORAGE_DIR);
Log.d(TAG,"exportDirectoryNameInPreferences: " + exportDirectoryNameInPreferences);
- File baseExportDirectory = new File(context.getExternalFilesDir(null),
+ File baseExportDirectory = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS),
exportDirectoryNameInPreferences);
-
- if(! baseExportDirectory.exists()){
+
+ // if folder not exists, create it
+ if (!baseExportDirectory.exists()) {
boolean ok = baseExportDirectory.mkdirs();
if (!ok) {
throw new ExportTrackException(
- context.getResources().getString(
- R.string.error_externalstorage_not_writable));
+ context.getResources().getString(R.string.error_externalstorage_not_writable));
}
}
- Log.d(TAG, "BaseExportDirectory: " + baseExportDirectory);
+ Log.d(TAG, "BaseExportDirectory: " + baseExportDirectory.getAbsolutePath());
return baseExportDirectory;
- }
+ }
@Override
protected boolean exportMediaFiles() {
diff --git a/app/src/main/java/net/osmtracker/gpx/ExportToTempFileTask.java b/app/src/main/java/net/osmtracker/gpx/ExportToTempFileTask.java
index 8c1a3d30d..fdc971180 100644
--- a/app/src/main/java/net/osmtracker/gpx/ExportToTempFileTask.java
+++ b/app/src/main/java/net/osmtracker/gpx/ExportToTempFileTask.java
@@ -2,12 +2,14 @@
import android.content.Context;
import android.database.Cursor;
+import android.preference.PreferenceManager;
import android.util.Log;
+import net.osmtracker.OSMTracker;
+import net.osmtracker.db.DataHelper;
import net.osmtracker.exception.ExportTrackException;
import java.io.File;
-import java.io.IOException;
import java.util.Date;
/**
@@ -24,10 +26,21 @@ public abstract class ExportToTempFileTask extends ExportTrackTask {
public ExportToTempFileTask(Context context, long trackId) {
super(context, trackId);
+ String desiredOutputFormat = PreferenceManager.getDefaultSharedPreferences(context).getString(
+ OSMTracker.Preferences.KEY_OUTPUT_FILENAME,
+ OSMTracker.Preferences.VAL_OUTPUT_FILENAME);
+
try {
- tmpFile = File.createTempFile("osm-upload", ".gpx", context.getCacheDir());
- Log.d(TAG, "Temporary file: " + tmpFile.getAbsolutePath());
- } catch (IOException ioe) {
+ String trackName = new DataHelper(context).getTrackById(trackId).getName();
+
+ long startDate = new DataHelper(context).getTrackById(trackId).getTrackDate();
+ String formattedTrackStartDate = DataHelper.FILENAME_FORMATTER.format(new Date(startDate));
+
+ // Create temporary file
+ String tmpFilename = super.formatGpxFilename(desiredOutputFormat, trackName, formattedTrackStartDate);
+ tmpFile = new File(context.getCacheDir(),tmpFilename + DataHelper.EXTENSION_GPX);
+ Log.d(TAG, "Temporary file: "+ tmpFile.getAbsolutePath());
+ } catch (Exception ioe) {
Log.e(TAG, "Could not create temporary file", ioe);
throw new IllegalStateException("Could not create temporary file", ioe);
}
diff --git a/app/src/main/java/net/osmtracker/gpx/ExportTrackTask.java b/app/src/main/java/net/osmtracker/gpx/ExportTrackTask.java
index 5e419a42f..a9dec64da 100644
--- a/app/src/main/java/net/osmtracker/gpx/ExportTrackTask.java
+++ b/app/src/main/java/net/osmtracker/gpx/ExportTrackTask.java
@@ -1,6 +1,5 @@
package net.osmtracker.gpx;
-import android.Manifest;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.ContentResolver;
@@ -8,13 +7,10 @@
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
-import android.content.pm.PackageManager;
import android.database.Cursor;
import android.media.MediaScannerConnection;
import android.os.AsyncTask;
-import android.os.Environment;
import android.preference.PreferenceManager;
-import androidx.core.content.ContextCompat;
import android.util.Log;
import android.widget.Toast;
@@ -569,6 +565,12 @@ public String buildGPXFilename(Cursor cursor, File parentDirectory) {
public String formatGpxFilename(String desiredOutputFormat, String sanitizedTrackName, String formattedTrackStartDate){
String result = "";
+ String exportLabelName = PreferenceManager.getDefaultSharedPreferences(context).getString(
+ OSMTracker.Preferences.KEY_OUTPUT_FILENAME_LABEL, OSMTracker.Preferences.VAL_OUTPUT_FILENAME_LABEL);
+ // If is required to avoid Unit Test fail
+ if(exportLabelName == null){
+ exportLabelName = OSMTracker.Preferences.VAL_OUTPUT_FILENAME_LABEL;
+ }
boolean thereIsTrackName = sanitizedTrackName != null && sanitizedTrackName.length() >= 1;
switch(desiredOutputFormat){
@@ -576,18 +578,36 @@ public String formatGpxFilename(String desiredOutputFormat, String sanitizedTrac
if(thereIsTrackName)
result += sanitizedTrackName;
else
- result += formattedTrackStartDate; // fallback case
+ result += formattedTrackStartDate;
break;
case OSMTracker.Preferences.VAL_OUTPUT_FILENAME_NAME_DATE:
if(thereIsTrackName)
- result += sanitizedTrackName + "_" + formattedTrackStartDate;
+ if(sanitizedTrackName.equals(formattedTrackStartDate)) {
+ result += sanitizedTrackName;
+ }else{
+ result += sanitizedTrackName + "_" + formattedTrackStartDate; // name is not equal
+ }
else
result += formattedTrackStartDate;
break;
+ case OSMTracker.Preferences.VAL_OUTPUT_FILENAME_DATE_NAME:
+ if(thereIsTrackName){
+ if(sanitizedTrackName.equals(formattedTrackStartDate)){
+ result += formattedTrackStartDate;
+ }else{
+ result += formattedTrackStartDate + "_" + sanitizedTrackName;
+ }
+ }else{
+ result += formattedTrackStartDate;
+ }
+ break;
case OSMTracker.Preferences.VAL_OUTPUT_FILENAME_DATE:
result += formattedTrackStartDate;
break;
}
+ if(!(exportLabelName.equals(""))) {
+ result += "_" + exportLabelName;
+ }
return result;
}
diff --git a/app/src/main/java/net/osmtracker/gpx/ZipHelper.java b/app/src/main/java/net/osmtracker/gpx/ZipHelper.java
new file mode 100644
index 000000000..20b1eb8ad
--- /dev/null
+++ b/app/src/main/java/net/osmtracker/gpx/ZipHelper.java
@@ -0,0 +1,94 @@
+package net.osmtracker.gpx;
+
+import android.content.Context;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Objects;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import net.osmtracker.db.DataHelper;
+
+public class ZipHelper {
+
+ private static final String TAG = "ZipHelper";
+
+ /**
+ * Compresses all files associated with a track into a ZIP file.
+ *
+ * @param context Application context.
+ * @param trackId Track ID.
+ * @param fileGPX GPX file.
+ * @return The created ZIP file or null if an error occurred.
+ */
+ public static File zipCacheFiles(Context context, long trackId, File fileGPX) {
+
+ String name = fileGPX.getName();
+ File zipFile = new File(context.getCacheDir(),
+ name.substring(0, name.lastIndexOf(".")) + DataHelper.EXTENSION_ZIP);
+
+ File traceFilesDirectory = DataHelper.getTrackDirectory(trackId, context);
+
+ try (FileOutputStream fos = new FileOutputStream(zipFile);
+ ZipOutputStream zos = new ZipOutputStream(fos)) {
+
+ // Add gpx file
+ addFileToZip(fileGPX, zos);
+
+ if(!traceFilesDirectory.exists()){
+ return zipFile;
+ }
+
+ for (File multimediaFile : Objects.requireNonNull(traceFilesDirectory.listFiles())) {
+ if (!multimediaFile.isDirectory()) { // Avoid adding empty folders
+ // only add files that are not .zip files
+ if (!multimediaFile.getName().endsWith(DataHelper.EXTENSION_ZIP)) {
+ addFileToZip(multimediaFile, zos);
+ } else {
+ Log.d(TAG, "Multimedia file: " + multimediaFile.getAbsolutePath() + " ignored. ");
+ }
+ } else {
+ Log.d(TAG, "Folder " + multimediaFile.getAbsolutePath() + " ignored. ");
+ }
+ }
+
+ Log.d(TAG, "ZIP file created: " + zipFile.getAbsolutePath());
+ return zipFile;
+
+ } catch (IOException e) {
+ Log.e(TAG, "Error creating ZIP file", e);
+ return null;
+ }
+ }
+
+
+ /**
+ * Adds a file to the ZIP archive.
+ *
+ * @param file The file to add.
+ * @param zos The ZipOutputStream to which the file will be added.
+ */
+ private static void addFileToZip(File file, ZipOutputStream zos) throws IOException {
+ if (!file.exists()) {
+ Log.e(TAG, "File does not exist: " + file.getAbsolutePath());
+ return;
+ }
+
+ try (FileInputStream fis = new FileInputStream(file)) {
+ ZipEntry zipEntry = new ZipEntry(file.getName());
+ zos.putNextEntry(zipEntry);
+
+ byte[] buffer = new byte[1024];
+ int length;
+ while ((length = fis.read(buffer)) > 0) {
+ zos.write(buffer, 0, length);
+ }
+
+ zos.closeEntry();
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/net/osmtracker/layout/GpsStatusRecord.java b/app/src/main/java/net/osmtracker/layout/GpsStatusRecord.java
index a7b3a4caa..f2b264d9d 100644
--- a/app/src/main/java/net/osmtracker/layout/GpsStatusRecord.java
+++ b/app/src/main/java/net/osmtracker/layout/GpsStatusRecord.java
@@ -8,7 +8,9 @@
import android.Manifest;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.PackageManager;
+import android.location.GnssStatus;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
@@ -114,15 +116,42 @@ public void requestLocationUpdates(boolean request) {
if (request) {
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
lmgr.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
+ lmgr.registerGnssStatusCallback(mStatusCallback);
} else {
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_CODE_GPS_PERMISSIONS);
}
} else {
lmgr.removeUpdates(this);
+ lmgr.unregisterGnssStatusCallback(mStatusCallback);
}
}
+ private GnssStatus.Callback mStatusCallback = new GnssStatus.Callback() {
+ @Override
+ public void onSatelliteStatusChanged(GnssStatus status) {
+ satCount = status.getSatelliteCount();
+ fixCount = 0;
+
+ for (int i = 0; i < satCount; i++) {
+ if (status.usedInFix(i)) {
+ fixCount++;
+ }
+ }
+
+ if (fixCount == 0) {
+ TextView tvAccuracy = findViewById(R.id.gpsstatus_record_tvAccuracy);
+ tvAccuracy.setText(getResources().getString(R.string.various_waiting_gps_fix)
+ .replace("{0}", Long.toString(fixCount))
+ .replace("{1}", Long.toString(satCount)));
+
+ ((ImageView) findViewById(R.id.gpsstatus_record_imgSatIndicator)).setImageResource(R.drawable.sat_indicator_unknown);
+ }
+
+ Log.v(TAG, "Found " + satCount + " satellites. " + fixCount + " used in fix.");
+ }
+ };
+
@Override
public void onLocationChanged(Location location) {
// first of all we check if the time from the last used fix to the current fix is greater than the logging interval
@@ -135,9 +164,10 @@ public void onLocationChanged(Location location) {
activity.onGpsEnabled();
manageRecordingIndicator(true);
}
-
- //TODO: get number of satellites used and visible.
- //Log.v(TAG, "Found " + satCount + " satellites. " + fixCount + " used in fix.
+ else if (gpsActive && !activity.getButtonsEnabled()) {
+ activity.onGpsEnabled();
+ manageRecordingIndicator(true);
+ }
TextView tvAccuracy = findViewById(R.id.gpsstatus_record_tvAccuracy);
if (location.hasAccuracy()) {
@@ -145,11 +175,8 @@ public void onLocationChanged(Location location) {
tvAccuracy.setText(getResources().getString(R.string.various_accuracy_with_sats)
.replace("{0}", ACCURACY_FORMAT.format(location.getAccuracy()))
.replace("{1}", getResources().getString(R.string.various_unit_meters))
- //TODO: use the number of satellites used and visible
- //.replace("{2}", Long.toString(fixCount))
- //.replace("{3}", Long.toString(satCount)));
- .replace("{2}", "?")
- .replace("{3}", "?"));
+ .replace("{2}", Long.toString(fixCount))
+ .replace("{3}", Long.toString(satCount)));
manageSatelliteStatusIndicator((int) location.getAccuracy());
diff --git a/app/src/main/java/net/osmtracker/listener/EditWaypointDialogOnClickListener.java b/app/src/main/java/net/osmtracker/listener/EditWaypointDialogOnClickListener.java
new file mode 100644
index 000000000..181a11ba2
--- /dev/null
+++ b/app/src/main/java/net/osmtracker/listener/EditWaypointDialogOnClickListener.java
@@ -0,0 +1,24 @@
+package net.osmtracker.listener;
+
+import android.app.AlertDialog;
+import android.database.Cursor;
+import android.view.View;
+
+/**
+ * Class that implements an OnClickListener to display an edit waypoint dialog.
+ */
+public class EditWaypointDialogOnClickListener implements View.OnClickListener {
+
+ private Cursor cursor;
+
+ protected AlertDialog alert;
+
+ protected EditWaypointDialogOnClickListener(AlertDialog alert, Cursor cu) {
+ this.cursor = cu; // Assigns the received cursor to the class attribute
+ this.alert = alert; // Assigns the received alert to the class attribute
+ }
+
+ @Override
+ public void onClick(View view) {
+ }
+}
diff --git a/app/src/main/java/net/osmtracker/listener/PressureListener.java b/app/src/main/java/net/osmtracker/listener/PressureListener.java
index 4f74b9d8c..c6a1ab7ed 100644
--- a/app/src/main/java/net/osmtracker/listener/PressureListener.java
+++ b/app/src/main/java/net/osmtracker/listener/PressureListener.java
@@ -9,9 +9,7 @@
/**
* Listener for pressure sensor.
- *
* Register the listener with your context using the register/unregister functions
- *
* Should be possible to enable/disable via preferences due to power consumption.
*
*/
diff --git a/app/src/main/java/net/osmtracker/listener/SensorListener.java b/app/src/main/java/net/osmtracker/listener/SensorListener.java
index 467c6d103..3eb287b68 100644
--- a/app/src/main/java/net/osmtracker/listener/SensorListener.java
+++ b/app/src/main/java/net/osmtracker/listener/SensorListener.java
@@ -17,9 +17,7 @@
/**
* Listener for sensors. In particular for the acceleration and magnetic sensors to provide compass
* heading.
- *
* Register the listener with your context using the register/unregister functions
- *
* most recent reading from the sensor is always available from azimuth, pitch, roll, accuracy and valid fields
*
* @author Christoph Gohle
diff --git a/app/src/main/java/net/osmtracker/listener/StillImageOnClickListener.java b/app/src/main/java/net/osmtracker/listener/StillImageOnClickListener.java
index c53cbd936..bfd963b5c 100644
--- a/app/src/main/java/net/osmtracker/listener/StillImageOnClickListener.java
+++ b/app/src/main/java/net/osmtracker/listener/StillImageOnClickListener.java
@@ -2,11 +2,6 @@
import net.osmtracker.activity.TrackLogger;
-import android.Manifest;
-import android.content.pm.PackageManager;
-import androidx.core.app.ActivityCompat;
-import androidx.core.content.ContextCompat;
-
import android.view.View;
import android.view.View.OnClickListener;
@@ -22,8 +17,6 @@ public class StillImageOnClickListener implements OnClickListener {
* Parent activity
*/
TrackLogger activity;
-
- final private int RC_STORAGE_CAMERA_PERMISSIONS = 2;
public StillImageOnClickListener(TrackLogger parent) {
activity = parent;
@@ -31,59 +24,6 @@ public StillImageOnClickListener(TrackLogger parent) {
@Override
public void onClick(View v) {
- if (ContextCompat.checkSelfPermission(activity,
- Manifest.permission.WRITE_EXTERNAL_STORAGE) + ContextCompat.checkSelfPermission(activity,
- Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
-
- // Should we show an explanation?
- if ( (ActivityCompat.shouldShowRequestPermissionRationale(activity,
- Manifest.permission.WRITE_EXTERNAL_STORAGE))
- || (ActivityCompat.shouldShowRequestPermissionRationale(activity,
- Manifest.permission.CAMERA)) ) {
-
- // Show an expanation to the user *asynchronously* -- don't block
- // this thread waiting for the user's response! After the user
- // sees the explanation, try again to request the permission.
- // TODO: explain why we need permission.
- //"we should explain why we need write and record audio permission"
-
- } else {
-
- // No explanation needed, we can request the permission.
- ActivityCompat.requestPermissions(activity,
- new String[]{
- Manifest.permission.WRITE_EXTERNAL_STORAGE,
- Manifest.permission.CAMERA},
- RC_STORAGE_CAMERA_PERMISSIONS);
- }
-
- } else {
- activity.requestStillImage();
- }
-
+ activity.requestStillImage();
}
-
- public void onRequestPermissionsResult(int requestCode,
- String permissions[], int[] grantResults) {
- switch (requestCode) {
- case RC_STORAGE_CAMERA_PERMISSIONS: {
- // If request is cancelled, the result arrays are empty.
- if (grantResults.length == 2
- && grantResults[0] == PackageManager.PERMISSION_GRANTED
- && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
-
- // permission was granted, yay!
- activity.requestStillImage();
-
- } else {
-
- // permission denied, boo! Disable the
- // functionality that depends on this permission.
- //TODO: add an informative message.
- }
- return;
- }
- }
- }
-
}
diff --git a/app/src/main/java/net/osmtracker/listener/TagButtonOnClickListener.java b/app/src/main/java/net/osmtracker/listener/TagButtonOnClickListener.java
index b4a8da875..4b5f35b4c 100644
--- a/app/src/main/java/net/osmtracker/listener/TagButtonOnClickListener.java
+++ b/app/src/main/java/net/osmtracker/listener/TagButtonOnClickListener.java
@@ -1,16 +1,18 @@
package net.osmtracker.listener;
-import net.osmtracker.OSMTracker;
-import net.osmtracker.R;
-
import android.content.Intent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
+import java.util.UUID;
+
+import net.osmtracker.OSMTracker;
+import net.osmtracker.R;
import net.osmtracker.db.TrackContentProvider;
+
/**
* Listener for standard waypoint tag button.
* Sends an Intent to track waypoint. Waypoint name is the
@@ -36,6 +38,11 @@ public void onClick(View view) {
Intent intent = new Intent(OSMTracker.INTENT_TRACK_WP);
intent.putExtra(TrackContentProvider.Schema.COL_TRACK_ID, currentTrackId);
intent.putExtra(OSMTracker.INTENT_KEY_NAME, label);
+ intent.putExtra(OSMTracker.INTENT_KEY_UUID, UUID.randomUUID().toString());
+
+ String packageName = view.getContext().getPackageName();
+ intent.setPackage(packageName);
+
view.getContext().sendBroadcast(intent);
// Inform user that the waypoint was tracked
diff --git a/app/src/main/java/net/osmtracker/service/gps/GPSLogger.java b/app/src/main/java/net/osmtracker/service/gps/GPSLogger.java
index d02ecdb24..fb785d279 100644
--- a/app/src/main/java/net/osmtracker/service/gps/GPSLogger.java
+++ b/app/src/main/java/net/osmtracker/service/gps/GPSLogger.java
@@ -1,13 +1,5 @@
package net.osmtracker.service.gps;
-import net.osmtracker.OSMTracker;
-import net.osmtracker.R;
-import net.osmtracker.activity.TrackLogger;
-import net.osmtracker.db.DataHelper;
-import net.osmtracker.db.TrackContentProvider;
-import net.osmtracker.listener.PressureListener;
-import net.osmtracker.listener.SensorListener;
-
import android.Manifest;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -27,9 +19,18 @@
import android.os.Bundle;
import android.os.IBinder;
import android.preference.PreferenceManager;
+import android.util.Log;
+
import androidx.core.app.NotificationCompat;
import androidx.core.content.ContextCompat;
-import android.util.Log;
+
+import net.osmtracker.OSMTracker;
+import net.osmtracker.R;
+import net.osmtracker.activity.TrackLogger;
+import net.osmtracker.db.DataHelper;
+import net.osmtracker.db.TrackContentProvider;
+import net.osmtracker.listener.PressureListener;
+import net.osmtracker.listener.SensorListener;
/**
* GPS logging service.
@@ -147,8 +148,15 @@ public void onReceive(Context context, Intent intent) {
// Delete an existing waypoint
Bundle extras = intent.getExtras();
if (extras != null) {
+ Long trackId = extras.getLong(TrackContentProvider.Schema.COL_TRACK_ID);
String uuid = extras.getString(OSMTracker.INTENT_KEY_UUID);
- dataHelper.deleteWayPoint(uuid);
+ String link = extras.getString(OSMTracker.INTENT_KEY_LINK);
+ String filePath = null;
+ try {
+ filePath = link.equals("null") ? null : DataHelper.getTrackDirectory(trackId, context) + "/" + link;
+ }
+ catch(NullPointerException ne){}
+ dataHelper.deleteWayPoint(uuid, filePath);
}
} else if (OSMTracker.INTENT_START_TRACKING.equals(intent.getAction())) {
Bundle extras = intent.getExtras();
@@ -222,7 +230,11 @@ public void onCreate() {
filter.addAction(OSMTracker.INTENT_DELETE_WP);
filter.addAction(OSMTracker.INTENT_START_TRACKING);
filter.addAction(OSMTracker.INTENT_STOP_TRACKING);
- registerReceiver(receiver, filter);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ registerReceiver(receiver, filter, Context.RECEIVER_NOT_EXPORTED);
+ } else {
+ registerReceiver(receiver, filter);
+ }
// Register ourselves for location updates
lmgr = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
diff --git a/app/src/main/java/net/osmtracker/service/gps/GPSLoggerServiceConnection.java b/app/src/main/java/net/osmtracker/service/gps/GPSLoggerServiceConnection.java
index fcf26a7c6..e7d22f67b 100644
--- a/app/src/main/java/net/osmtracker/service/gps/GPSLoggerServiceConnection.java
+++ b/app/src/main/java/net/osmtracker/service/gps/GPSLoggerServiceConnection.java
@@ -50,6 +50,7 @@ public void onServiceConnected(ComponentName name, IBinder service) {
activity.setEnabledActionButtons(false);
Intent intent = new Intent(OSMTracker.INTENT_START_TRACKING);
intent.putExtra(TrackContentProvider.Schema.COL_TRACK_ID, activity.getCurrentTrackId());
+ intent.setPackage(activity.getPackageName());
activity.sendBroadcast(intent);
}
}
diff --git a/app/src/main/java/net/osmtracker/util/CustomLayoutsUtils.java b/app/src/main/java/net/osmtracker/util/CustomLayoutsUtils.java
index ca12f4e8f..b07f7884f 100644
--- a/app/src/main/java/net/osmtracker/util/CustomLayoutsUtils.java
+++ b/app/src/main/java/net/osmtracker/util/CustomLayoutsUtils.java
@@ -16,6 +16,8 @@
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
+import java.nio.charset.StandardCharsets;
+
/**
* Created by adma9717 on 12/8/17.
*/
@@ -65,16 +67,18 @@ public static String createFileName(String layoutName, String iso) {
/**
* FIXME: Create a util class with this method. This method is a copy&paste of the one in {@link GetStringResponseTask}
- * @param stream
- * @return all the characters in the stream as a single String
- * @throws IOException
+ * Converts an InputStream to a String using the UTF-8 charset.
+ *
+ * @param stream the InputStream to read from
+ * @return a String containing all characters read from the stream
+ * @throws IOException if an I/O error occurs
*/
public static String getStringFromStream(InputStream stream) throws IOException {
if (stream != null) {
Writer writer = new StringWriter();
char[] buffer = new char[2048];
try {
- Reader reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
+ Reader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8));
int counter;
while ((counter = reader.read(buffer)) != -1) {
writer.write(buffer, 0, counter);
diff --git a/app/src/main/java/net/osmtracker/util/FileSystemUtils.java b/app/src/main/java/net/osmtracker/util/FileSystemUtils.java
index 49d30ad40..04e3e0fa1 100644
--- a/app/src/main/java/net/osmtracker/util/FileSystemUtils.java
+++ b/app/src/main/java/net/osmtracker/util/FileSystemUtils.java
@@ -1,5 +1,7 @@
package net.osmtracker.util;
+import android.util.Log;
+
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -8,8 +10,6 @@
import java.util.ArrayList;
import java.util.List;
-import android.util.Log;
-
public final class FileSystemUtils {
private static final String TAG = FileSystemUtils.class.getSimpleName();
@@ -148,7 +148,7 @@ public static boolean delete(File fileToDelete, boolean recursive) {
* @param fileToDelete the file/directory to be deleted
* @param recursive if the deletion should be recursive
* @param recursionDepth takes care of the depth of recursion and aborts deletion if DELETE_MAX_RECURSION_DEPTH is reached
- * @return
+ * @return true if file was deleted
*/
private static boolean delete(File fileToDelete, boolean recursive, int recursionDepth){
// if we're deeper than one recursion/subfolder, we'll cancel deletion
diff --git a/app/src/main/java/net/osmtracker/util/MercatorProjection.java b/app/src/main/java/net/osmtracker/util/MercatorProjection.java
index c3ba0ea36..3da1193b1 100644
--- a/app/src/main/java/net/osmtracker/util/MercatorProjection.java
+++ b/app/src/main/java/net/osmtracker/util/MercatorProjection.java
@@ -84,7 +84,7 @@ public MercatorProjection(double minLat, double minLon, double maxLat, double ma
* @param latitude
* Latitude to project
* @return An array of 2 int projected coordinates (use
- * {@link MercatorProjection.X} and {@link MercatorProjection.Y} for
+ * {@link MercatorProjection}.X and {@link MercatorProjection}.Y for
* access.
*/
public int[] project(double longitude, double latitude) {
diff --git a/app/src/main/java/net/osmtracker/util/UserDefinedLayoutReader.java b/app/src/main/java/net/osmtracker/util/UserDefinedLayoutReader.java
index f42ec9dbb..74262bad2 100644
--- a/app/src/main/java/net/osmtracker/util/UserDefinedLayoutReader.java
+++ b/app/src/main/java/net/osmtracker/util/UserDefinedLayoutReader.java
@@ -87,7 +87,7 @@ public class UserDefinedLayoutReader {
/**
* representing ScreenOrientation
- * see {@link Configuration.orientation}
+ * see {@link Configuration}.orientation
*/
private int orientation;
diff --git a/app/src/main/java/net/osmtracker/view/TextNoteDialog.java b/app/src/main/java/net/osmtracker/view/TextNoteDialog.java
index bf38bd573..6064dea12 100644
--- a/app/src/main/java/net/osmtracker/view/TextNoteDialog.java
+++ b/app/src/main/java/net/osmtracker/view/TextNoteDialog.java
@@ -1,10 +1,5 @@
package net.osmtracker.view;
-import java.util.UUID;
-
-import net.osmtracker.OSMTracker;
-import net.osmtracker.R;
-
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
@@ -13,8 +8,12 @@
import android.view.WindowManager.LayoutParams;
import android.widget.EditText;
+import net.osmtracker.OSMTracker;
+import net.osmtracker.R;
import net.osmtracker.db.TrackContentProvider;
+import java.util.UUID;
+
public class TextNoteDialog extends AlertDialog {
/**
@@ -63,7 +62,7 @@ public TextNoteDialog(Context context, long trackId) {
this.setCancelable(true);
this.setView(input);
- this.setButton(context.getResources().getString(android.R.string.ok), new DialogInterface.OnClickListener() {
+ this.setButton(context.getResources().getString(android.R.string.ok), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// Track waypoint with user input text
@@ -71,11 +70,12 @@ public void onClick(DialogInterface dialog, int which) {
intent.putExtra(TrackContentProvider.Schema.COL_TRACK_ID, TextNoteDialog.this.wayPointTrackId);
intent.putExtra(OSMTracker.INTENT_KEY_NAME, input.getText().toString());
intent.putExtra(OSMTracker.INTENT_KEY_UUID, TextNoteDialog.this.wayPointUuid);
- TextNoteDialog.this.context.sendBroadcast(intent);
+ intent.setPackage(getContext().getPackageName());
+ context.sendBroadcast(intent);
}
});
- this.setButton2(context.getResources().getString(android.R.string.cancel), new DialogInterface.OnClickListener() {
+ this.setButton2(context.getResources().getString(android.R.string.cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// cancel the dialog
@@ -89,7 +89,8 @@ public void onCancel(DialogInterface dialog) {
// delete the waypoint because user canceled this dialog
Intent intent = new Intent(OSMTracker.INTENT_DELETE_WP);
intent.putExtra(OSMTracker.INTENT_KEY_UUID, TextNoteDialog.this.wayPointUuid);
- TextNoteDialog.this.context.sendBroadcast(intent);
+ intent.setPackage(getContext().getPackageName());
+ context.sendBroadcast(intent);
}
});
@@ -100,7 +101,7 @@ public void onCancel(DialogInterface dialog) {
*/
@Override
protected void onStart() {
- if(wayPointUuid == null){
+ if (wayPointUuid == null) {
// there is no UUID set for the waypoint we're working on
// so we need to generate a UUID and track this point
wayPointUuid = UUID.randomUUID().toString();
@@ -108,6 +109,7 @@ protected void onStart() {
intent.putExtra(TrackContentProvider.Schema.COL_TRACK_ID, wayPointTrackId);
intent.putExtra(OSMTracker.INTENT_KEY_UUID, wayPointUuid);
intent.putExtra(OSMTracker.INTENT_KEY_NAME, context.getResources().getString(R.string.gpsstatus_record_textnote));
+ intent.setPackage(getContext().getPackageName());
context.sendBroadcast(intent);
}
@@ -120,18 +122,18 @@ protected void onStart() {
* resets values of this dialog
* such as the input fields text and the waypoints uuid
*/
- public void resetValues(){
+ public void resetValues() {
wayPointUuid = null;
input.setText("");
}
/**
- * restoring values from the savedInstaceState
+ * restoring values from the savedInstanceState
*/
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
String text = savedInstanceState.getString(KEY_INPUT_TEXT);
- if(text != null){
+ if (text != null) {
input.setText(text);
}
wayPointUuid = savedInstanceState.getString(KEY_WAYPOINT_UUID);
@@ -150,8 +152,4 @@ public Bundle onSaveInstanceState() {
extras.putString(KEY_WAYPOINT_UUID, wayPointUuid);
return extras;
}
-
-
-
-
}
diff --git a/app/src/main/java/net/osmtracker/view/VoiceRecDialog.java b/app/src/main/java/net/osmtracker/view/VoiceRecDialog.java
index 085e6551e..97018ffc0 100644
--- a/app/src/main/java/net/osmtracker/view/VoiceRecDialog.java
+++ b/app/src/main/java/net/osmtracker/view/VoiceRecDialog.java
@@ -1,14 +1,5 @@
package net.osmtracker.view;
-import java.io.File;
-import java.util.Date;
-import java.util.UUID;
-
-import net.osmtracker.OSMTracker;
-import net.osmtracker.R;
-import net.osmtracker.db.DataHelper;
-import net.osmtracker.db.TrackContentProvider.Schema;
-
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
@@ -24,6 +15,15 @@
import android.view.KeyEvent;
import android.widget.Toast;
+import net.osmtracker.OSMTracker;
+import net.osmtracker.R;
+import net.osmtracker.db.DataHelper;
+import net.osmtracker.db.TrackContentProvider.Schema;
+
+import java.io.File;
+import java.util.Date;
+import java.util.UUID;
+
public class VoiceRecDialog extends ProgressDialog implements OnInfoListener{
private final static String TAG = VoiceRecDialog.class.getSimpleName();
@@ -151,6 +151,7 @@ public void onStart() {
intent.putExtra(Schema.COL_TRACK_ID, wayPointTrackId);
intent.putExtra(OSMTracker.INTENT_KEY_UUID, wayPointUuid);
intent.putExtra(OSMTracker.INTENT_KEY_NAME, context.getResources().getString(R.string.wpt_voicerec));
+ intent.setPackage(getContext().getPackageName());
context.sendBroadcast(intent);
}
@@ -215,6 +216,7 @@ public void onStart() {
intent.putExtra(Schema.COL_TRACK_ID, wayPointTrackId);
intent.putExtra(OSMTracker.INTENT_KEY_UUID, wayPointUuid);
intent.putExtra(OSMTracker.INTENT_KEY_LINK, audioFile.getName());
+ intent.setPackage(getContext().getPackageName());
context.sendBroadcast(intent);
} else {
Log.w(TAG,"onStart() no suitable audioFile could be created");
diff --git a/app/src/main/res/drawable-mdpi/gps_center.png b/app/src/main/res/drawable-mdpi/gps_center.png
new file mode 100644
index 000000000..38f81f17e
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/gps_center.png differ
diff --git a/app/src/main/res/drawable-mdpi/zoom_in.png b/app/src/main/res/drawable-mdpi/zoom_in.png
index 3e63eae2a..8aaa8fa98 100644
Binary files a/app/src/main/res/drawable-mdpi/zoom_in.png and b/app/src/main/res/drawable-mdpi/zoom_in.png differ
diff --git a/app/src/main/res/drawable-mdpi/zoom_out.png b/app/src/main/res/drawable-mdpi/zoom_out.png
index f3079fd64..2a56507cf 100644
Binary files a/app/src/main/res/drawable-mdpi/zoom_out.png and b/app/src/main/res/drawable-mdpi/zoom_out.png differ
diff --git a/app/src/main/res/drawable/divider.xml b/app/src/main/res/drawable/divider.xml
new file mode 100644
index 000000000..b19ee985b
--- /dev/null
+++ b/app/src/main/res/drawable/divider.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/map_btn_style.xml b/app/src/main/res/drawable/map_btn_style.xml
new file mode 100644
index 000000000..a8c33d11f
--- /dev/null
+++ b/app/src/main/res/drawable/map_btn_style.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout-iw/waypointlist_item.xml b/app/src/main/res/layout-iw/waypointlist_item.xml
index 953fc6650..96fa5dc97 100644
--- a/app/src/main/res/layout-iw/waypointlist_item.xml
+++ b/app/src/main/res/layout-iw/waypointlist_item.xml
@@ -1,10 +1,10 @@
-
+
-
-
diff --git a/app/src/main/res/layout/about.xml b/app/src/main/res/layout/about.xml
index 9234145c3..c6288176a 100644
--- a/app/src/main/res/layout/about.xml
+++ b/app/src/main/res/layout/about.xml
@@ -1,125 +1,112 @@
+ android:id="@+id/about_root"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="10dp"
+ android:fitsSystemWindows="true">
-
-
-
-
-
+ android:layout_alignParentTop="true">
-
-
-
-
-
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
+ android:text="@string/about_text" />
-
-
-
-
+ android:text="@string/about_link" />
-
+ android:text="@string/about_translate_text" />
-
-
+ android:text="@string/about_translate_link" />
+
-
+
-
+
-
-
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/available_layouts.xml b/app/src/main/res/layout/available_layouts.xml
index 3a6d16c14..3ba6dee0b 100644
--- a/app/src/main/res/layout/available_layouts.xml
+++ b/app/src/main/res/layout/available_layouts.xml
@@ -1,9 +1,13 @@
-
-
+ android:fillViewport="true"
+ android:fitsSystemWindows="true">
-
-
\ No newline at end of file
+
+
diff --git a/app/src/main/res/layout/buttons_presets.xml b/app/src/main/res/layout/buttons_presets.xml
index 2d26d04de..67d9a2a18 100644
--- a/app/src/main/res/layout/buttons_presets.xml
+++ b/app/src/main/res/layout/buttons_presets.xml
@@ -5,7 +5,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/buttons_presets"
- android:paddingTop="4dp">
+ android:paddingTop="4dp"
+ android:fitsSystemWindows="true">
+ android:layout_height="wrap_content"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:fitsSystemWindows="true">
-
+
-
-
+
-
-
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/edit_waypoint_dialog.xml b/app/src/main/res/layout/edit_waypoint_dialog.xml
new file mode 100644
index 000000000..dfffa9478
--- /dev/null
+++ b/app/src/main/res/layout/edit_waypoint_dialog.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/github_repository_settings.xml b/app/src/main/res/layout/github_repository_settings.xml
index dbe90430f..db137427f 100644
--- a/app/src/main/res/layout/github_repository_settings.xml
+++ b/app/src/main/res/layout/github_repository_settings.xml
@@ -1,7 +1,7 @@
+ android:layout_height="match_parent" android:padding="7dp" android:fitsSystemWindows="true">
+ android:layout_height="fill_parent"
+ android:fitsSystemWindows="true">
+ android:orientation="vertical"
+ android:fitsSystemWindows="true">
+ android:layout_height="fill_parent"
+ android:fitsSystemWindows="true">
diff --git a/app/src/main/res/layout/tracklist_item.xml b/app/src/main/res/layout/tracklist_item.xml
index 4308c5aeb..7818fe44c 100644
--- a/app/src/main/res/layout/tracklist_item.xml
+++ b/app/src/main/res/layout/tracklist_item.xml
@@ -1,119 +1,150 @@
-
+ android:minHeight="72dp"
+ android:padding="5dp"
+ android:stateListAnimator="@null"
+ android:background="?android:attr/selectableItemBackground">
-
-
-
-
-
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent">
+ android:text="#"
+ android:padding="2dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:duplicateParentState="false"/>
+ android:text="{id}"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:duplicateParentState="false"/>
+
-
+
+
+
+ android:text="@string/trackmgr_waypoints_count"
+ android:textStyle="bold"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:duplicateParentState="false"/>
+ android:layout_marginStart="4dp"
+ android:text="{x}"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:duplicateParentState="false"/>
+
-
+
-
+ android:layout_height="wrap_content"
+ android:layout_marginStart="4dp"
+ android:text="{y}"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:duplicateParentState="false"
+ app:layout_constraintStart_toEndOf="@id/trackmgr_item_trackpoints"
+ app:layout_constraintTop_toTopOf="@id/trackmgr_item_trackpoints"
+ app:layout_constraintBottom_toBottomOf="@id/trackmgr_item_trackpoints"/>
-
+ android:layout_height="0dp"
+ android:minWidth="32dp"
+ android:paddingHorizontal="4dp"
+ android:stateListAnimator="@null"
+ android:duplicateParentState="false"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent">
+
-
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/tracklogger.xml b/app/src/main/res/layout/tracklogger.xml
index 2120a54c7..b1eafef04 100644
--- a/app/src/main/res/layout/tracklogger.xml
+++ b/app/src/main/res/layout/tracklogger.xml
@@ -1,6 +1,6 @@
+ android:orientation="vertical" android:layout_height="fill_parent" android:fitsSystemWindows="true">
diff --git a/app/src/main/res/layout/trackmanager.xml b/app/src/main/res/layout/trackmanager.xml
index fdf486428..4bbf0fb7e 100644
--- a/app/src/main/res/layout/trackmanager.xml
+++ b/app/src/main/res/layout/trackmanager.xml
@@ -3,7 +3,8 @@
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_height="fill_parent"
- android:layout_width="fill_parent">
+ android:layout_width="fill_parent"
+ android:fitsSystemWindows="true">
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-ar/strings-preferences.xml b/app/src/main/res/values-ar/strings-preferences.xml
index 27c027fc3..3815e2e3e 100644
--- a/app/src/main/res/values-ar/strings-preferences.xml
+++ b/app/src/main/res/values-ar/strings-preferences.xml
@@ -56,17 +56,11 @@
اعرض خريطة الشارع المفتوحة أسفل المسار. لابد من وجود اتصال بالانترنتعرض خلفية خريطة الشارع المفتوحة بشكل دائمًا؟ لابد من وجود اتصال بالانترنتإعدادات GPX
- مجلد التخزين الخارجي (SD)فعال للمسار التالي (ليس المسار الحالي)دليل واحد لكل مساراحفظ كل مسار والملفات المرتبطة به في الدليل الخاص بهاسم الملف للمسارات المسماةنمط اسم الملف إذا كان للمسار اسم
-
- اسم المسار
- الاسم, تاريخ البدء والوقت
- تاريخ البدء والوقت
- الدقة في ملف GPXأضف وسجل معلومات الدقة في ملف GPX ، مع اسم الإحداثية أو تعليق الإحداثية
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index dddee6d09..5699c71f2 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -37,8 +37,6 @@
إيقاف تقفِّي المسارمواصلة تقفِّي المسارحذف
- تصدير إلى GPX
- مشاركة GPXإرفع إلى خريطة الشارع المفتوحالعرضتفاصيل
@@ -84,6 +82,7 @@
تسجيل صوتيأخذ صورةملاحظة
+
الإعداداتالإحداثيات
@@ -106,6 +105,7 @@
يرجى التحقق مما إذا كان الذاكرة الخارجية مدخلة بشكل صحيح وتم تركيبها بشكل صحيح.فشل التسجيل الصوتيخطأ أثناء تحليل ملف تخطيط XML. يرجى العودة إلى التخطيط الافتراضي.
+
تتبع مع متتبع OSM لنظام أندرويد™تحذير: قيم HDOP ليست HDOP كما تم إرجاعها بواسطة جهاز GPS. يتم تقريبها من دقة الموقع بالأمتار.
@@ -139,6 +139,7 @@
انتظر الاتجاه...لا يمكن تحديد الاتجاهانتهت عملية التصدير بنجاح
+ {0} {1} / {2} {3}عرض مسار خريطة الشارع المفتوحة
diff --git a/app/src/main/res/values-be/accessibility.xml b/app/src/main/res/values-be/accessibility.xml
deleted file mode 100644
index 6542f214c..000000000
--- a/app/src/main/res/values-be/accessibility.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values-be/strings-preferences.xml b/app/src/main/res/values-be/strings-preferences.xml
deleted file mode 100644
index cc2bd985b..000000000
--- a/app/src/main/res/values-be/strings-preferences.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/values-be/waypoints.xml b/app/src/main/res/values-be/waypoints.xml
deleted file mode 100644
index 42eba3ccd..000000000
--- a/app/src/main/res/values-be/waypoints.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/app/src/main/res/values-bg-rBG/accessibility.xml b/app/src/main/res/values-bg-rBG/accessibility.xml
deleted file mode 100644
index b81e0d2e1..000000000
--- a/app/src/main/res/values-bg-rBG/accessibility.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values-bg-rBG/waypoints.xml b/app/src/main/res/values-bg-rBG/waypoints.xml
deleted file mode 100644
index c757504ac..000000000
--- a/app/src/main/res/values-bg-rBG/waypoints.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/app/src/main/res/values-ca/strings-preferences.xml b/app/src/main/res/values-ca/strings-preferences.xml
index e23248229..405bdaa8e 100644
--- a/app/src/main/res/values-ca/strings-preferences.xml
+++ b/app/src/main/res/values-ca/strings-preferences.xml
@@ -45,7 +45,7 @@
Mapa de fonsMostra el mapa de OpenstreetMap sota el track. És necessari disposar de connexió de dades.Preferències GPX
- Directori d\'emmagatzematge extern (SD)
+ Carpeta d\'emmagatzematge a DocumentsEls canvis tindran efecte en el proper track (no en l\'actual)Una carpeta per trackDesa cada track i els fitxers corresponents a la seva propia carpeta
@@ -54,7 +54,8 @@
Nom del trackNom, Hora d\'inici i de fi
- Hora i data d\'inici i de fi
+ Hora i data d\'inici i de fi, nom
+ Hora i data d\'inici i de fi,Precisió en el fitxer GPXAfegeix informació de precisió en el fitxer GPX afegint waypoints o comentaris
@@ -70,6 +71,7 @@
Restableix l\'autenticació OSMOblida les credencials i permisos OSM i força OSMTracer a preguntar-los una altre vegadaHauràs d\'autoritzar OSMTracker per pujar els track la propera vegada. N\'estàs segur?
+ Proveidor de mapaMapnik
@@ -81,5 +83,6 @@
en el comentarien l\'extensió
+ Exporta la Direcció de la BrúixolaDefineix si desar la informació de la brúixola en el fitxer GPX, i com fer-ho
diff --git a/app/src/main/res/values-cs-rCZ/accessibility.xml b/app/src/main/res/values-cs-rCZ/accessibility.xml
index c68c834eb..278ab3f4d 100644
--- a/app/src/main/res/values-cs-rCZ/accessibility.xml
+++ b/app/src/main/res/values-cs-rCZ/accessibility.xml
@@ -5,6 +5,7 @@
PřiblížitOddálit
+ Střed přiblíženíIndikátor síly satelitního signáluIndikátor průběhu záznamu
diff --git a/app/src/main/res/values-cs-rCZ/strings-preferences.xml b/app/src/main/res/values-cs-rCZ/strings-preferences.xml
index 5431fc72b..60f507564 100644
--- a/app/src/main/res/values-cs-rCZ/strings-preferences.xml
+++ b/app/src/main/res/values-cs-rCZ/strings-preferences.xml
@@ -56,17 +56,20 @@
Zobrazit mapu OpenStreetMap pod trasou. Je třeba datové spojení.Vždy zobrazovat pozadí OpenStreetMap? Vyžaduje datové připojeníNastavení GPX
- Adresář externího úložiště (SD)
+ Složka pro ukládání v dokumentechBude mít vliv pro příští trasu (ne současnou)Pro každou trasu novou složkuUložit každou trasu a její přidružené soubory do její vlastní složkyNázev souboru pro pojmenované trasyŠablona pro název souboru, pokud má trasa jméno
- Název trasy
- Jméno, datum a čas startu
- Datum a čas startu
+ Název stopy
+ Název, datum a čas zahájení
+ Datum a čas zahájení, název
+ Datum a čas zahájení
+ Označení souboru
+ Tento štítek bude připojen na konec názvu souboru.Přesnost v souboru GPXPřidat informaci o přesnosti do souboru GPX, se jménem bodu na trase nebo v komentáři k bodu na trase
diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml
index 138d5e897..e1959ca64 100644
--- a/app/src/main/res/values-cs-rCZ/strings.xml
+++ b/app/src/main/res/values-cs-rCZ/strings.xml
@@ -37,8 +37,8 @@
Zastavit trasováníObnovit trasováníSmazat
- Exportovat jako GPX
- Sdílet GPX
+ Export
+ SdíletNahrát na OpenStreetMapZobrazitPodrobnosti
@@ -62,6 +62,7 @@
(ještě není exportováno)(ještě není nahráno)Zobrazit
+ NázevPopisZnačky (oddělené čárkou)Je třeba vložit popis
@@ -78,12 +79,23 @@
Čekám na odpověď od serveru OpenStreetMapBěhem nahrávání trasy nastala chyba OSM server vrátil chybu: ({0}) zpráva {1}
- Chyba autorizace. Chcete smazat uložené údaje o přihlášení k OpenStreetMap?
+ Chyba autorizace. Chcete vymazat uložené přihlašovací údaje OpenStreetMap?Nahrání na OpenStreetMap bylo úspěšnéZáznam hlasuVyfotitTextová poznámka
+
+ Název bodu trasy/text
+ Zadejte název bodu trasy
+ Otevřít soubor
+ Uložit
+ Smazat
+ Zrušit
+ Smazat bod trasy
+ Smazat tento bod trasy?
+ Smazat
+ ZrušitNastaveníBody trasy
@@ -106,6 +118,14 @@
Zkontrolujte, prosím, jestli je externí úložiště správně vloženo a připojeno.Záznam hlasu selhalDošlo k chybě během zpracování XML souboru pro rozvržení. Přejděte, prosím, k výchozímu rozvržení.
+
+ Vyžadováno povolení
+ Pro export stopy GPX je třeba zapsat do úložiště.
+ Pro správné zobrazení stopy potřebujeme přístup k úložišti.
+ Pro správné sdílení stopy potřebujeme přístup k úložišti.
+ Pro nahrání stopy do OSM potřebujeme přístup k úložišti.
+ Přijmout
+ Nelze pokračovat bez povolení GPSTrasováno pomocí OSMTracker pro Android™Upozornění: hodnoty HDOP nejsou hodnotami HDOP vrácenými zařízením GPS. Hodnoty jsou aproximovány z přesnosti lokace v metrech.
@@ -139,6 +159,8 @@
Hledá se sever...Sever nelze určitProces exportu skončil úspěšně
+ Pozice není dostupná
+ {0} {1} / {2} {3}Zobrazení trasy OpenStreetMap
diff --git a/app/src/main/res/values-da/accessibility.xml b/app/src/main/res/values-da/accessibility.xml
index 31fba5134..b201888fb 100644
--- a/app/src/main/res/values-da/accessibility.xml
+++ b/app/src/main/res/values-da/accessibility.xml
@@ -5,6 +5,7 @@
Zoom indZoom ud
+ CentrumIndikator for satellit-signalstyrkeIndikator for optagestatus
diff --git a/app/src/main/res/values-da/strings-preferences.xml b/app/src/main/res/values-da/strings-preferences.xml
index 2bd217c98..65c88cbe9 100644
--- a/app/src/main/res/values-da/strings-preferences.xml
+++ b/app/src/main/res/values-da/strings-preferences.xml
@@ -56,7 +56,7 @@
Vis OpenStreetMap-kort under track. Kræver dataforbindelseVis altid OpenStreetMap-baggrund? Kræver en dataforbindelseGPX-indstillinger
- Mappe på eksternt lager (SD)
+ Lagringsmappe i dokumenterGældende for næste track (ikke det nuværende)En mappe per trackGem hvert track og tilhørende filer i egen mappe
@@ -65,8 +65,11 @@
Track-navnNavn, startdato og tid
+ Startdato og tid, navnStartdato og tid
+ Filnavnetiket
+ Denne etiket vil blive tilføjet i slutningen af filnavnetPræcision i GPX-filTilføj præcisionsinfo til GPX-fil, med waypoint-navn eller i waypoint-kommentar
diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml
index 457e2d33f..b0c219c1b 100644
--- a/app/src/main/res/values-da/strings.xml
+++ b/app/src/main/res/values-da/strings.xml
@@ -37,8 +37,8 @@
Stop trackingGentoptag trackingSlet
- Eksporter til GPX
- Del GPX
+ Eksporter
+ DelUpload til OpenStreetMapVisDetaljer
@@ -62,6 +62,7 @@
(Ikke eksporteret endnu)(Ikke uploaded endnu)Vis
+ NavnBeskrivelseTags (kommasepareret)Du skal skrive en beskrivelse
@@ -84,6 +85,17 @@
Tale-optagelseTag fotoTekstnote
+
+ Waypoint navn/tekst
+ Indtast waypointnavn
+ Åbn fil
+ Spare
+ Slet
+ Ophæve
+ Slet waypoint
+ Vil du slette dette waypoint?
+ Slet
+ OphæveIndstillingerWaypoints
@@ -106,6 +118,14 @@
Check at eksternt lager er korrekt indsat og mounted.Taleoptagelse fejledeFejl under parsing af XML-layout-fil. Brug i stedet standardlayout.
+
+ Tilladelse påkrævet
+ For at eksportere GPX-sporet skal vi skrive på lagerpladsen.
+ For at vise sporet korrekt skal vi have adgang til lagerpladsen.
+ For at kunne dele sporet ordentligt skal vi have adgang til lagerpladsen.
+ For at uploade sporet til OSM skal vi have adgang til lagerpladsen.
+ Accepter
+ Kan ikke fortsætte uden GPS-tilladelseTracked med OSMTracker til Android™Advarsel: HDOP-værdier er ikke de samme HDOP-værdier som kommer fra GPS. De er approksimeret fra placeringspræcisionen i meter.
@@ -139,6 +159,8 @@
Vent på retning...Retning kan ikke findesEksportproces afsluttet
+ Stillingen er ikke tilgængelig
+ {0} {1} / {2} {3}OpenStreetMap track visning
diff --git a/app/src/main/res/values-de/accessibility.xml b/app/src/main/res/values-de/accessibility.xml
index 0ec331eac..5c69a253f 100644
--- a/app/src/main/res/values-de/accessibility.xml
+++ b/app/src/main/res/values-de/accessibility.xml
@@ -5,6 +5,7 @@
HineinzoomenHinauszoomen
+ CenterSatellitensignalstärkenindikatorAufnahme-Fortschrittsindikator
diff --git a/app/src/main/res/values-de/strings-preferences.xml b/app/src/main/res/values-de/strings-preferences.xml
index d56550a81..9a65f62c3 100644
--- a/app/src/main/res/values-de/strings-preferences.xml
+++ b/app/src/main/res/values-de/strings-preferences.xml
@@ -56,7 +56,7 @@
OpenStreetMap-Karte unter Route anzeigen. Benötigt eine DatenverbindungOpenStreetMap als Track-Hintergrund anzeigen? Benötigt eine DatenverbindungGPX-Einstellungen
- Externes Speicherverzeichnis (SD)
+ Ablageordner in DokumentenGültig ab der nächste Route (nicht für die derzeitige)Ein Verzeichnis pro RouteJede Route und zugehörige Dateien in einem eigenen Verzeichnis speichern
@@ -65,8 +65,11 @@
RoutenameName, Startdatum und -zeit
+ Startdatum und -zeit, nameStartdatum und -zeit
+ Dateinamenbezeichnung
+ Dieses Label wird am Ende des Dateinamens angehängtGenauigkeit in der GPX-DateiErlaube Genauigkeitsanzeige in der GPX-Datei mit Wegpunkt-Name oder in einem separaten Tag
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index aa87bb3d5..b1076723c 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -37,8 +37,8 @@
Routenaufnahme stoppenRoutenaufnahme fortsetzenLöschen
- Als GPX exportieren
- GPX teilen
+ Exportieren
+ TeilenZu OpenStreetMap hochladenAuf Karte anzeigenDetails
@@ -62,6 +62,7 @@
(Noch nicht exportiert)(Noch nicht hochgeladen)Anzeigen
+ NameBeschreibungTags (mit Komma trennen)Sie müssen eine Beschreibung eingeben.
@@ -84,6 +85,17 @@
SprachaufnahmeFoto aufnehmenTextnotiz
+
+ Wegpunktname/Text
+ Geben Sie den Wegpunktnamen ein
+ Datei öffnen
+ Speichern
+ Löschen
+ Stornieren
+ Wegpunkt löschen
+ Diesen Wegpunkt löschen?
+ Löschen
+ StornierenEinstellungenWegpunkte
@@ -106,6 +118,14 @@
Bitte überprüfen Sie, ob der externe Speicher korrekt eingesetzt und eingebunden ist.Sprachaufnahme fehlgeschlagenFehler während Parsen der XML-Datei. Bitte auf Standardlayout wechseln.
+
+ Erlaubnis erforderlich
+ Um die GPX-Spur zu exportieren, müssen wir auf den Speicher schreiben.
+ Um den Titel richtig anzuzeigen, benötigen wir Zugriff auf den Speicher.
+ Um den Titel ordnungsgemäß freizugeben, benötigen wir Zugriff auf den Speicher.
+ Um den Track auf OSM hochzuladen, benötigen wir Zugriff auf den Speicher.
+ Akzeptieren
+ Ohne GPS-Berechtigung kann nicht fortgefahren werdenAufgezeichnet mit OSMTracker für Android™Warnung: HDOP-Werte sind nicht vom GPS. Es sind geschätzte Meter-Werte zur Ortsbestimmung, die nicht berücksichtigt werden sollten.
@@ -139,6 +159,8 @@
Warte auf Richtung...Richtung kann nicht bestimmt werdenExportierung erfolgreich abgeschlossen
+ Position nicht verfügbar
+ {0} {1} / {2} {3}OpenStreetMap-Routenanzeige
diff --git a/app/src/main/res/values-el/strings-preferences.xml b/app/src/main/res/values-el/strings-preferences.xml
index e0344efe5..156e13703 100644
--- a/app/src/main/res/values-el/strings-preferences.xml
+++ b/app/src/main/res/values-el/strings-preferences.xml
@@ -56,7 +56,6 @@
Εμφανίζει τον χάρτη του OpenStreetMap κάτω από το ίχνος. Χρειάζεται σύνδεση δεδομένωνΠάντα εμφάνιση του υπόβαθρου OpenStreetMap; Απαιτεί σύνδεση δεδομένωνΡυθμίσεις GPX
- Φάκελος εξωτερικού μέσου αποθήκευσης (SD)Θα επηρεάσει το επόμενο ίχνος (όχι το τρέχων)Ένα ευρετήριο ανά ίχνοςΑποθήκευση κάθε ίχνους και των σχετικών του αρχείων στον δικό του κατάλογο
@@ -65,6 +64,7 @@
Όνομα ίχνουςΌνομα, ημερομηνία και ώρα έναρξης
+ Ημερομηνία και ώρα έναρξης, ΌνομαΗμερομηνία και ώρα έναρξηςΑκρίβεια στο αρχείο GPX
diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml
index ca61e3e7c..7c601329a 100644
--- a/app/src/main/res/values-el/strings.xml
+++ b/app/src/main/res/values-el/strings.xml
@@ -38,8 +38,8 @@
Τερματισμός καταγραφής ίχνουςΣυνέχιση καταγραφής ίχνουςΔιαγραφή
- Εξαγωγή ως GPX
- Κοινοποίηση GPX
+ Εξαγωγή ως
+ ΚοινοποίησηΑποστολή στο OpenStreetMapΕμφάνισηΛεπτομέρειες
@@ -63,6 +63,7 @@
(Δεν έχει ακόμη εξαχθεί)(Δεν έχει ακόμη σταλεί)Προβολή
+ ΟνομαΠεριγραφήΕτικέτες (διαχωρισμένες με κόμμα)Πρέπει να εισάγετε μια περιγραφή
@@ -85,6 +86,7 @@
ΗχογράφησηΦωτογράφισηΣημείωση κειμένου
+
ΡυθμίσειςΣημεία
@@ -107,6 +109,7 @@
Παρακαλούμε ελέγξτε αν ο εξωτ. αποθηκευτικός χώρος έχει τοποθετηθεί σωστά. Η ηχογράφηση απέτυχεΣφάλμα στην ανάπτυξη του σχεδίου XML. Επαναφέρετε το προκαθορισμένο σχέδιο.
+
Ιχνηλατίστηκε με το OSMTracker για Android™Προσοχή: Οι τιμές HDOP δεν είναι ίδιες με αυτές που όρισε η συσκευή GPS. Είναι κατά προσέγγιση μέσω της ακρίβειας θέσης σε μέτρα.
@@ -140,6 +143,7 @@
Αναμονή για κατεύθυνση…Η κατεύθυνση δεν μπορεί να προσδιοριστείΗ διαδικασία εξαγωγής ολοκληρώθηκε με επιτυχία
+ {0} {1} / {2} {3}Προβολή ιχνών OpenStreetMap
diff --git a/app/src/main/res/values-es-rCL/accessibility.xml b/app/src/main/res/values-es-rCL/accessibility.xml
deleted file mode 100644
index 6542f214c..000000000
--- a/app/src/main/res/values-es-rCL/accessibility.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values-es-rCL/strings-preferences.xml b/app/src/main/res/values-es-rCL/strings-preferences.xml
deleted file mode 100644
index cc2bd985b..000000000
--- a/app/src/main/res/values-es-rCL/strings-preferences.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/values-es-rCL/strings-tags.xml b/app/src/main/res/values-es-rCL/strings-tags.xml
deleted file mode 100644
index c757504ac..000000000
--- a/app/src/main/res/values-es-rCL/strings-tags.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/app/src/main/res/values-es-rCL/strings.xml b/app/src/main/res/values-es-rCL/strings.xml
deleted file mode 100644
index e779edd5e..000000000
--- a/app/src/main/res/values-es-rCL/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values-es-rCL/waypoints.xml b/app/src/main/res/values-es-rCL/waypoints.xml
deleted file mode 100644
index 42eba3ccd..000000000
--- a/app/src/main/res/values-es-rCL/waypoints.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/app/src/main/res/values-es/accessibility.xml b/app/src/main/res/values-es/accessibility.xml
index bf6f34994..7b30e5ae8 100644
--- a/app/src/main/res/values-es/accessibility.xml
+++ b/app/src/main/res/values-es/accessibility.xml
@@ -5,6 +5,7 @@
AcercarAlejar
+ Centro del zoomIndicador de potencia de la señal de satéliteIndicador de grabación en curso
diff --git a/app/src/main/res/values-es/strings-preferences.xml b/app/src/main/res/values-es/strings-preferences.xml
index 8966676b3..f973b7c0d 100644
--- a/app/src/main/res/values-es/strings-preferences.xml
+++ b/app/src/main/res/values-es/strings-preferences.xml
@@ -56,7 +56,7 @@
Visualizar OpenStreetMap bajo la traza. Requiere una conexión de datos¿Mostrar siempre OpenStreetMap de fondo? Requiere una conexión de datosConfiguraciones GPX
- Carpeta de almacenamiento externo (SD)
+ Carpeta de almacenamiento en DocumentosSe hará efectivo en la traza siguiente (no la actual)Una carpeta por cada trazaGuarda cada rastro y los archivos asociados en su propia carpeta
@@ -65,8 +65,11 @@
Nombre de la trazaNombre, fecha y hora de inicio
+ Fecha y hora de inicio, nombreFecha y hora de inicio
+ Etiqueta de nombre de archivo
+ Esta etiqueta será añadida al final del nombre de archivoPrecisión en el archivo GPXAñadir información de precisión en el archivo GPX, con el nombre del punto o en un comentario del punto
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 0cdd1f8d3..b7a69122c 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -37,8 +37,8 @@
Detener el trazadoReanudar el trazadoEliminar
- Exportar como GPX
- Compartir GPX
+ Exportar
+ CompartirSubir a OpenStreetMapVisualizarDetalles
@@ -62,6 +62,7 @@
(aún no exportado)(aún no subido)Mostrar
+ NombreDescripción: Etiquetas (delimitado por comas)Debe introducir una descripción
@@ -84,6 +85,17 @@
Grabar vozTomar fotoNota de texto
+
+ Nombre de Punto/texto
+ Ingrese el nombre del punto
+ Abrir archivo
+ Guardar
+ Eliminar
+ Cancelar
+ Eliminar Punto
+ ¿Eliminar este punto?
+ Eliminar
+ CancelarConfiguraciónPuntos
@@ -106,6 +118,14 @@
Compruebe que el almacenamiento externo está correctamente insertado y montado.La grabación de voz ha falladoError al analizar el archivo XML de disposición. Por favor, vuelva a la disposición predeterminada.
+
+ Se requiere permiso
+ Para exportar la traza se requiere permiso de escritura en el almacenamiento.
+ Para mostrar la traza se requiere permiso de escritura en el almacenamiento.
+ Para compartir la traza se requiere permiso de escritura en el almacenamiento.
+ Para subir a OSM la traza se requiere permiso de escritura en el almacenamiento.
+ Aceptar
+ No se puede continuar sin el permiso de GPS.Trazado con OSMTracker para Android™Advertencia: los valores HDOP no son los valores HDOP devueltos por el dispositivo GPS. Son valores aproximados de acuerdo a la precisión de la ubicación en metros.
@@ -139,6 +159,8 @@
Espere por la cabecera…La cabecera no se puede determinarEl proceso de exportación finalizó con éxito
+ Posición no disponible
+ {0} {1} / {2} {3}Vista de la traza en OpenStreetMap
diff --git a/app/src/main/res/values-et/accessibility.xml b/app/src/main/res/values-et/accessibility.xml
deleted file mode 100644
index b81e0d2e1..000000000
--- a/app/src/main/res/values-et/accessibility.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values-et/strings-preferences.xml b/app/src/main/res/values-et/strings-preferences.xml
deleted file mode 100644
index cc2bd985b..000000000
--- a/app/src/main/res/values-et/strings-preferences.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/values-et/strings-tags.xml b/app/src/main/res/values-et/strings-tags.xml
deleted file mode 100644
index c757504ac..000000000
--- a/app/src/main/res/values-et/strings-tags.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/app/src/main/res/values-et/waypoints.xml b/app/src/main/res/values-et/waypoints.xml
deleted file mode 100644
index c757504ac..000000000
--- a/app/src/main/res/values-et/waypoints.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/app/src/main/res/values-fa-rIR/strings-preferences.xml b/app/src/main/res/values-fa-rIR/strings-preferences.xml
index 4dbfde9aa..7569146d4 100644
--- a/app/src/main/res/values-fa-rIR/strings-preferences.xml
+++ b/app/src/main/res/values-fa-rIR/strings-preferences.xml
@@ -50,17 +50,11 @@
نقشهٔ پسزمینهنمایش نقشهٔ OpenStreetMap زیر رد. به ارتباط اینترنتی نیاز داردتنظیمات GPS
- شاخهٔ حافظهٔ خارجی (SD)موثر برای رد بعدی (روی رد فعلی تاثیر نمیگذارد)یک شاخه برای هر ردهر رد و فایلهای پیوست آن را در شاخهٔ مجزا ذخیره کننام فایل برای ردهای نامدارالگو برای نام فایل، اگر رد نام داشته باشد
-
- نام رد
- نام، تاریخ و ساعت شروع
- تاریخ و ساعت شروع
- ثبت دقت در فایل GPXاطلاعات دقت را به فایل GPX اضافه میکند: در کنار نام نقطهٔ بینراهی یا در توضیحات نقطهٔ بینراهی
diff --git a/app/src/main/res/values-fa-rIR/strings.xml b/app/src/main/res/values-fa-rIR/strings.xml
index 75b2e73a5..4938b8691 100644
--- a/app/src/main/res/values-fa-rIR/strings.xml
+++ b/app/src/main/res/values-fa-rIR/strings.xml
@@ -37,8 +37,6 @@
توقف ثبت مسیرادامهٔ ثبت مسیرحذف
- خروجی در قالب GPX
- همرسانی GPXبارگذاری در OpenStreetMapنمایشجزئیات
@@ -84,6 +82,7 @@
ضبط صداگرفتن عکسیادداشت متنی
+
تنظیماتنقاط بینراهی
@@ -106,6 +105,7 @@
لطفاً مطمئن شوید حافظهٔ خارجی دستگاه بهدرستی جاگذاری و سوار (mount) شده باشد.ضبط صدا با اشکال مواجه شدخطا هنگام تجزیهٔ فایل چیدمان XML. لطفاً به چیدمان پیشفرض برگردانید.
+
با OSM Tracker for Android™ ضبط شده استهشدار: مقادیر HDOP همان مقادیر HDOP دریافتی از دستگاه GPS نیست؛ بلکه بر اساس دقت مکانی بر حسب متر تقریب زده شده.
@@ -139,6 +139,7 @@
منتظر جهت حرکت...سیستم قادر به تشخیص جهت حرکت نیستفرآیند برونبرد با موفقیت پایان یافت
+ {0} {1} / {2} {3}نمایش مسیر اوپناستریتمپ
diff --git a/app/src/main/res/values-fr/accessibility.xml b/app/src/main/res/values-fr/accessibility.xml
index d3265beab..3295ab9a8 100644
--- a/app/src/main/res/values-fr/accessibility.xml
+++ b/app/src/main/res/values-fr/accessibility.xml
@@ -5,6 +5,7 @@
Zoom avantZoom arrière
+ centre de zoomIndicateur de signal GPSIndicateur d\'enregistrement
diff --git a/app/src/main/res/values-fr/strings-preferences.xml b/app/src/main/res/values-fr/strings-preferences.xml
index 7328e8cf0..126e9468a 100644
--- a/app/src/main/res/values-fr/strings-preferences.xml
+++ b/app/src/main/res/values-fr/strings-preferences.xml
@@ -9,9 +9,14 @@
Vérifie si le GPS est désactivé au démarrage, et propose de l\'activerIgnorer l\'horloge du GPSIgnore l\'horloge du GPS et utilise celle d\'Android pour l\'horodatage
+ Enregistre la pression barométrique [hPa]
+ La modification requiert un redémarrage de la traceIntervalle d\'enregistrement GPSChoisissez 0 pour le plus petit intervalle possible (consomme plus de batterie)seconds
+ Distance d\'enregistrement GPS
+ Distance minimale entre les points de suivi en mètres, utilisez 0 pour la plus courte possible
+ mètresInterface utilisateurSource des photographies par défautPrendre des photographies à partir de l’appareil photo ou à partir de la gallerie d’images ?
@@ -49,8 +54,9 @@
L\'écran restera actif durant l\'enregistrement. Désactivez pour économiser la batterieOSM en arrière planAffiche OpenStreetMap sous la trace. Nécessite une connexion de données
+ Toujours afficher l\'arrière-plan OpenStreetMap? Nécessite une connexion de donnéesFichier d\'enregistrement GPX
- Répertoire de stockage externe (SD)
+ Dossier de stockage dans les documentsS\'applique à partir de la prochaine traceUn dossier par traceSauvegarde chaque trace et ses données associées dans son propre dossier
@@ -59,8 +65,11 @@
Nom de la traceNom, date et heure de début
+ Date et heure de début, nomDate et heure de début
+ Étiquette du nom de fichier
+ Cette étiquette sera ajoutée à la fin du nom de fichierContenu du fichier GPXIntègre la précision au fichier GPX, dans le nom des waypoint ou dans un tag séparé
@@ -75,6 +84,7 @@
Supprimer les identifiants OpenStreetMapForce OSMTracker à se ré-authentifier sur OpenStreetMapVous devrez vous reconnecter à OpenStreetMap. Êtes-vous sûr ?
+ Fournisseur de tuiles de carteMapnik
@@ -86,5 +96,6 @@
En commentaireEn extention
+ Exporter la direction de la boussoleDéfinit si et comment les données du compas devraient être exportés dans le fichier GPX
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 3460f2f00..fd8c2097d 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -31,12 +31,14 @@
Points d\'interêt :Pts. de trace :Vous n\'avez pas de traces.
+ Appuyer pour enregistrer une nouvelle traceImpossible de créer une nouvelle trace : {0}Vous enregistrez en ce momentla trace #{0}\nChoisissez-la dans la liste pour continuerArrêter l\'enregistrementReprendre l\'enregistrementSupprimer
- Exporter (GPX)
+ Exporter
+ PartagerUploader vers OpenStreetMapAfficherDétails
@@ -47,6 +49,7 @@
Préparation de l\'export...Impossible d\'exporter la trace : {0}Toutes les traces vont être exportées, cela peut prendre du temps. Êtes-vous sûr ?
+ Impossible de traiter la trace : {0}Détails de la traceDate et heure de début :
@@ -59,6 +62,7 @@
(Pas encore exporté)(Pas encore uploadé)Afficher
+ NomDescriptionTags (séparés par des virgules)La description est obligatoire
@@ -81,6 +85,17 @@
Mémo vocalPrendre une photoNote
+
+ Point d\'interêt/texte
+ Entrer le nom du point d\'intérêt
+ Ouvrir le fichier
+ Enregistrer
+ Supprimer
+ Annuler
+ Supprimer point d\'intérêt
+ Supprimer ce point d\'intérêt?
+ Supprimer
+ AnnulerParamètresPoints d\'interêt
@@ -103,6 +118,14 @@
Veuillez vérifier que le stockage est correctement inséré et monté.L\'enregistrement vocal a échoué.Erreur dans la définition des boutons. Veuillez revenir aux boutons par défaut.
+
+ Autorisation requise
+ Pour exporter la trace GPX, nous devons écrire sur le stockage.
+ Pour afficher correctement la trace, nous avons besoin d\'accéder au stockage.
+ Pour partager correctement la trace, nous avons besoin d\'accéder au stockage
+ Pour télécharger la trace sur OSM, nous avons besoin d\'accéder au stockage.
+ Accepter
+ Impossible de continuer sans autorisation GPSTraces enregistrées avec OSMTracker pour Android™Attention : les valeurs HDOP ne sont pas celles retournées par le GPS, mais approximées a partir de la précision en mètres. Elles ne sont donc pas très fiables.
@@ -135,29 +158,50 @@
En attente de coordonnées...Les coordonées n\'ont pas pu être déterminées
+ Export réalisé avec succès
+ Poste non disponible
+ {0} {1} / {2} {3}Voir le tracé avec OpenStreetMapMettre à jour & InstallerSupprimerMise à jour …
+ Calque mis à jour avec succès
+ Calque non chargé, réessayer plus tard
+ Êtes-vous sûr de supprimée le calque {0} ?Oui
+ Le calque a été supprimé avec succèsL\'icône du répertoire a été supprimé avec succès
+ Ce fichier n\'a pas de dossier icône associé
+ Ce calque ne peut pas être suppriméErreur : Impossible de se connecter à internet
+ Erreur : impossible de se connecter au dépôt des dispositions personnalisées par défaut(Connection …)TéléchargerVotre langue n\'est pas disponible. Sélectionner en une dans la liste.Langues disponibles
+ Vérification des versions locales des languesTéléchargement …
+ Le calque a été téléchargé avec succès
+ Ce calque ne peut pas être téléchargé, réessayer plus tard
+ Erreur de récupération des données du serveurDéfaut
+ PersonnaliséNom d\'utilisateur de GitHub:Nom du répertoire :Nom de la branche :Répertoire GitHub valideRépertoire GitHub invalide
+ OSMTracker pour Android™ Introduction
+ Bienvenue sur OSMTracker pour Android™ 👋
+ Cette application est un logiciel libre qui respecte votre liberté !
+ Bon cartographiage 🗺 😎
+ OSMTracker pour Android utilisera votre position GPS pour enregistrer des traces et des points de passage, même lorsque l\'application fonctionne en arrière-plan.
+\nVos données ne sont pas utilisées pour afficher des publicités.
diff --git a/app/src/main/res/values-he/strings-preferences.xml b/app/src/main/res/values-he/strings-preferences.xml
index 6366b0b1d..c5ea1e5e4 100644
--- a/app/src/main/res/values-he/strings-preferences.xml
+++ b/app/src/main/res/values-he/strings-preferences.xml
@@ -50,17 +50,11 @@
מפת רקעהצג את מפת OpenStreetMap כרקע למסלול. דורש חיבור לאינטרנטהגדרות GPX
- תיקיית אכסון חיצוני (SD)יחל במסלול הבא (לא במסלול הנוכחי)תיקייה אחת לכל מסלולשמור כל מסלול והקבצים הנלווים לו בתיקייה נפרדתשם קובץ למסלולים בעלי שםתבנית לשם הקובץ, אם למסלול יש שם
-
- שם המסלול
- שם, תאריך וזמן התחלה
- תאריך וזמן התחלה
- שמור את הדיוק בקובץ ה-GPXהוסף את הדיוק לקובץ ה-GPX בשם נקודת הציון או כהערה לנקודת הציון
diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml
index 64c758c8d..d5541046c 100644
--- a/app/src/main/res/values-he/strings.xml
+++ b/app/src/main/res/values-he/strings.xml
@@ -37,7 +37,6 @@
עצור מסלולחזור למסלולמחק
- ייצא כקובץ GPXהעלאה ל OpenStreetMapהצגפרטים
@@ -82,6 +81,7 @@
הקלטת הודעה קוליתצילום תמונההערה בכתב
+
הגדרותנקודות עיניין
@@ -104,6 +104,7 @@
בדקו אם התקן הזיכרון החיצוני הוכנס כראוי והתחברההקלטה הקולית נכשלהשגיאה בניתוח קובץ ה XML של העימוד. נא לחזור לעימוד המקורי
+
Tracked with OSMTracker for Android™אזהרה: ערכי HDOP לא התקבלו מה GPS אלא הוערכו על פי דיוק המיקום במטרים.
@@ -136,6 +137,7 @@
ממתין לכיוון...לא ניתן לקבוע את הכיוון
+ {0} {1} / {2} {3}הצגת הקלטת OpenStreetMap
diff --git a/app/src/main/res/values-hr/strings-tags.xml b/app/src/main/res/values-hr/strings-tags.xml
deleted file mode 100644
index c757504ac..000000000
--- a/app/src/main/res/values-hr/strings-tags.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/app/src/main/res/values-hu/strings-preferences.xml b/app/src/main/res/values-hu/strings-preferences.xml
index 20269649e..8558546c0 100644
--- a/app/src/main/res/values-hu/strings-preferences.xml
+++ b/app/src/main/res/values-hu/strings-preferences.xml
@@ -11,10 +11,10 @@
A GPS óra figyelmen kívül hagyása és az Android óra használata az időbélyegzéshezLégnyomás naplózása (hPa)Be-/kikapcsolásához újra kell indítani az alkalmazást
- GPS-naplózása időköze
+ GPS-naplózás időközeA lehető legsűrűbb naplózási időközhöz írjon 0-t (befolyásolja az akkumulátor élettartamát).másodperc
- GPS-naplózása távolságai
+ GPS-naplózás távolságaAz útvonalpotok közötti legkisebb távolság, a lehető legkisebbhez írjon be 0-tméterFelhasználói felület
@@ -54,9 +54,9 @@
A képernyő bekapcsolva marad nyomkövetéskor. Kapcsolja ki az akku jobb üzemidejéhezHáttértérképAz OpenStreetMap megjelenítése a nyomkövetés alatt. Adatkapcsolatot igényel
- Mindig megjelenjék az OpenStreetMap a háttérben? Adatkapcsolatot igényel
+ Mindig megjelenjen az OpenStreetMap a háttérben? Adatkapcsolatot igényelGPX beállítások
- Külső tároló (SD) könyvtár
+ Tárolómappa a dokumentumokbanA következő nyomkövetésnél lép életbe (nem az aktuálisnál)
Egy könyvtár nyomvonalanként
@@ -66,6 +66,7 @@
Nyomkövetés neveNév, kezdeti dátum és időpont
+ Kezdeti dátum és időpont, névKezdeti dátum és időpontPontosság a GPX fájlban
diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml
index 1c6417aad..ed7fd5844 100644
--- a/app/src/main/res/values-hu/strings.xml
+++ b/app/src/main/res/values-hu/strings.xml
@@ -37,8 +37,8 @@
Nyomvonalkészítés leállításaNyomvonalkészítés folytatásaTörlés
- Exportálás GPX fájlként
- GPX megosztása
+ Exportálás
+ MegosztásaFeltöltés az OpenStreetMapreMegjelenítésRészletek
@@ -62,6 +62,7 @@
(Még nincs exportálva)(Még nincs feltöltve)Megjelenítés
+ NévLeírásCímkék (vesszővel elválasztva)Meg kell adnia egy leírást
@@ -84,6 +85,7 @@
HangrögzítésFényképezésSzöveges megjegyzés
+
BeállításokÚtpontok
@@ -106,6 +108,7 @@
Kérjük, ellenőrizze a külső tároló csatlakoztatását.Hangrögzítés sikertelenHiba az XML-sémafájl értelmezésekor. Kérjük, térjen vissza az alapértelmezett sémához.
+
Tracked with OSMTracker for Android™Figyelem: A HDOP értékek nem azok a HDOP értékek, amit a GPS eszköz ad vissza. Ezek becsült értékek a hely pontosságából, méterben.
@@ -139,6 +142,7 @@
Várakozás az irányra…Az irány nem határozható megAz exportálási folyamat sikeresen befejeződött
+ {0} {1} / {2} {3}OpenStreetMap nyomvonal megjelenítése
diff --git a/app/src/main/res/values-hy/strings-preferences.xml b/app/src/main/res/values-hy/strings-preferences.xml
deleted file mode 100644
index cc2bd985b..000000000
--- a/app/src/main/res/values-hy/strings-preferences.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/values-hy/strings-tags.xml b/app/src/main/res/values-hy/strings-tags.xml
deleted file mode 100644
index c757504ac..000000000
--- a/app/src/main/res/values-hy/strings-tags.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/app/src/main/res/values-hy/waypoints.xml b/app/src/main/res/values-hy/waypoints.xml
deleted file mode 100644
index 42eba3ccd..000000000
--- a/app/src/main/res/values-hy/waypoints.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/app/src/main/res/values-it/accessibility.xml b/app/src/main/res/values-it/accessibility.xml
index 306ac68e4..ea31d0af0 100644
--- a/app/src/main/res/values-it/accessibility.xml
+++ b/app/src/main/res/values-it/accessibility.xml
@@ -5,6 +5,7 @@
Aumentare lo zoomDiminuire lo zoom
+ CentroIndicatore di potenza del segnale satellitareIndicatore di registrazione in corso
diff --git a/app/src/main/res/values-it/strings-preferences.xml b/app/src/main/res/values-it/strings-preferences.xml
index d2ff291cd..1fad30f9d 100644
--- a/app/src/main/res/values-it/strings-preferences.xml
+++ b/app/src/main/res/values-it/strings-preferences.xml
@@ -56,7 +56,7 @@
Mostra OpenStreetMap sotto la traccia. È necessaria una connessione datiMostra sempre lo sfondo OpenStreetMap? Richiede una connessione datiSalvataggio file GPX
- Directory memoria esterna (SD)
+ Cartella di archiviazione nei documentiValido per la traccia successiva (non per quella attuale)Una directory per tracciaSalva ogni traccia e i file associati nella propria directory
@@ -65,8 +65,11 @@
Nome tracciaNome, data e ora inizio
+ Data e ora inizio, nomeData e ora inizio
+ Etichetta del nome file
+ Questa etichetta verrà aggiunta alla fine del nome del filePrecisione nel file GPXInserisce le info di precisione nel file GPX, con il nome del POI o in una scheda separata
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 32057744d..795580080 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -37,8 +37,8 @@
Ferma registrazioneRiprendi registrazioneElimina
- Esporta in GPX
- Condividi GPX
+ Esporta
+ CondividiCarica su OpenStreetMapMostraDettagli
@@ -62,6 +62,7 @@
(non ancora esportata)(non è stato ancora caricata)Mostra
+ NomeDescrizione:Etichette (separate da una virgola)Devi inserire una descrizione
@@ -84,6 +85,17 @@
Registra voceScatta fotoScrivi nota
+
+ Nome do waypoint/testo
+ Inserisci il nome del waypoint
+ Apri file
+ Salvare
+ Eliminare
+ Annullare
+ Eliminare waypoint
+ Eliminare il waypoint?
+ Eliminare
+ AnnullareImpostazioniPOI
@@ -106,6 +118,14 @@
Verifica che la memoria esterna sia inserita e montata correttamente.Registrazione vocale fallitaErrore durante l\'elaborazione del file XML del layout. Ripristinare il layout predefinito.
+
+ Permesso richiesto
+ Per esportare la traccia GPX dobbiamo scrivere sullo storage.
+ Per visualizzare correttamente la traccia abbiamo bisogno di accedere allo spazio di archiviazione.
+ Per condividere correttamente la traccia abbiamo bisogno di accedere allo storage.
+ Per caricare la traccia su OSM abbiamo bisogno di accedere allo storage.
+ Accettare
+ Impossibile continuare senza l\'autorizzazione del GPSTracciato con OSMTracker per Android™Attenzione: i valori HDOP non corrispondono agli HDOP restituiti dal dispositivo GPS. La precisione della posizione è approssimativa in metri e non dovrebbe essere tenuta in conto.
@@ -139,6 +159,8 @@
In attesa della direzione...La direzione non può essere determinataEsportazione completata con successo
+ Posizione non disponibile
+ {0} {1} / {2} {3}Mostra traccia OpenStreetMap
diff --git a/app/src/main/res/values-ja/strings-preferences.xml b/app/src/main/res/values-ja/strings-preferences.xml
index 6f52d598d..c4a2222fe 100644
--- a/app/src/main/res/values-ja/strings-preferences.xml
+++ b/app/src/main/res/values-ja/strings-preferences.xml
@@ -56,7 +56,6 @@
トラックの背景にOpenStreetMapの地図を表示する。データ通信接続が必要常に背景にOpenStreetMapを表示しますか?データ通信接続が必要ですGPX設定
- 外部ストレージ(SD)ディレクトリ次回から反映 (現在の分は適用外)トラック別のディレクトリ各トラックと関連ファイルを個別のディレクトリに保存
@@ -65,6 +64,7 @@
トラック名トラック名_開始日時
+ 開始日時_トラック名開始日時GPXファイルの補完
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 808023511..4a00061f1 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -38,8 +38,6 @@
トラッキングの停止トラッキングの再開削除
- GPX にエクスポート
- GPX をシェアOpenStreetMap へアップロード表示詳細
@@ -85,6 +83,7 @@
音声録音写真撮影テキスト
+
設定ウェイポイント
@@ -107,6 +106,7 @@
外部ストレージが挿入され、正しくマウントされていることを確認してください。音声録音に失敗しましたXML レイアウトファイルが認識できません。デフォルトレイアウトに戻してください。
+
OSMTracker for Android™ で追跡警告: HDOP 値が GPS 機器から返された HDOP 値と異なります。これは正しい位置からのメートル単位の距離です。
@@ -140,6 +140,7 @@
方位測定待ち...方位が特定できませんでしたエクスポート処理が完了しました
+ {0} {1} / {2} {3}OpenStreetMap トラック表示
diff --git a/app/src/main/res/values-kn/accessibility.xml b/app/src/main/res/values-kn/accessibility.xml
deleted file mode 100644
index 6542f214c..000000000
--- a/app/src/main/res/values-kn/accessibility.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values-kn/strings-tags.xml b/app/src/main/res/values-kn/strings-tags.xml
deleted file mode 100644
index c757504ac..000000000
--- a/app/src/main/res/values-kn/strings-tags.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml
deleted file mode 100644
index e779edd5e..000000000
--- a/app/src/main/res/values-kn/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values-ko/strings-preferences.xml b/app/src/main/res/values-ko/strings-preferences.xml
index 5ea9367e0..9acc551ae 100644
--- a/app/src/main/res/values-ko/strings-preferences.xml
+++ b/app/src/main/res/values-ko/strings-preferences.xml
@@ -50,7 +50,6 @@
배경 지도트랙 아래에 OpenStreetMap 지도를 표시합니다. 데이터 연결이 필요합니다GPX 설정
- 외부 저장소 (SD) 디렉터리다음 트랙에 반영됩니다 (현재 것은 안됨)트랙마다 디렉터리 하나자신의 디렉터리에 각 트랙과 관련 파일을 저장합니다
@@ -59,6 +58,7 @@
트랙 이름이름, 시작 날짜와 시간
+ 시작 날짜와 시간, 이름시작 날짜와 시간GPX 파일의 정확도
@@ -75,6 +75,7 @@
OSM 인증 재설정OSM 자격 증명과 권한을 잊고 강제로 OSMTracker에 다시 묻도록 합니다트랙을 올리려면 OSMTracker를 다시 인증해야 합니다. 지우겠습니까?
+ 지도 타일 제공자Mapnik
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index 62fc29128..748faf0db 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -36,7 +36,7 @@
트래킹 멈춤트래킹 재개삭제
- GPX로 내보내기
+ 내보내기OpenStreetMap으로 올리기표시자세한 정보
@@ -81,6 +81,7 @@
음성 녹음사진 찍기텍스트 기록
+
설정웨이포인트
@@ -103,6 +104,7 @@
외부 저장소가 올바르게 삽입되어 있고 마운트되어 있는지 확인하세요.음성 녹음에 실패했습니다XML 레이아웃 파일을 구문 분석하는 동안 오류가 있습니다. 기본 레이아웃으로 되돌리세요.
+
Android용 OSMTracker™로 트랙됨경고: HDOP 값이 GPS 장치가 반환한 HDOP 값이지 않습니다. 이들은 미터 단위의 위치 정확도에서 근사하고 있습니다.
@@ -135,6 +137,7 @@
방위 측정을 기다리는 중…방위를 결정할 수 없습니다
+ {0} {1} / {2} {3}OpenStreetMap 트랙 표시
diff --git a/app/src/main/res/values-lv/strings-preferences.xml b/app/src/main/res/values-lv/strings-preferences.xml
index 2e590414a..ffbc89425 100644
--- a/app/src/main/res/values-lv/strings-preferences.xml
+++ b/app/src/main/res/values-lv/strings-preferences.xml
@@ -50,7 +50,6 @@
Fona karteAttēlot OpenStreetMap karti zem ierakstītā ceļa. Vajadzīgs datu savienojumsGPX iestatījumi
- Ārējās krātuves (SD) mapeFunkcionē nākamajam ierakstam (ne pašreizējam)Viena mape priekš ierakstaSaglabāt katru ierakstu un tā datnes savā mapē
@@ -59,6 +58,7 @@
Ieraksta nosaukumsNosaukums, sākuma datums un laiks
+ Sākuma datums un laiks, nosaukumsSākuma datums un laiksPrecizitāte GPX datnē
diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml
index c5c6cf655..3f5b20d6d 100644
--- a/app/src/main/res/values-lv/strings.xml
+++ b/app/src/main/res/values-lv/strings.xml
@@ -36,7 +36,7 @@
Beigt ierakstuTurpināt ierakstuDzēst
- Eksportēt uz GPX
+ EksportētAugšupielādēt uz OpenStreetMapDisplejsDetaļas
@@ -81,6 +81,7 @@
Ierakstīt balsiUzņemt attēluTeksta piezīme
+
IestatījumiIntereses punkti
@@ -103,6 +104,7 @@
Lūdzu pārbaudi vai ārējā krāt. ir pareizi ievietota un piemontēta.Balss ieraksts neizdevās Kļūda parsējot XML izkārtojuma datni. Lūdzu atiestati uz noklusējuma izkārtojumu.
+
Ierakstīts izmantojot OSMTracker priekš Android™Uzmanību: HDOP vērtības nav HDOP, kas tiek saņemts no GPS ierīces. Tās tiek aptuveni aprēķinātas no precizitātes metros.
@@ -135,6 +137,7 @@
Gaida virziena noteikšanu...Virzienu nevar noteikt
+ {0} {1} / {2} {3}OpenStreetMap ieraksta displejs
diff --git a/app/src/main/res/values-nl/accessibility.xml b/app/src/main/res/values-nl/accessibility.xml
index 2d4ce6035..102a9da15 100644
--- a/app/src/main/res/values-nl/accessibility.xml
+++ b/app/src/main/res/values-nl/accessibility.xml
@@ -5,6 +5,7 @@
InzoomenUitzoomen
+ CentrerenAanduiding satelliet-signaalsterkteAanduiding opname bezig
diff --git a/app/src/main/res/values-nl/strings-preferences.xml b/app/src/main/res/values-nl/strings-preferences.xml
index e4a703389..982e260fd 100644
--- a/app/src/main/res/values-nl/strings-preferences.xml
+++ b/app/src/main/res/values-nl/strings-preferences.xml
@@ -56,7 +56,7 @@
OpenStreetMap-kaart weergeven onder traject. Heeft een dataverbinding nodigAltijd OpenStreetMap-achtergrond weergeven? Vereist een dataverbinding.GPX-instellingen
- Map externe opslag (SD)
+ Opslagmap in documentenEffectief voor het volgende traject (niet het huidige)Een map per trajectElk traject en bijbehorende bestanden in eigen map opslaan
@@ -65,8 +65,11 @@
TrajectnaamNaam, begindatum en -tijd
+ Begindatum en -tijd, naamBegindatum en -tijd
+ Bestandsnaam-label
+ Dit label wordt toegevoegd aan het einde van de bestandsnaamNauwkeurigheid in GPX-bestandNauwkeurigheid toevoegen in GPX-bestand, met naam van referentiepunt of in referentiepunt-opmerking
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index d037aeba9..a9ceda2b4 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -38,8 +38,8 @@ Selecteer het in de lijst om door te gaan.Opnemen stoppenOpnemen hervattenVerwijderen
- Exporteren als GPX
- GPX delen
+ Exporteren
+ DelenUploaden naar OpenStreetMapWeergevenDetails
@@ -63,6 +63,7 @@ Selecteer het in de lijst om door te gaan.(Nog niet geëxporteerd)(Nog niet geüpload)Weergeven
+ NaamBeschrijvingTags (kommagescheiden)Een beschrijving is vereist
@@ -85,6 +86,17 @@ Selecteer het in de lijst om door te gaan.StemopnameFoto nemenNotitie
+
+ Referentiepunt naam/tekst
+ Naam van referentiepunt geven
+ Bestand open
+ Opslaan
+ Verwijderen
+ Annuleren
+ Referentiepunt verwijderen
+ Dit referentiepunt verwijderen?
+ Verwijderen
+ AnnulerenInstellingenReferentiepunten
@@ -107,6 +119,14 @@ Selecteer het in de lijst om door te gaan.Controleer of de SD-kaart correct geplaatst is.Stemopname misluktFout bij inlezen van XML lay-out-bestand. Gelieve terug te keren naar de standaard lay-out.
+
+ Toestemming vereist
+ Om het GPX-traject op te slaan, moeten we in de opslag schrijven.
+ Om het traject correct weer te geven hebben we toegang nodig tot de opslag.
+ Om het traject correct te delen hebben we toegang nodig tot de opslag.
+ Om het traject naar OSM te uploaden hebben we toegang nodig tot de opslag.
+ Accepteren
+ Kan niet verdergaan zonder gps-toestemmingOpgenomen met OSMTracker voor Android™Waarschuwing: HDOP-waarden worden niet uit de gps-apparaat gelezen. Ze worden benaderd op basis van de nauwkeurigheid van de locatie (in meter).
@@ -140,6 +160,8 @@ Selecteer het in de lijst om door te gaan.Wachten voor richting...Richting kan niet bepaald wordenExporteerproces met succes afgerond
+ Positie niet beschikbaar
+ {0} {1} / {2} {3}OpenStreetMap trajectweergave
diff --git a/app/src/main/res/values-pl/accessibility.xml b/app/src/main/res/values-pl/accessibility.xml
index 043f35b48..9f30a9279 100644
--- a/app/src/main/res/values-pl/accessibility.xml
+++ b/app/src/main/res/values-pl/accessibility.xml
@@ -5,6 +5,7 @@
PrzybliżOddal
+ CentrumWskaźnik siły sygnału satelitWskaźnik postępu zapisywania
diff --git a/app/src/main/res/values-pl/strings-preferences.xml b/app/src/main/res/values-pl/strings-preferences.xml
index d5c4257b3..b79dd6d7a 100644
--- a/app/src/main/res/values-pl/strings-preferences.xml
+++ b/app/src/main/res/values-pl/strings-preferences.xml
@@ -27,11 +27,11 @@
Układ przyciskówWybierz własny układ przycisków (zobacz dokumentację)
- DOMYŚLNY LAYOUT
+ UKŁAD DOMYŚLNYDomyślnie
- POBRANY LAYOUT
- Nie masz pobranych plików z layout-ami
- Dostępne Layout-y
+ POBRANY UKŁAD
+ Nie masz pobranych plików układu
+ Dostępne układyUstawienia repozytorium GithubOrientacjaPreferowana orientacja przycisków na ekranie
@@ -56,17 +56,20 @@
Włącza mapy OSM w podglądzie śladu. Wymaga aktywnego połączenia z Internetem.Wyświetlać zawsze tło OpenStreetMap? Wymaga połączenia z internetemUstawienia GPX
- Folder na karcie pamięci
+ Katalog przechowywania w dokumentachZmiana zadziała przy następnym śladzie
- Osobny folder dla każdego śladu
+ Osobny katalog dla każdego śladuZapisuje każdy ślad wraz z powiązanymi plikami w oddzielnych folderachNazwy plików śladówSchemat dla nazwanych śladówNazwa śladuNazwa, data i czas startu
+ Data i czas startu, nazwaData i czas startu
+ Etykieta nazwy pliku
+ Ta etykieta zostanie dodana na końcu nazwy plikuPrecyzja w pliku GPXDodaje informacje o dokładności w pliku GPX, w nazwie punktu lub w komantarzu punktu
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index ed4bacdad..343eb922c 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -37,8 +37,8 @@
Zakończ rejestracjęWznów rejestracjęUsuń
- Eksport do GPX
- Udostępnij GPX
+ Eksport
+ UdostępnijWyślij do OpenStreetMapWyświetlSzczegóły
@@ -62,6 +62,7 @@
(Jeszcze nie wyeksportowano)(Jeszcze nie wysłano)Wyświetl
+ NazwaOpisTagi (oddzielone przecinkami)Musisz wpisać opis.
@@ -85,6 +86,7 @@
głosową
Zrób zdjęcieNotka
+
UstawieniaPunkty nawigacji
@@ -107,6 +109,14 @@ głosową
Proszę sprawdzić wew. pamięć czy jest prawidłowo zamontowana.Nagrywanie głosu nie powiodło sięBłąd podczas przetwarzania pliku XML. Proszę powrócić do domyślnego układu.
+
+ Wymagane pozwolenie
+ Aby wyeksportować ślad GPX, musimy zapisać go w pamięci.
+ Aby poprawnie wyświetlić ścieżkę potrzebujemy dostępu do magazynu.
+ Aby prawidłowo udostępnić ścieżkę potrzebujemy dostępu do magazynu.
+ Aby przesłać ścieżkę do OSM, potrzebujemy dostępu do magazynu.
+ Zgoda
+ Nie można kontynuować bez przyzwolenia na GPSŚlad zapisany przez OSMTracker - Android™UWAGA: wartości HDOP nie są pobierane z urządzenia GPS. Są to dane orientacyjne podane w metrach.
@@ -140,6 +150,7 @@ głosową
Oczekuję na kierunek...Kierunek nie może zostać określonyProces eksportu zakończony sukcesem
+ {0} {1} / {2} {3}Podgląd śladu w OpenStreetMap
diff --git a/app/src/main/res/values-pt-rBR/accessibility.xml b/app/src/main/res/values-pt-rBR/accessibility.xml
index 5a9433723..e35650f5a 100644
--- a/app/src/main/res/values-pt-rBR/accessibility.xml
+++ b/app/src/main/res/values-pt-rBR/accessibility.xml
@@ -5,6 +5,7 @@
Aumentar zoomDiminuir zoom
+ Centro de zoomIndicador de força do sinal de satéliteIndicador do progresso da gravação
diff --git a/app/src/main/res/values-pt-rBR/strings-preferences.xml b/app/src/main/res/values-pt-rBR/strings-preferences.xml
index 4b9e97d1e..6b51c1316 100644
--- a/app/src/main/res/values-pt-rBR/strings-preferences.xml
+++ b/app/src/main/res/values-pt-rBR/strings-preferences.xml
@@ -56,17 +56,15 @@
Exibir mapa do OpenStreetMap por baixo. Precisa de uma conexão de dadosSempre exibir o fundo do OpenStreetMap? Requer uma conexão de dadosConfigurações GPX
- Pasta do armazenamento externo (SD)
+ Pasta do armazenamento nos documentosTerá efeito na próxima trilha (não na atual)Uma pasta por trilhaSalvar cada trilha e arquivos associados em uma pasta própriaNome do arquivo para trilhas nomeadasPadrão para nomes de arquivo se a trilha tem um nome
-
- Nome da trilha
- Nome, data e hora do início
- Data e hora do início
-
+
+ rótulo do nome do arquivo
+ Este rótulo será anexado ao final do nome do arquivoPrecisão do arquivo GPXAdicionar informações sobre precisão nos arquivos GPX, com nomes ou comentários dos pontos de referência
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index 00546df03..f5d3e2570 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -37,8 +37,8 @@
Parar gravaçãoContinuar gravaçãoApagar
- Exportar em GPX
- Compartilhar GPX
+ Exportar
+ CompartilharEnviar para OpenStreetMapExibirDetalhes
@@ -62,6 +62,7 @@
(Ainda não exportado)(Ainda não enviado)Exibir
+ NomeDescriçãoTags (separado com vírgula)Você precisa digitar uma descrição
@@ -84,6 +85,17 @@
Gravar vozTirar fotoNota
+
+ Nome do ponto de referência/texto
+ Insira o nome do ponto de referência
+ Abrir arquivo
+ Salvar
+ Apagar
+ Cancelar
+ Apagar ponto de referência
+ Apagar este ponto de referência?
+ Apagar
+ CancelarConfiguraçõesPontos de referência
@@ -106,6 +118,14 @@
Por favor confira se o armazenamento externo está corretamente inserido e montado.Gravação de voz falhouErro ao analisar arquivo de layout XML. Por favor, voltar para o layout padrão.
+
+ Permissão necessária
+ Para exportar o trilho GPX, é necessário escrever no armazenamento.
+ Para apresentar corretamente o trilho, é necessário o acesso ao armazenamento.
+ Para partilhar corretamente o trilho, é necessário o acesso ao armazenamento.
+ Para enviar o trilho para o OpenStreetMap, é necessário o acesso ao armazenamento.
+ Aceitar
+ Não é possível continuar sem a permissão do GPSGravado com OSMTracker para Android™Atenção: valores HDOP não são o HDOP como retornado pelo dispositivo GPS. Eles são relativamente precisos na localização em metros.
@@ -139,6 +159,8 @@
Aguardando rumo...O rumo não pôde ser determinadoProcesso de exportação concluído com sucesso
+ Posição não disponível
+ {0} {1} / {2} {3}Exibindo trilhas do OpenStreetMap
diff --git a/app/src/main/res/values-pt-rPT/accessibility.xml b/app/src/main/res/values-pt-rPT/accessibility.xml
index 9e73d1b00..7bec2697f 100644
--- a/app/src/main/res/values-pt-rPT/accessibility.xml
+++ b/app/src/main/res/values-pt-rPT/accessibility.xml
@@ -5,6 +5,7 @@
AmpliarDiminuir
+ CentroIndicador de força do sinal de satéliteIndicador de gravação em curso
diff --git a/app/src/main/res/values-pt-rPT/strings-preferences.xml b/app/src/main/res/values-pt-rPT/strings-preferences.xml
index a308aa636..f8ac2d6ee 100644
--- a/app/src/main/res/values-pt-rPT/strings-preferences.xml
+++ b/app/src/main/res/values-pt-rPT/strings-preferences.xml
@@ -56,7 +56,7 @@
Mostrar o mapa OpenStreetMap por baixo do trilho. Necessita de ligação de dadosMostrar sempre o fundo do OpenStreetMap? Requer uma conexão de dadosConfigurações de GPX
- Pasta de armazenamento externo (SD)
+ Pasta de armazenamento nos documentosAplicável apenas no próximo trilho (não ao atual)Uma pasta por trilhoGuardar cada trilho e os ficheiros relacionados numa pasta dedicada
@@ -65,8 +65,11 @@
Nome do trilhoNome, data e hora de início
+ Data e hora de início, nomeData e hora de início
+ Rótulo do nome do ficheiro
+ Este rótulo será anexado ao final do nome do ficheiroPrecisão no ficheiro GPXAdicionar informação de precisão no ficheiro GPX, com nome do ponto de referência ou no comentário
diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml
index 5d6db13ec..2415d5e55 100644
--- a/app/src/main/res/values-pt-rPT/strings.xml
+++ b/app/src/main/res/values-pt-rPT/strings.xml
@@ -37,8 +37,8 @@
Parar registoContinuar registoEliminar
- Exportar como GPX
- Partilhar GPX
+ Exportar
+ PartilharEnviar para o OpenStreetMapVerDetalhes
@@ -62,6 +62,7 @@
(Ainda não foi exportado)(Ainda não foi enviado)Ver
+ NomeDescriçãoEtiquetas (separadas por vírgulas)Tem de escrever uma descrição
@@ -84,6 +85,17 @@
Gravar vozFotografarNota de texto
+
+ Nome do ponto de referência/texto
+ Introduza o nome do ponto de referência
+ Abrir ficheiro
+ Salvar
+ Eliminar
+ Cancelar
+ Eliminar ponto de referência
+ Eliminar este ponto de referência
+ Eliminar
+ CancelarConfiguraçõesPontos de referência
@@ -106,6 +118,14 @@
Por favor, verifique se o armazenamento externo está inserido e montado corretamente.A gravação de voz falhouOcorreu um erro ao processar o ficheiro de layout XML. Por favor, reverta para o layout padrão.
+
+ Permissão necessária
+ Para exportar o trilho GPX, é necessário escrever no armazenamento.
+ Para apresentar corretamente o trilho, é necessário o acesso ao armazenamento.
+ Para partilhar corretamente o trilho, é necessário o acesso ao armazenamento.
+ Para enviar o trilho para o OpenStreetMap, é necessário o acesso ao armazenamento.
+ Aceitar
+ Não é possível continuar sem a permissão do GPSRegistado com o OSMTracker para Android™Aviso: os valores HDOP não são os valores HDOP indicados pelo GPS. São aproximações dadas pela exatidão da localização em metros.
@@ -139,6 +159,8 @@
Aguarde pela direção…A direção não pode ser determinadaProcesso de exportação concluído com êxito
+ Posição não disponível
+ {0} {1} / {2} {3}Visualização de trilhos OpenStreetMap
diff --git a/app/src/main/res/values-ro-rRO/accessibility.xml b/app/src/main/res/values-ro-rRO/accessibility.xml
deleted file mode 100644
index 6542f214c..000000000
--- a/app/src/main/res/values-ro-rRO/accessibility.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values-ro-rRO/strings-preferences.xml b/app/src/main/res/values-ro-rRO/strings-preferences.xml
deleted file mode 100644
index cc2bd985b..000000000
--- a/app/src/main/res/values-ro-rRO/strings-preferences.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/values-ro-rRO/strings-tags.xml b/app/src/main/res/values-ro-rRO/strings-tags.xml
deleted file mode 100644
index c757504ac..000000000
--- a/app/src/main/res/values-ro-rRO/strings-tags.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/app/src/main/res/values-ro-rRO/strings.xml b/app/src/main/res/values-ro-rRO/strings.xml
deleted file mode 100644
index e779edd5e..000000000
--- a/app/src/main/res/values-ro-rRO/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values-ro-rRO/waypoints.xml b/app/src/main/res/values-ro-rRO/waypoints.xml
deleted file mode 100644
index 42eba3ccd..000000000
--- a/app/src/main/res/values-ro-rRO/waypoints.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/app/src/main/res/values-ru/strings-preferences.xml b/app/src/main/res/values-ru/strings-preferences.xml
index 80716fff4..f8b3b68e6 100644
--- a/app/src/main/res/values-ru/strings-preferences.xml
+++ b/app/src/main/res/values-ru/strings-preferences.xml
@@ -56,7 +56,6 @@
Отображать картооснову OpenStreetMap при отображении трека. Требует интернет-соединенияВсегда показывать OpenStreetMap в фоне? Требуется подключение к сети.Настройки GPX файлов
- Сохранять в каталогеБудет использоваться для записи следующего трека (не для текущего)Отдельная директория для трекаСохранять каждый трек и связанные файлы в отдельную директорию
@@ -65,6 +64,7 @@
Имя трекаИмя, дата и время старта
+ Дата и время старта, ИмяДата и время стартаТочность в GPX файл
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index dd9438efa..5407b9a9a 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -37,8 +37,8 @@
Остановить запись трекаПродолжить запись трекаУдалить
- Экспортировать в GPX
- Поделиться GPX
+ Экспортировать
+ ПоделитьсяЗагрузить на OpenStreetMapПоказатьПодробности
@@ -84,6 +84,7 @@
Запись аудиоСделать фотоЗапись текста
+
НастройкиПутевые точки
@@ -106,6 +107,7 @@
Проверьте, что флеш карта правильно вставлена и примонтирована.Ошибка записи аудиоОшибка при загрузки файла расположения кнопок. Вернитесь к файлу по умолчанию.
+
Записано на OSMTracker for Android™Внимание: значения записываемые в файл как HDOP — это не значения HDOP, получаемые от GPS устройства. Это приближённо вычисленное значение, полученное из метровой точности местоположения.
@@ -139,6 +141,7 @@
Ожидание направления...Направление не может быть определеноПроцесс экспорта успешно завершен
+ {0} {1} / {2} {3}Отображение трека (OSM)
diff --git a/app/src/main/res/values-sk/strings-preferences.xml b/app/src/main/res/values-sk/strings-preferences.xml
index 3161578ec..69fc993d9 100644
--- a/app/src/main/res/values-sk/strings-preferences.xml
+++ b/app/src/main/res/values-sk/strings-preferences.xml
@@ -50,7 +50,6 @@
OSM pozadieZobraziť OpenStreetMap pod stopou. Potrebuje dátové pripojenieVýstup GPX súboru
- Adresár externého úložiska (SD)Použité až v ďalšej stope (nie v aktuálnej)Jeden adresár na stopuUloží každú stopu a priradené súbory do jej vlastného adresára
@@ -59,6 +58,7 @@
Názov stopyNázov, dátum a čas spustenia
+ Dátum a čas spustenia, názovDátum a čas spusteniaPresnosť v GPX súbore
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index b065354fd..c37a8fec4 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -36,7 +36,7 @@
Zastaviť záznamPokračovať v záznameOdstrániť
- Exportovať ako GPX
+ ExportovaťOdoslať na OpenStreetMapZobraziťPodrobnosti
@@ -81,6 +81,7 @@
Hlasový záznamZachytiť fotografiuPísomná poznámka
+
NastaveniaCestovné body
@@ -103,6 +104,7 @@
Prosím, skontrolujte, či je ext. úložisko správne vložené a pripojené.Hlasový záznam zlyhalChyba počas analýzi XML súboru rozloženia. Prosím, vráťte sa k štandardnému rozloženiu.
+
Stopované s OSMTracker-om pre Android™Varovanie: Hodnoty HDOP nie sú vrátené HDOP z GPS zariadenia. Sú odhadnuté pre presnosť umiestnenia v metroch, a nemali by byť vložené do účtu.
@@ -135,6 +137,7 @@
Čaká sa na smerovanie...Nedá sa zistiť smerovanie
+ {0} {1} / {2} {3}Zobrazenie OpenStreetMap stopy
diff --git a/app/src/main/res/values-sq/strings-preferences.xml b/app/src/main/res/values-sq/strings-preferences.xml
deleted file mode 100644
index cc2bd985b..000000000
--- a/app/src/main/res/values-sq/strings-preferences.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/values-sq/strings-tags.xml b/app/src/main/res/values-sq/strings-tags.xml
deleted file mode 100644
index c757504ac..000000000
--- a/app/src/main/res/values-sq/strings-tags.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml
deleted file mode 100644
index e779edd5e..000000000
--- a/app/src/main/res/values-sq/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values-sq/waypoints.xml b/app/src/main/res/values-sq/waypoints.xml
deleted file mode 100644
index c757504ac..000000000
--- a/app/src/main/res/values-sq/waypoints.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/app/src/main/res/values-sr/strings-preferences.xml b/app/src/main/res/values-sr/strings-preferences.xml
index 4ea39211f..fa9a78027 100644
--- a/app/src/main/res/values-sr/strings-preferences.xml
+++ b/app/src/main/res/values-sr/strings-preferences.xml
@@ -56,7 +56,6 @@
Приказуј OpenStreet мапу испод трага. Морате бити повезаниУвек приказати мапу у позадини? Морате бити повезаниGPX поставке
- Директоријум спољног складиштаОдноси се на наредно праћење (не на текуће)Један директоријум по праћењуСачувај свако праћење и његове фајлове у сопствени директоријум
@@ -65,6 +64,7 @@
Назив праћењаНазив, почетни датум и време
+ Почетни датум и време, НазивПочетни датум и времеТачност у GPX фајлу
diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml
index 59c9b3f09..5fb286ae0 100644
--- a/app/src/main/res/values-sr/strings.xml
+++ b/app/src/main/res/values-sr/strings.xml
@@ -37,7 +37,6 @@
Заустави праћењеНастави праћењеОбриши
- Извези као GPXПошаљи на OpenStreetMapПриказДетаљи
@@ -82,6 +81,7 @@
Гласовна белешкаСликајБелешка
+
ПоставкеПролазне тачке
@@ -104,6 +104,7 @@
Проверите да ли је спољно складиште убачено и монтирано.Гласовна белешка није успелаГрешка при читању XML фајла распореда. Вратите на подразумевани распоред.
+
Испраћено са OSM пратиоцем за АндроидПажња: HDOP вредности нису са GPS уређаја већ процењене на основу прецизности локације у метрима.
@@ -136,6 +137,7 @@
Чекам правац...Правац се не може одредити
+ {0} {1} / {2} {3}OpenStreetMap приказ праћења
diff --git a/app/src/main/res/values-sv/accessibility.xml b/app/src/main/res/values-sv/accessibility.xml
index 839a4ad3c..7be58c7ea 100644
--- a/app/src/main/res/values-sv/accessibility.xml
+++ b/app/src/main/res/values-sv/accessibility.xml
@@ -5,6 +5,7 @@
Zooma inZooma ut
+ CentrumIndikator för satellitsignalens styrkaFörloppsindikator för inspelning
diff --git a/app/src/main/res/values-sv/strings-preferences.xml b/app/src/main/res/values-sv/strings-preferences.xml
index b7edd739c..2a098f7ff 100644
--- a/app/src/main/res/values-sv/strings-preferences.xml
+++ b/app/src/main/res/values-sv/strings-preferences.xml
@@ -9,9 +9,13 @@
Kontrollera om GPS:en är inaktiv vid start och erbjud att aktivera den.Ignorera GPS-klockanIgnorera GPS-klockan och använd istället Androids klocka för tidsstämpling.
+ Logga barometertryck [hPa]
+ Växling kräver omstart av spårGPS logg-intervallAnvänd 0 för kortast möjliga intervall (påverkar batteriets livslängd).sekunder
+ GPS-loggningsavstånd
+ Minsta avstånd mellan spårpunkter i meter, använd 0 för kortast möjligameterAnvändargränssnittVald bildkälla
@@ -50,8 +54,9 @@
Skärmen släcks inte under tiden spår spelas in. Avmarkera för att spara batteri.BakgrundskartaVisa OpenStreetMap-karta under spåret. Dataanslutning krävs.
+ Visa alltid OpenStreetMap-bakgrund? Kräver dataanslutningGPX-fil
- Lagringsmapp (SD)
+ Lagringsmapp i dokumentGäller för nästa och kommande spår, inte aktuellt spår.En mapp per spårSpara varje spår och relaterade filer i en egen mapp
@@ -60,8 +65,11 @@
Spårets namnNamn, startdatum och tid
+ Startdatum och tid, namnStartdatum och tid
+ Filnamnsetikett
+ Denna etikett kommer att läggas till i slutet av filnamnetNoggrannhetTillåt noggrannhetsinformation i GPX-filen, i vägpunktsnamnet eller i en egen tagg.
@@ -76,6 +84,7 @@
Återställ inloggning till OSMGlöm bort inloggning och tillstånd och tvinga OSMTracker att fråga om detta igenDu måste godkänna OSMTracker för att kunna ladda upp spår igen. Är du säker?
+ Tillhandahållare av kartrutorMapnik
@@ -87,5 +96,6 @@
i kommentari extension
+ Exportera kompassriktningDefinierar om och hur kompassdata ska exporteras till GPX-filen
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index adb2ad33a..fea72d073 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -31,12 +31,14 @@
Vägpunkter: Spårpunkter: Du har inga spår.
+ Tryck för att spela in ett nytt spår.Kan inte skapa nytt spår: {0}Du spelar just nu in spår #{0}\nVälj det i listan för att fortsättaStoppa inspelningÅteruppta inspelningRadera
- Exportera som GPX
+ Exportera
+ DelaLadda upp till OpenStreetMapVisa spår på skärmVisa detaljer
@@ -47,6 +49,7 @@
Förbereder export...Kan inte exportera spår: {0}Alla spår kommer att exporteras vilket kan ta lång tid. Är du säker?
+ Kan inte hantera spåret: {0}SpårdetaljerStarttid:
@@ -59,6 +62,7 @@
(Inte exporterad ännu)(inte uppladdad än)Visa
+ NamnBeskrivningEtiketter (kommaseparerade)Du måste ange en beskrivning
@@ -81,6 +85,17 @@
RöstnoteringBildnoteringTextnotering
+
+ Waypoint Namn/text
+ Ange waypointnamn
+ Öppna filen
+ Spara
+ Radera
+ Avboka
+ Ta bort waypoint
+ Vill du ta bort den här waypointen?
+ Radera
+ AvbokaInställningarVägpunkter
@@ -103,6 +118,14 @@
Kontrollera att minneskortet är korrekt isatt och fungerar.Röstinspelningen misslyckadesEtt fel inträffade när layoutfilen lästes in. Återställ orginalfilen.
+
+ Tillstånd krävs
+ För att exportera GPX-spåret behöver vi skriva till enhetens filsystem.
+ För att visa spåret korrekt behöver vi åtkomst till filsystemet.
+ För att dela spåret korrekt behöver vi åtkomst till filsystemet.
+ För att ladda upp spåret till OSM behöver vi åtkomst till filsystemet.
+ Acceptera
+ Kan inte fortsätta utan GPS-tillståndSkapad med OSMTracker för Android™Varning: HDOP-värdena är inte riktiga HDOP-värden från GPS-enheten. De är uppskattade värden, angivna i hela metrar, och bör inte användas.
@@ -135,6 +158,9 @@
Väntar på riktning...Riktning kan inte bestämmas
+ Exporteringen slutfördes framgångsrikt
+ Position ej tillgänglig
+ {0} {1} / {2} {3}Spårvisning med OpenStreetMap
@@ -145,8 +171,10 @@
Layout uppdaterades ej, försök igen senareÄr du säker på att du vill ta bort layouten {0}?Ja
+ Layouten raderades framgångsriktikon-katalog har tagits bortDenna fil har inte någon ikon-katalog associerad
+ Layouten kunde inte raderasFel: kunde inte ansluta till internetFel: kunde inte ansluta till standard-repository för anpassade layouter
@@ -168,6 +196,12 @@
Github Repository giltigtGithub Repository felaktigt
+ Introduktion till OSMTracker for Android™
+ Välkommen till OSMTracker for Android™ 👋
+ Den här appen är fri programvara som respekterar din frihet!
+ Lycka till med spårningen 🗺😎
+ OSMTracker för Android kommer att använda din GPS-position för att spela in spårpunkter och vägpunkter, även när appen körs i bakgrunden.
+\nDina data används inte för annonsering.
diff --git a/app/src/main/res/values-ta/strings-preferences.xml b/app/src/main/res/values-ta/strings-preferences.xml
deleted file mode 100644
index cc2bd985b..000000000
--- a/app/src/main/res/values-ta/strings-preferences.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/values-th/accessibility.xml b/app/src/main/res/values-th/accessibility.xml
deleted file mode 100644
index 6542f214c..000000000
--- a/app/src/main/res/values-th/accessibility.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
diff --git a/app/src/main/res/values-th/strings-preferences.xml b/app/src/main/res/values-th/strings-preferences.xml
deleted file mode 100644
index cc2bd985b..000000000
--- a/app/src/main/res/values-th/strings-preferences.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/values-tr/strings-preferences.xml b/app/src/main/res/values-tr/strings-preferences.xml
index 7d2e7c6dc..43e30d240 100644
--- a/app/src/main/res/values-tr/strings-preferences.xml
+++ b/app/src/main/res/values-tr/strings-preferences.xml
@@ -56,7 +56,6 @@
İzin altında OpenStreetMap haritasını görüntüle. Bir veri bağlantısı gerektirirOpenStreetMap arka planı her zaman gösterilsin mi? Bir veri bağlantısı gerektirirGPX ayarları
- Harici depolama (SD) diziniBir sonraki izlemede etkili olacak (Şu ankinde değil)İzleme başına bir dizinHer bir izlemeyi ve ilişkili dosyalarını kendi dizinine kaydedin
@@ -65,6 +64,7 @@
İzleme adıİsim, başlama tarihi ve zamanı
+ Başlama tarihi ve zamanı, İsimBaşlama tarihi ve zamanıGPX dosyasının doğruluğu
diff --git a/app/src/main/res/values-uk/strings-preferences.xml b/app/src/main/res/values-uk/strings-preferences.xml
index 691fe0633..e7133125d 100644
--- a/app/src/main/res/values-uk/strings-preferences.xml
+++ b/app/src/main/res/values-uk/strings-preferences.xml
@@ -50,7 +50,6 @@
Фонова мапаПоказує трек поверх мапи OpenStreetMap. Вимагає з\'єднання з інтернетомНалаштування GPX
- Тека зовнішнього носія (SD)Буде використовуватися для запису наступного треку (не для поточного)Окрема тека для кожного трекуЗберігати кожен трек і пов\'язані файли в окрему теку
@@ -59,7 +58,8 @@
Ім\'я треку Ім\'я, дата та час старту
- Дата і час старту
+ Дата і час старту, Ім\'я
+ Дата і час старту Точність у GPX файліЗаписувати інформацію про точність в GPX файл, в назву дорожньої точки, або окремим тегом
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 6ce676f75..d903868e2 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -36,7 +36,6 @@
Зупинити записПродовжити записВилучити
- Експортувати як GPXЗавантажити на OpenStreetMapПоказатиПодробиці
@@ -81,6 +80,7 @@
Запис звукуЗробити фотоЗапис тексту
+
НалаштуванняДорожні точки
@@ -103,6 +103,7 @@
Перевірте, чи зовнішній носій вірно вставлений та змонтованийПомилка запису аудіоПомилка розбору файлу розмітки XML. Верніться до типової розмітки.
+
Записано на OSMTracker для Android™Увага: значення HDOP - це не значення HDOP, одержані від GPS пристрою. Це наближено обчислені значення, з метровою точністю.
@@ -135,6 +136,7 @@
Очікуємо на напрямок…Напрямок не можна визначити
+ {0} {1} / {2} {3}Показ треку з OpenStreetMap
diff --git a/app/src/main/res/values-zh-rCN/strings-preferences.xml b/app/src/main/res/values-zh-rCN/strings-preferences.xml
index 984734c26..d8a93299a 100644
--- a/app/src/main/res/values-zh-rCN/strings-preferences.xml
+++ b/app/src/main/res/values-zh-rCN/strings-preferences.xml
@@ -50,17 +50,11 @@
背景地图在轨迹下显示OpenStreetMap。需要数据连接GPX设置
- 外部存储设备(SD卡)路径下次追踪生效(本次无效)每次追踪新建一个目录分别将每条追踪记录及相关文件到各自目录已命名轨迹的文件名文件名格式(如果轨迹有名称)
-
- 轨迹名称
- 名称,起始日期与时间
- 起始日期与时间
- GPX文件精度将精度信息连同路径点名或路径点注释添加至GPX文件
diff --git a/app/src/main/res/values-zh-rTW/accessibility.xml b/app/src/main/res/values-zh-rTW/accessibility.xml
index d62287f11..23432e6fb 100644
--- a/app/src/main/res/values-zh-rTW/accessibility.xml
+++ b/app/src/main/res/values-zh-rTW/accessibility.xml
@@ -5,6 +5,7 @@
放大縮小
+ 縮放中心訊號強度正在記錄
diff --git a/app/src/main/res/values-zh-rTW/strings-preferences.xml b/app/src/main/res/values-zh-rTW/strings-preferences.xml
index 954a1102f..89ef7b2d2 100644
--- a/app/src/main/res/values-zh-rTW/strings-preferences.xml
+++ b/app/src/main/res/values-zh-rTW/strings-preferences.xml
@@ -56,7 +56,7 @@
在軌跡底下顯示 OpenStreetMap 地圖。本功能需有可用的網路連結以傳輸資料保持顯示 OpenStreetMap 底圖?此功能需要連線GPX 相關設定
- 外部儲存媒體 (SD) 資料夾位置
+ 文件當中的儲存資料夾設定值將在記錄下一筆軌跡資料時生效 (不是目前這筆)給每筆軌跡各配置一個專屬資料夾將每筆軌跡以及相關的檔案一起放在專屬的資料夾當中
@@ -64,9 +64,12 @@
軌跡資料檔的命名樣式軌跡名稱
- 軌跡名稱,起始日期與時間
- 起始日期與時間
+ 名稱、開始日期與時間
+ 開始日期與時間、名稱
+ 開始日期與時間
+ 檔案名稱標籤
+ 這個標籤會附在檔案名稱的結尾在 GPX 檔中附加精確度資訊將精確度資訊添加於 GPX 檔中的航點名稱,或是航點註解中
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index 3512b5f00..80f5fbc82 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -37,8 +37,8 @@
停止記錄繼續記錄刪除
- 匯出 GPX 檔
- 分享 GPX 檔案
+ 匯出
+ 分享上傳至 OpenStreetMap顯示詳細資訊
@@ -62,6 +62,7 @@
(尚未匯出)(尚未上傳)顯示
+ 名稱描述標籤 (以半形逗號分隔)您必須填寫描述欄
@@ -84,6 +85,17 @@
錄製語音拍照文字筆記
+
+ 路徑節點名稱/文字
+ 輸入路徑節點名稱
+ 開啟檔案
+ 儲存
+ 刪除
+ 取消
+ 刪除路徑節點
+ 刪除這個路徑節點?
+ 刪除
+ 取消設定航點
@@ -106,6 +118,14 @@
請檢查外部儲存媒體是否正確安置與掛載。語音錄製失敗解析 XML 外觀配置檔時發生錯誤。請改用預設外觀。
+
+ 需要權限
+ 要匯出 GPS 軌跡則需寫入儲存裝置的權限。
+ 要顯示軌跡的譯則需要儲存裝置的權限。
+ 要分享軌跡的話則需要儲存裝置的權限
+ 要上傳軌跡到 OSM 則需要儲存裝置的權限。
+ 接受
+ 沒有 GPS 權限則無法繼續使用 OSMTracker for Android™ 記錄警告:HDOP值並非GPS裝置回報的HDOP,HDOP值會與實際地點差大約幾公尺。
@@ -139,6 +159,8 @@
等待方位…無法偵測方位成功完成匯出程序
+ 無法取得位置
+ {0} {1} / {2} {3}OpenStreetMap 軌跡畫面
diff --git a/app/src/main/res/values/accessibility.xml b/app/src/main/res/values/accessibility.xml
index 430489701..8b6f1e6c3 100644
--- a/app/src/main/res/values/accessibility.xml
+++ b/app/src/main/res/values/accessibility.xml
@@ -7,6 +7,7 @@
Zoom inZoom out
+ Zoom centerSatellite signal strength indicator
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 36ef822f8..aeb96232d 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -8,6 +8,11 @@
#39796b#ffffff
+
+ #000000
+ #19303234
+ #303234
+
diff --git a/app/src/main/res/values/strings-preferences.xml b/app/src/main/res/values/strings-preferences.xml
index 685476434..828793888 100644
--- a/app/src/main/res/values/strings-preferences.xml
+++ b/app/src/main/res/values/strings-preferences.xml
@@ -73,7 +73,7 @@
Always display OpenStreetMap background? Requires a data connectionGPX settings
- External storage (SD) directory
+ Storage folder in documentsEffective for the next track (not the current one)One directory per trackSave each track and associated files to its own directory
@@ -82,9 +82,12 @@
Track nameName, start date and time
+ Start date and time, nameStart date and time
-
+ Filename label
+ This label will be appended at the end of filename
+
Accuracy in GPX fileAdd accuracy info in the GPX file, with waypoint name or in waypoint comment
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 42f3bc271..dfc879ac9 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -40,8 +40,8 @@
Stop trackingResume trackingDelete
- Export as GPX
- Share GPX
+ Export
+ ShareUpload to OpenStreetMapDisplayDetails
@@ -66,6 +66,7 @@
(Not exported yet)(Not uploaded yet)Display
+ NameDescriptionTags (comma separated)You must enter a description
@@ -83,7 +84,7 @@
Waiting for OpenStreetMap server response…Error while uploading trackThe OSM server returned an error: ({0}) message {1}
- Autorization error. Would you like to clear the saved OpenStreetMap credentials?
+ Authorization error. Would you like to clear the saved OpenStreetMap credentials?OpenStreetMap upload succeeded
@@ -91,6 +92,18 @@
Take photoText note
+
+ Waypoint Name/text
+ Enter waypoint name
+ Open file
+ Save
+ Delete
+ Cancel
+ Delete waypoint
+ Delete this waypoint?
+ Delete
+ Cancel
+
SettingsWaypoints
@@ -115,6 +128,15 @@
Voice recording has failedError while parsing XML layout file. Please revert to default layout.
+
+ Permission required
+ To export the GPX trace we need to write on the storage.
+ To display the track properly we need access to the storage.
+ To share the track properly we need access to the storage.
+ To upload the track to OSM we need access to the storage.
+ Accept
+ Can\'t continue without GPS permission
+
Tracked with OSMTracker for Android™Warning: HDOP values aren\'t the HDOP as returned by the GPS device. They\'re approximated from the location accuracy in meters.
@@ -150,7 +172,8 @@
Wait for heading…Heading can\'t be determinedExport process finished successfully
-
+ Position not available
+ {0} {1} / {2} {3}OpenStreetMap track display
diff --git a/app/src/main/res/values/values-preferences.xml b/app/src/main/res/values/values-preferences.xml
index ae34c3e4b..6aae963a7 100644
--- a/app/src/main/res/values/values-preferences.xml
+++ b/app/src/main/res/values/values-preferences.xml
@@ -33,6 +33,7 @@
namename_date
+ date_namedate
diff --git a/app/src/main/res/values/values.xml b/app/src/main/res/values/values.xml
index 91590768b..18fd850ac 100644
--- a/app/src/main/res/values/values.xml
+++ b/app/src/main/res/values/values.xml
@@ -2,6 +2,6 @@
https://github.com/labexp/osmtracker-android
- https://www.transifex.com/projects/p/osmtracker-android/
+ https://explore.transifex.com/labexp/osmtracker-android/
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index 55bb33f7d..dc1749d09 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -1,11 +1,15 @@
-
+
+
+ android:entries="@array/prefs_voicerec_durations"/>
+
-
+
-
-
-
diff --git a/app/src/test/assets/gpx/gpx-test.gpx b/app/src/test/assets/gpx/gpx-test.gpx
new file mode 100644
index 000000000..5509a41ec
--- /dev/null
+++ b/app/src/test/assets/gpx/gpx-test.gpx
@@ -0,0 +1,45 @@
+
+
+
+ 5812.2
+
+
+
+ http://link1.com
+
+ 2
+ 0.0625
+
+
+ 75.4
+
+
+
+ http://link2.com
+
+ 6
+ 0.1525000035762787
+
+
+
+
+
+
+ 4321.7
+
+ 0.10499999672174454
+
+ 45.79999923706055
+
+
+
+ 12.1
+
+ 0.05999999865889549
+
+ 12.600000381469727
+
+
+
+
+
diff --git a/app/src/test/java/net/osmtracker/gpx/ExportToStorageTaskTest.java b/app/src/test/java/net/osmtracker/gpx/ExportToStorageTaskTest.java
new file mode 100644
index 000000000..2bd6f8775
--- /dev/null
+++ b/app/src/test/java/net/osmtracker/gpx/ExportToStorageTaskTest.java
@@ -0,0 +1,354 @@
+package net.osmtracker.gpx;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNull;
+import static net.osmtracker.OSMTracker.Preferences;
+import static net.osmtracker.OSMTracker.Preferences.KEY_OUTPUT_FILENAME;
+import static net.osmtracker.OSMTracker.Preferences.VAL_OUTPUT_FILENAME;
+import static net.osmtracker.db.TrackContentProvider.Schema;
+import static net.osmtracker.util.UnitTestUtils.createDateFrom;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.powermock.api.mockito.PowerMockito.mock;
+import static org.powermock.api.mockito.PowerMockito.mockStatic;
+import static org.powermock.api.mockito.PowerMockito.when;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.os.Environment;
+import android.preference.PreferenceManager;
+
+import net.osmtracker.OSMTracker;
+import net.osmtracker.R;
+import net.osmtracker.db.DataHelper;
+import net.osmtracker.db.model.Track;
+import net.osmtracker.exception.ExportTrackException;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.powermock.core.classloader.annotations.PowerMockIgnore;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import java.io.File;
+import java.util.Date;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({Environment.class, PreferenceManager.class})
+@PowerMockIgnore("jdk.internal.reflect.*")
+public class ExportToStorageTaskTest {
+
+ @Rule
+ private final TemporaryFolder temporaryFolder = new TemporaryFolder();
+ private final Context mockContext = mock(Context.class);
+ private final DataHelper mockDataHelper = mock(DataHelper.class);
+ private final SharedPreferences mockPrefs = mock(SharedPreferences.class);
+ private final Resources mockResources = mock(Resources.class);
+
+ private ExportToStorageTask task;
+
+ private static final String ERROR_CREATE_TRACK_DIR = "Error creating track directory";
+ private static final String UNABLE_TO_WRITE_STORAGE = "Unable to write to external storage";
+
+ @Before
+ public void setUp() {
+ mockStatic(Environment.class);
+ mockStatic(PreferenceManager.class);
+
+ when(PreferenceManager.getDefaultSharedPreferences(mockContext)).thenReturn(mockPrefs);
+ when(mockContext.getResources()).thenReturn(mockResources);
+ when(mockResources.getString(R.string.error_create_track_dir)).thenReturn(ERROR_CREATE_TRACK_DIR);
+ when(mockResources.getString(R.string.error_externalstorage_not_writable)).thenReturn(UNABLE_TO_WRITE_STORAGE);
+
+ task = new ExportToStorageTask(mockContext, mockDataHelper, 1L);
+ }
+
+ @After
+ public void tearDown() {
+ temporaryFolder.delete();
+ }
+
+ @Test
+ public void testBuildGPXFilenameUsingOnlyTrackName() {
+ String trackNameInDatabase = "MyTrack";
+ Date trackStartDate = createDateFrom(2000, 1, 2, 3, 4, 5);
+ String preferenceSetting = Preferences.VAL_OUTPUT_FILENAME_NAME;
+
+ String expectedFilename = "MyTrack";
+ if(!(Preferences.VAL_OUTPUT_FILENAME_LABEL.equals("")))expectedFilename += "_";
+ expectedFilename += Preferences.VAL_OUTPUT_FILENAME_LABEL+".gpx";
+
+ doTestBuildGPXFilename(trackNameInDatabase, preferenceSetting, trackStartDate.getTime(), expectedFilename);
+ }
+
+ @Test
+ public void testBuildGPXFilenameUsingTrackNameAndStartDate() {
+ String trackNameInDatabase = "MyTrack";
+ Date trackStartDate = createDateFrom(2000, 1, 2, 3, 4, 5);
+ String preferenceSetting = Preferences.VAL_OUTPUT_FILENAME_NAME_DATE;
+
+ String expectedFilename = "MyTrack_2000-01-02_03-04-05";
+ if(!(Preferences.VAL_OUTPUT_FILENAME_LABEL.equals(""))) expectedFilename += "_";
+ expectedFilename += Preferences.VAL_OUTPUT_FILENAME_LABEL+".gpx";
+
+ doTestBuildGPXFilename(trackNameInDatabase, preferenceSetting, trackStartDate.getTime(), expectedFilename);
+ }
+
+ @Test
+ public void testBuildGPXFilenameUsingStartDateAndTrackName() {
+ String trackNameInDatabase = "MyTrack";
+ Date trackStartDate = createDateFrom(2000, 1, 2, 3, 4, 5);
+ String preferenceSetting = Preferences.VAL_OUTPUT_FILENAME_DATE_NAME;
+
+ String expectedFilename = "2000-01-02_03-04-05_MyTrack";
+ if(!(Preferences.VAL_OUTPUT_FILENAME_LABEL.equals(""))) expectedFilename += "_";
+ expectedFilename += Preferences.VAL_OUTPUT_FILENAME_LABEL+".gpx";
+
+ doTestBuildGPXFilename(trackNameInDatabase, preferenceSetting, trackStartDate.getTime(), expectedFilename);
+ }
+
+ @Test
+ public void testBuildGPXFilenameUsingOnlyStartDate() {
+ String trackNameInDatabase = "MyTrack";
+ Date trackStartDate = createDateFrom(2000, 1, 2, 3, 4, 5);
+ String preferenceSetting = Preferences.VAL_OUTPUT_FILENAME_DATE;
+
+ String expectedFilename = "2000-01-02_03-04-05";
+ if(!(Preferences.VAL_OUTPUT_FILENAME_LABEL.equals(""))) expectedFilename += "_";
+ expectedFilename += Preferences.VAL_OUTPUT_FILENAME_LABEL+".gpx";
+
+ doTestBuildGPXFilename(trackNameInDatabase, preferenceSetting, trackStartDate.getTime(), expectedFilename);
+ }
+
+ @Test
+ public void testBuildGPXFilenameWhenSanitizesTrackName() {
+ String trackNameInDatabase = ":M/y*T@r~a\\c?k:";
+ Date trackStartDate = createDateFrom(2000, 1, 2, 3, 4, 5);
+ String preferenceSetting = Preferences.VAL_OUTPUT_FILENAME_NAME;
+
+ String expectedFilename = ";M_y_T_r_a_c_k;";
+ if(!(Preferences.VAL_OUTPUT_FILENAME_LABEL.equals(""))) expectedFilename += "_";
+ expectedFilename += Preferences.VAL_OUTPUT_FILENAME_LABEL+".gpx";
+
+ doTestBuildGPXFilename(trackNameInDatabase, preferenceSetting, trackStartDate.getTime(), expectedFilename);
+ }
+
+ @Test
+ public void testBuildGPXFilenameWhenUsesTrackNameButThereIsNoName() {
+ String trackNameInDatabase = "";
+ Date trackStartDate = createDateFrom(2000, 1, 2, 3, 4, 5);
+ String preferenceSetting = Preferences.VAL_OUTPUT_FILENAME_NAME;
+
+ String expectedFilename = "2000-01-02_03-04-05";
+ if(!(Preferences.VAL_OUTPUT_FILENAME_LABEL.equals(""))) expectedFilename += "_";
+ expectedFilename += Preferences.VAL_OUTPUT_FILENAME_LABEL+".gpx";
+
+ doTestBuildGPXFilename(trackNameInDatabase, preferenceSetting, trackStartDate.getTime(), expectedFilename);
+ }
+
+ @Test
+ public void testBuildGPXFilenameWhenUsesTrackNameAndStartDateButThereIsNoName() {
+ String trackNameInDatabase = "";
+ Date trackStartDate = createDateFrom(2000, 1, 2, 3, 4, 5);
+ String preferenceSetting = Preferences.VAL_OUTPUT_FILENAME_NAME_DATE;
+
+ String expectedFilename = "2000-01-02_03-04-05";
+ if(!(Preferences.VAL_OUTPUT_FILENAME_LABEL.equals(""))) expectedFilename += "_";
+ expectedFilename += Preferences.VAL_OUTPUT_FILENAME_LABEL+".gpx";
+
+ doTestBuildGPXFilename(trackNameInDatabase, preferenceSetting, trackStartDate.getTime(), expectedFilename);
+ }
+
+ private void doTestBuildGPXFilename(String trackName, String desiredFormat, long trackStartDate, String expectedFilename) {
+ when(mockPrefs.getString(KEY_OUTPUT_FILENAME, VAL_OUTPUT_FILENAME)).thenReturn(desiredFormat);
+
+ String result = task.buildGPXFilename(createMockCursor(trackName, trackStartDate), temporaryFolder.getRoot());
+
+ assertEquals(expectedFilename, result);
+ }
+
+ private Cursor createMockCursor(String trackName, long trackStartDate) {
+ Cursor mockCursor = mock(Cursor.class);
+ when(mockCursor.getColumnIndex(Schema.COL_NAME)).thenReturn(1);
+ when(mockCursor.getString(1)).thenReturn(trackName);
+
+ when(mockCursor.getColumnIndex(Schema.COL_START_DATE)).thenReturn(2);
+ when(mockCursor.getLong(2)).thenReturn(trackStartDate);
+
+ return mockCursor;
+ }
+
+ @Test
+ public void testGetExportDirectoryWhenStorageIsWritableAndDirExists() throws Exception {
+ // Mocking external storage state
+ when(Environment.getExternalStorageState()).thenReturn(Environment.MEDIA_MOUNTED);
+
+ // Mocking preferences and context
+ when(mockPrefs.getString(any(), any())).thenReturn(OSMTracker.Preferences.VAL_STORAGE_DIR);
+ when(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)).thenReturn(temporaryFolder.getRoot());
+
+ var osmTrackerFolder = temporaryFolder.newFolder("osmtracker");
+
+ // Creating task and invoking method
+ File exportDirectory = task.getExportDirectory(new Date());
+
+ // Verifying the directory path
+ assertEquals(osmTrackerFolder.getAbsolutePath(), exportDirectory.getAbsolutePath());
+ }
+
+ @Test
+ public void testGetExportDirectoryWhenStorageIsNotWritable() {
+ // Mocking external storage state
+ when(Environment.getExternalStorageState()).thenReturn(Environment.MEDIA_MOUNTED_READ_ONLY);
+
+ // Verifying the exception
+ assertThrows(ExportTrackException.class, () -> task.getExportDirectory(new Date()));
+ }
+
+ @Test
+ public void testGetExportDirectoryWhenStorageIsWritableAndDirNotExists() throws Exception {
+ // Mocking external storage state
+ when(Environment.getExternalStorageState()).thenReturn(Environment.MEDIA_MOUNTED);
+
+ // Mocking preferences and context
+ when(mockPrefs.getString(any(), any())).thenReturn(OSMTracker.Preferences.VAL_STORAGE_DIR);
+ when(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)).thenReturn(temporaryFolder.getRoot());
+
+ // Creating task and invoking method
+ File exportDirectory = task.getExportDirectory(new Date());
+
+ var osmTrackerFolder = new File(temporaryFolder.getRoot(), OSMTracker.Preferences.VAL_STORAGE_DIR);
+
+ // Verifying the directory path
+ assertEquals(osmTrackerFolder.getAbsolutePath(), exportDirectory.getAbsolutePath());
+ }
+
+ @Test
+ public void testGetExportDirectoryWhenDirDoesNotExistAndCreatesIt() throws Exception {
+ // Mocking external storage state
+ when(Environment.getExternalStorageState()).thenReturn(Environment.MEDIA_MOUNTED);
+
+ // Mocking preferences and context
+ when(mockPrefs.getString(any(), any())).thenReturn("NonExistentDir");
+
+ // Creating task and invoking method
+ File exportDirectory = task.getExportDirectory(new Date());
+
+ // Verifying the directory creation
+ assertEquals(new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), "NonExistentDir").getAbsolutePath(), exportDirectory.getAbsolutePath());
+ }
+
+ @Test
+ public void testGetSanitizedTrackNameByStartDateWithValidTrackName() {
+ // Mock track data
+ Track mockTrack = new Track();
+ mockTrack.setName("My/Track");
+ when(mockDataHelper.getTrackByStartDate(any(Date.class))).thenReturn(mockTrack);
+
+ // Execute the method
+ String result = task.getSanitizedTrackNameByStartDate(new Date());
+
+ // Verify the sanitized track name
+ assertEquals("My_Track", result);
+ }
+
+ @Test
+ public void testGetSanitizedTrackNameByStartDateWithEmptyTrackName() {
+ // Mock track data
+ Track mockTrack = new Track();
+ mockTrack.setName("");
+ when(mockDataHelper.getTrackByStartDate(any(Date.class))).thenReturn(mockTrack);
+
+ // Execute the method
+ String result = task.getSanitizedTrackNameByStartDate(new Date());
+
+ // Verify the sanitized track name
+ assertEquals("", result);
+ }
+
+ @Test
+ public void testGetSanitizedTrackNameByStartDateWithNullTrackName() {
+ // Mock track data
+ Track mockTrack = new Track();
+ mockTrack.setName(null);
+ when(mockDataHelper.getTrackByStartDate(any(Date.class))).thenReturn(mockTrack);
+
+ // Execute the method
+ String result = task.getSanitizedTrackNameByStartDate(new Date());
+
+ // Verify the sanitized track name
+ assertNull(result);
+ }
+
+ @Test
+ public void testGetSanitizedTrackNameByStartDateWithSpecialCharacters() {
+ // Mock track data
+ Track mockTrack = new Track();
+ mockTrack.setName("/M/y/T/r/@/c/k/");
+ when(mockDataHelper.getTrackByStartDate(any(Date.class))).thenReturn(mockTrack);
+
+ // Execute the method
+ String result = task.getSanitizedTrackNameByStartDate(new Date());
+
+ // Verify the sanitized track name
+ assertEquals("_M_y_T_r_@_c_k_", result);
+ }
+
+ @Test
+ public void testGetSanitizedTrackNameByStartDateWithNoTrackFound() {
+ // Mock no track data
+ when(mockDataHelper.getTrackByStartDate(any(Date.class))).thenReturn(null);
+
+ // Execute the method
+ String result = task.getSanitizedTrackNameByStartDate(new Date());
+
+ // Verify the sanitized track name
+ assertEquals("", result);
+ }
+
+ @Test
+ public void testConstructorCallsSuperclassConstructor() {
+ long trackId = 1L;
+
+ // Use a spy to verify the constructor call
+ var taskSpy = new ExportToStorageTask(mockContext, trackId);
+
+ assertTrue(taskSpy.exportMediaFiles());
+ assertTrue(taskSpy.updateExportDate());
+ }
+
+ @Test
+ public void testCreateDirectoryPerTrack() throws Exception{
+ when(Environment.getExternalStorageState()).thenReturn(Environment.MEDIA_MOUNTED);
+ when(mockPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(true);
+ when(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)).thenReturn(temporaryFolder.getRoot());
+ when(mockPrefs.getString(any(), any())).thenReturn(OSMTracker.Preferences.VAL_STORAGE_DIR);
+ Track mockTrack = new Track();
+ mockTrack.setName("MyTrack");
+ when(mockDataHelper.getTrackByStartDate(any(Date.class))).thenReturn(mockTrack);
+
+ task.getExportDirectory(new Date());
+ }
+
+ @Test
+ public void testCreateDirectoryPerTrackEmptyTrackname() throws Exception{
+ when(Environment.getExternalStorageState()).thenReturn(Environment.MEDIA_MOUNTED);
+ when(mockPrefs.getBoolean(anyString(), anyBoolean())).thenReturn(true);
+ when(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)).thenReturn(temporaryFolder.getRoot());
+ when(mockPrefs.getString(any(), any())).thenReturn(OSMTracker.Preferences.VAL_STORAGE_DIR);
+ Track mockTrack = new Track();
+ mockTrack.setName("");
+ when(mockDataHelper.getTrackByStartDate(any(Date.class))).thenReturn(mockTrack);
+
+ task.getExportDirectory(new Date());
+ }
+}
diff --git a/app/src/test/java/net/osmtracker/gpx/ExportTrackTest.java b/app/src/test/java/net/osmtracker/gpx/ExportTrackTest.java
deleted file mode 100644
index 29d9190e5..000000000
--- a/app/src/test/java/net/osmtracker/gpx/ExportTrackTest.java
+++ /dev/null
@@ -1,159 +0,0 @@
-package net.osmtracker.gpx;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.database.Cursor;
-import android.preference.PreferenceManager;
-
-import net.osmtracker.R;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.junit.runner.RunWith;
-import org.mockito.Mockito;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
-
-import java.util.Date;
-
-import static junit.framework.TestCase.assertEquals;
-import static net.osmtracker.db.TrackContentProvider.Schema;
-import static net.osmtracker.OSMTracker.Preferences.KEY_OUTPUT_FILENAME;
-import static net.osmtracker.OSMTracker.Preferences.VAL_OUTPUT_FILENAME;
-import static org.powermock.api.mockito.PowerMockito.mock;
-import static org.powermock.api.mockito.PowerMockito.mockStatic;
-import static org.powermock.api.mockito.PowerMockito.when;
-import static net.osmtracker.util.UnitTestUtils.createDateFrom;
-import static net.osmtracker.OSMTracker.Preferences;
-
-@RunWith(PowerMockRunner.class)
-@PrepareForTest({ PreferenceManager.class })
-//https://stackoverflow.com/questions/52966897/powermock-java-11
-@PowerMockIgnore("jdk.internal.reflect.*")
-public class ExportTrackTest {
-
- @Rule
- public TemporaryFolder temporaryFolder = new TemporaryFolder();
-
- ExportToStorageTask task;
- Context mockContext = Mockito.mock(Context.class);
-
-
- @Test
- public void testBuildGPXFilenameUsingOnlyTrackName() {
- // Method parameters
- String trackNameInDatabase = "MyTrack";
- Date trackStartDate = createDateFrom(2000, 1, 2, 3, 4, 5);
- String preferenceSetting = Preferences.VAL_OUTPUT_FILENAME_NAME;
-
- String expectedFilename = "MyTrack.gpx";
-
- doTestBuildGPXFilename(trackNameInDatabase, preferenceSetting, trackStartDate.getTime(), expectedFilename);
-
- }
-
- @Test
- public void testBuildGPXFilenameUsingTrackNameAndStartDate() {
- // Method parameters
- String trackNameInDatabase = "MyTrack";
- Date trackStartDate = createDateFrom(2000, 1, 2, 3, 4, 5);
- String preferenceSetting = Preferences.VAL_OUTPUT_FILENAME_NAME_DATE;
-
- String expectedFilename = "MyTrack_2000-01-02_03-04-05.gpx";
-
- doTestBuildGPXFilename(trackNameInDatabase, preferenceSetting, trackStartDate.getTime(), expectedFilename);
- }
-
- @Test
- public void testBuildGPXFilenameUsingOnlyStartDate() {
- // Method parameters
- String trackNameInDatabase = "MyTrack";
- Date trackStartDate = createDateFrom(2000, 1, 2, 3, 4, 5);
- String preferenceSetting = Preferences.VAL_OUTPUT_FILENAME_DATE;
-
- String expectedFilename = "2000-01-02_03-04-05.gpx";
-
- doTestBuildGPXFilename(trackNameInDatabase, preferenceSetting, trackStartDate.getTime(), expectedFilename);
- }
-
- @Test
- public void testBuildGPXFilenameWhenSanitizesTrackName() {
- // Method parameters
- String trackNameInDatabase = ":M/y*T@r~a\\c?k:";
- Date trackStartDate = createDateFrom(2000, 1, 2, 3, 4, 5);
- String preferenceSetting = Preferences.VAL_OUTPUT_FILENAME_NAME;
-
- String expectedFilename = ";M_y_T_r_a_c_k;.gpx";
-
- doTestBuildGPXFilename(trackNameInDatabase, preferenceSetting, trackStartDate.getTime(), expectedFilename);
- }
-
- @Test
- public void testBuildGPXFilenameWhenUsesTrackNameButThereIsNoName() {
- // Method parameters
- String trackNameInDatabase = "";
- Date trackStartDate = createDateFrom(2000, 1, 2, 3, 4, 5);
- String preferenceSetting = Preferences.VAL_OUTPUT_FILENAME_NAME;
-
- String expectedFilename = "2000-01-02_03-04-05.gpx"; // Must fallback to use the start date
-
- doTestBuildGPXFilename(trackNameInDatabase, preferenceSetting, trackStartDate.getTime(), expectedFilename);
- }
-
- @Test
- public void testBuildGPXFilenameWhenUsesTrackNameAndStartDateButThereIsNoName() {
- // Method parameters
- String trackNameInDatabase = "";
- Date trackStartDate = createDateFrom(2000, 1, 2, 3, 4, 5);
- String preferenceSetting = Preferences.VAL_OUTPUT_FILENAME_NAME_DATE;
-
- String expectedFilename = "2000-01-02_03-04-05.gpx"; // Must fallback to use the start date
-
- doTestBuildGPXFilename(trackNameInDatabase, preferenceSetting, trackStartDate.getTime(), expectedFilename);
- }
-
-
- void doTestBuildGPXFilename(String trackName, String desiredFormat, long trackStartDate,
- String expectedFilename) {
- setupPreferencesToReturn(desiredFormat);
-
- when(mockContext.getString(R.string.error_create_track_dir)).thenReturn("Any");
-
- task = new ExportToStorageTask(mockContext, 3);
-
- String result = task.buildGPXFilename(createMockCursor(trackName, trackStartDate), temporaryFolder.getRoot());
-
- assertEquals(expectedFilename, result);
- }
-
-
- // Used for testing buildGPXFilename
- void setupPreferencesToReturn(String desiredFormat) {
- // Mock preferences
- SharedPreferences mockPrefs = mock(SharedPreferences.class);
- when(mockPrefs.getString(KEY_OUTPUT_FILENAME, VAL_OUTPUT_FILENAME)).thenReturn(desiredFormat);
-
- mockStatic(PreferenceManager.class);
-
- when(PreferenceManager.getDefaultSharedPreferences(mockContext)).thenReturn(mockPrefs);
- }
-
-
- // Used for testing buildGPXFilename
- Cursor createMockCursor(String trackName, long trackStartDate){
- Cursor mockCursor = Mockito.mock(Cursor.class);
- when(mockCursor.getColumnIndex(Schema.COL_NAME)).thenReturn(1);
- when(mockCursor.getString(1)).thenReturn(trackName);
-
- when(mockCursor.getColumnIndex(Schema.COL_START_DATE)).thenReturn(2);
- when(mockCursor.getLong(2)).thenReturn(trackStartDate);
-
- return mockCursor;
-
- }
-
-
-
-}
diff --git a/app/src/test/java/net/osmtracker/util/ArrayUtilsTest.java b/app/src/test/java/net/osmtracker/util/ArrayUtilsTest.java
index 871ce9fe9..6e7060e8d 100644
--- a/app/src/test/java/net/osmtracker/util/ArrayUtilsTest.java
+++ b/app/src/test/java/net/osmtracker/util/ArrayUtilsTest.java
@@ -1,31 +1,36 @@
package net.osmtracker.util;
-import org.junit.Test;
+import static org.junit.Assert.assertTrue;
-import static org.hamcrest.CoreMatchers.is;
-import static org.junit.Assert.*;
+import org.junit.Test;
public class ArrayUtilsTest {
- double data[][] = {
- {1,1,1},
- {2,2,2},
- {3,3,3}
- };
-
- @Test
- public void findMin() {
-
- double min = ArrayUtils.findMin(data, 0);
- assertTrue(min == 1);
-
- }
-
- @Test
- public void findMax() {
-
- double max = ArrayUtils.findMax(data, 0);
- assertTrue(max == 3);
-
- }
+ double[][] arrayAsc = {{1, 1, 1}, {2, 2, 2}, {3, 3, 3}};
+
+ double[][] arrayDesc = {{3, 3, 3}, {2, 2, 2}, {1, 1, 1}};
+
+ @Test
+ public void findMinAsc() {
+ double min = ArrayUtils.findMin(arrayAsc, 0);
+ assertTrue(min == 1);
+ }
+
+ @Test
+ public void findMinDesc() {
+ double min = ArrayUtils.findMin(arrayDesc, 2);
+ assertTrue(min == 1);
+ }
+
+ @Test
+ public void findMaxAsc() {
+ double max = ArrayUtils.findMax(arrayAsc, 0);
+ assertTrue(max == 3);
+ }
+
+ @Test
+ public void findMaxDesc() {
+ double max = ArrayUtils.findMax(arrayDesc, 2);
+ assertTrue(max == 3);
+ }
}
\ No newline at end of file
diff --git a/app/src/test/java/net/osmtracker/util/CustomLayoutsUtilsTest.java b/app/src/test/java/net/osmtracker/util/CustomLayoutsUtilsTest.java
index d78e68f02..0d0a2efaa 100644
--- a/app/src/test/java/net/osmtracker/util/CustomLayoutsUtilsTest.java
+++ b/app/src/test/java/net/osmtracker/util/CustomLayoutsUtilsTest.java
@@ -13,9 +13,12 @@
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
+import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
import java.util.Scanner;
import static org.junit.Assert.assertEquals;
@@ -85,17 +88,22 @@ public void createFileName() {
@Test
public void getStringFromStream() throws IOException {
setupMocks();
- InputStream is = mockAssetManager.open("result.gpx");
- String result = CustomLayoutsUtils.getStringFromStream(is);
- is.close();
- is = mockAssetManager.open("expected.gpx");
- Scanner s = new Scanner(is).useDelimiter("\\A");
- String expected = s.hasNext() ? s.next() : "";
- is.close();
- System.out.println(expected);
-
- assertEquals(expected, result);
+ InputStream resultIs = mockAssetManager.open("result.gpx");
+ String result = CustomLayoutsUtils.getStringFromStream(resultIs);
+
+ String expected;
+ try (InputStream expectedIs = mockAssetManager.open("expected.gpx");
+ InputStreamReader expectedIsr = new InputStreamReader(expectedIs, StandardCharsets.UTF_8);
+ BufferedReader expectedReader = new BufferedReader(expectedIsr)) {
+ StringBuilder expectedBuilder = new StringBuilder();
+ String line;
+ while ((line = expectedReader.readLine()) != null) {
+ expectedBuilder.append(line).append(System.lineSeparator());
+ }
+ expected = expectedBuilder.toString();
+ }
+ assertEquals("String should have same content", expected, result);
}
@Test
diff --git a/app/src/test/java/net/osmtracker/util/FileSystemUtilsTest.java b/app/src/test/java/net/osmtracker/util/FileSystemUtilsTest.java
new file mode 100644
index 000000000..4ce032fb4
--- /dev/null
+++ b/app/src/test/java/net/osmtracker/util/FileSystemUtilsTest.java
@@ -0,0 +1,122 @@
+package net.osmtracker.util;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.File;
+import java.io.IOException;
+
+import static org.junit.Assert.*;
+
+public class FileSystemUtilsTest {
+
+ @Rule
+ public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+ private File sourceFile;
+ private File destinationDirectory;
+ private File sourceDirectory;
+ private File targetFile;
+
+ @Before
+ public void setUp() throws IOException {
+ // Create a temporary source file
+ sourceFile = temporaryFolder.newFile("source.txt");
+
+ // Create a temporary destination directory
+ destinationDirectory = temporaryFolder.newFolder("destinationDir");
+
+ // Create a temporary source directory
+ sourceDirectory = temporaryFolder.newFolder("sourceDir");
+
+ // Create a temporary target file
+ targetFile = new File(destinationDirectory, "target.txt");
+ }
+
+ @After
+ public void tearDown() {
+ // No cleanup needed for temporary files and directories
+ }
+
+ @Test
+ public void testCopyFileSuccess() {
+ assertTrue(FileSystemUtils.copyFile(destinationDirectory, sourceFile, "target.txt"));
+ assertTrue(targetFile.exists());
+ }
+
+ @Test
+ public void testCopyFileFailure() {
+ // Attempt to copy to a non-existent directory
+ File nonExistentDirectory = new File(temporaryFolder.getRoot(), "nonExistentDir");
+ assertFalse(FileSystemUtils.copyFile(nonExistentDirectory, sourceFile, "target.txt"));
+ }
+
+ @Test
+ public void testCopyDirectoryContentsSuccess() throws IOException {
+ // Create a file in the source directory
+ File fileInSourceDirectory = new File(sourceDirectory, "fileInSource.txt");
+ assertTrue(fileInSourceDirectory.createNewFile());
+
+ assertTrue(FileSystemUtils.copyDirectoryContents(destinationDirectory, sourceDirectory));
+ assertTrue(new File(destinationDirectory, "fileInSource.txt").exists());
+ }
+
+ @Test
+ public void testCopyDirectoryContentsFailure() {
+ // Attempt to copy from a non-existent directory
+ File nonExistentDirectory = new File(temporaryFolder.getRoot(), "nonExistentDir");
+ assertFalse(FileSystemUtils.copyDirectoryContents(destinationDirectory, nonExistentDirectory));
+ }
+
+ @Test
+ public void testCopyDirectoryContentsDestinationNull() {
+ // Attempt to copy with a null destination directory
+ assertFalse(FileSystemUtils.copyDirectoryContents(null, sourceDirectory));
+ }
+
+ @Test
+ public void testCopyDirectoryContentsSourceNull() {
+ // Attempt to copy with a null source directory
+ assertFalse(FileSystemUtils.copyDirectoryContents(destinationDirectory, null));
+ }
+
+ @Test
+ public void testDeleteFileSuccess() {
+ assertTrue(FileSystemUtils.delete(sourceFile, false));
+ assertFalse(sourceFile.exists());
+ }
+
+ @Test
+ public void testDeleteDirectorySuccess() {
+ assertTrue(FileSystemUtils.delete(sourceDirectory, true));
+ assertFalse(sourceDirectory.exists());
+ }
+
+ @Test
+ public void testDeleteDirectoryFailure() throws IOException {
+ // Create a nested directory structure
+ File nestedDirectory = new File(sourceDirectory, "nestedDir");
+ assertTrue(nestedDirectory.mkdir());
+ File nestedFile = new File(nestedDirectory, "nestedFile.txt");
+ assertTrue(nestedFile.createNewFile());
+
+ // Attempt to delete the directory with recursion depth greater than 1
+ assertFalse(FileSystemUtils.delete(sourceDirectory, true));
+ }
+
+ @Test
+ public void testGetUniqueChildNameFor() throws IOException {
+ String uniqueName = FileSystemUtils.getUniqueChildNameFor(destinationDirectory, "test", ".txt");
+ assertEquals("test.txt", uniqueName);
+
+ // Create a file with the same name to test the uniqueness
+ File existingFile = new File(destinationDirectory, "test.txt");
+ assertTrue(existingFile.createNewFile());
+
+ uniqueName = FileSystemUtils.getUniqueChildNameFor(destinationDirectory, "test", ".txt");
+ assertEquals("test1.txt", uniqueName);
+ }
+}
diff --git a/app/src/test/java/net/osmtracker/util/MercatorProjectionTest.java b/app/src/test/java/net/osmtracker/util/MercatorProjectionTest.java
new file mode 100644
index 000000000..7bbff9d64
--- /dev/null
+++ b/app/src/test/java/net/osmtracker/util/MercatorProjectionTest.java
@@ -0,0 +1,77 @@
+package net.osmtracker.util;
+
+import static org.junit.Assert.*;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+@RunWith(Parameterized.class)
+public class MercatorProjectionTest {
+
+ @Parameterized.Parameter(0)
+ public double minLat;
+
+ @Parameterized.Parameter(1)
+ public double lat;
+
+ @Parameterized.Parameter(2)
+ public double maxLat;
+
+ @Parameterized.Parameter(3)
+ public double minLon;
+
+ @Parameterized.Parameter(4)
+ public double lon;
+
+ @Parameterized.Parameter(5)
+ public double maxLon;
+
+ @Parameterized.Parameter(6)
+ public int expectedX;
+
+ @Parameterized.Parameter(7)
+ public int expectedY;
+
+ @Parameterized.Parameter(8)
+ public double expectedScale;
+
+ @Parameterized.Parameter(9)
+ public Float degre;
+
+ @Parameterized.Parameter(10)
+ public boolean isLat;
+
+ @Parameterized.Parameter(11)
+ public String expectedDms;
+
+ @Parameterized.Parameters
+ public static Collection