Full Code of labexp/osmtracker-android for AI

master 0c32781db83e cached
462 files
1.8 MB
525.3k tokens
815 symbols
1 requests
Download .txt
Showing preview only (2,087K chars total). Download the full file or copy to clipboard to get everything.
Repository: labexp/osmtracker-android
Branch: master
Commit: 0c32781db83e
Files: 462
Total size: 1.8 MB

Directory structure:
gitextract_nkc4_91h/

├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.yml
│   │   ├── config.yml
│   │   ├── documentation_report.yml
│   │   └── feature_request.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       ├── android.yml
│       └── nightly.yml
├── .gitignore
├── .travis.yml
├── .tx/
│   └── config
├── AUTHORS
├── CONTRIBUTING.md
├── COPYING
├── LICENSES
├── README.md
├── app/
│   ├── build.gradle
│   ├── jacoco.gradle
│   ├── lint.xml
│   └── src/
│       ├── androidTest/
│       │   ├── assets/
│       │   │   └── gpx/
│       │   │       └── gpx-test.gpx
│       │   ├── java/
│       │   │   └── net/
│       │   │       └── osmtracker/
│       │   │           ├── activity/
│       │   │           │   └── PreferencesTest.java
│       │   │           ├── data/
│       │   │           │   └── Mocks.java
│       │   │           ├── layouts/
│       │   │           │   ├── DeleteLayoutTest.java
│       │   │           │   ├── DownloadLayoutTest.java
│       │   │           │   └── RepositorySettingsDialogTest.java
│       │   │           └── util/
│       │   │               ├── LogcatHelper.java
│       │   │               ├── TestUtils.java
│       │   │               ├── ToastMatcher.java
│       │   │               └── WaitForView.java
│       │   └── res/
│       │       └── values/
│       │           └── strings.xml
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   ├── java/
│       │   │   └── net/
│       │   │       └── osmtracker/
│       │   │           ├── GitHubUser.java
│       │   │           ├── OSMTracker.java
│       │   │           ├── activity/
│       │   │           │   ├── About.java
│       │   │           │   ├── AvailableLayouts.java
│       │   │           │   ├── ButtonsPresets.java
│       │   │           │   ├── DisplayTrack.java
│       │   │           │   ├── DisplayTrackMap.java
│       │   │           │   ├── GitHubConfig.java
│       │   │           │   ├── GitHubNewFork.java
│       │   │           │   ├── GitHubNewRepo.java
│       │   │           │   ├── GitHubPullRequest.java
│       │   │           │   ├── GitHubUpload.java
│       │   │           │   ├── Intro.kt
│       │   │           │   ├── NoteList.java
│       │   │           │   ├── OpenStreetMapNotesUpload.java
│       │   │           │   ├── OpenStreetMapUpload.java
│       │   │           │   ├── Preferences.java
│       │   │           │   ├── TrackDetail.java
│       │   │           │   ├── TrackDetailEditor.java
│       │   │           │   ├── TrackListRVAdapter.java
│       │   │           │   ├── TrackLogger.java
│       │   │           │   ├── TrackManager.java
│       │   │           │   └── WaypointList.java
│       │   │           ├── adapter/
│       │   │           │   └── NoteAdapter.java
│       │   │           ├── db/
│       │   │           │   ├── DataHelper.java
│       │   │           │   ├── DatabaseHelper.java
│       │   │           │   ├── ExportDatabaseTask.java
│       │   │           │   ├── TrackContentProvider.java
│       │   │           │   ├── TracklistAdapter.java
│       │   │           │   ├── WaypointListAdapter.java
│       │   │           │   └── model/
│       │   │           │       ├── Point.java
│       │   │           │       ├── Track.java
│       │   │           │       ├── TrackPoint.java
│       │   │           │       └── WayPoint.java
│       │   │           ├── exception/
│       │   │           │   ├── CreateTrackException.java
│       │   │           │   └── ExportTrackException.java
│       │   │           ├── github/
│       │   │           │   └── GitHubConstants.java
│       │   │           ├── gpx/
│       │   │           │   ├── ExportToStorageTask.java
│       │   │           │   ├── ExportToTempFileTask.java
│       │   │           │   ├── ExportTrackTask.java
│       │   │           │   └── ZipHelper.java
│       │   │           ├── layout/
│       │   │           │   ├── DisablableTableLayout.java
│       │   │           │   ├── DownloadCustomLayoutTask.java
│       │   │           │   ├── GetStringResponseTask.java
│       │   │           │   ├── GpsStatusRecord.java
│       │   │           │   ├── TLSSocketFactory.java
│       │   │           │   ├── URLValidatorTask.java
│       │   │           │   └── UserDefinedLayout.java
│       │   │           ├── listener/
│       │   │           │   ├── EditWaypointDialogOnClickListener.java
│       │   │           │   ├── PageButtonOnClickListener.java
│       │   │           │   ├── PressureListener.java
│       │   │           │   ├── SensorListener.java
│       │   │           │   ├── StillImageOnClickListener.java
│       │   │           │   ├── TagButtonOnClickListener.java
│       │   │           │   ├── TextNoteOnClickListener.java
│       │   │           │   └── VoiceRecOnClickListener.java
│       │   │           ├── osm/
│       │   │           │   ├── OpenStreetMapConstants.java
│       │   │           │   ├── UploadToOpenStreetMapNotesTask.java
│       │   │           │   └── UploadToOpenStreetMapTask.java
│       │   │           ├── overlay/
│       │   │           │   └── WayPointsOverlay.java
│       │   │           ├── receiver/
│       │   │           │   └── MediaButtonReceiver.java
│       │   │           ├── service/
│       │   │           │   ├── gps/
│       │   │           │   │   ├── GPSLogger.java
│       │   │           │   │   └── GPSLoggerServiceConnection.java
│       │   │           │   └── resources/
│       │   │           │       ├── AppResourceIconResolver.java
│       │   │           │       ├── ExternalDirectoryIconResolver.java
│       │   │           │       └── IconResolver.java
│       │   │           ├── util/
│       │   │           │   ├── ArrayUtils.java
│       │   │           │   ├── Callback.java
│       │   │           │   ├── CustomLayoutsUtils.java
│       │   │           │   ├── DialogUtils.java
│       │   │           │   ├── FileSystemUtils.java
│       │   │           │   ├── GitHubUtils.java
│       │   │           │   ├── MercatorProjection.java
│       │   │           │   ├── ThemeValidator.java
│       │   │           │   ├── URLCreator.java
│       │   │           │   └── UserDefinedLayoutReader.java
│       │   │           └── view/
│       │   │               ├── DisplayTrackView.java
│       │   │               ├── TextNoteDialog.java
│       │   │               └── VoiceRecDialog.java
│       │   └── res/
│       │       ├── drawable/
│       │       │   ├── divider.xml
│       │       │   ├── ic_fab_add_track.xml
│       │       │   └── map_btn_style.xml
│       │       ├── drawable-mdpi/
│       │       │   └── theme_highcontrast_btn.xml
│       │       ├── layout/
│       │       │   ├── about.xml
│       │       │   ├── available_layouts.xml
│       │       │   ├── buttons_presets.xml
│       │       │   ├── displaytrackmap.xml
│       │       │   ├── edit_note_dialog.xml
│       │       │   ├── edit_waypoint_dialog.xml
│       │       │   ├── git_configuration_fields.xml
│       │       │   ├── git_create_fork.xml
│       │       │   ├── git_create_fork_fields.xml
│       │       │   ├── git_create_pullrequest.xml
│       │       │   ├── git_create_pullrequest_fields.xml
│       │       │   ├── git_newrepo.xml
│       │       │   ├── git_newrepo_fields.xml
│       │       │   ├── git_trackdetail_fields.xml
│       │       │   ├── github_configuration_token.xml
│       │       │   ├── github_repository_settings.xml
│       │       │   ├── gpsstatus_record.xml
│       │       │   ├── notelist.xml
│       │       │   ├── notelist_item.xml
│       │       │   ├── osm_note_upload.xml
│       │       │   ├── osm_upload.xml
│       │       │   ├── osmtracker.xml
│       │       │   ├── settings_activity.xml
│       │       │   ├── trackdetail.xml
│       │       │   ├── trackdetail_fields.xml
│       │       │   ├── trackdetail_item.xml
│       │       │   ├── tracklist_item.xml
│       │       │   ├── tracklogger.xml
│       │       │   ├── trackmanager.xml
│       │       │   ├── upload_github_menu.xml
│       │       │   └── waypointlist_item.xml
│       │       ├── layout-iw/
│       │       │   ├── trackdetail_item.xml
│       │       │   ├── tracklist_item.xml
│       │       │   └── waypointlist_item.xml
│       │       ├── menu/
│       │       │   ├── btnprecb_context_menu.xml
│       │       │   ├── displaytrackmap_menu.xml
│       │       │   ├── github_repository_settings_menu.xml
│       │       │   ├── githubupload_settings_menu.xml
│       │       │   ├── launch_available_layouts_menu.xml
│       │       │   ├── note_contextmenu.xml
│       │       │   ├── trackdetail_menu.xml
│       │       │   ├── tracklogger_menu.xml
│       │       │   ├── trackmgr_contextmenu.xml
│       │       │   └── trackmgr_menu.xml
│       │       ├── values/
│       │       │   ├── accessibility.xml
│       │       │   ├── colors.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   ├── styles.xml
│       │       │   ├── theme_highcontrast.xml
│       │       │   ├── themes.xml
│       │       │   ├── values-preferences.xml
│       │       │   ├── values.xml
│       │       │   └── waypoints.xml
│       │       ├── values-ar/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-b+sr+Latn/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-be/
│       │       │   ├── strings-tags.xml
│       │       │   └── strings.xml
│       │       ├── values-bg-rBG/
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   └── strings.xml
│       │       ├── values-ca/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-cs-rCZ/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-da/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-de/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-el/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-es/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-et/
│       │       │   └── strings.xml
│       │       ├── values-eu/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-fa/
│       │       │   ├── accessibility.xml
│       │       │   └── waypoints.xml
│       │       ├── values-fa-rIR/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-fi/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-fr/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-fr-rFR/
│       │       │   ├── accessibility.xml
│       │       │   └── waypoints.xml
│       │       ├── values-gl/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-he/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-hr/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-hu/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-hy/
│       │       │   ├── accessibility.xml
│       │       │   └── strings.xml
│       │       ├── values-id/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-it/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-ja/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-kn/
│       │       │   ├── strings-preferences.xml
│       │       │   └── waypoints.xml
│       │       ├── values-ko/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-lt/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-lv/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-nb/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-nl/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-nn/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-pl/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-pt/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-pt-rBR/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-pt-rPT/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-ru/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-sk/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-sl/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-sq/
│       │       │   └── accessibility.xml
│       │       ├── values-sr/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-sv/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-ta/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-th/
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-tr/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   └── waypoints.xml
│       │       ├── values-tr-rTR/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-tags.xml
│       │       │   └── waypoints.xml
│       │       ├── values-uk/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-v11/
│       │       │   ├── strings.xml
│       │       │   ├── theme_highcontrast.xml
│       │       │   └── themes.xml
│       │       ├── values-v14/
│       │       │   ├── theme_highcontrast.xml
│       │       │   └── themes.xml
│       │       ├── values-vi/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-zh-rCN/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-zh-rTW/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── xml/
│       │       │   ├── default_buttons_layout.xml
│       │       │   ├── filepaths.xml
│       │       │   └── preferences.xml
│       │       ├── xml-da/
│       │       │   └── default_buttons_layout.xml
│       │       ├── xml-de/
│       │       │   └── default_buttons_layout.xml
│       │       ├── xml-en-rGB/
│       │       │   └── default_buttons_layout.xml
│       │       ├── xml-it/
│       │       │   └── default_buttons_layout.xml
│       │       ├── xml-ja/
│       │       │   └── default_buttons_layout.xml
│       │       ├── xml-nb/
│       │       │   └── default_buttons_layout.xml
│       │       ├── xml-nn/
│       │       │   └── default_buttons_layout.xml
│       │       └── xml-th/
│       │           └── default_buttons_layout.xml
│       └── test/
│           ├── assets/
│           │   └── gpx/
│           │       └── gpx-test.gpx
│           ├── java/
│           │   └── net/
│           │       └── osmtracker/
│           │           ├── activity/
│           │           │   ├── ButtonsPresetsTest.java
│           │           │   ├── OpenStreetMapNotesUploadTest.java
│           │           │   └── TrackDetailEditorTest.java
│           │           ├── data/
│           │           │   ├── GPXMocks.java
│           │           │   ├── MockDataHelper.java
│           │           │   ├── TrackMocks.java
│           │           │   ├── TrackPointMocks.java
│           │           │   └── WayPointMocks.java
│           │           ├── db/
│           │           │   ├── DataHelperNoteTest.java
│           │           │   └── model/
│           │           │       ├── OSMVisibilityTest.java
│           │           │       └── TrackTest.java
│           │           ├── gpx/
│           │           │   └── ExportToStorageTaskTest.java
│           │           ├── layout/
│           │           │   ├── DownloadCustomLayoutTaskTest.java
│           │           │   └── URLValidatorTaskTest.java
│           │           └── util/
│           │               ├── ArrayUtilsTest.java
│           │               ├── CustomLayoutsUtilsTest.java
│           │               ├── FileSystemUtilsTest.java
│           │               ├── MercatorProjectionTest.java
│           │               ├── ThemeValidatorTest.java
│           │               └── URLCreatorTest.java
│           └── resources/
│               └── gpx/
│                   └── gpx-test.gpx
├── build.gradle
├── fastlane/
│   └── metadata/
│       └── android/
│           ├── ca/
│           │   └── short_description.txt
│           ├── cs/
│           │   └── short_description.txt
│           ├── de/
│           │   └── short_description.txt
│           ├── en-US/
│           │   ├── changelogs/
│           │   │   ├── 57.txt
│           │   │   ├── 62.txt
│           │   │   ├── 65.txt
│           │   │   └── 66.txt
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── es/
│           │   ├── full_description.txt
│           │   └── short_description.txt
│           ├── fr/
│           │   └── short_description.txt
│           ├── ga/
│           │   └── short_description.txt
│           ├── he/
│           │   └── short_description.txt
│           ├── id/
│           │   └── short_description.txt
│           ├── nn/
│           │   └── short_description.txt
│           ├── pl/
│           │   └── short_description.txt
│           ├── pt/
│           │   └── short_description.txt
│           ├── pt-BR/
│           │   └── short_description.txt
│           ├── pt-PT/
│           │   └── short_description.txt
│           ├── ro/
│           │   └── short_description.txt
│           ├── ru/
│           │   └── short_description.txt
│           ├── sq/
│           │   └── short_description.txt
│           ├── sr/
│           │   └── short_description.txt
│           ├── sw/
│           │   └── short_description.txt
│           ├── ta/
│           │   └── short_description.txt
│           ├── tr/
│           │   └── short_description.txt
│           ├── uk/
│           │   └── short_description.txt
│           └── zh-CN/
│               └── short_description.txt
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── transifex.yml

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
# github: [labexp]
liberapay: OSMTracker


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: "Bug Report"
description: "Report an issue or bug with OSMTracker for Android."
labels: "bug"
body:
  - type: markdown
    attributes:
      value: "**Thank you for reporting a bug! Before reporting, please have a look at the [Q&A](https://github.com/labexp/osmtracker-android/discussions/categories/q-a), make sure the issue has no duplicate and hasn't been already addressed by searching through [the existing and past issues](https://github.com/labexp/osmtracker-android/issues).\nPlease fill out the following details to help us diagnose the issue.**"
  
  - type: textarea
    id: description
    attributes:
      label: "Description"
      description: "Provide a clear and concise description of the issue."
      placeholder: "Describe the issue here..."
    validations:
      required: true
  
  - type: textarea
    id: steps_to_reproduce
    attributes:
      label: "Steps to Reproduce"
      description: "Please list the steps to reproduce the issue."
      value: |
        1. 
        2. 
        3. 
      placeholder: "Step-by-step instructions..."
    validations:
      required: true
  
  - type: textarea
    id: logs
    attributes:
      label: "Logs or Console Errors (if applicable)"
      description: |
        If available, please paste or attach any relevant logs or error messages.  
        - On Android, you can use [adb logcat](https://developer.android.com/studio/command-line/logcat) to capture logs.
      render: "plaintext"
    validations:
      required: false
  
  - type: textarea
    id: screenshots
    attributes:
      label: "Screenshots or Videos (Optional)"
      description: "Attach any relevant screenshots or videos."
    validations:
      required: false

  - type: input
    id: android_version
    attributes:
      label: "Android Version"
      placeholder: "e.g., Android 12"
    validations:
      required: true
  
  - type: input
    id: device_model
    attributes:
      label: "Device Model"
      placeholder: "e.g., Samsung Galaxy S21"
    validations:
      required: true
  
  - type: checkboxes
    id: submission_checklist
    attributes:
      label: "Submission Checklist"
      description: "Please confirm the following before submitting:"
      options:
        - label: "I am reporting an issue, not asking a question."
          required: true
        - label: "I have updated to the latest OSMTracker version, and the issue still exists."
          required: true
        - label: "I have checked the Q&A Discussions, open and closed issues, and the wiki, but did not find a solution."
          required: false


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: true
contact_links:
  - name: "Questions about OSMTracker"
    url: "https://github.com/labexp/osmtracker-android/discussions/categories/q-a"
    about: "Ask questions and discuss with the OSMTracker community in GitHub Discussions."


================================================
FILE: .github/ISSUE_TEMPLATE/documentation_report.yml
================================================
name: "Documentation Report"
description: "Report errors or suggest improvements for project documentation."
labels: "documentation"
body:
  - type: markdown
    attributes:
      value: "**Thank you for helping us improve our documentation! Please complete the following information to help us address the issue efficiently.**"
  
  - type: textarea
    id: description
    attributes:
      label: "Description"
      description: "Explain the problem or the section that needs improvement in the documentation."
      placeholder: "Describe the documentation issue here..."
    validations:
      required: true
  
  - type: textarea
    id: affected_section
    attributes:
      label: "Affected Section"
      description: "Indicate which part of the documentation contains the error. This can be specified using a URL, section, or specific page."
      placeholder: "E.g., https://example.com/docs/section or 'Installation Guide' section or page 3 of PDF manual..."
    validations:
      required: true
  
  - type: textarea
    id: error_found
    attributes:
      label: "Error Found"
      description: "Describe what is wrong. This could be: lack of clarity, spelling/grammar errors, outdated information, or missing information."
      placeholder: "Detail the specific error or issue found..."
    validations:
      required: true
  
  - type: textarea
    id: improvement_proposal
    attributes:
      label: "Improvement Proposal (optional)"
      description: "How should the documentation be corrected or improved?"
      placeholder: "Suggest how the documentation could be improved..."
    validations:
      required: false
  
  - type: textarea
    id: error_evidence
    attributes:
      label: "Evidence of Error (optional)"
      description: "Provide evidence of the error, such as screenshots, manual captures, videos, or examples."
      placeholder: "Upload or describe evidence of the documentation issue..."
    validations:
      required: false
  
  - type: checkboxes
    id: submission_checklist
    attributes:
      label: "Submission Checklist"
      description: "Please confirm the following before submitting:"
      options:
        - label: "I have verified that this documentation issue hasn't been reported previously."
          required: true
        - label: "I have provided specific details about the location of the issue in the documentation."
          required: true
        - label: "The information I've provided is accurate and clear."
          required: true


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yml
================================================
name: "Feature Request"
description: "Suggest an enhancement or new feature for OSMTracker for Android."
labels: "enhancement"
body:
  - type: markdown
    attributes:
      value: "**Thank you for suggesting a new feature! Before submitting your request, please review the [Discussions](https://github.com/labexp/osmtracker-android/discussions) and make sure this feature hasn't already been requested by searching through [existing issues](https://github.com/labexp/osmtracker-android/issues).\nPlease complete the following details to help us better understand your proposal.**"
  
  - type: textarea
    id: description
    attributes:
      label: "Description"
      description: "Briefly explain the requested functionality."
      placeholder: "Describe the feature here..."
    validations:
      required: true
  
  - type: textarea
    id: use_cases
    attributes:
      label: "Use Cases"
      description: "Who would use this functionality?"
      placeholder: "Describe who would benefit from this feature..."
    validations:
      required: true
  
  - type: textarea
    id: benefits
    attributes:
      label: "Benefits"
      description: "How would this improve OSMTracker?"
      placeholder: "Explain how this feature would enhance the application..."
    validations:
      required: true
  
  - type: textarea
    id: alternatives
    attributes:
      label: "Alternatives"
      description: "Are there other options that could be implemented/considered instead?"
      placeholder: "Describe alternatives you've considered..."
    validations:
      required: false
  
  - type: textarea
    id: example
    attributes:
      label: "Example (optional)"
      description: "Provide visual or schematic examples of how the feature would look/function."
      placeholder: "Attach or describe visual examples..."
    validations:
      required: false
  
  - type: checkboxes
    id: submission_checklist
    attributes:
      label: "Submission Checklist"
      description: "Please confirm the following before submitting:"
      options:
        - label: "I have verified that this feature hasn't been requested previously."
          required: true
        - label: "I have provided sufficient information to understand my proposal."
          required: true
        - label: "I am willing to provide additional information if necessary."
          required: true


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
[comment]: # (Thank you for your contribution! Please fill out the following details to help us review your pull request.)
# 📝 Pull Request Title

## 🛠️ Issue
- Closes #add-your-issue-ID

[comment]: # (Link to the ISSUES related, if applicable)
[comment]: # (* Related to: ISSUE #number)
## 🛠️ Related issues (if applicable)
- ISSUES #add-ISSUE



## 📖 Description
[comment]: # (Provide a clear explanation of the changes in this PR)



## 🖼️ Screenshots (if applicable)
- Include any relevant screenshots or examples of the changes made. This helps reviewers visualize the updates



## ✅ Pull Request Checklist
[comment]: # (Please confirm the following before submitting your PR)
[comment]: # (To check a task please put a "x" inside the `[]`)
[comment]: # ([ ] : not done)
[comment]: # ([x] : done)
[comment]: # (Make sure how your PR looks clicking the "Preview" tab at the top of this editor)

- [ ] The PR is proposed to the DEVELOP branch.
- [ ] The changes have been tested on the target Android API and minimum Android API.
- [ ] Automated tests have been added (if applicable).
- [ ] The feature is well documented.
- [ ] There is a reference to the original ISSUE and related work.


## 📝 Additional Notes
- Any additional context, questions, or considerations for the reviewers.
- Take a look at our [Contributing Guide](https://github.com/labexp/osmtracker-android/blob/develop/CONTRIBUTING.md) before submitting your PR.


================================================
FILE: .github/workflows/android.yml
================================================
name: Build

on:
  push:
    branches:
      - master
      - develop
  pull_request:
    branches:
      - develop

jobs:
  build_and_test:
    name: Build and Test
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Java JDK
        uses: actions/setup-java@v4
        with:
          distribution: 'temurin'
          java-version: '17'
          cache: 'gradle'

      - name: Grant execute permission for gradlew
        run: chmod +x gradlew

      - name: Build with Gradle
        run: ./gradlew assembleDebug --stacktrace

      - name: Rename output APK
        run: |
          DATE=$(date +'%Y%m%d-%H%M')
          mv app/build/outputs/apk/debug/app-debug.apk app/build/outputs/apk/debug/OSMTracker-debug-$DATE.apk
          echo "ARTIFACT_DATE=$DATE" >> $GITHUB_ENV

      - name: Upload a Build Artifact
        uses: actions/upload-artifact@v4
        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 -Dorg.gradle.jvmargs="-Xmx4g -XX:MaxMetaspaceSize=1g"
        env:
          GRADLE_OPTS: "-Dorg.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1g"

      - 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


================================================
FILE: .github/workflows/nightly.yml
================================================
name: Nightly APK

on:
  schedule: # Scheduled jobs only run on the default repository branch
    - cron: "0 1 * * *"
  workflow_dispatch:

jobs:
  check-recent-commits:
    runs-on: ubuntu-latest
    outputs:
      has_recent_commits: ${{ steps.check-commits.outputs.has_recent_commits }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0  # Fetch all history for git log
          ref: develop    # Checkout the develop branch specifically

      - name: Check for recent commits on develop branch
        id: check-commits
        run: |
          # Get the current timestamp and calculate 24 hours ago
          CURRENT_TIMESTAMP=$(date +%s)
          TWENTY_FOUR_HOURS_AGO=$((CURRENT_TIMESTAMP - 86400))

          # Check if there are any commits in the last 24 hours
          RECENT_COMMITS=$(git log --since="$TWENTY_FOUR_HOURS_AGO" --oneline | wc -l)

          if [ "$RECENT_COMMITS" -gt 0 ]; then
            echo "Found $RECENT_COMMITS commits in the last 24 hours"
            echo "has_recent_commits=true" >> $GITHUB_OUTPUT
          else
            echo "No commits found in the last 24 hours"
            echo "has_recent_commits=false" >> $GITHUB_OUTPUT
          fi

  build:
    needs: check-recent-commits
    if: needs.check-recent-commits.outputs.has_recent_commits == 'true'
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          ref: develop    # Build from develop branch

      - name: Set up Java JDK
        uses: actions/setup-java@v4
        with:
          distribution: 'temurin'
          java-version: '17'

      - name: Setup Signing Keystore
        run: |                 
          # 1. Decode nightly keystore secret
          echo "${{ secrets.NIGHTLY_KEYSTORE }}" | base64 -d > /tmp/nightly-keystore.jks
          echo "Keystore created in /tmp/nightly-keystore.jks"
          
          # Verify
          echo "✅ Keystore created in: /tmp/nightly-keystore.jks"
          ls -la /tmp/nightly-keystore.jks
          echo "ks_path=/tmp/nightly-keystore.jks" >> $GITHUB_OUTPUT
          

      - name: Setup Android SDK
        uses: android-actions/setup-android@v2

      - name: Build with Gradle
        run: |        
          ./gradlew assembleDebug --stacktrace \
            -Pandroid.injected.signing.store.file="/tmp/nightly-keystore.jks" \
            -Pandroid.injected.signing.store.password="${{ secrets.KEYSTORE_PASSWORD }}" \
            -Pandroid.injected.signing.key.alias="${{ secrets.KEY_ALIAS }}" \
            -Pandroid.injected.signing.key.password="${{ secrets.KEY_PASSWORD }}"

      - name: Cleanup Keystore
        run: |
          echo "Cleaning keystore..."
          rm -f app/nightly-keystore.jks
          echo "✅ Keystore removed"

      - name: Rename output APK
        run: |
          DATE=$(date +'%Y%m%d-%H%M')
          mv app/build/outputs/apk/debug/app-debug.apk app/build/outputs/apk/debug/OSMTracker-nightly-$DATE.apk
          echo "ARTIFACT_DATE=$DATE" >> $GITHUB_ENV

      - name: Upload a Build Artifact
        uses: actions/upload-artifact@v4
        with:
          name: nightly-${{ env.ARTIFACT_DATE }}
          path: app/build/outputs/apk/debug/OSMTracker-nightly-*.apk

      - name: Check if nightly tag exists and delete
        run: |
          gh auth login --with-token <<< "${{ secrets.GITHUB_TOKEN }}"
          if gh release view nightly > /dev/null 2>&1; then
            echo "Nightly tag exists, deleting release..."
            gh release delete nightly --cleanup-tag --yes
          else
            echo "Nightly tag does not exist, skipping deletion"
          fi

      - name: Create GitHub Nightly Release
        uses: softprops/action-gh-release@v2.2.2
        with:
          tag_name: 'nightly'
          name: 'Nightly Build'
          draft: false
          prerelease: true
          files: app/build/outputs/apk/debug/OSMTracker-nightly-*.apk
          body: "Nightly build for OSMTracker"
          token: ${{ secrets.GITHUB_TOKEN }}


  skip-build:
    needs: check-recent-commits
    if: needs.check-recent-commits.outputs.has_recent_commits == 'false'
    runs-on: ubuntu-latest
    steps:
      - name: Skip build notification
        run: echo "No recent commits in the last 24 hours. Skipping build."


================================================
FILE: .gitignore
================================================
build
.idea
*.iml
.gradle
trash
local.properties
proguard-project.txt
# -no resources are copied from -nb
app/src/main/res/*-no
*.pl
*.sh
app/null/
app/release/

# python vitualenv
.venv

# GPX created during tests
app/src/test/assets/gpx/output-test.gpx


================================================
FILE: .travis.yml
================================================
sudo: true

language: android
jdk: oraclejdk8

android:
    components:
        - build-tools-29.0.2
        - android-29
        - tools
        - android-22
        - sys-img-armeabi-v7a-android-22

licenses:
    - android-sdk-license-.+
    - '.+'

before_install:
- mkdir "$ANDROID_HOME/licenses" || true
- yes | sdkmanager "platforms;android-27"

before_script:
     - echo no | android create avd --force -n test -c 100M -t android-22 --abi armeabi-v7a
     - emulator -avd test -no-audio -no-window -no-skin &
     - android-wait-for-emulator
     - adb shell input keyevent 82 &
     - android list targets


================================================
FILE: .tx/config
================================================
[main]
host = https://www.transifex.com
lang_map = cs_CZ: cs-rCZ, pt_PT: pt-rPT, zh_CN: zh-rCN, zh_TW: zh-rTW, pt_BR: pt-rBR, tr_TR: tr-rTR, fa_IR: fa-rIR, ro_RO: ro-rRO, es_CL: es-rCL, id_ID: id-rID, bg_BG: bg-rBG, fr_FR: fr-rFR

[osmtracker-android.strings]
file_filter = app/src/main/res/values-<lang>/strings.xml
source_file = app/src/main/res/values/strings.xml
source_lang = en
type = ANDROID

[osmtracker-android.accessibility]
file_filter = app/src/main/res/values-<lang>/accessibility.xml
source_file = app/src/main/res/values/accessibility.xml
source_lang = en
type = ANDROID

[osmtracker-android.waypoints]
file_filter = app/src/main/res/values-<lang>/waypoints.xml
source_file = app/src/main/res/values/waypoints.xml
source_lang = en
type = ANDROID

[osmtracker-android.strings-preferences]
file_filter = app/src/main/res/values-<lang>/strings-preferences.xml
source_file = app/src/main/res/values/strings-preferences.xml
source_lang = en
type = ANDROID

[osmtracker-android.strings-tags]
file_filter = app/src/main/res/values-<lang>/strings-tags.xml
source_file = app/src/main/res/values/strings-tags.xml
source_lang = en
type = ANDROID



================================================
FILE: AUTHORS
================================================
Contributors
------------

Nicolas Guillaumin <nguillaumin+osmtracker at googlemail.com>
 - Initial author.
 
Viesturs Zarins < ? >
 - Draw OSM map as background feature.
 
Matthias Glaub <matthias.glaub at googlemail.com>
 - Various patches and bug fixes.
 
Jeremy D Monin <jdmonin at nand.net>
 - Help for multi-track ability.
 - Various patches and bug fixes.
 
 Paul O'Shea
 - Various patches and bug fixes
 


================================================
FILE: CONTRIBUTING.md
================================================

# 🤝 Contributing to OSMTracker

Thank you for your interest in contributing! 🎉  
Whether you're fixing a bug, adding a new feature, or improving documentation — all contributions are welcome.

---
## 📚 Table of Contents
1. 🔰 Before You Start
2. 🛠️ How to Run the App (Setup Guide)
3. 🧪 Running Tests
4. 🌍 Translations
5. 🧑‍💻 Git Workflow (GitFlow)
6. 👶 New Contributor Quick Guide (For Beginners)
7. 📜 Code Style & Commit Guidelines
8. 🚀 How to Submit a Pull Request
9. 💬 Community & Support

---

## 🔰 1. Before You Start
- Make sure you have **Git** and **Android Studio** installed.
- Familiarity with **GitFlow** is recommended (see below).
- If you're new to open/free source, check out our **beginner section** ↓

---

## 🛠️ 2. How to Run the App Locally

```bash
# Clone the repository
git clone https://github.com/labexp/osmtracker-android/
cd osmtracker-android
```

1. Install Android Studio. - [Here](https://developer.android.com/studio/install) is the official guide on how to install it on different operating systems
2. Open the IDE and click `open an existing Android Studio project`, then look for the folder where you cloned the repository  
3. Build the project (`Ctrl + F9` or the 🛠️ hammer button)



## 🧪 3. Running Tests

This repository has an automated way to run the tests on branches but if you already have the project installed on you computer then you can also run them from a terminal.
It's recommended to run the tests locally before making a new pull request to make sure the changes doesn't break any previous functionality. You can run the tests locally as follows:
 - Make sure you are at the *root directory of the project*
	 - `$ cd YOUR_PATH/osmtracker-android`

### 📱 Instrumentation Tests (Require Emulator or Device)
 - For running **instrumentation** tests it's needed to previously start up an emulator (or real device),  you can do it from Android Studio but also without it using the command line. For that,  you need to move to the Android SDK installation directory and look for a folder called `emulator` once there, start any already created emulator by typing:
	-	`$ ./emulator -avd NAME` to start the emulator called *NAME* (run `$ ./emulator -list-avds` for a valid list of AVD names)
	- When  it's up, go back to the root project folder and run the instrumentation tests with
	- `$ ./gradlew connectedAndroidTest`

### ✅ Unit Tests
 - For running the **unit tests** no emulator or device is needed, just run 
	 - `$ ./gradlew test`
 - Now just wait for gradle to run the tests for you, it'll show the results of which tests passed or failed when it's finished


## 🌍 4. Translations
OSMTracker is translated using Transifex (see the [wiki](https://github.com/labexp/osmtracker-android/wiki/Translating)).
Once translations are complete, they will be updated via automated Transifex PR.


## 🧑‍💻 5. Git Workflow (GitFlow)

We use **GitFlow** branching model:

| Branch | Purpose |
|--------|----------|
| `master` | Stable production releases |
| `develop` | Main development branch |
| `feature/*` | New features |
| `hotfix/*` | Quick fixes for production |

- If you want more information, take a look [here](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow)
---

## 👶 6. New Contributor Quick Guide (Beginner-Friendly)

> ✨ If this is your first open-source contribution, start here!

1. ⭐ **Fork the repository**

   After forking, make sure your fork includes **all the required branches** (especially `develop`, not only `master`).

   You can verify this by checking the branches tab in your fork on GitHub.

2. 📥 **Clone your fork**
   ```bash
   git clone https://github.com/YOUR_USERNAME/osmtracker-android.git
   ```
3. 🔄 (Optional) Add upstream to stay updated  
   ```bash
   git remote add upstream https://github.com/labexp/osmtracker-android.git
   ```
4. 🌱 Create a branch  
   ```bash
   git checkout develop
   git checkout -b feature/your-feature-name
   ```
5. ✍️ Make changes + commit (atomic commits)
6. 🚀 Push your branch
7. 📝 Open a Pull Request (PR)

---

## 📜 7. Commit Message Convention
Use **clear and descriptive** commit messages.

✅ Good:
```
feat: add option to export GPX file
fix: resolve crash when no GPS signal
docs: update contributing guide
```
❌ Bad:
```
update stuff
fix bug
```

---
## 🚀 8. How to Submit a Pull Request
1. Push your changes
2. Go to GitHub → Open PR
3. Fill the PR template fully
4. Wait for review ✅

⚠️ PRs without a complete template may be rejected.

---

## 💬 9. Community & Support
- Have questions? Open a **Discussion** or an **Issue**
- (Optional): Join the contributors chat/[Telegram](https://t.me/OSMTracker).

Let's build something awesome together! 🚀

================================================
FILE: COPYING
================================================
                    GNU GENERAL PUBLIC LICENSE
                       Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU General Public License is a free, copyleft license for
software and other kinds of works.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.  We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors.  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights.  Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received.  You must make sure that they, too, receive
or can get the source code.  And you must show them these terms so they
know their rights.

  Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.

  For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software.  For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.

  Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so.  This is fundamentally incompatible with the aim of
protecting users' freedom to change the software.  The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable.  Therefore, we
have designed this version of the GPL to prohibit the practice for those
products.  If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.

  Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary.  To prevent this, the GPL assures that
patents cannot be used to render the program non-free.

  The precise terms and conditions for copying, distribution and
modification follow.

                       TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this
License.  Each licensee is addressed as "you".  "Licensees" and
"recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based
on the Program.

  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work
for making modifications to it.  "Object code" means any non-source
form of a work.

  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

  The Corresponding Source for a work in source code form is that
same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

  13. Use with the GNU Affero General Public License.

  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

  Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

  Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

  If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:

    <program>  Copyright (C) <year>  <name of author>
    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".

  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.

  The GNU General Public License does not permit incorporating your program
into proprietary programs.  If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.  But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.


================================================
FILE: LICENSES
================================================
==== File lib/osmdroid-android-*.jar ====

	osmtracker-android reuses code from osmdroid (http://osmdroid.googlecode.com/),
	released under LGPL.
	
		                   GNU LESSER GENERAL PUBLIC LICENSE
	                       Version 3, 29 June 2007
	
	 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
	 Everyone is permitted to copy and distribute verbatim copies
	 of this license document, but changing it is not allowed.
	
	
	  This version of the GNU Lesser General Public License incorporates
	the terms and conditions of version 3 of the GNU General Public
	License, supplemented by the additional permissions listed below.
	
	  0. Additional Definitions.
	
	  As used herein, "this License" refers to version 3 of the GNU Lesser
	General Public License, and the "GNU GPL" refers to version 3 of the GNU
	General Public License.
	
	  "The Library" refers to a covered work governed by this License,
	other than an Application or a Combined Work as defined below.
	
	  An "Application" is any work that makes use of an interface provided
	by the Library, but which is not otherwise based on the Library.
	Defining a subclass of a class defined by the Library is deemed a mode
	of using an interface provided by the Library.
	
	  A "Combined Work" is a work produced by combining or linking an
	Application with the Library.  The particular version of the Library
	with which the Combined Work was made is also called the "Linked
	Version".
	
	  The "Minimal Corresponding Source" for a Combined Work means the
	Corresponding Source for the Combined Work, excluding any source code
	for portions of the Combined Work that, considered in isolation, are
	based on the Application, and not on the Linked Version.
	
	  The "Corresponding Application Code" for a Combined Work means the
	object code and/or source code for the Application, including any data
	and utility programs needed for reproducing the Combined Work from the
	Application, but excluding the System Libraries of the Combined Work.
	
	  1. Exception to Section 3 of the GNU GPL.
	
	  You may convey a covered work under sections 3 and 4 of this License
	without being bound by section 3 of the GNU GPL.
	
	  2. Conveying Modified Versions.
	
	  If you modify a copy of the Library, and, in your modifications, a
	facility refers to a function or data to be supplied by an Application
	that uses the facility (other than as an argument passed when the
	facility is invoked), then you may convey a copy of the modified
	version:
	
	   a) under this License, provided that you make a good faith effort to
	   ensure that, in the event an Application does not supply the
	   function or data, the facility still operates, and performs
	   whatever part of its purpose remains meaningful, or
	
	   b) under the GNU GPL, with none of the additional permissions of
	   this License applicable to that copy.
	
	  3. Object Code Incorporating Material from Library Header Files.
	
	  The object code form of an Application may incorporate material from
	a header file that is part of the Library.  You may convey such object
	code under terms of your choice, provided that, if the incorporated
	material is not limited to numerical parameters, data structure
	layouts and accessors, or small macros, inline functions and templates
	(ten or fewer lines in length), you do both of the following:
	
	   a) Give prominent notice with each copy of the object code that the
	   Library is used in it and that the Library and its use are
	   covered by this License.
	
	   b) Accompany the object code with a copy of the GNU GPL and this license
	   document.
	
	  4. Combined Works.
	
	  You may convey a Combined Work under terms of your choice that,
	taken together, effectively do not restrict modification of the
	portions of the Library contained in the Combined Work and reverse
	engineering for debugging such modifications, if you also do each of
	the following:
	
	   a) Give prominent notice with each copy of the Combined Work that
	   the Library is used in it and that the Library and its use are
	   covered by this License.
	
	   b) Accompany the Combined Work with a copy of the GNU GPL and this license
	   document.
	
	   c) For a Combined Work that displays copyright notices during
	   execution, include the copyright notice for the Library among
	   these notices, as well as a reference directing the user to the
	   copies of the GNU GPL and this license document.
	
	   d) Do one of the following:
	
	       0) Convey the Minimal Corresponding Source under the terms of this
	       License, and the Corresponding Application Code in a form
	       suitable for, and under terms that permit, the user to
	       recombine or relink the Application with a modified version of
	       the Linked Version to produce a modified Combined Work, in the
	       manner specified by section 6 of the GNU GPL for conveying
	       Corresponding Source.
	
	       1) Use a suitable shared library mechanism for linking with the
	       Library.  A suitable mechanism is one that (a) uses at run time
	       a copy of the Library already present on the user's computer
	       system, and (b) will operate properly with a modified version
	       of the Library that is interface-compatible with the Linked
	       Version.
	
	   e) Provide Installation Information, but only if you would otherwise
	   be required to provide such information under section 6 of the
	   GNU GPL, and only to the extent that such information is
	   necessary to install and execute a modified version of the
	   Combined Work produced by recombining or relinking the
	   Application with a modified version of the Linked Version. (If
	   you use option 4d0, the Installation Information must accompany
	   the Minimal Corresponding Source and Corresponding Application
	   Code. If you use option 4d1, you must provide the Installation
	   Information in the manner specified by section 6 of the GNU GPL
	   for conveying Corresponding Source.)
	
	  5. Combined Libraries.
	
	  You may place library facilities that are a work based on the
	Library side by side in a single library together with other library
	facilities that are not Applications and are not covered by this
	License, and convey such a combined library under terms of your
	choice, if you do both of the following:
	
	   a) Accompany the combined library with a copy of the same work based
	   on the Library, uncombined with any other library facilities,
	   conveyed under the terms of this License.
	
	   b) Give prominent notice with the combined library that part of it
	   is a work based on the Library, and explaining where to find the
	   accompanying uncombined form of the same work.
	
	  6. Revised Versions of the GNU Lesser General Public License.
	
	  The Free Software Foundation may publish revised and/or new versions
	of the GNU Lesser General Public License from time to time. Such new
	versions will be similar in spirit to the present version, but may
	differ in detail to address new problems or concerns.
	
	  Each version is given a distinguishing version number. If the
	Library as you received it specifies that a certain numbered version
	of the GNU Lesser General Public License "or any later version"
	applies to it, you have the option of following the terms and
	conditions either of that published version or of any later version
	published by the Free Software Foundation. If the Library as you
	received it does not specify a version number of the GNU Lesser
	General Public License, you may choose any version of the GNU Lesser
	General Public License ever published by the Free Software Foundation.
	
	  If the Library as you received it specifies that a proxy can decide
	whether future versions of the GNU Lesser General Public License shall
	apply, that proxy's public statement of acceptance of any version is
	permanent authorization for you to choose that version for the
	Library.
	

==== File lib/slf4-android-*.jar ====
	
	osmtracker-android uses a library from slf4j (http://www.slf4j.org/android/)
	This library is published under the MIT license: http://www.slf4j.org/license.html
	

==== OpenStreetMap data ====

	Map tiles are provided by OpenStreetMap (http://www.openstreetmap.org/),
	under the Creative Commons Attribution-ShareAlike 2.0 license
	(http://creativecommons.org/licenses/by-sa/2.0/).

	(c) OpenStreetMap (and) contributors, CC-BY-SA
	
	
==== File res/raw/beep.mp3 ====

	http://www.freesound.org/samplesViewSingle.php?id=26777
	http://creativecommons.org/licenses/sampling+/1.0/


==== File res/raw/beepbeep.mp3 ====

	http://www.freesound.org/samplesViewSingle.php?id=107786
	http://creativecommons.org/licenses/sampling+/1.0/


================================================
FILE: README.md
================================================
# OSMTracker for Android™

![Build](https://github.com/labexp/osmtracker-android/actions/workflows/android.yml/badge.svg?branch=develop)
[![Coverage Status](https://coveralls.io/repos/github/labexp/osmtracker-android/badge.svg?branch=develop)](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.

![Main screen](https://wiki.openstreetmap.org/w/images/thumb/7/7b/OSMTracker-Android-main-screen-en.jpg/200px-OSMTracker-Android-main-screen-en.jpg)

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 📲

[<img alt='Get it on Google Play' src='https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png' height="80"/>](https://play.google.com/store/apps/details?id=net.osmtracker)
[<img src="https://f-droid.org/badge/get-it-on.png" alt="Get it on F-Droid" height="80">](https://f-droid.org/app/net.osmtracker)

## More Info ℹ️

- 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/)

## Community & Support 🌐

Join our **Telegram group** to connect with users, developers, translators, and contributors:  
👉 [https://t.me/OSMTracker](https://t.me/OSMTracker)

Use this space for real-time discussions, guidance, and support.  
For bug reports or feature requests, continue using the [GitHub Issues](https://github.com/labexp/osmtracker-android/issues) tracker.


## Note 📝

OSMTracker for Android™ official source code repository is [https://github.com/labexp/osmtracker-android](https://github.com/labexp/osmtracker-android).


================================================
FILE: app/build.gradle
================================================
plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'jacoco'
}

android {
    namespace 'net.osmtracker'
    compileSdk 35

    defaultConfig {
        applicationId "net.osmtracker"
        minSdk 25
        targetSdk 35
        multiDexEnabled true

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

        testApplicationId "net.osmtracker.test"
        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 {
        if (project.hasProperty('signing.storeFile')) {
            release {
                storeFile file(project.property('signing.storeFile'))
                storePassword project.property('signing.storePassword')
                keyAlias project.property('signing.keyAlias')
                keyPassword project.property('signing.keyPassword')
            }
        }
    }

    packagingOptions {
        resources.excludes += [
            'META-INF/DEPENDENCIES', 
            'META-INF/LICENSE.txt', 
            'META-INF/NOTICE.txt'
        ]
    }
    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',
                       '--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
    }
}

dependencies {
    // Lib to show OSM map as background
    implementation 'org.osmdroid:osmdroid-android:6.1.20'
    // OAuth
    implementation 'net.openid:appauth:0.11.1'
    implementation 'com.android.volley:volley:1.2.1'
    // 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'
    }
    // 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'

    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'
    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
    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"

    // 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'
}

repositories {
    google()
    mavenCentral()
    maven { url "https://jitpack.io" }
}

apply from: "jacoco.gradle"


================================================
FILE: app/jacoco.gradle
================================================
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'
    ]))
}

================================================
FILE: app/lint.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<lint>
    <issue id="ExtraTranslation" severity="ignore" />
    <issue id="HardcodedText" severity="ignore" />
    <issue id="MissingTranslation" severity="ignore" />
    <issue id="UnusedResources" severity="ignore" />
</lint>

================================================
FILE: app/src/androidTest/assets/gpx/gpx-test.gpx
================================================
<?xml version="1.0" encoding="UTF-8" ?>
<gpx xmlns="http://www.topografix.com/GPX/1/1" version="1.1" creator="OSMTracker for Android™ - https://github.com/labexp/osmtracker-android" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd ">
	<wpt lat="34.12" lon="18.45">
		<ele>5812.2</ele>
		<time>2012-03-12T16:46:38Z</time>
		<name><![CDATA[wp1]]></name>
		<link href="http%3A%2F%2Flink1.com">
			<text>http://link1.com</text>
		</link>
		<sat>2</sat>
		<hdop>0.0625</hdop>
	</wpt>
	<wpt lat="43.76" lon="31.89">
		<ele>75.4</ele>
		<time>2012-03-12T16:46:38Z</time>
		<name><![CDATA[wp2]]></name>
		<link href="http%3A%2F%2Flink2.com">
			<text>http://link2.com</text>
		</link>
		<sat>6</sat>
		<hdop>0.1525000035762787</hdop>
	</wpt>
	<trk>
		<name><![CDATA[Tracked with OSMTracker for Android™]]></name>
		<cmt><![CDATA[Warning: HDOP values aren't the HDOP as returned by the GPS device. They're approximated from the location accuracy in meters.]]></cmt>
		<trkseg>
			<trkpt lat="12.34" lon="56.78">
				<ele>4321.7</ele>
				<time>2012-03-12T16:46:38Z</time>
				<hdop>0.10499999672174454</hdop>
				<extensions>
					<speed>45.79999923706055</speed>
				</extensions>
			</trkpt>
			<trkpt lat="21.57" lon="12.6">
				<ele>12.1</ele>
				<time>2012-03-12T16:46:38Z</time>
				<hdop>0.05999999865889549</hdop>
				<extensions>
					<speed>12.600000381469727</speed>
				</extensions>
			</trkpt>
		</trkseg>
	</trk>
</gpx>


================================================
FILE: app/src/androidTest/java/net/osmtracker/activity/PreferencesTest.java
================================================
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()));
	}

}


================================================
FILE: app/src/androidTest/java/net/osmtracker/data/Mocks.java
================================================
package net.osmtracker.data;

public class Mocks {
    public static String MOCK_LAYOUT_CONTENT =
            "<layouts>"
                    +"    <layout name=\"root\">"
                    +"		<row>"
                    +"			<button type=\"tag\" label=\"A\"/>"
                    +"			<button type=\"tag\" label=\"B\"/>"
                    +"			<button type=\"tag\" label=\"C\"/>"
                    +"		</row>"
                    +"	</layout>"
                    +"</layouts>";
}


================================================
FILE: app/src/androidTest/java/net/osmtracker/layouts/DeleteLayoutTest.java
================================================
package net.osmtracker.layouts;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.longClick;
import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static net.osmtracker.util.TestUtils.checkToastIsShownWith;
import static net.osmtracker.util.TestUtils.getLayoutsDirectory;
import static net.osmtracker.util.TestUtils.getStringResource;
import static net.osmtracker.util.TestUtils.injectMockLayout;
import static net.osmtracker.util.TestUtils.listFiles;
import static org.apache.commons.io.FileUtils.deleteDirectory;
import static org.hamcrest.Matchers.equalToIgnoringCase;
import static org.junit.Assert.assertFalse;

import android.Manifest;

import androidx.lifecycle.Lifecycle;
import androidx.test.core.app.ActivityScenario;
import androidx.test.rule.GrantPermissionRule;

import net.osmtracker.R;
import net.osmtracker.activity.ButtonsPresets;
import net.osmtracker.db.DataHelper;
import net.osmtracker.util.CustomLayoutsUtils;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

import java.io.IOException;
import java.util.ArrayList;

public class DeleteLayoutTest {

	@Rule
	public GrantPermissionRule storagePermission = GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE);

	public ActivityScenario<ButtonsPresets> activity;

	private static final String layoutName = "mock";
	private static final String ISOLanguageCode = "es";

	@Before
	public void setUp() {
		// Makes sure that only the mock layout exists
		try {
			deleteDirectory(getLayoutsDirectory());
			injectMockLayout(layoutName, ISOLanguageCode);
		} catch (IOException e) {
			e.printStackTrace();
		}
		// Launch activity
		activity = ActivityScenario.launch(ButtonsPresets.class);
		activity.moveToState(Lifecycle.State.RESUMED);
	}

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

	/**
	 * Assumes being in the ButtonsPresets activity
	 * Deletes the layout with the received name
	 */
	private void deleteLayout() {
		onView(withText(layoutName)).perform(longClick());
		onView(withText(getStringResource(R.string.buttons_presets_context_menu_delete))).perform(click());
		String textToMatch = getStringResource(R.string.buttons_presets_delete_positive_confirmation);
		onView(withText(equalToIgnoringCase(textToMatch))).perform(click());
	}

	/**
	 * Deletes the mock layout and then checks that:
	 * - The UI option doesn't appear anymore
	 * - The XML file is deleted
	 * - A Toast is shown to inform about what happened
	 * - The icons directory is deleted
	 */
	@Test
	public void layoutDeletionTest() {
		deleteLayout();

		// Check the informative Toast is shown
		checkToastIsShownWith(getStringResource(R.string.buttons_presets_successful_delete));

		// Check the layout doesn't appear anymore
		onView(withText(layoutName)).check(doesNotExist());

		// List files after the deletion
		ArrayList<String> filesAfterDeletion = listFiles(getLayoutsDirectory());

		// Check the xml file was deleted
		String layoutFileName = CustomLayoutsUtils.createFileName(layoutName, ISOLanguageCode);
		assertFalse(filesAfterDeletion.contains(layoutFileName));

		// Check the icons folder was deleted
		assertFalse(filesAfterDeletion.contains(layoutName + DataHelper.LAYOUTS_ICONS_DIR_SUFFIX));
	}
}


================================================
FILE: app/src/androidTest/java/net/osmtracker/layouts/DownloadLayoutTest.java
================================================
package net.osmtracker.layouts;

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.assertion.ViewAssertions.matches;
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 androidx.lifecycle.Lifecycle;
import androidx.preference.PreferenceManager;
import androidx.test.core.app.ActivityScenario;
import androidx.test.espresso.Espresso;
import androidx.test.espresso.contrib.RecyclerViewActions;
import androidx.test.espresso.matcher.ViewMatchers;
import androidx.test.rule.GrantPermissionRule;

import net.osmtracker.OSMTracker;
import net.osmtracker.R;
import net.osmtracker.activity.TrackManager;
import net.osmtracker.util.TestUtils;

import org.apache.commons.io.FileUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

import java.util.Locale;

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);

	public ActivityScenario<TrackManager> activity;

	@Before
	public void setUp() {
		// Skip cool intro
		SharedPreferences dtPrefs = PreferenceManager
				.getDefaultSharedPreferences(getInstrumentation().getTargetContext());
		dtPrefs.edit().putBoolean(OSMTracker.Preferences.KEY_DISPLAY_APP_INTRO, false).apply();
		// Launch activity
		activity = ActivityScenario.launch(TrackManager.class);
		activity.moveToState(Lifecycle.State.RESUMED);
	}

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

	@Test
	public void downloadLayoutTest() {
		deleteLayoutsDirectory();

		TestUtils.setLayoutsTestingRepository();

		String layoutName = "abc";

		navigateToAvailableLayouts();

		clickButtonsToDownloadLayout(layoutName);

		makePostDownloadAssertions(layoutName);
	}


	public void deleteLayoutsDirectory() {
		try {
			FileUtils.deleteDirectory(TestUtils.getLayoutsDirectory());
		} catch (Exception e) {
			e.printStackTrace();
			fail();
		}
	}


	/**
	 * Assuming being in TrackManager
	 */
	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
		onView(ViewMatchers.withId(androidx.preference.R.id.recycler_view))
				.perform(RecyclerViewActions.actionOnItem(
						ViewMatchers.hasDescendant(withText(R.string.prefs_ui_buttons_layout)),
						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());
	}


	/**
	 * Check the new layouts appears as a new option
	 * Select the layout and check its buttons are shown when tracking
	 *
	 * @param layoutName layout name
	 */
	private void makePostDownloadAssertions(String layoutName) {
		Espresso.pressBack();

		// Check the layout appears as a new option in AvailableLayouts
		onView(withText(layoutName.toLowerCase())).check(matches(isDisplayed()));

		// Select the layout
		onView(withText(layoutName.toLowerCase())).perform(click());

		// Go to TrackLogger
		Espresso.pressBack();
		Espresso.pressBack();
		onView(withId(R.id.trackmgr_fab)).perform(click());

		// Check the buttons are loaded correctly
		String[] expectedButtonsLabels = new String[]{"A", "B", "C"};
		for (String label : expectedButtonsLabels)
			onView(withText(label)).check(matches(isDisplayed()));

	}


	private void clickButtonsToDownloadLayout(String layoutName) {
		onView(withText(layoutName)).perform(click());

		// Catch languages available dialog that shows up when the cell phone is not in English
		if (!Locale.getDefault().getLanguage().equalsIgnoreCase("en")) {
			onView(withText("English")).perform(click());
		}

		onView(withText(TestUtils.getStringResource(R.string.available_layouts_description_dialog_positive_confirmation))).
				perform(click());

		TestUtils.checkToastIsShownWith(TestUtils.getStringResource(R.string.available_layouts_successful_download));
	}
}

================================================
FILE: app/src/androidTest/java/net/osmtracker/layouts/RepositorySettingsDialogTest.java
================================================
package net.osmtracker.layouts;

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.closeSoftKeyboard;
import static androidx.test.espresso.action.ViewActions.typeText;
import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isChecked;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.isEnabled;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static net.osmtracker.util.TestUtils.checkToastIsShownWith;
import static net.osmtracker.util.TestUtils.getStringResource;
import static org.hamcrest.core.IsNot.not;

import android.view.View;

import androidx.lifecycle.Lifecycle;
import androidx.test.core.app.ActivityScenario;
import androidx.test.espresso.ViewAssertion;

import net.osmtracker.OSMTracker;
import net.osmtracker.R;
import net.osmtracker.activity.AvailableLayouts;

import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class RepositorySettingsDialogTest {

	public ActivityScenario<AvailableLayouts> activity;

	@Before
	public void setUp() {
		// Launch activity
		activity = ActivityScenario.launch(AvailableLayouts.class);
		activity.moveToState(Lifecycle.State.RESUMED);
	}

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

	@Test
	public void testToggleBehaviour() {
		onView(withId(R.id.github_config)).perform(click());

		onView(withId(R.id.default_server)).perform(click(), closeSoftKeyboard());
		checkStateAfterToggle(R.id.default_server, R.id.custom_server);
		checkTextFieldsState(not(isEnabled()));
		checkTextFieldsDefaultValues();

		onView(withId(R.id.custom_server)).perform(click(), closeSoftKeyboard());
		checkStateAfterToggle(R.id.custom_server, R.id.default_server);
		checkTextFieldsState(isEnabled());
	}

	@Test
	public void testRepositoryValidation() {
		String validUser = OSMTracker.Preferences.VAL_GITHUB_USERNAME;
		String validRepository = OSMTracker.Preferences.VAL_REPOSITORY_NAME;
		String validBranch = OSMTracker.Preferences.VAL_BRANCH_NAME;
		String invalidBranch = "NONE";

		checkRepositoryValidity(validUser, validRepository, validBranch, true);
		checkRepositoryValidity(validUser, validRepository, invalidBranch, false);
	}


	public void checkStateAfterToggle(int expectedActiveId, int expectedInactiveId) {
		onView(withId(expectedActiveId)).check(matches(not(isEnabled())));
		onView(withId(expectedActiveId)).check(matches(isChecked()));
		onView(withId(expectedInactiveId)).check(matches(not(isChecked())));
		onView(withId(expectedInactiveId)).check(matches(isEnabled()));
	}

	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());

		onView(withId(R.id.github_username)).perform(clearText(), typeText(user), closeSoftKeyboard());
		onView(withId(R.id.repository_name)).perform(clearText(), typeText(repo), closeSoftKeyboard());
		onView(withId(R.id.branch_name)).perform(clearText(), typeText(branch), closeSoftKeyboard());

		onView(withText(getStringResource(R.string.menu_save))).perform(click());

		String expectedMessage = (isValid) ? getStringResource(R.string.github_repository_settings_valid_server) :
				getStringResource(R.string.github_repository_settings_invalid_server);

		checkToastIsShownWith(expectedMessage);

		ViewAssertion expectedDialogState = (isValid) ? doesNotExist() : matches(isDisplayed());
		checkDialogState(expectedDialogState);
	}

	/**
	 * Check if the dialog is shown by looking for its title on the screen
	 */
	private void checkDialogState(ViewAssertion assertion) {
		onView(withText(getStringResource(R.string.prefs_ui_github_repository_settings))).check(assertion);
	}

	/**
	 * Check that the text fields values match the expected default ones
	 */
	private void checkTextFieldsDefaultValues() {
		onView(withId(R.id.repository_name)).check(matches(withText(OSMTracker.Preferences.VAL_REPOSITORY_NAME)));
		onView(withId(R.id.branch_name)).check(matches(withText(OSMTracker.Preferences.VAL_BRANCH_NAME)));
		onView(withId(R.id.github_username)).check(matches(withText(OSMTracker.Preferences.VAL_GITHUB_USERNAME)));
	}

	/**
	 * @param matcher can be isEnabled or not(isEnabled()) or any matcher
	 */
	public void checkTextFieldsState(Matcher<View> matcher) {

		onView(withId(R.id.github_username)).check(matches(matcher));
		onView(withId(R.id.repository_name)).check(matches(matcher));
		onView(withId(R.id.branch_name)).check(matches(matcher));
	}


}


================================================
FILE: app/src/androidTest/java/net/osmtracker/util/LogcatHelper.java
================================================
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;
    }
}


================================================
FILE: app/src/androidTest/java/net/osmtracker/util/TestUtils.java
================================================
package net.osmtracker.util;

import static net.osmtracker.util.LogcatHelper.checkLogForMessage;

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

import androidx.preference.PreferenceManager;
import androidx.test.platform.app.InstrumentationRegistry;

import net.osmtracker.OSMTracker;
import net.osmtracker.data.Mocks;
import net.osmtracker.db.DataHelper;

import java.io.File;
import java.io.FileWriter;
import java.util.ArrayList;

/**
 * Contains common and reusable static methods used for tests
 */
public class TestUtils {

    public static String TESTING_GITHUB_USER = "labexp";
    public static String TESTING_GITHUB_REPOSITORY = "osmtracker-android-layouts";
    public static String TESTING_GITHUB_BRANCH = "for_tests";

    /**
     * List all the files in a folder and return a list of the names
     */
    public static ArrayList<String> listFiles(File directory){
        ArrayList result = new ArrayList();
        for(File file : directory.listFiles()){
            result.add(file.getName());
        }
        return  result;
    }

    /**
     * Create a directory inside a directory and return the corresponding file
     */
    public static File createDirectory(File parentDir, String newDirName){
        File newDir = new File(parentDir.getAbsolutePath() + File.separator + newDirName);
        newDir.mkdir();
        return newDir;
    }

    /**
     * Create a file inside a directory
     */
    public static File createFile(File parentDir, String newFileName){
        File newFile = new File(parentDir.getAbsolutePath()+ File.separator + newFileName);
        return newFile;
    }

    /**
     * Install a mock layout in the phone
     *  - Creates the xml, the icons directory and some empty png files inside
     */
    public static void injectMockLayout(String layoutName, String ISOLangCode) {
        File layoutsDir = getLayoutsDirectory();

        // Create a mock layout file
        String layoutFileName = CustomLayoutsUtils.createFileName(layoutName, ISOLangCode);
        File newLayout = createFile(layoutsDir,layoutFileName);
        writeToFile(newLayout, Mocks.MOCK_LAYOUT_CONTENT);

        // Create the icons directory
        File iconsDir = createDirectory(layoutsDir, layoutName + DataHelper.LAYOUTS_ICONS_DIR_SUFFIX);

        // And put some mock files inside
        int pngsToCreate = 4;
        File png;
        for (int i = 1; i <= pngsToCreate; i++) {
            png = createFile(iconsDir, i+".png");
            writeToFile(png, "foo");
        }
    }


    /**
     * Write content to a file
     */
    public static void writeToFile(File file, String content){
        try{
            FileWriter writer = new FileWriter(file);
            writer.write(content);
            writer.close();
        }catch (Exception e){
            System.out.println("Error writing to file");
        }
    }

    /**
     * Get the app's storage directory
     * - If it doesn't exist then should create it before returning
     */
    public static File getAppDirectory(){
        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
        String storageDir =  context.getExternalFilesDir(null).getAbsolutePath();
        File appDirectory = new File(storageDir + OSMTracker.Preferences.VAL_STORAGE_DIR);
        appDirectory.mkdirs();
        return appDirectory;
    }

    /**
     * Get the app's layouts directory
     * - If it doesn't exist then should create it before returning
     */
    public static File getLayoutsDirectory(){
        String appDirectory = getAppDirectory().getAbsolutePath();
        File layoutsDirectory = new File(appDirectory + File.separator + DataHelper.LAYOUTS_SUBDIR);
        layoutsDirectory.mkdirs();
        return layoutsDirectory;
    }

    public static void checkToastIsShownWith(String text){
        // 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){
        return InstrumentationRegistry.getInstrumentation().getTargetContext().getString(resourceId);
    }

    public static void setGithubRepositorySettings(String user, String repo, String branch){
        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
        SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();
        editor.putString(OSMTracker.Preferences.KEY_GITHUB_USERNAME, user);
        editor.putString(OSMTracker.Preferences.KEY_REPOSITORY_NAME, repo);
        editor.putString(OSMTracker.Preferences.KEY_BRANCH_NAME, branch);
        editor.commit();
    }

    public static void setLayoutsTestingRepository(){
        setGithubRepositorySettings(TESTING_GITHUB_USER,TESTING_GITHUB_REPOSITORY,TESTING_GITHUB_BRANCH);
    }

}


================================================
FILE: app/src/androidTest/java/net/osmtracker/util/ToastMatcher.java
================================================
package net.osmtracker.util;

import android.os.IBinder;
import androidx.test.espresso.Root;
import android.view.WindowManager;

import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;

/**
 * Used to search for Toasts in the UI
 * This class was taken from <a href="https://stackoverflow.com/questions/28390574/checking-toast-message-in-android-espresso">stackoverflow</a>
 */
public class ToastMatcher extends TypeSafeMatcher<Root> {

    @Override
    public void describeTo(Description description) {
        description.appendText("is toast");
    }

    @Override
    public boolean matchesSafely(Root root) {
        int type = root.getWindowLayoutParams().get().type;
        if ((type == WindowManager.LayoutParams.TYPE_TOAST)) {
            IBinder windowToken = root.getDecorView().getWindowToken();
            IBinder appToken = root.getDecorView().getApplicationWindowToken();
            if (windowToken == appToken) {
                return true;
            }
        }
        return false;
    }
}

================================================
FILE: app/src/androidTest/java/net/osmtracker/util/WaitForView.java
================================================
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<View> 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<View> 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);
    }
}


================================================
FILE: app/src/androidTest/res/values/strings.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">OSMTrackerTestsTest</string>

</resources>

================================================
FILE: app/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:installLocation="auto">

    <!-- Location permissions -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION"/>

    <!-- Foreground services and network permissions -->
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>

    <!-- External storage permissions -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

    <application
        android:description="@string/app_description"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/HighContrast"
        android:requestLegacyExternalStorage="true"
        android:usesCleartextTraffic="true" >

        <uses-library
            android:name="org.apache.http.legacy"
            android:required="false" />

        <activity android:name="net.osmtracker.activity.Intro"
            android:label="@string/app_intro"
            android:theme="@style/AppIntroStyle"/>
        <activity
            android:name=".activity.TrackManager"
            android:theme="@style/AppTheme"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".activity.TrackLogger"
            android:label="@string/tracklogger"
            android:launchMode="singleTop" />
        <activity
            android:name=".activity.Preferences"
            android:exported="false"
            android:label="@string/prefs"
            android:theme="@style/AppTheme" />
        <activity
            android:name=".activity.WaypointList"
            android:label="@string/wplist" />
        <activity
            android:name=".activity.NoteList"
            android:theme="@style/AppTheme"
            android:label="@string/notelist" />
        <activity
            android:name=".activity.TrackDetail"
            android:label="@string/trackdetail" />
        <activity
            android:name=".activity.OpenStreetMapUpload"
            android:label="@string/osm_upload"
            android:exported="true">
        </activity>
        <activity android:name=".activity.OpenStreetMapNotesUpload"
            android:theme="@style/AppTheme"/>
        <activity
            android:name="net.openid.appauth.RedirectUriReceiverActivity"
            tools:node="replace" android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data
                    android:scheme="osmtracker"/>
            </intent-filter>
        </activity>
        <activity
            android:name=".activity.GitHubUpload"
            android:label="Github" />
        <activity
            android:name=".activity.GitHubConfig"
            android:label="Github" />
        <activity
            android:name=".activity.GitHubNewRepo"
            android:label="Github" />
        <activity
            android:name=".activity.GitHubNewFork"
            android:label="Github" />
        <activity
            android:name=".activity.GitHubPullRequest"
            android:label="Github" />
        <activity
            android:name=".activity.About"
            android:label="@string/about" />
        <activity
            android:name=".activity.DisplayTrack"
            android:label="@string/displaytrack" />
        <activity
            android:name=".activity.DisplayTrackMap"
            android:label="@string/displaytrackmap" />
        <activity android:name=".activity.ButtonsPresets"
            android:exported="false">
            <intent-filter>
                <action android:name="launch_buttons_presets" />

                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
        <activity android:name=".activity.AvailableLayouts" />

        <service
            android:name=".service.gps.GPSLogger"
            android:foregroundServiceType="location"
            android:exported="false">
            <intent-filter>
                <action android:name="osmtracker.intent.GPS_SERVICE" />
            </intent-filter>
        </service>

        <provider
            android:name=".db.TrackContentProvider"
            android:authorities="net.osmtracker.provider"
            android:exported="false" />

        <receiver android:name=".receiver.MediaButtonReceiver"
            android:exported="false">
            <intent-filter>
                <action android:name="android.intent.action.MEDIA_BUTTON" />
            </intent-filter>
        </receiver>

        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="net.osmtracker.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/filepaths" />
        </provider>

    </application>

</manifest>


================================================
FILE: app/src/main/java/net/osmtracker/GitHubUser.java
================================================
package net.osmtracker;

import net.osmtracker.github.*;

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

public class GitHubUser {
    private SharedPreferences sharedPreferences;

    public GitHubUser(Context context) {
        sharedPreferences = context.getSharedPreferences(GitHubConstants.SHARED_PREFS_NAME, Context.MODE_PRIVATE);
    }

    public void saveCredentials(String username, String token) {
        sharedPreferences.edit()
                .putString(GitHubConstants.KEY_USERNAME, username)
                .putString(GitHubConstants.KEY_TOKEN, token)
                .apply();
    }

    public String getUsername() {
        return sharedPreferences.getString(GitHubConstants.KEY_USERNAME, "");
    }

    public String getToken() {
        return sharedPreferences.getString(GitHubConstants.KEY_TOKEN, "");
    }

    public boolean hasCredentials() {
        return !getUsername().isEmpty() && getToken().length() == 40;
    }

    public void clear() {
        sharedPreferences.edit().clear().apply();
    }
}


================================================
FILE: app/src/main/java/net/osmtracker/OSMTracker.java
================================================
package net.osmtracker;


/**
 * Constants & app-wide variables.
 * 
 * @author Nicolas Guillaumin
 * 
 */
public class OSMTracker {

	/**
	 * Stores settings keys and default values.
	 * See preferences.xml for layout, strings-preferences.xml for text.
	 */
	public static final class Preferences {
		// Property names
		public final static String KEY_STORAGE_DIR = "logging.storage.dir";
		public final static String KEY_VOICEREC_DURATION = "voicerec.duration";
		public final static String KEY_UI_THEME = "ui.theme";
		public final static String KEY_GPS_OSSETTINGS = "gps.ossettings";
		public final static String KEY_GPS_CHECKSTARTUP = "gps.checkstartup";
		public final static String KEY_GPS_IGNORE_CLOCK = "gps.ignoreclock";
		public final static String KEY_GPS_LOGGING_INTERVAL = "gps.logging.interval";
		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_USE_NOTES = "gpx.notes";
		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";
		public final static String KEY_OUTPUT_COMPASS = "gpx.compass_heading";
		
		public final static String KEY_UI_PICTURE_SOURCE = "ui.picture.source";
		public final static String KEY_UI_BUTTONS_LAYOUT = "ui.buttons.layout";
		public final static String KEY_UI_DISPLAYTRACK_OSM = "ui.displaytrack.osm";
		/** True if DisplayTrack activity has already asked the user whether they'd rather see DisplayTrackMap. */
		public static final String KEY_UI_ASKED_DISPLAYTRACK_OSM = "ui.displaytrack.asked_osm";
		public final static String KEY_UI_DISPLAY_KEEP_ON = "ui.display_keep_on";
		public final static String KEY_UI_MAP_TILE = "ui.map.tile";
		public final static String KEY_SOUND_ENABLED = "sound_enabled";
		public final static String KEY_UI_ORIENTATION = "ui.orientation";
		public final static String KEY_OSM_TRACK_VISIBILITY = "osm.track.visibility";
		public final static String KEY_OSM_OAUTH2_ACCESSTOKEN = "osm.oauth2.accesstoken";
		public final static String KEY_OSM_OAUTH_CLEAR_DATA = "osm.oauth.clear-data";

		//keys for repository settings
		public final static String KEY_GITHUB_USERNAME = "github_username";
		public final static String KEY_REPOSITORY_NAME = "repository_name";
		public final static String KEY_BRANCH_NAME = "branch_name";

		// intro flag
		public final static String KEY_DISPLAY_APP_INTRO = "app.intro";

		// Default values
		public final static String VAL_STORAGE_DIR = "/osmtracker";
		public final static String VAL_VOICEREC_DURATION = "2";
		public final static String VAL_UI_THEME = "net.osmtracker:style/DefaultTheme";
		public final static boolean VAL_GPS_CHECKSTARTUP = true;
		public final static boolean VAL_GPS_IGNORE_CLOCK = false;
		public final static String VAL_GPS_LOGGING_INTERVAL = "0";
		public final static String VAL_GPS_LOGGING_MIN_DISTANCE = "0";
		public final static boolean VAL_USE_BAROMETER = false;
		public final static String VAL_USE_NOTES = "both";
		
		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";
		public final static String VAL_OUTPUT_ACCURACY_WPT_CMT = "wpt_cmt";
		public final static String VAL_OUTPUT_ACCURACY = VAL_OUTPUT_ACCURACY_NONE;

		public final static String VAL_OUTPUT_COMPASS_NONE = "none";
		public final static String VAL_OUTPUT_COMPASS_COMMENT = "comment";
		public final static String VAL_OUTPUT_COMPASS_EXTENSION = "extension";
		public final static String VAL_OUTPUT_COMPASS = VAL_OUTPUT_COMPASS_NONE;
		
		public final static boolean VAL_OUTPUT_GPX_HDOP_APPROXIMATION = false;
		public final static boolean VAL_OUTPUT_GPX_OUTPUT_DIR_PER_TRACK = true;

		public final static String VAL_UI_PICTURE_SOURCE_CAMERA = "camera";
		public final static String VAL_UI_PICTURE_SOURCE_GALLERY = "gallery";
		public final static String VAL_UI_PICTURE_SOURCE_ASK = "ask";
		public final static String VAL_UI_PICTURE_SOURCE = VAL_UI_PICTURE_SOURCE_CAMERA;
		
		public final static String VAL_UI_BUTTONS_LAYOUT = "default";
		public final static boolean VAL_UI_DISPLAYTRACK_OSM = false;
		public final static boolean VAL_UI_DISPLAY_KEEP_ON = true;
		public final static boolean VAL_SOUND_ENABLED = true;
		// Matches Track.OSMVisibility.Identifiable;
		public final static String VAL_OSM_TRACK_VISIBILITY = "Identifiable";
		public final static String VAL_UI_ORIENTATION_NONE = "none";
		public final static String VAL_UI_ORIENTATION_PORTRAIT = "portrait";
		public final static String VAL_UI_ORIENTATION_LANDSCAPE = "landscape";
		public final static String VAL_UI_ORIENTATION = VAL_UI_ORIENTATION_NONE;
		
		public final static String VAL_UI_MAP_TILE_MAPNIK = "MAPNIK";

		//default values for repository settings
		public final static String VAL_GITHUB_USERNAME = "labexp";
		public final static String VAL_REPOSITORY_NAME = "osmtracker-android-layouts";
		public final static String VAL_BRANCH_NAME = "master";

		// intro flag
		public final static boolean VAL_DISPLAY_APP_INTRO = true;


	};
	
	/**
	 * The full Package name of OSMTracker returned by calling
	 * OSMTracker.class.getPackage().getName()
	 */
	public final static String PACKAGE_NAME = OSMTracker.class.getPackage().getName();

	/**
	 * Intent for tracking a waypoint
	 */
	public final static String INTENT_TRACK_WP = OSMTracker.PACKAGE_NAME + ".intent.TRACK_WP";

	/**
	 * Intent for updating a previously tracked waypoint
	 */
	public final static String INTENT_UPDATE_WP = OSMTracker.PACKAGE_NAME + ".intent.UPDATE_WP";
	
	/**
	 * Intent for deleting a previously tracked waypoint
	 */
	public final static String INTENT_DELETE_WP = OSMTracker.PACKAGE_NAME + ".intent.DELETE_WP";

	/**
	 * Intent for tracking a note
	 */
	public final static String INTENT_TRACK_NOTE = OSMTracker.PACKAGE_NAME + ".intent.TRACK_NOTE";
	/**
	 * Intent for updating a previously tracked waypoint
	 */
	public final static String INTENT_UPDATE_NOTE = OSMTracker.PACKAGE_NAME + ".intent.UPDATE_NOTE";

	/**
	 * Intent to start tracking
	 */
	public final static String INTENT_START_TRACKING = OSMTracker.PACKAGE_NAME + ".intent.START_TRACKING";

	/**
	 * Intent to stop tracking
	 */
	public final static String INTENT_STOP_TRACKING = OSMTracker.PACKAGE_NAME + ".intent.STOP_TRACKING";

	/**
	 * Key for extra data "waypoint name" in Intent
	 */
	public final static String INTENT_KEY_NAME = "name";

	/**
	 * Key for extra data "link" in Intent
	 */
	public final static String INTENT_KEY_LINK = "link";
	
	/**
	 * Key for extra data "uuid" in Intent
	 */
	public final static String INTENT_KEY_UUID = "uuid";
	
	/**
	 * Approximation factor for calculating Horizontal Dilution of Precision
	 * from location.getAccuracy(). location.getAccuracy() returns an accuracy measured
	 * in meters, and HDOP is obtained by dividing accuracy by this factor.
	 * The value is totally false (!), but is still useful for certain use case like
	 * track display in JOSM.
	 * See: http://code.google.com/p/osmtracker-android/issues/detail?id=15 
	 */
	public final static int HDOP_APPROXIMATION_FACTOR = 4;
	
	/**
	 * time (in ms) we use to handle a key press as a long press
	 */
	public final static long LONG_PRESS_TIME = 1000;
	
	/** Device string identifiers */
	public static final class Devices {
		public static final String NEXUS_S = "Nexus S";
	}
}

================================================
FILE: app/src/main/java/net/osmtracker/activity/About.java
================================================
package net.osmtracker.activity;

import net.osmtracker.OSMTracker;
import net.osmtracker.R;
import net.osmtracker.db.DatabaseHelper;
import net.osmtracker.db.ExportDatabaseTask;

import android.app.Activity;
import android.app.AlertDialog;
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;
import android.os.Environment;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;

import androidx.preference.PreferenceManager;

import java.io.File;

/**
 * Simply display the about screen.
 * 
 * @author Nicolas Guillaumin
 *
 */
public class About extends Activity {

	public static final int DIALOG_EXPORT_DB = 0;
	public static final int DIALOG_EXPORT_DB_COMPLETED = 1;

	private ProgressDialog exportDbProgressDialog;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.about);

		// Retrieve app. version number
		try {
			PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(), 0);
			((TextView) findViewById(R.id.about_version)).setText(pi.versionName);
		} catch (NameNotFoundException nnfe) { 
			// Should not occur
		}

		findViewById(R.id.about_debug_info_button).setOnClickListener(
				new OnClickListener() {
					
					@Override
					public void onClick(View v) {
						new AlertDialog.Builder(v.getContext())
							.setTitle(R.string.about_debug_info)
							.setMessage(getDebugInfo())
							.setCancelable(true)
							.setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener() {
								@Override
								public void onClick(DialogInterface dialog, int which) {
									dialog.dismiss();
								}
							})
							.create().show();						
					}
				}
		);

		findViewById(R.id.about_export_db_button).setOnClickListener(
				new OnClickListener() {
					@Override
					public void onClick(View view) {
						showDialog(DIALOG_EXPORT_DB);

						File dbFile = getDatabasePath(DatabaseHelper.DB_NAME);
						File targetFolder = new File(
								Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS),
								//Environment.getExternalStorageDirectory(),
								PreferenceManager.getDefaultSharedPreferences(About.this).getString(
										OSMTracker.Preferences.KEY_STORAGE_DIR,
										OSMTracker.Preferences.VAL_STORAGE_DIR));

						new ExportDatabaseTask(About.this, targetFolder)
								.execute(dbFile);
					}
				}
		);
	}

	@Override
	protected Dialog onCreateDialog(int id, Bundle args) {
		switch(id) {
			case DIALOG_EXPORT_DB:
				exportDbProgressDialog = new ProgressDialog(this);
				exportDbProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
				exportDbProgressDialog.setIndeterminate(false);
				exportDbProgressDialog.setProgress(0);
				exportDbProgressDialog.setMax(100);
				exportDbProgressDialog.setCancelable(false);
				exportDbProgressDialog.setMessage(getResources().getString(R.string.about_exporting_db));
				exportDbProgressDialog.show();
				return exportDbProgressDialog;
			case DIALOG_EXPORT_DB_COMPLETED:
				new AlertDialog.Builder(this)
						.setTitle(R.string.about_export_db)
						.setIcon(android.R.drawable.ic_dialog_info)
						.setMessage(getString(R.string.about_export_db_result, args.getString("result")))
						.setCancelable(true)
						.setNeutralButton(android.R.string.ok,
							new DialogInterface.OnClickListener() {
								@Override
								public void onClick(DialogInterface dialog, int which) {
									dialog.dismiss();
								}
						})
						.create()
						.show();
		}

		return null;
	}

	public ProgressDialog getExportDbProgressDialog() {
		return exportDbProgressDialog;
	}

	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"
				+ "Export External Public Storage Directory: '"
				+ baseExportDirectory + "'\n";
	}

}


================================================
FILE: app/src/main/java/net/osmtracker/activity/AvailableLayouts.java
================================================
package net.osmtracker.activity;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import androidx.preference.PreferenceManager;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import net.osmtracker.OSMTracker;
import net.osmtracker.R;
import net.osmtracker.layout.DownloadCustomLayoutTask;
import net.osmtracker.layout.GetStringResponseTask;
import net.osmtracker.layout.URLValidatorTask;
import net.osmtracker.util.CustomLayoutsUtils;
import net.osmtracker.util.URLCreator;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;

import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Set;

/**
 * Created by emmanuel on 10/11/17.
 */

public class AvailableLayouts extends Activity {

    private final static String TMP_SHARED_PREFERENCES_FILE = "net.osmtracker.tmpspfile";

    //this variable indicates if the default github configuration is activated
    private boolean isDefChecked;
    private SharedPreferences sharedPrefs;
    private SharedPreferences.Editor editor;

    //options for repository settings
    private EditText etxGithubUsername;
    private EditText etxRepositoryName;
    private EditText etxBranchName;
    private CheckBox defaultServerCheckBox;
    private CheckBox customServerCheckBox;

    private boolean checkBoxPressed;

    public static final int ISO_CHARACTER_LENGTH = 2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
        editor = sharedPrefs.edit();
        setTitle(getResources().getString(R.string.prefs_ui_available_layout));
        // call task to download and parse the response to get the list of available layouts
        if (isNetworkAvailable(this)) {
            validateDefaultOptions();
        } else {
            Toast.makeText(getApplicationContext(),getResources().getString(R.string.available_layouts_connection_error),Toast.LENGTH_LONG).show();
            finish();
        }
    }

    @SuppressLint("StaticFieldLeak")
    public void validateDefaultOptions(){
        String usernameGitHub = sharedPrefs.getString(OSMTracker.Preferences.KEY_GITHUB_USERNAME, OSMTracker.Preferences.VAL_GITHUB_USERNAME);
        String repositoryName = sharedPrefs.getString(OSMTracker.Preferences.KEY_REPOSITORY_NAME, OSMTracker.Preferences.VAL_REPOSITORY_NAME);
        String branchName = sharedPrefs.getString(OSMTracker.Preferences.KEY_BRANCH_NAME, OSMTracker.Preferences.VAL_BRANCH_NAME);
        final String[] repositoryDefaultOptions = {usernameGitHub, repositoryName, branchName};
        //we verify if the entered options are correct
        new URLValidatorTask(){
            protected void onPostExecute(Boolean result){
                //validating the github repository
                if(result){
                    retrieveAvailableLayouts();
                }else{
                    Toast.makeText(getApplicationContext(),getResources().getString(R.string.available_layouts_response_null_exception),Toast.LENGTH_LONG).show();
                    finish();
                }
            }
        }.execute(repositoryDefaultOptions);
    }

    @SuppressLint("StaticFieldLeak")
    public void retrieveAvailableLayouts(){
        //while it makes the request
        final String waitingMessage = getResources().getString(R.string.available_layouts_connecting_message);
        setTitle(getResources().getString(R.string.prefs_ui_available_layout) + waitingMessage);
        String url = URLCreator.createMetadataDirUrl(this);
        new GetStringResponseTask() {
            protected void onPostExecute(String response) {
                if(response == null){
                    Toast.makeText(getApplicationContext(),getResources().getString(R.string.available_layouts_response_null_exception),Toast.LENGTH_LONG).show();
                    finish();
                }
                else{
                    setContentView(R.layout.available_layouts);
                    setAvailableLayouts(parseResponse(response));
                    //when the request is done
                    setTitle(getResources().getString(R.string.prefs_ui_available_layout));
                }
            }

        }.execute(url);
    }

    /**
     * It's used for asking there is internet before doing any other networking
     */
    public static boolean isNetworkAvailable(Context context) {
        ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();

        return activeNetworkInfo != null && activeNetworkInfo.isConnected();
    }


    /**
     * It receives a string list with the names of the layouts to be listed in the activity
     */
    public void setAvailableLayouts(List<String> options) {
        LinearLayout rootLayout = (LinearLayout)findViewById(R.id.root_layout);
        int AT_START = 0; //the position to insert the view at
        ClickListener listener = new ClickListener();
        Log.e("#",options.toString());
        for(String option : options) {
            Button layoutButton = new Button(this);
            layoutButton.setHeight(150);
            layoutButton.setText(CustomLayoutsUtils.convertFileName(option));
            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(50, 10, 50, 10);
            layoutButton.setLayoutParams(layoutParams);
            layoutButton.setPadding(40, 30, 40, 30);
            layoutButton.setOnClickListener(listener);
            rootLayout.addView(layoutButton,AT_START);
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.github_repository_settings_menu, menu);
        return super.onCreateOptionsMenu(menu);
    }

    //this override method creates the github repository settings windows, and upload the values in the shared preferences file if those changed
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    @Override
    public boolean onOptionsItemSelected(final MenuItem item) {
        if(item.getItemId() == R.id.github_config){
            LayoutInflater inflater = (LayoutInflater) getBaseContext().getSystemService(LAYOUT_INFLATER_SERVICE);
            //this is for prevent any error with the inflater
            assert inflater != null;
            //This is the pop up that's appears when the config button in the top right corner is pressed
            @SuppressLint("InflateParams") final View repositoryConfigWindow = inflater.inflate(R.layout.github_repository_settings, null);
            //instancing the edit texts of the layoutName inflate
            etxGithubUsername = (EditText) repositoryConfigWindow.findViewById(R.id.github_username);
            etxRepositoryName = (EditText) repositoryConfigWindow.findViewById(R.id.repository_name);
            etxBranchName = (EditText) repositoryConfigWindow.findViewById(R.id.branch_name);
            //instancing the checkbox option and setting the click listener
            defaultServerCheckBox = (CheckBox) repositoryConfigWindow.findViewById(R.id.default_server);
            customServerCheckBox = (CheckBox) repositoryConfigWindow.findViewById(R.id.custom_server);

            //internal private shared preferences to manage the incorrect server requested by the user
            final SharedPreferences tmpSharedPref = getApplicationContext().getSharedPreferences(TMP_SHARED_PREFERENCES_FILE, Context.MODE_PRIVATE);
            //flag to manage if the user put an invalid server
            boolean isCallBack = tmpSharedPref.getBoolean("isCallBack", false);

            //if the user put an invalid GitHub server, the user can edit the values again until the server is valid
            if(!isCallBack){
                //first, we verify if the default checkbox is activated, if true we put the default options into the edit texts and make them not editable
                if(sharedPrefs.getBoolean("defCheck", true)){
                    toggleRepositoryOptions(true);
                }
                //if the default checkbox isn't checked we put the shared preferences values into the edit texts
                else{
                    toggleRepositoryOptions(false);
                }
            }
            else{
                toggleRepositoryOptions(false);
                etxGithubUsername.setText(tmpSharedPref.getString(OSMTracker.Preferences.KEY_GITHUB_USERNAME, ""));
                etxRepositoryName.setText(tmpSharedPref.getString(OSMTracker.Preferences.KEY_REPOSITORY_NAME, ""));
                etxBranchName.setText(tmpSharedPref.getString(OSMTracker.Preferences.KEY_BRANCH_NAME, ""));
            }

            checkBoxPressed = false;

            defaultServerCheckBox.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    checkBoxPressed = true;
                    toggleRepositoryOptions(true);
                    isDefChecked = true;
                    //we save the status into the sharedPreferences file
                    editor.putBoolean("defCheck", isDefChecked);
                    editor.commit();
                }
            });
            customServerCheckBox.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    checkBoxPressed = true;
                    toggleRepositoryOptions(false);
                    isDefChecked = false;
                    //we save the status into the sharedPreferences file
                    editor.putBoolean("defCheck", isDefChecked);
                    editor.commit();
                }
            });
            //creating the alert dialog with the github_repository_setting view
            new AlertDialog.Builder(this)
                    .setTitle(getResources().getString(R.string.prefs_ui_github_repository_settings))
                    .setView(repositoryConfigWindow)
                    .setPositiveButton(getResources().getString(R.string.menu_save), new DialogInterface.OnClickListener() {
                        @SuppressLint("StaticFieldLeak")
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            final String[] repositoryCustomOptions = {etxGithubUsername.getText().toString(), etxRepositoryName.getText().toString(), etxBranchName.getText().toString()};
                            //we verify if the entered options are correct
                            new URLValidatorTask(){
                                protected void onPostExecute(Boolean result){
                                    //validating the github repository
                                    if(result){
                                        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]);
                                        editor.putString(OSMTracker.Preferences.KEY_BRANCH_NAME, repositoryCustomOptions[2]);
                                        editor.commit();
                                        //to avoid the request of invalid server at the beginning
                                        tmpSharedPref.edit().putBoolean("isCallBack", false).commit();
                                        retrieveAvailableLayouts();
                                    }else{
                                        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();
                                        //to make a request at the beginning of pop-up
                                        tmpSharedPref.edit().putBoolean("isCallBack", true).commit();
                                        onOptionsItemSelected(item);
                                    }
                                }
                            }.execute(repositoryCustomOptions);
                        }
                    })
                    .setNegativeButton(getResources().getString(R.string.menu_cancel), new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            tmpSharedPref.edit().putBoolean("isCallBack", false).commit();
                            if (checkBoxPressed){
                                if(!isDefChecked){
                                    toggleRepositoryOptions(true);
                                    isDefChecked = true;
                                    //save the status into the sharedPreferences file
                                    editor.putBoolean("defCheck", isDefChecked);
                                    editor.commit();
                                }
                                else{
                                    toggleRepositoryOptions(false);
                                    isDefChecked = false;
                                    //save the status into the sharedPreferences file
                                    editor.putBoolean("defCheck", isDefChecked);
                                    editor.commit();
                                }
                            }
                            dialog.cancel();
                        }
                    })
                    .setCancelable(true)
                    .create().show();
        }
        return super.onOptionsItemSelected(item);
    }

    /*
    * This toggles (default/custom) the states of repository settings options in function of boolean param
    * status true: tries to activated default options
    * status false: tries to activated custom options
    * */
    private void toggleRepositoryOptions(boolean status){
        customServerCheckBox.setChecked(!status);
        customServerCheckBox.setEnabled(status);
        defaultServerCheckBox.setChecked(status);
        defaultServerCheckBox.setEnabled(!status);
        etxGithubUsername.setEnabled(!status);
        etxBranchName.setEnabled(!status);
        etxRepositoryName.setEnabled(!status);

        //setting the default options into text fields
        if(status){
            etxGithubUsername.setText(OSMTracker.Preferences.VAL_GITHUB_USERNAME);
            etxRepositoryName.setText(OSMTracker.Preferences.VAL_REPOSITORY_NAME);
            etxBranchName.setText(OSMTracker.Preferences.VAL_BRANCH_NAME);
        }
        //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, OSMTracker.Preferences.VAL_REPOSITORY_NAME));
            etxBranchName.setText(sharedPrefs.getString(OSMTracker.Preferences.KEY_BRANCH_NAME, OSMTracker.Preferences.VAL_BRANCH_NAME));
        }
    }

    /*
    parse the string (representation of a json) to get only the values associated with
    key "name", which are the file names of the folder requested before.
    */
    private List<String> parseResponse(String response) {
        List<String> options = new ArrayList<String>();
        try {
            // create JSON Object
            JSONArray jsonArray = new JSONArray(response);
            for (int i= 0; i < jsonArray.length(); i++) {
                // create json object for every element of the array
                JSONObject object = jsonArray.getJSONObject(i);
                // get the value associated with
                options.add( object.getString("name") );
            }
        } catch (JSONException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return null;
        }
        return options;
    }

    /**
     * @param xmlFile is the meta xmlFile put in a String
     * @return a HashMap like (LanguageName,IsoCode) Example: English -> en.
     */
    private HashMap<String,String> getLanguagesFor(String xmlFile){
        HashMap<String,String> languages = new HashMap<String,String>();
        try{
            XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
            parser.setInput (new ByteArrayInputStream(xmlFile.getBytes()),"UTF-8");
            int eventType = parser.getEventType();
            while(eventType != XmlPullParser.END_DOCUMENT){
                //Move to a <option> tag
                if(parser.getEventType() == XmlPullParser.START_TAG
                        && parser.getName().equals("option")){
                    String name = parser.getAttributeValue(null,"name");
                    String iso = parser.getAttributeValue(null,"iso");
                    languages.put(name,iso);
                }
                eventType = parser.next();
            }
        }catch(Exception e){
            e.printStackTrace();
        }
        return languages;
    }

    /* xmlFile is the XML meta file parsed to string
    *  localeLanguage is the ISO code of the phone's locale language
    * Searches a description in the locale language and returns it if it is in xmlFile
    * or null if it is not there
    */
    private String getDescriptionFor(String xmlFile, String localeLanguage){
        String description = null;
        try{
            XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
            parser.setInput (new ByteArrayInputStream(xmlFile.getBytes()),"UTF-8");
            int eventType = parser.getEventType();

            while(eventType != XmlPullParser.END_DOCUMENT
                    && description == null ){
                if(eventType == XmlPullParser.START_TAG
                        && parser.getName().equals("option")){
                    //We are in an option start tag
                    //Ask for the option's iso
                    String iso = parser.getAttributeValue("","iso");
                    if(iso != null && iso.equals(localeLanguage)){
                        //If the start tag has "iso" attribute and matches the locale language
                        //Move to the content to the tag
                        parser.next();
                        //Save its content
                        description = parser.getText();
                    }
                }
                eventType = parser.next();
            }

        }catch(Exception e){
            Log.e("#","Error parsing metadata files: "+e.toString());
        }
        return description;
    }

    private void showDescriptionDialog(String layoutName, String description, String iso){
        AlertDialog.Builder b = new AlertDialog.Builder(this);
        b.setTitle(layoutName);
        b.setNegativeButton(getResources().getString(R.string.menu_cancel),null);
        b.setPositiveButton(getResources().getString(R.string.available_layouts_description_dialog_positive_confirmation), new DownloadListener(layoutName, iso, this));
        b.setMessage(description);
        b.create().show();
    }

    private void showLanguageSelectionDialog(final HashMap<String,String> languages, final String xmlFile, final String layoutName){
        Set<String> keys = languages.keySet();
        final CharSequence options[] = new CharSequence[keys.toArray().length];
        for(int i=0 ; i<keys.toArray().length ; i++){
            options[i] = (String)keys.toArray()[i];
        }
        Toast.makeText(this,getResources().getString(R.string.available_layouts_not_available_language),
                        Toast.LENGTH_LONG).show();
        AlertDialog.Builder b = new AlertDialog.Builder(this);
        b.setTitle(getResources().getString(R.string.available_layouts_language_dialog_title));
        b.setItems(options, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                String desc = getDescriptionFor(xmlFile,languages.get(options[i]));
                showDescriptionDialog(layoutName,desc,languages.get(options[i]));
            }
        });
        b.create().show();
    }

    private class ClickListener implements View.OnClickListener{
        @Override
        public void onClick(View view) {
            final String layoutName = ""+((TextView) view).getText();
            String url = URLCreator.createMetadataFileURL(view.getContext(), layoutName);
            final ProgressDialog dialog = new ProgressDialog(view.getContext());
            dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
            dialog.setMessage(getResources().getString(R.string.available_layouts_checking_language_dialog));
            dialog.show();
            new GetStringResponseTask(){
                @Override
                protected void onPostExecute(String response) {
                    dialog.dismiss();
                    String xmlFile = response;
                    String localLang = Locale.getDefault().getLanguage();
                    String description = getDescriptionFor(xmlFile, localLang);
                    if (description != null) {
                        showDescriptionDialog(layoutName,description,localLang);
                    } else {//List all other languages
                        HashMap<String, String> languages = getLanguagesFor(xmlFile);
                        Log.e("#",languages.toString());
                        showLanguageSelectionDialog(languages, xmlFile, layoutName);
                    }
                }
            }.execute(url);
        }
    }

    private class DownloadListener implements AlertDialog.OnClickListener{
        private String layoutName;
        private String iso;
        private Context context;

        public DownloadListener(String layoutName, String iso, Context context) {
            this.layoutName = layoutName;
            this.iso = iso;
            this.context = context;
        }

        @Override
        public void onClick(DialogInterface dialogInterface, int i) {
            //Code for downloading the layoutName, must get the layoutName name here
            String info[] = {this.layoutName, this.iso};
            final ProgressDialog dialog = new ProgressDialog(this.context);
            dialog.setMessage(getResources().getString(R.string.available_layouts_downloading_dialog));
            dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
            dialog.show();
            new DownloadCustomLayoutTask(this.context){
                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();
                }
            }.execute(info);
        }
    }
}


================================================
FILE: app/src/main/java/net/osmtracker/activity/ButtonsPresets.java
================================================
package net.osmtracker.activity;

import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.preference.PreferenceManager;

import android.util.Log;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import net.osmtracker.OSMTracker;
import net.osmtracker.R;
import net.osmtracker.db.DataHelper;
import net.osmtracker.layout.DownloadCustomLayoutTask;
import net.osmtracker.util.CustomLayoutsUtils;
import net.osmtracker.util.FileSystemUtils;

import java.io.File;
import java.io.FilenameFilter;
import java.util.Hashtable;

/**
 * Created by emmanuel on 20/10/17.
 */

public class ButtonsPresets extends Activity {

    @SuppressWarnings("unused")
    private static final String TAG = ButtonsPresets.class.getSimpleName();

    final private int RC_WRITE_PERMISSION = 1;

    private CheckBox checkboxHeld;
    private CheckBoxChangedListener listener;
    private CheckBox selected;
    private CheckBox defaultCheckBox;
    private SharedPreferences prefs;
    //Container for the file names and the presentation names
    private static Hashtable<String, String> layoutsFileNames;
    private static String storageDir;

    @Override
    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        initializeAttributes();
    }

    @Override
    protected void onResume() {
        super.onResume();

        if ( writeExternalStoragePermissionGranted() ) {
            refreshActivity();
        } else {
            // Should we show an explanation?
            if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE)) {

                // Show an explanation 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 read permission");

            } else {

                // No explanation needed, we can request the permission.
                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, RC_WRITE_PERMISSION);
            }

        }
    }

    public void refreshActivity(){
        LinearLayout downloadedLayouts = (LinearLayout) findViewById(R.id.list_layouts);
        //main layout for the default layout checkbox
        LinearLayout defaultSection = (LinearLayout) findViewById(R.id.buttons_presets);
        //restart the hashtable
        layoutsFileNames = new Hashtable<String, String>();
        listLayouts(downloadedLayouts);
        checkCurrentLayout(downloadedLayouts, defaultSection);
    }

    private void initializeAttributes(){
        setTitle(getResources().getString(R.string.prefs_ui_buttons_layout));
        setContentView(R.layout.buttons_presets);
        listener = new CheckBoxChangedListener();
        prefs = PreferenceManager.getDefaultSharedPreferences(this);
        layoutsFileNames = new Hashtable<String, String>();
        storageDir = File.separator + OSMTracker.Preferences.VAL_STORAGE_DIR;
    }

    private void listLayouts(LinearLayout rootLayout){
        File layoutsDir = new File(this.getExternalFilesDir(null), storageDir +
                File.separator + DataHelper.LAYOUTS_SUBDIR + File.separator);
        int AT_START = 0; //the position to insert the view at
        int fontSize = 20;
        if (layoutsDir.exists() && layoutsDir.canRead()) {
            //Ask for the layout's filenames
            String[] layoutFiles = layoutsDir.list(new FilenameFilter() {
                @Override
                public boolean accept(File dir, String filename) {
                    return filename.endsWith(DataHelper.LAYOUT_FILE_EXTENSION);
                }
            });
            //Remove all the layouts
            while(rootLayout.getChildAt(0) instanceof CheckBox){
                rootLayout.removeViewAt(0);
            }
            //Fill with the new ones
            for(String name : layoutFiles) {
                CheckBox newCheckBox = new CheckBox(this);
                newCheckBox.setTextSize((float) fontSize);
                String newName = CustomLayoutsUtils.convertFileName(name);
                layoutsFileNames.put(newName, name);
                newCheckBox.setText(newName);
                LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
                layoutParams.setMargins(60, 0, 0, 0);
                newCheckBox.setLayoutParams(layoutParams);
                newCheckBox.setPadding(10,20,10,20);
                newCheckBox.setOnClickListener(listener);
                registerForContextMenu(newCheckBox);
                rootLayout.addView(newCheckBox, AT_START);
            }
        }

        defaultCheckBox = (CheckBox) findViewById(R.id.def_layout);
        defaultCheckBox.setOnClickListener(listener);
        //this is the maping default(It depends on the language of the mobile)->default
        layoutsFileNames.put(defaultCheckBox.getText().toString(),OSMTracker.Preferences.VAL_UI_BUTTONS_LAYOUT);
        //verify the size of the layoutsFileNames, if it is greater than 1, we put invisible the message (in the downloaded layouts section)
        if(layoutsFileNames.size() > 1){
            TextView empyText = (TextView) findViewById(R.id.btnpre_empty);
            empyText.setVisibility(View.INVISIBLE);
        }else{
            TextView empyText = (TextView) findViewById(R.id.btnpre_empty);
            empyText.setVisibility(View.VISIBLE);
        }
    }


    /**
     * @param downloadedLayouts: this linear layout contains the downloaded custom layouts representation
     * @param defaultSection: it contains the default layout representation
     * It asks for the layout being used and checks it in the list
     */
    private void checkCurrentLayout(LinearLayout downloadedLayouts, LinearLayout defaultSection){
        String activeLayoutName = CustomLayoutsUtils.getCurrentLayoutName(getApplicationContext());
        boolean defLayout = false;

        //first, we check if the default layout is activated
        View defCheck = defaultSection.getChildAt(1); //the default checkbox in the activity
        if(defCheck instanceof CheckBox){
            CheckBox defCheckCast = (CheckBox) defCheck;
            String defCheckName = layoutsFileNames.get(defCheckCast.getText());
            if (activeLayoutName.equals(defCheckName)) {
                selected = defCheckCast;
                defLayout = true;
            }
        }

        boolean found = false;
        //then, if the default layout isn't activated, we verify the other layouts
        if (!defLayout) {
            for (int i = 0; i < downloadedLayouts.getChildCount(); i++) {
                View current = downloadedLayouts.getChildAt(i);
                if (current instanceof CheckBox) {
                    CheckBox currentCast = (CheckBox) current;
                    String currentName = layoutsFileNames.get(currentCast.getText());
                    if (activeLayoutName.equals(currentName)) {
                        selected = currentCast;
                        found = true;
                        break;
                    }
                }
            }
            //if not found the active layout then set the default
            if(!found){
                selected = (CheckBox) defCheck;
                String targetLayout = layoutsFileNames.get(selected.getText());
                prefs.edit().putString(OSMTracker.Preferences.KEY_UI_BUTTONS_LAYOUT,
                        targetLayout).commit();
                //reload the activity
                refreshActivity();
            }
        }

        selected.setChecked(true);
    }

    private void selectLayout(CheckBox pressed){
        selected.setChecked(false);
        pressed.setChecked(true);
        selected=pressed;
        String targetLayout = layoutsFileNames.get(pressed.getText());
        prefs.edit().putString(OSMTracker.Preferences.KEY_UI_BUTTONS_LAYOUT,
                targetLayout).commit();
    }

    //Class that manages the changes on the selected layout
    private class CheckBoxChangedListener implements View.OnClickListener {
        @Override
        public void onClick(View view) {
            selectLayout( (CheckBox)view );
        }
    }

    //methods for the context menu for each checkbox
    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        //inflate the menu for the view selected
        getMenuInflater().inflate(R.menu.btnprecb_context_menu, menu);
        checkboxHeld = (CheckBox) v;
    }

    @SuppressLint("StaticFieldLeak")
    @Override
    public boolean onContextItemSelected(MenuItem item) {
        File externalFilesDir = this.getExternalFilesDir(null);
        switch (item.getItemId()){
            //this case download again the layout held and install it
            case R.id.cb_update_and_install:
                String layoutName = checkboxHeld.getText().toString();
                String iso = getIso(layoutsFileNames.get(checkboxHeld.getText()));
                String info[]= {layoutName, iso};
                final ProgressDialog dialog = new ProgressDialog(checkboxHeld.getContext());
                dialog.setMessage(getResources().getString(R.string.buttons_presets_updating_layout));
                dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
                dialog.show();
                new DownloadCustomLayoutTask(this){
                    protected void onPostExecute(Boolean status){
                        //if the download is correct we activate it
                        if (status) {
                            selectLayout(checkboxHeld);
                            //re-load the activity
                            refreshActivity();
                            Toast.makeText(getApplicationContext(), getResources().getString(R.string.buttons_presets_successful_update), Toast.LENGTH_LONG).show();
                        }
                        else {
                            Toast.makeText(getApplicationContext(), getResources().getString(R.string.buttons_presets_unsuccessful_update), Toast.LENGTH_LONG).show();
                        }
                        dialog.dismiss();
                    }
                }.execute(info);
                checkboxHeld.setChecked(false);
                break;
            //this case open a new confirm dialog to delete a layout, also, if the layout have a icon directory, it is deleted
            case R.id.cb_delete:
                new AlertDialog.Builder(this).
                setTitle(checkboxHeld.getText())
                .setMessage(getResources().getString(R.string.buttons_presets_delete_message).replace("{0}", checkboxHeld.getText()))
                .setCancelable(true)
                .setIcon(android.R.drawable.ic_dialog_alert)
                .setPositiveButton(getResources().getString(R.string.buttons_presets_delete_positive_confirmation), new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        String fileName = layoutsFileNames.get(checkboxHeld.getText());
                        String rootDir = storageDir + File.separator + DataHelper.LAYOUTS_SUBDIR + File.separator;
                        File fileToDelete = new File(externalFilesDir, rootDir + fileName);
                        String iconDirName = fileName.substring(0, fileName.length() - CustomLayoutsUtils.LAYOUT_EXTENSION_ISO.length())
                                + DataHelper.LAYOUTS_ICONS_DIR_SUFFIX;
                        File iconDirToDelete = new File(externalFilesDir, rootDir + iconDirName);

                        boolean successfulDeletion = FileSystemUtils.delete(fileToDelete, false);

                        if(iconDirToDelete.exists())
                            successfulDeletion &= FileSystemUtils.delete(iconDirToDelete, true);

                        int messageToShowId = (successfulDeletion) ? R.string.buttons_presets_successful_delete :
                                R.string.buttons_presets_unsuccessful_delete;
                        String message = getResources().getString(messageToShowId);

                        Log.println(successfulDeletion ? Log.INFO : Log.ERROR, "TOAST", message);

                        Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();

                        //reload the activity
                        refreshActivity();
                    }
                })
                .setNegativeButton(getResources().getString(R.string.menu_cancel), new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                }).create().show();
                break;
        }
        return super.onContextItemSelected(item);
    }

    /**
     * This method obtain the iso of any layout file name
     * Assumes that layoutName looks like a filename => name_xx.ext
     * Example: given "foo_es.xml" return only "es"
     */
    private String getIso(String layoutName){
        String tmp = layoutName.substring(0, layoutName.length() - DataHelper.LAYOUT_FILE_EXTENSION.length());
        String iso = "";
        for (int i=tmp.length() - AvailableLayouts.ISO_CHARACTER_LENGTH; i<tmp.length(); i++){
                iso += tmp.charAt(i);
        }
        return iso;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.launch_available_layouts_menu, menu);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if(item.getItemId() == R.id.launch_available){
            startActivity(new Intent(this,AvailableLayouts.class));
        }
        return super.onOptionsItemSelected(item);
    }


    public void onRequestPermissionsResult(int requestCode,
                                           String permissions[], int[] grantResults) {
        switch (requestCode) {
            case RC_WRITE_PERMISSION: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                    refreshActivity();

                } else {

                    // permission denied, boo! Disable the
                    // functionality that depends on this permission.
                    //TODO: add an informative message.
                    Log.w(TAG, "we should explain why we need read permission");
                }
            }
        }
    }

    //TODO: improve permissions management.
    private boolean writeExternalStoragePermissionGranted(){
        // On versions lower than Android 11, write external storage permission is required.
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
            Log.d(TAG, "CHECKING - Write");
            return ContextCompat.checkSelfPermission(this,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
        } else {
            Log.d(TAG, "Write External Storage is granted");
            return true;
        }
    }

}

================================================
FILE: app/src/main/java/net/osmtracker/activity/DisplayTrack.java
================================================
package net.osmtracker.activity;

import net.osmtracker.OSMTracker;
import net.osmtracker.util.ThemeValidator;
import net.osmtracker.view.DisplayTrackView;
import net.osmtracker.db.TrackContentProvider;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.preference.PreferenceManager;

import android.view.ViewGroup.LayoutParams;

/**
 * Displays current track in 2D view.
 *<P>
 * Used only if {@link OSMTracker.Preferences#KEY_UI_DISPLAYTRACK_OSM} is not true.
 * Otherwise {@link DisplayTrackMap} is used.
 * 
 * @author Nicolas Guillaumin
 *
 */
public class DisplayTrack extends Activity {

	@SuppressWarnings("unused")
	private static final String TAG = DisplayTrack.class.getSimpleName();

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// Set application theme according to user settings
		setTheme(getResources().getIdentifier(ThemeValidator.getValidTheme(
				PreferenceManager.getDefaultSharedPreferences(this), getResources()), null, null));
		
		super.onCreate(savedInstanceState);
		
		// Create special view and displays it
		final long trackId = getIntent().getExtras().getLong(TrackContentProvider.Schema.COL_TRACK_ID);
		DisplayTrackView dtv = new DisplayTrackView(this, trackId);
		dtv.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
		setTitle(getTitle() + ": #" + getIntent().getExtras().getLong(TrackContentProvider.Schema.COL_TRACK_ID));
		setContentView(dtv);		

		// If this is the first time showing this activity,
		// wait for everything to initialize and then ask
		// the user if they'd rather see the OSM background.
		SharedPreferences dtPrefs = PreferenceManager.getDefaultSharedPreferences(this);
		if (! dtPrefs.getBoolean(OSMTracker.Preferences.KEY_UI_ASKED_DISPLAYTRACK_OSM, false)) {
			dtPrefs.edit().putBoolean(OSMTracker.Preferences.KEY_UI_ASKED_DISPLAYTRACK_OSM, true).commit();
			dtv.post(new Runnable() {
				@Override
				public void run() {
					new AlertDialog.Builder(DisplayTrack.this)
						.setTitle(net.osmtracker.R.string.prefs_displaytrack_osm)
						.setMessage(net.osmtracker.R.string.prefs_displaytrack_osm_summary_ask)
						.setNegativeButton(android.R.string.no, null)
						.setPositiveButton(net.osmtracker.R.string.displaytrack_map, new DialogInterface.OnClickListener() {
							@Override
							public void onClick(DialogInterface dialog, int which) {
								PreferenceManager.getDefaultSharedPreferences(DisplayTrack.this).edit()
									.putBoolean(OSMTracker.Preferences.KEY_UI_DISPLAYTRACK_OSM, true).commit();
								Intent i = new Intent(DisplayTrack.this, DisplayTrackMap.class);
								i.putExtra(TrackContentProvider.Schema.COL_TRACK_ID, trackId);
								startActivity(i);
								finish();  // DisplayTrackMap replaces our activity
							}
						})
						.show();
				}
			});
		}
	}	
	
}


================================================
FILE: app/src/main/java/net/osmtracker/activity/DisplayTrackMap.java
================================================
package net.osmtracker.activity;

import android.app.Activity;
import android.content.ContentUris;
import android.content.Intent;
import android.content.SharedPreferences;
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.util.DisplayMetrics;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;

import androidx.preference.PreferenceManager;

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.tileprovider.tilesource.ITileSource;
import org.osmdroid.tileprovider.tilesource.XYTileSource;

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 <a href="http://osmdroid.googlecode.com/">osmdroid code</a>
 *<P>
 * 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
	 */
	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
	 */
	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})
	 */
	private static final String CURRENT_ZOOMED_TO_TRACK = "currentZoomedToTrack";

	/**
	 * Key for keeping the last zoom level across app. restart
	 */
	private static final String LAST_ZOOM = "lastZoomLevel";

	/**
	 * Default zoom level
	 */
	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 Polyline polyline;

	/**
	 * OSM view overlay that displays waypoints
	 */
	private WayPointsOverlay wayPointsOverlay;

	/**
	 * 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
	 */
	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
	 */
	private GeoPoint currentPosition;

	/**
	 * 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.
	 */
	private Integer lastTrackPointIdProcessed = null;

	/**
	 * Observes changes on track points
	 */
	private ContentObserver trackpointContentObserver;

	/**
	 * Keeps the SharedPreferences
	 */
	private SharedPreferences prefs = null;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		// loading the preferences
		prefs = PreferenceManager.getDefaultSharedPreferences(this);

		setContentView(R.layout.displaytrackmap);

		currentTrackId = getIntent().getExtras().getLong(TrackContentProvider.Schema.COL_TRACK_ID);
		setTitle(getTitle() + ": #" + currentTrackId);

		// Initialize OSM view
		Configuration.getInstance().load(this, prefs);

		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) {
			osmViewController.setZoom(savedInstanceState.getInt(CURRENT_ZOOM, DEFAULT_ZOOM));
			osmView.scrollTo(savedInstanceState.getInt(CURRENT_SCROLL_X, 0),
					savedInstanceState.getInt(CURRENT_SCROLL_Y, 0));
			centerToGpsPos = savedInstanceState.getBoolean(CURRENT_CENTER_TO_GPS_POS, centerToGpsPos);
			zoomedToTrackAlready = savedInstanceState.getBoolean(CURRENT_ZOOMED_TO_TRACK, zoomedToTrackAlready);
		} else {
			// Try to get last zoom Level from Shared Preferences
			SharedPreferences settings = getPreferences(MODE_PRIVATE);
			osmViewController.setZoom(settings.getInt(LAST_ZOOM, DEFAULT_ZOOM));
		}

		selectTileSource();

		setTileDpiScaling();

		createOverlays();

		// Create content observer for track points
		trackpointContentObserver = new ContentObserver(new Handler()) {
			@Override
			public void onChange(boolean selfChange) {
				pathChanged();
			}
		};

		// Register listeners for zoom buttons
		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);
			}
		});
	}

	/**
	 * Sets the map tile provider according to the user's demands in the settings.
	 */
	public void selectTileSource() {
		String mapTile = prefs.getString(OSMTracker.Preferences.KEY_UI_MAP_TILE, OSMTracker.Preferences.VAL_UI_MAP_TILE_MAPNIK);
		Log.e("TileMapName active", mapTile);
		osmView.setTileSource(selectMapTile(mapTile));
	}

	/**
	 * Make text on map better readable on high DPI displays
	 */
	public void setTileDpiScaling() {
		osmView.setTilesScaledToDpi(true);
	}


	static {
		TileSourceFactory.addTileSource(new XYTileSource("CyclOSM",
								 0, 18, 256, ".png", 
								 new String[] {
									 "https://a.tile-cyclosm.openstreetmap.fr/cyclosm/",
									 "https://b.tile-cyclosm.openstreetmap.fr/cyclosm/",
									 "https://c.tile-cyclosm.openstreetmap.fr/cyclosm/"},
								 "© OpenStreetMap contributors"));
	}

	/**
	 * Returns a ITileSource for the map according to the selected mapTile
	 * String. The default is mapnik.
	 *
	 * @param mapTile String that is the name of the tile provider
	 * @return ITileSource with the selected Tile-Source
	 */
	private ITileSource selectMapTile(String mapTile) {
		try {
			return TileSourceFactory.getTileSource(mapTile);
		} catch (Exception e) {
			Log.e(TAG, "Invalid tile source '"+mapTile+"'", e);
			Log.e(TAG, "Default tile source selected: '" + TileSourceFactory.DEFAULT_TILE_SOURCE.name() +"'");
			return TileSourceFactory.DEFAULT_TILE_SOURCE;
		}
	}


	@Override
	protected void onSaveInstanceState(Bundle outState) {
		outState.putInt(CURRENT_ZOOM, osmView.getZoomLevel());
		outState.putInt(CURRENT_SCROLL_X, osmView.getScrollX());
		outState.putInt(CURRENT_SCROLL_Y, osmView.getScrollY());
		outState.putBoolean(CURRENT_CENTER_TO_GPS_POS, centerToGpsPos);
		outState.putBoolean(CURRENT_ZOOMED_TO_TRACK, zoomedToTrackAlready);
		super.onSaveInstanceState(outState);
	}

	@Override
	protected void onResume() {
		super.onResume();
		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 track point changes
		getContentResolver().registerContentObserver(
				TrackContentProvider.trackPointsUri(currentTrackId),
				true, trackpointContentObserver);

		// Forget the last waypoint read from the DB
		// This ensures that all waypoints for the track will be reloaded
		// from the database to populate the path layout
		lastTrackPointIdProcessed = null;

		// Reload path
		pathChanged();

		selectTileSource();

		setTileDpiScaling();

		// Refresh way points
		wayPointsOverlay.refresh();
	}

	@Override
	protected void onPause() {
		// Unregister content observer
		getContentResolver().unregisterContentObserver(trackpointContentObserver);

		// Clear the points list.
		polyline.setPoints(new ArrayList<>());

		super.onPause();
	}

	@Override
	protected void onStop() {
		super.onStop();

		// Save zoom level in shared preferences
		SharedPreferences settings = getPreferences(MODE_PRIVATE);
		SharedPreferences.Editor editor = settings.edit();
		editor.putInt(LAST_ZOOM, osmView.getZoomLevel());
		editor.apply();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getMenuInflater();
		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));
		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;
		}
		return super.onOptionsItemSelected(item);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
			case MotionEvent.ACTION_MOVE:
				if (currentPosition != null)
					centerToGpsPos = false;
				break;
		}
		return super.onTouchEvent(event);
	}

	/**
	 * Creates overlays over the OSM view
	 */
	private void createOverlays() {
		DisplayMetrics metrics = new DisplayMetrics();
		this.getWindowManager().getDefaultDisplay().getMetrics(metrics);

		// set with to hopefully DPI independent 0.5mm
		polyline = new Polyline();
		Paint paint = polyline.getOutlinePaint();
		paint.setColor(Color.BLUE);
		paint.setStrokeWidth((float) (metrics.densityDpi / 25.4 / 2));
		osmView.getOverlayManager().add(polyline);

		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
	 * from {@link #onResume()}, and not the periodic call from
	 * {@link ContentObserver#onChange(boolean) trackpointContentObserver.onChange(boolean)}
	 * while recording.
	 */
	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.
		// (the first point will overwrite these lat/lon bounds.)
		boolean doInitialBoundsCalc = false;
		double minLat = 91.0, minLon = 181.0;
		double maxLat = -91.0, maxLon = -181.0;
		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 != 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();
			}
		}

		// 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};
		// Selection: The where clause to use
		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
		// 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<String> selectionArgsList = new ArrayList<>();
			selectionArgsList.add(lastTrackPointIdProcessed.toString());
			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");

		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();
				}

				// 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();
		}
	}
}


================================================
FILE: app/src/main/java/net/osmtracker/activity/GitHubConfig.java
================================================
package net.osmtracker.activity;

import android.app.Activity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import net.osmtracker.GitHubUser;
import net.osmtracker.R;
import static net.osmtracker.github.GitHubConstants.GITHUB_TOKENS_URL;

public class GitHubConfig extends Activity {
    EditText editTextUserName, editTextUserToken;
    private GitHubUser gitHubUser;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.github_configuration_token);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

        editTextUserName = findViewById(R.id.git_configuration_user_name);
        editTextUserToken = findViewById(R.id.git_configuration_user_token);
        gitHubUser = new GitHubUser(this);

        final Button btnGitHub = (Button) findViewById(R.id.git_link_create_token_btn_ok);
        btnGitHub.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(Intent.ACTION_VIEW);
                intent.setData(Uri.parse(GITHUB_TOKENS_URL));
                startActivity(intent);
            }
        });

        final Button btnSave = (Button) findViewById(R.id.git_save_credentials_btn_ok);
        btnSave.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                String username = editTextUserName.getText().toString().trim();
                String ghToken = editTextUserToken.getText().toString().trim();

                // Empty is enabled to allow saving without credentials to erase the existing ones
                /*
                if (username.isEmpty()) {
                    editTextUserName.setError("Username required");
                    return;
                }
                */
                if (ghToken.length() != 40 && !ghToken.isEmpty()) {
                    editTextUserToken.setError(getString(R.string.error_gh_token_lenght));
                    return;
                }

                gitHubUser.saveCredentials(username, ghToken);
                Toast.makeText(GitHubConfig.this, R.string.successfully_saved, Toast.LENGTH_SHORT).show();

                Intent intent = new Intent(GitHubConfig.this, TrackManager.class);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                intent.setPackage(this.getClass().getPackage().getName());
                startActivity(intent);
                finish();
            }
        });

        final Button btnCancel = (Button) findViewById(R.id.git_back_credentials_btn_cancel);
        btnCancel.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {

                finish();
            }
        });

        // Do not show soft keyboard by default
        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
    }
}


================================================
FILE: app/src/main/java/net/osmtracker/activity/GitHubNewFork.java
================================================
package net.osmtracker.activity;

import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import androidx.annotation.Nullable;

import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.Volley;

import net.osmtracker.GitHubUser;
import net.osmtracker.R;
import static net.osmtracker.github.GitHubConstants.getRepoForksUrl;

import org.json.JSONException;
import org.json.JSONObject;

import java.util.HashMap;
import java.util.Map;

public class GitHubNewFork extends Activity {
    EditText editTextRootUsername, editTextRootRepo;
    private GitHubUser gitHubUser;
    private String newForkFullName;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.git_create_fork);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

        gitHubUser = new GitHubUser(this);

        editTextRootUsername = findViewById(R.id.git_username_to_fork_editText_user);
        editTextRootRepo = findViewById(R.id.git_repo_to_fork_editText_name);

        editTextRootUsername.setHint(R.string.upload_to_github_forked_repo_owner);
        editTextRootRepo.setHint(R.string.upload_to_github_forked_repo_name);

        final Button btnCreate = (Button) findViewById(R.id.git_create_newfork_btn_ok);
        btnCreate.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                String username = editTextRootUsername.getText().toString().trim();
                String repo = editTextRootRepo.getText().toString().trim();
                if (username.isEmpty()
Download .txt
gitextract_nkc4_91h/

├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.yml
│   │   ├── config.yml
│   │   ├── documentation_report.yml
│   │   └── feature_request.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows/
│       ├── android.yml
│       └── nightly.yml
├── .gitignore
├── .travis.yml
├── .tx/
│   └── config
├── AUTHORS
├── CONTRIBUTING.md
├── COPYING
├── LICENSES
├── README.md
├── app/
│   ├── build.gradle
│   ├── jacoco.gradle
│   ├── lint.xml
│   └── src/
│       ├── androidTest/
│       │   ├── assets/
│       │   │   └── gpx/
│       │   │       └── gpx-test.gpx
│       │   ├── java/
│       │   │   └── net/
│       │   │       └── osmtracker/
│       │   │           ├── activity/
│       │   │           │   └── PreferencesTest.java
│       │   │           ├── data/
│       │   │           │   └── Mocks.java
│       │   │           ├── layouts/
│       │   │           │   ├── DeleteLayoutTest.java
│       │   │           │   ├── DownloadLayoutTest.java
│       │   │           │   └── RepositorySettingsDialogTest.java
│       │   │           └── util/
│       │   │               ├── LogcatHelper.java
│       │   │               ├── TestUtils.java
│       │   │               ├── ToastMatcher.java
│       │   │               └── WaitForView.java
│       │   └── res/
│       │       └── values/
│       │           └── strings.xml
│       ├── main/
│       │   ├── AndroidManifest.xml
│       │   ├── java/
│       │   │   └── net/
│       │   │       └── osmtracker/
│       │   │           ├── GitHubUser.java
│       │   │           ├── OSMTracker.java
│       │   │           ├── activity/
│       │   │           │   ├── About.java
│       │   │           │   ├── AvailableLayouts.java
│       │   │           │   ├── ButtonsPresets.java
│       │   │           │   ├── DisplayTrack.java
│       │   │           │   ├── DisplayTrackMap.java
│       │   │           │   ├── GitHubConfig.java
│       │   │           │   ├── GitHubNewFork.java
│       │   │           │   ├── GitHubNewRepo.java
│       │   │           │   ├── GitHubPullRequest.java
│       │   │           │   ├── GitHubUpload.java
│       │   │           │   ├── Intro.kt
│       │   │           │   ├── NoteList.java
│       │   │           │   ├── OpenStreetMapNotesUpload.java
│       │   │           │   ├── OpenStreetMapUpload.java
│       │   │           │   ├── Preferences.java
│       │   │           │   ├── TrackDetail.java
│       │   │           │   ├── TrackDetailEditor.java
│       │   │           │   ├── TrackListRVAdapter.java
│       │   │           │   ├── TrackLogger.java
│       │   │           │   ├── TrackManager.java
│       │   │           │   └── WaypointList.java
│       │   │           ├── adapter/
│       │   │           │   └── NoteAdapter.java
│       │   │           ├── db/
│       │   │           │   ├── DataHelper.java
│       │   │           │   ├── DatabaseHelper.java
│       │   │           │   ├── ExportDatabaseTask.java
│       │   │           │   ├── TrackContentProvider.java
│       │   │           │   ├── TracklistAdapter.java
│       │   │           │   ├── WaypointListAdapter.java
│       │   │           │   └── model/
│       │   │           │       ├── Point.java
│       │   │           │       ├── Track.java
│       │   │           │       ├── TrackPoint.java
│       │   │           │       └── WayPoint.java
│       │   │           ├── exception/
│       │   │           │   ├── CreateTrackException.java
│       │   │           │   └── ExportTrackException.java
│       │   │           ├── github/
│       │   │           │   └── GitHubConstants.java
│       │   │           ├── gpx/
│       │   │           │   ├── ExportToStorageTask.java
│       │   │           │   ├── ExportToTempFileTask.java
│       │   │           │   ├── ExportTrackTask.java
│       │   │           │   └── ZipHelper.java
│       │   │           ├── layout/
│       │   │           │   ├── DisablableTableLayout.java
│       │   │           │   ├── DownloadCustomLayoutTask.java
│       │   │           │   ├── GetStringResponseTask.java
│       │   │           │   ├── GpsStatusRecord.java
│       │   │           │   ├── TLSSocketFactory.java
│       │   │           │   ├── URLValidatorTask.java
│       │   │           │   └── UserDefinedLayout.java
│       │   │           ├── listener/
│       │   │           │   ├── EditWaypointDialogOnClickListener.java
│       │   │           │   ├── PageButtonOnClickListener.java
│       │   │           │   ├── PressureListener.java
│       │   │           │   ├── SensorListener.java
│       │   │           │   ├── StillImageOnClickListener.java
│       │   │           │   ├── TagButtonOnClickListener.java
│       │   │           │   ├── TextNoteOnClickListener.java
│       │   │           │   └── VoiceRecOnClickListener.java
│       │   │           ├── osm/
│       │   │           │   ├── OpenStreetMapConstants.java
│       │   │           │   ├── UploadToOpenStreetMapNotesTask.java
│       │   │           │   └── UploadToOpenStreetMapTask.java
│       │   │           ├── overlay/
│       │   │           │   └── WayPointsOverlay.java
│       │   │           ├── receiver/
│       │   │           │   └── MediaButtonReceiver.java
│       │   │           ├── service/
│       │   │           │   ├── gps/
│       │   │           │   │   ├── GPSLogger.java
│       │   │           │   │   └── GPSLoggerServiceConnection.java
│       │   │           │   └── resources/
│       │   │           │       ├── AppResourceIconResolver.java
│       │   │           │       ├── ExternalDirectoryIconResolver.java
│       │   │           │       └── IconResolver.java
│       │   │           ├── util/
│       │   │           │   ├── ArrayUtils.java
│       │   │           │   ├── Callback.java
│       │   │           │   ├── CustomLayoutsUtils.java
│       │   │           │   ├── DialogUtils.java
│       │   │           │   ├── FileSystemUtils.java
│       │   │           │   ├── GitHubUtils.java
│       │   │           │   ├── MercatorProjection.java
│       │   │           │   ├── ThemeValidator.java
│       │   │           │   ├── URLCreator.java
│       │   │           │   └── UserDefinedLayoutReader.java
│       │   │           └── view/
│       │   │               ├── DisplayTrackView.java
│       │   │               ├── TextNoteDialog.java
│       │   │               └── VoiceRecDialog.java
│       │   └── res/
│       │       ├── drawable/
│       │       │   ├── divider.xml
│       │       │   ├── ic_fab_add_track.xml
│       │       │   └── map_btn_style.xml
│       │       ├── drawable-mdpi/
│       │       │   └── theme_highcontrast_btn.xml
│       │       ├── layout/
│       │       │   ├── about.xml
│       │       │   ├── available_layouts.xml
│       │       │   ├── buttons_presets.xml
│       │       │   ├── displaytrackmap.xml
│       │       │   ├── edit_note_dialog.xml
│       │       │   ├── edit_waypoint_dialog.xml
│       │       │   ├── git_configuration_fields.xml
│       │       │   ├── git_create_fork.xml
│       │       │   ├── git_create_fork_fields.xml
│       │       │   ├── git_create_pullrequest.xml
│       │       │   ├── git_create_pullrequest_fields.xml
│       │       │   ├── git_newrepo.xml
│       │       │   ├── git_newrepo_fields.xml
│       │       │   ├── git_trackdetail_fields.xml
│       │       │   ├── github_configuration_token.xml
│       │       │   ├── github_repository_settings.xml
│       │       │   ├── gpsstatus_record.xml
│       │       │   ├── notelist.xml
│       │       │   ├── notelist_item.xml
│       │       │   ├── osm_note_upload.xml
│       │       │   ├── osm_upload.xml
│       │       │   ├── osmtracker.xml
│       │       │   ├── settings_activity.xml
│       │       │   ├── trackdetail.xml
│       │       │   ├── trackdetail_fields.xml
│       │       │   ├── trackdetail_item.xml
│       │       │   ├── tracklist_item.xml
│       │       │   ├── tracklogger.xml
│       │       │   ├── trackmanager.xml
│       │       │   ├── upload_github_menu.xml
│       │       │   └── waypointlist_item.xml
│       │       ├── layout-iw/
│       │       │   ├── trackdetail_item.xml
│       │       │   ├── tracklist_item.xml
│       │       │   └── waypointlist_item.xml
│       │       ├── menu/
│       │       │   ├── btnprecb_context_menu.xml
│       │       │   ├── displaytrackmap_menu.xml
│       │       │   ├── github_repository_settings_menu.xml
│       │       │   ├── githubupload_settings_menu.xml
│       │       │   ├── launch_available_layouts_menu.xml
│       │       │   ├── note_contextmenu.xml
│       │       │   ├── trackdetail_menu.xml
│       │       │   ├── tracklogger_menu.xml
│       │       │   ├── trackmgr_contextmenu.xml
│       │       │   └── trackmgr_menu.xml
│       │       ├── values/
│       │       │   ├── accessibility.xml
│       │       │   ├── colors.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   ├── styles.xml
│       │       │   ├── theme_highcontrast.xml
│       │       │   ├── themes.xml
│       │       │   ├── values-preferences.xml
│       │       │   ├── values.xml
│       │       │   └── waypoints.xml
│       │       ├── values-ar/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-b+sr+Latn/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-be/
│       │       │   ├── strings-tags.xml
│       │       │   └── strings.xml
│       │       ├── values-bg-rBG/
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   └── strings.xml
│       │       ├── values-ca/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-cs-rCZ/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-da/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-de/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-el/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-es/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-et/
│       │       │   └── strings.xml
│       │       ├── values-eu/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-fa/
│       │       │   ├── accessibility.xml
│       │       │   └── waypoints.xml
│       │       ├── values-fa-rIR/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-fi/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-fr/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-fr-rFR/
│       │       │   ├── accessibility.xml
│       │       │   └── waypoints.xml
│       │       ├── values-gl/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-he/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-hr/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-hu/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-hy/
│       │       │   ├── accessibility.xml
│       │       │   └── strings.xml
│       │       ├── values-id/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-it/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-ja/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-kn/
│       │       │   ├── strings-preferences.xml
│       │       │   └── waypoints.xml
│       │       ├── values-ko/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-lt/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-lv/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-nb/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-nl/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-nn/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-pl/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-pt/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-pt-rBR/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-pt-rPT/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-ru/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-sk/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-sl/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-sq/
│       │       │   └── accessibility.xml
│       │       ├── values-sr/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-sv/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-ta/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-th/
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-tr/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   └── waypoints.xml
│       │       ├── values-tr-rTR/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-tags.xml
│       │       │   └── waypoints.xml
│       │       ├── values-uk/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-v11/
│       │       │   ├── strings.xml
│       │       │   ├── theme_highcontrast.xml
│       │       │   └── themes.xml
│       │       ├── values-v14/
│       │       │   ├── theme_highcontrast.xml
│       │       │   └── themes.xml
│       │       ├── values-vi/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-zh-rCN/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── values-zh-rTW/
│       │       │   ├── accessibility.xml
│       │       │   ├── strings-preferences.xml
│       │       │   ├── strings-tags.xml
│       │       │   ├── strings.xml
│       │       │   └── waypoints.xml
│       │       ├── xml/
│       │       │   ├── default_buttons_layout.xml
│       │       │   ├── filepaths.xml
│       │       │   └── preferences.xml
│       │       ├── xml-da/
│       │       │   └── default_buttons_layout.xml
│       │       ├── xml-de/
│       │       │   └── default_buttons_layout.xml
│       │       ├── xml-en-rGB/
│       │       │   └── default_buttons_layout.xml
│       │       ├── xml-it/
│       │       │   └── default_buttons_layout.xml
│       │       ├── xml-ja/
│       │       │   └── default_buttons_layout.xml
│       │       ├── xml-nb/
│       │       │   └── default_buttons_layout.xml
│       │       ├── xml-nn/
│       │       │   └── default_buttons_layout.xml
│       │       └── xml-th/
│       │           └── default_buttons_layout.xml
│       └── test/
│           ├── assets/
│           │   └── gpx/
│           │       └── gpx-test.gpx
│           ├── java/
│           │   └── net/
│           │       └── osmtracker/
│           │           ├── activity/
│           │           │   ├── ButtonsPresetsTest.java
│           │           │   ├── OpenStreetMapNotesUploadTest.java
│           │           │   └── TrackDetailEditorTest.java
│           │           ├── data/
│           │           │   ├── GPXMocks.java
│           │           │   ├── MockDataHelper.java
│           │           │   ├── TrackMocks.java
│           │           │   ├── TrackPointMocks.java
│           │           │   └── WayPointMocks.java
│           │           ├── db/
│           │           │   ├── DataHelperNoteTest.java
│           │           │   └── model/
│           │           │       ├── OSMVisibilityTest.java
│           │           │       └── TrackTest.java
│           │           ├── gpx/
│           │           │   └── ExportToStorageTaskTest.java
│           │           ├── layout/
│           │           │   ├── DownloadCustomLayoutTaskTest.java
│           │           │   └── URLValidatorTaskTest.java
│           │           └── util/
│           │               ├── ArrayUtilsTest.java
│           │               ├── CustomLayoutsUtilsTest.java
│           │               ├── FileSystemUtilsTest.java
│           │               ├── MercatorProjectionTest.java
│           │               ├── ThemeValidatorTest.java
│           │               └── URLCreatorTest.java
│           └── resources/
│               └── gpx/
│                   └── gpx-test.gpx
├── build.gradle
├── fastlane/
│   └── metadata/
│       └── android/
│           ├── ca/
│           │   └── short_description.txt
│           ├── cs/
│           │   └── short_description.txt
│           ├── de/
│           │   └── short_description.txt
│           ├── en-US/
│           │   ├── changelogs/
│           │   │   ├── 57.txt
│           │   │   ├── 62.txt
│           │   │   ├── 65.txt
│           │   │   └── 66.txt
│           │   ├── full_description.txt
│           │   ├── short_description.txt
│           │   └── title.txt
│           ├── es/
│           │   ├── full_description.txt
│           │   └── short_description.txt
│           ├── fr/
│           │   └── short_description.txt
│           ├── ga/
│           │   └── short_description.txt
│           ├── he/
│           │   └── short_description.txt
│           ├── id/
│           │   └── short_description.txt
│           ├── nn/
│           │   └── short_description.txt
│           ├── pl/
│           │   └── short_description.txt
│           ├── pt/
│           │   └── short_description.txt
│           ├── pt-BR/
│           │   └── short_description.txt
│           ├── pt-PT/
│           │   └── short_description.txt
│           ├── ro/
│           │   └── short_description.txt
│           ├── ru/
│           │   └── short_description.txt
│           ├── sq/
│           │   └── short_description.txt
│           ├── sr/
│           │   └── short_description.txt
│           ├── sw/
│           │   └── short_description.txt
│           ├── ta/
│           │   └── short_description.txt
│           ├── tr/
│           │   └── short_description.txt
│           ├── uk/
│           │   └── short_description.txt
│           └── zh-CN/
│               └── short_description.txt
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── transifex.yml
Download .txt
SYMBOL INDEX (815 symbols across 107 files)

FILE: app/src/androidTest/java/net/osmtracker/activity/PreferencesTest.java
  class PreferencesTest (line 37) | @RunWith(AndroidJUnit4.class)
    method setup (line 43) | @Before
    method tearDown (line 55) | @After
    method testStorageDirectoryValidatesNonEmpty (line 63) | @Test
    method testStorageDirectoryValidatesAppendLeadingSlash (line 84) | @Test
    method testNumericInputLogic (line 107) | @Test
    method testResetButtonResetsValue (line 128) | @Test
    method testListPreferenceCustomSummary (line 162) | @Test
    method testClearOAuthData (line 181) | @Test
    method scrollToAndClick (line 211) | private void scrollToAndClick(String text) {

FILE: app/src/androidTest/java/net/osmtracker/data/Mocks.java
  class Mocks (line 3) | public class Mocks {

FILE: app/src/androidTest/java/net/osmtracker/layouts/DeleteLayoutTest.java
  class DeleteLayoutTest (line 36) | public class DeleteLayoutTest {
    method setUp (line 46) | @Before
    method tearDown (line 60) | @After
    method deleteLayout (line 69) | private void deleteLayout() {
    method layoutDeletionTest (line 83) | @Test

FILE: app/src/androidTest/java/net/osmtracker/layouts/DownloadLayoutTest.java
  class DownloadLayoutTest (line 39) | public class DownloadLayoutTest {
    method setUp (line 52) | @Before
    method tearDown (line 63) | @After
    method downloadLayoutTest (line 68) | @Test
    method deleteLayoutsDirectory (line 84) | public void deleteLayoutsDirectory() {
    method navigateToAvailableLayouts (line 97) | public void navigateToAvailableLayouts() {
    method makePostDownloadAssertions (line 121) | private void makePostDownloadAssertions(String layoutName) {
    method clickButtonsToDownloadLayout (line 143) | private void clickButtonsToDownloadLayout(String layoutName) {

FILE: app/src/androidTest/java/net/osmtracker/layouts/RepositorySettingsDialogTest.java
  class RepositorySettingsDialogTest (line 34) | public class RepositorySettingsDialogTest {
    method setUp (line 38) | @Before
    method tearDown (line 45) | @After
    method testToggleBehaviour (line 50) | @Test
    method testRepositoryValidation (line 64) | @Test
    method checkStateAfterToggle (line 76) | public void checkStateAfterToggle(int expectedActiveId, int expectedIn...
    method checkRepositoryValidity (line 83) | public void checkRepositoryValidity(String user, String repo, String b...
    method checkDialogState (line 106) | private void checkDialogState(ViewAssertion assertion) {
    method checkTextFieldsDefaultValues (line 113) | private void checkTextFieldsDefaultValues() {
    method checkTextFieldsState (line 122) | public void checkTextFieldsState(Matcher<View> matcher) {

FILE: app/src/androidTest/java/net/osmtracker/util/LogcatHelper.java
  class LogcatHelper (line 8) | public class LogcatHelper {
    method checkLogForMessage (line 10) | public static boolean checkLogForMessage(String tag, String message) {

FILE: app/src/androidTest/java/net/osmtracker/util/TestUtils.java
  class TestUtils (line 22) | public class TestUtils {
    method listFiles (line 31) | public static ArrayList<String> listFiles(File directory){
    method createDirectory (line 42) | public static File createDirectory(File parentDir, String newDirName){
    method createFile (line 51) | public static File createFile(File parentDir, String newFileName){
    method injectMockLayout (line 60) | public static void injectMockLayout(String layoutName, String ISOLangC...
    method writeToFile (line 84) | public static void writeToFile(File file, String content){
    method getAppDirectory (line 98) | public static File getAppDirectory(){
    method getLayoutsDirectory (line 110) | public static File getLayoutsDirectory(){
    method checkToastIsShownWith (line 117) | public static void checkToastIsShownWith(String text){
    method getStringResource (line 124) | public static String getStringResource(int resourceId){
    method setGithubRepositorySettings (line 128) | public static void setGithubRepositorySettings(String user, String rep...
    method setLayoutsTestingRepository (line 137) | public static void setLayoutsTestingRepository(){

FILE: app/src/androidTest/java/net/osmtracker/util/ToastMatcher.java
  class ToastMatcher (line 14) | public class ToastMatcher extends TypeSafeMatcher<Root> {
    method describeTo (line 16) | @Override
    method matchesSafely (line 21) | @Override

FILE: app/src/androidTest/java/net/osmtracker/util/WaitForView.java
  class WaitForView (line 15) | public class WaitForView implements ViewAction {
    method WaitForView (line 24) | public WaitForView(int viewId, long timeout) {
    method getConstraints (line 29) | @Override
    method getDescription (line 34) | @Override
    method perform (line 39) | @Override
    method waitForView (line 62) | public static ViewAction waitForView(final int viewId, final long time...

FILE: app/src/main/java/net/osmtracker/GitHubUser.java
  class GitHubUser (line 8) | public class GitHubUser {
    method GitHubUser (line 11) | public GitHubUser(Context context) {
    method saveCredentials (line 15) | public void saveCredentials(String username, String token) {
    method getUsername (line 22) | public String getUsername() {
    method getToken (line 26) | public String getToken() {
    method hasCredentials (line 30) | public boolean hasCredentials() {
    method clear (line 34) | public void clear() {

FILE: app/src/main/java/net/osmtracker/OSMTracker.java
  class OSMTracker (line 10) | public class OSMTracker {
    class Preferences (line 16) | public static final class Preferences {
    class Devices (line 187) | public static final class Devices {

FILE: app/src/main/java/net/osmtracker/activity/About.java
  class About (line 32) | public class About extends Activity {
    method onCreate (line 39) | @Override
    method onCreateDialog (line 93) | @Override
    method getExportDbProgressDialog (line 126) | public ProgressDialog getExportDbProgressDialog() {
    method getDebugInfo (line 130) | private String getDebugInfo() {

FILE: app/src/main/java/net/osmtracker/activity/AvailableLayouts.java
  class AvailableLayouts (line 54) | public class AvailableLayouts extends Activity {
    method onCreate (line 74) | @Override
    method validateDefaultOptions (line 89) | @SuppressLint("StaticFieldLeak")
    method retrieveAvailableLayouts (line 109) | @SuppressLint("StaticFieldLeak")
    method isNetworkAvailable (line 135) | public static boolean isNetworkAvailable(Context context) {
    method setAvailableLayouts (line 146) | public void setAvailableLayouts(List<String> options) {
    method onCreateOptionsMenu (line 167) | @Override
    method onOptionsItemSelected (line 174) | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    method toggleRepositoryOptions (line 312) | private void toggleRepositoryOptions(boolean status){
    method parseResponse (line 339) | private List<String> parseResponse(String response) {
    method getLanguagesFor (line 362) | private HashMap<String,String> getLanguagesFor(String xmlFile){
    method getDescriptionFor (line 389) | private String getDescriptionFor(String xmlFile, String localeLanguage){
    method showDescriptionDialog (line 420) | private void showDescriptionDialog(String layoutName, String descripti...
    method showLanguageSelectionDialog (line 429) | private void showLanguageSelectionDialog(final HashMap<String,String> ...
    class ClickListener (line 449) | private class ClickListener implements View.OnClickListener{
      method onClick (line 450) | @Override
    class DownloadListener (line 477) | private class DownloadListener implements AlertDialog.OnClickListener{
      method DownloadListener (line 482) | public DownloadListener(String layoutName, String iso, Context conte...
      method onClick (line 488) | @Override

FILE: app/src/main/java/net/osmtracker/activity/ButtonsPresets.java
  class ButtonsPresets (line 43) | public class ButtonsPresets extends Activity {
    method onCreate (line 59) | @Override
    method onResume (line 65) | @Override
    method refreshActivity (line 92) | public void refreshActivity(){
    method initializeAttributes (line 102) | private void initializeAttributes(){
    method listLayouts (line 111) | private void listLayouts(LinearLayout rootLayout){
    method checkCurrentLayout (line 165) | private void checkCurrentLayout(LinearLayout downloadedLayouts, Linear...
    method selectLayout (line 209) | private void selectLayout(CheckBox pressed){
    class CheckBoxChangedListener (line 219) | private class CheckBoxChangedListener implements View.OnClickListener {
      method onClick (line 220) | @Override
    method onCreateContextMenu (line 227) | @Override
    method onContextItemSelected (line 235) | @SuppressLint("StaticFieldLeak")
    method getIso (line 316) | private String getIso(String layoutName){
    method onCreateOptionsMenu (line 325) | @Override
    method onOptionsItemSelected (line 331) | @Override
    method onRequestPermissionsResult (line 340) | public void onRequestPermissionsResult(int requestCode,
    method writeExternalStoragePermissionGranted (line 362) | private boolean writeExternalStoragePermissionGranted(){

FILE: app/src/main/java/net/osmtracker/activity/DisplayTrack.java
  class DisplayTrack (line 29) | public class DisplayTrack extends Activity {
    method onCreate (line 34) | @Override

FILE: app/src/main/java/net/osmtracker/activity/DisplayTrackMap.java
  class DisplayTrackMap (line 53) | public class DisplayTrackMap extends Activity {
    method onCreate (line 171) | @Override
    method selectTileSource (line 235) | public void selectTileSource() {
    method setTileDpiScaling (line 244) | public void setTileDpiScaling() {
    method selectMapTile (line 266) | private ITileSource selectMapTile(String mapTile) {
    method onSaveInstanceState (line 277) | @Override
    method onResume (line 287) | @Override
    method resumeActivity (line 293) | private void resumeActivity() {
    method onPause (line 318) | @Override
    method onStop (line 329) | @Override
    method onCreateOptionsMenu (line 340) | @Override
    method onPrepareOptionsMenu (line 348) | @Override
    method onOptionsItemSelected (line 354) | @Override
    method onTouchEvent (line 371) | @Override
    method createOverlays (line 385) | private void createOverlays() {
    method pathChanged (line 413) | private void pathChanged() {

FILE: app/src/main/java/net/osmtracker/activity/GitHubConfig.java
  class GitHubConfig (line 19) | public class GitHubConfig extends Activity {
    method onCreate (line 23) | @Override

FILE: app/src/main/java/net/osmtracker/activity/GitHubNewFork.java
  class GitHubNewFork (line 32) | public class GitHubNewFork extends Activity {
    method onCreate (line 37) | @Override
    method createNewFork (line 87) | private void createNewFork(String username, String repo) {
    method setNewForkFullName (line 123) | public void setNewForkFullName(String newForkFullName) {

FILE: app/src/main/java/net/osmtracker/activity/GitHubNewRepo.java
  class GitHubNewRepo (line 34) | public class GitHubNewRepo extends Activity {
    method onCreate (line 40) | @Override
    method createNewRepo (line 79) | private void createNewRepo(String repoName, boolean isPrivate) {
    method setNewRepoFullName (line 143) | public void setNewRepoFullName(String newRepoFullName) {

FILE: app/src/main/java/net/osmtracker/activity/GitHubPullRequest.java
  class GitHubPullRequest (line 35) | public class GitHubPullRequest extends Activity {
    method onCreate (line 42) | @Override
    method createPullRequest (line 88) | private void createPullRequest() {
    method getInfoRepo (line 167) | private void getInfoRepo(String repoFullName) {
    method setRepoOrigen (line 202) | public void setRepoOrigen(String repoOrigen) {
    method getDefaultBranch (line 206) | public String getDefaultBranch() {
    method setDefaultBranch (line 210) | public void setDefaultBranch(String defaultBranch) {

FILE: app/src/main/java/net/osmtracker/activity/GitHubUpload.java
  class GitHubUpload (line 54) | public class GitHubUpload extends Activity {
    method onCreate (line 63) | @Override
    method uploadCommit (line 88) | private void uploadCommit() {
    method onCreateOptionsMenu (line 135) | @Override
    method onOptionsItemSelected (line 141) | @Override
    method openActivityOnClick (line 153) | private void openActivityOnClick(int btnId, Class<? extends Activity> ...
    method startUploadGitHub (line 168) | private void startUploadGitHub(final String fileInBase64, String filen...
    method createSpinnerListRepos (line 238) | private void createSpinnerListRepos(Spinner spinner){
    method listRepos (line 272) | private void listRepos() {
    method listRepoResponseAction (line 322) | private void listRepoResponseAction(JSONArray response, ProgressDialog...
    method getRepoName (line 344) | public String getRepoName() {
    method setRepoName (line 348) | public void setRepoName(String repoName) {

FILE: app/src/main/java/net/osmtracker/activity/NoteList.java
  class NoteList (line 26) | public class NoteList extends AppCompatActivity implements NoteAdapter.O...
    method onCreate (line 33) | @Override
    method onResume (line 48) | @Override
    method refreshData (line 54) | private void refreshData() {
    method onNoteClick (line 64) | @Override
    method uploadNoteToOSM (line 123) | private void uploadNoteToOSM(long noteId) {

FILE: app/src/main/java/net/osmtracker/activity/OpenStreetMapNotesUpload.java
  class OpenStreetMapNotesUpload (line 41) | public class OpenStreetMapNotesUpload extends AppCompatActivity {
    method onCreate (line 58) | @Override
    method startUpload (line 121) | private void startUpload(long noteId) {
    method requestOsmAuth (line 138) | public void requestOsmAuth() {
    method exchangeAuthorizationCode (line 161) | private void exchangeAuthorizationCode(AuthorizationResponse resp) {
    method persistToken (line 173) | private void persistToken(String token) {
    method uploadToOsm (line 182) | public void uploadToOsm(String accessToken, long noteId) {

FILE: app/src/main/java/net/osmtracker/activity/OpenStreetMapUpload.java
  class OpenStreetMapUpload (line 43) | public class OpenStreetMapUpload extends TrackDetailEditor {
    method onCreate (line 53) | @Override
    method getTrackId (line 87) | private long getTrackId() {
    method onResume (line 101) | @Override
    method startUpload (line 124) | private void startUpload() {
    method requestOsmAuth (line 137) | public void requestOsmAuth() {
    method onActivityResult (line 161) | protected void onActivityResult(int requestCode, int resultCode, Inten...
    method uploadToOsm (line 205) | public void uploadToOsm(String accessToken) {

FILE: app/src/main/java/net/osmtracker/activity/Preferences.java
  class Preferences (line 30) | public class Preferences extends AppCompatActivity {
    method onCreate (line 32) | @Override
    class SettingsFragment (line 48) | public static class SettingsFragment extends PreferenceFragmentCompat {
      method onCreatePreferences (line 52) | @Override
      method setupStorageDirectory (line 144) | private void setupStorageDirectory() {
      method setupVoiceRecDuration (line 185) | private void setupVoiceRecDuration() {
      method setupOSMAuthClearData (line 203) | private void setupOSMAuthClearData(SharedPreferences prefs) {
      method setupPreferenceNavigation (line 236) | private void setupPreferenceNavigation(String preferenceKey, Intent ...
      method setupEditTextNum (line 255) | private void setupEditTextNum(String preferenceKey, String valueSuff...
      method onDisplayPreferenceDialog (line 285) | @SuppressWarnings("deprecation") // Required to link the dialog to t...
      method setupListPreference (line 344) | private void setupListPreference(String preferenceKey, String static...

FILE: app/src/main/java/net/osmtracker/activity/TrackDetail.java
  class TrackDetail (line 53) | public class TrackDetail extends TrackDetailEditor implements AdapterVie...
    method onCreate (line 85) | @Override
    method onResume (line 115) | @Override
    method onCreateOptionsMenu (line 220) | @Override
    method onOptionsItemSelected (line 227) | @Override
    method writeExternalStoragePermissionGranted (line 275) | private boolean writeExternalStoragePermissionGranted() {
    method exportTrack (line 315) | private void exportTrack(){
    method onRequestPermissionsResult (line 325) | public void onRequestPermissionsResult(int requestCode,
    method onItemClick (line 353) | public void onItemClick(AdapterView<?> parent, View view, final int po...
    class TrackDetailSimpleAdapter (line 378) | private class TrackDetailSimpleAdapter extends SimpleAdapter
      method TrackDetailSimpleAdapter (line 380) | public TrackDetailSimpleAdapter
      method getView (line 390) | public View getView(final int position, View convertView, ViewGroup ...

FILE: app/src/main/java/net/osmtracker/activity/TrackDetailEditor.java
  class TrackDetailEditor (line 24) | public abstract class TrackDetailEditor extends Activity {
    method onCreate (line 44) | protected void onCreate(Bundle savedInstanceState, int viewResId, long...
    method bindTrack (line 67) | protected void bindTrack(Track t) {
    method save (line 81) | protected boolean save() {

FILE: app/src/main/java/net/osmtracker/activity/TrackListRVAdapter.java
  class TrackListRVAdapter (line 18) | public class TrackListRVAdapter extends RecyclerView.Adapter<TrackListRV...
    method TrackListRVAdapter (line 27) | public TrackListRVAdapter(Context context, Cursor cursor,
    method getCursorAdapter (line 34) | public TracklistAdapter getCursorAdapter() {
    type TrackListRecyclerViewAdapterListener (line 38) | public interface TrackListRecyclerViewAdapterListener {
      method onClick (line 39) | void onClick(long trackId);
      method onCreateContextMenu (line 41) | void onCreateContextMenu(ContextMenu contextMenu, View view,
    class TrackItemVH (line 48) | public class TrackItemVH extends RecyclerView.ViewHolder
      method TrackItemVH (line 59) | public TrackItemVH(View view) {
      method getvId (line 75) | public TextView getvId() {
      method getvNameOrStartDate (line 79) | public TextView getvNameOrStartDate() {
      method getvWps (line 83) | public TextView getvWps() {
      method getvTps (line 87) | public TextView getvTps() {
      method getvStatus (line 91) | public ImageView getvStatus() {
      method getvUploadStatus (line 95) | public ImageView getvUploadStatus() {
      method onClick (line 104) | @Override
      method onCreateContextMenu (line 110) | @Override
    method onCreateViewHolder (line 118) | @NonNull
    method onBindViewHolder (line 127) | @Override
    method getItemCount (line 137) | @Override

FILE: app/src/main/java/net/osmtracker/activity/TrackLogger.java
  class TrackLogger (line 70) | public class TrackLogger extends Activity {
    method getButtonsEnabled (line 179) | public boolean getButtonsEnabled() {
    method onCreate (line 183) | @Override
    method saveTagsForTrack (line 233) | private void saveTagsForTrack(){
    method onResume (line 285) | @Override
    method checkGPSProvider (line 367) | private void checkGPSProvider() {
    method onPause (line 390) | @Override
    method onSaveInstanceState (line 419) | @Override
    method onGpsDisabled (line 433) | public void onGpsDisabled() {
    method onGpsEnabled (line 441) | public void onGpsEnabled() {
    method setEnabledActionButtons (line 453) | public void setEnabledActionButtons(boolean enabled) {
    method onCreateOptionsMenu (line 461) | @Override
    method onOptionsItemSelected (line 469) | @Override
    method onKeyDown (line 515) | @Override
    method requestStillImage (line 555) | public void requestStillImage() {
    method getRealPathFromURI (line 598) | public String getRealPathFromURI(Uri contentUri) {
    method onActivityResult (line 605) | @Override
    method getGpsLogger (line 657) | public GPSLogger getGpsLogger() {
    method setGpsLogger (line 667) | public void setGpsLogger(GPSLogger l) {
    method createImageFile (line 676) | public File createImageFile() {
    method onCreateDialog (line 691) | @Override
    method onPrepareDialog (line 729) | @Override
    method onNewIntent (line 741) | @Override
    method getCurrentTrackId (line 755) | public long getCurrentTrackId() {
    method startCamera (line 762) | private void startCamera() {
    method startGallery (line 782) | private void startGallery() {
    method onRequestPermissionsResult (line 789) | public void onRequestPermissionsResult(int requestCode, @NonNull Strin...

FILE: app/src/main/java/net/osmtracker/activity/TrackManager.java
  class TrackManager (line 62) | public class TrackManager extends AppCompatActivity
    method onCreate (line 102) | @Override
    method onResume (line 140) | @Override
    method setRecyclerView (line 167) | private void setRecyclerView() {
    method onSaveInstanceState (line 189) | @Override
    method onRestoreInstanceState (line 195) | @Override
    method onCreateOptionsMenu (line 201) | @Override
    method onPrepareOptionsMenu (line 207) | @Override
    method onOptionsItemSelected (line 231) | @Override
    method tryStartTrackLogger (line 295) | private void tryStartTrackLogger(Intent intent){
    method startTrackLoggerForNewTrack (line 324) | private void startTrackLoggerForNewTrack(){
    method exportTracks (line 343) | private void exportTracks(boolean onlyContextMenuSelectedTrack) {
    method onCreateContextMenu (line 394) | @Override
    method onContextItemSelected (line 415) | @Override
    method uploadTrack (line 524) | private void uploadTrack(long trackId){
    method encodeFileToBase64 (line 531) | private static void encodeFileToBase64(File inputFile, File outputFile...
    method uploadTrackToGitHub (line 545) | private void uploadTrackToGitHub(final long trackId, Context context){
    method uploadTrackToGitHubAUX (line 555) | private void uploadTrackToGitHubAUX(File tmpGPXFile, Context context) {
    method displayTrack (line 586) | private void displayTrack(long trackId){
    method writeExternalStoragePermissionGranted (line 603) | private boolean writeExternalStoragePermissionGranted(){
    method onClick (line 615) | @Override
    method createNewTrack (line 638) | private long createNewTrack() throws CreateTrackException {
    method prepareAndShareTrack (line 667) | private static void prepareAndShareTrack(final long trackId, Context c...
    method shareFile (line 705) | private static void shareFile(File tmpGPXFile, Context context) {
    method deleteTrack (line 724) | private void deleteTrack(long id) {
    method updateTrackItemsInRecyclerView (line 741) | private void updateTrackItemsInRecyclerView() {
    method deleteAllTracks (line 749) | private void deleteAllTracks() {
    method setActiveTrack (line 773) | private void setActiveTrack(long trackId){
    method stopActiveTrack (line 792) | private void stopActiveTrack(){
    method onRequestPermissionsResult (line 812) | public void onRequestPermissionsResult(int requestCode, String permiss...
    method getGPXinBase64 (line 933) | public String getGPXinBase64() {
    method setGPXinBase64 (line 937) | public void setGPXinBase64(String GPXinBase64) {

FILE: app/src/main/java/net/osmtracker/activity/WaypointList.java
  class WaypointList (line 35) | public class WaypointList extends ListActivity {
    method onCreate (line 39) | @Override
    method onResume (line 48) | @Override
    method onPause (line 60) | @Override
    method onListItemClick (line 83) | @Override
    method isImageFile (line 213) | private boolean isImageFile(String path) {
    method isAudioFile (line 223) | private boolean isAudioFile(String path) {

FILE: app/src/main/java/net/osmtracker/adapter/NoteAdapter.java
  class NoteAdapter (line 22) | public class NoteAdapter extends RecyclerView.Adapter<NoteAdapter.NoteVi...
    type OnNoteClickListener (line 33) | public interface OnNoteClickListener {
      method onNoteClick (line 34) | void onNoteClick(long id, long noteId, String uuid, String name);
    method NoteAdapter (line 37) | public NoteAdapter(OnNoteClickListener listener) {
    method swapCursor (line 41) | public void swapCursor(Cursor newCursor) {
    method onCreateViewHolder (line 48) | @NonNull
    method onBindViewHolder (line 56) | @Override
    method getItemCount (line 102) | @Override
    class NoteViewHolder (line 107) | public static class NoteViewHolder extends RecyclerView.ViewHolder {
      method NoteViewHolder (line 114) | public NoteViewHolder(View v) {

FILE: app/src/main/java/net/osmtracker/db/DataHelper.java
  class DataHelper (line 32) | public class DataHelper {
    method DataHelper (line 129) | public DataHelper(Context c) {
    method track (line 149) | public void track(long trackId, Location location, float azimuth, int ...
    method wayPoint (line 206) | public void wayPoint(long trackId, Location location, String name, Str...
    method updateWayPoint (line 272) | public void updateWayPoint(long trackId, String uuid, String name, Str...
    method deleteWayPoint (line 298) | public void deleteWayPoint(String uuid, String filepath) {
    method trackNote (line 319) | public void trackNote(long trackId, Location location, String name, St...
    method updateNote (line 355) | public void updateNote(long trackId, String uuid, String name) {
    method deleteNote (line 374) | public void deleteNote(String uuid) {
    method stopTracking (line 385) | public void stopTracking(long trackId) {
    method getActiveTrackId (line 397) | public static long getActiveTrackId(ContentResolver cr) {
    method setTrackName (line 413) | public static void setTrackName(long trackId, String name, ContentReso...
    method setTrackExportDate (line 426) | public static void setTrackExportDate(long trackId, long exportTime, C...
    method setTrackUploadDate (line 433) | public static void setTrackUploadDate(long trackId, long uploadTime, C...
    method setNoteUploadDate (line 440) | public static void setNoteUploadDate(long noteId, long uploadTime, Con...
    method renameFile (line 455) | private String renameFile(Long trackId, String from, String to) {
    method getTrackDirFromDB (line 485) | public static File getTrackDirFromDB(ContentResolver cr, long trackId) {
    method getTrackDirectory (line 513) | public static File getTrackDirectory(long trackId, Context context) {
    method getTrackNameInDB (line 523) | public static String getTrackNameInDB(long trackId, ContentResolver co...
    method getTrackByStartDate (line 541) | public Track getTrackByStartDate(Date startDate) {
    method getTrackById (line 560) | public Track getTrackById(long trackId) {
    method getWayPointIdsOfTrack (line 573) | public List<Integer> getWayPointIdsOfTrack(long trackId) {
    method getWayPointById (line 594) | public WayPoint getWayPointById(Integer wayPointId) {
    method getTrackPointIdsOfTrack (line 608) | public List<Integer> getTrackPointIdsOfTrack(long trackId) {
    method getTrackPointById (line 628) | public TrackPoint getTrackPointById(Integer trackPointId) {

FILE: app/src/main/java/net/osmtracker/db/DatabaseHelper.java
  class DatabaseHelper (line 23) | public class DatabaseHelper extends SQLiteOpenHelper {
    method DatabaseHelper (line 149) | public DatabaseHelper(Context context) {
    method onCreate (line 154) | @Override
    method onUpgrade (line 168) | @Override
    method manageNewStoragePath (line 212) | @SuppressWarnings("deprecation")

FILE: app/src/main/java/net/osmtracker/db/ExportDatabaseTask.java
  class ExportDatabaseTask (line 20) | public class ExportDatabaseTask extends AsyncTask<File, Float, String>{
    method ExportDatabaseTask (line 37) | public ExportDatabaseTask(About activity, File targetFolder) {
    method doInBackground (line 42) | @Override
    method onProgressUpdate (line 82) | @Override
    method onPostExecute (line 89) | @Override

FILE: app/src/main/java/net/osmtracker/db/TrackContentProvider.java
  class TrackContentProvider (line 25) | public class TrackContentProvider extends ContentProvider {
    method waypointsUri (line 129) | public static final Uri waypointsUri(long trackId) {
    method waypointUri (line 139) | public static final Uri waypointUri(long waypointId) {
    method noteUri (line 147) | public static final Uri noteUri(long noteId) {
    method notesUri (line 155) | public static final Uri notesUri(long trackId) {
    method trackPointsUri (line 165) | public static final Uri trackPointsUri(long trackId) {
    method trackpointUri (line 175) | public static final Uri trackpointUri(long trackpointId) {
    method trackStartUri (line 183) | public static final Uri trackStartUri(long trackId) {
    method trackEndUri (line 193) | public static final Uri trackEndUri(long trackId) {
    method onCreate (line 204) | @Override
    method delete (line 210) | @Override
    method getType (line 257) | @Override
    method insert (line 280) | @Override
    method query (line 351) | @Override
    method update (line 497) | @Override
    class Schema (line 567) | public static final class Schema {

FILE: app/src/main/java/net/osmtracker/db/TracklistAdapter.java
  class TracklistAdapter (line 23) | public class TracklistAdapter extends CursorAdapter {
    method TracklistAdapter (line 25) | public TracklistAdapter(Context context, Cursor c) {
    method bindView (line 29) | @Override
    method newView (line 34) | @Override
    method bind (line 52) | private View bind(Cursor cursor, View v, Context context) {

FILE: app/src/main/java/net/osmtracker/db/WaypointListAdapter.java
  class WaypointListAdapter (line 25) | public class WaypointListAdapter extends CursorAdapter {
    method WaypointListAdapter (line 43) | public WaypointListAdapter(Context context, Cursor c) {
    method bindView (line 47) | @Override
    method newView (line 53) | @Override
    method bind (line 71) | private View bind(Cursor cursor, TableLayout tl, Context context) {

FILE: app/src/main/java/net/osmtracker/db/model/Point.java
  class Point (line 10) | public abstract class Point {
    method Point (line 25) | protected Point(Cursor c) {
    method Point (line 51) | public Point() {
    method getLatitude (line 56) | public double getLatitude() {
    method setLatitude (line 60) | public void setLatitude(double latitude) {
    method getLongitude (line 64) | public double getLongitude() {
    method setLongitude (line 68) | public void setLongitude(double longitude) {
    method getElevation (line 72) | public Double getElevation() {
    method setElevation (line 76) | public void setElevation(Double elevation) {
    method getPointTimestamp (line 80) | public long getPointTimestamp() {
    method setPointTimestamp (line 84) | public void setPointTimestamp(long pointTimestamp) {
    method getAccuracy (line 89) | public Double getAccuracy() {
    method setAccuracy (line 93) | public void setAccuracy(Double accuracy) {
    method getCompassHeading (line 97) | public Double getCompassHeading() {
    method setCompassHeading (line 101) | public void setCompassHeading(Double compassHeading) {
    method getCompassAccuracy (line 105) | public Double getCompassAccuracy() {
    method setCompassAccuracy (line 109) | public void setCompassAccuracy(Double compassAccuracy) {
    method getAtmosphericPressure (line 113) | public Double getAtmosphericPressure() {
    method setAtmosphericPressure (line 117) | public void setAtmosphericPressure(Double atmosphericPressure) {
    method getId (line 121) | public Integer getId() {
    method setId (line 125) | public void setId(Integer id) {
    method getTrackId (line 129) | public Integer getTrackId() {
    method setTrackId (line 133) | public void setTrackId(Integer trackId) {

FILE: app/src/main/java/net/osmtracker/db/model/Track.java
  class Track (line 21) | public class Track {
    type OSMVisibility (line 25) | public enum OSMVisibility {
      method OSMVisibility (line 34) | private OSMVisibility(int position, int resId) {
      method fromPosition (line 39) | public static OSMVisibility fromPosition(int position) {
    method build (line 74) | public static Track build(final long trackId, Cursor tc, ContentResolv...
    method readExtraInformation (line 105) | private void readExtraInformation(){
    method setName (line 127) | public void setName(String name) {
    method setTrackId (line 131) | public void setTrackId(long trackId) {
    method getTrackId (line 134) | public long getTrackId() {
    method setDescription (line 138) | public void setDescription(String description) {
    method setTpCount (line 142) | public void setTpCount(int tpCount) {
    method setWpCount (line 146) | public void setWpCount(int wpCount) {
    method setNoteCount (line 150) | public void setNoteCount(int noteCount) {
    method setTracktDate (line 154) | public void setTracktDate(long tracktDate) {
    method setEndDate (line 158) | public void setEndDate(long endDate) {
    method setStartLat (line 162) | public void setStartLat(float startLat) {
    method setTrackDate (line 166) | public void setTrackDate(long trackDate) { this.trackDate = trackDate; }
    method getTrackDate (line 168) | public long getTrackDate() {
    method setStartDate (line 172) | public void setStartDate(long startDate) {
    method getStartDate (line 176) | public long getStartDate() {
    method setStartLong (line 180) | public void setStartLong(float startLong) {
    method setEndLat (line 184) | public void setEndLat(float endLat) {
    method setEndLong (line 188) | public void setEndLong(float endLong) {
    method getWpCount (line 192) | public Integer getWpCount() {
    method getTpCount (line 196) | public Integer getTpCount() {
    method getNoteCount (line 200) | public Integer getNoteCount() {
    method getDisplayName (line 205) | public String getDisplayName() {
    method getName (line 214) | public String getName() { return name; }
    method getDescription (line 216) | public String getDescription() {
    method setTags (line 220) | public void setTags(List<String> tags) {
    method setTags (line 224) | public void setTags(String tags) {
    method getTags (line 230) | public List<String> getTags() {
    method setVisibility (line 234) | public void setVisibility(OSMVisibility visibility) {
    method getVisibility (line 238) | public OSMVisibility getVisibility() {
    method getCommaSeparatedTags (line 242) | public String getCommaSeparatedTags() {
    method getStartDateAsString (line 253) | public String getStartDateAsString() {
    method getEndDateAsString (line 262) | public String getEndDateAsString() {
    method getStartLat (line 271) | public Float getStartLat() {
    method getStartLong (line 276) | public Float getStartLong() {
    method getEndLat (line 281) | public Float getEndLat() {
    method getEndLong (line 286) | public Float getEndLong() {

FILE: app/src/main/java/net/osmtracker/db/model/TrackPoint.java
  class TrackPoint (line 10) | public class TrackPoint extends Point {
    method TrackPoint (line 15) | public TrackPoint(Cursor c) {
    method TrackPoint (line 22) | public TrackPoint() {
    method getSpeed (line 27) | public Double getSpeed() {
    method setSpeed (line 31) | public void setSpeed(Double speed) {

FILE: app/src/main/java/net/osmtracker/db/model/WayPoint.java
  class WayPoint (line 10) | public class WayPoint extends Point {
    method WayPoint (line 18) | public WayPoint(Cursor c) {
    method WayPoint (line 25) | public WayPoint() {
    method getName (line 29) | public String getName() {
    method setName (line 33) | public void setName(String name) {
    method getLink (line 37) | public String getLink() {
    method setLink (line 41) | public void setLink(String link) {
    method getNumberOfSatellites (line 45) | public Integer getNumberOfSatellites() {
    method setNumberOfSatellites (line 49) | public void setNumberOfSatellites(Integer numberOfSatellites) {
    method getUuid (line 53) | public String getUuid() {
    method setUuid (line 57) | public void setUuid(String uuid) {

FILE: app/src/main/java/net/osmtracker/exception/CreateTrackException.java
  class CreateTrackException (line 3) | public class CreateTrackException extends Exception {
    method CreateTrackException (line 7) | public CreateTrackException(String message) {

FILE: app/src/main/java/net/osmtracker/exception/ExportTrackException.java
  class ExportTrackException (line 3) | public class ExportTrackException extends Exception {
    method ExportTrackException (line 7) | public ExportTrackException(String message) {

FILE: app/src/main/java/net/osmtracker/github/GitHubConstants.java
  class GitHubConstants (line 3) | public final class GitHubConstants {
    method GitHubConstants (line 15) | private GitHubConstants() {
    method getRepoUrl (line 20) | public static String getRepoUrl(String repoFullName) {
    method getRepoForksUrl (line 31) | public static String getRepoForksUrl(String username, String repo) {
    method getRepoPullsUrl (line 40) | public static String getRepoPullsUrl(String repoOrigen) {
    method getRepoFileContentUrl (line 50) | public static String getRepoFileContentUrl(String repoOrigen, String f...
    method getUserReposUrl (line 59) | public static String getUserReposUrl(String sortBy) {
    method getUserReposUrl (line 63) | public static String getUserReposUrl(){

FILE: app/src/main/java/net/osmtracker/gpx/ExportToStorageTask.java
  class ExportToStorageTask (line 26) | public class ExportToStorageTask extends ExportTrackTask {
    method ExportToStorageTask (line 39) | public ExportToStorageTask(Context context, long... trackId) {
    method ExportToStorageTask (line 50) | public ExportToStorageTask(Context context, DataHelper dataHelper, lon...
    method getExportDirectory (line 64) | @Override
    method getSanitizedTrackNameByStartDate (line 88) | public String getSanitizedTrackNameByStartDate(Date startDate) {
    method shouldCreateDirectoryPerTrack (line 106) | public boolean shouldCreateDirectoryPerTrack(){
    method isExternalStorageWritable (line 116) | private boolean isExternalStorageWritable() {
    method getBaseExportDirectory (line 127) | public File getBaseExportDirectory() throws ExportTrackException {
    method exportMediaFiles (line 153) | @Override
    method updateExportDate (line 158) | @Override

FILE: app/src/main/java/net/osmtracker/gpx/ExportToTempFileTask.java
  class ExportToTempFileTask (line 21) | public abstract class ExportToTempFileTask extends ExportTrackTask {
    method ExportToTempFileTask (line 28) | public ExportToTempFileTask(Context context, long trackId) {
    method getExportDirectory (line 50) | @Override
    method buildGPXFilename (line 55) | @Override
    method exportMediaFiles (line 61) | @Override
    method updateExportDate (line 66) | @Override
    method getTmpFile (line 71) | public File getTmpFile() {
    method getFilename (line 75) | public String getFilename() {
    method onPostExecute (line 79) | @Override
    method executionCompleted (line 85) | protected abstract void executionCompleted();

FILE: app/src/main/java/net/osmtracker/gpx/ExportTrackTask.java
  class ExportTrackTask (line 47) | public abstract class ExportTrackTask extends AsyncTask<Void, Long, Bool...
    method getExportDirectory (line 107) | protected abstract File getExportDirectory(Date startDate) throws Expo...
    method exportMediaFiles (line 113) | protected abstract boolean exportMediaFiles();
    method updateExportDate (line 119) | protected abstract boolean updateExportDate();
    method ExportTrackTask (line 121) | public ExportTrackTask(Context context, long... trackIds) {
    method onPreExecute (line 127) | @Override
    method doInBackground (line 139) | @Override
    method onProgressUpdate (line 152) | @Override
    method onPostExecute (line 176) | @Override
    method exportTrackAsGpx (line 198) | protected void exportTrackAsGpx(long trackId) throws ExportTrackExcept...
    method writeGpxFile (line 276) | private void writeGpxFile(String trackName, String tags, String track_...
    method writeTrackPoints (line 332) | private void writeTrackPoints(String trackName, Writer fw, Cursor c, b...
    method writeWayPoints (line 414) | private void writeWayPoints(Writer fw, Cursor c, String accuracyInfo, ...
    method copyWaypointFiles (line 529) | private void copyWaypointFiles(long trackId, File gpxOutputDirectory) {
    method buildGPXFilename (line 548) | public String buildGPXFilename(Cursor cursor, File parentDirectory) {
    method formatGpxFilename (line 567) | public String formatGpxFilename(String desiredOutputFormat, String san...
    method sanitizeTrackName (line 617) | public String sanitizeTrackName(String trackName){
    method getErrorMsg (line 626) | public String getErrorMsg() {

FILE: app/src/main/java/net/osmtracker/gpx/ZipHelper.java
  class ZipHelper (line 16) | public class ZipHelper {
    method zipCacheFiles (line 28) | public static File zipCacheFiles(Context context, long trackId, File f...
    method addFileToZip (line 75) | private static void addFileToZip(File file, ZipOutputStream zos) throw...

FILE: app/src/main/java/net/osmtracker/layout/DisablableTableLayout.java
  class DisablableTableLayout (line 16) | public class DisablableTableLayout extends TableLayout {
    method DisablableTableLayout (line 18) | public DisablableTableLayout(Context context) {
    method setOnClickListenerForAllChild (line 26) | public void setOnClickListenerForAllChild(OnClickListener l) {
    method setEnabled (line 45) | @Override

FILE: app/src/main/java/net/osmtracker/layout/DownloadCustomLayoutTask.java
  class DownloadCustomLayoutTask (line 33) | public class DownloadCustomLayoutTask extends AsyncTask<String, Integer,...
    method DownloadCustomLayoutTask (line 38) | public DownloadCustomLayoutTask(Context context) {
    method doInBackground (line 42) | @Override
    method downloadLayout (line 51) | public boolean downloadLayout(String layoutName, String iso){
    method createDir (line 97) | private void createDir(String dirPath) throws IOException {
    method isSDCardPresent (line 114) | private boolean isSDCardPresent() {
    method downloadFile (line 118) | private void downloadFile( String downloadUrl, String outputFile) thro...
    method getIconsHash (line 146) | private HashMap<String,String> getIconsHash(String layoutName) {

FILE: app/src/main/java/net/osmtracker/layout/GetStringResponseTask.java
  class GetStringResponseTask (line 16) | public class GetStringResponseTask extends AsyncTask<String, Integer, St...
    method doInBackground (line 24) | @Override

FILE: app/src/main/java/net/osmtracker/layout/GpsStatusRecord.java
  class GpsStatusRecord (line 37) | public class GpsStatusRecord extends LinearLayout implements LocationLis...
    method GpsStatusRecord (line 95) | public GpsStatusRecord(Context context, AttributeSet attrs) {
    method requestLocationUpdates (line 116) | public void requestLocationUpdates(boolean request) {
    method onSatelliteStatusChanged (line 132) | @Override
    method onLocationChanged (line 156) | @Override
    method onProviderDisabled (line 191) | @Override
    method onProviderEnabled (line 200) | @Override
    method onStatusChanged (line 206) | @Override
    method manageRecordingIndicator (line 235) | public void manageRecordingIndicator(boolean isTracking) {
    method manageSatelliteStatusIndicator (line 248) | private void manageSatelliteStatusIndicator(int accuracy){

FILE: app/src/main/java/net/osmtracker/layout/TLSSocketFactory.java
  class TLSSocketFactory (line 17) | public class TLSSocketFactory extends SSLSocketFactory {
    method TLSSocketFactory (line 21) | public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorit...
    method getDefaultCipherSuites (line 27) | @Override
    method getSupportedCipherSuites (line 32) | @Override
    method createSocket (line 37) | @Override
    method createSocket (line 42) | @Override
    method createSocket (line 47) | @Override
    method createSocket (line 52) | @Override
    method createSocket (line 57) | @Override
    method createSocket (line 62) | @Override
    method enableTLSOnSocket (line 67) | private Socket enableTLSOnSocket(Socket socket) {

FILE: app/src/main/java/net/osmtracker/layout/URLValidatorTask.java
  class URLValidatorTask (line 18) | public class URLValidatorTask extends AsyncTask<String, Integer, Boolean>{
    method doInBackground (line 22) | @Override
    method customLayoutsRepoValidator (line 35) | protected boolean customLayoutsRepoValidator(String githubUsername, St...

FILE: app/src/main/java/net/osmtracker/layout/UserDefinedLayout.java
  class UserDefinedLayout (line 31) | public class UserDefinedLayout extends LinearLayout {
    method UserDefinedLayout (line 51) | public UserDefinedLayout(Context ctx) {
    method UserDefinedLayout (line 55) | public UserDefinedLayout(TrackLogger activity, long trackId, File xmlL...
    method push (line 90) | public void push(String s) {
    method pop (line 105) | public String pop() {
    method getStackSize (line 117) | public int getStackSize() {
    method setEnabled (line 121) | @Override

FILE: app/src/main/java/net/osmtracker/listener/EditWaypointDialogOnClickListener.java
  class EditWaypointDialogOnClickListener (line 10) | public class EditWaypointDialogOnClickListener implements View.OnClickLi...
    method EditWaypointDialogOnClickListener (line 16) | protected EditWaypointDialogOnClickListener(AlertDialog alert, Cursor ...
    method onClick (line 21) | @Override

FILE: app/src/main/java/net/osmtracker/listener/PageButtonOnClickListener.java
  class PageButtonOnClickListener (line 14) | public class PageButtonOnClickListener implements OnClickListener {
    method PageButtonOnClickListener (line 26) | public PageButtonOnClickListener(UserDefinedLayout layout, String targ...
    method onClick (line 31) | @Override

FILE: app/src/main/java/net/osmtracker/listener/PressureListener.java
  class PressureListener (line 16) | public class PressureListener implements SensorEventListener {
    method onSensorChanged (line 27) | @Override
    method onAccuracyChanged (line 32) | @Override
    method register (line 37) | public boolean register (Context context, boolean use_barometer) {
    method unregister (line 62) | public void unregister () {
    method getPressure (line 71) | public float getPressure () {

FILE: app/src/main/java/net/osmtracker/listener/SensorListener.java
  class SensorListener (line 26) | public class SensorListener implements SensorEventListener {
    method onAccuracyChanged (line 94) | public void onAccuracyChanged(Sensor sensor, int accuracy) {
    method onSensorChanged (line 98) | public void onSensorChanged(SensorEvent event) {
    method calcOrientation (line 162) | private boolean calcOrientation() {
    method register (line 197) | public boolean register(Activity activity) {
    method register (line 207) | public boolean register(Context context){
    method register (line 217) | public boolean register(Context context, boolean use_orientation) {
    method unregister (line 260) | public void unregister() {
    method getAzimuth (line 269) | public float getAzimuth() {
    method getAccuracy (line 277) | public int getAccuracy() {

FILE: app/src/main/java/net/osmtracker/listener/StillImageOnClickListener.java
  class StillImageOnClickListener (line 14) | public class StillImageOnClickListener implements OnClickListener {
    method StillImageOnClickListener (line 21) | public StillImageOnClickListener(TrackLogger parent) {
    method onClick (line 25) | @Override

FILE: app/src/main/java/net/osmtracker/listener/TagButtonOnClickListener.java
  class TagButtonOnClickListener (line 24) | public class TagButtonOnClickListener implements OnClickListener {
    method TagButtonOnClickListener (line 28) | public TagButtonOnClickListener(long trackId) {
    method onClick (line 32) | @Override

FILE: app/src/main/java/net/osmtracker/listener/TextNoteOnClickListener.java
  class TextNoteOnClickListener (line 14) | public class TextNoteOnClickListener implements OnClickListener {
    method TextNoteOnClickListener (line 19) | public TextNoteOnClickListener(TrackLogger trackLogger) {
    method onClick (line 23) | @Override

FILE: app/src/main/java/net/osmtracker/listener/VoiceRecOnClickListener.java
  class VoiceRecOnClickListener (line 14) | public class VoiceRecOnClickListener implements OnClickListener{
    method VoiceRecOnClickListener (line 21) | public VoiceRecOnClickListener(TrackLogger trackLogger) {
    method onClick (line 25) | @Override

FILE: app/src/main/java/net/osmtracker/osm/OpenStreetMapConstants.java
  class OpenStreetMapConstants (line 3) | public class OpenStreetMapConstants {
    class Api (line 10) | public static class Api {
    class OAuth2 (line 16) | public static class OAuth2 {
      class Urls (line 25) | public static class Urls {

FILE: app/src/main/java/net/osmtracker/osm/UploadToOpenStreetMapNotesTask.java
  class UploadToOpenStreetMapNotesTask (line 31) | public class UploadToOpenStreetMapNotesTask {
    method UploadToOpenStreetMapNotesTask (line 55) | public UploadToOpenStreetMapNotesTask(Activity activity, String access...
    method run (line 68) | public void run() {
    method handleResult (line 111) | private void handleResult(Activity activity) {
    method createProgressDialog (line 135) | private AlertDialog createProgressDialog(Activity activity) {
    method showAuthErrorDialog (line 159) | private void showAuthErrorDialog(Activity activity) {

FILE: app/src/main/java/net/osmtracker/osm/UploadToOpenStreetMapTask.java
  class UploadToOpenStreetMapTask (line 37) | public class UploadToOpenStreetMapTask extends AsyncTask<Void, Void, Voi...
    method UploadToOpenStreetMapTask (line 78) | public UploadToOpenStreetMapTask(Activity activity, String accessToken...
    method onPreExecute (line 91) | @Override
    method onPostExecute (line 111) | @Override
    method doInBackground (line 174) | @Override
    method getVisibilityForOsmapi (line 200) | private GpsTraceDetails.Visibility getVisibilityForOsmapi(Track.OSMVis...

FILE: app/src/main/java/net/osmtracker/overlay/WayPointsOverlay.java
  class WayPointsOverlay (line 20) | public class WayPointsOverlay extends ItemizedOverlay<OverlayItem> {
    method WayPointsOverlay (line 31) | public WayPointsOverlay(
    method WayPointsOverlay (line 44) | public WayPointsOverlay(
    method onSnapToItem (line 52) | @Override
    method createItem (line 58) | @Override
    method size (line 63) | @Override
    method refresh (line 68) | public void refresh() {

FILE: app/src/main/java/net/osmtracker/receiver/MediaButtonReceiver.java
  class MediaButtonReceiver (line 9) | public class MediaButtonReceiver extends BroadcastReceiver {
    method onReceive (line 11) | @Override

FILE: app/src/main/java/net/osmtracker/service/gps/GPSLogger.java
  class GPSLogger (line 41) | public class GPSLogger extends Service implements LocationListener {
    method onReceive (line 112) | @Override
    method onBind (line 205) | @Override
    method onUnbind (line 211) | @Override
    class GPSLoggerBinder (line 228) | public class GPSLoggerBinder extends Binder {
      method getService (line 235) | public GPSLogger getService() {
    method onCreate (line 240) | @Override
    method onStartCommand (line 284) | @Override
    method onDestroy (line 292) | @Override
    method startTracking (line 319) | private void startTracking(long trackId) {
    method stopTrackingAndSave (line 331) | private void stopTrackingAndSave() {
    method onLocationChanged (line 338) | @Override
    method getNotification (line 358) | private Notification getNotification() {
    method createNotificationChannel (line 375) | private void createNotificationChannel() {
    method stopNotifyBackgroundService (line 396) | private void stopNotifyBackgroundService() {
    method onProviderDisabled (line 401) | @Override
    method onProviderEnabled (line 406) | @Override
    method onStatusChanged (line 411) | @Override
    method isGpsEnabled (line 420) | public boolean isGpsEnabled() {
    method isTracking (line 428) | public boolean isTracking() {

FILE: app/src/main/java/net/osmtracker/service/gps/GPSLoggerServiceConnection.java
  class GPSLoggerServiceConnection (line 20) | public class GPSLoggerServiceConnection implements ServiceConnection {
    method GPSLoggerServiceConnection (line 27) | public GPSLoggerServiceConnection(TrackLogger tl) {
    method onServiceDisconnected (line 31) | @Override
    method onServiceConnected (line 37) | @Override

FILE: app/src/main/java/net/osmtracker/service/resources/AppResourceIconResolver.java
  class AppResourceIconResolver (line 13) | public class AppResourceIconResolver implements IconResolver {
    method AppResourceIconResolver (line 30) | public AppResourceIconResolver(Resources r, String defPackage) {
    method getIcon (line 35) | @Override

FILE: app/src/main/java/net/osmtracker/service/resources/ExternalDirectoryIconResolver.java
  class ExternalDirectoryIconResolver (line 17) | public class ExternalDirectoryIconResolver implements IconResolver {
    method ExternalDirectoryIconResolver (line 24) | public ExternalDirectoryIconResolver(File baseDir) {
    method getIcon (line 32) | @Override

FILE: app/src/main/java/net/osmtracker/service/resources/IconResolver.java
  type IconResolver (line 11) | public interface IconResolver {
    method getIcon (line 17) | public Drawable getIcon(String key);

FILE: app/src/main/java/net/osmtracker/util/ArrayUtils.java
  class ArrayUtils (line 8) | public final class ArrayUtils {
    method findMin (line 19) | public static double findMin(double[][] in, int offset) {
    method findMax (line 38) | public static double findMax(double[][] in, int offset) {

FILE: app/src/main/java/net/osmtracker/util/Callback.java
  type Callback (line 8) | public interface Callback {
    method onResult (line 16) | String onResult(String result);

FILE: app/src/main/java/net/osmtracker/util/CustomLayoutsUtils.java
  class CustomLayoutsUtils (line 26) | public class CustomLayoutsUtils {
    method convertFileName (line 34) | public static String convertFileName(String fileName) {
    method unconvertFileName (line 52) | public static String unconvertFileName(String representation){
    method createFileName (line 63) | public static String createFileName(String layoutName, String iso) {
    method getStringFromStream (line 77) | public static String getStringFromStream(InputStream stream) throws IO...
    method getCurrentLayoutName (line 102) | public static String getCurrentLayoutName(Context context){

FILE: app/src/main/java/net/osmtracker/util/DialogUtils.java
  class DialogUtils (line 7) | public class DialogUtils {
    method showErrorDialog (line 14) | public static void showErrorDialog(Context ctx, CharSequence msg) {
    method showSuccessDialog (line 34) | public static void showSuccessDialog(Context context, int message) {

FILE: app/src/main/java/net/osmtracker/util/FileSystemUtils.java
  class FileSystemUtils (line 13) | public final class FileSystemUtils {
    method copyFile (line 36) | public static boolean copyFile(final File destinationDirectory, final ...
    method copyDirectoryContents (line 86) | public static boolean copyDirectoryContents(File destinationDirectory,...
    method delete (line 142) | public static boolean delete(File fileToDelete, boolean recursive) {
    method delete (line 153) | private static boolean delete(File fileToDelete, boolean recursive, in...
    method getUniqueChildNameFor (line 189) | public static String getUniqueChildNameFor(File parentDirectory, Strin...

FILE: app/src/main/java/net/osmtracker/util/GitHubUtils.java
  class GitHubUtils (line 27) | public class GitHubUtils {
    method getFileSHAAsync (line 40) | public static void getFileSHAAsync(String repoOwner, String repoName, ...
    method getGHFilenameAsync (line 86) | public static void getGHFilenameAsync(String repoOwner, String repoNam...
    method checkFileExists (line 103) | private static void checkFileExists(String repoOwner, String repoName,...

FILE: app/src/main/java/net/osmtracker/util/MercatorProjection.java
  class MercatorProjection (line 10) | public class MercatorProjection {
    method MercatorProjection (line 47) | public MercatorProjection(double minLat, double minLon, double maxLat,...
    method project (line 90) | public int[] project(double longitude, double latitude) {
    method convertLongitude (line 106) | private double convertLongitude(double longitude) {
    method convertLatitude (line 117) | private double convertLatitude(double latitude) {
    method getScale (line 127) | public double getScale() {
    method formatDegreesAsDMS (line 137) | public static String formatDegreesAsDMS(Float degrees, final boolean i...

FILE: app/src/main/java/net/osmtracker/util/ThemeValidator.java
  class ThemeValidator (line 22) | public class ThemeValidator {
    method getValidTheme (line 30) | public static String getValidTheme(SharedPreferences prefs, Resources ...

FILE: app/src/main/java/net/osmtracker/util/URLCreator.java
  class URLCreator (line 14) | public class URLCreator {
    method createMetadataDirUrl (line 34) | public static String createMetadataDirUrl(Context context) {
    method createMetadataFileURL (line 48) | public static String createMetadataFileURL(Context context, String lay...
    method createLayoutFileURL (line 67) | public static String createLayoutFileURL(Context context, String layou...
    method createIconsDirUrl (line 82) | public static String createIconsDirUrl(Context context, String layoutF...
    method createTestURL (line 102) | public static String createTestURL(String ghUsername, String repositor...
    method getGithubParams (line 117) | private static String[] getGithubParams(Context context) {

FILE: app/src/main/java/net/osmtracker/util/UserDefinedLayoutReader.java
  class UserDefinedLayoutReader (line 38) | public class UserDefinedLayoutReader {
    method UserDefinedLayoutReader (line 126) | public UserDefinedLayoutReader(UserDefinedLayout udl, Context c, Track...
    method parseLayout (line 148) | public HashMap<String, ViewGroup> parseLayout() throws XmlPullParserEx...
    method inflateLayout (line 175) | private void inflateLayout() throws IOException, XmlPullParserException {
    method inflateRow (line 232) | private void inflateRow(TableLayout layout) throws XmlPullParserExcept...
    method inflateButton (line 266) | public void inflateButton(TableRow row) {
    method findLabel (line 345) | private String findLabel(String text, Resources r) {
    class XmlSchema (line 361) | private static final class XmlSchema {

FILE: app/src/main/java/net/osmtracker/view/DisplayTrackView.java
  class DisplayTrackView (line 22) | public class DisplayTrackView extends TextView {
    class TrackPointContentObserver (line 110) | private class TrackPointContentObserver extends ContentObserver {
      method TrackPointContentObserver (line 112) | public TrackPointContentObserver(Handler handler) {
      method onChange (line 116) | @Override
    method DisplayTrackView (line 137) | public DisplayTrackView(Context context) {
    method DisplayTrackView (line 141) | public DisplayTrackView(Context context, long trackId) {
    method onSizeChanged (line 166) | @Override
    method onDetachedFromWindow (line 178) | @Override
    method onDraw (line 185) | @Override
    method drawScale (line 230) | private void drawScale(Canvas canvas) {
    method drawStatic (line 253) | private void drawStatic(Canvas canvas) {
    method populateCoords (line 262) | public void populateCoords() {
    method projectData (line 296) | public void projectData(int width, int height) {

FILE: app/src/main/java/net/osmtracker/view/TextNoteDialog.java
  class TextNoteDialog (line 20) | public class TextNoteDialog extends AlertDialog {
    method TextNoteDialog (line 65) | public TextNoteDialog(Context context, long trackId) {
    method onStart (line 130) | @Override
    method resetValues (line 189) | public void resetValues() {
    method onRestoreInstanceState (line 198) | @Override
    method onSaveInstanceState (line 213) | @Override

FILE: app/src/main/java/net/osmtracker/view/VoiceRecDialog.java
  class VoiceRecDialog (line 28) | public class VoiceRecDialog extends ProgressDialog implements OnInfoList...
    method VoiceRecDialog (line 93) | public VoiceRecDialog(Context context, long trackId) {
    method onStart (line 116) | @Override
    method onInfo (line 235) | @Override
    method onStop (line 256) | @Override
    method onKeyDown (line 279) | @Override
    method unMuteMicrophone (line 299) | private void unMuteMicrophone() {
    method getAudioFile (line 309) | public File getAudioFile() {
    method safeClose (line 338) | private void safeClose(MediaPlayer mp) {
    method safeClose (line 355) | private void safeClose(MediaRecorder mr, boolean stopIt) {

FILE: app/src/test/java/net/osmtracker/activity/ButtonsPresetsTest.java
  class ButtonsPresetsTest (line 31) | @RunWith(RobolectricTestRunner.class)
    method setUp (line 37) | @Before
    method getIsoTest (line 47) | @Test
    method testSelectLayout_UpdatesUIAndPreferences (line 67) | @Test
    method testRefreshActivity_PopulatesUIFromFilesystem (line 108) | @Test
    method setInternalState (line 161) | private void setInternalState(Object target, String fieldName, Object ...
    method getInternalState (line 172) | private Object getInternalState(Object target, String fieldName) throw...

FILE: app/src/test/java/net/osmtracker/activity/OpenStreetMapNotesUploadTest.java
  class OpenStreetMapNotesUploadTest (line 25) | @RunWith(RobolectricTestRunner.class)
    method setUp (line 31) | @Before
    method onCreate_populatesViewsCorrectly (line 53) | @Test
    method startUpload_withExistingToken_skipsAuthFlow (line 80) | @Test

FILE: app/src/test/java/net/osmtracker/activity/TrackDetailEditorTest.java
  class TrackDetailEditorTest (line 18) | @RunWith(RobolectricTestRunner.class)
    class TrackDetailEditorActivity (line 27) | public static class TrackDetailEditorActivity extends TrackDetailEditor {
      method onCreate (line 28) | @Override
    method setup (line 34) | @Before
    method testBindTrackSetsCorrectSpinnerPosition (line 46) | @Test
    method testSaveCapturesCorrectEnumValue (line 58) | @Test

FILE: app/src/test/java/net/osmtracker/data/GPXMocks.java
  class GPXMocks (line 3) | public class GPXMocks {

FILE: app/src/test/java/net/osmtracker/data/MockDataHelper.java
  class MockDataHelper (line 14) | public class MockDataHelper extends DataHelper {
    method MockDataHelper (line 16) | public MockDataHelper(Context context) {
    method setTrackExportDate (line 20) | public void setTrackExportDate(long trackId, long exportTime){
    method getTrackByStartDate (line 25) | public Track getTrackByStartDate(Date startDate) {
    method getTrackById (line 29) | public Track getTrackById(long trackId) {
    method getWayPointIdsOfTrack (line 33) | public List<Integer> getWayPointIdsOfTrack(long trackId) {
    method getWayPointById (line 42) | public WayPoint getWayPointById(Integer wayPointId) {
    method getTrackPointIdsOfTrack (line 46) | public List<Integer> getTrackPointIdsOfTrack(long trackId) {
    method getTrackPointById (line 55) | public TrackPoint getTrackPointById(Integer trackPointId) {

FILE: app/src/test/java/net/osmtracker/data/TrackMocks.java
  class TrackMocks (line 5) | public class TrackMocks {
    method getMockTrackForGPX (line 16) | public static Track getMockTrackForGPX() {
    method createMockTrack (line 40) | public static Track createMockTrack(String trackName, long trackStartD...

FILE: app/src/test/java/net/osmtracker/data/TrackPointMocks.java
  class TrackPointMocks (line 5) | public class TrackPointMocks {
    method getMockTrackPointForXML (line 93) | public static TrackPoint getMockTrackPointForXML(){
    method getMockTrackPointForGPX (line 115) | public static TrackPoint getMockTrackPointForGPX(Integer trkptId) {

FILE: app/src/test/java/net/osmtracker/data/WayPointMocks.java
  class WayPointMocks (line 6) | public class WayPointMocks {
    method getMockWayPointForXML (line 253) | public static WayPoint getMockWayPointForXML(){
    method getMockWayPointForGPX (line 275) | public static WayPoint getMockWayPointForGPX(Integer wptId) {

FILE: app/src/test/java/net/osmtracker/db/DataHelperNoteTest.java
  class DataHelperNoteTest (line 18) | @RunWith(RobolectricTestRunner.class)
    method setup (line 24) | @Before
    method testDeleteNote_RemovesFromDatabase (line 30) | @Test
    method noteExists (line 56) | private boolean noteExists(long TrackId) {

FILE: app/src/test/java/net/osmtracker/db/model/OSMVisibilityTest.java
  class OSMVisibilityTest (line 17) | @RunWith(RobolectricTestRunner.class)
    method setUp (line 24) | @Before
    method testEnumMappingToName (line 30) | @Test
    method testFromPosition (line 39) | @Test
    method testResourceIdsMapToCorrectStrings (line 48) | @Test
    method testFromPosition_Invalid (line 60) | @Test(expected = IllegalArgumentException.class)

FILE: app/src/test/java/net/osmtracker/db/model/TrackTest.java
  class TrackTest (line 21) | @RunWith(RobolectricTestRunner.class)
    method initMockCursor (line 34) | public Cursor initMockCursor(){
    method testBuild (line 63) | @Test

FILE: app/src/test/java/net/osmtracker/gpx/ExportToStorageTaskTest.java
  class ExportToStorageTaskTest (line 38) | @RunWith(RobolectricTestRunner.class)
    method setUp (line 51) | @Before
    method testBuildGPXFilename_OnlyTrackName (line 63) | @Test
    method testBuildGPXFilename_TrackNameAndDate (line 70) | @Test
    method testBuildGPXFilename_DateAndTrackName (line 77) | @Test
    method testBuildGPXFilename_OnlyDate (line 84) | @Test
    method testBuildGPXFilename_Sanitization (line 91) | @Test
    method testBuildGPXFilename_FallbackToDateWhenNameEmpty (line 99) | @Test
    method testGetExportDirectory_CreatesMissingFolders (line 110) | @Test
    method testGetExportDirectory_ThrowsWhenNotWritable (line 121) | @Test
    method testGetSanitizedTrackName_ReplacesSlashes (line 127) | @Test
    method setupFilenamePreference (line 139) | private void setupFilenamePreference(String format) {
    method executeBuildFilename (line 147) | private String executeBuildFilename(String name, Date date) {
    method createMockCursor (line 151) | private Cursor createMockCursor(String trackName, long trackStartDate) {
    method createDate (line 163) | private static Date createDate() {

FILE: app/src/test/java/net/osmtracker/layout/DownloadCustomLayoutTaskTest.java
  class DownloadCustomLayoutTaskTest (line 24) | @RunWith(RobolectricTestRunner.class)
    method setUp (line 36) | @Before
    method downloadLayoutWithoutIconsTest (line 54) | @Test

FILE: app/src/test/java/net/osmtracker/layout/URLValidatorTaskTest.java
  class URLValidatorTaskTest (line 9) | public class URLValidatorTaskTest {
    method customLayoutsRepoValidatorTest (line 13) | @Test

FILE: app/src/test/java/net/osmtracker/util/ArrayUtilsTest.java
  class ArrayUtilsTest (line 7) | public class ArrayUtilsTest {
    method findMinAsc (line 13) | @Test
    method findMinDesc (line 19) | @Test
    method findMaxAsc (line 25) | @Test
    method findMaxDesc (line 31) | @Test

FILE: app/src/test/java/net/osmtracker/util/CustomLayoutsUtilsTest.java
  class CustomLayoutsUtilsTest (line 24) | @RunWith(RobolectricTestRunner.class)
    method setUp (line 31) | @Before
    method convertFileName (line 39) | @Test
    method unconvertFileName (line 45) | @Test
    method createFileName (line 50) | @Test
    method getStringFromStream (line 55) | @Test
    method getCurrentLayoutName (line 63) | @Test
    method getCurrentLayoutName_ReturnsDefaultWhenEmpty (line 71) | @Test

FILE: app/src/test/java/net/osmtracker/util/FileSystemUtilsTest.java
  class FileSystemUtilsTest (line 14) | public class FileSystemUtilsTest {
    method setUp (line 24) | @Before
    method tearDown (line 39) | @After
    method testCopyFileSuccess (line 44) | @Test
    method testCopyFileFailure (line 50) | @Test
    method testCopyDirectoryContentsSuccess (line 57) | @Test
    method testCopyDirectoryContentsFailure (line 67) | @Test
    method testCopyDirectoryContentsDestinationNull (line 74) | @Test
    method testCopyDirectoryContentsSourceNull (line 80) | @Test
    method testDeleteFileSuccess (line 86) | @Test
    method testDeleteDirectorySuccess (line 92) | @Test
    method testDeleteDirectoryFailure (line 98) | @Test
    method testGetUniqueChildNameFor (line 110) | @Test

FILE: app/src/test/java/net/osmtracker/util/MercatorProjectionTest.java
  class MercatorProjectionTest (line 11) | @RunWith(Parameterized.class)
    method data (line 50) | @Parameterized.Parameters
    method testProject (line 61) | @Test
    method testFormatDegreesAsDMS (line 71) | @Test

FILE: app/src/test/java/net/osmtracker/util/ThemeValidatorTest.java
  class ThemeValidatorTest (line 23) | @RunWith(RobolectricTestRunner.class)
    method setUp (line 30) | @Before
    method validateDefaultTheme (line 48) | @Test
    method validateWrongTheme (line 63) | @Test

FILE: app/src/test/java/net/osmtracker/util/URLCreatorTest.java
  class URLCreatorTest (line 15) | @RunWith(RobolectricTestRunner.class)
    method setUp (line 21) | @Before
    method createMetadataDirUrl (line 26) | @Test
    method createMetadataFileURL (line 33) | @Test
    method createLayoutFileURL (line 40) | @Test
    method createIconsDirUrl (line 48) | @Test
    method createTestURL (line 55) | @Test
Condensed preview — 462 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,138K chars).
[
  {
    "path": ".github/FUNDING.yml",
    "chars": 87,
    "preview": "# These are supported funding model platforms\n# github: [labexp]\nliberapay: OSMTracker\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "chars": 2601,
    "preview": "name: \"Bug Report\"\ndescription: \"Report an issue or bug with OSMTracker for Android.\"\nlabels: \"bug\"\nbody:\n  - type: mark"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 256,
    "preview": "blank_issues_enabled: true\ncontact_links:\n  - name: \"Questions about OSMTracker\"\n    url: \"https://github.com/labexp/osm"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/documentation_report.yml",
    "chars": 2522,
    "preview": "name: \"Documentation Report\"\ndescription: \"Report errors or suggest improvements for project documentation.\"\nlabels: \"do"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yml",
    "chars": 2397,
    "preview": "name: \"Feature Request\"\ndescription: \"Suggest an enhancement or new feature for OSMTracker for Android.\"\nlabels: \"enhanc"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 1436,
    "preview": "[comment]: # (Thank you for your contribution! Please fill out the following details to help us review your pull request"
  },
  {
    "path": ".github/workflows/android.yml",
    "chars": 1934,
    "preview": "name: Build\n\non:\n  push:\n    branches:\n      - master\n      - develop\n  pull_request:\n    branches:\n      - develop\n\njob"
  },
  {
    "path": ".github/workflows/nightly.yml",
    "chars": 4367,
    "preview": "name: Nightly APK\n\non:\n  schedule: # Scheduled jobs only run on the default repository branch\n    - cron: \"0 1 * * *\"\n  "
  },
  {
    "path": ".gitignore",
    "chars": 255,
    "preview": "build\n.idea\n*.iml\n.gradle\ntrash\nlocal.properties\nproguard-project.txt\n# -no resources are copied from -nb\napp/src/main/r"
  },
  {
    "path": ".travis.yml",
    "chars": 615,
    "preview": "sudo: true\n\nlanguage: android\njdk: oraclejdk8\n\nandroid:\n    components:\n        - build-tools-29.0.2\n        - android-2"
  },
  {
    "path": ".tx/config",
    "chars": 1151,
    "preview": "[main]\nhost = https://www.transifex.com\nlang_map = cs_CZ: cs-rCZ, pt_PT: pt-rPT, zh_CN: zh-rCN, zh_TW: zh-rTW, pt_BR: pt"
  },
  {
    "path": "AUTHORS",
    "chars": 413,
    "preview": "Contributors\n------------\n\nNicolas Guillaumin <nguillaumin+osmtracker at googlemail.com>\n - Initial author.\n \nViesturs Z"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 4708,
    "preview": "\n# 🤝 Contributing to OSMTracker\n\nThank you for your interest in contributing! 🎉  \nWhether you're fixing a bug, adding a "
  },
  {
    "path": "COPYING",
    "chars": 35147,
    "preview": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free "
  },
  {
    "path": "LICENSES",
    "chars": 8932,
    "preview": "==== File lib/osmdroid-android-*.jar ====\r\n\r\n\tosmtracker-android reuses code from osmdroid (http://osmdroid.googlecode.c"
  },
  {
    "path": "README.md",
    "chars": 2433,
    "preview": "# OSMTracker for Android™\n\n![Build](https://github.com/labexp/osmtracker-android/actions/workflows/android.yml/badge.svg"
  },
  {
    "path": "app/build.gradle",
    "chars": 4461,
    "preview": "plugins {\n    id 'com.android.application'\n    id 'kotlin-android'\n    id 'jacoco'\n}\n\nandroid {\n    namespace 'net.osmtr"
  },
  {
    "path": "app/jacoco.gradle",
    "chars": 852,
    "preview": "jacoco {\n    toolVersion = jacocoVersion\n}\n\ntasks.withType(Test).configureEach {\n    jacoco.includeNoLocationClasses = t"
  },
  {
    "path": "app/lint.xml",
    "chars": 267,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<lint>\n    <issue id=\"ExtraTranslation\" severity=\"ignore\" />\n    <issue id=\"Hardc"
  },
  {
    "path": "app/src/androidTest/assets/gpx/gpx-test.gpx",
    "chars": 1528,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<gpx xmlns=\"http://www.topografix.com/GPX/1/1\" version=\"1.1\" creator=\"OSMTracker"
  },
  {
    "path": "app/src/androidTest/java/net/osmtracker/activity/PreferencesTest.java",
    "chars": 7285,
    "preview": "package net.osmtracker.activity;\n\nimport static androidx.test.espresso.Espresso.onView;\nimport static androidx.test.espr"
  },
  {
    "path": "app/src/androidTest/java/net/osmtracker/data/Mocks.java",
    "chars": 489,
    "preview": "package net.osmtracker.data;\n\npublic class Mocks {\n    public static String MOCK_LAYOUT_CONTENT =\n            \"<layouts>"
  },
  {
    "path": "app/src/androidTest/java/net/osmtracker/layouts/DeleteLayoutTest.java",
    "chars": 3469,
    "preview": "package net.osmtracker.layouts;\n\nimport static androidx.test.espresso.Espresso.onView;\nimport static androidx.test.espre"
  },
  {
    "path": "app/src/androidTest/java/net/osmtracker/layouts/DownloadLayoutTest.java",
    "chars": 5127,
    "preview": "package net.osmtracker.layouts;\n\nimport static androidx.test.espresso.Espresso.onView;\nimport static androidx.test.espre"
  },
  {
    "path": "app/src/androidTest/java/net/osmtracker/layouts/RepositorySettingsDialogTest.java",
    "chars": 5010,
    "preview": "package net.osmtracker.layouts;\n\nimport static androidx.test.espresso.Espresso.onView;\nimport static androidx.test.espre"
  },
  {
    "path": "app/src/androidTest/java/net/osmtracker/util/LogcatHelper.java",
    "chars": 917,
    "preview": "package net.osmtracker.util;\n\nimport android.util.Log;\nimport java.io.BufferedReader;\nimport java.io.InputStreamReader;\n"
  },
  {
    "path": "app/src/androidTest/java/net/osmtracker/util/TestUtils.java",
    "chars": 5012,
    "preview": "package net.osmtracker.util;\n\nimport static net.osmtracker.util.LogcatHelper.checkLogForMessage;\n\nimport android.content"
  },
  {
    "path": "app/src/androidTest/java/net/osmtracker/util/ToastMatcher.java",
    "chars": 1030,
    "preview": "package net.osmtracker.util;\n\nimport android.os.IBinder;\nimport androidx.test.espresso.Root;\nimport android.view.WindowM"
  },
  {
    "path": "app/src/androidTest/java/net/osmtracker/util/WaitForView.java",
    "chars": 2197,
    "preview": "package net.osmtracker.util;\n\nimport android.view.View;\nimport androidx.test.espresso.PerformException;\nimport androidx."
  },
  {
    "path": "app/src/androidTest/res/values/strings.xml",
    "chars": 122,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <string name=\"app_name\">OSMTrackerTestsTest</string>\n\n</resource"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "chars": 6192,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:to"
  },
  {
    "path": "app/src/main/java/net/osmtracker/GitHubUser.java",
    "chars": 1062,
    "preview": "package net.osmtracker;\n\nimport net.osmtracker.github.*;\n\nimport android.content.Context;\nimport android.content.SharedP"
  },
  {
    "path": "app/src/main/java/net/osmtracker/OSMTracker.java",
    "chars": 8097,
    "preview": "package net.osmtracker;\n\n\n/**\n * Constants & app-wide variables.\n * \n * @author Nicolas Guillaumin\n * \n */\npublic class "
  },
  {
    "path": "app/src/main/java/net/osmtracker/activity/About.java",
    "chars": 4753,
    "preview": "package net.osmtracker.activity;\n\nimport net.osmtracker.OSMTracker;\nimport net.osmtracker.R;\nimport net.osmtracker.db.Da"
  },
  {
    "path": "app/src/main/java/net/osmtracker/activity/AvailableLayouts.java",
    "chars": 25624,
    "preview": "package net.osmtracker.activity;\n\nimport android.annotation.SuppressLint;\nimport android.annotation.TargetApi;\nimport an"
  },
  {
    "path": "app/src/main/java/net/osmtracker/activity/ButtonsPresets.java",
    "chars": 16337,
    "preview": "package net.osmtracker.activity;\n\nimport android.Manifest;\nimport android.annotation.SuppressLint;\nimport android.app.Ac"
  },
  {
    "path": "app/src/main/java/net/osmtracker/activity/DisplayTrack.java",
    "chars": 3159,
    "preview": "package net.osmtracker.activity;\r\n\r\nimport net.osmtracker.OSMTracker;\r\nimport net.osmtracker.util.ThemeValidator;\r\nimpor"
  },
  {
    "path": "app/src/main/java/net/osmtracker/activity/DisplayTrackMap.java",
    "chars": 16510,
    "preview": "package net.osmtracker.activity;\n\nimport android.app.Activity;\nimport android.content.ContentUris;\nimport android.conten"
  },
  {
    "path": "app/src/main/java/net/osmtracker/activity/GitHubConfig.java",
    "chars": 3306,
    "preview": "package net.osmtracker.activity;\n\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.content.pm."
  },
  {
    "path": "app/src/main/java/net/osmtracker/activity/GitHubNewFork.java",
    "chars": 4791,
    "preview": "package net.osmtracker.activity;\n\nimport android.app.Activity;\nimport android.content.pm.ActivityInfo;\nimport android.os"
  },
  {
    "path": "app/src/main/java/net/osmtracker/activity/GitHubNewRepo.java",
    "chars": 5512,
    "preview": "package net.osmtracker.activity;\n\nimport android.app.Activity;\nimport android.app.ProgressDialog;\nimport android.content"
  },
  {
    "path": "app/src/main/java/net/osmtracker/activity/GitHubPullRequest.java",
    "chars": 8565,
    "preview": "package net.osmtracker.activity;\n\nimport android.app.Activity;\nimport android.content.pm.ActivityInfo;\nimport android.os"
  },
  {
    "path": "app/src/main/java/net/osmtracker/activity/GitHubUpload.java",
    "chars": 14337,
    "preview": "package net.osmtracker.activity;\n\nimport android.app.Activity;\nimport android.app.ProgressDialog;\nimport android.content"
  },
  {
    "path": "app/src/main/java/net/osmtracker/activity/Intro.kt",
    "chars": 2965,
    "preview": "package net.osmtracker.activity\n\nimport android.os.Bundle\nimport androidx.activity.enableEdgeToEdge\nimport androidx.core"
  },
  {
    "path": "app/src/main/java/net/osmtracker/activity/NoteList.java",
    "chars": 4990,
    "preview": "package net.osmtracker.activity;\n\nimport android.content.Intent;\nimport android.content.pm.PackageInfo;\nimport android.c"
  },
  {
    "path": "app/src/main/java/net/osmtracker/activity/OpenStreetMapNotesUpload.java",
    "chars": 7587,
    "preview": "package net.osmtracker.activity;\n\nimport android.content.Intent;\nimport android.content.SharedPreferences;\nimport androi"
  },
  {
    "path": "app/src/main/java/net/osmtracker/activity/OpenStreetMapUpload.java",
    "chars": 7622,
    "preview": "package net.osmtracker.activity;\n\nimport android.content.ContentUris;\nimport android.content.Intent;\nimport android.cont"
  },
  {
    "path": "app/src/main/java/net/osmtracker/activity/Preferences.java",
    "chars": 11986,
    "preview": "package net.osmtracker.activity;\n\nimport android.content.Intent;\nimport android.content.SharedPreferences;\nimport androi"
  },
  {
    "path": "app/src/main/java/net/osmtracker/activity/TrackDetail.java",
    "chars": 14797,
    "preview": "package net.osmtracker.activity;\n\nimport java.sql.Date;\nimport java.text.DateFormat;\nimport java.util.ArrayList;\nimport "
  },
  {
    "path": "app/src/main/java/net/osmtracker/activity/TrackDetailEditor.java",
    "chars": 4058,
    "preview": "package net.osmtracker.activity;\n\nimport android.app.Activity;\nimport android.content.ContentUris;\nimport android.conten"
  },
  {
    "path": "app/src/main/java/net/osmtracker/activity/TrackListRVAdapter.java",
    "chars": 4639,
    "preview": "package net.osmtracker.activity;\n\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.view.Co"
  },
  {
    "path": "app/src/main/java/net/osmtracker/activity/TrackLogger.java",
    "chars": 26471,
    "preview": "package net.osmtracker.activity;\n\nimport android.Manifest;\nimport android.app.Activity;\nimport android.app.AlertDialog;\n"
  },
  {
    "path": "app/src/main/java/net/osmtracker/activity/TrackManager.java",
    "chars": 33119,
    "preview": "package net.osmtracker.activity;\r\n\r\nimport androidx.appcompat.app.AppCompatActivity;\r\nimport androidx.appcompat.widget.T"
  },
  {
    "path": "app/src/main/java/net/osmtracker/activity/WaypointList.java",
    "chars": 7815,
    "preview": "package net.osmtracker.activity;\n\nimport android.app.AlertDialog;\nimport android.app.ListActivity;\nimport android.conten"
  },
  {
    "path": "app/src/main/java/net/osmtracker/adapter/NoteAdapter.java",
    "chars": 3827,
    "preview": "package net.osmtracker.adapter;\n\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.view.Lay"
  },
  {
    "path": "app/src/main/java/net/osmtracker/db/DataHelper.java",
    "chars": 21495,
    "preview": "package net.osmtracker.db;\n\nimport android.content.ContentResolver;\nimport android.content.ContentUris;\nimport android.c"
  },
  {
    "path": "app/src/main/java/net/osmtracker/db/DatabaseHelper.java",
    "chars": 12465,
    "preview": "package net.osmtracker.db;\r\n\r\nimport java.io.File;\r\nimport java.io.FilenameFilter;\r\n\r\nimport net.osmtracker.OSMTracker;\r"
  },
  {
    "path": "app/src/main/java/net/osmtracker/db/ExportDatabaseTask.java",
    "chars": 2920,
    "preview": "package net.osmtracker.db;\n\nimport android.os.AsyncTask;\nimport android.os.Bundle;\n\nimport java.io.File;\nimport java.io."
  },
  {
    "path": "app/src/main/java/net/osmtracker/db/TrackContentProvider.java",
    "chars": 22881,
    "preview": "package net.osmtracker.db;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport net.osmtracker.OSMTracker;\n\nimpor"
  },
  {
    "path": "app/src/main/java/net/osmtracker/db/TracklistAdapter.java",
    "chars": 3537,
    "preview": "package net.osmtracker.db;\r\n\r\nimport net.osmtracker.R;\r\nimport net.osmtracker.db.model.Track;\r\nimport net.osmtracker.act"
  },
  {
    "path": "app/src/main/java/net/osmtracker/db/WaypointListAdapter.java",
    "chars": 3809,
    "preview": "package net.osmtracker.db;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.TimeZone;\n\nimport"
  },
  {
    "path": "app/src/main/java/net/osmtracker/db/model/Point.java",
    "chars": 3838,
    "preview": "package net.osmtracker.db.model;\n\nimport android.database.Cursor;\n\nimport net.osmtracker.db.TrackContentProvider;\n\n/**\n "
  },
  {
    "path": "app/src/main/java/net/osmtracker/db/model/Track.java",
    "chars": 7093,
    "preview": "package net.osmtracker.db.model;\n\nimport java.text.DateFormat;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimpo"
  },
  {
    "path": "app/src/main/java/net/osmtracker/db/model/TrackPoint.java",
    "chars": 635,
    "preview": "package net.osmtracker.db.model;\n\nimport android.database.Cursor;\n\nimport net.osmtracker.db.TrackContentProvider;\n\n/**\n "
  },
  {
    "path": "app/src/main/java/net/osmtracker/db/model/WayPoint.java",
    "chars": 1271,
    "preview": "package net.osmtracker.db.model;\n\nimport android.database.Cursor;\n\nimport net.osmtracker.db.TrackContentProvider;\n\n/**\n "
  },
  {
    "path": "app/src/main/java/net/osmtracker/exception/CreateTrackException.java",
    "chars": 224,
    "preview": "package net.osmtracker.exception;\r\n\r\npublic class CreateTrackException extends Exception {\r\n\r\n\tprivate static final long"
  },
  {
    "path": "app/src/main/java/net/osmtracker/exception/ExportTrackException.java",
    "chars": 224,
    "preview": "package net.osmtracker.exception;\r\n\r\npublic class ExportTrackException extends Exception {\r\n\r\n\tprivate static final long"
  },
  {
    "path": "app/src/main/java/net/osmtracker/github/GitHubConstants.java",
    "chars": 2473,
    "preview": "package net.osmtracker.github;\n\npublic final class GitHubConstants {\n    // Config constans\n    public static final Stri"
  },
  {
    "path": "app/src/main/java/net/osmtracker/gpx/ExportToStorageTask.java",
    "chars": 5560,
    "preview": "package net.osmtracker.gpx;\n\nimport static net.osmtracker.util.FileSystemUtils.getUniqueChildNameFor;\n\nimport android.co"
  },
  {
    "path": "app/src/main/java/net/osmtracker/gpx/ExportToTempFileTask.java",
    "chars": 2358,
    "preview": "package net.osmtracker.gpx;\n\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.util.Log;\n\ni"
  },
  {
    "path": "app/src/main/java/net/osmtracker/gpx/ExportTrackTask.java",
    "chars": 24856,
    "preview": "package net.osmtracker.gpx;\n\nimport android.app.AlertDialog;\nimport android.app.ProgressDialog;\nimport android.content.C"
  },
  {
    "path": "app/src/main/java/net/osmtracker/gpx/ZipHelper.java",
    "chars": 3055,
    "preview": "package net.osmtracker.gpx;\n\nimport android.content.Context;\nimport android.util.Log;\n\nimport java.io.File;\nimport java."
  },
  {
    "path": "app/src/main/java/net/osmtracker/layout/DisablableTableLayout.java",
    "chars": 1509,
    "preview": "package net.osmtracker.layout;\n\nimport android.content.Context;\nimport android.view.View;\nimport android.view.ViewGroup;"
  },
  {
    "path": "app/src/main/java/net/osmtracker/layout/DownloadCustomLayoutTask.java",
    "chars": 6087,
    "preview": "package net.osmtracker.layout;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport android"
  },
  {
    "path": "app/src/main/java/net/osmtracker/layout/GetStringResponseTask.java",
    "chars": 1070,
    "preview": "package net.osmtracker.layout;\n\nimport android.os.AsyncTask;\nimport android.util.Log;\n\nimport net.osmtracker.util.Custom"
  },
  {
    "path": "app/src/main/java/net/osmtracker/layout/GpsStatusRecord.java",
    "chars": 8867,
    "preview": "package net.osmtracker.layout;\n\nimport java.text.DecimalFormat;\n\nimport net.osmtracker.OSMTracker;\nimport net.osmtracker"
  },
  {
    "path": "app/src/main/java/net/osmtracker/layout/TLSSocketFactory.java",
    "chars": 2561,
    "preview": "package net.osmtracker.layout;\n\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.net.Socket;\nimport "
  },
  {
    "path": "app/src/main/java/net/osmtracker/layout/URLValidatorTask.java",
    "chars": 2203,
    "preview": "package net.osmtracker.layout;\n\nimport android.os.AsyncTask;\nimport android.util.Log;\n\nimport java.net.HttpURLConnection"
  },
  {
    "path": "app/src/main/java/net/osmtracker/layout/UserDefinedLayout.java",
    "chars": 3777,
    "preview": "package net.osmtracker.layout;\r\n\r\nimport java.io.File;\r\nimport java.io.FileReader;\r\nimport java.io.IOException;\r\nimport "
  },
  {
    "path": "app/src/main/java/net/osmtracker/listener/EditWaypointDialogOnClickListener.java",
    "chars": 633,
    "preview": "package net.osmtracker.listener;\n\nimport android.app.AlertDialog;\nimport android.database.Cursor;\nimport android.view.Vi"
  },
  {
    "path": "app/src/main/java/net/osmtracker/listener/PageButtonOnClickListener.java",
    "chars": 805,
    "preview": "package net.osmtracker.listener;\r\n\r\nimport net.osmtracker.layout.UserDefinedLayout;\r\nimport android.view.View;\r\nimport a"
  },
  {
    "path": "app/src/main/java/net/osmtracker/listener/PressureListener.java",
    "chars": 2098,
    "preview": "package net.osmtracker.listener;\n\nimport android.content.Context;\nimport android.hardware.Sensor;\nimport android.hardwar"
  },
  {
    "path": "app/src/main/java/net/osmtracker/listener/SensorListener.java",
    "chars": 8023,
    "preview": "package net.osmtracker.listener;\n\nimport java.text.DecimalFormat;\n\nimport net.osmtracker.R;\nimport net.osmtracker.db.Dat"
  },
  {
    "path": "app/src/main/java/net/osmtracker/listener/StillImageOnClickListener.java",
    "chars": 528,
    "preview": "package net.osmtracker.listener;\n\nimport net.osmtracker.activity.TrackLogger;\n\nimport android.view.View;\nimport android."
  },
  {
    "path": "app/src/main/java/net/osmtracker/listener/TagButtonOnClickListener.java",
    "chars": 1555,
    "preview": "package net.osmtracker.listener;\r\n\r\nimport android.content.Intent;\r\nimport android.view.View;\r\nimport android.view.View."
  },
  {
    "path": "app/src/main/java/net/osmtracker/listener/TextNoteOnClickListener.java",
    "chars": 594,
    "preview": "package net.osmtracker.listener;\r\n\r\nimport net.osmtracker.activity.TrackLogger;\r\n\r\nimport android.view.View;\r\nimport and"
  },
  {
    "path": "app/src/main/java/net/osmtracker/listener/VoiceRecOnClickListener.java",
    "chars": 529,
    "preview": "package net.osmtracker.listener;\n\nimport net.osmtracker.activity.TrackLogger;\n\nimport android.view.View;\nimport android."
  },
  {
    "path": "app/src/main/java/net/osmtracker/osm/OpenStreetMapConstants.java",
    "chars": 1148,
    "preview": "package net.osmtracker.osm;\n\npublic class OpenStreetMapConstants {\n\n\tprivate static final boolean DEV_MODE = false;\n\tpri"
  },
  {
    "path": "app/src/main/java/net/osmtracker/osm/UploadToOpenStreetMapNotesTask.java",
    "chars": 5484,
    "preview": "package net.osmtracker.osm;\n\nimport android.app.Activity;\nimport android.util.Log;\nimport android.view.Gravity;\nimport a"
  },
  {
    "path": "app/src/main/java/net/osmtracker/osm/UploadToOpenStreetMapTask.java",
    "chars": 6592,
    "preview": "package net.osmtracker.osm;\n\nimport android.app.Activity;\nimport android.app.AlertDialog;\nimport android.app.ProgressDia"
  },
  {
    "path": "app/src/main/java/net/osmtracker/overlay/WayPointsOverlay.java",
    "chars": 2241,
    "preview": "package net.osmtracker.overlay;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport net.osmtracker.R;\nimport net"
  },
  {
    "path": "app/src/main/java/net/osmtracker/receiver/MediaButtonReceiver.java",
    "chars": 556,
    "preview": "package net.osmtracker.receiver;\n\nimport net.osmtracker.activity.TrackLogger;\n\nimport android.content.BroadcastReceiver;"
  },
  {
    "path": "app/src/main/java/net/osmtracker/service/gps/GPSLogger.java",
    "chars": 14266,
    "preview": "package net.osmtracker.service.gps;\n\nimport android.Manifest;\nimport android.app.Notification;\nimport android.app.Notifi"
  },
  {
    "path": "app/src/main/java/net/osmtracker/service/gps/GPSLoggerServiceConnection.java",
    "chars": 1667,
    "preview": "package net.osmtracker.service.gps;\n\nimport net.osmtracker.OSMTracker;\nimport net.osmtracker.R;\nimport net.osmtracker.ac"
  },
  {
    "path": "app/src/main/java/net/osmtracker/service/resources/AppResourceIconResolver.java",
    "chars": 992,
    "preview": "package net.osmtracker.service.resources;\r\n\r\nimport android.content.res.Resources;\r\nimport android.graphics.drawable.Dra"
  },
  {
    "path": "app/src/main/java/net/osmtracker/service/resources/ExternalDirectoryIconResolver.java",
    "chars": 1182,
    "preview": "package net.osmtracker.service.resources;\r\n\r\nimport java.io.File;\r\n\r\nimport android.graphics.Bitmap;\r\nimport android.gra"
  },
  {
    "path": "app/src/main/java/net/osmtracker/service/resources/IconResolver.java",
    "chars": 348,
    "preview": "package net.osmtracker.service.resources;\r\n\r\nimport android.graphics.drawable.Drawable;\r\n\r\n/**\r\n * Resolver for finding "
  },
  {
    "path": "app/src/main/java/net/osmtracker/util/ArrayUtils.java",
    "chars": 1021,
    "preview": "package net.osmtracker.util;\r\n\r\n/**\r\n * Array utilities.\r\n * \r\n * @author Nicolas Guillaumin\r\n */\r\npublic final class Ar"
  },
  {
    "path": "app/src/main/java/net/osmtracker/util/Callback.java",
    "chars": 610,
    "preview": "package net.osmtracker.util;\n\n/**\n * A generic callback interface used for asynchronous operations.\n * Implementations o"
  },
  {
    "path": "app/src/main/java/net/osmtracker/util/CustomLayoutsUtils.java",
    "chars": 3983,
    "preview": "package net.osmtracker.util;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\n\nimport androidx"
  },
  {
    "path": "app/src/main/java/net/osmtracker/util/DialogUtils.java",
    "chars": 1360,
    "preview": "package net.osmtracker.util;\n\nimport android.app.AlertDialog;\nimport android.content.Context;\nimport android.content.Dia"
  },
  {
    "path": "app/src/main/java/net/osmtracker/util/FileSystemUtils.java",
    "chars": 7522,
    "preview": "package net.osmtracker.util;\n\nimport android.util.Log;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java"
  },
  {
    "path": "app/src/main/java/net/osmtracker/util/GitHubUtils.java",
    "chars": 5646,
    "preview": "package net.osmtracker.util;\n\nimport android.os.AsyncTask;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\n"
  },
  {
    "path": "app/src/main/java/net/osmtracker/util/MercatorProjection.java",
    "chars": 4319,
    "preview": "package net.osmtracker.util;\r\n\r\n\r\n/**\r\n * Geopoint to 2D projection using Mercator system.\r\n * \r\n * @author Nicolas Guil"
  },
  {
    "path": "app/src/main/java/net/osmtracker/util/ThemeValidator.java",
    "chars": 1229,
    "preview": "package net.osmtracker.util;\n\nimport java.util.Arrays;\n\nimport net.osmtracker.OSMTracker;\nimport net.osmtracker.R;\n\nimpo"
  },
  {
    "path": "app/src/main/java/net/osmtracker/util/URLCreator.java",
    "chars": 5281,
    "preview": "package net.osmtracker.util;\n\nimport android.content.Context;\nimport android.content.SharedPreferences;\n\nimport androidx"
  },
  {
    "path": "app/src/main/java/net/osmtracker/util/UserDefinedLayoutReader.java",
    "chars": 12555,
    "preview": "package net.osmtracker.util;\n\nimport java.io.IOException;\nimport java.util.HashMap;\n\nimport net.osmtracker.OSMTracker;\ni"
  },
  {
    "path": "app/src/main/java/net/osmtracker/view/DisplayTrackView.java",
    "chars": 10118,
    "preview": "package net.osmtracker.view;\r\n\r\nimport java.text.DecimalFormat;\r\n\r\nimport net.osmtracker.R;\r\nimport net.osmtracker.db.Tr"
  },
  {
    "path": "app/src/main/java/net/osmtracker/view/TextNoteDialog.java",
    "chars": 6659,
    "preview": "package net.osmtracker.view;\n\nimport android.app.AlertDialog;\nimport android.content.Context;\nimport android.content.Dia"
  },
  {
    "path": "app/src/main/java/net/osmtracker/view/VoiceRecDialog.java",
    "chars": 10973,
    "preview": "package net.osmtracker.view;\n\nimport android.app.ProgressDialog;\nimport android.content.Context;\nimport android.content."
  },
  {
    "path": "app/src/main/res/drawable/divider.xml",
    "chars": 183,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <size andr"
  },
  {
    "path": "app/src/main/res/drawable/ic_fab_add_track.xml",
    "chars": 439,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n  "
  },
  {
    "path": "app/src/main/res/drawable/map_btn_style.xml",
    "chars": 387,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ripple xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:co"
  },
  {
    "path": "app/src/main/res/drawable-mdpi/theme_highcontrast_btn.xml",
    "chars": 889,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\t<item and"
  },
  {
    "path": "app/src/main/res/layout/about.xml",
    "chars": 3935,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    an"
  },
  {
    "path": "app/src/main/res/layout/available_layouts.xml",
    "chars": 457,
    "preview": "<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    andro"
  },
  {
    "path": "app/src/main/res/layout/buttons_presets.xml",
    "chars": 1880,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmln"
  },
  {
    "path": "app/src/main/res/layout/displaytrackmap.xml",
    "chars": 2789,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    an"
  },
  {
    "path": "app/src/main/res/layout/edit_note_dialog.xml",
    "chars": 3471,
    "preview": "<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    andro"
  },
  {
    "path": "app/src/main/res/layout/edit_waypoint_dialog.xml",
    "chars": 3406,
    "preview": "<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    andro"
  },
  {
    "path": "app/src/main/res/layout/git_configuration_fields.xml",
    "chars": 6449,
    "preview": "<merge xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <EditText\n        android:id=\"@+id/git_configurat"
  },
  {
    "path": "app/src/main/res/layout/git_create_fork.xml",
    "chars": 1446,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    an"
  },
  {
    "path": "app/src/main/res/layout/git_create_fork_fields.xml",
    "chars": 683,
    "preview": "<merge xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <EditText\n        android:id=\"@+id/git_username_"
  },
  {
    "path": "app/src/main/res/layout/git_create_pullrequest.xml",
    "chars": 1457,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    an"
  },
  {
    "path": "app/src/main/res/layout/git_create_pullrequest_fields.xml",
    "chars": 794,
    "preview": "<merge xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <EditText\n        android:id=\"@+id/git_title_pul"
  },
  {
    "path": "app/src/main/res/layout/git_newrepo.xml",
    "chars": 1442,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    an"
  },
  {
    "path": "app/src/main/res/layout/git_newrepo_fields.xml",
    "chars": 701,
    "preview": "<merge xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <EditText\n        android:id=\"@+id/git_newrepo_n"
  },
  {
    "path": "app/src/main/res/layout/git_trackdetail_fields.xml",
    "chars": 1874,
    "preview": "<merge xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <EditText\n        android:id=\"@+id/git_trackdeta"
  },
  {
    "path": "app/src/main/res/layout/github_configuration_token.xml",
    "chars": 1749,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    an"
  },
  {
    "path": "app/src/main/res/layout/github_repository_settings.xml",
    "chars": 3275,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    andr"
  },
  {
    "path": "app/src/main/res/layout/gpsstatus_record.xml",
    "chars": 1665,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    andr"
  },
  {
    "path": "app/src/main/res/layout/notelist.xml",
    "chars": 306,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.recyclerview.widget.RecyclerView\n    xmlns:android=\"http://schemas.andr"
  },
  {
    "path": "app/src/main/res/layout/notelist_item.xml",
    "chars": 2333,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout\n    xmlns:android=\"http://sche"
  },
  {
    "path": "app/src/main/res/layout/osm_note_upload.xml",
    "chars": 2639,
    "preview": "<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    andro"
  },
  {
    "path": "app/src/main/res/layout/osm_upload.xml",
    "chars": 1701,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    an"
  },
  {
    "path": "app/src/main/res/layout/osmtracker.xml",
    "chars": 440,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    andr"
  },
  {
    "path": "app/src/main/res/layout/settings_activity.xml",
    "chars": 315,
    "preview": "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    and"
  },
  {
    "path": "app/src/main/res/layout/trackdetail.xml",
    "chars": 1757,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    an"
  },
  {
    "path": "app/src/main/res/layout/trackdetail_fields.xml",
    "chars": 2683,
    "preview": "<merge xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n   "
  },
  {
    "path": "app/src/main/res/layout/trackdetail_item.xml",
    "chars": 657,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid"
  },
  {
    "path": "app/src/main/res/layout/tracklist_item.xml",
    "chars": 6649,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout\n\txmlns:android=\"http://schemas"
  },
  {
    "path": "app/src/main/res/layout/tracklogger.xml",
    "chars": 585,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\" android:"
  },
  {
    "path": "app/src/main/res/layout/trackmanager.xml",
    "chars": 1860,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\txmlns:t"
  },
  {
    "path": "app/src/main/res/layout/upload_github_menu.xml",
    "chars": 1457,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    an"
  },
  {
    "path": "app/src/main/res/layout/waypointlist_item.xml",
    "chars": 1276,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<TableLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid:"
  },
  {
    "path": "app/src/main/res/layout-iw/trackdetail_item.xml",
    "chars": 715,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tandroid"
  },
  {
    "path": "app/src/main/res/layout-iw/tracklist_item.xml",
    "chars": 4093,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    andr"
  },
  {
    "path": "app/src/main/res/layout-iw/waypointlist_item.xml",
    "chars": 1043,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<RelativeLayout\n\txmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tand"
  },
  {
    "path": "app/src/main/res/menu/btnprecb_context_menu.xml",
    "chars": 368,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item\n    "
  },
  {
    "path": "app/src/main/res/menu/displaytrackmap_menu.xml",
    "chars": 439,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu\n  xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\t<item \n\t\tand"
  },
  {
    "path": "app/src/main/res/menu/github_repository_settings_menu.xml",
    "chars": 450,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools="
  },
  {
    "path": "app/src/main/res/menu/githubupload_settings_menu.xml",
    "chars": 397,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"h"
  },
  {
    "path": "app/src/main/res/menu/launch_available_layouts_menu.xml",
    "chars": 349,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item\n     "
  },
  {
    "path": "app/src/main/res/menu/note_contextmenu.xml",
    "chars": 239,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item\n     "
  },
  {
    "path": "app/src/main/res/menu/trackdetail_menu.xml",
    "chars": 1159,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\" >\n\n    <item\n   "
  },
  {
    "path": "app/src/main/res/menu/tracklogger_menu.xml",
    "chars": 961,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n\txmlns:android=\"http://"
  },
  {
    "path": "app/src/main/res/menu/trackmgr_contextmenu.xml",
    "chars": 1276,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu\n  xmlns:android=\"http://schemas.android.com/apk/res/android\" >\n    <item\n  "
  },
  {
    "path": "app/src/main/res/menu/trackmgr_menu.xml",
    "chars": 1359,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n\txmlns:android=\"http://"
  },
  {
    "path": "app/src/main/res/values/accessibility.xml",
    "chars": 677,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <!--About screen-->\n  <string name=\"acc.osmtracker_logo\">OSMTracker"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "chars": 935,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <!-- Base application colors -->\n    <!-- https://material.io/re"
  },
  {
    "path": "app/src/main/res/values/strings-preferences.xml",
    "chars": 7566,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <!--Preferences-->\n  <string name=\"prefs\">Settings</string>\n  <stri"
  },
  {
    "path": "app/src/main/res/values/strings-tags.xml",
    "chars": 5494,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <string name=\"tag.misc\">Misc</string>\n  <string name=\"tag.restricti"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "chars": 19978,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <!--Generic strings-->\n  <string name=\"app_name\">OSMTracker for And"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "chars": 804,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<resources>\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" pare"
  },
  {
    "path": "app/src/main/res/values/theme_highcontrast.xml",
    "chars": 672,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\t<style name=\"HighContrast\" parent=\"@android:style/Theme\">\n\t\t<!-- ite"
  },
  {
    "path": "app/src/main/res/values/themes.xml",
    "chars": 339,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <style name=\"DefaultTheme\" parent=\"@android:style/Theme\"/>\n    <s"
  },
  {
    "path": "app/src/main/res/values/values-preferences.xml",
    "chars": 1957,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n\t<string-array name=\"prefs_ui_picture_source_values\">\n\t\t<item>camera"
  },
  {
    "path": "app/src/main/res/values/values.xml",
    "chars": 316,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n\t<string name=\"about_link\"><a href=\"https://github.com/labexp/osmtra"
  },
  {
    "path": "app/src/main/res/values/waypoints.xml",
    "chars": 166,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <string name=\"wpt_voicerec\">Voice recording</string>\n  <string name"
  },
  {
    "path": "app/src/main/res/values-ar/accessibility.xml",
    "chars": 598,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <!--About screen-->\n  <string name=\"acc.osmtracker_logo\">شعار OSMTr"
  },
  {
    "path": "app/src/main/res/values-ar/strings-preferences.xml",
    "chars": 5837,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <!--Preferences-->\n  <string name=\"prefs\">الاعدادت</string>\n  <stri"
  },
  {
    "path": "app/src/main/res/values-ar/strings-tags.xml",
    "chars": 5520,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <string name=\"tag.misc\">منوعات</string>\n  <string name=\"tag.restric"
  },
  {
    "path": "app/src/main/res/values-ar/strings.xml",
    "chars": 12206,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <!--Generic strings-->\n  <string name=\"app_name\">متتبع OSM للأندروي"
  },
  {
    "path": "app/src/main/res/values-ar/waypoints.xml",
    "chars": 160,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <string name=\"wpt_voicerec\">تسجيل صوتي</string>\n  <string name=\"wpt"
  },
  {
    "path": "app/src/main/res/values-b+sr+Latn/accessibility.xml",
    "chars": 628,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <!--About screen-->\n  <string name=\"acc.osmtracker_logo\">OSMTracker"
  },
  {
    "path": "app/src/main/res/values-b+sr+Latn/strings-preferences.xml",
    "chars": 5107,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <!--Preferences-->\n  <string name=\"prefs\">Postavke</string>\n  <stri"
  },
  {
    "path": "app/src/main/res/values-b+sr+Latn/strings-tags.xml",
    "chars": 5344,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <string name=\"tag.misc\">Razno</string>\n  <string name=\"tag.restrict"
  },
  {
    "path": "app/src/main/res/values-b+sr+Latn/strings.xml",
    "chars": 8704,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <!--Generic strings-->\n  <string name=\"app_name\">Pratilac za Androi"
  },
  {
    "path": "app/src/main/res/values-b+sr+Latn/waypoints.xml",
    "chars": 167,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <string name=\"wpt_voicerec\">Glasovna beleška</string>\n  <string nam"
  },
  {
    "path": "app/src/main/res/values-be/strings-tags.xml",
    "chars": 4257,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <string name=\"tag.misc\">Рознае</string>\n  <string name=\"tag.restric"
  },
  {
    "path": "app/src/main/res/values-be/strings.xml",
    "chars": 5057,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <!--Generic strings-->\n  <string name=\"app_name\">OSMTracker для And"
  },
  {
    "path": "app/src/main/res/values-bg-rBG/strings-preferences.xml",
    "chars": 411,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <!--Preferences-->\n  <string name=\"prefs\">Настройки</string>\n  <str"
  },
  {
    "path": "app/src/main/res/values-bg-rBG/strings-tags.xml",
    "chars": 1255,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <string name=\"tag.restriction\">Ограничение</string>\n  <string name="
  },
  {
    "path": "app/src/main/res/values-bg-rBG/strings.xml",
    "chars": 1070,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <!--Generic strings-->\n  <string name=\"app_name\">OSMTracker за Andr"
  },
  {
    "path": "app/src/main/res/values-ca/accessibility.xml",
    "chars": 632,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <!--About screen-->\n  <string name=\"acc.osmtracker_logo\">Logo OSMTr"
  },
  {
    "path": "app/src/main/res/values-ca/strings-preferences.xml",
    "chars": 5679,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <!--Preferences-->\n  <string name=\"prefs\">Preferències</string>\n  <"
  },
  {
    "path": "app/src/main/res/values-ca/strings-tags.xml",
    "chars": 5579,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <string name=\"tag.misc\">Varis</string>\n  <string name=\"tag.restrict"
  },
  {
    "path": "app/src/main/res/values-ca/strings.xml",
    "chars": 9604,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <!--Generic strings-->\n  <string name=\"app_name\">OSMTracker per a A"
  },
  {
    "path": "app/src/main/res/values-ca/waypoints.xml",
    "chars": 171,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <string name=\"wpt_voicerec\">Gravació de veu</string>\n  <string name"
  },
  {
    "path": "app/src/main/res/values-cs-rCZ/accessibility.xml",
    "chars": 684,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <!--About screen-->\n  <string name=\"acc.osmtracker_logo\">Logo OSMTr"
  },
  {
    "path": "app/src/main/res/values-cs-rCZ/strings-preferences.xml",
    "chars": 6422,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <!--Preferences-->\n  <string name=\"prefs\">Nastavení</string>\n  <str"
  },
  {
    "path": "app/src/main/res/values-cs-rCZ/strings-tags.xml",
    "chars": 5693,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <string name=\"tag.misc\">Různé</string>\n  <string name=\"tag.restrict"
  },
  {
    "path": "app/src/main/res/values-cs-rCZ/strings.xml",
    "chars": 14763,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <!--Generic strings-->\n  <string name=\"app_name\">OSMTracker pro And"
  },
  {
    "path": "app/src/main/res/values-cs-rCZ/waypoints.xml",
    "chars": 168,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <string name=\"wpt_voicerec\">Záznam hlasu</string>\n  <string name=\"w"
  },
  {
    "path": "app/src/main/res/values-da/accessibility.xml",
    "chars": 681,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <!--About screen-->\n  <string name=\"acc.osmtracker_logo\">OSMTracker"
  },
  {
    "path": "app/src/main/res/values-da/strings-preferences.xml",
    "chars": 6234,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <!--Preferences-->\n  <string name=\"prefs\">Indstillinger</string>\n  "
  },
  {
    "path": "app/src/main/res/values-da/strings-tags.xml",
    "chars": 5482,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <string name=\"tag.misc\">Div.</string>\n  <string name=\"tag.restricti"
  },
  {
    "path": "app/src/main/res/values-da/strings.xml",
    "chars": 14363,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <!--Generic strings-->\n  <string name=\"app_name\">OSMTracker til And"
  },
  {
    "path": "app/src/main/res/values-da/waypoints.xml",
    "chars": 163,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <string name=\"wpt_voicerec\">Taleoptagelse</string>\n  <string name=\""
  },
  {
    "path": "app/src/main/res/values-de/accessibility.xml",
    "chars": 677,
    "preview": "<?xml version='1.0' encoding='UTF-8'?>\n<resources>\n  <!--About screen-->\n  <string name=\"acc.osmtracker_logo\">OSMTracker"
  }
]

// ... and 262 more files (download for full content)

About this extraction

This page contains the full source code of the labexp/osmtracker-android GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 462 files (1.8 MB), approximately 525.3k tokens, and a symbol index with 815 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!