Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
a80de15
Increases version number to 71
jamescr Sep 10, 2025
c51ec9f
Add community and support section to README
Andyporras Sep 11, 2025
5f31522
Merge pull request #605 from Andyporras/develop
jamescr Sep 12, 2025
62aaa76
Clear the text fields in the track details editor
Andyporras Oct 9, 2025
204d9f5
DOCS: Improve the contributing.md file
gadyrcdz Nov 4, 2025
c5e2960
Docs: Update PR Template structure
gadyrcdz Nov 13, 2025
707a601
Translate strings.xml in nl
transifex-integration[bot] Oct 19, 2025
3072c48
Translate strings.xml in zh_TW
transifex-integration[bot] Oct 31, 2025
772150c
Translate strings.xml in sv
transifex-integration[bot] Nov 7, 2025
8845028
Translate strings.xml in sv
transifex-integration[bot] Nov 8, 2025
585a493
[Transifex] Updates for project OSMTracker for Android™ and language …
transifex-integration[bot] Dec 9, 2025
f556eb1
[Transifex] Updates for project OSMTracker for Android™ and language …
transifex-integration[bot] Dec 9, 2025
16412e1
Allow again to chose map tile provider. CyclOSM and OpenTopo are now …
Aug 27, 2025
8a83ffa
- Undo changes on translations
Aug 27, 2025
2c0c431
fix: resolve text overlap in osm_upload layout
JoseAndresVargas Nov 13, 2025
e134b03
Add map tiles summary text and set tile options to non-translatable. …
jamescr Dec 14, 2025
93c8028
refactor: Clean tile provider names from translation files.
jamescr Dec 15, 2025
7c9621b
refactor: Clean tile provider names from translation files (x2).
jamescr Dec 15, 2025
0278ff5
Fix: Remove trackdetail_name_and_title string to clarify filename con…
Andyporras Dec 21, 2025
26bb1ae
[Transifex] Updates for project OSMTracker for Android™ (#655)
transifex-integration[bot] Dec 22, 2025
0ce7051
Replace deprecated ActivityTestRule
Binnette Dec 4, 2024
91036df
[Transifex] Updates for project OSMTracker for Android™ (#656)
transifex-integration[bot] Dec 23, 2025
96cf84a
Migrate preferences to AndroidX
jamescr Dec 27, 2025
e7d6326
Fix: Add validation in GPS logging interval and GPS logging distance
jamescr Dec 28, 2025
b5676fc
Refactor: Improve code formatting and readability in Preferences.java
jamescr Dec 29, 2025
1ab2564
Feature: Add default values for List Preferences.
jamescr Dec 29, 2025
f1e8b15
Refactor: replace redundant methods with generic implementations
jamescr Dec 29, 2025
bf174e3
Feature: Display current preference value in ListPreferences summarie…
jamescr Dec 29, 2025
721fb64
Add Preferences Test
jamescr Dec 30, 2025
e52a24f
Feature: add button to restore default values for logging interval an…
jamescr Jan 2, 2026
77afd19
UI: add visibility label in track details
jamescr Jan 2, 2026
ebdc4ce
Feature: add OSM track visibility to preferences
jamescr Jan 3, 2026
f7cfb76
Feature: get OSM track visibility from preferences
jamescr Jan 3, 2026
e0acc86
Refactor: delete unused import
jamescr Jan 12, 2026
b52014e
Add Tests for OSM Visibility
jamescr Jan 13, 2026
990db5a
[Transifex] Updates for project OSMTracker for Android™
jamescr Jan 14, 2026
ee89228
scope and client_id change for notes auth
JoseAndresVargas Nov 21, 2025
349bf38
Implemented notes upload using osmapi
JoseAndresVargas Nov 21, 2025
bea2013
Notes upload UI text added
JoseAndresVargas Nov 21, 2025
d37b5bf
In waypoint list, implemented context menu to upload notes
JoseAndresVargas Nov 21, 2025
cfb3d3c
Feature UI: add Text Note to preferences
jamescr Jan 18, 2026
17fb885
(WIP) Feature: DB Add Note table, UI trackdetails include notes, UI n…
jamescr Jan 18, 2026
3f86c52
Feature: Use Text Note preferences to save the note
jamescr Jan 18, 2026
4c2acc0
Feature UI: Add icon to show when note is uploaded to OSM
jamescr Jan 18, 2026
514bf03
Feature UI: Add underline to note count in track detail
jamescr Jan 18, 2026
325eaf8
OAuth2 Client ID restore (changed for upload notes development)
jamescr Jan 19, 2026
3759f1f
Refactor Note List, add unit test
jamescr Jan 20, 2026
b12a1f7
Open Street Map Notes Upload clases and add unit test
jamescr Jan 21, 2026
7da026b
Refactor: Delete EditNoteDialogOnClickListener Java class
jamescr Jan 21, 2026
4e2c374
Refactor(test): Replace PowerMock with Mockito
jamescr Jan 22, 2026
4430980
[Transifex] Updates for project OSMTracker for Android™
jamescr Jan 22, 2026
321d261
UI: Remove attention title in successful GPX upload to OSM
jamescr Jan 22, 2026
f340945
Merge branch 'master' into develop
jamescr Jan 22, 2026
4986abf
Feature: Show note count in tracks list
jamescr Jan 27, 2026
06927be
Feature: Add a What's New slide to the App intro.
jamescr Feb 3, 2026
ed0d5ea
[Transifex] Updates for project OSMTracker for Android™
jamescr Feb 4, 2026
9e873de
[Transifex] Updates for project OSMTracker for Android™ (#679)
transifex-integration[bot] Feb 6, 2026
b18f3a8
Increases version number to 72
jamescr Feb 7, 2026
5e51f1c
Fix: Add fallback when pref value is not set
jamescr Feb 11, 2026
6563525
Translate strings-preferences.xml in es
transifex-integration[bot] Feb 11, 2026
26c62a8
Translate strings-preferences.xml in pt_BR
transifex-integration[bot] Feb 11, 2026
7285732
Translate strings-preferences.xml in zh_TW
transifex-integration[bot] Feb 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ android {
multiDexEnabled true

// Version code should be increased after each release
versionCode 70
versionCode 72
versionName new Date().format('yyyy.MM.dd')

testApplicationId "net.osmtracker.test"
Expand Down Expand Up @@ -61,6 +61,8 @@ android {
}
testOptions {
unitTests.returnDefaultValues = true
// This flag is required for Robolectric to find XML resources
unitTests.includeAndroidResources = true
unitTests.all {
it.jvmArgs = [
'--add-opens', 'java.base/java.io=ALL-UNNAMED',
Expand All @@ -85,6 +87,12 @@ dependencies {
exclude group: 'net.sf.kxml', module: 'kxml2'
exclude group: 'xmlpull', module: 'xmlpull'
}
// For upload notes to osm server
implementation ('de.westnordost:osmapi-notes: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'

Expand All @@ -95,22 +103,24 @@ dependencies {
implementation 'androidx.constraintlayout:constraintlayout:2.2.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.7.0'
implementation 'androidx.preference:preference:1.2.0'
implementation 'androidx.preference:preference-ktx:1.2.1'

// Required -- JUnit 4 framework
testImplementation 'junit:junit:4.13.2'
// Robolectric environment
// Robolectric
testImplementation 'org.robolectric:robolectric:4.11.1'
// AndroidX Test core for Robolectric
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.espresso:espresso-core:3.6.1'
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.7.0'
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
androidTestImplementation 'androidx.test:rules:1.6.1'
}
Expand Down
218 changes: 218 additions & 0 deletions app/src/androidTest/java/net/osmtracker/activity/PreferencesTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
package net.osmtracker.activity;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.clearText;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.typeText;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
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 org.hamcrest.Matchers.stringContainsInOrder;

import android.content.Context;
import android.content.SharedPreferences;

import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.test.core.app.ActivityScenario;
import androidx.test.espresso.contrib.RecyclerViewActions;
import androidx.test.espresso.matcher.ViewMatchers;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;

import net.osmtracker.OSMTracker;
import net.osmtracker.R;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.io.File;
import java.util.Arrays;


@RunWith(AndroidJUnit4.class)
public class PreferencesTest {

private Context context;
private ActivityScenario<Preferences> activity;

@Before
public void setup() {
context = InstrumentationRegistry.getInstrumentation().getTargetContext();

// Reset preferences to default before each test to ensure a clean state
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
prefs.edit().clear().commit();

// Launch the activity
activity = ActivityScenario.launch(Preferences.class);
}

@After
public void tearDown() {
activity.close();
}

/**
* Test that the Storage Directory preference logic works to rejects empty input.
*/
@Test
public void testStorageDirectoryValidatesNonEmpty() {
String keyTitle = context.getString(R.string.prefs_storage_dir);
String defaultValue = OSMTracker.Preferences.VAL_STORAGE_DIR;

// Looks for storage directory preference
scrollToAndClick(keyTitle);

// Try to save an empty value
onView(withId(android.R.id.edit)).perform(clearText());
onView(withText(android.R.string.ok)).perform(click());

// Open the preference to verify the value in the list remains the default (unchanged)
onView(ViewMatchers.isAssignableFrom(RecyclerView.class))
.check(matches(hasDescendant(withText(defaultValue))));
}

/**
* Test that the Storage Directory preference logic works to automatically append a leading
* slash separator if missing.
*/
@Test
public void testStorageDirectoryValidatesAppendLeadingSlash() {
String keyTitle = context.getString(R.string.prefs_storage_dir);
String expected = File.separator + "my_folder";


// Looks for storage directory preference
scrollToAndClick(keyTitle);

// Try to type a value without a slash
onView(withId(android.R.id.edit)).perform(clearText());
onView(withId(android.R.id.edit))
.perform(typeText("my_folder"));
onView(withText(android.R.string.ok)).perform(click());

// Open the preference to verify the value in the list is the expected
onView(ViewMatchers.isAssignableFrom(RecyclerView.class))
.check(matches(hasDescendant(withText(expected))));
}

/**
* Test Numeric Input logic (GPS Logging Interval): update summary with suffix.
*/
@Test
public void testNumericInputLogic() {
String title = context.getString(R.string.prefs_gps_logging_interval);
String suffix = context.getString(R.string.prefs_gps_logging_interval_seconds);

scrollToAndClick(title);

// Enter a valid number
onView(withId(android.R.id.edit))
.perform(clearText(), typeText("30"));
onView(withText(android.R.string.ok)).perform(click());

// Verify summary format: "30 seconds. <Static Summary>"
onView(ViewMatchers.isAssignableFrom(RecyclerView.class))
.check(matches(hasDescendant(withText(stringContainsInOrder(Arrays.asList("30",
suffix))))));
}

/**
* Test that the Reset button in numeric preferences restores the default value.
*/
@Test
public void testResetButtonResetsValue() {
String title = context.getString(R.string.prefs_gps_logging_interval);
String suffix = context.getString(R.string.prefs_gps_logging_interval_seconds);
String defaultValue = OSMTracker.Preferences.VAL_GPS_LOGGING_INTERVAL;

scrollToAndClick(title);

// Set a custom value "50"
onView(withId(android.R.id.edit)).perform(clearText(), typeText("50"));
onView(withText(android.R.string.ok)).perform(click());

// Verify custom value is set
onView(ViewMatchers.isAssignableFrom(RecyclerView.class))
.check(matches(hasDescendant(withText(stringContainsInOrder(Arrays.asList("50",
suffix))))));

// Reopen dialog
scrollToAndClick(title);

// Click the Reset button (Neutral button)
onView(withText(R.string.prefs_reset_default_value)).perform(click());

// Verify value is back to default "0"
onView(ViewMatchers.isAssignableFrom(RecyclerView.class))
.check(matches(hasDescendant(withText(stringContainsInOrder(Arrays.asList(
defaultValue,
suffix))))));
}

/**
* Test ListPreference custom summary logic (Screen Orientation)
* Should show "Selected Value. \n ..." (don't check for the 2nd line of the summary)
*/
@Test
public void testListPreferenceCustomSummary() {
String title = context.getString(R.string.prefs_ui_orientation);

scrollToAndClick(title);

// Select 1st option from array resource entries
String[] entries = context.getResources()
.getStringArray(R.array.prefs_ui_orientation_options_keys);
onView(withText(entries[0])).perform(click());

// Verify the two-line summary exists
onView(ViewMatchers.isAssignableFrom(RecyclerView.class)).check(matches(hasDescendant(
withText(stringContainsInOrder(Arrays.asList(entries[0], ".\n"))))));
}

/**
* Test Clear OAuth Data logic.
*/
@Test
public void testClearOAuthData() {
String title = context.getString(R.string.prefs_osm_clear_oauth_data);

// Inject a fake token to enable the button
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context)
.edit();
editor.putString(OSMTracker.Preferences.KEY_OSM_OAUTH2_ACCESSTOKEN, "fake_token");
editor.commit();

// Relaunch to refresh UI state
ActivityScenario.launch(Preferences.class);


scrollToAndClick(title);

// Click OK on Confirmation Dialog
onView(withText(R.string.prefs_osm_clear_oauth_data_dialog)).check(matches(isDisplayed()));
onView(withText(android.R.string.ok)).perform(click());

// Verify token is gone in prefs
assert(!PreferenceManager.getDefaultSharedPreferences(context)
.contains(OSMTracker.Preferences.KEY_OSM_OAUTH2_ACCESSTOKEN));
}

// --- Helper Methods ---

/**
* Helper to scroll to a preference in the RecyclerView and click it.
*/
private void scrollToAndClick(String text) {
onView(ViewMatchers.isAssignableFrom(RecyclerView.class))
.perform(RecyclerViewActions.actionOnItem(
hasDescendant(withText(text)),
click()));
}

}
Loading