Compare commits

..

53 Commits

Author SHA1 Message Date
Dennis Guse a2b53c75f4 Release v3.7.6 2020-07-13 18:47:50 +02:00
Dennis Guse 7a5f69d3a5 Settings: default export format is now KMZ incl. data and pictures. 2020-07-13 18:19:34 +02:00
Dennis Guse 7fd9893560 TrackDeleteActivity: keep a wakelock while deletion is ongoing. 2020-07-13 18:19:34 +02:00
Román f209031df0 Pass extra TrackFileFormat enum like a Serializable instead of String (with String resulted in Null after get extra). 2020-07-13 18:19:34 +02:00
Dennis Guse 2dbab929ac Export: replaced async task with thread.
Almost identical to the import.
2020-07-13 18:19:34 +02:00
Dennis Guse e9eb764d12 Import: split ImportActivity into fragments.
The fragements keep the state while the activity is re-created (e.g., orientation change).
2020-07-13 18:19:34 +02:00
Dennis Guse 41a4a5527a Import: replaced async task with thread.
Extracted directory chooser into a separate activity.
2020-07-13 18:19:34 +02:00
Dennis Guse 4ded3c6bc8 TrackDelete: use a Thread and always require TrackIds. 2020-07-08 08:25:36 +02:00
Dennis Guse 51e573e475 TrackDelete: hide progress bar before finishing and add custom animations. 2020-07-08 08:25:36 +02:00
Dennis Guse 98c3169929 Track deletion UI: code cleanup. 2020-07-08 08:25:36 +02:00
Dennis Guse ff65a4d04f Track deletion shows an infinite progressbar.
Fixes #242.
2020-07-08 08:25:36 +02:00
Dennis Guse 278f49dec8 TrackRecordingActivity: removed start/resume toast message. 2020-07-07 18:13:37 +02:00
Dennis Guse d444e1ca3c TrackRecordingActivity: recreate activity properly. 2020-07-07 18:13:37 +02:00
Dennis Guse 13e07696b9 Re-implemented landscape mode.
Fixes #32.
2020-07-07 18:13:37 +02:00
Román Martínez 23c3a4d651 Import/Export speed in GPX and KML files. (#306)
Import/Export speed in GPX and KML files. Fixes #300.
2020-06-30 14:39:44 +02:00
Dennis Guse c9869bab0e Generate UUID for each track (necessary for synchronization).
Fixes #294.
2020-06-30 12:39:30 +02:00
Dennis Guse ecdb509371 Export/import test now verifies the TrackPoints. 2020-06-22 21:56:22 +02:00
transifex-integration[bot] de376a84f8 Apply translations in fr
translation completed for the source file '/fastlane/metadata/android/en-US/short_description.txt'
on the 'fr' language.
2020-06-21 18:19:11 +02:00
transifex-integration[bot] 9abd8bcaa6 Apply translations in de
translation completed for the source file '/fastlane/metadata/android/en-US/short_description.txt'
on the 'de' language.
2020-06-21 18:18:58 +02:00
Román Martínez 8ddadb8e6d Aggregated statistics: updated PlantUML and handle empty list. (#296) 2020-06-20 19:02:02 +02:00
transifex-integration[bot] 924ef3313e Translate /src/main/res/values/strings.xml in nl
translation completed for the source file '/src/main/res/values/strings.xml'
on the 'nl' language.
2020-06-16 19:33:52 +02:00
Dennis Guse 8cb68a6e64 Migrate to default branch main. 2020-06-15 12:10:26 +02:00
Dennis Guse 23f47f10f8 Release v3.7.5 2020-06-14 20:09:27 +02:00
Román Martínez 251fa17487 Merge pull request #271 from OpenTracksApp/all_statistics#112
Aggregated statistics by activity type.
2020-06-14 20:03:29 +02:00
Román bae3373fe9 Aggregated statistics by activity type. Fixes #112. 2020-06-14 19:57:07 +02:00
Dennis Guse 0a685a66e3 Added list of tested Bluetooth LE sensors.
Fixes #202.
2020-06-14 11:58:45 +02:00
Dennis Guse 5ca9ca857a Cleanup. 2020-06-13 23:07:58 +02:00
Dennis Guse d9dfa6d746 Dashboard API: add data transfer as GPX and removed unnecessary IntentChooser. 2020-06-13 17:56:59 +02:00
Dennis Guse 3a5220546e Dashboard API: fallback to "open as KMZ" implemented as proxy activity.
We are not allowed to query for installed packages as of Android R (API 30).
2020-06-13 17:56:59 +02:00
Dennis Guse 047d5c2340 Manifest reordered to fix warning. 2020-06-13 17:56:59 +02:00
Dennis Guse 725c21c0a1 Bugfix: parse URI with trackIds correctly. 2020-06-13 17:56:59 +02:00
Dennis Guse 7939e39fc1 Go for Android R (API level 30). 2020-06-13 17:56:59 +02:00
Dennis Guse a133bd8f9e Bugfix: adaptive icon was too large.
We now use the same as OSMDashboard.
Thanks Peter.
2020-06-13 00:08:00 +02:00
transifex-integration[bot] 1f9790cba8 Translate /src/main/res/values/strings.xml in gl
translation completed for the source file '/src/main/res/values/strings.xml'
on the 'gl' language.
2020-06-10 22:20:23 +02:00
Dennis Guse a3ba1e1490 Cleanup: use lambda instead of runnable. 2020-06-09 23:01:09 +02:00
Dennis Guse accb30152f Translations fixed. 2020-06-09 19:58:23 +02:00
Dennis Guse dc7daa6e8c App icon: be complaint with Android requirements. (#253)
Fixes #121.
2020-06-09 19:37:47 +02:00
Dennis Guse 1d8e788d53 Documentation: add internal data flow. 2020-06-09 19:36:54 +02:00
Dennis Guse 568b908b26 Removed unused strings. 2020-06-09 19:35:57 +02:00
transifex-integration[bot] 3ab63adb34 Translate '/src/main/res/values/strings.xml' in 'de' [manual sync] (#269)
* Translate /src/main/res/values/strings.xml in de

at least 95% translated for the source file '/src/main/res/values/strings.xml'
on the 'de' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format

Co-authored-by: transifex-integration[bot] <43880903+transifex-integration[bot]@users.noreply.github.com>
2020-06-08 23:42:16 +02:00
transifex-integration[bot] 91692f345e Translate /src/main/res/values/strings.xml in fr
at least 95% translated for the source file '/src/main/res/values/strings.xml'
on the 'fr' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format
2020-06-08 23:32:34 +02:00
transifex-integration[bot] dbdb98a682 Translate /src/main/res/values/strings.xml in eu
at least 95% translated for the source file '/src/main/res/values/strings.xml'
on the 'eu' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format
2020-06-08 23:32:21 +02:00
transifex-integration[bot] df66cd5753 Translate /src/main/res/values/strings.xml in cs
at least 95% translated for the source file '/src/main/res/values/strings.xml'
on the 'cs' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format
2020-06-08 23:31:19 +02:00
transifex-integration[bot] 6e39dd37f8 Translate /src/main/res/values/strings.xml in da
at least 95% translated for the source file '/src/main/res/values/strings.xml'
on the 'da' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format
2020-06-08 23:31:08 +02:00
transifex-integration[bot] 7212f7be94 Translate /src/main/res/values/strings.xml in nl
at least 95% translated for the source file '/src/main/res/values/strings.xml'
on the 'nl' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format
2020-06-08 23:31:00 +02:00
transifex-integration[bot] 77fe494e6c Translate /src/main/res/values/strings.xml in et
at least 95% translated for the source file '/src/main/res/values/strings.xml'
on the 'et' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format
2020-06-08 23:30:55 +02:00
transifex-integration[bot] 2f491e8c10 Translate /src/main/res/values/strings.xml in es
at least 95% translated for the source file '/src/main/res/values/strings.xml'
on the 'es' language.

 Manual sync of partially translated files: untranslated content is included with an empty translation or source language content depending on file format
2020-06-08 23:30:48 +02:00
Dennis Guse f4fe7b1ab3 Import: convert all failed date parsing into SAXExceptions. 2020-06-08 22:51:01 +02:00
Peter Storch 7e444cbf31 Merge pull request #248 from OpenTracksApp/keep_screen_on_dashboard
forward keep screen on while recording preference to dashboard
2020-06-06 23:12:24 +02:00
Dennis Guse ed6ab9c53c Import: be lenient with missing timezone information.
Fixes #249.
2020-06-06 22:58:02 +02:00
Peter Storch f91ca3154c forward show on lockscreen to dashboard 2020-06-05 20:52:00 +02:00
Peter Storch bbfab1b201 forward keep screen on while recording preference to dashboard 2020-06-05 20:17:24 +02:00
Dennis Guse 1f2e3bcf70 Upgrade to Android Gradle plugin 4.0.0 2020-05-30 20:25:36 +02:00
108 changed files with 3687 additions and 1347 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
+9 -3
View File
@@ -51,13 +51,19 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
__Only required permissions:__
* _ACCESS_FINE_LOCATION_: required to use the GPS.
An overview of Bluetooth LE sensors that are known to work with OpenTracks is in [README_TESTED_SENSORS.md](README_TESTED_SENSORS.md).
_Please note:_
_OpenTracks_ does not contain a _map_ (as this would required Internet access).
To show your tracks on a map, a 3rd party application (e.g., [OsmAnd](https://play.google.com/store/apps/details?id=net.osmand) or [MAPS.ME](https://play.google.com/store/apps/details?id=com.mapswithme.maps.pro)) needs to be installed.
## Custom Dashboards
## Custom Dashboards (incl. Map)
As of v3.3.1 OpenTracks enables to use custom dashboards.
The reference implementation is [OSMDashboard](https://github.com/OpenTracksApp/OSMDashboard), which presents an OpenStreetMap map (showing the current track, incl. updates).
The developer documentation is in [README_API.md](README_API.md).
Alternatively, recorded tracks can be shared as KMZ/GPX to installed applications (e.g., [OsmAnd](https://play.google.com/store/apps/details?id=net.osmand) or [MAPS.ME](https://play.google.com/store/apps/details?id=com.mapswithme.maps.pro)).
However, this is rather slow and does not provide updates while recording.
## Screenshots
<div>
@@ -87,7 +93,7 @@ Rework of _OpenTracks_ included:
* removing calorie estimation and activity estimation,
* removing support for ANT+ and Classic Bluetooth,
* adding support for Bluetooth LE heart rate sensors,
* removing Protobuf (store sensor data in SQLite columns directly), and
* removing Protobuf (store sensosensorr data in SQLite columns directly), and
* removing Android Service API for other apps.
Artwork, logos and user interface remained more or less unchanged.
-3
View File
@@ -7,9 +7,6 @@ Although this might be (mostly) sufficient, it has some limitations.
This include limited configurability (e.g., ordering of shown elements) and the lack of Internet access.
In fact, the later is considered a feature but severely limits the implementation presenting a map (at least in an easy way).
_NOTE:_ for showing a track on a map a workaround was implemented in OpenTracks.
Data could be exported as KMZ and presented in 3rd-party application (e.g., [OsmAnd](https://play.google.com/store/apps/details?id=net.osmand) or [MAPS.ME](https://play.google.com/store/apps/details?id=com.mapswithme.maps.pro)).
For the time being, the OpenTracks Data API can be used to implement mainly custom dashboards.
## Implementation
+1 -1
View File
@@ -2,7 +2,7 @@
## Craft new releases
1. Get next _version code_ (master branch): `git rev-list HEAD --count master`
1. Get next _version code_ (main): `git rev-list HEAD --count main`
2. Decide on _version name_ (semantic versioning)
3. Manually update _version code_ and _version name_ in `build.gradle`
4. Create changelog (`_version code_.txt`)
+20
View File
@@ -0,0 +1,20 @@
# Overview of tested Bluetooth LE sensors
## HRS: Heartrate Service
* Decathlon|Geonaute|Kalenji Dual HRM Belt
* Polar H7
* Polar OH1
Has updatable firmware that requires a user account at polar.com
* Wahoo Tickr (Model: WFBTHR02)
## CSCP: Cycling Cadence and Speed Service
Please note that the according to the specification these sensors _may_ provide speed as well as cadence.
However, often only one value is provided.
### Speed
* Wahoo Speed (Model: WFRPMSPD)
### Cadence
* Wahoo Cadence (Model: WFPODCAD2)
This sensor reports cadence data as speed.
A workaround is in place.
+14 -9
View File
@@ -6,7 +6,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.2'
classpath 'com.android.tools.build:gradle:4.0.0'
}
}
@@ -50,25 +50,30 @@ def getVersionName = { ->
}
android {
compileSdkVersion 29
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
compileSdkVersion 30
defaultConfig {
applicationId "de.dennisguse.opentracks"
manifestPlaceholders = [
applicationLogo: "@drawable/ic_logo_color_24dp",
applicationLogo: "@mipmap/ic_launcher",
applicationName: "@string/app_name"
]
versionCode 3396
versionCode 3449
//versionCode getVersionCode() //Useful for debugging.
versionName "v3.7.4"
versionName "v3.7.6"
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
minSdkVersion 21
targetSdkVersion 29
targetSdkVersion 30
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@@ -78,7 +83,7 @@ android {
applicationIdSuffix ".debug"
versionNameSuffix "-debug"
manifestPlaceholders = [
applicationLogo: "@drawable/ic_logo_black_24dp",
applicationLogo: "@mipmap/ic_launcher_debug",
applicationName: "OpenTracks (Debug)"
]
}
@@ -102,6 +107,8 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.preference:preference:1.1.1'
implementation 'androidx.documentfile:documentfile:1.0.1'
implementation 'com.google.android.material:material:1.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
androidTestImplementation 'androidx.test:core:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
@@ -113,6 +120,4 @@ dependencies {
testImplementation 'androidx.test:core:1.2.0'
testImplementation 'org.mockito:mockito-core:3.0.0'
implementation 'com.google.android.material:material:1.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
}
+5 -1
View File
@@ -12,6 +12,7 @@ endlegend
TrackListActivity --> TrackRecordingActivity : Start recording
TrackListActivity --> SearchListActivity
TrackListActivity --> MarkerListActivity : Show all markers
TrackListActivity --> AggregatedStatisticsActivity : Aggregated stats
TrackListActivity --> TrackRecordedActivity : Show track
TrackListActivity --> TrackDeleteActivity
@@ -46,6 +47,9 @@ MarkerDetailActivity --> CoordinateOnMapIntent : URI
SettingsActivity --> AboutActivity
SettingsActivity --> HelpActivity
SettingsActivity --> ExportActivity
SettingsActivity --> ImportActivity
SettingsActivity --> DirectoryChooserActivity
SettingsActivity --> DirectoryChooserActivity
DirectoryChooserActivity$ImportDirectoryChooserActivity --> ImportActivity
DirectoryChooserActivity$ExportDirectoryChooserActivity --> ExportActivity
@enduml
+18
View File
@@ -0,0 +1,18 @@
@startuml
SensorManager --> ElevationSumManager
LocationManager --> TrackRecordingService
ElevationSumManager --> TrackRecordingService
TrackRecordingService --> TrackRecordingServiceConnection
TrackRecordingService --> Database
Database --> CustomContentProvider
CustomContentProvider --> TrackDataHub
CustomContentProvider --> ShareContentProvider
TrackDataHub --> StatisticsRecordingFragment : TrackPoints, TrackStatistics, Waypoints (push)
TrackDataHub --> StatisticsRecordedFragment : TrackPoints, TrackStatistics, Waypoints (push)
TrackDataHub --> ChartFragment --> ChartView
TrackRecordingServiceConnection --> StatisticsRecordingFragment : Sensor data (heartrate, cadence, elevation gain; pull 1s)
TrackRecordingServiceConnection --> TrackController : Start time (pull, onStart)
@enduml
@@ -1 +1 @@
OpenTracks – ein GPS-Tracker, der deine Daten für sich behält.
OpenTracks – ein GPS-Tracker, der deine Daten für sich behält.
@@ -0,0 +1,11 @@
v3.7.5: OpenTracks
Changes:
- Added aggregated statistics summary (from main the screen's menu)
- Dashboard API: pass "show on lock screen"- and "keep screen on"-setting
- Updated translations
- Import: be lenient with ISO8601 date format
- Show track on map: file format can be selected and opening dashboard works on Android R (API 30+)
Bugfix:
- App icon now correctly implemented
@@ -0,0 +1,11 @@
v3.7.6: OpenTracks
Changes:
- Landscape mode
- Track deletion UI is now only a spinner
- Speed is exported and imported (GPX/KML/KMZ)
- Default export format is KMZ incl. all data and pictures
Developer:
- Replaced AsyncTasks with Threads
- Synchronization: UUID is generated per track (not yet exported)
@@ -1 +1 @@
OpenTracks - Suivi d'activités sportives respectueux de votre vie privée.
OpenTracks - Suivi d'activités sportives respectueux de votre vie privée.
@@ -39,7 +39,7 @@ public class TestDataUtil {
TrackPoint[] trackPoints = new TrackPoint[numPoints];
for (int i = 0; i < numPoints; i++) {
trackPoints[i] = (createTrackPoint(i));
trackPoints[i] = createTrackPoint(i);
}
return new Pair<>(track, trackPoints);
@@ -56,17 +56,22 @@ public class TestDataUtil {
/**
* Creates a location.
*
* @param i the index to set the value of location.
* @return created location
* @param i the index for the TrackPoint.
*/
public static TrackPoint createTrackPoint(int i) {
Location location = new Location("test");
location.setLatitude(INITIAL_LATITUDE + (double) i / 10000.0);
location.setLongitude(INITIAL_LONGITUDE - (double) i / 10000.0);
location.setAccuracy((float) i / 100.0f);
location.setAltitude(i * ALTITUDE_INTERVAL);
location.setTime(i + 1);
return new TrackPoint(location);
TrackPoint trackPoint = new TrackPoint();
trackPoint.setLatitude(INITIAL_LATITUDE + (double) i / 10000.0);
trackPoint.setLongitude(INITIAL_LONGITUDE - (double) i / 10000.0);
trackPoint.setAccuracy((float) i / 100.0f);
trackPoint.setAltitude(i * ALTITUDE_INTERVAL);
trackPoint.setTime(i + 1);
trackPoint.setSpeed(5f + (i / 10));
trackPoint.setHeartRate_bpm(100f + i);
trackPoint.setCyclingCadence_rpm(200f + i);
trackPoint.setCyclingCadence_rpm(300f + i);
trackPoint.setPower(400f + i);
return trackPoint;
}
/**
@@ -37,6 +37,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import de.dennisguse.opentracks.content.data.TestDataUtil;
import de.dennisguse.opentracks.content.data.Track;
@@ -47,6 +48,7 @@ import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.content.data.WaypointsColumns;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.FileUtils;
import de.dennisguse.opentracks.util.UUIDUtils;
import static org.mockito.Mockito.when;
@@ -171,22 +173,26 @@ public class CustomContentProviderUtilsTest {
*/
@Test
public void testCreateTrack() {
int startColumnIndex = 1;
int columnIndex = startColumnIndex;
when(cursorMock.getColumnIndexOrThrow(TracksColumns._ID)).thenReturn(columnIndex++);
when(cursorMock.getColumnIndexOrThrow(TracksColumns.NAME)).thenReturn(columnIndex++);
columnIndex = startColumnIndex;
// Id
when(cursorMock.isNull(columnIndex++)).thenReturn(false);
// Name
when(cursorMock.isNull(columnIndex++)).thenReturn(false);
long trackId = System.currentTimeMillis();
columnIndex = startColumnIndex;
int columnIndex = 1;
// Id
when(cursorMock.getLong(columnIndex++)).thenReturn(trackId);
when(cursorMock.getColumnIndexOrThrow(TracksColumns._ID)).thenReturn(columnIndex);
when(cursorMock.isNull(columnIndex)).thenReturn(false);
when(cursorMock.getLong(columnIndex)).thenReturn(trackId);
//Uuid
columnIndex++;
when(cursorMock.getColumnIndexOrThrow(TracksColumns.UUID)).thenReturn(columnIndex);
when(cursorMock.isNull(columnIndex)).thenReturn(false);
when(cursorMock.getBlob(columnIndex)).thenReturn(UUIDUtils.toBytes(UUID.randomUUID()));
// Name
columnIndex++;
String name = NAME_PREFIX + trackId;
when(cursorMock.getString(columnIndex++)).thenReturn(name);
when(cursorMock.getColumnIndexOrThrow(TracksColumns.NAME)).thenReturn(columnIndex);
when(cursorMock.isNull(columnIndex)).thenReturn(false);
when(cursorMock.getString(columnIndex)).thenReturn(name);
Track track = contentProviderUtils.createTrack(cursorMock);
Assert.assertEquals(trackId, track.getId());
@@ -5,6 +5,7 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import androidx.annotation.VisibleForTesting;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -36,7 +37,8 @@ public class CustomSQLiteOpenHelperTest {
*
* @return Map(TableName, SQL)
*/
private static Map<String, String> getTableSQL(SQLiteDatabase db) {
@VisibleForTesting
public static Map<String, String> getTableSQL(SQLiteDatabase db) {
HashMap<String, String> tableSQL = new HashMap<>();
try (Cursor cursor = db.query("sqlite_master", new String[]{"name", "SQL"}, "name IN ('" + TracksColumns.TABLE_NAME + "', '" + TrackPointsColumns.TABLE_NAME + "', '" + WaypointsColumns.TABLE_NAME + "')", null, null, null, "name")) {
if (cursor != null) {
@@ -53,6 +55,7 @@ public class CustomSQLiteOpenHelperTest {
*
* @return Map(tableName, SQL)
*/
@VisibleForTesting
public static Map<String, String> getIndexSQL(SQLiteDatabase db) {
HashMap<String, String> indexSQL = new HashMap<>();
try (Cursor cursor = db.rawQuery("SELECT tbl_name, SQL FROM sqlite_master WHERE type = 'index'", null)) {
@@ -104,24 +107,7 @@ public class CustomSQLiteOpenHelperTest {
@Test
public void onUpgrade_FromVersion23() {
// Manually create database schema with version 23 (base version)
{
SQLiteDatabase dbBase = new SQLiteOpenHelper(context, DATABASE_NAME, null, 23) {
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}.getWritableDatabase();
dbBase.execSQL(TRACKS_CREATE_TABLE_V23);
dbBase.execSQL(TRACKPOINTS_CREATE_TABLE_V23);
dbBase.execSQL(WAYPOINTS_CREATE_TABLE_V23);
dbBase.close();
}
createVersion23();
// Open database with SQL upgrade
Map<String, String> tableByUpgrade;
@@ -130,10 +116,9 @@ public class CustomSQLiteOpenHelperTest {
tableByUpgrade = getTableSQL(dbUpgraded);
indicesByUpgrade = getIndexSQL(dbUpgraded);
}
// Open database via
context.deleteDatabase(DATABASE_NAME);
// Open database via creation script
Map<String, String> tablesByCreate;
Map<String, String> indicesByCreate;
try (SQLiteDatabase dbCreated = new CustomSQLiteOpenHelper(context, DATABASE_NAME).getReadableDatabase()) {
@@ -150,7 +135,8 @@ public class CustomSQLiteOpenHelperTest {
Assert.assertEquals(tablesByCreate.get(WaypointsColumns.TABLE_NAME), tableByUpgrade.get(WaypointsColumns.TABLE_NAME));
// then - verify custom indices
Assert.assertEquals(2, indicesByCreate.size());
Assert.assertEquals(3, indicesByCreate.size());
Assert.assertEquals(indicesByUpgrade.get(TracksColumns.TABLE_NAME), indicesByCreate.get(TracksColumns.TABLE_NAME));
Assert.assertEquals(indicesByUpgrade.get(TrackPointsColumns.TABLE_NAME), indicesByCreate.get(TrackPointsColumns.TABLE_NAME));
Assert.assertEquals(indicesByUpgrade.get(WaypointsColumns.TABLE_NAME), indicesByCreate.get(WaypointsColumns.TABLE_NAME));
}
@@ -176,4 +162,23 @@ public class CustomSQLiteOpenHelperTest {
// then - verify custom indices
Assert.assertEquals(0, indicesByDowngrade.size());
}
private void createVersion23() {
// Manually create database schema with version 23 (base version)
SQLiteDatabase dbBase = new SQLiteOpenHelper(context, DATABASE_NAME, null, 23) {
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}.getWritableDatabase();
dbBase.execSQL(TRACKS_CREATE_TABLE_V23);
dbBase.execSQL(TRACKPOINTS_CREATE_TABLE_V23);
dbBase.execSQL(WAYPOINTS_CREATE_TABLE_V23);
dbBase.close();
}
}
@@ -11,7 +11,14 @@ import de.dennisguse.opentracks.io.file.TrackFileFormat;
public class ShareContentProviderTest {
@Test
public void testCreateandParseURI() {
public void testCreateandParseURI_invalid() {
Uri uri = Uri.parse("content://de.dennisguse.opentracks.debug.content/tracks/1");
Assert.assertArrayEquals(new long[]{}, ShareContentProvider.parseURI(uri));
}
@Test
public void testCreateandParseURI_valid() {
long[] trackIds = {1, 3, 5};
Pair<Uri, String> shareURIandMIME = ShareContentProvider.createURI(trackIds, "TrackName", TrackFileFormat.KML_ONLY_TRACK);
@@ -17,6 +17,7 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import de.dennisguse.opentracks.content.data.TestDataUtil;
@@ -48,7 +49,10 @@ public class ExportImportTest {
private static final String TRACK_ICON = "the track icon";
private static final String TRACK_CATEGORY = "the category";
private static final String TRACK_DESCRIPTION = "the description";
private final List<Waypoint> waypoints = new ArrayList<>();
private final List<TrackPoint> trackPoints = new ArrayList<>();
private long importTrackId;
private final long trackId = System.currentTimeMillis();
@@ -61,6 +65,9 @@ public class ExportImportTest {
contentProviderUtils.insertTrack(track.first);
contentProviderUtils.bulkInsertTrackPoint(track.second, track.first.getId());
trackPoints.clear();
trackPoints.addAll(Arrays.asList(track.second));
for (int i = 0; i < 3; i++) {
Waypoint waypoint = new Waypoint(track.second[i].getLocation());
waypoint.setName("the waypoint " + i);
@@ -104,7 +111,40 @@ public class ExportImportTest {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
trackExporter.writeTrack(context, outputStream);
System.out.println(outputStream.toString());
// 2. import
InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
AbstractFileTrackImporter trackImporter = new KmlFileTrackImporter(context, -1L);
importTrackId = trackImporter.importFile(inputStream);
// then
// 1. track
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
assertNotNull(importedTrack);
assertEquals(track.getCategory(), importedTrack.getCategory());
assertEquals(track.getDescription(), importedTrack.getDescription());
assertEquals(track.getName(), importedTrack.getName());
assertEquals(track.getIcon(), importedTrack.getIcon());
// 2. waypoints
assertWaypoints();
// 3. trackpoints
assertTrackpoints(false, false, false);
}
@LargeTest
@Test
public void kml_with_trackdetail_and_sensordata() {
// given
Track track = contentProviderUtils.getTrack(trackId);
TrackFileFormat trackFileFormat = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA;
TrackExporter trackExporter = trackFileFormat.newTrackExporter(context, new Track[]{track});
// when
// 1. export
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
trackExporter.writeTrack(context, outputStream);
// 2. import
InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
@@ -115,7 +155,6 @@ public class ExportImportTest {
// 1. track
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
assertNotNull(importedTrack);
//TODO assertEquals(track.getTrackPoints(), importedTrack.getTrackPoints());
assertEquals(track.getCategory(), importedTrack.getCategory());
assertEquals(track.getDescription(), importedTrack.getDescription());
assertEquals(track.getName(), importedTrack.getName());
@@ -124,14 +163,8 @@ public class ExportImportTest {
// 2. waypoints
assertWaypoints();
//TODO Check absolute time of trackpoints
}
@LargeTest
@Test
public void kml_with_trackdetail_and_sensordata() {
// TODO
Log.e(TAG, "Test not implemented.");
// 3. trackpoints
assertTrackpoints(true, true, true);
}
@LargeTest
@@ -185,7 +218,6 @@ public class ExportImportTest {
// 1. track
Track trackImported = contentProviderUtils.getTrack(importTrackId);
assertNotNull(trackImported);
//TODO assertEquals(track.getTrackPoints(), trackImported.getTrackPoints());
assertEquals(track.getCategory(), trackImported.getCategory());
assertEquals(track.getDescription(), trackImported.getDescription());
assertEquals(track.getName(), trackImported.getName());
@@ -196,7 +228,8 @@ public class ExportImportTest {
// 2. waypoints
assertWaypoints();
//TODO Check absolute time of trackpoints
// 3. trackpoints
assertTrackpoints(false, false, false);
}
private void assertWaypoints() {
@@ -217,4 +250,33 @@ public class ExportImportTest {
assertEquals(waypoint.getLocation().getAltitude(), importedWaypoint.getLocation().getAltitude(), 0.001);
}
}
private void assertTrackpoints(boolean verifyPower, boolean verifyHeartrate, boolean verifyCadence) {
List<TrackPoint> importedTrackPoints = contentProviderUtils.getTrackPoints(importTrackId);
assertEquals(trackPoints.size(), importedTrackPoints.size());
for (int i = 0; i < trackPoints.size(); i++) {
TrackPoint trackPoint = trackPoints.get(i);
TrackPoint importedTrackPoint = importedTrackPoints.get(i);
assertEquals(trackPoint.getTime(), importedTrackPoint.getTime(), 0.01);
// TODO Not exported for GPX/KML
// assertEquals(trackPoint.getAccuracy(), importedTrackPoint.getAccuracy(), 0.01);
assertEquals(trackPoint.getLatitude(), importedTrackPoint.getLatitude(), 0.001);
assertEquals(trackPoint.getLongitude(), importedTrackPoint.getLongitude(), 0.001);
assertEquals(trackPoint.getAltitude(), importedTrackPoint.getAltitude(), 0.001);
assertEquals(trackPoint.getSpeed(), importedTrackPoint.getSpeed(), 0.001);
if (verifyHeartrate) {
assertEquals(trackPoint.getHeartRate_bpm(), importedTrackPoint.getHeartRate_bpm(), 0.01);
}
if (verifyCadence) {
assertEquals(trackPoint.getCyclingCadence_rpm(), importedTrackPoint.getCyclingCadence_rpm(), 0.01);
}
if (verifyPower) {
assertEquals(trackPoint.getPower(), importedTrackPoint.getPower(), 0.01);
}
}
}
}
@@ -128,6 +128,7 @@ public class StringUtilsTest {
*/
@Test
public void testGetTime_timezone() {
assertGetTime("2010-05-04T03:02:01", 2010, 5, 4, 3, 2, 1, 0);
assertGetTime("2010-05-04T03:02:01Z", 2010, 5, 4, 3, 2, 1, 0);
assertGetTime("2010-05-04T03:02:01+00:00", 2010, 5, 4, 3, 2, 1, 0);
assertGetTime("2010-05-04T03:02:01-00:00", 2010, 5, 4, 3, 2, 1, 0);
@@ -0,0 +1,23 @@
package de.dennisguse.opentracks.util;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.UUID;
@RunWith(AndroidJUnit4.class)
public class UUIDUtilsTest {
@Test
public void test() {
UUID uuid = UUID.randomUUID();
byte[] bytes = UUIDUtils.toBytes(uuid);
UUID output = UUIDUtils.fromBytes(bytes);
Assert.assertEquals(uuid, output);
}
}
@@ -0,0 +1,257 @@
package de.dennisguse.opentracks.viewmodels;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.TrackIconUtils;
@RunWith(JUnit4.class)
public class AggregatedStatisticsTest {
private final Context context = ApplicationProvider.getApplicationContext();
/**
* Create a TrackStatistics object.
*
* @param totalDistance distance in meters.
* @param totalTime total time in milliseconds.
* @return TrackStatistics object.
*/
private static Track createTrack(Context context, long totalDistance, long totalTime, String category) {
TrackStatistics statistics = new TrackStatistics();
statistics.setStartTime_ms(1000L); // Resulting start time
statistics.setStopTime_ms(1000L + totalTime);
statistics.setTotalTime(totalTime);
statistics.setMovingTime(totalTime);
statistics.setTotalDistance(totalDistance);
statistics.setTotalElevationGain(50.0);
statistics.setMaxSpeed(50.0); // Resulting max speed
statistics.setMaxElevation(1250.0);
statistics.setMinElevation(1200.0); // Resulting min elevation
Track track = new Track();
track.setIcon(TrackIconUtils.getIconValue(context, category));
track.setCategory(category);
track.setTrackStatistics(statistics);
return track;
}
@Test
public void testAggregate() {
// given
// 10km in 40 minutes.
long totalDistance = 10000;
long totalTime = 2400000;
String biking = context.getString(R.string.activity_type_biking);
Track track = createTrack(context, totalDistance, totalTime, biking);
// when
AggregatedStatistics aggregatedStatistics = new AggregatedStatistics();
aggregatedStatistics.aggregate(track);
// then
Assert.assertEquals(1, aggregatedStatistics.getCount());
Assert.assertTrue(aggregatedStatistics.get(biking) != null);
Assert.assertEquals(1, aggregatedStatistics.get(biking).getCountTracks());
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
Assert.assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
Assert.assertEquals(totalTime, statistics2.getMovingTime());
}
@Test
public void testAggregate_mountainBiking() {
// given
// 10km in 40 minutes.
long totalDistance = 10000;
long totalTime = 2400000;
String mountainBiking = context.getString(R.string.activity_type_mountain_biking);
Track track = createTrack(context, totalDistance, totalTime, mountainBiking);
// when
AggregatedStatistics aggregatedStatistics = new AggregatedStatistics();
aggregatedStatistics.aggregate(track);
// then
Assert.assertTrue(aggregatedStatistics.get(mountainBiking) != null);
}
@Test
public void testAggregate_trailRunning() {
// given
// 10km in 40 minutes.
long totalDistance = 10000;
long totalTime = 2400000;
String trailRunning = context.getString(R.string.activity_type_trail_running);
Track track = createTrack(context, totalDistance, totalTime, trailRunning);
// when
AggregatedStatistics aggregatedStatistics = new AggregatedStatistics();
aggregatedStatistics.aggregate(track);
// then
Assert.assertTrue(aggregatedStatistics.get(trailRunning) != null);
}
@Test
public void testAggregate_twoBikingTracks() {
// given
// 10km in 40 minutes.
long totalDistance = 10000;
long totalTime = 2400000;
String biking = context.getString(R.string.activity_type_biking);
Track[] tracks = new Track[2];
for (int i = 0; i < 2; i++) {
tracks[i] = createTrack(context, totalDistance, totalTime, biking);
}
// when
AggregatedStatistics aggregatedStatistics = new AggregatedStatistics();
aggregatedStatistics.aggregate(tracks[0]); // biking activity 1.
aggregatedStatistics.aggregate(tracks[1]); // biking activity 2.
// then
Assert.assertEquals(1, aggregatedStatistics.getCount());
Assert.assertTrue(aggregatedStatistics.get(biking) != null);
Assert.assertEquals(2, aggregatedStatistics.get(biking).getCountTracks());
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
Assert.assertEquals(totalDistance * 2, statistics2.getTotalDistance(), 0);
Assert.assertEquals(totalTime * 2L, statistics2.getMovingTime());
}
@Test
public void testAggregate_threeDifferentTracks() {
// given
// 10km in 40 minutes.
long totalDistance = 10000;
String biking = context.getString(R.string.activity_type_biking);
String running = context.getString(R.string.activity_type_running);
String walking = context.getString(R.string.activity_type_walking);
long totalTime = 2400000;
Track[] tracks = new Track[]{
createTrack(context, totalDistance, totalTime, biking),
createTrack(context, totalDistance, totalTime, running),
createTrack(context, totalDistance, totalTime, walking)
};
// when
AggregatedStatistics aggregatedStatistics = new AggregatedStatistics();
aggregatedStatistics.aggregate(tracks[0]); // biking activity 1.
aggregatedStatistics.aggregate(tracks[1]); // biking activity 2.
aggregatedStatistics.aggregate(tracks[2]); // biking activity 3.
// then
Assert.assertEquals(3, aggregatedStatistics.getCount());
Assert.assertTrue(aggregatedStatistics.get(biking) != null);
Assert.assertTrue(aggregatedStatistics.get(running) != null);
Assert.assertTrue(aggregatedStatistics.get(walking) != null);
Assert.assertEquals(1, aggregatedStatistics.get(biking).getCountTracks());
Assert.assertEquals(1, aggregatedStatistics.get(running).getCountTracks());
Assert.assertEquals(1, aggregatedStatistics.get(walking).getCountTracks());
{
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
Assert.assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
Assert.assertEquals(totalTime, statistics2.getMovingTime());
}
{
TrackStatistics statistics2 = aggregatedStatistics.get(running).getTrackStatistics();
Assert.assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
Assert.assertEquals(totalTime, statistics2.getMovingTime());
}
{
TrackStatistics statistics2 = aggregatedStatistics.get(walking).getTrackStatistics();
Assert.assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
Assert.assertEquals(totalTime, statistics2.getMovingTime());
}
}
@Test
public void testAggregate_severalTracksWithSeveralActivities() {
// given
// 10km in 40 minutes.
long totalDistance = 10000;
long totalTime = 2400000;
String biking = context.getString(R.string.activity_type_biking);
String running = context.getString(R.string.activity_type_running);
String walking = context.getString(R.string.activity_type_walking);
String driving = context.getString(R.string.activity_type_driving);
Track[] tracks = new Track[]{
createTrack(context, totalDistance, totalTime, biking),
createTrack(context, totalDistance, totalTime, running),
createTrack(context, totalDistance, totalTime, walking),
createTrack(context, totalDistance, totalTime, biking),
createTrack(context, totalDistance, totalTime, running),
createTrack(context, totalDistance, totalTime, walking),
createTrack(context, totalDistance, totalTime, biking),
createTrack(context, totalDistance, totalTime, biking),
createTrack(context, totalDistance, totalTime, biking),
createTrack(context, totalDistance, totalTime, driving),
};
// when
AggregatedStatistics aggregatedStatistics = new AggregatedStatistics();
for (Track track : tracks) {
aggregatedStatistics.aggregate(track);
}
// then
// 4 sports.
Assert.assertEquals(4, aggregatedStatistics.getCount());
// There is a map for every sport.
Assert.assertTrue(aggregatedStatistics.get(biking) != null);
Assert.assertTrue(aggregatedStatistics.get(running) != null);
Assert.assertTrue(aggregatedStatistics.get(walking) != null);
Assert.assertTrue(aggregatedStatistics.get(driving) != null);
// Number of tracks by sport.
Assert.assertEquals(5, aggregatedStatistics.get(biking).getCountTracks()); // Biking.
Assert.assertEquals(2, aggregatedStatistics.get(running).getCountTracks()); // Running.
Assert.assertEquals(2, aggregatedStatistics.get(walking).getCountTracks()); // Walking.
Assert.assertEquals(1, aggregatedStatistics.get(driving).getCountTracks()); // Driving.
// Biking.
{
TrackStatistics statistics2 = aggregatedStatistics.get(biking).getTrackStatistics();
Assert.assertEquals(totalDistance * 5, statistics2.getTotalDistance(), 0);
Assert.assertEquals(totalTime * 5, statistics2.getMovingTime());
}
// Running.
{
TrackStatistics statistics2 = aggregatedStatistics.get(running).getTrackStatistics();
Assert.assertEquals(totalDistance * 2, statistics2.getTotalDistance(), 0);
Assert.assertEquals(totalTime * 2, statistics2.getMovingTime());
}
// Walking.
{
TrackStatistics statistics2 = aggregatedStatistics.get(walking).getTrackStatistics();
Assert.assertEquals(totalDistance * 2, statistics2.getTotalDistance(), 0);
Assert.assertEquals(totalTime * 2, statistics2.getMovingTime());
}
// Driving.
{
TrackStatistics statistics2 = aggregatedStatistics.get(driving).getTrackStatistics();
Assert.assertEquals(totalDistance, statistics2.getTotalDistance(), 0);
Assert.assertEquals(totalTime, statistics2.getMovingTime());
}
}
}
+33 -16
View File
@@ -16,7 +16,14 @@ limitations under the License.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.dennisguse.opentracks"
android:installLocation="auto">
<!-- Permission to initialize services -->
<supports-screens
android:anyDensity="true"
android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true"
android:xlargeScreens="true" />
<application
android:name=".Startup"
android:allowBackup="false"
@@ -31,15 +38,17 @@ limitations under the License.
<activity
android:name=".TrackDeleteActivity"
android:theme="@style/ThemeCustomTransparent" />
android:theme="@style/TrackDeleteActivity" />
<activity
android:name=".HelpActivity"
android:label="@string/menu_help" />
<activity android:name=".io.file.importer.DirectoryChooserActivity$ImportDirectoryChooserActivity" />
<activity
android:name=".io.file.importer.ImportActivity"
android:theme="@style/ThemeCustomTransparent">
android:theme="@style/ThemeCustomTransparentActivity">
<intent-filter>
<action android:name="android.intent.action.ATTACH_DATA" />
<action android:name="android.intent.action.VIEW" />
@@ -78,13 +87,9 @@ limitations under the License.
android:resource="@xml/searchable" />
</activity>
<activity
android:name=".TrackRecordedActivity"
android:screenOrientation="userPortrait" />
<activity android:name=".TrackRecordedActivity" />
<activity
android:name=".TrackRecordingActivity"
android:screenOrientation="userPortrait" />
<activity android:name=".TrackRecordingActivity" />
<activity android:name=".TrackEditActivity" />
@@ -98,12 +103,29 @@ limitations under the License.
android:value=".SearchListActivity" />
</activity>
<activity android:name=".AggregatedStatisticsActivity" />
<activity android:name=".io.file.importer.DirectoryChooserActivity$ExportDirectoryChooserActivity" />
<activity
android:name=".io.file.exporter.ExportActivity"
android:theme="@style/ThemeCustomTransparent" />
android:theme="@style/ThemeCustomTransparentActivity" />
<activity android:name=".settings.SettingsActivity" />
<activity android:name=".ShowOnMapProxyActivity$KMZ">
<intent-filter android:label="@string/open_track_as_KMZ">
<action android:name="Intent.OpenTracks-Dashboard" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name=".ShowOnMapProxyActivity$GPX">
<intent-filter android:label="@string/open_track_as_GPX">
<action android:name="Intent.OpenTracks-Dashboard" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<provider
android:name=".content.provider.ShareContentProvider"
android:authorities="${applicationId}.content"
@@ -128,12 +150,7 @@ limitations under the License.
android:icon="@drawable/ic_logo_color_24dp"
android:label="@string/recording_service" />
</application>
<supports-screens
android:anyDensity="true"
android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true"
android:xlargeScreens="true" />
<!-- Permissions to record locations -->
<uses-feature
android:name="android.hardware.location.gps"
@@ -62,23 +62,22 @@ public abstract class AbstractListActivity extends AbstractActivity implements C
@Override
public void onConfirmDeleteDone(long[] trackIds) {
boolean stopRecording = false;
if (trackIds.length == 1 && trackIds[0] == -1L) {
stopRecording = true;
} else {
long recordingTrackId = PreferencesUtils.getRecordingTrackId(this);
for (long trackId : trackIds) {
if (trackId == recordingTrackId) {
stopRecording = true;
break;
}
long recordingTrackId = PreferencesUtils.getRecordingTrackId(this);
for (long trackId : trackIds) {
if (trackId == recordingTrackId) {
stopRecording = true;
break;
}
}
if (stopRecording) {
getTrackRecordingServiceConnection().stopRecording(this, false);
}
Intent intent = IntentUtils.newIntent(this, TrackDeleteActivity.class);
intent.putExtra(TrackDeleteActivity.EXTRA_TRACK_IDS, trackIds);
startActivityForResult(intent, DELETE_REQUEST_CODE);
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
}
/**
@@ -0,0 +1,42 @@
package de.dennisguse.opentracks;
import android.os.Bundle;
import android.widget.ListView;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import de.dennisguse.opentracks.adapters.AggregatedStatisticsAdapter;
import de.dennisguse.opentracks.viewmodels.AggregatedStatistics;
import de.dennisguse.opentracks.viewmodels.AggregatedStatisticsModel;
public class AggregatedStatisticsActivity extends AbstractActivity {
private ListView listView;
private AggregatedStatisticsAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
listView = findViewById(R.id.aggregated_stats_list);
listView.setEmptyView(findViewById(R.id.aggregated_stats_empty_view));
final AggregatedStatisticsModel viewModel = new ViewModelProvider(this).get(AggregatedStatisticsModel.class);
viewModel.getAggregatedStats().observe(this, new Observer<AggregatedStatistics>() {
@Override
public void onChanged(AggregatedStatistics aggregatedStatistics) {
if (aggregatedStatistics != null) {
adapter = new AggregatedStatisticsAdapter(getApplicationContext(), aggregatedStatistics);
listView.setAdapter(adapter);
}
adapter.notifyDataSetChanged();
}
});
}
@Override
protected int getLayoutResId() {
return R.layout.aggregated_stats;
}
}
@@ -1,94 +0,0 @@
package de.dennisguse.opentracks;
/*
* Copyright 2013 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
import android.content.Context;
import android.os.AsyncTask;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
/**
* Async Task to delete tracks.
*
* @author Jimmy Shih
*/
class DeleteAsyncTask extends AsyncTask<Void, Integer, Boolean> {
private final long[] trackIds;
private final Context context;
private TrackDeleteActivity deleteActivity;
// true if the AsyncTask has completed
private boolean completed;
/**
* Creates an AsyncTask.
*
* @param deleteActivity the activity currently associated with this task
* @param trackIds the track ids to delete. To delete all, set to size 1 with
* trackIds[0] == -1L
*/
public DeleteAsyncTask(TrackDeleteActivity deleteActivity, long[] trackIds) {
this.deleteActivity = deleteActivity;
this.trackIds = trackIds;
context = deleteActivity.getApplicationContext();
completed = false;
}
/**
* Sets the current activity associated with this AyncTask.
*
* @param deleteActivity the current activity, can be null
*/
public void setActivity(TrackDeleteActivity deleteActivity) {
this.deleteActivity = deleteActivity;
if (completed && deleteActivity != null) {
deleteActivity.onAsyncTaskCompleted();
}
}
@Override
protected void onPreExecute() {
if (deleteActivity != null) {
deleteActivity.showProgressDialog();
}
}
@Override
protected Boolean doInBackground(Void... params) {
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
if (trackIds.length == 1 && trackIds[0] == -1L) {
contentProviderUtils.deleteAllTracks(context);
} else {
for (long id : trackIds) {
if (isCancelled()) {
return false;
}
contentProviderUtils.deleteTrack(context, id);
}
}
return true;
}
@Override
protected void onPostExecute(Boolean result) {
completed = true;
if (deleteActivity != null) {
deleteActivity.onAsyncTaskCompleted();
}
}
}
@@ -109,12 +109,7 @@ public class MarkerDetailActivity extends AbstractActivity implements DeleteMark
@Override
public void onDeleteMarkerDone() {
runOnUiThread(new Runnable() {
@Override
public void run() {
finish();
}
});
runOnUiThread(this::finish);
}
/**
@@ -82,12 +82,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(MarkerListActivity.this);
}
if (key != null) {
runOnUiThread(new Runnable() {
@Override
public void run() {
MarkerListActivity.this.invalidateOptionsMenu();
}
});
runOnUiThread(() -> MarkerListActivity.this.invalidateOptionsMenu());
}
}
};
@@ -153,12 +153,7 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(SearchListActivity.this);
}
if (key != null) {
runOnUiThread(new Runnable() {
@Override
public void run() {
arrayAdapter.notifyDataSetChanged();
}
});
runOnUiThread(() -> arrayAdapter.notifyDataSetChanged());
}
}
};
@@ -346,12 +341,9 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
final List<Map<String, Object>> displayResults = prepareResultsforDisplay(scoredResults);
// Use the UI thread to display the results
runOnUiThread(new Runnable() {
@Override
public void run() {
arrayAdapter.clear();
arrayAdapter.addAll(displayResults);
}
runOnUiThread(() -> {
arrayAdapter.clear();
arrayAdapter.addAll(displayResults);
});
}
@@ -448,12 +440,7 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
@Override
public void onDeleteMarkerDone() {
runOnUiThread(new Runnable() {
@Override
public void run() {
handleIntent(getIntent());
}
});
runOnUiThread(() -> handleIntent(getIntent()));
}
@Override
@@ -463,11 +450,6 @@ public class SearchListActivity extends AbstractListActivity implements DeleteMa
@Override
protected void onDeleted() {
runOnUiThread(new Runnable() {
@Override
public void run() {
handleIntent(getIntent());
}
});
runOnUiThread(() -> handleIntent(getIntent()));
}
}
@@ -0,0 +1,69 @@
package de.dennisguse.opentracks;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Pair;
import androidx.appcompat.app.AppCompatActivity;
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
import de.dennisguse.opentracks.content.provider.ShareContentProvider;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
import de.dennisguse.opentracks.util.IntentDashboardUtils;
/**
* Used to convert IntentDashboardUtils.startDashboard-requests into {@link TrackFileFormat}.
*/
public abstract class ShowOnMapProxyActivity extends AppCompatActivity {
private TrackFileFormat trackFileFormat;
protected ShowOnMapProxyActivity(TrackFileFormat trackFileFormat) {
this.trackFileFormat = trackFileFormat;
}
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
long[] trackIds = IntentDashboardUtils.extractTrackIdsFromIntent(getIntent());
showTrackTrackfileFormat(this, trackFileFormat, trackIds);
finish();
}
/**
* Send intent to show tracks on a map (needs an another app) as KMZ.
*
* @param context the context
* @param trackIds the track ids
*/
private static void showTrackTrackfileFormat(Context context, TrackFileFormat trackFileFormat, long[] trackIds) {
if (trackIds.length == 0) {
return;
}
Intent intent = new Intent(android.content.Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(TrackPointsColumns.TRACKID, trackIds[0]);
Pair<Uri, String> uriAndMime = ShareContentProvider.createURI(trackIds, "SharingTrack", trackFileFormat);
intent.setDataAndType(uriAndMime.first, uriAndMime.second);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
context.startActivity(Intent.createChooser(intent, context.getString(R.string.open_track_as_trackfileformat, trackFileFormat.getExtension())));
}
public static class KMZ extends ShowOnMapProxyActivity {
public KMZ() {
super(TrackFileFormat.KMZ_WITH_TRACKDETAIL);
}
}
public static class GPX extends ShowOnMapProxyActivity {
public GPX() {
super(TrackFileFormat.GPX);
}
}
}
@@ -51,7 +51,6 @@ public class TrackController {
private boolean isPaused;
private long totalTime = 0;
// the timestamp for the toal time
private long totalTimeTimestamp = 0;
private boolean isResumed = false;
@@ -16,26 +16,26 @@
package de.dennisguse.opentracks;
import android.app.Activity;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.PowerManager;
import android.view.View;
import de.dennisguse.opentracks.util.DialogUtils;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.util.SystemUtils;
/**
* An activity for delete tracks.
* An activity for deleting tracks.
*
* @author Jimmy Shih
*/
public class TrackDeleteActivity extends Activity {
public class TrackDeleteActivity extends AbstractActivity {
public static final String EXTRA_TRACK_IDS = "track_ids";
private static final int DIALOG_PROGRESS_ID = 0;
private long[] trackIds;
private DeleteAsyncTask deleteAsyncTask;
private Thread deleteThread;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -44,53 +44,48 @@ public class TrackDeleteActivity extends Activity {
setResult(RESULT_CANCELED);
Intent intent = getIntent();
long[] trackIds = intent.getLongArrayExtra(EXTRA_TRACK_IDS);
trackIds = intent.getLongArrayExtra(EXTRA_TRACK_IDS);
deleteThread = new Thread(() -> {
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(TrackDeleteActivity.this);
Object retained = getLastNonConfigurationInstance();
if (retained instanceof DeleteAsyncTask) {
deleteAsyncTask = (DeleteAsyncTask) retained;
deleteAsyncTask.setActivity(this);
} else {
deleteAsyncTask = new DeleteAsyncTask(this, trackIds);
deleteAsyncTask.execute();
}
PowerManager.WakeLock wakeLock = SystemUtils.acquireWakeLock(TrackDeleteActivity.this, null);
for (long id : trackIds) {
if (Thread.interrupted()) {
break;
}
contentProviderUtils.deleteTrack(TrackDeleteActivity.this, id);
}
wakeLock = SystemUtils.releaseWakeLock(wakeLock);
if (Thread.interrupted()) {
return;
}
runOnUiThread(TrackDeleteActivity.this::onAsyncTaskCompleted);
});
}
@Override
public Object onRetainNonConfigurationInstance() {
deleteAsyncTask.setActivity(null);
return deleteAsyncTask;
protected void onStart() {
super.onStart();
deleteThread.start();
}
@Override
protected Dialog onCreateDialog(int id) {
if (id != DIALOG_PROGRESS_ID) {
return null;
}
return DialogUtils.createSpinnerProgressDialog(
this, R.string.track_delete_progress_message, new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
deleteAsyncTask.cancel(true);
dialog.dismiss();
finish();
}
});
protected void onStop() {
super.onStop();
deleteThread.interrupt();
}
@Override
protected int getLayoutResId() {
return R.layout.track_delete;
}
/**
* Invokes when the associated AsyncTask completes.
*/
public void onAsyncTaskCompleted() {
removeDialog(DIALOG_PROGRESS_ID);
findViewById(R.id.progressbar).setVisibility(View.INVISIBLE);
setResult(RESULT_OK);
finish();
}
/**
* Shows the progress dialog.
*/
public void showProgressDialog() {
showDialog(DIALOG_PROGRESS_ID);
}
}
@@ -56,6 +56,7 @@ import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.services.TrackRecordingServiceInterface;
import de.dennisguse.opentracks.settings.SettingsActivity;
import de.dennisguse.opentracks.util.ActivityUtils;
import de.dennisguse.opentracks.util.IntentDashboardUtils;
import de.dennisguse.opentracks.util.IntentUtils;
import de.dennisguse.opentracks.util.ListItemUtils;
import de.dennisguse.opentracks.util.PreferencesUtils;
@@ -143,14 +144,11 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(TrackListActivity.this);
}
if (key != null) {
runOnUiThread(new Runnable() {
@Override
public void run() {
TrackListActivity.this.invalidateOptionsMenu();
LoaderManager.getInstance(TrackListActivity.this).restartLoader(0, null, loaderCallbacks);
boolean isRecording = PreferencesUtils.isRecording(recordingTrackId);
trackController.update(isRecording, recordingTrackPaused);
}
runOnUiThread(() -> {
TrackListActivity.this.invalidateOptionsMenu();
LoaderManager.getInstance(TrackListActivity.this).restartLoader(0, null, loaderCallbacks);
boolean isRecording = PreferencesUtils.isRecording(recordingTrackId);
trackController.update(isRecording, recordingTrackPaused);
});
}
}
@@ -174,15 +172,8 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
private final Runnable bindChangedCallback = new Runnable() {
@Override
public void run() {
/*
* After binding changes (e.g., becomes available), update the total time in trackController.
*/
runOnUiThread(new Runnable() {
@Override
public void run() {
trackController.update(PreferencesUtils.isRecording(recordingTrackId), recordingTrackPaused);
}
});
// After binding changes (e.g., becomes available), update the total time in trackController.
runOnUiThread(() -> trackController.update(PreferencesUtils.isRecording(recordingTrackId), recordingTrackPaused));
if (!startGps) {
return;
@@ -404,6 +395,10 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
this.invalidateOptionsMenu();
}
return true;
case R.id.track_list_aggregated_stats:
intent = IntentUtils.newIntent(this, AggregatedStatisticsActivity.class);
startActivity(intent);
return true;
case R.id.track_list_markers:
intent = IntentUtils.newIntent(this, MarkerListActivity.class);
startActivity(intent);
@@ -487,7 +482,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
Intent intent;
switch (itemId) {
case R.id.list_context_menu_show_on_map:
IntentUtils.showTrackOnMap(this, trackIds);
IntentDashboardUtils.startDashboard(this, trackIds);
return true;
case R.id.list_context_menu_share:
intent = IntentUtils.newShareFileIntent(this, trackIds);
@@ -500,9 +495,6 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
startActivity(intent);
return true;
case R.id.list_context_menu_delete:
if (trackIds.length > 1 && trackIds.length == listView.getCount()) {
trackIds = new long[]{-1L};
}
deleteTracks(trackIds);
return true;
case R.id.list_context_menu_select_all:
@@ -16,7 +16,6 @@
package de.dennisguse.opentracks;
import android.app.ActivityOptions;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
@@ -40,6 +39,7 @@ import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
import de.dennisguse.opentracks.fragments.StatisticsRecordedFragment;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.settings.SettingsActivity;
import de.dennisguse.opentracks.util.IntentDashboardUtils;
import de.dennisguse.opentracks.util.IntentUtils;
import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.util.TrackIconUtils;
@@ -187,7 +187,7 @@ public class TrackRecordedActivity extends AbstractListActivity implements Choos
startActivity(intent);
return true;
case R.id.track_detail_menu_show_on_map:
IntentUtils.showTrackOnMap(this, new long[]{trackId});
IntentDashboardUtils.startDashboard(this, trackId);
return true;
case R.id.track_detail_markers:
intent = IntentUtils.newIntent(this, MarkerListActivity.class)
@@ -226,12 +226,7 @@ public class TrackRecordedActivity extends AbstractListActivity implements Choos
@Override
protected void onDeleted() {
runOnUiThread(new Runnable() {
@Override
public void run() {
finish();
}
});
runOnUiThread(this::finish);
}
/**
@@ -259,7 +254,6 @@ public class TrackRecordedActivity extends AbstractListActivity implements Choos
if (trackId == -1L) {
Log.e(TAG, "TrackDetailActivity needs EXTRA_TRACK_ID.");
finish();
return;
}
}
@@ -1,6 +1,5 @@
package de.dennisguse.opentracks;
import android.app.ActivityOptions;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
@@ -12,10 +11,10 @@ import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager;
@@ -30,6 +29,7 @@ import de.dennisguse.opentracks.fragments.StatisticsRecordingFragment;
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
import de.dennisguse.opentracks.services.TrackRecordingServiceInterface;
import de.dennisguse.opentracks.settings.SettingsActivity;
import de.dennisguse.opentracks.util.IntentDashboardUtils;
import de.dennisguse.opentracks.util.IntentUtils;
import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.util.TrackIconUtils;
@@ -41,6 +41,8 @@ import de.dennisguse.opentracks.util.TrackUtils;
* @author Leif Hendrik Wilden
* @author Rodrigo Damazio
*/
//NOTE: This activity does NOT react to preference changes of R.string.recording_track_id_key.
//This mode of communication should be removed anyhow.
public class TrackRecordingActivity extends AbstractActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller, TrackActivityDataHubInterface {
public static final String EXTRA_TRACK_ID = "track_id";
@@ -57,47 +59,34 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
private ViewPager pager;
private TrackController trackController;
// From intent
// Initialized from Intent; if a new track recording is started, a new TrackId will be provided by TrackRecordingService
private long trackId;
// Preferences
private long recordingTrackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
private boolean recordingTrackPaused;
private final Runnable bindChangedCallback = new Runnable() {
@Override
public void run() {
// After binding changes (is available), update the total time in trackController.
runOnUiThread(new Runnable() {
@Override
public void run() {
trackController.update(true, recordingTrackPaused);
}
});
if (recordingTrackId == -1L) {
TrackRecordingServiceInterface service = trackRecordingServiceConnection.getServiceIfBound();
if (service == null) {
Log.d(TAG, "could not get TrackRecordingService");
return;
}
runOnUiThread(() -> trackController.update(true, recordingTrackPaused));
TrackRecordingServiceInterface service = trackRecordingServiceConnection.getServiceIfBound();
if (service == null) {
Log.d(TAG, "could not get TrackRecordingService");
return;
}
if (!service.isRecording()) {
// Starts or resumes a track.
int msg;
if (trackId == -1L) {
// trackId isn't initialized -> leads a new recording.
trackId = service.startNewTrack();
recordingTrackId = trackId;
msg = R.string.track_detail_record_success;
} else {
// trackId is initialized -> resumes the track.
recordingTrackId = trackId;
service.resumeTrack(trackId);
msg = R.string.track_detail_resume_success;
}
// A recording track is on.
Toast.makeText(TrackRecordingActivity.this, msg, Toast.LENGTH_SHORT).show();
trackDataHub.loadTrack(trackId);
trackController.update(true, false);
trackController.onResume(true, recordingTrackPaused);
@@ -108,12 +97,6 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
if (PreferencesUtils.isKey(TrackRecordingActivity.this, R.string.recording_track_id_key, key)) {
recordingTrackId = PreferencesUtils.getRecordingTrackId(TrackRecordingActivity.this);
setLockscreenPolicy();
setScreenOnPolicy();
}
if (PreferencesUtils.isKey(TrackRecordingActivity.this, R.string.recording_track_paused_key, key)) {
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(TrackRecordingActivity.this);
setLockscreenPolicy();
@@ -130,12 +113,9 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
if (key == null) return;
runOnUiThread(new Runnable() {
@Override
public void run() {
TrackRecordingActivity.this.invalidateOptionsMenu();
trackController.update(true, recordingTrackPaused);
}
runOnUiThread(() -> {
TrackRecordingActivity.this.invalidateOptionsMenu();
trackController.update(true, recordingTrackPaused);
});
}
};
@@ -176,52 +156,27 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
recordingTrackPaused = PreferencesUtils.isRecordingTrackPausedDefault(this);
contentProviderUtils = new ContentProviderUtils(this);
handleIntent(getIntent());
trackId = PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
if (savedInstanceState != null) {
//Activity was recreated.
trackId = savedInstanceState.getLong(EXTRA_TRACK_ID, PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
} else {
trackId = getIntent().getLongExtra(EXTRA_TRACK_ID, PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
if (trackId != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT && contentProviderUtils.getTrack(trackId) == null) {
finish();
}
}
recordingTrackPaused = PreferencesUtils.isRecordingTrackPausedDefault(this);
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback);
trackDataHub = new TrackDataHub(this);
FragmentPagerAdapter adapter = new FragmentPagerAdapter(getSupportFragmentManager(), 1) {
@Override
public int getCount() {
return 3;
}
@NonNull
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new StatisticsRecordingFragment();
case 1:
return ChartFragment.newInstance(false);
case 2:
return ChartFragment.newInstance(true);
default:
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
}
}
@Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return getString(R.string.track_detail_stats_tab);
case 1:
return getString(R.string.settings_chart_by_time);
case 2:
return getString(R.string.settings_chart_by_distance);
}
return "Unknown Tab";
}
};
pager = findViewById(R.id.track_detail_activity_view_pager);
pager.setAdapter(adapter);
pager.setAdapter(new CustomFragmentPagerAdapter(getSupportFragmentManager(), 1));
TabLayout tabs = findViewById(R.id.track_detail_activity_tablayout);
tabs.setupWithViewPager(pager);
if (savedInstanceState != null) {
@@ -285,7 +240,20 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
trackController.onResume(true, recordingTrackPaused);
}
startRecording();
/*
* If the binding has happened, then invoke the callback to start a new recording.
* If the binding hasn't happened, then invoking the callback will have no effect.
* But when the binding occurs, the callback will get invoked.
*/
trackRecordingServiceConnection.startAndBind(this);
bindChangedCallback.run();
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(CURRENT_TAB_TAG_KEY, pager.getCurrentItem());
outState.putLong(EXTRA_TRACK_ID, trackId);
}
@Override
@@ -302,24 +270,11 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
trackDataHub.stop();
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(CURRENT_TAB_TAG_KEY, pager.getCurrentItem());
}
@Override
protected int getLayoutResId() {
return R.layout.track_record;
}
@Override
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
handleIntent(intent);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.track_record, menu);
@@ -347,7 +302,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
startActivity(intent);
return true;
case R.id.track_detail_menu_show_on_map:
IntentUtils.showTrackOnMap(this, new long[]{trackId});
IntentDashboardUtils.startDashboard(this, trackId);
return true;
case R.id.track_detail_markers:
intent = IntentUtils.newIntent(this, MarkerListActivity.class)
@@ -376,22 +331,6 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
return trackDataHub;
}
private void handleIntent(Intent intent) {
trackId = intent.getLongExtra(EXTRA_TRACK_ID, -1L);
if (trackId == -1L) {
return;
}
Track track = contentProviderUtils.getTrack(trackId);
if (track == null) {
track = contentProviderUtils.getLastTrack();
if (track != null) {
trackId = track.getId();
return;
}
finish();
}
}
/**
* Updates the menu items.
*/
@@ -412,14 +351,44 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
TrackUtils.updateTrack(this, track, null, category, null, contentProviderUtils);
}
private void startRecording() {
trackRecordingServiceConnection.startAndBind(this);
private class CustomFragmentPagerAdapter extends FragmentPagerAdapter {
/*
* If the binding has happened, then invoke the callback to start a new recording.
* If the binding hasn't happened, then invoking the callback will have no effect.
* But when the binding occurs, the callback will get invoked.
*/
bindChangedCallback.run();
public CustomFragmentPagerAdapter(@NonNull FragmentManager fm, int behavior) {
super(fm, behavior);
}
@Override
public int getCount() {
return 3;
}
@NonNull
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new StatisticsRecordingFragment();
case 1:
return ChartFragment.newInstance(false);
case 2:
return ChartFragment.newInstance(true);
default:
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
}
}
@Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return getString(R.string.track_detail_stats_tab);
case 1:
return getString(R.string.settings_chart_by_time);
case 2:
return getString(R.string.settings_chart_by_distance);
default:
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
}
}
}
}
@@ -0,0 +1,181 @@
package de.dennisguse.opentracks.adapters;
import android.content.Context;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.util.StringUtils;
import de.dennisguse.opentracks.util.TrackIconUtils;
import de.dennisguse.opentracks.viewmodels.AggregatedStatistics;
public class AggregatedStatisticsAdapter extends BaseAdapter {
private AggregatedStatistics aggregatedStatistics;
private Context context;
public AggregatedStatisticsAdapter(Context context, AggregatedStatistics aggregatedStatistics) {
this.context = context;
this.aggregatedStatistics = aggregatedStatistics;
}
@Override
public int getCount() {
if (aggregatedStatistics != null) {
return aggregatedStatistics.getCount();
}
return 0;
}
@Override
public AggregatedStatistics.AggregatedStatistic getItem(int position) {
return aggregatedStatistics.getItem(position);
}
@Override
public long getItemId(int position) {
// Not important because data are not database register but cooked data.
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
AggregatedStatistics.AggregatedStatistic aggregatedStats = getItem(position);
ViewHolder viewHolder;
if (isSpeedSport(position)) {
viewHolder = new ViewSpeedHolder();
} else {
viewHolder = new ViewPaceHolder();
}
convertView = LayoutInflater.from(context).inflate(R.layout.aggregated_stats_list_item, parent, false);
viewHolder.setValues(convertView, getIcon(position), getName(position), aggregatedStats);
return convertView;
}
private boolean isSpeedSport(int position) {
String category = aggregatedStatistics.getSportName(position);
return TrackIconUtils.isSpeedIcon(context, category);
}
private int getIcon(int position) {
String iconValue = TrackIconUtils.getIconValue(context, aggregatedStatistics.getSportName(position));
return TrackIconUtils.getIconDrawable(iconValue);
}
private String getName(int position) {
return aggregatedStatistics.getSportName(position);
}
private class ViewHolder {
protected ImageView sportIcon;
protected TextView typeLabel;
protected TextView numTracks;
protected TextView distance;
protected TextView distanceUnit;
protected TextView time;
protected boolean metricsUnits;
public ViewHolder() {
metricsUnits = PreferencesUtils.isMetricUnits(context);
}
public void setValues(View view, int iconDrawable, String name, AggregatedStatistics.AggregatedStatistic aggregatedStats) {
sportIcon = view.findViewById(R.id.aggregated_stats_sport_icon);
typeLabel = view.findViewById(R.id.aggregated_stats_type_label);
numTracks = view.findViewById(R.id.aggregated_stats_num_tracks);
distance = view.findViewById(R.id.aggregated_stats_distance);
distanceUnit = view.findViewById(R.id.aggregated_stats_distance_unit);
time = view.findViewById(R.id.aggregated_stats_time);
sportIcon.setImageResource(iconDrawable);
typeLabel.setText(name);
numTracks.setText(StringUtils.valueInParentheses(String.valueOf(aggregatedStats.getCountTracks())));
Pair<String, String> parts = StringUtils.getDistanceParts(context, aggregatedStats.getTrackStatistics().getTotalDistance(), metricsUnits);
distance.setText(parts.first);
distanceUnit.setText(parts.second);
time.setText(StringUtils.formatElapsedTime(aggregatedStats.getTrackStatistics().getMovingTime()));
}
}
private class ViewSpeedHolder extends ViewHolder {
private TextView avgSpeed;
private TextView avgSpeedUnit;
private TextView avgSpeedLabel;
private TextView maxSpeed;
private TextView maxSpeedUnit;
private TextView maxSpeedLabel;
@Override
public void setValues(View view, int iconDrawable, String name, AggregatedStatistics.AggregatedStatistic aggregatedStats) {
super.setValues(view, iconDrawable, name, aggregatedStats);
avgSpeed = view.findViewById(R.id.aggregated_stats_avg_rate);
avgSpeedUnit = view.findViewById(R.id.aggregated_stats_avg_rate_unit);
avgSpeedLabel = view.findViewById(R.id.aggregated_stats_avg_rate_label);
maxSpeed = view.findViewById(R.id.aggregated_stats_max_rate);
maxSpeedUnit = view.findViewById(R.id.aggregated_stats_max_rate_unit);
maxSpeedLabel = view.findViewById(R.id.aggregated_stats_max_rate_label);
{
Pair<String, String> parts = StringUtils.getSpeedParts(context, aggregatedStats.getTrackStatistics().getAverageMovingSpeed(), metricsUnits, true);
avgSpeed.setText(parts.first);
avgSpeedUnit.setText(parts.second);
avgSpeedLabel.setText(context.getString(R.string.stats_average_moving_speed));
}
{
Pair<String, String> parts = StringUtils.getSpeedParts(context, aggregatedStats.getTrackStatistics().getMaxSpeed(), metricsUnits, true);
maxSpeed.setText(parts.first);
maxSpeedUnit.setText(parts.second);
maxSpeedLabel.setText(context.getString(R.string.stats_max_speed));
}
}
}
private class ViewPaceHolder extends ViewHolder {
private TextView avgPace;
private TextView avgPaceUnit;
private TextView avgPaceLabel;
private TextView maxPace;
private TextView maxPaceUnit;
private TextView maxPaceLabel;
@Override
public void setValues(View view, int iconDrawable, String name, AggregatedStatistics.AggregatedStatistic aggregatedStats) {
super.setValues(view, iconDrawable, name, aggregatedStats);
avgPace = view.findViewById(R.id.aggregated_stats_avg_rate);
avgPaceUnit = view.findViewById(R.id.aggregated_stats_avg_rate_unit);
avgPaceLabel = view.findViewById(R.id.aggregated_stats_avg_rate_label);
maxPace = view.findViewById(R.id.aggregated_stats_max_rate);
maxPaceUnit = view.findViewById(R.id.aggregated_stats_max_rate_unit);
maxPaceLabel = view.findViewById(R.id.aggregated_stats_max_rate_label);
{
Pair<String, String> parts = StringUtils.getSpeedParts(context, aggregatedStats.getTrackStatistics().getAverageMovingSpeed(), metricsUnits, false);
avgPace.setText(parts.first);
avgPaceUnit.setText(parts.second);
avgPaceLabel.setText(R.string.stats_average_moving_pace);
}
{
Pair<String, String> parts = StringUtils.getSpeedParts(context, aggregatedStats.getTrackStatistics().getMaxSpeed(), metricsUnits, false);
maxPace.setText(parts.first);
maxPaceUnit.setText(parts.second);
maxPaceLabel.setText(R.string.stats_fastest_pace);
}
}
}
}
@@ -144,12 +144,9 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
PreferencesUtils.register(context, this);
onSharedPreferenceChanged(null, null);
runInHandlerThread(new Runnable() {
@Override
public void run() {
if (started) {
loadDataForAll();
}
runInHandlerThread(() -> {
if (started) {
loadDataForAll();
}
});
}
@@ -178,16 +175,13 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
}
public void loadTrack(final long trackId) {
runInHandlerThread(new Runnable() {
@Override
public void run() {
if (trackId == selectedTrackId) {
Log.i(TAG, "Not reloading track " + trackId);
return;
}
selectedTrackId = trackId;
loadDataForAll();
runInHandlerThread(() -> {
if (trackId == selectedTrackId) {
Log.i(TAG, "Not reloading track " + trackId);
return;
}
selectedTrackId = trackId;
loadDataForAll();
});
}
@@ -197,13 +191,10 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
* @param trackDataListener the track data listener
*/
public void registerTrackDataListener(final TrackDataListener trackDataListener, final boolean tracksTable, final boolean waypointsTable, final boolean trackPointsTable_SampleIn, final boolean trackPointsTable_SampleOut) {
runInHandlerThread(new Runnable() {
@Override
public void run() {
trackDataManager.registerTrackDataListener(trackDataListener, tracksTable, waypointsTable, trackPointsTable_SampleIn, trackPointsTable_SampleOut);
if (started) {
loadDataForListener(trackDataListener);
}
runInHandlerThread(() -> {
trackDataManager.registerTrackDataListener(trackDataListener, tracksTable, waypointsTable, trackPointsTable_SampleIn, trackPointsTable_SampleOut);
if (started) {
loadDataForListener(trackDataListener);
}
});
}
@@ -214,12 +205,7 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
* @param trackDataListener the track data listener
*/
public void unregisterTrackDataListener(final TrackDataListener trackDataListener) {
runInHandlerThread(new Runnable() {
@Override
public void run() {
trackDataManager.unregisterTrackDataListener(trackDataListener);
}
});
runInHandlerThread(() -> trackDataManager.unregisterTrackDataListener(trackDataListener));
}
/**
@@ -238,15 +224,12 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, final String key) {
runInHandlerThread(new Runnable() {
@Override
public void run() {
if (PreferencesUtils.isKey(context, R.string.recording_track_id_key, key)) {
recordingTrackId = PreferencesUtils.getRecordingTrackId(context);
}
if (PreferencesUtils.isKey(context, R.string.recording_track_paused_key, key)) {
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(context);
}
runInHandlerThread(() -> {
if (PreferencesUtils.isKey(context, R.string.recording_track_id_key, key)) {
recordingTrackId = PreferencesUtils.getRecordingTrackId(context);
}
if (PreferencesUtils.isKey(context, R.string.recording_track_paused_key, key)) {
recordingTrackPaused = PreferencesUtils.isRecordingTrackPaused(context);
}
});
}
@@ -289,7 +272,7 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
if (isOnlyListener) {
resetSamplingState();
}
Set<TrackDataListener> sampledOutListeners = hasSampledOut ? trackDataListeners : Collections.<TrackDataListener>emptySet();
Set<TrackDataListener> sampledOutListeners = hasSampledOut ? trackDataListeners : Collections.emptySet();
notifyTrackPointsTableUpdate(isOnlyListener, trackDataListeners, sampledOutListeners);
}
@@ -16,6 +16,8 @@
package de.dennisguse.opentracks.content.data;
import java.util.UUID;
import de.dennisguse.opentracks.stats.TrackStatistics;
/**
@@ -27,6 +29,8 @@ import de.dennisguse.opentracks.stats.TrackStatistics;
public class Track {
private long id = -1L;
private UUID uuid = UUID.randomUUID();
private String name = "";
private String description = "";
private String category = "";
@@ -46,6 +50,14 @@ public class Track {
this.id = id;
}
public UUID getUuid() {
return uuid;
}
public void setUuid(UUID uuid) {
this.uuid = uuid;
}
public String getName() {
return name;
}
@@ -35,6 +35,7 @@ public interface TracksColumns extends BaseColumns {
String DEFAULT_SORT_ORDER = "_id";
// Columns
String UUID = "uuid";
String NAME = "name"; // track name
String DESCRIPTION = "description"; // track description
String CATEGORY = "category"; // track activity type
@@ -71,5 +72,9 @@ public interface TracksColumns extends BaseColumns {
+ MINELEVATION + " FLOAT, " // min elevation
+ MAXELEVATION + " FLOAT, " // max elevation
+ ELEVATIONGAIN + " FLOAT, " // elevation gain
+ ICON + " TEXT)"; // icon
+ ICON + " TEXT, " // icon
+ UUID + " BLOB)"; // UUID
String CREATE_TABLE_INDEX = "CREATE UNIQUE INDEX " + TABLE_NAME + "_" + UUID + "_index ON " + TABLE_NAME + "(" + UUID + ")";
}
@@ -42,6 +42,7 @@ import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.content.data.WaypointsColumns;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.FileUtils;
import de.dennisguse.opentracks.util.UUIDUtils;
/**
* {@link ContentProviderUtils} implementation.
@@ -99,6 +100,7 @@ public class ContentProviderUtils {
*/
public Track createTrack(Cursor cursor) {
int idIndex = cursor.getColumnIndexOrThrow(TracksColumns._ID);
int uuidIndex = cursor.getColumnIndexOrThrow(TracksColumns.UUID);
int nameIndex = cursor.getColumnIndexOrThrow(TracksColumns.NAME);
int descriptionIndex = cursor.getColumnIndexOrThrow(TracksColumns.DESCRIPTION);
int categoryIndex = cursor.getColumnIndexOrThrow(TracksColumns.CATEGORY);
@@ -118,6 +120,9 @@ public class ContentProviderUtils {
if (!cursor.isNull(idIndex)) {
track.setId(cursor.getLong(idIndex));
}
if (!cursor.isNull(uuidIndex)) {
track.setUuid(UUIDUtils.fromBytes(cursor.getBlob(uuidIndex)));
}
if (!cursor.isNull(nameIndex)) {
track.setName(cursor.getString(nameIndex));
}
@@ -160,9 +165,7 @@ public class ContentProviderUtils {
return track;
}
/**
* Deletes all tracks (including waypoints and trackPoints).
*/
@VisibleForTesting
public void deleteAllTracks(Context context) {
contentResolver.delete(TrackPointsColumns.CONTENT_URI_BY_ID, null, null);
contentResolver.delete(WaypointsColumns.CONTENT_URI, null, null);
@@ -287,6 +290,7 @@ public class ContentProviderUtils {
if (track.getId() >= 0) {
values.put(TracksColumns._ID, track.getId());
}
values.put(TracksColumns.UUID, UUIDUtils.toBytes(track.getUuid()));
values.put(TracksColumns.NAME, track.getName());
values.put(TracksColumns.DESCRIPTION, track.getDescription());
values.put(TracksColumns.CATEGORY, track.getCategory());
@@ -910,5 +914,4 @@ public class ContentProviderUtils {
public static String[] parseTrackIdsFromUri(Uri url) {
return TextUtils.split(url.getLastPathSegment(), ID_SEPARATOR);
}
}
@@ -1,15 +1,20 @@
package de.dennisguse.opentracks.content.provider;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import java.util.UUID;
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
import de.dennisguse.opentracks.content.data.TracksColumns;
import de.dennisguse.opentracks.content.data.WaypointsColumns;
import de.dennisguse.opentracks.util.UUIDUtils;
/**
* Database helper for creating and upgrading the database.
@@ -17,13 +22,11 @@ import de.dennisguse.opentracks.content.data.WaypointsColumns;
@VisibleForTesting
class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
@VisibleForTesting
private static final int DATABASE_VERSION = 25;
private static final String TAG = CustomSQLiteOpenHelper.class.getSimpleName();
@VisibleForTesting
private static final String DATABASE_NAME = "database.db";
private static final int DATABASE_VERSION = 26;
private static final String DATABASE_NAME = "database.db";
public CustomSQLiteOpenHelper(Context context) {
this(context, DATABASE_NAME);
@@ -45,6 +48,7 @@ class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
db.execSQL(TrackPointsColumns.CREATE_TABLE_INDEX);
db.execSQL(TracksColumns.CREATE_TABLE);
db.execSQL(TracksColumns.CREATE_TABLE_INDEX);
db.execSQL(WaypointsColumns.CREATE_TABLE);
db.execSQL(WaypointsColumns.CREATE_TABLE_INDEX);
@@ -61,6 +65,9 @@ class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
case 25:
upgradeFrom24to25(db);
break;
case 26:
upgradeFrom25to26(db);
break;
default:
throw new RuntimeException("Not implemented: upgrade to " + toVersion);
@@ -79,6 +86,9 @@ class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
case 24:
downgradeFrom25to24(db);
break;
case 25:
downgradeFrom26to25(db);
break;
default:
throw new RuntimeException("Not implemented: downgrade to " + toVersion);
@@ -107,7 +117,7 @@ class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
}
private void downgradeFrom24to23(SQLiteDatabase db) {
//Not needed as the delete columns did not contain any data
//Not needed as the deleted columns did not contain any data
db.beginTransaction();
db.execSQL("ALTER TABLE tracks RENAME TO tracks_old");
db.execSQL("CREATE TABLE tracks (_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, description TEXT, category TEXT, starttime INTEGER, stoptime INTEGER, numpoints INTEGER, totaldistance FLOAT, totaltime INTEGER, movingtime INTEGER, avgspeed FLOAT, avgmovingspeed FLOAT, maxspeed FLOAT, minelevation FLOAT, maxelevation FLOAT, elevationgain FLOAT, mingrade FLOAT, maxgrade FLOAT, icon TEXT)");
@@ -142,4 +152,40 @@ class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
db.setTransactionSuccessful();
db.endTransaction();
}
private void upgradeFrom25to26(SQLiteDatabase db) {
db.beginTransaction();
db.execSQL("ALTER TABLE tracks ADD COLUMN uuid BLOB");
try (Cursor cursor = db.query("tracks", new String[]{"_id"}, null, null, null, null, null)) {
if (cursor.moveToFirst()) {
int trackIdIndex = cursor.getColumnIndexOrThrow("_id");
do {
long trackId = cursor.getLong(trackIdIndex);
ContentValues cv = new ContentValues();
cv.put("uuid", UUIDUtils.toBytes(UUID.randomUUID()));
db.update("tracks", cv, "_id = ?", new String[]{String.valueOf(trackId)});
} while (cursor.moveToNext());
}
}
db.execSQL("CREATE UNIQUE INDEX tracks_uuid_index ON tracks(uuid)");
db.setTransactionSuccessful();
db.endTransaction();
}
private void downgradeFrom26to25(SQLiteDatabase db) {
db.beginTransaction();
db.execSQL("DROP INDEX tracks_uuid_index");
db.execSQL("ALTER TABLE tracks RENAME TO tracks_old");
db.execSQL("CREATE TABLE tracks (_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, description TEXT, category TEXT, starttime INTEGER, stoptime INTEGER, numpoints INTEGER, totaldistance FLOAT, totaltime INTEGER, movingtime INTEGER, avgspeed FLOAT, avgmovingspeed FLOAT, maxspeed FLOAT, minelevation FLOAT, maxelevation FLOAT, elevationgain FLOAT, mingrade FLOAT, maxgrade FLOAT, icon TEXT)");
db.execSQL("INSERT INTO tracks SELECT _id, name, description, category, starttime, stoptime, numpoints, totaldistance, totaltime, movingtime, avgspeed, avgmovingspeed, maxspeed, minelevation, maxelevation, elevationgain, 0, 0, icon FROM tracks_old");
db.execSQL("DROP TABLE tracks_old");
db.setTransactionSuccessful();
db.endTransaction();
}
}
@@ -92,7 +92,7 @@ public class ShareContentProvider extends CustomContentProvider implements ICont
static long[] parseURI(Uri uri) {
List<String> uriPaths = uri.getPathSegments();
if (uriPaths == null || uriPaths.size() < 2) {
if (uriPaths == null || uriPaths.size() < 3) {
Log.d(TAG, "URI does not contain any trackIds.");
return new long[]{};
}
@@ -90,12 +90,9 @@ public class ChartFragment extends Fragment implements TrackDataListener {
boolean metricUnits = PreferencesUtils.isMetricUnits(getContext());
if (metricUnits != chartView.getMetricUnits()) {
chartView.setMetricUnits(metricUnits);
runOnUiThread(new Runnable() {
@Override
public void run() {
if (isResumed()) {
chartView.requestLayout();
}
runOnUiThread(() -> {
if (isResumed()) {
chartView.requestLayout();
}
});
}
@@ -106,12 +103,9 @@ public class ChartFragment extends Fragment implements TrackDataListener {
chartView.setReportSpeed(reportSpeed);
chartView.applyReportSpeed();
runOnUiThread(new Runnable() {
@Override
public void run() {
if (isResumed()) {
chartView.requestLayout();
}
runOnUiThread(() -> {
if (isResumed()) {
chartView.requestLayout();
}
});
}
@@ -214,12 +208,9 @@ public class ChartFragment extends Fragment implements TrackDataListener {
trackStatisticsUpdater = startTime != -1L ? new TrackStatisticsUpdater(startTime) : null;
pendingPoints.clear();
chartView.reset();
runOnUiThread(new Runnable() {
@Override
public void run() {
if (isResumed()) {
chartView.resetScroll();
}
runOnUiThread(() -> {
if (isResumed()) {
chartView.resetScroll();
}
});
}
@@ -18,7 +18,6 @@ package de.dennisguse.opentracks.fragments;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import androidx.annotation.NonNull;
@@ -43,7 +42,7 @@ public class ConfirmDeleteDialogFragment extends DialogFragment {
/**
* Create a new instance.
*
* @param trackIds list of track ids to delete. To delete all, set to size 1 with trackIds[0] == -1L
* @param trackIds list of track ids to delete.
*/
public static void showDialog(FragmentManager fragmentManager, long[] trackIds) {
Bundle bundle = new Bundle();
@@ -69,17 +68,14 @@ public class ConfirmDeleteDialogFragment extends DialogFragment {
public Dialog onCreateDialog(Bundle savedInstanceState) {
final long[] trackIds = getArguments().getLongArray(KEY_TRACK_IDS);
int titleId;
int messageId;
titleId = trackIds.length > 1 ? R.string.generic_delete_selected_confirm_title : R.string.track_delete_one_confirm_title;
messageId = trackIds.length > 1 ? R.string.track_delete_multiple_confirm_message : R.string.track_delete_one_confirm_message;
int titleId = trackIds.length > 1 ? R.string.generic_delete_selected_confirm_title : R.string.track_delete_one_confirm_title;
int messageId = trackIds.length > 1 ? R.string.track_delete_multiple_confirm_message : R.string.track_delete_one_confirm_message;
return DialogUtils.createConfirmationDialog(
getActivity(), titleId, getString(messageId), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
caller.onConfirmDeleteDone(trackIds);
}
});
getActivity(),
titleId,
getString(messageId),
(dialog, which) -> caller.onConfirmDeleteDone(trackIds)
);
}
/**
@@ -89,12 +85,6 @@ public class ConfirmDeleteDialogFragment extends DialogFragment {
*/
public interface ConfirmDeleteCaller {
/**
* Called when confirm delete is done.
*
* @param trackIds list of track ids to delete. To delete all, set to size 1
* with trackIds[0] == -1L
*/
void onConfirmDeleteDone(long[] trackIds);
}
}
@@ -80,15 +80,12 @@ public class DeleteMarkerDialogFragment extends DialogFragment {
fragmentActivity, titleId, getString(messageId), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
new Thread(new Runnable() {
@Override
public void run() {
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(fragmentActivity);
for (long markerId : markerIds) {
contentProviderUtils.deleteWaypoint(getContext(), markerId);
}
caller.onDeleteMarkerDone();
new Thread(() -> {
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(fragmentActivity);
for (long markerId : markerIds) {
contentProviderUtils.deleteWaypoint(getContext(), markerId);
}
caller.onDeleteMarkerDone();
}).start();
}
});
@@ -0,0 +1,105 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package de.dennisguse.opentracks.fragments;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager;
import de.dennisguse.opentracks.R;
public class ExportFinishedDialogFragment extends DialogFragment {
private static final String TAG = ExportFinishedDialogFragment.class.getSimpleName();
private static final String EXTRA_DIRECTORY_NAME_KEY = "directory_name";
private static final String EXTRA_EXPORTED_TRACK_COUNT = "exported_track_count";
private static final String EXTRA_TOTAL_TRACK_COUNT = "";
private static final String FINISHED_DIALOG_TAG = "finished_dialog_tag";
private DismissCallback caller;
public static void showDialog(FragmentManager fragmentManager, String directoryDisplayName, int successCount, int fileCount) {
Bundle bundle = new Bundle();
bundle.putString(EXTRA_DIRECTORY_NAME_KEY, directoryDisplayName);
bundle.putInt(EXTRA_EXPORTED_TRACK_COUNT, successCount);
bundle.putInt(EXTRA_TOTAL_TRACK_COUNT, fileCount);
ExportFinishedDialogFragment dialogFragment = new ExportFinishedDialogFragment();
dialogFragment.setArguments(bundle);
dialogFragment.show(fragmentManager, FINISHED_DIALOG_TAG);
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
try {
caller = (DismissCallback) context;
} catch (ClassCastException e) {
throw new ClassCastException(context + " must implement " + DismissCallback.class.getSimpleName());
}
}
@Override
@NonNull
public Dialog onCreateDialog(Bundle savedInstanceState) {
String directoryDisplayName = getArguments().getString(EXTRA_DIRECTORY_NAME_KEY);
int exportedTracksCount = getArguments().getInt(EXTRA_EXPORTED_TRACK_COUNT);
int totalTracksCount = getArguments().getInt(EXTRA_TOTAL_TRACK_COUNT);
int iconId;
int titleId;
String message;
String totalTracks = getResources().getQuantityString(R.plurals.tracks, totalTracksCount, totalTracksCount);
if (exportedTracksCount == totalTracksCount) {
iconId = R.drawable.ic_dialog_success_24dp;
titleId = R.string.generic_success_title;
message = getString(
R.string.export_success, totalTracks, directoryDisplayName);
} else {
iconId = R.drawable.ic_dialog_error_24dp;
titleId = R.string.generic_error_title;
message = getString(R.string.export_error, exportedTracksCount, totalTracks, directoryDisplayName);
}
Dialog alertDialog = new AlertDialog.Builder(getContext()).setCancelable(true)
.setIcon(iconId).setMessage(message)
.setPositiveButton(R.string.generic_ok, (dialog, arg1) -> {
dialog.dismiss();
caller.onDismissed();
}).setTitle(titleId)
.create();
alertDialog.setCanceledOnTouchOutside(false);
return alertDialog;
}
@Override
public void onDetach() {
super.onDetach();
caller = null;
}
public interface DismissCallback {
void onDismissed();
}
}
@@ -0,0 +1,245 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package de.dennisguse.opentracks.fragments;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.documentfile.provider.DocumentFile;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TracksColumns;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
import de.dennisguse.opentracks.util.DialogUtils;
import de.dennisguse.opentracks.util.FileUtils;
public class ExportProgressDialogFragment extends DialogFragment {
private static final String EXTRA_DIRECTORY_URI_KEY = "directory_uri";
private static final String EXTRA_TRACKFILEFORMAT_KEY = "trackfileformat";
private static final String TAG = ExportProgressDialogFragment.class.getSimpleName();
public static final String EXPORT_DIALOG_TAG = "export_dialog_tag";
private DismissCallback caller;
private ExportThread exportThread;
private String directoryDisplayName;
private int trackExportSuccessCount;
private int trackCount;
/**
* Create a new instance.
*/
public static void showDialog(FragmentManager fragmentManager, TrackFileFormat trackFileFormat, Uri documentFileUri) {
Bundle bundle = new Bundle();
bundle.putParcelable(EXTRA_DIRECTORY_URI_KEY, documentFileUri);
bundle.putSerializable(EXTRA_TRACKFILEFORMAT_KEY, trackFileFormat);
ExportProgressDialogFragment dialogFragment = new ExportProgressDialogFragment();
dialogFragment.setArguments(bundle);
dialogFragment.setRetainInstance(true);
dialogFragment.show(fragmentManager, EXPORT_DIALOG_TAG);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// This fragment uses a background thread, so it should not be recreated during activity re-creation.
setRetainInstance(true);
Uri directoryUri = getArguments().getParcelable(EXTRA_DIRECTORY_URI_KEY);
TrackFileFormat trackFileFormat = (TrackFileFormat) getArguments().getSerializable(EXTRA_TRACKFILEFORMAT_KEY);
DocumentFile documentFile = DocumentFile.fromTreeUri(getContext(), directoryUri);
directoryDisplayName = FileUtils.getPath(documentFile);
exportThread = new ExportThread(trackFileFormat, documentFile);
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
try {
caller = (DismissCallback) context;
} catch (ClassCastException e) {
throw new ClassCastException(context + " must implement " + DismissCallback.class.getSimpleName());
}
}
@Override
@NonNull
public Dialog onCreateDialog(Bundle savedInstanceState) {
ProgressDialog progressDialog = DialogUtils.createHorizontalProgressDialog(getContext(),
R.string.export_progress_message,
dialog -> {
exportThread.interrupt();
dialog.dismiss();
caller.onExportCanceled(directoryDisplayName, trackExportSuccessCount, trackCount);
}, directoryDisplayName);
progressDialog.setIndeterminate(false);
progressDialog.setMax(trackCount);
progressDialog.setProgress(Math.min(trackExportSuccessCount, trackCount));
return progressDialog;
}
@Override
public void onStart() {
super.onStart();
setProgress();
if (!exportThread.isAlive()) {
exportThread.start();
}
}
@Override
public void onPause() {
super.onPause();
if (!exportThread.isAlive()) {
exportThread.interrupt();
}
}
@Override
public void onDetach() {
super.onDetach();
caller = null;
}
private void onProgress(int trackImportSuccessCount, int fileCount) {
this.trackExportSuccessCount = trackImportSuccessCount;
this.trackCount = fileCount;
setProgress();
}
private void setProgress() {
ProgressDialog dialog = (ProgressDialog) getDialog();
if (dialog != null) {
dialog.setIndeterminate(false);
dialog.setMax(trackCount);
dialog.setProgress(Math.min(trackExportSuccessCount, trackCount));
}
}
private void onExportCompleted(int successCount, int fileCount) {
if (caller != null && !isDetached()) {
caller.onExportFinished(directoryDisplayName, successCount, fileCount);
}
dismissAllowingStateLoss();
}
private class ExportThread extends Thread {
private TrackFileFormat trackFileFormat;
private DocumentFile directory;
ExportThread(TrackFileFormat trackFileFormat, DocumentFile directory) {
this.directory = directory;
this.trackFileFormat = trackFileFormat;
}
@Override
public void run() {
Context context = ExportProgressDialogFragment.this.getContext();
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
try (Cursor cursor = contentProviderUtils.getTrackCursor(null, null, TracksColumns._ID)) {
if (cursor == null) {
onExportCompleted(0, 0);
return;
}
trackCount = cursor.getCount();
for (int i = 0; i < trackCount; i++) {
onProgress(i, trackCount);
if (Thread.interrupted()) {
return;
}
cursor.moveToPosition(i);
Track track = contentProviderUtils.createTrack(cursor);
if (track != null && exportTrack(context, track)) {
trackExportSuccessCount++;
}
}
onExportCompleted(trackExportSuccessCount, trackCount);
}
}
private Boolean exportTrack(Context context, Track track) {
TrackExporter trackExporter = trackFileFormat.newTrackExporter(context, new Track[]{track});
//TODO Move to helper function
String fileName = track.getId() + "." + trackFileFormat.getExtension();
// Overwrite a file if it exists; DocumentFile.createFile() creates a new file appending a suffix if the displayname already exists.
DocumentFile file = directory.findFile(fileName);
if (file == null) {
file = directory.createFile(trackFileFormat.getMimeType(), fileName);
}
try (OutputStream outputStream = context.getContentResolver().openOutputStream(file.getUri())) {
if (trackExporter.writeTrack(context, outputStream)) {
return true;
} else {
if (!file.delete()) {
Log.e(TAG, "Unable to delete file");
}
Log.e(TAG, "Unable to export track");
return false;
}
} catch (FileNotFoundException e) {
Log.e(TAG, "Unable to open file " + file.getName(), e);
return false;
} catch (IOException e) {
Log.e(TAG, "Unable to close file output stream", e);
return false;
}
}
}
public interface DismissCallback {
void onExportCanceled(String directoryDisplayName, int successCount, int fileCount);
void onExportFinished(String directoryDisplayName, int successCount, int fileCount);
}
}
@@ -0,0 +1,111 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package de.dennisguse.opentracks.fragments;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager;
import de.dennisguse.opentracks.R;
public class ImportFinishedDialogFragment extends DialogFragment {
private static final String TAG = ImportFinishedDialogFragment.class.getSimpleName();
private static final String EXTRA_DIRECTORY_NAME_KEY = "directory_name";
private static final String EXTRA_IMPORTED_TRACK_COUNT = "imported_track_count";
private static final String EXTRA_FILE_COUNT = "file_count";
private static final String FINISHED_DIALOG_TAG = "finished_dialog_tag";
private DismissCallback caller;
public static void showDialog(FragmentManager fragmentManager, String directoryDisplayName, int successCount, int fileCount) {
Bundle bundle = new Bundle();
bundle.putString(EXTRA_DIRECTORY_NAME_KEY, directoryDisplayName);
bundle.putInt(EXTRA_IMPORTED_TRACK_COUNT, successCount);
bundle.putInt(EXTRA_FILE_COUNT, fileCount);
ImportFinishedDialogFragment dialogFragment = new ImportFinishedDialogFragment();
dialogFragment.setArguments(bundle);
dialogFragment.show(fragmentManager, FINISHED_DIALOG_TAG);
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
try {
caller = (DismissCallback) context;
} catch (ClassCastException e) {
throw new ClassCastException(context + " must implement " + DismissCallback.class.getSimpleName());
}
}
@Override
@NonNull
public Dialog onCreateDialog(Bundle savedInstanceState) {
String directoryDisplayName = getArguments().getString(EXTRA_DIRECTORY_NAME_KEY);
int importedTrackCount = getArguments().getInt(EXTRA_IMPORTED_TRACK_COUNT);
int totalTrackCount = getArguments().getInt(EXTRA_FILE_COUNT);
int iconId;
int titleId;
String message;
String totalFiles = getResources().getQuantityString(R.plurals.files, totalTrackCount, totalTrackCount);
if (importedTrackCount == totalTrackCount) {
if (totalTrackCount == 0) {
iconId = R.drawable.ic_dialog_info_24dp;
titleId = R.string.import_no_file_title;
message = getString(R.string.import_no_file, directoryDisplayName);
} else {
iconId = R.drawable.ic_dialog_success_24dp;
titleId = R.string.generic_success_title;
message = getString(R.string.import_success, totalFiles, directoryDisplayName);
}
} else {
iconId = R.drawable.ic_dialog_error_24dp;
titleId = R.string.generic_error_title;
message = getString(R.string.import_error, importedTrackCount, totalFiles, directoryDisplayName);
}
Dialog dialog = new AlertDialog.Builder(getContext())
.setCancelable(true).setIcon(iconId)
.setMessage(message)
.setPositiveButton(R.string.generic_ok, (dialogInterface, which) -> {
dialogInterface.dismiss();
caller.onDismissed();
})
.setTitle(titleId)
.create();
dialog.setCanceledOnTouchOutside(false);
return dialog;
}
@Override
public void onDetach() {
super.onDetach();
caller = null;
}
public interface DismissCallback {
void onDismissed();
}
}
@@ -0,0 +1,226 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package de.dennisguse.opentracks.fragments;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.documentfile.provider.DocumentFile;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
import de.dennisguse.opentracks.io.file.importer.GpxFileTrackImporter;
import de.dennisguse.opentracks.io.file.importer.KmlFileTrackImporter;
import de.dennisguse.opentracks.io.file.importer.KmzTrackImporter;
import de.dennisguse.opentracks.io.file.importer.TrackImporter;
import de.dennisguse.opentracks.util.DialogUtils;
import de.dennisguse.opentracks.util.FileUtils;
public class ImportProgressDialogFragment extends DialogFragment {
private static final String EXTRA_DIRECTORY_URI_KEY = "directory_uri";
private static final String TAG = ImportProgressDialogFragment.class.getSimpleName();
public static final String IMPORT_DIALOG_TAG = "import_dialog_tag";
private DismissCallback caller;
private ImportThread importThread;
private String directoryDisplayName;
private int trackImportSuccessCount;
private int fileCount;
/**
* Create a new instance.
*/
public static void showDialog(FragmentManager fragmentManager, Uri documentFileUri) {
Bundle bundle = new Bundle();
bundle.putParcelable(EXTRA_DIRECTORY_URI_KEY, documentFileUri);
ImportProgressDialogFragment dialogFragment = new ImportProgressDialogFragment();
dialogFragment.setArguments(bundle);
dialogFragment.setRetainInstance(true);
dialogFragment.show(fragmentManager, IMPORT_DIALOG_TAG);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// This fragment uses a background thread, so it should not be recreated during activity re-creation.
setRetainInstance(true);
Uri directoryUri = getArguments().getParcelable(EXTRA_DIRECTORY_URI_KEY);
DocumentFile documentFile = DocumentFile.fromTreeUri(getContext(), directoryUri);
directoryDisplayName = FileUtils.getPath(documentFile);
importThread = new ImportThread(documentFile);
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
try {
caller = (DismissCallback) context;
} catch (ClassCastException e) {
throw new ClassCastException(context + " must implement " + DismissCallback.class.getSimpleName());
}
}
@Override
@NonNull
public Dialog onCreateDialog(Bundle savedInstanceState) {
ProgressDialog progressDialog = DialogUtils.createHorizontalProgressDialog(
getContext(), R.string.import_progress_message,
dialogInterface -> {
importThread.interrupt();
dialogInterface.dismiss();
caller.onImportCanceled(directoryDisplayName, trackImportSuccessCount, fileCount);
}, directoryDisplayName);
progressDialog.setIndeterminate(false);
progressDialog.setMax(fileCount);
progressDialog.setProgress(Math.min(trackImportSuccessCount, fileCount));
return progressDialog;
}
@Override
public void onStart() {
super.onStart();
setProgress();
if (!importThread.isAlive()) {
importThread.start();
}
}
@Override
public void onPause() {
super.onPause();
if (!importThread.isAlive()) {
importThread.interrupt();
}
}
@Override
public void onDetach() {
super.onDetach();
caller = null;
}
private void onProgress(int trackImportSuccessCount, int fileCount) {
this.trackImportSuccessCount = trackImportSuccessCount;
this.fileCount = fileCount;
setProgress();
}
private void setProgress() {
ProgressDialog dialog = (ProgressDialog) getDialog();
if (dialog != null) {
dialog.setIndeterminate(false);
dialog.setMax(fileCount);
dialog.setProgress(Math.min(trackImportSuccessCount, fileCount));
}
}
private void onImportCompleted(int successCount, int fileCount) {
if (caller != null && !isDetached()) {
caller.onImportFinished(directoryDisplayName, successCount, fileCount);
}
dismissAllowingStateLoss();
}
private class ImportThread extends Thread {
private DocumentFile file;
ImportThread(DocumentFile file) {
this.file = file;
}
@Override
public void run() {
Context context = ImportProgressDialogFragment.this.getContext();
List<DocumentFile> files = FileUtils.getFiles(file);
int importedTrackCount = 0;
for (int i = 0; i < files.size(); i++) {
onProgress(i, files.size());
if (Thread.interrupted()) {
return;
}
if (importFile(context, files.get(i))) {
importedTrackCount++;
}
}
onImportCompleted(importedTrackCount, files.size());
}
/**
* Imports a file.
*
* @param file the file
*/
private boolean importFile(final Context context, final DocumentFile file) {
TrackImporter trackImporter;
String fileExtension = FileUtils.getExtension(file);
if (TrackFileFormat.GPX.getExtension().equals(fileExtension)) {
trackImporter = new GpxFileTrackImporter(context);
} else if (TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.getExtension().equals(fileExtension)) {
trackImporter = new KmlFileTrackImporter(ImportProgressDialogFragment.this.getContext(), -1L);
} else if (TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES.getExtension().equals(fileExtension)) {
trackImporter = new KmzTrackImporter(context, -1L, file.getUri());
} else {
Log.i(TAG, "Unsupported file format.");
return false;
}
try (InputStream inputStream = context.getContentResolver().openInputStream(file.getUri())) {
return trackImporter.importFile(inputStream) != -1L;
} catch (IOException e) {
Log.e(TAG, "Unable to import file", e);
return false;
}
}
}
public interface DismissCallback {
void onImportCanceled(String directoryDisplayName, int successCount, int fileCount);
void onImportFinished(String directoryDisplayName, int successCount, int fileCount);
}
}
@@ -59,12 +59,9 @@ public class StatisticsRecordedFragment extends Fragment implements TrackDataLis
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
if (PreferencesUtils.isKey(getContext(), R.string.stats_units_key, key) || PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key)) {
if (isResumed()) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
if (isResumed()) {
updateUI();
}
getActivity().runOnUiThread(() -> {
if (isResumed()) {
updateUI();
}
});
}
@@ -195,14 +192,11 @@ public class StatisticsRecordedFragment extends Fragment implements TrackDataLis
@Override
public void onTrackUpdated(final Track track) {
if (isResumed()) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
if (isResumed()) {
trackStatistics = track != null ? track.getTrackStatistics() : null;
category = track != null ? track.getCategory() : "";
updateUI();
}
getActivity().runOnUiThread(() -> {
if (isResumed()) {
trackStatistics = track != null ? track.getTrackStatistics() : null;
category = track != null ? track.getCategory() : "";
updateUI();
}
});
}
@@ -68,12 +68,9 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
if (PreferencesUtils.isKey(getContext(), R.string.stats_units_key, key) || PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key)) {
if (isResumed()) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
if (isResumed()) {
updateUI();
}
getActivity().runOnUiThread(() -> {
if (isResumed()) {
updateUI();
}
});
}
@@ -308,14 +305,11 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
@Override
public void onTrackUpdated(final Track track) {
if (isResumed()) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
if (isResumed()) {
lastTrackStatistics = track != null ? track.getTrackStatistics() : null;
category = track != null ? track.getCategory() : "";
updateUI();
}
getActivity().runOnUiThread(() -> {
if (isResumed()) {
lastTrackStatistics = track != null ? track.getTrackStatistics() : null;
category = track != null ? track.getCategory() : "";
updateUI();
}
});
}
@@ -339,24 +333,21 @@ public class StatisticsRecordingFragment extends Fragment implements TrackDataLi
@Override
public void onNewTrackPointsDone() {
if (isResumed()) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
if (isResumed()) {
if (!isSelectedTrackRecording() || isSelectedTrackPaused()) {
getActivity().runOnUiThread(() -> {
if (isResumed()) {
if (!isSelectedTrackRecording() || isSelectedTrackPaused()) {
lastTrackPoint = null;
}
if (lastTrackPoint != null) {
boolean hasFix = !LocationUtils.isLocationOld(lastTrackPoint.getLocation());
boolean hasGoodFix = TrackPointUtils.fulfillsAccuracy(lastTrackPoint, recordingGpsAccuracy);
if (!hasFix || !hasGoodFix) {
lastTrackPoint = null;
}
if (lastTrackPoint != null) {
boolean hasFix = !LocationUtils.isLocationOld(lastTrackPoint.getLocation());
boolean hasGoodFix = TrackPointUtils.fulfillsAccuracy(lastTrackPoint, recordingGpsAccuracy);
if (!hasFix || !hasGoodFix) {
lastTrackPoint = null;
}
}
setLocationValues();
}
setLocationValues();
}
});
}
@@ -16,160 +16,60 @@
package de.dennisguse.opentracks.io.file.exporter;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.documentfile.provider.DocumentFile;
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentActivity;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.fragments.ExportFinishedDialogFragment;
import de.dennisguse.opentracks.fragments.ExportProgressDialogFragment;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
import de.dennisguse.opentracks.util.DialogUtils;
import de.dennisguse.opentracks.util.FileUtils;
import de.dennisguse.opentracks.util.PreferencesUtils;
/**
* An activity for saving tracks to the external storage.
*
* @author Rodrigo Damazio
*/
public class ExportActivity extends FragmentActivity {
public class ExportActivity extends FragmentActivity implements ExportProgressDialogFragment.DismissCallback, ExportFinishedDialogFragment.DismissCallback {
private static final int DIRECTORY_PICKER_REQUEST_CODE = 6;
public static final String EXTRA_DIRECTORY_URI_KEY = "directory_uri";
private static final int DIALOG_PROGRESS_ID = 0;
private static final int DIALOG_RESULT_ID = 1;
public static final String EXTRA_TRACKFILEFORMAT_KEY = "trackfileformat";
private String directoryDisplayName;
private ExportAsyncTask exportAsyncTask;
private ProgressDialog progressDialog;
private int processedTrackCount;
private int totalTrackCount;
private static final String BUNDLE_ACTIVITY_RECREATED = "activity_recreated";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
startActivityForResult(intent, DIRECTORY_PICKER_REQUEST_CODE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent resultData) {
super.onActivityResult(requestCode, resultCode, resultData);
if (requestCode == DIRECTORY_PICKER_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
Uri directoryUri = resultData.getData();
DocumentFile pickedDirectory = DocumentFile.fromTreeUri(this, directoryUri);
DocumentFile exportDirectory = pickedDirectory.findFile(FileUtils.EXPORT_DIR);
if (exportDirectory == null) {
exportDirectory = pickedDirectory.createDirectory(FileUtils.EXPORT_DIR);
}
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(this);
directoryDisplayName = FileUtils.getPath(exportDirectory);
exportAsyncTask = new ExportAsyncTask(this, trackFileFormat, exportDirectory);
exportAsyncTask.execute();
} else {
finish();
}
if (savedInstanceState == null || !savedInstanceState.getBoolean(BUNDLE_ACTIVITY_RECREATED, false)) {
Uri directoryUri = getIntent().getParcelableExtra(EXTRA_DIRECTORY_URI_KEY);
TrackFileFormat trackfileformat = (TrackFileFormat) getIntent().getSerializableExtra(EXTRA_TRACKFILEFORMAT_KEY);
ExportProgressDialogFragment.showDialog(getSupportFragmentManager(), trackfileformat, directoryUri);
}
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_PROGRESS_ID:
progressDialog = DialogUtils.createHorizontalProgressDialog(this,
R.string.export_progress_message,
new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
exportAsyncTask.cancel(true);
dialog.dismiss();
finish();
}
}, directoryDisplayName);
return progressDialog;
case DIALOG_RESULT_ID:
int iconId;
int titleId;
String message;
String totalTracks = getResources()
.getQuantityString(R.plurals.tracks, totalTrackCount, totalTrackCount);
if (processedTrackCount == totalTrackCount) {
iconId = R.drawable.ic_dialog_success_24dp;
titleId = R.string.generic_success_title;
message = getString(
R.string.export_success, totalTracks, directoryDisplayName);
} else {
iconId = R.drawable.ic_dialog_error_24dp;
titleId = R.string.generic_error_title;
message = getString(R.string.export_error, processedTrackCount, totalTracks, directoryDisplayName);
}
AlertDialog.Builder builder = new AlertDialog.Builder(this).setCancelable(true)
.setIcon(iconId).setMessage(message)
.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
dialog.dismiss();
finish();
}
}).setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int arg1) {
dialog.dismiss();
finish();
}
}).setTitle(titleId);
return builder.create();
default:
return null;
}
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(BUNDLE_ACTIVITY_RECREATED, true);
}
/**
* Invokes when the associated AsyncTask completes.
*
* @param aSuccessCount the number of tracks successfully saved
* @param aTotalCount the number of tracks to save
*/
public void onAsyncTaskCompleted(int aSuccessCount, int aTotalCount) {
processedTrackCount = aSuccessCount;
totalTrackCount = aTotalCount;
removeDialog(DIALOG_PROGRESS_ID);
showDialog(DIALOG_RESULT_ID);
@Override
public void onExportCanceled(String directoryDisplayName, int successCount, int fileCount) {
onExportFinished(directoryDisplayName, successCount, fileCount);
}
/**
* Shows the progress dialog.
*/
public void showProgressDialog() {
showDialog(DIALOG_PROGRESS_ID);
@Override
public void onExportFinished(final String directoryDisplayName, final int successCount, final int fileCount) {
runOnUiThread(() -> {
ExportFinishedDialogFragment.showDialog(getSupportFragmentManager(), directoryDisplayName, successCount, fileCount);
});
}
/**
* Sets the progress dialog value.
*
* @param number the number of points saved
* @param max the maximum number of points
*/
public void setProgressDialogValue(int number, int max) {
if (progressDialog != null) {
progressDialog.setIndeterminate(false);
progressDialog.setMax(max);
progressDialog.setProgress(Math.min(number, max));
}
@Override
public void onDismissed() {
finish();
}
}
@@ -1,168 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package de.dennisguse.opentracks.io.file.exporter;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.PowerManager.WakeLock;
import android.util.Log;
import androidx.documentfile.provider.DocumentFile;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TracksColumns;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
import de.dennisguse.opentracks.util.SystemUtils;
/**
* Async Task to save tracks to the external storage.
*
* @author Jimmy Shih
*/
//TODO Make independent from ExportActivity?
class ExportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
private static final String TAG = ExportAsyncTask.class.getSimpleName();
private final TrackFileFormat trackFileFormat;
private final DocumentFile directory;
private final ContentProviderUtils contentProviderUtils;
private ExportActivity exportActivity;
private WakeLock wakeLock;
private int processedTrackCount = 0;
private int totalTrackCount = 0;
/**
* Creates an AsyncTask.
*
* @param exportActivity the activity currently associated with this task
* @param trackFileFormat the track file format
* @param directory the directory to write the file
*/
public ExportAsyncTask(ExportActivity exportActivity, TrackFileFormat trackFileFormat, DocumentFile directory) {
this.exportActivity = exportActivity;
this.trackFileFormat = trackFileFormat;
this.directory = directory;
contentProviderUtils = new ContentProviderUtils(exportActivity);
}
@Override
protected void onPreExecute() {
if (exportActivity != null) {
exportActivity.showProgressDialog();
}
}
@Override
protected Boolean doInBackground(Void... params) {
try {
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
wakeLock = SystemUtils.acquireWakeLock(exportActivity, wakeLock);
return exportAllTracks();
} finally {
wakeLock = SystemUtils.releaseWakeLock(wakeLock);
}
}
@Override
protected void onProgressUpdate(Integer... values) {
if (exportActivity != null) {
exportActivity.setProgressDialogValue(values[0], values[1]);
}
}
@Override
protected void onPostExecute(Boolean result) {
if (exportActivity != null) {
exportActivity.onAsyncTaskCompleted(processedTrackCount, totalTrackCount);
}
}
@Override
protected void onCancelled() {
if (exportActivity != null) {
exportActivity.onAsyncTaskCompleted(processedTrackCount, totalTrackCount);
}
}
/**
* Export the track to one file.
*
* @param track the track
*/
private Boolean exportTrack(Track track) {
TrackExporter trackExporter = trackFileFormat.newTrackExporter(exportActivity, new Track[]{track});
String fileName = track.getId() + "." + trackFileFormat.getExtension();
// Overwrite a file if it exists; DocumentFile.createFile() creates a new file appending a suffix if the displayname already exists.
DocumentFile file = directory.findFile(fileName);
if (file == null) {
file = directory.createFile(trackFileFormat.getMimeType(), fileName);
}
try (OutputStream outputStream = exportActivity.getContentResolver().openOutputStream(file.getUri())) {
if (trackExporter.writeTrack(exportActivity, outputStream)) {
return true;
} else {
if (!file.delete()) {
Log.d(TAG, "Unable to delete file");
}
Log.e(TAG, "Unable to export track");
return false;
}
} catch (FileNotFoundException e) {
Log.e(TAG, "Unable to open file " + file.getName(), e);
return false;
} catch (IOException e) {
Log.e(TAG, "Unable to close file output stream", e);
return false;
}
}
/**
* Saves all the tracks.
*/
private Boolean exportAllTracks() {
try (Cursor cursor = contentProviderUtils.getTrackCursor(null, null, TracksColumns._ID)) {
if (cursor == null) {
return false;
}
totalTrackCount = cursor.getCount();
for (int i = 0; i < totalTrackCount; i++) {
if (isCancelled()) {
return false;
}
cursor.moveToPosition(i);
Track track = contentProviderUtils.createTrack(cursor);
if (track != null && exportTrack(track)) {
processedTrackCount++;
}
publishProgress(i + 1, totalTrackCount);
}
return true;
}
}
}
@@ -176,6 +176,7 @@ public class GpxTrackWriter implements TrackWriter {
}
printWriter.println(
"<time>" + StringUtils.formatDateTimeIso8601(trackPoint.getTime()) + "</time>");
printWriter.println("<speed>" + trackPoint.getSpeed() + "</speed>");
printWriter.println("</trkpt>");
}
}
@@ -46,9 +46,10 @@ public class KmlTrackWriter implements TrackWriter {
private static final String TRACK_STYLE = "track";
private static final String SCHEMA_ID = "schema";
public static final String SENSOR_TYPE_CADENCE = "cadence";
public static final String SENSOR_TYPE_HEART_RATE = "heart_rate";
public static final String SENSOR_TYPE_POWER = "power";
public static final String EXTENDED_DATA_TYPE_SPEED = "speed";
public static final String EXTENDED_DATA_TYPE_CADENCE = "cadence";
public static final String EXTENDED_DATA_TYPE_HEART_RATE = "heart_rate";
public static final String EXTENDED_DATA_TYPE_POWER = "power";
private static final String WAYPOINT_ICON = "http://maps.google.com/mapfiles/kml/pushpin/blue-pushpin.png";
private static final String START_ICON = "http://maps.google.com/mapfiles/kml/paddle/grn-circle.png";
@@ -64,6 +65,7 @@ public class KmlTrackWriter implements TrackWriter {
private final ContentProviderUtils contentProviderUtils;
private PrintWriter printWriter;
private final List<Float> speedList = new ArrayList<>();
private final List<Float> powerList = new ArrayList<>();
private final List<Float> cadenceList = new ArrayList<>();
private final List<Float> heartRateList = new ArrayList<>();
@@ -123,10 +125,12 @@ public class KmlTrackWriter implements TrackWriter {
writePlacemarkerStyle(WAYPOINT_STYLE, WAYPOINT_ICON, 20, 2);
printWriter.println("<Schema id=\"" + SCHEMA_ID + "\">");
writeSimpleArrayStyle(EXTENDED_DATA_TYPE_SPEED, context.getString(R.string.description_speed_ms));
if (exportSensorData) {
writeSensorStyle(SENSOR_TYPE_POWER, context.getString(R.string.description_sensor_power));
writeSensorStyle(SENSOR_TYPE_CADENCE, context.getString(R.string.description_sensor_cadence));
writeSensorStyle(SENSOR_TYPE_HEART_RATE, context.getString(R.string.description_sensor_heart_rate));
writeSimpleArrayStyle(EXTENDED_DATA_TYPE_POWER, context.getString(R.string.description_sensor_power));
writeSimpleArrayStyle(EXTENDED_DATA_TYPE_CADENCE, context.getString(R.string.description_sensor_cadence));
writeSimpleArrayStyle(EXTENDED_DATA_TYPE_HEART_RATE, context.getString(R.string.description_sensor_heart_rate));
}
printWriter.println("</Schema>");
}
@@ -229,6 +233,7 @@ public class KmlTrackWriter implements TrackWriter {
public void writeOpenSegment() {
if (printWriter != null) {
printWriter.println("<gx:Track>");
speedList.clear();
powerList.clear();
cadenceList.clear();
heartRateList.clear();
@@ -240,15 +245,18 @@ public class KmlTrackWriter implements TrackWriter {
if (printWriter != null) {
printWriter.println("<ExtendedData>");
printWriter.println("<SchemaData schemaUrl=\"#" + SCHEMA_ID + "\">");
if (speedList.size() > 0) {
writeSimpleArrayData(speedList, EXTENDED_DATA_TYPE_SPEED);
}
if (exportSensorData) {
if (powerList.size() > 0) {
writeSensorData(powerList, SENSOR_TYPE_POWER);
writeSimpleArrayData(powerList, EXTENDED_DATA_TYPE_POWER);
}
if (cadenceList.size() > 0) {
writeSensorData(cadenceList, SENSOR_TYPE_CADENCE);
writeSimpleArrayData(cadenceList, EXTENDED_DATA_TYPE_CADENCE);
}
if (heartRateList.size() > 0) {
writeSensorData(heartRateList, SENSOR_TYPE_HEART_RATE);
writeSimpleArrayData(heartRateList, EXTENDED_DATA_TYPE_HEART_RATE);
}
}
printWriter.println("</SchemaData>");
@@ -266,6 +274,10 @@ public class KmlTrackWriter implements TrackWriter {
printWriter.println("<gx:coord>" + getCoordinates(trackPoint.getLocation(), " ") + "</gx:coord>");
if (trackPoint.hasSpeed()) {
speedList.add(trackPoint.getSpeed());
}
if (exportSensorData) {
if (trackPoint.hasHeartRate()) {
heartRateList.add(trackPoint.getHeartRate_bpm());
@@ -281,12 +293,12 @@ public class KmlTrackWriter implements TrackWriter {
}
/**
* Writes the sensor data.
* Writes the simple array data.
*
* @param list a list of sensor data
* @param name the name of the sensor data
* @param list a list of simple array data
* @param name the name of the simple array data
*/
private void writeSensorData(List<Float> list, String name) {
private void writeSimpleArrayData(List<Float> list, String name) {
printWriter.println("<gx:SimpleArrayData name=\"" + name + "\">");
for (int i = 0; i < list.size(); i++) {
printWriter.println("<gx:value>" + list.get(i) + "</gx:value>");
@@ -446,14 +458,14 @@ public class KmlTrackWriter implements TrackWriter {
}
/**
* Writes a sensor style.
* Writes a simple array style.
*
* @param name the name of the sesnor
* @param sensorType the sensor display name
* @param name the name of the simple array.
* @param extendedDataType the extended data display name
*/
private void writeSensorStyle(String name, String sensorType) {
private void writeSimpleArrayStyle(String name, String extendedDataType) {
printWriter.println("<gx:SimpleArrayField name=\"" + name + "\" type=\"float\">");
printWriter.println("<displayName>" + StringUtils.formatCData(sensorType) + "</displayName>");
printWriter.println("<displayName>" + StringUtils.formatCData(extendedDataType) + "</displayName>");
printWriter.println("</gx:SimpleArrayField>");
}
}
@@ -31,6 +31,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
@@ -72,6 +73,7 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
// The current element content
//TODO Should be made private and getter be used by child classes.
protected String content;
protected String icon;
protected String name;
protected String description;
@@ -80,8 +82,10 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
protected String longitude;
protected String altitude;
protected String time;
protected String speed;
protected String waypointType;
protected String photoUrl;
protected String uuid;
// The current track data
private TrackData trackData;
@@ -90,11 +94,11 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
private Locator locator;
/**
* Constructor.
*
* @param context the context
* @param importTrackId the track id to import to. -1L to import to a new track.
*/
@Deprecated
// Remove importTrackId
AbstractFileTrackImporter(Context context, long importTrackId, ContentProviderUtils contentProviderUtils) {
this.context = context;
this.importTrackId = importTrackId;
@@ -243,6 +247,16 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
if (name != null) {
trackData.track.setName(name);
}
UUID uuidParsed;
try {
uuidParsed = UUID.fromString(uuid);
} catch (IllegalArgumentException | NullPointerException e) {
Log.w(TAG, "could not parse Track UUID, generating a new one.");
uuidParsed = UUID.randomUUID();
}
trackData.track.setUuid(uuidParsed);
if (description != null) {
trackData.track.setDescription(description);
}
@@ -319,20 +333,22 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
// Calculate derived attributes from the previous point
if (trackData.lastLocationInCurrentSegment != null && trackData.lastLocationInCurrentSegment.getTime() != 0) {
long timeDifference = trackPoint.getTime() - trackData.lastLocationInCurrentSegment.getTime();
if (!trackPoint.hasSpeed()) {
long timeDifference = trackPoint.getTime() - trackData.lastLocationInCurrentSegment.getTime();
// Check for negative time change
if (timeDifference <= 0) {
Log.w(TAG, "Time difference not postive.");
} else {
// Check for negative time change
if (timeDifference <= 0) {
Log.w(TAG, "Time difference not positive.");
} else {
/*
* We don't have a speed and bearing in GPX, make something up from the last two points.
* GPS points tend to have some inherent imprecision, speed and bearing will likely be off, so the statistics for things like max speed will also be off.
*/
double duration = timeDifference * UnitConversions.MS_TO_S;
double speed = trackData.lastLocationInCurrentSegment.distanceTo(trackPoint) / duration;
trackPoint.setSpeed((float) speed);
/*
* We don't have a speed and bearing in GPX, make something up from the last two points.
* GPS points tend to have some inherent imprecision, speed and bearing will likely be off, so the statistics for things like max speed will also be off.
*/
double duration = timeDifference * UnitConversions.MS_TO_S;
double speed = trackData.lastLocationInCurrentSegment.distanceTo(trackPoint) / duration;
trackPoint.setSpeed((float) speed);
}
}
trackPoint.setBearing(trackData.lastLocationInCurrentSegment.bearingTo(trackPoint));
}
@@ -389,9 +405,8 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
Uri photoUri = FileUtils.getUriForFile(context, file);
return "" + photoUri;
}
else {
return null;
}
return null;
}
/**
@@ -424,11 +439,24 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
} else {
try {
timeValue = StringUtils.parseTime(time);
} catch (IllegalArgumentException e) {
} catch (Exception e) {
throw new SAXException(createErrorMessage(String.format(Locale.US, "Unable to parse time: %s", time)), e);
}
}
return new TrackPoint(latitudeValue, longitudeValue, altitudeValue, timeValue);
TrackPoint trackPoint = new TrackPoint(latitudeValue, longitudeValue, altitudeValue, timeValue);
float speedValue;
if (speed != null) {
try {
speedValue = Float.valueOf(speed);
trackPoint.setSpeed(speedValue);
} catch (Exception e) {
throw new SAXException(createErrorMessage(String.format(Locale.US, "Unable to parse speed: %s", speed)), e);
}
}
return trackPoint;
}
/**
@@ -0,0 +1,66 @@
package de.dennisguse.opentracks.io.file.importer;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import de.dennisguse.opentracks.io.file.exporter.ExportActivity;
import de.dennisguse.opentracks.util.IntentUtils;
import de.dennisguse.opentracks.util.PreferencesUtils;
public abstract class DirectoryChooserActivity extends AppCompatActivity {
private static final int DIRECTORY_PICKER_REQUEST_CODE = 6;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivityForResult(intent, DIRECTORY_PICKER_REQUEST_CODE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent resultData) {
super.onActivityResult(requestCode, resultCode, resultData);
if (requestCode == DIRECTORY_PICKER_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
Uri directoryUri = resultData.getData();
startActivity(createIntent(directoryUri));
}
finish();
}
}
protected abstract Intent createIntent(Uri directoryUri);
public static class ImportDirectoryChooserActivity extends DirectoryChooserActivity {
@Override
protected Intent createIntent(Uri directoryUri) {
Intent intent = IntentUtils.newIntent(this, ImportActivity.class);
intent.putExtra(ImportActivity.EXTRA_DIRECTORY_URI_KEY, directoryUri);
return intent;
}
}
public static class ExportDirectoryChooserActivity extends DirectoryChooserActivity {
@Override
protected Intent createIntent(Uri directoryUri) {
Intent intent = IntentUtils.newIntent(this, ExportActivity.class);
intent.putExtra(ExportActivity.EXTRA_DIRECTORY_URI_KEY, directoryUri);
intent.putExtra(ExportActivity.EXTRA_TRACKFILEFORMAT_KEY, PreferencesUtils.getExportTrackFileFormat(this));
return intent;
}
}
}
@@ -39,6 +39,7 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
private static final String TAG_GPX = "gpx";
private static final String TAG_NAME = "name";
private static final String TAG_TIME = "time";
private static final String TAG_SPEED = "speed";
private static final String TAG_TRACK = "trk";
private static final String TAG_TRACK_POINT = "trkpt";
private static final String TAG_TRACK_SEGMENT = "trkseg";
@@ -115,6 +116,11 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
time = content.trim();
}
break;
case TAG_SPEED:
if (content != null) {
speed = content.trim();
}
break;
case TAG_ELEVATION:
if (content != null) {
altitude = content.trim();
@@ -149,6 +155,7 @@ public class GpxFileTrackImporter extends AbstractFileTrackImporter {
longitude = attributes.getValue(ATTRIBUTE_LON);
altitude = null;
time = null;
speed = null;
}
/**
@@ -16,159 +16,58 @@
package de.dennisguse.opentracks.io.file.importer;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.documentfile.provider.DocumentFile;
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentActivity;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
import de.dennisguse.opentracks.util.DialogUtils;
import de.dennisguse.opentracks.util.FileUtils;
import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.fragments.ImportFinishedDialogFragment;
import de.dennisguse.opentracks.fragments.ImportProgressDialogFragment;
/**
* An activity to import files from the external storage.
*
* @author Rodrigo Damazio
*/
public class ImportActivity extends FragmentActivity {
public class ImportActivity extends FragmentActivity implements ImportProgressDialogFragment.DismissCallback, ImportFinishedDialogFragment.DismissCallback {
private static final int DIRECTORY_PICKER_REQUEST_CODE = 6;
private static final String TAG = ImportActivity.class.getSimpleName();
private static final int DIALOG_PROGRESS_ID = 0;
private static final int DIALOG_RESULT_ID = 1;
public static final String EXTRA_DIRECTORY_URI_KEY = "directory_uri";
private String directoryDisplayName;
private ImportAsyncTask importAsyncTask;
private ProgressDialog progressDialog;
private int importedTrackCount;
private int totalTrackCount;
private static final String BUNDLE_ACTIVITY_RECREATED = "activity_recreated";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivityForResult(intent, DIRECTORY_PICKER_REQUEST_CODE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent resultData) {
super.onActivityResult(requestCode, resultCode, resultData);
if (requestCode == DIRECTORY_PICKER_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
Uri directoryUri = resultData.getData();
DocumentFile pickedDirectory = DocumentFile.fromTreeUri(this, directoryUri);
directoryDisplayName = FileUtils.getPath(pickedDirectory);
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(this);
importAsyncTask = new ImportAsyncTask(this, trackFileFormat, pickedDirectory);
importAsyncTask.execute();
} else {
finish();
}
if (savedInstanceState == null || !savedInstanceState.getBoolean(BUNDLE_ACTIVITY_RECREATED, false)) {
Uri directoryUri = getIntent().getParcelableExtra(EXTRA_DIRECTORY_URI_KEY);
ImportProgressDialogFragment.showDialog(getSupportFragmentManager(), directoryUri);
}
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_PROGRESS_ID:
progressDialog = DialogUtils.createHorizontalProgressDialog(
this, R.string.import_progress_message, new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
importAsyncTask.cancel(true);
dialog.dismiss();
finish();
}
}, directoryDisplayName);
return progressDialog;
case DIALOG_RESULT_ID:
int iconId;
int titleId;
String message;
String totalFiles = getResources()
.getQuantityString(R.plurals.files, totalTrackCount, totalTrackCount);
if (importedTrackCount == totalTrackCount) {
if (totalTrackCount == 0) {
iconId = R.drawable.ic_dialog_info_24dp;
titleId = R.string.import_no_file_title;
message = getString(R.string.import_no_file, directoryDisplayName);
} else {
iconId = R.drawable.ic_dialog_success_24dp;
titleId = R.string.generic_success_title;
message = getString(R.string.import_success, totalFiles, directoryDisplayName);
}
} else {
iconId = R.drawable.ic_dialog_error_24dp;
titleId = R.string.generic_error_title;
message = getString(R.string.import_error, importedTrackCount, totalFiles, directoryDisplayName);
}
return new AlertDialog.Builder(this).setCancelable(true).setIcon(iconId)
.setMessage(message).setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialogInterface) {
dialogInterface.dismiss();
finish();
}
}).setPositiveButton(R.string.generic_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int which) {
dialogInterface.dismiss();
finish();
}
}).setTitle(titleId).create();
default:
return null;
}
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(BUNDLE_ACTIVITY_RECREATED, true);
}
/**
* Invokes when the associated AsyncTask completes.
*
* @param aSuccessCount the number of files successfully imported
* @param aTotalCount the number of files to import
*/
public void onAsyncTaskCompleted(int aSuccessCount, int aTotalCount) {
importedTrackCount = aSuccessCount;
totalTrackCount = aTotalCount;
removeDialog(DIALOG_PROGRESS_ID);
showDialog(DIALOG_RESULT_ID);
@Override
public void onImportCanceled(String directoryDisplayName, int successCount, int fileCount) {
onImportFinished(directoryDisplayName, successCount, fileCount);
}
/**
* Shows the progress dialog.
*/
public void showProgressDialog() {
showDialog(DIALOG_PROGRESS_ID);
@Override
public void onImportFinished(final String directoryDisplayName, final int successCount, final int fileCount) {
runOnUiThread(() -> {
ImportFinishedDialogFragment.showDialog(getSupportFragmentManager(), directoryDisplayName, successCount, fileCount);
});
}
/**
* Sets the progress dialog value.
*
* @param number the number of files imported
* @param max the maximum number of files
*/
public void setProgressDialogValue(int number, int max) {
if (progressDialog != null) {
progressDialog.setIndeterminate(false);
progressDialog.setMax(max);
progressDialog.setProgress(Math.min(number, max));
}
@Override
public void onDismissed() {
finish();
}
}
@@ -1,179 +0,0 @@
/*
* Copyright 2012 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package de.dennisguse.opentracks.io.file.importer;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.PowerManager.WakeLock;
import android.util.Log;
import androidx.documentfile.provider.DocumentFile;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
import de.dennisguse.opentracks.util.FileUtils;
import de.dennisguse.opentracks.util.PreferencesUtils;
import de.dennisguse.opentracks.util.SystemUtils;
/**
* AsyncTask to import files from the external storage.
*
* @author Jimmy Shih
*/
class ImportAsyncTask extends AsyncTask<Void, Integer, Boolean> {
private static final String TAG = ImportAsyncTask.class.getSimpleName();
private final TrackFileFormat trackFileFormat;
private final DocumentFile directory;
// TODO Use weak reference
private ImportActivity importActivity;
private WakeLock wakeLock;
private int importTrackCount;
private int totalTrackCount;
private long lastSuccessfulTrackId;
public ImportAsyncTask(ImportActivity importActivity, TrackFileFormat trackFileFormat, DocumentFile directory) {
this.importActivity = importActivity;
this.trackFileFormat = trackFileFormat;
this.directory = directory;
importTrackCount = 0;
totalTrackCount = 0;
lastSuccessfulTrackId = -1L;
}
@Override
protected void onPreExecute() {
if (importActivity != null) {
importActivity.showProgressDialog();
}
}
/**
* Gets a list of files.
*/
private static List<DocumentFile> getFiles(DocumentFile file, TrackFileFormat trackFileFormat) {
List<DocumentFile> files = new ArrayList<>();
for (DocumentFile candidate : file.listFiles()) {
if (!candidate.isDirectory()) {
String extension = FileUtils.getExtension(candidate.getName());
if (extension != null && trackFileFormat.getExtension().equals(extension)) {
files.add(candidate);
}
} else {
files.addAll(getFiles(candidate, trackFileFormat));
}
}
return files;
}
@Override
protected void onProgressUpdate(Integer... values) {
if (importActivity != null) {
importActivity.setProgressDialogValue(values[0], values[1]);
}
}
@Override
protected void onPostExecute(Boolean result) {
if (importActivity != null) {
importActivity.onAsyncTaskCompleted(importTrackCount, totalTrackCount);
}
}
@Override
protected void onCancelled() {
if (importActivity != null) {
importActivity.onAsyncTaskCompleted(importTrackCount, totalTrackCount);
importActivity = null;
}
}
@Override
protected Boolean doInBackground(Void... params) {
try {
Thread.currentThread().setPriority(Thread.MAX_PRIORITY); // TODO Should be set to previous level at the end of this method.
// Get the wake lock if not recording or paused
boolean isRecording = PreferencesUtils.isRecording(importActivity);
boolean isPaused = PreferencesUtils.isRecordingTrackPaused(importActivity);
if (!isRecording || isPaused) {
wakeLock = SystemUtils.acquireWakeLock(importActivity, wakeLock);
}
List<DocumentFile> files = getFiles(directory, trackFileFormat);
totalTrackCount = files.size();
for (int i = 0; i < totalTrackCount; i++) {
if (isCancelled()) {
// If cancelled, return true to show the number of files imported
return true;
}
if (importFile(files.get(i))) {
importTrackCount++;
}
publishProgress(i + 1, totalTrackCount);
}
return true;
} finally {
if (wakeLock != null && wakeLock.isHeld()) {
wakeLock.release();
}
}
}
/**
* Imports a file.
*
* @param file the file
*/
private boolean importFile(final DocumentFile file) {
TrackImporter trackImporter;
if (trackFileFormat == TrackFileFormat.GPX) {
trackImporter = new GpxFileTrackImporter(importActivity);
} else {
//KML or KMZ
String extension = FileUtils.getExtension(file.getName());
if (TrackFileFormat.KML_ONLY_TRACK.getExtension().equals(extension)) {
trackImporter = new KmlFileTrackImporter(importActivity, -1L);
} else {
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(importActivity);
Uri uri = contentProviderUtils.insertTrack(new Track());
long newId = Long.parseLong(uri.getLastPathSegment());
trackImporter = new KmzTrackImporter(importActivity, newId, file.getUri());
}
}
try (InputStream inputStream = importActivity.getContentResolver().openInputStream(file.getUri())) {
lastSuccessfulTrackId = trackImporter.importFile(inputStream);
return lastSuccessfulTrackId != -1L;
} catch (IOException e) {
Log.e(TAG, "Unable to import file", e);
return false;
}
}
}
@@ -61,18 +61,18 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
private static final String ATTRIBUTE_NAME = "name";
private boolean trackStarted = false;
private String sensorType;
private String extendedDataType;
private ArrayList<TrackPoint> trackPoints;
private ArrayList<Float> speedList;
private ArrayList<Float> cadenceList;
private ArrayList<Float> heartRateList;
private ArrayList<Float> powerList;
/**
* Constructor.
*
* @param context the context
* @param importTrackId track id to import to. -1L to import to a new track.
* @param importTrackId track id to import to. This should not be -1L so that images in the kmz file can be imported.
*/
@Deprecated //TODO Do not pass importTrackId here; get it from somewhere else
public KmlFileTrackImporter(Context context, long importTrackId) {
this(context, importTrackId, new ContentProviderUtils(context));
}
@@ -102,7 +102,7 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
onTrackSegmentStart();
break;
case TAG_GX_SIMPLE_ARRAY_DATA:
onSensorDataStart(attributes);
onExtendedDataStart(attributes);
break;
}
}
@@ -124,7 +124,7 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
} else if (tag.equals(TAG_GX_COORD)) {
onTrackPointEnd();
} else if (tag.equals(TAG_GX_VALUE)) {
onSensorValueEnd();
onExtendedDataValueEnd();
} else if (tag.equals(TAG_NAME)) {
if (content != null) {
name = content.trim();
@@ -209,6 +209,7 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
protected void onTrackSegmentStart() {
super.onTrackSegmentStart();
trackPoints = new ArrayList<>();
speedList = new ArrayList<>();
heartRateList = new ArrayList<>();
cadenceList = new ArrayList<>();
powerList = new ArrayList<>();
@@ -222,6 +223,9 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
for (int i = 0; i < trackPoints.size(); i++) {
TrackPoint trackPoint = trackPoints.get(i);
if (i < speedList.size()) {
trackPoint.setSpeed(speedList.get(i));
}
if (i < heartRateList.size()) {
trackPoint.setHeartRate_bpm(heartRateList.get(i));
}
@@ -229,7 +233,7 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
trackPoint.setCyclingCadence_rpm(cadenceList.get(i));
}
if (i < powerList.size()) {
trackPoint.setPower(cadenceList.get(i));
trackPoint.setPower(powerList.get(i));
}
insertTrackPoint(trackPoint);
@@ -261,16 +265,16 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
}
/**
* On sensor data start. gx:SimpleArrayData start tag.
* On extended data start. gx:SimpleArrayData start tag.
*/
private void onSensorDataStart(Attributes attributes) {
sensorType = attributes.getValue(ATTRIBUTE_NAME);
private void onExtendedDataStart(Attributes attributes) {
extendedDataType = attributes.getValue(ATTRIBUTE_NAME);
}
/**
* On sensor value end. gx:value end tag.
* On extended data value end. gx:value end tag.
*/
private void onSensorValueEnd() throws SAXException {
private void onExtendedDataValueEnd() throws SAXException {
if (content == null) {
return;
}
@@ -284,18 +288,21 @@ public class KmlFileTrackImporter extends AbstractFileTrackImporter {
} catch (NumberFormatException e) {
throw new SAXException(createErrorMessage("Unable to parse gx:value:" + content), e);
}
switch (sensorType) {
case KmlTrackWriter.SENSOR_TYPE_POWER:
switch (extendedDataType) {
case KmlTrackWriter.EXTENDED_DATA_TYPE_SPEED:
speedList.add(value);
break;
case KmlTrackWriter.EXTENDED_DATA_TYPE_POWER:
powerList.add(value);
break;
case KmlTrackWriter.SENSOR_TYPE_HEART_RATE:
case KmlTrackWriter.EXTENDED_DATA_TYPE_HEART_RATE:
heartRateList.add(value);
break;
case KmlTrackWriter.SENSOR_TYPE_CADENCE:
case KmlTrackWriter.EXTENDED_DATA_TYPE_CADENCE:
cadenceList.add(value);
break;
default:
Log.w(TAG, "Data from sensor " + sensorType + " is not (yet) supported.");
Log.w(TAG, "Data from extended data " + extendedDataType + " is not (yet) supported.");
}
}
}
@@ -56,13 +56,12 @@ public class KmzTrackImporter implements TrackImporter {
private Uri uriKmzFile;
/**
* Constructor.
*
* @param context the context
* @param importTrackId track id to import to. This should not be -1L so that images in the kmz file can be imported.
* @param uriFile URI of the kmz file.
*/
KmzTrackImporter(Context context, long importTrackId, Uri uriFile) {
@Deprecated //TODO Do not pass importTrackId here; get it from somewhere else
public KmzTrackImporter(Context context, long importTrackId, Uri uriFile) {
this.context = context;
this.importTrackId = importTrackId;
this.uriKmzFile = uriFile;
@@ -22,7 +22,7 @@ import java.io.InputStream;
*
* @author Jimmy Shih
*/
interface TrackImporter {
public interface TrackImporter {
/**
* Import a file.
@@ -30,5 +30,6 @@ interface TrackImporter {
* @param inputStream the file's input stream
* @return the imported track id or RECORDING_TRACK_ID_DEFAULT.
*/
//TODO Figure out how can make the import an atomic operation (incl. database transaction rollback).
long importFile(InputStream inputStream);
}
@@ -150,12 +150,7 @@ public class TrackRecordingService extends Service {
if (locationExecutorService == null || locationExecutorService.isShutdown() || locationExecutorService.isTerminated()) {
return;
}
locationExecutorService.submit(new Runnable() {
@Override
public void run() {
onLocationChangedAsync(location);
}
});
locationExecutorService.submit(() -> onLocationChangedAsync(location));
}
@Override
@@ -60,20 +60,10 @@ public class SettingsActivity extends AppCompatActivity implements ChooseActivit
@Override
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
if (PreferencesUtils.isKey(getActivity(), R.string.recording_track_id_key, key)) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
updateReset();
}
});
getActivity().runOnUiThread(() -> updateReset());
}
if (PreferencesUtils.isKey(getActivity(), R.string.stats_units_key, key)) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
updateUnits();
}
});
getActivity().runOnUiThread(() -> updateUnits());
}
}
};
@@ -61,13 +61,10 @@ public class BluetoothUtils {
final Object mutex = new Object();
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
adapters.add(BluetoothAdapter.getDefaultAdapter());
synchronized (mutex) {
mutex.notify();
}
handler.post(() -> {
adapters.add(BluetoothAdapter.getDefaultAdapter());
synchronized (mutex) {
mutex.notify();
}
});
@@ -52,17 +52,6 @@ public class DialogUtils {
.setTitle(titleId).create();
}
/**
* Creates a spinner progress dialog.
*
* @param context the context
* @param messageId the progress message id
* @param onCancelListener the cancel listener
*/
public static ProgressDialog createSpinnerProgressDialog(Context context, int messageId, DialogInterface.OnCancelListener onCancelListener) {
return createProgressDialog(true, context, messageId, onCancelListener);
}
/**
* Creates a horizontal progress dialog.
*
@@ -72,27 +61,26 @@ public class DialogUtils {
* @param formatArgs the format arguments for the messageId
*/
public static ProgressDialog createHorizontalProgressDialog(Context context, int messageId, DialogInterface.OnCancelListener onCancelListener, Object... formatArgs) {
return createProgressDialog(false, context, messageId, onCancelListener, formatArgs);
return createProgressDialog(context, messageId, onCancelListener, formatArgs);
}
/**
* Creates a progress dialog.
*
* @param spinner true to use the spinner style
* @param context the context
* @param messageId the progress message id
* @param onCancelListener the cancel listener
* @param formatArgs the format arguments for the message id
*/
private static ProgressDialog createProgressDialog(boolean spinner, final Context context, int messageId, DialogInterface.OnCancelListener onCancelListener, Object... formatArgs) {
final ProgressDialog progressDialog = new ProgressDialog(context, R.style.ThemeCustomNotTransparent);
private static ProgressDialog createProgressDialog(final Context context, int messageId, DialogInterface.OnCancelListener onCancelListener, Object... formatArgs) {
final ProgressDialog progressDialog = new ProgressDialog(context, R.style.ThemeCustomNotTransparentDialog);
progressDialog.setCancelable(true);
progressDialog.setCanceledOnTouchOutside(false);
progressDialog.setIcon(R.drawable.ic_dialog_info_24dp);
progressDialog.setIndeterminate(true);
progressDialog.setMessage(context.getString(messageId, formatArgs));
progressDialog.setOnCancelListener(onCancelListener);
progressDialog.setProgressStyle(spinner ? ProgressDialog.STYLE_SPINNER : ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setTitle(R.string.generic_progress_title);
return progressDialog;
}
@@ -29,6 +29,7 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.channels.FileChannel;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import de.dennisguse.opentracks.BuildConfig;
@@ -111,6 +112,10 @@ public class FileUtils {
return fileName.substring(index + 1);
}
public static String getExtension(DocumentFile file) {
return getExtension(file.getName());
}
/**
* Builds a filename with the given base and the given extension, possibly adding a suffix to ensure the file doesn't exist.
*
@@ -229,13 +234,13 @@ public class FileUtils {
return file.getAbsolutePath();
}
/**
/**
* Copy a File (src) to a File (dst).
*
* @param src source file.
* @param dst destination file.
*/
public static void copy(FileDescriptor src, File dst) {
*/
public static void copy(FileDescriptor src, File dst) {
try (FileChannel in = new FileInputStream(src).getChannel();
FileChannel out = new FileOutputStream(dst).getChannel()) {
in.transferTo(0, in.size(), out);
@@ -262,7 +267,7 @@ public class FileUtils {
* @param context the Context.
* @param trackId the id of the Track.
* @param uri the uri to check.
* @return File object or null.
* @return File object or null.
*/
public static File getPhotoFileIfExists(Context context, long trackId, Uri uri) {
if (uri == null) {
@@ -300,4 +305,20 @@ public class FileUtils {
file.delete();
}
}
public static ArrayList<DocumentFile> getFiles(DocumentFile file) {
ArrayList<DocumentFile> files = new ArrayList<>();
if (!file.isDirectory()) return files;
for (DocumentFile candidate : file.listFiles()) {
if (!candidate.isDirectory()) {
files.add(candidate);
} else {
files.addAll(getFiles(candidate));
}
}
return files;
}
}
@@ -5,6 +5,8 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import androidx.annotation.NonNull;
import java.util.ArrayList;
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
@@ -21,32 +23,61 @@ public class IntentDashboardUtils {
private static final String ACTION_DASHBOARD_PAYLOAD = ACTION_DASHBOARD + ".Payload";
private static final String EXTRAS_SHOULD_KEEP_SCREEN_ON = "EXTRAS_SHOULD_KEEP_SCREEN_ON";
private static final String EXTRAS_SHOW_WHEN_LOCKED = "EXTRAS_SHOULD_KEEP_SCREEN_ON";
private static int TRACK_URI_INDEX = 0;
private static int TRACKPOINTS_URI_INDEX = 1;
private IntentDashboardUtils() {
}
public static boolean startDashboard(Context context, long[] trackIds) {
ArrayList<Uri> uris = new ArrayList<>();
/**
* Send intent to show tracks on a map (needs an another app) as resource URIs.
*
* @param context the context
* @param trackIds the track ids
*/
public static void startDashboard(Context context, long[] trackIds) {
if (trackIds.length == 0) {
return;
}
String trackIdList = ContentProviderUtils.formatIdListForUri(trackIds);
uris.add(0, Uri.withAppendedPath(TracksColumns.CONTENT_URI, trackIdList));
uris.add(1, Uri.withAppendedPath(TrackPointsColumns.CONTENT_URI_BY_TRACKID, trackIdList));
ArrayList<Uri> uris = new ArrayList<>();
uris.add(TRACK_URI_INDEX, Uri.withAppendedPath(TracksColumns.CONTENT_URI, trackIdList));
uris.add(TRACKPOINTS_URI_INDEX, Uri.withAppendedPath(TrackPointsColumns.CONTENT_URI_BY_TRACKID, trackIdList));
Intent intent = new Intent(ACTION_DASHBOARD);
intent.putParcelableArrayListExtra(ACTION_DASHBOARD_PAYLOAD, uris);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(EXTRAS_SHOULD_KEEP_SCREEN_ON, PreferencesUtils.shouldKeepScreenOn(context));
intent.putExtra(EXTRAS_SHOW_WHEN_LOCKED, PreferencesUtils.shouldShowStatsOnLockscreen(context));
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
ClipData clipData = ClipData.newRawUri(null, uris.get(0));
clipData.addItem(new ClipData.Item(uris.get(1)));
ClipData clipData = ClipData.newRawUri(null, uris.get(TRACK_URI_INDEX));
clipData.addItem(new ClipData.Item(uris.get(TRACKPOINTS_URI_INDEX)));
intent.setClipData(clipData);
if (intent.resolveActivity(context.getPackageManager()) != null) {
context.startActivity(Intent.createChooser(intent, null));
return true;
}
return false;
context.startActivity(intent);
}
public static void startDashboard(Context context, long trackId) {
startDashboard(context, new long[]{trackId});
}
public static long[] extractTrackIdsFromIntent(@NonNull Intent intent) {
final ArrayList<Uri> uris = intent.getParcelableArrayListExtra(ACTION_DASHBOARD_PAYLOAD);
final Uri tracksUri = uris.get(TRACK_URI_INDEX);
String[] trackIdsString = ContentProviderUtils.parseTrackIdsFromUri(tracksUri);
long[] trackIds = new long[trackIdsString.length];
for (int i = 0; i < trackIdsString.length; i++) {
trackIds[i] = Long.parseLong(trackIdsString[i]);
}
return trackIds;
}
}
@@ -16,14 +16,12 @@
package de.dennisguse.opentracks.util;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.provider.MediaStore;
import android.util.Log;
import android.util.Pair;
import android.widget.Toast;
import androidx.core.content.FileProvider;
@@ -35,11 +33,9 @@ import java.util.Date;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.DescriptionGenerator;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
import de.dennisguse.opentracks.content.data.Waypoint;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.content.provider.ShareContentProvider;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
/**
* Utilities for creating intents.
@@ -153,42 +149,11 @@ public class IntentUtils {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(uri));
try {
context.startActivity(intent);
} catch (ActivityNotFoundException e) {
Toast.makeText(context, context.getString(R.string.app_not_installed_show_on_map), Toast.LENGTH_SHORT).show();
}
context.startActivity(Intent.createChooser(intent, null));
}
/**
* Send intent to show tracks on a map (needs an another app).
*
* @param context the context
* @param trackIds the track ids
*/
public static void showTrackOnMap(Context context, long[] trackIds) {
if (trackIds.length == 0) {
return;
}
if (IntentDashboardUtils.startDashboard(context, trackIds)) {
return;
}
Intent intent = new Intent(android.content.Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(TrackPointsColumns.TRACKID, trackIds[0]);
Pair<Uri, String> uriAndMime = ShareContentProvider.createURI(trackIds, "SharingTrack", TrackFileFormat.KMZ_WITH_TRACKDETAIL);
intent.setDataAndType(uriAndMime.first, uriAndMime.second);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
if (intent.resolveActivity(context.getPackageManager()) != null) {
context.startActivity(intent);
} else {
Toast.makeText(context, context.getString(R.string.app_not_installed_show_on_map), Toast.LENGTH_SHORT).show();
}
}
/**
* Sends a take picture request to the camera app.
@@ -20,12 +20,16 @@ import android.location.Location;
import android.os.Build;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
import android.util.Pair;
import java.text.DecimalFormat;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.util.Date;
@@ -44,6 +48,8 @@ import de.dennisguse.opentracks.R;
*/
public class StringUtils {
private static final String TAG = StringUtils.class.getSimpleName();
private static final String COORDINATE_DEGREE = "\u00B0";
//TODO Remove when upgrading to API level 26+.
@@ -280,22 +286,27 @@ public class StringUtils {
}
/**
* Gets the time, in milliseconds, from an XML date time string as defined at http://www.w3.org/TR/xmlschema-2/#dateTime
* Gets the time, in milliseconds, from an XML date time string (ISO8601) as defined at http://www.w3.org/TR/xmlschema-2/#dateTime
* Let's be lenient: if timezone information is not provided, UTC will be used.
*
* @param xmlDateTime the XML date time string
*/
public static long parseTime(String xmlDateTime) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
try {
TemporalAccessor t = DateTimeFormatter.ISO_DATE_TIME.parse(xmlDateTime);
TemporalAccessor t = DateTimeFormatter.ISO_DATE_TIME.parseBest(xmlDateTime, ZonedDateTime::from, LocalDateTime::from);
if (t instanceof LocalDateTime) {
Log.w(TAG, "Date does not contain timezone information: using UTC.");
t = ((LocalDateTime) t).atZone(ZoneOffset.UTC);
}
return Instant.from(t).toEpochMilli();
} catch (Exception e) {
throw new IllegalArgumentException("Invalid XML dateTime value: " + e);
Log.e(TAG, "Invalid XML dateTime value");
throw e;
}
}
//TODO Remove the following when upgrading to API level 26+.
//ATTENTION: The following code does not require a time zone (+01 or Z); while ISO_DATE_TIME requires this!
// Parse the date time base
ParsePosition position = new ParsePosition(0);
@@ -415,4 +426,8 @@ public class StringUtils {
}
return new Pair<>(value, unit);
}
public static String valueInParentheses(String text) {
return "(" + text + ")";
}
}
@@ -0,0 +1,23 @@
package de.dennisguse.opentracks.util;
import androidx.annotation.NonNull;
import java.nio.ByteBuffer;
import java.util.UUID;
public class UUIDUtils {
public static UUID fromBytes(byte[] bytes) {
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
long mostSignificant = byteBuffer.getLong();
long lestSignificant = byteBuffer.getLong();
return new UUID(mostSignificant, lestSignificant);
}
public static byte[] toBytes(@NonNull UUID uuid) {
ByteBuffer byteBuffer = ByteBuffer.allocate(16);
byteBuffer.putLong(uuid.getMostSignificantBits());
byteBuffer.putLong(uuid.getLeastSignificantBits());
return byteBuffer.array();
}
}
@@ -0,0 +1,74 @@
package de.dennisguse.opentracks.viewmodels;
import android.content.Context;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.stats.TrackStatistics;
import de.dennisguse.opentracks.util.TrackIconUtils;
public class AggregatedStatistics {
private HashMap<String, AggregatedStatistic> aggregatedStatistics = new HashMap<>();
public void aggregate(List<Track> tracks) {
for (Track track : tracks) {
aggregate(track);
}
}
public void aggregate(Track track) {
String category = track.getCategory();
if (aggregatedStatistics.containsKey(category)) {
aggregatedStatistics.get(category).add(track.getTrackStatistics());
} else {
aggregatedStatistics.put(category, new AggregatedStatistic(track.getTrackStatistics()));
}
}
public int getCount() {
return aggregatedStatistics.size();
}
public String getSportName(int position) {
return getKey(position);
}
public AggregatedStatistic get(String category) {
return aggregatedStatistics.get(category);
}
private String getKey(int position) {
return (new ArrayList<>(aggregatedStatistics.keySet())).get(position);
}
public AggregatedStatistic getItem(int position) {
return (new ArrayList<>(aggregatedStatistics.values())).get(position);
}
public static class AggregatedStatistic {
private TrackStatistics trackStatistics;
private int countTracks;
public AggregatedStatistic(TrackStatistics trackStatistics) {
this.trackStatistics = trackStatistics;
this.countTracks = 1;
}
public TrackStatistics getTrackStatistics() {
return trackStatistics;
}
public int getCountTracks() {
return countTracks;
}
void add(TrackStatistics statistics) {
trackStatistics.merge(statistics);
countTracks++;
}
}
}
@@ -0,0 +1,42 @@
package de.dennisguse.opentracks.viewmodels;
import android.app.Application;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import java.util.List;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
public class AggregatedStatisticsModel extends AndroidViewModel {
private MutableLiveData<AggregatedStatistics> aggregatedStats;
public AggregatedStatisticsModel(@NonNull Application application) {
super(application);
}
public LiveData<AggregatedStatistics> getAggregatedStats() {
if (aggregatedStats == null) {
aggregatedStats = new MutableLiveData<>();
loadAggregatedStats();
}
return aggregatedStats;
}
private void loadAggregatedStats() {
new Thread(() -> {
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(getApplication().getApplicationContext());
List<Track> tracks = contentProviderUtils.getAllTracks();
AggregatedStatistics aggregatedStatistics = new AggregatedStatistics();
aggregatedStatistics.aggregate(tracks);
aggregatedStats.postValue(aggregatedStatistics);
}).start();
}
}
@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:fillColor="#000000"
android:pathData="M82.489,12.834C83.196,10.418 82.043,9.556 79.926,10.917L10.087,55.828c-2.117,1.361 -1.795,2.628 0.714,2.815L39.806,60.807c2.51,0.188 5.291,2.267 6.18,4.621l10.282,27.208c0.89,2.354 2.195,2.304 2.902,-0.111z" />
</vector>
@@ -0,0 +1,33 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<group
android:scaleX="0.56"
android:scaleY="0.56"
android:translateX="23.76"
android:translateY="23.76">
<path
android:strokeWidth="1"
android:pathData="M85.133,20.59C85.731,18.546 84.756,17.817 82.965,18.968L23.874,56.967c-1.791,1.152 -1.519,2.224 0.604,2.382L49.019,61.18c2.124,0.159 4.477,1.918 5.229,3.91l8.7,23.021c0.753,1.992 1.857,1.949 2.455,-0.094z"
android:strokeColor="#f69123">
<aapt:attr name="android:fillColor">
<gradient
android:startY="80.33152"
android:startX="38.773293"
android:endY="19.211866"
android:endX="84.4913"
android:type="linear">
<item
android:offset="0"
android:color="#FFFBB22F" />
<item
android:offset="1"
android:color="#FFF58820" />
</gradient>
</aapt:attr>
</path>
</group>
</vector>
@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M19,3L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM9,17L7,17v-7h2v7zM13,17h-2L11,7h2v10zM17,17h-2v-4h2v4z"/>
</vector>
+36
View File
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/toolbar" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<include layout="@layout/track_controller" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.tabs.TabLayout
android:id="@+id/track_detail_activity_tablayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabIndicatorColor="@color/holo_orange_dark" />
<androidx.viewpager.widget.ViewPager
android:id="@+id/track_detail_activity_view_pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
+42
View File
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/toolbar" />
<ListView
android:id="@+id/aggregated_stats_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.0" />
<RelativeLayout
android:id="@+id/aggregated_stats_empty_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.0">
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<TextView
style="@style/TextLarge"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="4"
android:gravity="center_horizontal|center_vertical"
android:text="@string/aggregated_stats_empty_message"
android:textColor="@color/list_empty_text" />
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
</RelativeLayout>
</LinearLayout>
@@ -0,0 +1,150 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="24dp">
<!-- Guidelines -->
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline1"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintGuide_begin="8dp" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline3"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintGuide_end="8dp" />
<!-- Icon and sport name -->
<ImageView
android:id="@+id/aggregated_stats_sport_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
app:layout_constraintLeft_toLeftOf="@id/guideline1"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_activity_bike_24dp" />
<TextView
android:id="@+id/aggregated_stats_type_label"
style="@style/StatsLargeLabel"
android:layout_marginTop="0dp"
android:layout_marginBottom="0dp"
app:layout_constraintBottom_toBottomOf="@+id/aggregated_stats_sport_icon"
app:layout_constraintLeft_toRightOf="@+id/aggregated_stats_sport_icon"
app:layout_constraintTop_toTopOf="@+id/aggregated_stats_sport_icon"
tools:text="@string/activity_type_cycling"
android:layout_marginLeft="16dp" />
<TextView
android:id="@+id/aggregated_stats_num_tracks"
style="@style/StatsLargeLabel"
android:layout_marginTop="0dp"
android:layout_marginBottom="0dp"
app:layout_constraintBottom_toBottomOf="@+id/aggregated_stats_type_label"
app:layout_constraintLeft_toRightOf="@+id/aggregated_stats_type_label"
app:layout_constraintTop_toTopOf="@+id/aggregated_stats_type_label"
tools:text="(1)"
android:layout_marginLeft="4dp" />
<!-- Distance -->
<TextView
android:id="@+id/aggregated_stats_distance"
style="@style/StatsLargeValue"
app:layout_constraintLeft_toLeftOf="@id/guideline1"
app:layout_constraintTop_toBottomOf="@+id/aggregated_stats_sport_icon"
tools:text="100000"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp" />
<TextView
android:id="@+id/aggregated_stats_distance_unit"
style="@style/StatsUnit"
android:textColor="@android:color/white"
app:layout_constraintBottom_toBottomOf="@+id/aggregated_stats_distance"
app:layout_constraintLeft_toRightOf="@+id/aggregated_stats_distance"
tools:text="km" />
<!-- Time -->
<TextView
android:id="@+id/aggregated_stats_time"
style="@style/StatsLargeValue"
app:layout_constraintRight_toRightOf="@id/guideline3"
app:layout_constraintTop_toBottomOf="@+id/aggregated_stats_sport_icon"
tools:text="00:00:00"
android:layout_marginRight="16dp"
android:layout_marginTop="16dp" />
<!-- Avg. rate -->
<TextView
android:id="@+id/aggregated_stats_avg_rate"
style="@style/StatsSmallValue"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintTop_toBottomOf="@id/aggregated_stats_distance"
app:layout_constraintLeft_toRightOf="@id/guideline1"
app:layout_constraintRight_toLeftOf="@+id/aggregated_stats_avg_rate_unit"
android:layout_marginTop="24dp"
tools:text="30" />
<TextView
android:id="@+id/aggregated_stats_avg_rate_unit"
style="@style/StatsUnit"
android:textColor="@android:color/white"
app:layout_constraintBottom_toBottomOf="@+id/aggregated_stats_avg_rate"
app:layout_constraintLeft_toRightOf="@+id/aggregated_stats_avg_rate"
app:layout_constraintRight_toRightOf="@id/guideline2"
tools:text="km/h" />
<TextView
android:id="@+id/aggregated_stats_avg_rate_label"
style="@style/StatsTinyLabel"
app:layout_constrainedWidth="true"
app:layout_constraintTop_toBottomOf="@+id/aggregated_stats_avg_rate"
app:layout_constraintLeft_toRightOf="@id/guideline1"
app:layout_constraintRight_toRightOf="@id/guideline2"
tools:text="@string/stats_average_moving_speed" />
<!-- Max. rate -->
<TextView
android:id="@+id/aggregated_stats_max_rate"
style="@style/StatsSmallValue"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintTop_toBottomOf="@id/aggregated_stats_time"
app:layout_constraintLeft_toRightOf="@id/guideline2"
app:layout_constraintRight_toLeftOf="@+id/aggregated_stats_max_rate_unit"
android:layout_marginTop="24dp"
tools:text="70" />
<TextView
android:id="@+id/aggregated_stats_max_rate_unit"
style="@style/StatsUnit"
android:textColor="@android:color/white"
app:layout_constraintBottom_toBottomOf="@+id/aggregated_stats_max_rate"
app:layout_constraintLeft_toRightOf="@+id/aggregated_stats_max_rate"
app:layout_constraintRight_toLeftOf="@id/guideline3"
tools:text="km/h" />
<TextView
android:id="@+id/aggregated_stats_max_rate_label"
style="@style/StatsTinyLabel"
app:layout_constrainedWidth="true"
app:layout_constraintTop_toBottomOf="@+id/aggregated_stats_max_rate"
app:layout_constraintLeft_toRightOf="@id/guideline2"
app:layout_constraintRight_toLeftOf="@id/guideline3"
tools:text="@string/stats_max_speed" />
</androidx.constraintlayout.widget.ConstraintLayout>
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/progressbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:minHeight="100dp"
android:minWidth="100dp" />
</LinearLayout>
+5
View File
@@ -28,6 +28,11 @@ limitations under the License.
android:title="@string/menu_start_gps"
android:visible="false"
app:showAsAction="ifRoom" />
<item
android:id="@+id/track_list_aggregated_stats"
android:icon="@drawable/ic_statistics_24dp"
android:title="@string/menu_aggregated_statistics"
app:showAsAction="ifRoom" />
<item
android:id="@+id/track_list_markers"
android:icon="@drawable/ic_marker_show_24dp"
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_logo_color_108dp" />
</adaptive-icon>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_debug_background" />
<foreground android:drawable="@drawable/ic_logo_color_108dp" />
</adaptive-icon>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

+149 -1
View File
@@ -1,5 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?><!--
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<string name="about_preference_title">O OpenTracks</string>
<string name="about_description"><i> OpenTracks </i> je sportovní sledovací aplikace, která zcela respektuje vaše soukromí.</string>
<string name="about_url">Webová stránka projektu: <a href="%1$s">%1$s</a></string>
<string name="about_version_name">Název verze: %1$s</string>
<string name="about_version_code">Kód verze: %1$d</string>
<string name="about_history">OpenTracks je vidlička <a href="https://code.google.com/archive/p/mytracks/"> <i> MyTracks Google </i> </a> (v roce 2016 ukončena).</string>
<string name="about_libraries">Knihovny</string>
<!-- Help -->
<string name="help_feature_title">OpenTracks má následující funkce:</string>
<string name="help_feature_content">
* vytvářet GPS stopy \ n
* použijte snímač srdeční frekvence (pouze Bluetooth LE) \ n
* hlasová oznámení (podle času nebo vzdálenosti) \ n
* vytvářejte značky (včetně obrázků) \ n
* funguje zcela offline \ n
* zobrazit skladby na mapě (viz níže)</string>
<string name="help_track_map_title">Jak ukázat skladbu na mapě?</string>
<string name="help_track_map_content">
Použijte malou ikonu mapy.\n
a) (živé aktualizace) Nainstalujte <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>.
Poskytuje online nebo offline mapy prostřednictvím <a href="https://www.openstreetmap.org">OpenStreetMap</a>.\n
Ve skutečnosti je OSMDashboard samostatná aplikace, která má přístup (dočasně) pouze k datům stop, které se bud(ou) zobrazovat.\n
b) Prostřednictvím „normální“ mapové aplikace (např. <a href="http://osmand.net/">OsmAnd</a> a <a href="https://maps.me/">MAPS.ME</a>).
Tyto aplikace musí podporovat formát souboru KMZ (pouze umístění a časová razítka).
Ve skutečnosti musí aplikace podporovat <i>ACTION_VIEW</i> pomocí MIME <i>application/vnd.google-earth.kmz</i>.</string>
<string name="help_storage_pictures_title">Jak ukládá OpenTracks obrázky pořízené jako značky?</string>
<string name="help_storage_pictures_content">Jsou uloženy v interním úložišti aplikace (tj. Přístupné pouze v rámci OpenTracks).</string>
<string name="help_track_export_title">Jak mohu exportovat / sdílet zaznamenané stopy?</string>
<string name="help_track_export_content">
Skladby lze exportovat z Nastavení jako KMZ, KML nebo GPX.
KMZ / KML všechna data; Pouze umístění GPX.
Skladby sdílené pomocí ikony sdílení <i> </i> jsou sdíleny jako KMZ.</string>
<string name="help_data_google_play_services_title">Vyžaduje služba OpenTracks / používá službu Google Play?</string>
<string name="help_recording_title">Jak OpenTracks zaznamenává data?</string>
<string name="help_recording_content">
OpenTracks používá GPS senzor zařízení k nepřetržitému zaznamenávání polohy.
Pokud zařízení GPS hlásí nepřesná data (např. Polohu, rychlost, nadmořskou výšku), OpenTracks s tím nemůže nic udělat.</string>
<string name="help_data_storage_title">Jak OpenTracks ukládá data senzorů?</string>
<string name="help_data_storage_content">
Data senzoru se ukládají na zaznamenané místo.
Pokud jde o snímač srdeční frekvence, znamená to, že na každé místo je uloženo pouze <i> jedno </i> měření srdeční frekvence.
Pokud stojíte v klidu nebo jste uvnitř, nebudou zaznamenána žádná data ze senzorů (ale budou zobrazena).</string>
<string name="help_gps_troubleshooting_title">OpenTracks nezaznamenává místa?</string>
<string name="help_gps_troubleshooting_content">
Co můžete udělat: \ n
a) zkontrolujte, zda je GPS povoleno, \ n
b) zlepšit příjem GPS (např. jít venku), \ n
c) zkontrolujte, zda používáte aplikaci <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/"> GPSTest </a> a \ n
d) deaktivujte úsporu energie. \ n
\ n
Pokud všechno nepomůže, nahláste chybu.</string>
<string name="help_support_title">Jak mohu podporovat OpenTracks?</string>
<string name="help_support_content">
Můžete: \ n
a) šířit slovo o OpenTracks, \ n
b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues"> navrhnout funkce </a>, \ n
c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues"> nahlaste chyby </a>, \ n
d) pomoc při vývoji softwaru, \ n
e) nakupujte v <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore"> Obchod Google Play </a>, \ n
f) darovat, a \ n
g) udělejte, co si myslíte, že pomůže OpenTracks.</string>
<!-- Activity Type -->
<string name="activity_type_airplane">letadlo</string>
<string name="activity_type_atv">terénní auta</string>
<string name="activity_type_biking">cyklistika</string>
@@ -66,6 +151,8 @@
<string name="activity_type_walking">chůze</string>
<string name="activity_type_water_skiing">vodní lyžování</string>
<string name="activity_type_wind_surfing">windsurfing</string>
<string name="app_not_installed_show_on_map">Nainstalujte prosím aplikaci, která podporuje prezentaci mapových dat.</string>
<!-- Description -->
<string name="description_activity_type">Typ aktivity: %1$s</string>
<string name="description_average_moving_pace_in_minute">Průměrné tempo pohybu: %1$s min/km (%2$s min/míli)</string>
<string name="description_average_moving_speed">Průměrná rychlost přesunu: %1$.2f km/h (%2$.1f mil/h)</string>
@@ -93,6 +180,7 @@
<string name="description_sensor_cadence">Tempo šlapání (ot./min)</string>
<string name="description_sensor_heart_rate">Srdeční frekvence (údery/min)</string>
<string name="description_sensor_power">Výkon (W)</string>
<string name="description_default_speed_or_pace">Podle typu činnosti</string>
<string name="description_speed_imperial">Rychlost (míle/h)</string>
<string name="description_speed_metric">Rychlost (km/h)</string>
<string name="description_time">Doba</string>
@@ -100,19 +188,25 @@
<string name="description_total_time">Celková doba: %1$s</string>
<string name="description_track_point">Bod</string>
<string name="description_track_segment">Segment</string>
<!-- Export -->
<string name="export_all_option">Vše ve formátu %1$s</string>
<string name="export_all_title">Export do externího úložiště</string>
<string name="export_with_photos">s fotkami</string>
<string name="export_without_photos">bez fotografií</string>
<string name="export_error">Trasy byly exportovány do složky %3$s (%1$d z %2$s).</string>
<string name="export_option">jako %1$s do složky %2$s</string>
<string name="export_progress_message">Exportování do složky %1$s…</string>
<string name="export_success">Trasy (celkem %1$s) byly exportovány do složky %2$s.</string>
<string name="export_title">Export do…</string>
<!-- External Storage -->
<!-- Files -->
<string name="files_few">%1$d soubory</string>
<string name="files_many">%1$d souborů</string>
<string name="files_one">1 soubor</string>
<string name="files_other">%1$d souborů</string>
<string name="files_two">2 soubory</string>
<string name="files_zero">0 souborů</string>
<!-- Generic -->
<string name="generic_add">Přidat</string>
<string name="generic_cancel">Zrušit</string>
<string name="generic_delete_all_confirm_title">Smazat vše?</string>
@@ -131,6 +225,7 @@
<string name="generic_success_title">Úspěch</string>
<string name="generic_tracks">Trasy</string>
<string name="generic_yes">Ano</string>
<!-- Gps -->
<string name="gps_location_access">Přístup k poloze</string>
<string name="gps_disabled">Systém GPS není zapnutý. Klepnutím sem přejdete na možnost %1$s. Stisknutím tlačítka Zpět se vrátíte.</string>
<string name="gps_disabled_my_location">Aktuální polohu nelze určit. V části %1$s aktivujte další zdroje pro zjišťování polohy a ujistěte se, že vidíte oblohu. Zkuste to znovu později.</string>
@@ -138,6 +233,7 @@
<string name="gps_starting">Spouštění GPS</string>
<string name="gps_wait_for_better_signal">Čekání na lepší signál GPS</string>
<string name="gps_wait_for_signal">Čekání na signál GPS</string>
<!-- Image -->
<string name="image_application">Aplikace</string>
<string name="image_arrow">Šipka</string>
<string name="image_marker">Značka</string>
@@ -148,6 +244,7 @@
<string name="image_record">Zaznamenat</string>
<string name="image_stop">Ukončit</string>
<string name="image_track">Trasa</string>
<!-- Import -->
<string name="import_error">Trasy byly importovány ze složky %3$s (%1$d z %2$s).</string>
<string name="import_no_directory">Složka %1$s neexistuje.</string>
<string name="import_no_file">Nebyly nalezeny žádné soubory, které by bylo možné importovat. Připojte zařízení k počítači a umístěte soubory, které chcete importovat, do složky %1$s.</string>
@@ -156,9 +253,11 @@
<string name="import_selection_option">Všechny soubory %1$s</string>
<string name="import_selection_title">Import z externího úložiště</string>
<string name="import_success">Trasy (celkem %1$s) byly importovány ze složky %2$s.</string>
<!-- Marker -->
<string name="marker_add_error">Značku nelze vložit. Signál GPS není dostupný. Zkuste to znovu.</string>
<string name="marker_add_success">Značka byla vložena.</string>
<string name="marker_add_canceled">Zrušeno, značky nebyly přidány.</string>
<string name="marker_add_photo_canceled">Zrušeno, nebyla přidána žádná značka</string>
<string name="marker_delete_all_confirm_message">Všechny značky této trasy budou trvale smazány ze zařízení.</string>
<string name="marker_delete_one_confirm_message">Značka bude trvale smazána ze zařízení.</string>
<string name="marker_delete_one_confirm_title">Smazat značku?</string>
@@ -168,10 +267,14 @@
<string name="marker_name_format">Značka %1$d</string>
<string name="marker_split_name_format">Dělicí značka %1$d</string>
<string name="marker_title">%1$d z %2$d</string>
<!-- Marker Detail -->
<string name="marker_detail_statistics_title">Od poslední dělicí značky:</string>
<!-- Marker Edit -->
<string name="marker_edit_marker_type">Typ značky (například vodopád…)</string>
<string name="marker_edit_marker_type_hint">Typ značky</string>
<!-- Marker List -->
<string name="marker_list_empty_message">Nemáte žádné značky.</string>
<!-- Menu -->
<string name="menu_aggregated_statistics">Souhrnná statistika</string>
<string name="menu_delete">Smazat</string>
<string name="menu_edit">Upravit</string>
@@ -180,6 +283,7 @@
<string name="menu_import_all">Importovat vše</string>
<string name="menu_insert_marker">Vložit značku</string>
<string name="menu_insert_photo">Vložit fotku</string>
<string name="menu_insert_gallery_img">Vložte obrázek galerie</string>
<string name="menu_markers">Značky</string>
<string name="menu_search">Vyhledat</string>
<string name="menu_select_all">Vybrat vše</string>
@@ -192,11 +296,17 @@
<string name="menu_stop_gps">Ukončit GPS</string>
<string name="menu_view_photo">Zobrazit fotku</string>
<string name="menu_voice_frequency">Frekvence oznámení</string>
<string name="menu_resume_track">Obnovit skladbu</string>
<!-- OpenTracks -->
<string name="app_name">OpenTracks</string>
<string name="recording_service">Služba pro záznam</string>
<!-- Permission -->
<string name="permission_gps_failed">OpenTracks vyžaduje povolení k používání GPS.</string>
<!-- Search -->
<string name="search_global_description">Trasy a značky</string>
<string name="search_list_empty_message">Vyhledávacímu dotazu neodpovídají žádné trasy ani značky.</string>
<string name="search_list_marker_track_location">Na trase %1$s</string>
<!-- Sensor State -->
<string name="sensor_state_battery">Stav baterie</string>
<string name="sensor_state_cadence">Frekvence šlapání</string>
<string name="sensor_state_cadence_value">%1$d ot./min</string>
@@ -205,12 +315,22 @@
<string name="sensor_state_heart_rate_value">%1$d úderů/min</string>
<string name="sensor_state_power">Napájení</string>
<string name="sensor_state_power_value">%1$d W</string>
<string name="sensor_not_known">Senzor %1$s není znám. Zkontrolujte nastavení.</string>
<string name="sensor_could_not_scan">Nelze vyhledat zařízení Bluetooth (chyba %1$i).</string>
<string name="bluetooth_disabled">Aktivujte prosím Bluetooth.</string>
<string name="settings_not_while_recording">Při záznamu trasy není tato funkce dostupná.</string>
<string name="settings_show">Zobrazit</string>
<!-- Settings Advanced -->
<string name="settings_advanced">Pokročilé</string>
<!-- Settings Chart -->
<string name="settings_chart_by_distance">Podle vzdálenosti</string>
<string name="settings_chart_by_time">Podle doby</string>
<string name="settings_chart_x_axis">Osa X</string>
<!-- Settings Recording -->
<string name="settings_recording_show_on_lockscreen_while_recording_title">Zobrazit statistiky na obrazovce uzamčení</string>
<string name="settings_recording_show_on_lockscreen_while_recording_summary">Během nahrávání zobrazte statistiky bez odemknutí zařízení.</string>
<string name="settings_recording_keep_screen_on_while_recording_title">Nechte obrazovku zapnutou</string>
<string name="settings_recording_keep_screen_on_while_recording_summary">Během nahrávání udržujte obrazovku zapnutou.</string>
<string name="settings_recording">Záznam</string>
<string name="settings_recording_auto_resume_track_timeout_always_summary">Po restartu vždy pokračovat v záznamu</string>
<string name="settings_recording_auto_resume_track_timeout_never_summary">Po restartu nikdy nepokračovat v záznamu</string>
@@ -228,21 +348,32 @@
<string name="settings_recording_track_name_date_local_option">Datum (místní)</string>
<string name="settings_recording_track_name_number_option">Číslo</string>
<string name="settings_recording_track_name_title">Výchozí název trasy</string>
<!-- Settings Reset -->
<string name="settings_reset">Resetovat</string>
<string name="settings_reset_confirm_message">Všechna nastavení budou obnovena na výchozí hodnoty.</string>
<string name="settings_reset_confirm_title">Resetovat nyní?</string>
<string name="settings_reset_done">Všechna nastavení byla obnovena na výchozí hodnoty.</string>
<string name="settings_error_initial_values_restored">Litujeme, ale došlo k chybě. Všechna nastavení byla vrácena na výchozí hodnoty.</string>
<!-- Settings Sensor -->
<string name="settings_sensor">Senzor</string>
<string name="settings_sensor_bluetooth">Bluetooth</string>
<string name="settings_sensor_bluetooth_sensor">Senzor Bluetooth</string>
<string name="settings_sensor_connecting">Připojeno k senzoru %1$s</string>
<string name="settings_sensor_disconnected">Odpojeno od %1$s</string>
<string name="settings_sensor_wheel_circumference">Velikost kola (ETRTO)</string>
<!-- Settings Stats -->
<string name="settings_stats_coordinate">Zeměpisná šířka/délka</string>
<string name="settings_stats_rate_title">Preferovaná rychlost</string>
<string name="settings_stats_units_imperial">Imperiální jednotky (míle, stopy)</string>
<string name="settings_stats_units_metric">Metrické jednotky (km, m)</string>
<string name="settings_stats_units_title">Preferované jednotky</string>
<!-- Share Track -->
<string name="share_track_share_file">Sdílet soubor trasy</string>
<string name="share_track_share_file_body">Možná by vás mohla zajímat tato trasa.\n\nChcete-li ji zobrazit, použijte Google Earth nebo jiný mapový software.\n\n%1$s</string>
<string name="share_track_subject">Chci s vámi sdílet trasu</string>
<!-- Stats -->
<string name="stats_average_moving_pace">Průměrné tempo pohybu</string>
<string name="stats_average_moving_speed">Průměrná rychlost pohybu</string>
<string name="stats_average_pace">Průměrné tempo</string>
@@ -261,25 +392,36 @@
<string name="stats_speed">Rychlost</string>
<string name="stats_distance">Vzdálenost</string>
<string name="stats_total_time">Celková doba</string>
<!-- Track -->
<string name="track_name_format">Trasa %1$d</string>
<string name="track_record_notification">Záznam trasy…</string>
<string name="track_recording_notification_accuracy">Přesnost polohy: %1$s</string>
<string name="track_markers">Značky na trase %1$s</string>
<!-- Track Delete -->
<string name="track_delete_one_confirm_message">Trasa a související značky budou trvale smazány ze zařízení.</string>
<string name="track_delete_one_confirm_title">Smazat trasu?</string>
<string name="track_delete_multiple_confirm_message">Vybrané trasy a jejich značky budou trvale smazány ze zařízení.</string>
<string name="track_delete_progress_message">Mazání…</string>
<!-- Track Detail -->
<string name="track_detail_chart_tab">Graf</string>
<string name="track_detail_stats_tab">Statistika</string>
<string name="track_detail_record_success">Zaznamenávání nové trasy</string>
<string name="track_detail_resume_success">Nyní se obnovuje skladba</string>
<!-- Track Edit -->
<string name="track_edit_activity_type">Typ aktivity (například chůze…)</string>
<string name="track_edit_activity_type_hint">Typ aktivity</string>
<string name="track_edit_new_track_title">Nová trasa</string>
<!-- Track List -->
<string name="track_list_empty_message">Zde zaznamenejte příští výpravu</string>
<!-- Track Widget -->
<!-- Tracks -->
<string name="tracks_few">%1$d trasy</string>
<string name="tracks_many">%1$d tras</string>
<string name="tracks_one">1 trasa</string>
<string name="tracks_other">%1$d tras</string>
<string name="tracks_two">2 trasy</string>
<string name="tracks_zero">0 tras</string>
<!-- Unit -->
<string name="unit_feet">stop</string>
<string name="unit_kilometer">km</string>
<string name="unit_kilometer_per_hour">km/h</string>
@@ -288,6 +430,7 @@
<string name="unit_mile_per_hour">mil/h</string>
<string name="unit_minute_per_kilometer">min/km</string>
<string name="unit_minute_per_mile">min/míle</string>
<!-- Value -->
<string name="value_adapt_accuracy">Přizpůsobit na přesnost</string>
<string name="value_adapt_battery_life">Přizpůsobit výdrži baterie</string>
<string name="value_always">Vždy</string>
@@ -314,6 +457,7 @@
<string name="value_off">Vypnuto</string>
<string name="value_smallest">Nejmenší</string>
<string name="value_smallest_recommended">Nejnižší hodnota (doporučeno)</string>
<!-- Voice -->
<string name="voice_hours_few">%1$d hodiny</string>
<string name="voice_hours_many">%1$d hodin</string>
<string name="voice_hours_one">1 hodina</string>
@@ -360,6 +504,7 @@
<string name="voice_total_distance_miles_two">celková vzdálenost 2 míle</string>
<string name="voice_total_distance_miles_zero">celková vzdálenost 0 mil</string>
<string name="voice_total_distance_zero">Celková vzdálenost Mých tras je nulová.</string>
<!-- Waypoint Type -->
<string name="waypoint_type_atm">Bankomat</string>
<string name="waypoint_type_bank">banka</string>
<string name="waypoint_type_bar">bar</string>
@@ -411,4 +556,7 @@
<string name="waypoint_type_waterfall">vodopád</string>
<string name="waypoint_type_water_fountain">kašna</string>
<string name="waypoint_type_waypoint">navigační bod</string>
<!-- Share image -->
<string name="share_image_subject">Chtěl bych s vámi sdílet obrázek</string>
<string name="share_image_body">Myslím, že by vás mohl tento obrázek zajímat.</string>
</resources>
+150 -1
View File
@@ -1,5 +1,91 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?><!--
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<string name="about_preference_title">Om OpenTracks</string>
<string name="about_description"><i> OpenTracks </i> er en sportssporingsapplikation, der fuldt ud respekterer dit privatliv.</string>
<string name="about_url">Projektwebside: <a href="%1$s">%1$s</a></string>
<string name="about_version_name">Versionsnavn: %1$s</string>
<string name="about_version_code">Versionskode: %1$d</string>
<string name="about_history">OpenTracks er en gaffel fra <a href="https://code.google.com/archive/p/mytracks/"> <i> Googles MyTracks </i> </a> (afviklet i 2016).</string>
<string name="about_libraries">Biblioteker</string>
<!-- Help -->
<string name="help_feature_title">OpenTracks har følgende funktioner:</string>
<string name="help_feature_content">
* Opret GPS-spor \n
* Brug en pulssensor (kun Bluetooth LE) \n
* stemmemeddelelser (efter tid eller afstand) \n
* Opret markører (inkl. billeder) \n
* fungerer helt offline \n
* vis spor på et kort (se nedenfor)</string>
<string name="help_track_map_title">Hvordan viser man et spor på et kort?</string>
<string name="help_track_map_content">
Brug det lille kortikon. \n
a)
(liveopdateringer) Installer <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>.
Det giver online eller offline kort via <a href="https://www.openstreetmap.org">OpenStreetMap</a>.\n
Faktisk er OSMDashboard en separat app, der kun kan få adgang til (midlertidigt) adgang til dataene fra spor (e), der skal vises.\n
b) Via et normalt kortprogram (f.eks. <a href="http://osmand.net/">OsmAnd</a> og <a href="https://maps.me/">MAPS.ME</a>).
Disse app skal understøtte KMZ-filformatet (kun placeringer og tidsstempler).
Faktisk skal en app understøtte <i>ACTION_VIEW</i> med MIME <i>application/vnd.google-earth.kmz</i>.</string>
<string name="help_storage_pictures_title">Hvordan gemmer OpenTracks billeder, der blev taget som markører?</string>
<string name="help_storage_pictures_content">Disse gemmes i appens interne lager (dvs. kun tilgængelig inden for OpenTracks).</string>
<string name="help_track_export_title">Hvordan kan jeg eksportere / dele indspillede numre?</string>
<string name="help_track_export_content">
Spor kan eksporteres fra indstillingerne som KMZ, KML eller GPX.
KMZ / KML alle data; GPX kun placeringer.
Spor, der deles via <i>delingsikonet</i> deles som KMZ.</string>
<string name="help_data_google_play_services_title">Kræver/bruger OpenTracks Google Play Service?</string>
<string name="help_recording_title">Hvordan registrerer OpenTracks data?</string>
<string name="help_recording_content">
OpenTracks bruger enhedens GPS-sensor til at registrere placeringen kontinuerligt.
Hvis GPS-enheden rapporterer unøjagtige data (f.eks. Placering, hastighed, højde), kan OpenTracks ikke gøre noget ved det.</string>
<string name="help_data_storage_title">Hvordan gemmer OpenTracks sensordata?</string>
<string name="help_data_storage_content">
Sensordata gemmes pr. Optaget placering.
Med hensyn til en pulssensor betyder det, at der kun er <i> en </i> måling af hjertefrekvensen lagret pr. Placering.
Hvis du står stille eller er indendørs, registreres ingen sensordata (men de vil blive vist).</string>
<string name="help_gps_troubleshooting_title">OpenTracks optager ikke placeringer?</string>
<string name="help_gps_troubleshooting_content">
Hvad du kan gøre:\n
a) Kontroller, om GPS er aktiveret,\n
b) forbedre GPS-modtagelse (f.eks. gå udendørs), \n
c) Kontroller, om du har appen <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/"> GPSTest </a> og \n
d) deaktiver strømbesparelse. \n
\n
Hvis alt ikke hjælper, skal du rapportere en fejl.</string>
<string name="help_support_title">Hvordan kan jeg støtte OpenTracks?</string>
<string name="help_support_content">
Du kan: \n
a) sprede ordet om OpenTracks, \n
b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues"> foreslå funktioner </a>, \n
c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues"> rapportfejl </a>, \n
d) hjælp til softwareudvikling, \n
e) køb i <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore"> Google Play Store </a>, \n
f) donere, og \n
g) gør hvad du synes hjælper OpenTracks.</string>
<!-- Activity Type -->
<string name="activity_type_airplane">fly</string>
<string name="activity_type_atv">Terrængående køretøj</string>
<string name="activity_type_biking">cykling</string>
@@ -66,6 +152,8 @@
<string name="activity_type_walking">til fods</string>
<string name="activity_type_water_skiing">vandski</string>
<string name="activity_type_wind_surfing">windsurfing</string>
<string name="app_not_installed_show_on_map">Installer venligst en app, der understøtter præsentation af kortdata.</string>
<!-- Description -->
<string name="description_activity_type">Aktivitetstype: %1$s</string>
<string name="description_average_moving_pace_in_minute">Gennemsnitsfart: %1$s min./km (%2$s min./mil)</string>
<string name="description_average_moving_speed">Gennemsnitlig bevægelseshastighed: %1$.2f km/t (%2$.1f mil/t)</string>
@@ -93,6 +181,7 @@
<string name="description_sensor_cadence">Kadence (omdr./min.)</string>
<string name="description_sensor_heart_rate">Puls (slag/min.)</string>
<string name="description_sensor_power">Effekt (W)</string>
<string name="description_default_speed_or_pace">Efter aktivitetstype</string>
<string name="description_speed_imperial">Hastighed (mil/t)</string>
<string name="description_speed_metric">Hastighed (km/t)</string>
<string name="description_time">Tid</string>
@@ -100,19 +189,25 @@
<string name="description_total_time">Samlet tid: %1$s</string>
<string name="description_track_point">Punkt</string>
<string name="description_track_segment">Segment</string>
<!-- Export -->
<string name="export_all_option">Alle som %1$s</string>
<string name="export_all_title">Eksportér til eksternt lager</string>
<string name="export_with_photos">med fotos</string>
<string name="export_without_photos">uden fotos</string>
<string name="export_error">%1$d af %2$s blev eksporteret til %3$s</string>
<string name="export_option">som %1$s til %2$s</string>
<string name="export_progress_message">Eksporterer til %1$s…</string>
<string name="export_success">%1$s blev eksporteret til %2$s</string>
<string name="export_title">Eksportér til…</string>
<!-- External Storage -->
<!-- Files -->
<string name="files_few">%1$d filer</string>
<string name="files_many">%1$d filer</string>
<string name="files_one">1 fil</string>
<string name="files_other">%1$d filer</string>
<string name="files_two">2 filer</string>
<string name="files_zero">0 filer</string>
<!-- Generic -->
<string name="generic_add">Tilføj</string>
<string name="generic_cancel">Annuller</string>
<string name="generic_delete_all_confirm_title">Skal alle slettes?</string>
@@ -131,6 +226,7 @@
<string name="generic_success_title">Gennemført</string>
<string name="generic_tracks">Ruter</string>
<string name="generic_yes">Ja</string>
<!-- Gps -->
<string name="gps_location_access">\"Placeringsadgang\"</string>
<string name="gps_disabled">GPS er ikke aktiveret. Tryk her for at gå til %1$s. Tryk på knappen Tilbage for at vende tilbage.</string>
<string name="gps_disabled_my_location">Den aktuelle placering kan ikke bestemmes. Aktivér flere placeringskilder i %1$s, og sørg for, at
@@ -139,6 +235,7 @@
<string name="gps_starting">Starter GPS</string>
<string name="gps_wait_for_better_signal">Venter på et bedre GPS-signal</string>
<string name="gps_wait_for_signal">Venter på GPS-signal</string>
<!-- Image -->
<string name="image_application">Applikation</string>
<string name="image_arrow">Pil</string>
<string name="image_marker">Markør</string>
@@ -149,6 +246,7 @@
<string name="image_record">Registrer</string>
<string name="image_stop">Stop</string>
<string name="image_track">Rute</string>
<!-- Import -->
<string name="import_error">%1$d af %2$s er importeret fra %3$s</string>
<string name="import_no_directory">%1$s findes ikke</string>
<string name="import_no_file">Der var ingen filer at importere. Slut din enhed til din computer, og placer filer, der skal
@@ -158,9 +256,11 @@
<string name="import_selection_option">Alle %1$s</string>
<string name="import_selection_title">Importér fra eksternt lager</string>
<string name="import_success">%1$s er importeret fra %2$s</string>
<!-- Marker -->
<string name="marker_add_error">En markør kan ikke indsættes. Intet GPS-signal. Prøv igen.</string>
<string name="marker_add_success">Der blev indsat en markør</string>
<string name="marker_add_canceled">Annulleret, ingen markør blev tilføjet</string>
<string name="marker_add_photo_canceled">Annulleret, ingen fotomarkering tilføjet</string>
<string name="marker_delete_all_confirm_message">Alle markører for denne rute slettes permanent fra enheden</string>
<string name="marker_delete_one_confirm_message">Markøren slettes permanent fra enheden</string>
<string name="marker_delete_one_confirm_title">Vil du slette markøren?</string>
@@ -170,10 +270,14 @@
<string name="marker_name_format">Markør %1$d</string>
<string name="marker_split_name_format">Deling %1$d</string>
<string name="marker_title">%1$d af %2$d</string>
<!-- Marker Detail -->
<string name="marker_detail_statistics_title">Siden sidste delte markør:</string>
<!-- Marker Edit -->
<string name="marker_edit_marker_type">Markørtype (f.eks. vandfald)</string>
<string name="marker_edit_marker_type_hint">Markørtype</string>
<!-- Marker List -->
<string name="marker_list_empty_message">Du har ingen markører</string>
<!-- Menu -->
<string name="menu_aggregated_statistics">Samlet statistik</string>
<string name="menu_delete">Slet</string>
<string name="menu_edit">Rediger</string>
@@ -182,6 +286,7 @@
<string name="menu_import_all">Importér alle</string>
<string name="menu_insert_marker">Indsæt markør</string>
<string name="menu_insert_photo">Indsæt billede</string>
<string name="menu_insert_gallery_img">Indsæt galleribillede</string>
<string name="menu_markers">Markører</string>
<string name="menu_search">Søg</string>
<string name="menu_select_all">Vælg alle</string>
@@ -194,11 +299,17 @@
<string name="menu_stop_gps">Stop GPS</string>
<string name="menu_view_photo">Se billede</string>
<string name="menu_voice_frequency">Stemmefrekvens</string>
<string name="menu_resume_track">Genoptag sporet</string>
<!-- OpenTracks -->
<string name="app_name">OpenTracks</string>
<string name="recording_service">Registreringstjeneste</string>
<!-- Permission -->
<string name="permission_gps_failed">OpenTracks kræver tilladelse til at bruge GPS.</string>
<!-- Search -->
<string name="search_global_description">Ruter og markører</string>
<string name="search_list_empty_message">Ingen ruter eller markører matchede din søgning</string>
<string name="search_list_marker_track_location">I ruten \"%1$s\".</string>
<!-- Sensor State -->
<string name="sensor_state_battery">Batteriniveau</string>
<string name="sensor_state_cadence">Kadence</string>
<string name="sensor_state_cadence_value">%1$d omdr/min</string>
@@ -207,12 +318,22 @@
<string name="sensor_state_heart_rate_value">%1$d slag per minut</string>
<string name="sensor_state_power">Strøm</string>
<string name="sensor_state_power_value">%1$d W</string>
<string name="sensor_not_known">Sensor %1$s er ukendt. Kontroller indstillingerne.</string>
<string name="sensor_could_not_scan">Kunne ikke scanne efter Bluetooth-enheder (fejl %1$i).</string>
<string name="bluetooth_disabled">Aktivér Bluetooth.</string>
<string name="settings_not_while_recording">Ikke tilgængelig ved registrering af rute</string>
<string name="settings_show">Vis</string>
<!-- Settings Advanced -->
<string name="settings_advanced">Avanceret</string>
<!-- Settings Chart -->
<string name="settings_chart_by_distance">Efter afstand</string>
<string name="settings_chart_by_time">Efter tid</string>
<string name="settings_chart_x_axis">X-akse</string>
<!-- Settings Recording -->
<string name="settings_recording_show_on_lockscreen_while_recording_title">Vis statistik på låseskærm</string>
<string name="settings_recording_show_on_lockscreen_while_recording_summary">Vis statistik, mens du optager, uden at låse enheden op.</string>
<string name="settings_recording_keep_screen_on_while_recording_title">Hold skærmen tændt</string>
<string name="settings_recording_keep_screen_on_while_recording_summary">Hold skærmen tændt, mens du optager.</string>
<string name="settings_recording">Registrering</string>
<string name="settings_recording_auto_resume_track_timeout_always_summary">Genoptag altid en registrering efter en genstart</string>
<string name="settings_recording_auto_resume_track_timeout_never_summary">Genoptag altid en registrering efter en genstart</string>
@@ -230,22 +351,33 @@
<string name="settings_recording_track_name_date_local_option">Dato (lokal)</string>
<string name="settings_recording_track_name_number_option">Nummer</string>
<string name="settings_recording_track_name_title">Standardnavn på spor</string>
<!-- Settings Reset -->
<string name="settings_reset">Nulstil</string>
<string name="settings_reset_confirm_message">Alle indstillinger vil blive ændret til standardværdierne..</string>
<string name="settings_reset_confirm_title">Vil du nulstille nu?</string>
<string name="settings_reset_done">Alle indstillinger er blevet gendannet til standardværdierne</string>
<string name="settings_error_initial_values_restored">Beklager, men der opstod en fejl. Alle indstillinger er vendt tilbage til standardværdierne.</string>
<!-- Settings Sensor -->
<string name="settings_sensor">Sensor</string>
<string name="settings_sensor_bluetooth">Bluetooth</string>
<string name="settings_sensor_bluetooth_sensor">Bluetooth-sensor</string>
<string name="settings_sensor_connecting">Tilsluttet %1$s</string>
<string name="settings_sensor_disconnected">Frakoblet %1$s</string>
<string name="settings_sensor_wheel_circumference">Hjulstørrelse (ETRTO)</string>
<!-- Settings Stats -->
<string name="settings_stats_coordinate">Breddegrad/længdegrad</string>
<string name="settings_stats_rate_title">Foretrukkenfartenhed</string>
<string name="settings_stats_units_imperial">Britisk (mil, ft)</string>
<string name="settings_stats_units_metric">Metrisk (km, m)</string>
<string name="settings_stats_units_title">Foretrukne enheder</string>
<!-- Share Track -->
<string name="share_track_share_file">Del rutefil</string>
<string name="share_track_share_file_body">Du kunne måske være interesseret i denne rute.\n\nDu kan bruge Google Earth eller anden
kortlægningssoftware til at se ruten.\n\n%1$s</string>
<string name="share_track_subject">Jeg vil gerne dele en rute med dig</string>
<!-- Stats -->
<string name="stats_average_moving_pace">Gns. bevægelsestempo</string>
<string name="stats_average_moving_speed">Gns. bevægelseshastighed</string>
<string name="stats_average_pace">Gns. tempo</string>
@@ -264,25 +396,36 @@
<string name="stats_speed">Hastighed</string>
<string name="stats_distance">Afstand</string>
<string name="stats_total_time">Samlet tid</string>
<!-- Track -->
<string name="track_name_format">Rute %1$d</string>
<string name="track_record_notification">Registrerer din rute…</string>
<string name="track_recording_notification_accuracy">Placeringsnøjagtighed: %1$s</string>
<string name="track_markers">Markører på %1$s</string>
<!-- Track Delete -->
<string name="track_delete_one_confirm_message">Ruten og de tilhørende markører slettes permanent fra enheden.</string>
<string name="track_delete_one_confirm_title">Vil du slette ruten?</string>
<string name="track_delete_multiple_confirm_message">De valgte ruter og tilhørende markører slettes permanent fra enheden.</string>
<string name="track_delete_progress_message">Sletter…</string>
<!-- Track Detail -->
<string name="track_detail_chart_tab">Diagram</string>
<string name="track_detail_stats_tab">Statistik</string>
<string name="track_detail_record_success">Registrerer en ny rute</string>
<string name="track_detail_resume_success">Genoptager nu sporet</string>
<!-- Track Edit -->
<string name="track_edit_activity_type">Aktivitetstype (f.eks. vandretur)</string>
<string name="track_edit_activity_type_hint">Aktivitetstype</string>
<string name="track_edit_new_track_title">Ny rute</string>
<!-- Track List -->
<string name="track_list_empty_message">Her kan du gå i gang med at registrere dit næste eventyr</string>
<!-- Track Widget -->
<!-- Tracks -->
<string name="tracks_few">%1$d ruter</string>
<string name="tracks_many">%1$d ruter</string>
<string name="tracks_one">1 rute</string>
<string name="tracks_other">%1$d ruter</string>
<string name="tracks_two">2 ruter</string>
<string name="tracks_zero">0 ruter</string>
<!-- Unit -->
<string name="unit_feet">fod</string>
<string name="unit_kilometer">km</string>
<string name="unit_kilometer_per_hour">km/t</string>
@@ -291,6 +434,7 @@
<string name="unit_mile_per_hour">mil/t</string>
<string name="unit_minute_per_kilometer">min./km</string>
<string name="unit_minute_per_mile">min./mil</string>
<!-- Value -->
<string name="value_adapt_accuracy">Juster nøjagtighed</string>
<string name="value_adapt_battery_life">Tilpas til batterilevetid</string>
<string name="value_always">Altid</string>
@@ -317,6 +461,7 @@
<string name="value_off">Fra</string>
<string name="value_smallest">Mindste</string>
<string name="value_smallest_recommended">Mindst (anbefalet)</string>
<!-- Voice -->
<string name="voice_hours_few">%1$d timer</string>
<string name="voice_hours_many">%1$d timer</string>
<string name="voice_hours_one">1 time</string>
@@ -363,6 +508,7 @@
<string name="voice_total_distance_miles_two">afstand i alt 2 mil</string>
<string name="voice_total_distance_miles_zero">afstand i alt 0 mil</string>
<string name="voice_total_distance_zero">Afstand i alt i OpenTracks er nul.</string>
<!-- Waypoint Type -->
<string name="waypoint_type_atm">Pengeautomat</string>
<string name="waypoint_type_bank">bank</string>
<string name="waypoint_type_bar">bar</string>
@@ -414,4 +560,7 @@
<string name="waypoint_type_waterfall">vandfald</string>
<string name="waypoint_type_water_fountain">springvand</string>
<string name="waypoint_type_waypoint">vejpunkt</string>
<!-- Share image -->
<string name="share_image_subject">Jeg vil gerne dele et billede med dig</string>
<string name="share_image_body">Jeg tror, du måske er interesseret i dette billede.</string>
</resources>
+3 -2
View File
@@ -36,7 +36,7 @@ limitations under the License.
Verwende das kleine Kartensymbol.\n
a)
(Live-Aktualisierungen) Installiere <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>.
OSMDashboard stellt Online- oder Offline-Karten über <a href="https://www.openstreetmap.org">OpenStreetmap</a>.\n zur Verfügung.
OSMDashboard stellt Online- oder Offline-Karten über <a href="https://www.openstreetmap.org">OpenStreetMap</a>.\n zur Verfügung.
Eigentlich ist OSMDashboard eine separate App, die nur (vorübergehend) auf die Daten der anzuzeigenden Strecke(n) zugreifen kann.
\n
@@ -322,11 +322,12 @@ g) das tun, was Deiner Meinung nach OpenTracks hilft.</string>
<string name="sensor_state_power_value">%1$d W</string>
<string name="sensor_not_known">Sensor %1$s ist unbekannt. Bitte überprüfe die Einstellungen.</string>
<string name="sensor_could_not_scan">Konnte nicht nach Bluetooth-Geräten scannen (Fehler %1$i).</string>
<string name="bluetooth_disabled">Bitte Bluetooth aktivieren.</string><!-- Settings -->
<string name="bluetooth_disabled">Bitte Bluetooth aktivieren.</string>
<string name="settings_not_while_recording">Während der Aufzeichnung eines Tracks nicht verfügbar</string>
<string name="settings_show">Anzeigen</string>
<!-- Settings Advanced -->
<string name="settings_advanced">Erweitert</string>
<string name="settings_default_trackfileformat">Dateiformat für export/teilen</string>
<!-- Settings Chart -->
<string name="settings_chart_by_distance">Nach Strecke</string>
<string name="settings_chart_by_time">Nach Zeit</string>
+144 -1
View File
@@ -1,5 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?><!--
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<string name="about_preference_title">Acerca de OpenTracks</string>
<string name="about_description"><i>OpenTracks</i> es una aplicación de seguimiento deportivo que respeta completamente su privacidad.</string>
<string name="about_url">Página web del proyecto: <a href="%1$s">%1$s</a></string>
<string name="about_version_name">Nombre de la versión: %1$s</string>
<string name="about_version_code">Código de la versión: %1$d</string>
<string name="about_history">OpenTracks es una bifurcación de <a href="https://code.google.com/archive/p/mytracks/"><i>MyTracks </i></a> de Google (descontinuado en 2016).</string>
<string name="about_libraries">Bibliotecas</string>
<!-- Help -->
<string name="help_feature_title">OpenTracks tiene las siguientes características:</string>
<string name="help_feature_content">
* crear un seguimiento GPS *
* usa un sensor de ritmo cardíaco (sólo Bluetooth LE) *
* anuncios de voz (por tiempo o distancia)*
* crear marcadores (incluyendo imágenes) *
* funciona completamente fuera de línea *
* mostrar las pistas en un mapa (ver abajo)</string>
<string name="help_track_map_title">¿Cómo mostrar una pista en un mapa?</string>
<string name="help_track_map_content">
Usa el icono del mapa pequeño.
a)
(actualizaciones en vivo) Instalar <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>.
Proporciona mapas en línea o fuera de línea a través de <a href="https://www.openstreetmap.org">OpenStreetMap</a>.\n
En realidad, OSMDashboard es una aplicación independiente que sólo puede acceder (temporalmente) a los datos de las pist(as) que se van a mostrar.
\n
b)
A través de una aplicación de mapas normal (por ejemplo, <a href="http://osmand.net/">OsmAnd</a> y <a href="https://maps.me/">MAPS.ME</a>).
Estas aplicaciones necesitan soportar el formato de archivo KMZ (sólo las ubicaciones y las marcas de tiempo).
De hecho, una aplicación necesita soportar <i>ACTION_VIEW</i> con MIME <i>application/vnd.google-earth.kmz</i>.</string>
<string name="help_storage_pictures_title">¿Cómo almacena OpenTracks las fotos que fueron tomadas como marcadores?</string>
<string name="help_storage_pictures_content">Estos se almacenan en el almacenamiento interno de la aplicación (es decir, sólo accesible dentro de OpenTracks).</string>
<string name="help_track_export_title">¿Cómo puedo exportar/compartir las pistas grabadas?</string>
<string name="help_track_export_content">
Las pistas pueden ser exportadas desde los ajustes como KMZ, KML o GPX.
KMZ/KML todos los datos; GPX sólo las ubicaciones.
Las pistas compartidas a través del <i> icono de compartir </i> se comparten como KMZ.</string>
<string name="help_data_google_play_services_title">¿Requiere OpenTracks usar el servicio de Google Play?</string>
<string name="help_recording_title">¿Cómo registra OpenTracks los datos?</string>
<string name="help_recording_content">
OpenTracks utiliza el sensor GPS del dispositivo para registrar la ubicación de forma continua.
Si el dispositivo GPS reporta datos inexactos (por ejemplo, ubicación, velocidad, elevación), OpenTracks no puede hacer nada al respecto.</string>
<string name="help_data_storage_title">¿Cómo almacena OpenTracks los datos de los sensores?</string>
<string name="help_data_storage_content">
Los datos de los sensores se almacenan por cada ubicación registrada.
En términos de un sensor de frecuencia cardíaca, esto significa que sólo <i>uno</i> la medición de la frecuencia cardíaca se almacena por ubicación.
Si está parado o en el interior, no se registrarán los datos del sensor (pero se mostrarán).</string>
<string name="help_gps_troubleshooting_title">OpenTracks no está grabando las localizaciones?</string>
<string name="help_gps_troubleshooting_content">
Lo que puedes hacer...
a) Comprobar si el GPS está activado.
b) mejorar la recepción del GPS (por ejemplo, ir al exterior),\n
c) comprueba si con <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/">GPSTest</a> app, y\n
d) desactivar el ahorro de energía.
\n
Si todo no ayuda, por favor informe de un error.</string>
<string name="help_support_title">¿Cómo puedo apoyar OpenTracks?</string>
<string name="help_support_content">
Puedes...
a) difundir la palabra sobre OpenTracks,...
b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">proponer características</a>,\n
c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">report bugs</a>,\n
d) ayudar en el desarrollo de software,\n
e) comprar en <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Google Play Store</a>,\n
f) donar, y
g) haz lo que creas que ayude a OpenTracks.</string>
<!-- Activity Type -->
<string name="activity_type_airplane">avión</string>
<string name="activity_type_atv">vehículos todoterreno</string>
<string name="activity_type_biking">ciclismo</string>
@@ -66,6 +155,8 @@
<string name="activity_type_walking">recorrido a pie</string>
<string name="activity_type_water_skiing">esquí acuático</string>
<string name="activity_type_wind_surfing">windsurf</string>
<string name="app_not_installed_show_on_map">Por favor, instale una aplicación que soporte la presentación de datos de mapas.</string>
<!-- Description -->
<string name="description_activity_type">Tipo de actividad: %1$s</string>
<string name="description_average_moving_pace_in_minute">Ritmo de movimiento medio: %1$s min/km (%2$s min/mi)</string>
<string name="description_average_moving_speed">Velocidad de movimiento media: %1$.2f km/h (%2$.1f mi/h)</string>
@@ -93,6 +184,7 @@
<string name="description_sensor_cadence">Cadencia (rpm)</string>
<string name="description_sensor_heart_rate">Frecuencia cardíaca (bpm)</string>
<string name="description_sensor_power">Potencia (W)</string>
<string name="description_default_speed_or_pace">Por la clase de actividad</string>
<string name="description_speed_imperial">Velocidad (mi/h)</string>
<string name="description_speed_metric">Velocidad (km / h)</string>
<string name="description_time">Tiempo</string>
@@ -100,6 +192,7 @@
<string name="description_total_time">Tiempo total: %1$s</string>
<string name="description_track_point">Punto</string>
<string name="description_track_segment">Segmento</string>
<!-- Export -->
<string name="export_all_option">Todo como %1$s</string>
<string name="export_all_title">Exportar a unidad de almacenamiento externa</string>
<string name="export_with_photos">con fotos</string>
@@ -109,12 +202,15 @@
<string name="export_progress_message">Exportando a %1$s…</string>
<string name="export_success">Se exportaron %1$s a %2$s</string>
<string name="export_title">Exportar a…</string>
<!-- External Storage -->
<!-- Files -->
<string name="files_few">%1$d archivos</string>
<string name="files_many">%1$d archivos</string>
<string name="files_one">1 archivo</string>
<string name="files_other">%1$d archivos</string>
<string name="files_two">2 archivos</string>
<string name="files_zero">0 archivos</string>
<!-- Generic -->
<string name="generic_add">Añadir</string>
<string name="generic_cancel">Cancelar</string>
<string name="generic_delete_all_confirm_title">¿Eliminar todo?</string>
@@ -133,6 +229,7 @@
<string name="generic_success_title">Correcto</string>
<string name="generic_tracks">Rutas</string>
<string name="generic_yes">Sí</string>
<!-- Gps -->
<string name="gps_location_access">\"Acceso a la ubicación\"</string>
<string name="gps_disabled">El GPS no está habilitado. Toca aquí para ir a %1$s. Pulsa el botón Atrás para volver.</string>
<string name="gps_disabled_my_location">No se puede determinar la ubicación actual. Habilita más fuentes de ubicación en %1$s y asegúrate de que se pueda ver el cielo. Vuelve a intentarlo más tarde.</string>
@@ -140,6 +237,7 @@
<string name="gps_starting">Iniciando GPS</string>
<string name="gps_wait_for_better_signal">Esperando una mejor señal de GPS</string>
<string name="gps_wait_for_signal">Esperando señal de GPS</string>
<!-- Image -->
<string name="image_application">Aplicación</string>
<string name="image_arrow">Flecha</string>
<string name="image_marker">Marcador</string>
@@ -150,6 +248,7 @@
<string name="image_record">Grabar</string>
<string name="image_stop">Detener</string>
<string name="image_track">Ruta</string>
<!-- Import -->
<string name="import_error">Se importaron %1$d de %2$s desde %3$s</string>
<string name="import_no_directory">%1$s no existe</string>
<string name="import_no_file">No hay archivos que importar. Conecta tu dispositivo al ordenador y coloca los archivos que quieras
@@ -159,6 +258,7 @@
<string name="import_selection_option">Todos los archivos %1$s</string>
<string name="import_selection_title">Importar desde la unidad de almacenamiento externa</string>
<string name="import_success">Se importaron %1$s desde %2$s</string>
<!-- Marker -->
<string name="marker_add_error">No se puede insertar el marcador. No se recibe señal de GPS. Inténtalo de nuevo.</string>
<string name="marker_add_success">Se ha insertado un marcador</string>
<string name="marker_add_canceled">Cancelado, no se ha añadido ningún marcador</string>
@@ -172,10 +272,14 @@
<string name="marker_name_format">Marcador %1$d</string>
<string name="marker_split_name_format">Dividir %1$d</string>
<string name="marker_title">%1$d de %2$d</string>
<!-- Marker Detail -->
<string name="marker_detail_statistics_title">Desde el último marcador de división:</string>
<!-- Marker Edit -->
<string name="marker_edit_marker_type">Tipo de marcador (p. ej., cascada…)</string>
<string name="marker_edit_marker_type_hint">Tipo de marcador</string>
<!-- Marker List -->
<string name="marker_list_empty_message">No tienes ningún marcador</string>
<!-- Menu -->
<string name="menu_aggregated_statistics">Estadísticas globales</string>
<string name="menu_delete">Eliminar</string>
<string name="menu_edit">Editar</string>
@@ -198,11 +302,16 @@
<string name="menu_view_photo">Ver foto</string>
<string name="menu_voice_frequency">Frecuencia de voz</string>
<string name="menu_resume_track">Reanudar ruta</string>
<!-- OpenTracks -->
<string name="app_name">OpenTracks</string>
<string name="recording_service">Servicio de grabación</string>
<!-- Permission -->
<string name="permission_gps_failed">OpenTracks requiere permiso para usar el GPS.</string>
<!-- Search -->
<string name="search_global_description">Recorridos y marcadores</string>
<string name="search_list_empty_message">No hay rutas ni marcadores que coincidan con tu búsqueda</string>
<string name="search_list_marker_track_location">En la ruta \"%1$s\"</string>
<!-- Sensor State -->
<string name="sensor_state_battery">Nivel de batería</string>
<string name="sensor_state_cadence">Cadencia</string>
<string name="sensor_state_cadence_value">%1$d rpm</string>
@@ -211,12 +320,22 @@
<string name="sensor_state_heart_rate_value">%1$d bpm</string>
<string name="sensor_state_power">Potencia</string>
<string name="sensor_state_power_value">%1$d W</string>
<string name="sensor_not_known">Sensor %1$s es desconocido. Por favor, compruebe la configuración.</string>
<string name="sensor_could_not_scan">No se pudieron buscar dispositivos Bluetooth (error %1$i).</string>
<string name="bluetooth_disabled">Por favor, activa el Bluetooth.</string>
<string name="settings_not_while_recording">No disponible mientras se graba una ruta</string>
<string name="settings_show">Mostrar</string>
<!-- Settings Advanced -->
<string name="settings_advanced">Avanzado</string>
<!-- Settings Chart -->
<string name="settings_chart_by_distance">Por distancia</string>
<string name="settings_chart_by_time">Por tiempo</string>
<string name="settings_chart_x_axis">Eje X</string>
<!-- Settings Recording -->
<string name="settings_recording_show_on_lockscreen_while_recording_title">Mostrar las estadísticas en la pantalla de bloqueo</string>
<string name="settings_recording_show_on_lockscreen_while_recording_summary">Mientras graba, muestre las estadísticas sin desbloquear el dispositivo.</string>
<string name="settings_recording_keep_screen_on_while_recording_title">Mantén la pantalla encendida</string>
<string name="settings_recording_keep_screen_on_while_recording_summary">Mientras graba, mantenga la pantalla encendida.</string>
<string name="settings_recording">Grabación</string>
<string name="settings_recording_auto_resume_track_timeout_always_summary">Reanudar siempre una grabación después de reiniciar</string>
<string name="settings_recording_auto_resume_track_timeout_never_summary">No reanudar nunca una grabación después de reiniciar</string>
@@ -234,21 +353,32 @@
<string name="settings_recording_track_name_date_local_option">Fecha (local)</string>
<string name="settings_recording_track_name_number_option">Número</string>
<string name="settings_recording_track_name_title">Nombre de la ruta</string>
<!-- Settings Reset -->
<string name="settings_reset">Restablecer</string>
<string name="settings_reset_confirm_message">Todos los ajustes volverán a los valores predeterminados.</string>
<string name="settings_reset_confirm_title">¿Restablecer ahora?</string>
<string name="settings_reset_done">Se han restablecido los valores predeterminados de todas las opciones de configuración</string>
<string name="settings_error_initial_values_restored">Lo siento, pero ha habido un error. Todos los ajustes se han vuelto a los valores predeterminados.</string>
<!-- Settings Sensor -->
<string name="settings_sensor">Sensor</string>
<string name="settings_sensor_bluetooth">Bluetooth</string>
<string name="settings_sensor_bluetooth_sensor">Sensor Bluetooth</string>
<string name="settings_sensor_connecting">Conectado a %1$s</string>
<string name="settings_sensor_disconnected">Desconectado de %1$s</string>
<string name="settings_sensor_wheel_circumference">Tamaño de la rueda (ETRTO)</string>
<!-- Settings Stats -->
<string name="settings_stats_coordinate">Latitud/Longitud</string>
<string name="settings_stats_rate_title">Ritmo preferido</string>
<string name="settings_stats_units_imperial">Imperiales (millas, pies)</string>
<string name="settings_stats_units_metric">Métricas (km, m)</string>
<string name="settings_stats_units_title">Unidades preferidas</string>
<!-- Share Track -->
<string name="share_track_share_file">Compartir el archivo de la ruta</string>
<string name="share_track_share_file_body">Creo que esta ruta te podría interesar.\n\nPara ver esta ruta, usa Google Earth u otro software de mapas.\n\n%1$s</string>
<string name="share_track_subject">Me gustaría compartir una ruta contigo</string>
<!-- Stats -->
<string name="stats_average_moving_pace">Ritmo de movimiento medio</string>
<string name="stats_average_moving_speed">Velocidad de movimiento media</string>
<string name="stats_average_pace">Ritmo medio</string>
@@ -267,26 +397,36 @@
<string name="stats_speed">Velocidad</string>
<string name="stats_distance">Distancia</string>
<string name="stats_total_time">Tiempo total</string>
<!-- Track -->
<string name="track_name_format">Ruta %1$d</string>
<string name="track_record_notification">Grabando tu ruta…</string>
<string name="track_recording_notification_accuracy">Precisión de la localización: %1$s</string>
<string name="track_markers">Marcadores de %1$s</string>
<!-- Track Delete -->
<string name="track_delete_one_confirm_message">La ruta y sus marcadores se eliminarán de forma permanente del dispositivo.</string>
<string name="track_delete_one_confirm_title">¿Eliminar ruta?</string>
<string name="track_delete_multiple_confirm_message">Las rutas seleccionadas y sus marcadores se eliminarán de forma permanente del dispositivo.</string>
<string name="track_delete_progress_message">Eliminando…</string>
<!-- Track Detail -->
<string name="track_detail_chart_tab">Gráfico</string>
<string name="track_detail_stats_tab">Estadísticas</string>
<string name="track_detail_record_success">Grabando una nueva ruta</string>
<string name="track_detail_resume_success">Ahora reanudando la pista</string>
<!-- Track Edit -->
<string name="track_edit_activity_type">Tipo de actividad (p. ej., senderismo…)</string>
<string name="track_edit_activity_type_hint">Tipo de actividad</string>
<string name="track_edit_new_track_title">Nueva pista</string>
<!-- Track List -->
<string name="track_list_empty_message">Empieza a grabar aquí tu próxima aventura</string>
<!-- Track Widget -->
<!-- Tracks -->
<string name="tracks_few">%1$d rutas</string>
<string name="tracks_many">%1$d rutas</string>
<string name="tracks_one">1 ruta</string>
<string name="tracks_other">%1$d rutas</string>
<string name="tracks_two">2 rutas</string>
<string name="tracks_zero">0 ruta</string>
<!-- Unit -->
<string name="unit_feet">pies</string>
<string name="unit_kilometer">km</string>
<string name="unit_kilometer_per_hour">km/h</string>
@@ -295,6 +435,7 @@
<string name="unit_mile_per_hour">millas/h</string>
<string name="unit_minute_per_kilometer">min/km</string>
<string name="unit_minute_per_mile">min/milla</string>
<!-- Value -->
<string name="value_adapt_accuracy">Adaptar para obtener valores exactos</string>
<string name="value_adapt_battery_life">Adaptar a la duración de la batería</string>
<string name="value_always">Siempre</string>
@@ -321,6 +462,7 @@
<string name="value_off">No</string>
<string name="value_smallest">Menor</string>
<string name="value_smallest_recommended">Mínimo (recomendado)</string>
<!-- Voice -->
<string name="voice_hours_few">%1$d horas</string>
<string name="voice_hours_many">%1$d horas</string>
<string name="voice_hours_one">1 hora</string>
@@ -367,6 +509,7 @@
<string name="voice_total_distance_miles_two">distancia total de 2 millas</string>
<string name="voice_total_distance_miles_zero">distancia total de 0 millas</string>
<string name="voice_total_distance_zero">La distancia total de OpenTracks es igual a cero.</string>
<!-- Waypoint Type -->
<string name="waypoint_type_atm">cajero automático</string>
<string name="waypoint_type_bank">banco</string>
<string name="waypoint_type_bar">bar</string>
+151 -1
View File
@@ -1,5 +1,92 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?><!--
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<string name="about_preference_title">Umbes OpenTracks</string>
<string name="about_description"><i>OpenTracks</i> on spordi jälgimise rakendus, mis austab täielikult teie privaatsust.</string>
<string name="about_url">Projekti veebileht: <a href="%1$s">%1$s</a></string>
<string name="about_version_name">Versiooni nimi: %1$s</string>
<string name="about_version_code">Versiooni kood: %1$d</string>
<string name="about_history">OpenTracks on <a href="https://code.google.com/archive/p/mytracks/"> <i> Googlei MyTracks </i> </a> kahvel (lõpetatud 2016. aastal).</string>
<string name="about_libraries">Raamatukogud</string>
<!-- Help -->
<string name="help_feature_title">OpenTracks sellel on järgmised omadused:</string>
<string name="help_feature_content">
* GPS-radade loomine \ n
* kasutage pulsisensorit (ainult Bluetooth LE) \ n
* häälteated (aja või vahemaa järgi) \ n
* loo markerid (koos piltidega) \ n
* töötab täiesti võrguühenduseta \ n
* näidata radu kaardil (vt allpool)</string>
<string name="help_track_map_title">Kuidas näidata pala kaardil?</string>
<string name="help_track_map_content">
Kasutage väikest kaardiikooni. \n
a)
(reaalajas värskendused) Installige <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>.
See pakub võrgu- või võrguühenduseta kaarte <a href="https://www.openstreetmap.org">OpenStreetMap</a> kaudu.\n
Tegelikult on OSMDashboard eraldiseisev rakendus, mis pääseb juurde (ajutiselt) ainult kuvatavate rada(de) andmetele.
\n
b) „Tavalise” kaardirakenduse (nt <a href="http://osmand.net/">OsmAnd</a> ja <a href="https://maps.me/">MAPS.ME</a>.
Need rakendused peavad toetama KMZ-failivormingut (ainult asukohad ja ajatemplid).
Tegelikult peab rakendus toetama rakendust MIME <i>application/vnd.google-earth.</i> <i>ACTION_VIEW</i>.</string>
<string name="help_storage_pictures_title">Kuidas OpenTracks salvestab markeritena tehtud pilte?</string>
<string name="help_storage_pictures_content">Neid hoitakse rakenduse sisemises salvestusruumis (st pääseb juurde ainult OpenTracks is).</string>
<string name="help_track_export_title">Kuidas saab salvestatud lugusid eksportida / jagada?</string>
<string name="help_track_export_content">
Lugusid saab seadetest eksportida KMZ-, KML- või GPX-vormingus.
KMZ / KML kõik andmed; Ainult GPX-i asukohad.
Radasid, mis on jagatud <i> jagamise ikooni </i> kaudu, jagatakse KMZ-na.</string>
<string name="help_data_google_play_services_title">Kas OpenTracks nõuab / kasutab Google Play teenust?</string>
<string name="help_recording_title">Kuidas OpenTracks andmeid registreerib?</string>
<string name="help_recording_content">
OpenTracks kasutab seadme GPS-andurit asukoha pidevaks registreerimiseks.
Kui GPS-seade teatab ebatäpsetest andmetest (nt asukoht, kiirus, kõrgus), ei saa OpenTracks sellega midagi teha.</string>
<string name="help_data_storage_title">Kuidas OpenTracks sensori andmeid salvestab?</string>
<string name="help_data_storage_content">
Anduri andmed salvestatakse salvestatud asukoha kohta.
Südame löögisageduse anduri puhul tähendab see, et asukoha kohta salvestatakse ainult <i>üks</i> südame löögisageduse mõõtmine.
Kui seisate paigal või siseruumides, siis anduri andmeid ei registreerita (kuid need näidatakse).</string>
<string name="help_gps_troubleshooting_title">OpenTracks ei salvesta asukohti?</string>
<string name="help_gps_troubleshooting_content">
Mida saate teha: \ n
a) kontrollige, kas GPS on lubatud, \ n
b) parandage GPS-vastuvõttu (nt käige õues), \ n
c) kontrollige, kas rakendusega <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/"> GPSTest </a> ja \ n
d) keelake energiasääst. \ n
\ n
Kui kõik ei aita, teatage veast.</string>
<string name="help_support_title">Kuidas OpenTracks it toetada?</string>
<string name="help_support_content">
Saate: \ n
a) levitage sõna OpenTracks kohta, \ n
b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues"> funktsioonide pakkumine </a>, \ n
c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues"> teatage vigadest </a>, \ n
d) abi tarkvara arendamisel, \ n
e) ostke sisse <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore"> Google Play pood </a>, \ n
f) annetama ja \ n
g) tehke kõike, mis teie arvates OpenTracks aitab.</string>
<!-- Activity Type -->
<string name="activity_type_airplane">lennuk</string>
<string name="activity_type_atv">ATV</string>
<string name="activity_type_biking">rattasõit</string>
@@ -66,6 +153,8 @@
<string name="activity_type_walking">kõndimine</string>
<string name="activity_type_water_skiing">veesuusatamine</string>
<string name="activity_type_wind_surfing">purilauasõit</string>
<string name="app_not_installed_show_on_map">Installige rakendus, mis toetab kaardiandmete esitamist.</string>
<!-- Description -->
<string name="description_activity_type">Tegevuse tüüp: %1$s</string>
<string name="description_average_moving_pace_in_minute">Keskmine liikumiskiirus: %1$s min/km (%2$s min/mi)</string>
<string name="description_average_moving_speed">Keskmine liikumiskiirus: %1$.2f km/h (%2$.1f mi/h)</string>
@@ -93,6 +182,7 @@
<string name="description_sensor_cadence">Rütm (p/min)</string>
<string name="description_sensor_heart_rate">Pulsisagedus (l/min)</string>
<string name="description_sensor_power">Võimsus (W)</string>
<string name="description_default_speed_or_pace">Tegevuse tüübi järgi</string>
<string name="description_speed_imperial">Kiirus (mi/h)</string>
<string name="description_speed_metric">Kiirus (km/h)</string>
<string name="description_time">Aeg</string>
@@ -100,19 +190,25 @@
<string name="description_total_time">Koguaeg: %1$s</string>
<string name="description_track_point">Punkt</string>
<string name="description_track_segment">Lõik</string>
<!-- Export -->
<string name="export_all_option">Kõik %1$s-failina</string>
<string name="export_all_title">Ekspordi välismällu</string>
<string name="export_with_photos">fotodega</string>
<string name="export_without_photos">ilma fotodeta</string>
<string name="export_error">Eksporditud %1$d faili %2$s-st kataloogi %3$s</string>
<string name="export_option">%1$s-failina kataloogi %2$s</string>
<string name="export_progress_message">Eksportimine kataloogi %1$s…</string>
<string name="export_success">Eksporditud %1$s rada kataloogi %2$s</string>
<string name="export_title">Eksportimine …</string>
<!-- External Storage -->
<!-- Files -->
<string name="files_few">%1$d faili</string>
<string name="files_many">%1$d faili</string>
<string name="files_one">1 fail</string>
<string name="files_other">%1$d faili</string>
<string name="files_two">2 faili</string>
<string name="files_zero">0 faili</string>
<!-- Generic -->
<string name="generic_add">Lisa</string>
<string name="generic_cancel">Tühista</string>
<string name="generic_delete_all_confirm_title">Kas kustutada kõik?</string>
@@ -131,6 +227,7 @@
<string name="generic_success_title">Õnnestus</string>
<string name="generic_tracks">Rajad</string>
<string name="generic_yes">Jah</string>
<!-- Gps -->
<string name="gps_location_access">Juurdepääs asukohale</string>
<string name="gps_disabled">GPS ei ole lubatud. Kohta %1$s minemiseks puudutage seda. Naasmiseks vajutage nuppu Tagasi.</string>
<string name="gps_disabled_my_location">Praegust asukohta ei saa kindlaks teha. Lubage rohkem asukohaallikaid menüüs %1$s ja veenduge, et taevas on nähtav. Proovige hiljem uuesti.</string>
@@ -138,6 +235,7 @@
<string name="gps_starting">GPS-i käivitamine</string>
<string name="gps_wait_for_better_signal">Oodatakse paremat GPS-signaali</string>
<string name="gps_wait_for_signal">Oodatakse GPS-signaali</string>
<!-- Image -->
<string name="image_application">Rakendus</string>
<string name="image_arrow">Nool</string>
<string name="image_marker">Marker</string>
@@ -148,6 +246,7 @@
<string name="image_record">Salvesta</string>
<string name="image_stop">Peata</string>
<string name="image_track">Rada</string>
<!-- Import -->
<string name="import_error">Imporditud %1$d faili %2$s-st kataloogist %3$s</string>
<string name="import_no_directory">Kataloogi %1$s ei ole olemas</string>
<string name="import_no_file">Imporditavad failid puudusid. Ühendage seade oma arvutiga ning salvestage imporditavad failid kataloogi %1$s.</string>
@@ -156,9 +255,11 @@
<string name="import_selection_option">Kõik %1$s-failid</string>
<string name="import_selection_title">Impordi välismälust</string>
<string name="import_success">Imporditud %1$s faili kataloogist %2$s</string>
<!-- Marker -->
<string name="marker_add_error">Ei saa markerit lisada. GPS-signaal puudub. Proovige uuesti.</string>
<string name="marker_add_success">Marker lisati</string>
<string name="marker_add_canceled">Tühistatud, markerit ei lisatud</string>
<string name="marker_add_photo_canceled">Tühistatud, fotomarkerit pole lisatud</string>
<string name="marker_delete_all_confirm_message">Kõik selle raja markerid kustutatakse seadmest jäädavalt</string>
<string name="marker_delete_one_confirm_message">Marker kustutatakse seadmest jäädavalt</string>
<string name="marker_delete_one_confirm_title">Kas kustutada marker?</string>
@@ -168,10 +269,14 @@
<string name="marker_name_format">Marker %1$d</string>
<string name="marker_split_name_format">Jaotus %1$d</string>
<string name="marker_title">%1$d/%2$d</string>
<!-- Marker Detail -->
<string name="marker_detail_statistics_title">Viimasest jaotusmarkerist alates:</string>
<!-- Marker Edit -->
<string name="marker_edit_marker_type">Markeri tüüp (nt juga)</string>
<string name="marker_edit_marker_type_hint">Markeri tüüp</string>
<!-- Marker List -->
<string name="marker_list_empty_message">Teil ei ole markereid</string>
<!-- Menu -->
<string name="menu_aggregated_statistics">Agreg.statistika</string>
<string name="menu_delete">Kustuta</string>
<string name="menu_edit">Muuda</string>
@@ -180,6 +285,7 @@
<string name="menu_import_all">Impordi kõik</string>
<string name="menu_insert_marker">Lisa marker</string>
<string name="menu_insert_photo">Foto lisamine</string>
<string name="menu_insert_gallery_img">Sisestage galerii pilt</string>
<string name="menu_markers">Markerid</string>
<string name="menu_search">Otsing</string>
<string name="menu_select_all">Vali kõik</string>
@@ -192,11 +298,17 @@
<string name="menu_stop_gps">Peata GPS</string>
<string name="menu_view_photo">Foto kuvamine</string>
<string name="menu_voice_frequency">Häälteadete sagedus</string>
<string name="menu_resume_track">Jätka pala</string>
<!-- OpenTracks -->
<string name="app_name">OpenTracks</string>
<string name="recording_service">Salvestamisteenus</string>
<!-- Permission -->
<string name="permission_gps_failed">OpenTracks vajab GPS-i kasutamiseks luba.</string>
<!-- Search -->
<string name="search_global_description">Rajad ja markerid</string>
<string name="search_list_empty_message">Ükski rada ega marker ei sobinud teie otsinguga</string>
<string name="search_list_marker_track_location">Rajal „%1$s”.</string>
<!-- Sensor State -->
<string name="sensor_state_battery">Aku tase</string>
<string name="sensor_state_cadence">Rütm</string>
<string name="sensor_state_cadence_value">%1$d rpm</string>
@@ -205,12 +317,22 @@
<string name="sensor_state_heart_rate_value">%1$d lööki minutis</string>
<string name="sensor_state_power">Võimsus</string>
<string name="sensor_state_power_value">%1$d W</string>
<string name="sensor_not_known">Andur %1$s pole teada. Kontrollige seadeid.</string>
<string name="sensor_could_not_scan">Bluetooth-seadmeid ei õnnestunud skannida (tõrge %1$i).</string>
<string name="bluetooth_disabled">Lubage Bluetooth.</string>
<string name="settings_not_while_recording">Raja salvestamise ajal ei ole kättesaadav</string>
<string name="settings_show">Kuva</string>
<!-- Settings Advanced -->
<string name="settings_advanced">Täpsemad seaded</string>
<!-- Settings Chart -->
<string name="settings_chart_by_distance">Vahemaa järgi</string>
<string name="settings_chart_by_time">Aja järgi</string>
<string name="settings_chart_x_axis">X-telg</string>
<!-- Settings Recording -->
<string name="settings_recording_show_on_lockscreen_while_recording_title">Kuva lukukraanil statistikat</string>
<string name="settings_recording_show_on_lockscreen_while_recording_summary">Salvestamise ajal näidake statistikat ilma seadet avamata.</string>
<string name="settings_recording_keep_screen_on_while_recording_title">Hoidke ekraani peal</string>
<string name="settings_recording_keep_screen_on_while_recording_summary">Salvestamise ajal hoidke ekraani sisse lülitatud.</string>
<string name="settings_recording">Salvestamine</string>
<string name="settings_recording_auto_resume_track_timeout_always_summary">Jätka alati salvestamist pärast taaskäivitamist</string>
<string name="settings_recording_auto_resume_track_timeout_never_summary">Ärge kunagi jätka salvestamist pärast taaskäivitamist</string>
@@ -228,21 +350,32 @@
<string name="settings_recording_track_name_date_local_option">Kuupäev (kohalik)</string>
<string name="settings_recording_track_name_number_option">Number</string>
<string name="settings_recording_track_name_title">Raja vaikenimi</string>
<!-- Settings Reset -->
<string name="settings_reset">Lähtesta</string>
<string name="settings_reset_confirm_message">Taastatakse kõikide seadete vaikeväärtus.</string>
<string name="settings_reset_confirm_title">Kas lähtestada kohe?</string>
<string name="settings_reset_done">Kõik seaded on taastatud vaikeväärtustena</string>
<string name="settings_error_initial_values_restored">Vabandust, kuid ilmnes viga. Kõik sätted on taastatud vaikeväärtustele.</string>
<!-- Settings Sensor -->
<string name="settings_sensor">Andur</string>
<string name="settings_sensor_bluetooth">Bluetooth</string>
<string name="settings_sensor_bluetooth_sensor">Bluetoothi andur</string>
<string name="settings_sensor_connecting">Ühendatud anduriga %1$s</string>
<string name="settings_sensor_disconnected">Lahti ühendatud %1$s</string>
<string name="settings_sensor_wheel_circumference">Ratta suurus (ETRTO)</string>
<!-- Settings Stats -->
<string name="settings_stats_coordinate">Laiuskraad/pikkuskraad</string>
<string name="settings_stats_rate_title">Eelistatud määr</string>
<string name="settings_stats_units_imperial">Inglise mõõtühikud (miili, jalga)</string>
<string name="settings_stats_units_metric">Meetermõõdustik (km, m)</string>
<string name="settings_stats_units_title">Eelistatud ühikud</string>
<!-- Share Track -->
<string name="share_track_share_file">Jagage rajafaili</string>
<string name="share_track_share_file_body">Teile võib see rada huvi pakkuda.\n\nRaja vaatamiseks kasutage rakendust Google Earth või muud kaardistamistarkvara.\n\n%1$s</string>
<string name="share_track_subject">Ma soovin teiega rada jagada</string>
<!-- Stats -->
<string name="stats_average_moving_pace">Keskmine liikumistempo</string>
<string name="stats_average_moving_speed">Keskmine liikumiskiirus</string>
<string name="stats_average_pace">Keskmine tempo</string>
@@ -261,25 +394,36 @@
<string name="stats_speed">Kiirus</string>
<string name="stats_distance">Kaugus</string>
<string name="stats_total_time">Aeg kokku</string>
<!-- Track -->
<string name="track_name_format">Rada %1$d</string>
<string name="track_record_notification">Raja salvestamine…</string>
<string name="track_recording_notification_accuracy">Asukoha täpsus: %1$s</string>
<string name="track_markers">Raja %1$s markerid</string>
<!-- Track Delete -->
<string name="track_delete_one_confirm_message">Rada ja selle markerid kustutatakse seadmest jäädavalt.</string>
<string name="track_delete_one_confirm_title">Kas kustutada rada?</string>
<string name="track_delete_multiple_confirm_message">Valitud rajad ja nende markerid kustutatakse seadmest jäädavalt.</string>
<string name="track_delete_progress_message">Kustutamine…</string>
<!-- Track Detail -->
<string name="track_detail_chart_tab">Diagramm</string>
<string name="track_detail_stats_tab">Statistika</string>
<string name="track_detail_record_success">Nüüd salvestatakse uut rada</string>
<string name="track_detail_resume_success">Nüüd jätkame rada</string>
<!-- Track Edit -->
<string name="track_edit_activity_type">Tegevus (nt matkamine)</string>
<string name="track_edit_activity_type_hint">Tegevuse tüüp</string>
<string name="track_edit_new_track_title">Uus rada</string>
<!-- Track List -->
<string name="track_list_empty_message">Alustage oma järgmise seikluse salvestamist siin</string>
<!-- Track Widget -->
<!-- Tracks -->
<string name="tracks_few">%1$d rada</string>
<string name="tracks_many">%1$d rada</string>
<string name="tracks_one">1 rada</string>
<string name="tracks_other">%1$d rada</string>
<string name="tracks_two">2 rada</string>
<string name="tracks_zero">0 rada</string>
<!-- Unit -->
<string name="unit_feet">jalga</string>
<string name="unit_kilometer">km</string>
<string name="unit_kilometer_per_hour">km/h</string>
@@ -288,6 +432,7 @@
<string name="unit_mile_per_hour">miili/h</string>
<string name="unit_minute_per_kilometer">min/km</string>
<string name="unit_minute_per_mile">min/miilis</string>
<!-- Value -->
<string name="value_adapt_accuracy">Täpsuse kohandamine</string>
<string name="value_adapt_battery_life">Kohandage aku elueale</string>
<string name="value_always">Alati</string>
@@ -314,6 +459,7 @@
<string name="value_off">Väljas</string>
<string name="value_smallest">Kõige väiksem</string>
<string name="value_smallest_recommended">Väikseim (soovitatav)</string>
<!-- Voice -->
<string name="voice_hours_few">%1$d tundi</string>
<string name="voice_hours_many">%1$d tundi</string>
<string name="voice_hours_one">1 tund</string>
@@ -360,6 +506,7 @@
<string name="voice_total_distance_miles_two">kogupikkus 2 miili</string>
<string name="voice_total_distance_miles_zero">kogupikkus 0 miili</string>
<string name="voice_total_distance_zero">Radade kogupikkus on null.</string>
<!-- Waypoint Type -->
<string name="waypoint_type_atm">Sularahaautomaat</string>
<string name="waypoint_type_bank">pank</string>
<string name="waypoint_type_bar">baar</string>
@@ -411,4 +558,7 @@
<string name="waypoint_type_waterfall">juga</string>
<string name="waypoint_type_water_fountain">purskkaev</string>
<string name="waypoint_type_waypoint">teekonnapunkt</string>
<!-- Share image -->
<string name="share_image_subject">Tahaksin teiega pilti jagada</string>
<string name="share_image_body">Ma arvan, et see pilt võiks teid huvitada.</string>
</resources>
+149 -1
View File
@@ -1,5 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?><!--
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<string name="about_preference_title">OpenTracks-i buruz</string>
<string name="about_description"><i> OpenTracks </i> kirol jarraipena egiteko aplikazioa da, zure pribatutasuna erabat errespetatzen duena.</string>
<string name="about_url">Proiektuaren web orria: <a href="%1$s">%1$s</a></string>
<string name="about_version_name">Bertsioaren izena: %1$s</string>
<string name="about_version_code">Bertsio kodea: %1$d</string>
<string name="about_history">OpenTracks <a href="https://code.google.com/archive/p/mytracks/"> <i> Google \ n MyTracks </i> </a> (horixe utzi zen 2016an).</string>
<string name="about_libraries">Liburutegiak</string>
<!-- Help -->
<string name="help_feature_title">OpenTracks ek ezaugarri hauek ditu:</string>
<string name="help_feature_content">
* GPS ibilbideak sortu\n
* erabili bihotz-maiztasuneko sentsorea (Bluetooth LE soilik)\n
* ahots iragarpenak (denboraren edo distantziaren arabera)\n
* Sortu markatzaileak (irudiak barne)\n
* guztiz lineaz kanpo funtzionatzen du\n
* Erakutsi ibilbideak mapa batean (ikusi azpian)</string>
<string name="help_track_map_title">Nola erakutsi pista batean mapa batean?</string>
<string name="help_track_map_content">
Erabili maparen ikono txikia.\n
a) (zuzeneko eguneratzeak) Instalatu <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>.
Lineako edo lineaz kanpoko mapak eskaintzen ditu <a href="https://www.openstreetmap.org">OpenStreetMap</a>.\n
Egia esan, OSMDashboard erakundea da erakutsiko diren pista (k) datuetara sartzeko soilik (aldi baterako) sar daitekeena.\n
b) Mapa aplikazio normal baten bidez (adibidez, <a href="http://osmand.net/">OsmAnd</a> eta <a href="https://maps.me/">MAPS.ME</a>).
Aplikazio horiek KMZ fitxategi formatua onartzen dute (kokapenak eta timestamps soilik).
Izan ere, aplikazio batek <i>ACTION_VIEW</i> onartzen du MIME <i>application/vnd.google-earth.kmz</i> rekin.</string>
<string name="help_storage_pictures_title">Nola gordetzen du OpenTracks markatzaile gisa ateratako argazkiak?</string>
<string name="help_storage_pictures_content">Aplikazioaren barne biltegian gordetzen dira (hau da, OpenTracks en soilik eskura daiteke).</string>
<string name="help_track_export_title">Nola esportatu/berdinak diren ibilbideak grabatutako ibilbideak?</string>
<string name="help_track_export_content">
Pistak Ezarpenetatik KMZ, KML edo GPX gisa esportatu daitezke.
KMZ / KML datu guztiak; GPX kokapenak soilik.
<i> Partekatutako ikonoa </i> bidez partekatutako ibilbideak KMZ gisa partekatzen dira.</string>
<string name="help_data_google_play_services_title">OpenTracks-ek Google Play zerbitzua erabiltzea eskatzen al du?</string>
<string name="help_recording_title">Nola grabatzen ditu OpenTracks datuak?</string>
<string name="help_recording_content">
OpenTracks gailuaren GPS sentsorea erabiltzen du kokapena etengabe grabatzeko.
GPS gailuak datu okerrak salatzen baditu (adibidez, kokapena, abiadura, kota), OpenTracks-ek ezin du ezer egin.</string>
<string name="help_data_storage_title">Nola gordetzen du OpenTracks ek sentsoreen datuak?</string>
<string name="help_data_storage_content">
Sentsoreen datuak grabatutako kokapen bakoitzeko gordetzen dira.
Bihotz-maiztasuneko sentsoreari dagokionez, horrek esan nahi du <i> bat </i> bihotz-tasaren neurketa kokapen bakoitzean gordetzen dela.
Geldirik egon edo barrualdean bazaude, ez da sentsoreen daturik grabatuko (baina erakutsiko da).</string>
<string name="help_gps_troubleshooting_title">OpenTracks ez dira kokapenak grabatzen?</string>
<string name="help_gps_troubleshooting_content">
Zer egin dezakezu: \n
a) egiaztatu GPSa gaituta dagoen ala ez, \n
b) GPS harrera hobetu (adibidez, kanpora joan), \n
c) egiaztatu <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/"> GPSTest </a> aplikazioarekin eta \n
d) desgaitu energia aurreztea. \n
\n
Dena laguntzen ez baduzu, jakinarazi akats bat.</string>
<string name="help_support_title">Nola onartzen dut OpenTracks?</string>
<string name="help_support_content">
Aukera hau: \ n
a) zabaldu OpenTracks-i buruzko hitza, \ n
b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues"> proposatu ezaugarriak </a>, \ n
c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues"> txostenen akatsak </a>, \ n
d) softwarearen garapenean laguntzeko, \ n
e) erosi <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore"> Google Play Store </a>, \ n
f) eman, eta \ n
g) Egin uste duzu OpenTracks-ek laguntzen duela.</string>
<!-- Activity Type -->
<string name="activity_type_airplane">hegazkina</string>
<string name="activity_type_atv">Lurreko ibilgailu guztiak</string>
<string name="activity_type_biking">ziklismoa</string>
@@ -66,6 +151,8 @@
<string name="activity_type_walking">oinez</string>
<string name="activity_type_water_skiing">uretako eskia</string>
<string name="activity_type_wind_surfing">windsurfa</string>
<string name="app_not_installed_show_on_map">Mesedez, instalatu maparen datuak aurkezten dituen aplikazioa.</string>
<!-- Description -->
<string name="description_activity_type">Jarduera mota: %1$s</string>
<string name="description_average_moving_pace_in_minute">Batez besteko abiadura, abian: %1$s min/km (%2$s min/mi)</string>
<string name="description_average_moving_speed">Batez besteko abiadura, abian: %1$.2f km/h (%2$.1f mi/h)</string>
@@ -93,6 +180,7 @@
<string name="description_sensor_cadence">Kadentzia (rpm)</string>
<string name="description_sensor_heart_rate">Maiztasun kardiakoa (bpm)</string>
<string name="description_sensor_power">Potentzia (W)</string>
<string name="description_default_speed_or_pace">Jarduera motaren arabera</string>
<string name="description_speed_imperial">Abiadura (mi/h)</string>
<string name="description_speed_metric">Abiadura (km/h)</string>
<string name="description_time">Iraupena</string>
@@ -100,19 +188,25 @@
<string name="description_total_time">Denbora, guztira: %1$s</string>
<string name="description_track_point">Puntua</string>
<string name="description_track_segment">Segmentua</string>
<!-- Export -->
<string name="export_all_option">Guztiak %1$s</string>
<string name="export_all_title">Esportatu kanpoko biltegira</string>
<string name="export_with_photos">argazkiekin</string>
<string name="export_without_photos">argazkirik gabe</string>
<string name="export_error">%1$d/%2$s esportatu dira %3$s direktoriora</string>
<string name="export_option">%1$s gisa %2$s karpetara</string>
<string name="export_progress_message">%1$s karpetara esportatzen…</string>
<string name="export_success">%1$s %2$s direktoriora esportatu dira</string>
<string name="export_title">Esportatu hona…</string>
<!-- External Storage -->
<!-- Files -->
<string name="files_few">%1$d fitxategi</string>
<string name="files_many">%1$d fitxategi</string>
<string name="files_one">1 fitxategi</string>
<string name="files_other">%1$d fitxategi</string>
<string name="files_two">2 fitxategi</string>
<string name="files_zero">0 fitxategi</string>
<!-- Generic -->
<string name="generic_add">Gehitu</string>
<string name="generic_cancel">Utzi</string>
<string name="generic_delete_all_confirm_title">Guztiak ezabatu nahi dituzu?</string>
@@ -131,6 +225,7 @@
<string name="generic_success_title">Behar bezala burutu da</string>
<string name="generic_tracks">Ibilbideak</string>
<string name="generic_yes">Bai</string>
<!-- Gps -->
<string name="gps_location_access">\"Kokapenerako sarbidea\"</string>
<string name="gps_disabled">GPSa ez dago gaituta. %1$s atalera joateko, sakatu hau. Itzultzeko, sakatu Atzera botoia.</string>
<string name="gps_disabled_my_location">Ezin da uneko kokapena zehaztu. Gaitu kokapen-iturburu gehiago %1$s atalean eta ziurtatu zerua ikus dezakezula. Saiatu berriro geroago.</string>
@@ -138,6 +233,7 @@
<string name="gps_starting">GPSa hasten</string>
<string name="gps_wait_for_better_signal">GPS seinale hobearen zain</string>
<string name="gps_wait_for_signal">GPS seinalearen zain</string>
<!-- Image -->
<string name="image_application">Aplikazioa</string>
<string name="image_arrow">Gezia</string>
<string name="image_marker">Markatzailea</string>
@@ -148,6 +244,7 @@
<string name="image_record">Grabatu</string>
<string name="image_stop">Gelditu</string>
<string name="image_track">Ibilbidea</string>
<!-- Import -->
<string name="import_error">%1$d/%2$s inportatu dira %3$s direktoriotik</string>
<string name="import_no_directory">%1$s ez dago</string>
<string name="import_no_file">Ez dago fitxategirik inportatzeko. Konektatu gailua ordenagailura eta ezarri inportatu beharreko fitxategiak %1$s karpetan.</string>
@@ -156,9 +253,11 @@
<string name="import_selection_option">%1$s guztiak</string>
<string name="import_selection_title">Inportatu kanpoko memoriatik</string>
<string name="import_success">%1$s %2$s direktoriotik inportatu da</string>
<!-- Marker -->
<string name="marker_add_error">Ezin da markatzailea txertatu. Ez dago GPS seinalerik. Saiatu berriro.</string>
<string name="marker_add_success">Markatzailea txertatu da</string>
<string name="marker_add_canceled">Bertan behera utzi da; ez da markatzailea gehitu.</string>
<string name="marker_add_photo_canceled">Ezeztatu, ez da argazki-markarik gehitu</string>
<string name="marker_delete_all_confirm_message">Ibilbideko markatzaile guztiak behin betiko ezabatuko dira gailutik</string>
<string name="marker_delete_one_confirm_message">Markatzailea behin betiko ezabatuko da gailutik</string>
<string name="marker_delete_one_confirm_title">Markatzailea ezabatu nahi duzu?</string>
@@ -168,10 +267,14 @@
<string name="marker_name_format">%1$d markatzailea</string>
<string name="marker_split_name_format">Zatitu %1$d</string>
<string name="marker_title">%1$d/%2$d</string>
<!-- Marker Detail -->
<string name="marker_detail_statistics_title">Azken zatitze-markatzaileaz geroztik:</string>
<!-- Marker Edit -->
<string name="marker_edit_marker_type">Markatzaile mota (adibidez, ur-jauzia)</string>
<string name="marker_edit_marker_type_hint">Markatzaile mota</string>
<!-- Marker List -->
<string name="marker_list_empty_message">Ez duzu markatzailerik</string>
<!-- Menu -->
<string name="menu_aggregated_statistics">Estatistika orokorrak</string>
<string name="menu_delete">Ezabatu</string>
<string name="menu_edit">Editatu</string>
@@ -180,6 +283,7 @@
<string name="menu_import_all">Inportatu guztiak</string>
<string name="menu_insert_marker">Txertatu markatzailea</string>
<string name="menu_insert_photo">Txertatu argazkia</string>
<string name="menu_insert_gallery_img">Txertatu galeriako irudia</string>
<string name="menu_markers">Markatzaileak</string>
<string name="menu_search">Bilatu</string>
<string name="menu_select_all">Hautatu guztiak</string>
@@ -192,11 +296,17 @@
<string name="menu_stop_gps">Gelditu GPSa</string>
<string name="menu_view_photo">Ikusi argazkia</string>
<string name="menu_voice_frequency">Ahotsaren frekuentzia</string>
<string name="menu_resume_track">Jarraitu pista</string>
<!-- OpenTracks -->
<string name="app_name">OpenTracks</string>
<string name="recording_service">Grabaketa-zerbitzua</string>
<!-- Permission -->
<string name="permission_gps_failed">OpenTracks GPSa erabiltzeko baimena behar du.</string>
<!-- Search -->
<string name="search_global_description">Ibilbideak eta markatzaileak</string>
<string name="search_list_empty_message">Ez da bilaketarekin bat datorren ibilbide edo markatzailerik aurkitu</string>
<string name="search_list_marker_track_location">\"%1$s\" ibilbidean.</string>
<!-- Sensor State -->
<string name="sensor_state_battery">Bateria maila</string>
<string name="sensor_state_cadence">Erritmoa</string>
<string name="sensor_state_cadence_value">%1$d rpm</string>
@@ -205,12 +315,22 @@
<string name="sensor_state_heart_rate_value">%1$d bpm</string>
<string name="sensor_state_power">Potentzia</string>
<string name="sensor_state_power_value">%1$d W</string>
<string name="sensor_not_known">Sensor %1$s ezezaguna da. Egiaztatu ezarpenak.</string>
<string name="sensor_could_not_scan">Ezin izan da Bluetooth gailuak bilatu (%1$i errorea).</string>
<string name="bluetooth_disabled">Mesedez, aktibatu Bluetooth.</string>
<string name="settings_not_while_recording">Ez dago erabilgarri ibilbidea grabatzen ari den bitartean.</string>
<string name="settings_show">Erakutsi</string>
<!-- Settings Advanced -->
<string name="settings_advanced">Aurreratua</string>
<!-- Settings Chart -->
<string name="settings_chart_by_distance">Distantziaren arabera</string>
<string name="settings_chart_by_time">Denboraren arabera</string>
<string name="settings_chart_x_axis">X ardatza</string>
<!-- Settings Recording -->
<string name="settings_recording_show_on_lockscreen_while_recording_title">Erakutsi estatistika blokeo-pantailan</string>
<string name="settings_recording_show_on_lockscreen_while_recording_summary">Grabatzen ari zaren bitartean, erakutsi estatistikak gailua desblokeatu gabe.</string>
<string name="settings_recording_keep_screen_on_while_recording_title">Mantendu pantaila</string>
<string name="settings_recording_keep_screen_on_while_recording_summary">Grabatzen ari zaren bitartean, mantendu pantaila.</string>
<string name="settings_recording">Grabaketa</string>
<string name="settings_recording_auto_resume_track_timeout_always_summary">Berrabiarazi ondoren, berrekin grabaketei</string>
<string name="settings_recording_auto_resume_track_timeout_never_summary">Berrabiarazi ondoren, ez berrekin grabaketei</string>
@@ -228,21 +348,32 @@
<string name="settings_recording_track_name_date_local_option">Data (tokikoa)</string>
<string name="settings_recording_track_name_number_option">Zenbakia</string>
<string name="settings_recording_track_name_title">Ibilbidearen izen lehenetsia</string>
<!-- Settings Reset -->
<string name="settings_reset">Berrezarri</string>
<string name="settings_reset_confirm_message">Ezarpen guztiak balio lehenetsietara leheneratuko dira.</string>
<string name="settings_reset_confirm_title">Berrezarri egin nahi duzu?</string>
<string name="settings_reset_done">Ezarpen guztiak balio lehenetsietara leheneratu dira</string>
<string name="settings_error_initial_values_restored">Sentitzen dugu, baina akats bat gertatu da. Ezarpen guztiak lehenetsitako balioetara itzuli dira.</string>
<!-- Settings Sensor -->
<string name="settings_sensor">Sentsorea</string>
<string name="settings_sensor_bluetooth">Bluetootha</string>
<string name="settings_sensor_bluetooth_sensor">Bluetooth sentsorea</string>
<string name="settings_sensor_connecting">Honekin konektatuta: %1$s</string>
<string name="settings_sensor_disconnected">Deskonektatuta %1$s</string>
<string name="settings_sensor_wheel_circumference">Gurpilaren tamaina (ETRTO)</string>
<!-- Settings Stats -->
<string name="settings_stats_coordinate">Latitudea/Longitudea</string>
<string name="settings_stats_rate_title">Erritmo hobetsia</string>
<string name="settings_stats_units_imperial">Inperiala (mi, ft)</string>
<string name="settings_stats_units_metric">Metrikoa (km, m)</string>
<string name="settings_stats_units_title">Unitate hobetsiak</string>
<!-- Share Track -->
<string name="share_track_share_file">Partekatu ibilbide-fitxategia</string>
<string name="share_track_share_file_body">Ibilbidea interesgarria gerta dakizukeela uste dut.\n\nIbilbidea ikusteko, erabili Google Earth edo beste kartografia-softwareren bat.\n\n%1$s</string>
<string name="share_track_subject">Bide bat partekatu nahi nuke zurekin</string>
<!-- Stats -->
<string name="stats_average_moving_pace">Batez besteko pausoa, mugitzerakoan</string>
<string name="stats_average_moving_speed">Batez besteko abiadura, mugitzerakoan</string>
<string name="stats_average_pace">Batez besteko pausoa</string>
@@ -261,25 +392,36 @@
<string name="stats_speed">Abiadura</string>
<string name="stats_distance">Distantzia</string>
<string name="stats_total_time">Denbora, guztira</string>
<!-- Track -->
<string name="track_name_format">%1$d. bidea</string>
<string name="track_record_notification">Ibilbidea grabatzen…</string>
<string name="track_recording_notification_accuracy">Kokapenaren zehaztasuna: %1$s</string>
<string name="track_markers">%1$s ibilbideko markatzaileak</string>
<!-- Track Delete -->
<string name="track_delete_one_confirm_message">Bidea eta bideko markatzaileak behin betiko ezabatuko dira gailutik.</string>
<string name="track_delete_one_confirm_title">Ibilbidea ezabatu nahi duzu?</string>
<string name="track_delete_multiple_confirm_message">Hautatutako bidea eta bideko markatzaileak behin betiko ezabatuko dira gailutik.</string>
<string name="track_delete_progress_message">Ezabatzen…</string>
<!-- Track Detail -->
<string name="track_detail_chart_tab">Diagrama</string>
<string name="track_detail_stats_tab">Estatistikak</string>
<string name="track_detail_record_success">Ibilbide berria grabatzen ari da</string>
<string name="track_detail_resume_success">Orain pista berriro hasi zen</string>
<!-- Track Edit -->
<string name="track_edit_activity_type">Jarduera mota (adibidez, mendi-txangoa)</string>
<string name="track_edit_activity_type_hint">Jarduera mota</string>
<string name="track_edit_new_track_title">Ibilbide berria</string>
<!-- Track List -->
<string name="track_list_empty_message">Hasi hurrengo abentura grabatzen</string>
<!-- Track Widget -->
<!-- Tracks -->
<string name="tracks_few">%1$d ibilbide</string>
<string name="tracks_many">%1$d ibilbide</string>
<string name="tracks_one">1 ibilbide</string>
<string name="tracks_other">%1$d ibilbide</string>
<string name="tracks_two">2 ibilbide</string>
<string name="tracks_zero">0 ibilbide</string>
<!-- Unit -->
<string name="unit_feet">oin</string>
<string name="unit_kilometer">km</string>
<string name="unit_kilometer_per_hour">km/h</string>
@@ -288,6 +430,7 @@
<string name="unit_mile_per_hour">mi/h</string>
<string name="unit_minute_per_kilometer">min/km</string>
<string name="unit_minute_per_mile">min/mi</string>
<!-- Value -->
<string name="value_adapt_accuracy">Doitu zehaztasuna</string>
<string name="value_adapt_battery_life">Doitu bateriaren iraupenera</string>
<string name="value_always">Beti</string>
@@ -314,6 +457,7 @@
<string name="value_off">Desaktibatuta</string>
<string name="value_smallest">Txikiena</string>
<string name="value_smallest_recommended">Txikiena (gomendatua)</string>
<!-- Voice -->
<string name="voice_hours_few">%1$d ordu</string>
<string name="voice_hours_many">%1$d ordu</string>
<string name="voice_hours_one">1 ordu</string>
@@ -360,6 +504,7 @@
<string name="voice_total_distance_miles_two">distantzia, guztira: 2 milia</string>
<string name="voice_total_distance_miles_zero">distantzia, guztira: 0 milia</string>
<string name="voice_total_distance_zero">Nire ibilbideen distantzia, guztira, zero da.</string>
<!-- Waypoint Type -->
<string name="waypoint_type_atm">kutxazaina</string>
<string name="waypoint_type_bank">bankua</string>
<string name="waypoint_type_bar">taberna</string>
@@ -411,4 +556,7 @@
<string name="waypoint_type_waterfall">ur-jauzia</string>
<string name="waypoint_type_water_fountain">iturria</string>
<string name="waypoint_type_waypoint">bideko puntua</string>
<!-- Share image -->
<string name="share_image_subject">Irudi bat zurekin partekatu nahi nuke</string>
<string name="share_image_body">Irudi hau interesatuko zaizuela uste dut.</string>
</resources>
+152 -8
View File
@@ -1,5 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?><!--
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<string name="about_preference_title">À propos d\'OpenTracks</string>
<string name="about_description"><i>OpenTracks</i> est une application de suivi sportif qui respecte totalement votre vie privée.</string>
<string name="about_url">Page web du projet: <a href="%1$s">%1$s</a></string>
<string name="about_version_name">Nom de la version: %1$s</string>
<string name="about_version_code">Code de version: %1$d</string>
<string name="about_history">OpenTracks est une bifurcation de <a href="https://code.google.com/archive/p/mytracks/"><i>Google\'s MyTracks</i></a> (abandonné en 2016).</string>
<string name="about_libraries">Bibliothèques</string>
<!-- Help -->
<string name="help_feature_title">OpenTracks présente les caractéristiques suivantes:</string>
<string name="help_feature_content">
* créer des traces GPS\n
* utiliser un capteur de fréquence cardiaque (Bluetooth LE uniquement)\n
* annonces vocales (par temps ou distance)\n
* créer des marqueurs (y compris des images)\n
* fonctionne complètement hors ligne
* montrer les traces sur une carte (voir ci-dessous)</string>
<string name="help_track_map_title">Comment faire apparaître une trace sur une carte ?</string>
<string name="help_track_map_content">
Utilisez la petite icône de carte.\n
a)
(mises à jour en direct) Installez <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>.
Il fournit des cartes en ligne ou hors ligne via <a href="https://www.openstreetmap.org ">OpenStreetMap</a>.\n
En fait, OSMDashboard est une application séparée qui ne peut accéder (temporairement) qu aux données des piste(s) qui vont être affichées.
\n
b)
Via une application cartographique normale (par exemple) , <a href="http://osmand.net/">OsmAnd</a> et <a href="https://maps.me/">MAPS.ME</a>
Ces applications doivent prendre en charge le format de fichier KMZ (uniquement les lieux et les horodatages).
En fait, une application doit prendre en charge <i>ACTION_VIEW</i> avec MIME <i>application/vnd.google-earth.kmz</i>.</string>
<string name="help_storage_pictures_title">Comment OpenTracks stocke-t-il les photos qui ont été prises comme marqueurs ?</string>
<string name="help_storage_pictures_content">Elles sont stockées dans le stockage interne de lapplication (c est-à-dire uniquement accessible dans OpenTracks).</string>
<string name="help_track_export_title">Comment puis-je exporter/partager des pistes enregistrées ?</string>
<string name="help_track_export_content">
Les pistes peuvent être exportées à partir des paramètres sous forme de KMZ, KML ou GPX.
KMZ/KML toutes les données; GPX seulement les emplacements.
Les pistes partagées via l icône <i>partager</i> sont partagées sous la forme KMZ.</string>
<string name="help_data_google_play_services_title">OpenTracks nécessite-t-il/utilise-t-il le service Google Play ?</string>
<string name="help_recording_title">Comment OpenTracks enregistre-t-il les données ?</string>
<string name="help_recording_content">
OpenTracks utilise le capteur GPS de lappareil pour enregistrer la position en continu.
Si le dispositif GPS signale des données inexactes (par exemple, la localisation, la vitesse, laltitude), OpenTracks ne peut rien y faire.</string>
<string name="help_data_storage_title">Comment OpenTracks stocke-t-il les données des capteurs?</string>
<string name="help_data_storage_content">
Les données des capteurs sont stockées par emplacement enregistré.
Dans le cas dun capteur de fréquence cardiaque, cela signifie que seule la mesure de la fréquence cardiaque <i>one</i> est stockée par emplacement.
Si vous êtes à larrêt ou à lintérieur, aucune donnée du capteur ne sera enregistrée (mais elle sera affichée).</string>
<string name="help_gps_troubleshooting_title">OpenTracks nenregistre pas les lieux?</string>
<string name="help_gps_troubleshooting_content">
Ce que vous pouvez faire :\n
a) vérifier si le GPS est activé,\n
b) améliorer la réception GPS (par exemple, aller à lextérieur),\n
c) vérifier si avec <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/">GPSTest</a> app, et\n
d) désactiver léconomie dénergie.\n
\n
Si tout ne va pas dans le bon sens, veuillez signaler un bug.</string>
<string name="help_support_title">Comment puis-je soutenir OpenTracks?</string>
<string name="help_support_content">
Vous pouvez:\n
a) faire connaître OpenTracks,\n
b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">proposer des fonctionnalités</a>,\n
c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">rapporter les bugs</a>,\n
d) aide au développement de logiciels,\n
e) acheter dans <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Google Play Store</a>,\n
f) faire un don, et
g) faire tout ce qui vous semble utile à OpenTracks.</string>
<!-- Activity Type -->
<string name="activity_type_airplane">navigation aérienne</string>
<string name="activity_type_atv">quad</string>
<string name="activity_type_biking">cyclisme</string>
@@ -66,6 +154,8 @@
<string name="activity_type_walking">marche</string>
<string name="activity_type_water_skiing">ski nautique</string>
<string name="activity_type_wind_surfing">planche à voile</string>
<string name="app_not_installed_show_on_map">Veuillez installer une application qui prend en charge la présentation des données cartographiques.</string>
<!-- Description -->
<string name="description_activity_type">Type d\'activité : %1$s</string>
<string name="description_average_moving_pace_in_minute">Allure moyenne : %1$s min/km (%2$s min/mi)</string>
<string name="description_average_moving_speed">Vitesse moyenne de déplacement : %1$.2f km/h (%2$.1f mi/h)</string>
@@ -93,6 +183,7 @@
<string name="description_sensor_cadence">Cadence (tr/min)</string>
<string name="description_sensor_heart_rate">Fréquence cardiaque (bpm)</string>
<string name="description_sensor_power">Puissance (W)</string>
<string name="description_default_speed_or_pace">Par type d activité</string>
<string name="description_speed_imperial">Vitesse (mi/h)</string>
<string name="description_speed_metric">Vitesse (km/h)</string>
<string name="description_time">Durée</string>
@@ -100,19 +191,25 @@
<string name="description_total_time">Durée totale : %1$s</string>
<string name="description_track_point">Point</string>
<string name="description_track_segment">Segment</string>
<!-- Export -->
<string name="export_all_option">Tous les parcours au format %1$s</string>
<string name="export_all_title">Exporter vers la mémoire de stockage externe</string>
<string name="export_with_photos">avec les photos</string>
<string name="export_without_photos">sans les photos</string>
<string name="export_error">%1$d parcours sur %2$s exporté(s) vers le répertoire %3$s.</string>
<string name="export_option">au format %1$s vers le répertoire %2$s</string>
<string name="export_progress_message">Exportation vers le répertoire %1$s en cours…</string>
<string name="export_success">%1$s parcours exporté(s) vers le répertoire %2$s.</string>
<string name="export_title">Exporter vers…</string>
<!-- External Storage -->
<!-- Files -->
<string name="files_few">%1$d fichiers</string>
<string name="files_many">%1$d fichiers</string>
<string name="files_one">1 fichier</string>
<string name="files_other">%1$d fichiers</string>
<string name="files_two">2 fichiers</string>
<string name="files_zero">Aucun fichier</string>
<!-- Generic -->
<string name="generic_add">Ajouter</string>
<string name="generic_cancel">Annuler</string>
<string name="generic_delete_all_confirm_title">Tout supprimer ?</string>
@@ -131,6 +228,7 @@
<string name="generic_success_title">Opération réussie</string>
<string name="generic_tracks">Parcours</string>
<string name="generic_yes">Oui</string>
<!-- Gps -->
<string name="gps_location_access">\"Données de localisation\"</string>
<string name="gps_disabled">Le GPS n\'est pas activé. Appuyez ici pour accéder aux %1$s. Appuyez sur le bouton Retour pour revenir en arrière.</string>
<string name="gps_disabled_my_location">Impossible de déterminer la position actuelle. Veuillez activer d\'autres sources de localisation dans les %1$s et vous assurer
@@ -139,6 +237,7 @@
<string name="gps_starting">Lancement du GPS en cours…</string>
<string name="gps_wait_for_better_signal">En attente d\'un signal GPS de meilleure qualité…</string>
<string name="gps_wait_for_signal">En attente du signal GPS…</string>
<!-- Image -->
<string name="image_application">Application</string>
<string name="image_arrow">Flèche</string>
<string name="image_marker">Repère</string>
@@ -149,6 +248,7 @@
<string name="image_record">Enregistrer</string>
<string name="image_stop">Arrêter</string>
<string name="image_track">Parcours</string>
<!-- Import -->
<string name="import_error">%1$d fichier(s) sur %2$s importé(s) depuis le répertoire %3$s.</string>
<string name="import_no_directory">Le répertoire %1$s n\'existe pas.</string>
<string name="import_no_file">Aucun fichier à importer. Connectez votre appareil à l\'ordinateur, puis déplacez les fichiers à importer vers le répertoire %1$s.</string>
@@ -157,9 +257,11 @@
<string name="import_selection_option">Tous les parcours au format %1$s</string>
<string name="import_selection_title">Importer depuis la mémoire de stockage externe</string>
<string name="import_success">%1$s fichier(s) importé(s) depuis le répertoire %2$s.</string>
<!-- Marker -->
<string name="marker_add_error">Impossible d\'insérer un repère. Pas de signal GPS. Veuillez réessayer.</string>
<string name="marker_add_success">Un repère a bien été inséré.</string>
<string name="marker_add_canceled">Opération annulée. Aucun repère n\'a été ajouté.</string>
<string name="marker_add_photo_canceled">Annulé, aucun marqueur de photo ajouté</string>
<string name="marker_delete_all_confirm_message">Tous les repères de ce parcours vont être définitivement supprimés de l\'appareil.</string>
<string name="marker_delete_one_confirm_message">Le repère va être définitivement supprimé de l\'appareil.</string>
<string name="marker_delete_one_confirm_title">Supprimer le repère ?</string>
@@ -169,10 +271,14 @@
<string name="marker_name_format">Repère %1$d</string>
<string name="marker_split_name_format">Point de cheminement %1$d</string>
<string name="marker_title">%1$d sur %2$d</string>
<!-- Marker Detail -->
<string name="marker_detail_statistics_title">Depuis le dernier point de cheminement :</string>
<!-- Marker Edit -->
<string name="marker_edit_marker_type">Type de repère (exemple : cascade)</string>
<string name="marker_edit_marker_type_hint">Type de repère</string>
<!-- Marker List -->
<string name="marker_list_empty_message">Vous n\'avez aucun repère.</string>
<!-- Menu -->
<string name="menu_aggregated_statistics">Statistiques cumulées</string>
<string name="menu_delete">Supprimer</string>
<string name="menu_edit">Modifier</string>
@@ -181,6 +287,7 @@
<string name="menu_import_all">Tout importer</string>
<string name="menu_insert_marker">Insérer un repère</string>
<string name="menu_insert_photo">Insérer une photo</string>
<string name="menu_insert_gallery_img">Insérer une image de la galerie</string>
<string name="menu_markers">Repères</string>
<string name="menu_search">Rechercher</string>
<string name="menu_select_all">Tout sélectionner</string>
@@ -193,11 +300,17 @@
<string name="menu_stop_gps">Arrêter le GPS</string>
<string name="menu_view_photo">Afficher la photo</string>
<string name="menu_voice_frequency">Fréquence vocale</string>
<string name="menu_resume_track">Reprendre la piste</string>
<!-- OpenTracks -->
<string name="app_name">OpenTracks</string>
<string name="recording_service">Service enregistrement</string>
<!-- Permission -->
<string name="permission_gps_failed">OpenTracks nécessite une autorisation pour utiliser le GPS.</string>
<!-- Search -->
<string name="search_global_description">Parcours et repères</string>
<string name="search_list_empty_message">Aucun parcours ni repère ne correspond à votre recherche.</string>
<string name="search_list_marker_track_location">Dans le parcours \"%1$s\"</string>
<!-- Sensor State -->
<string name="sensor_state_battery">Niveau de la batterie</string>
<string name="sensor_state_cadence">Cadence</string>
<string name="sensor_state_cadence_value">%1$d tr/min</string>
@@ -206,12 +319,22 @@
<string name="sensor_state_heart_rate_value">%1$d bpm</string>
<string name="sensor_state_power">Puissance</string>
<string name="sensor_state_power_value">%1$d W</string>
<string name="sensor_not_known">Capteur %1$s est inconnu. Veuillez vérifier les paramètres.</string>
<string name="sensor_could_not_scan">Impossible de rechercher les appareils Bluetooth (erreur %1$i).</string>
<string name="bluetooth_disabled">Veuillez activer Bluetooth.</string>
<string name="settings_not_while_recording">Non disponible pendant l\'enregistrement d\'un parcours.</string>
<string name="settings_show">Afficher</string>
<!-- Settings Advanced -->
<string name="settings_advanced">Avancé</string>
<!-- Settings Chart -->
<string name="settings_chart_by_distance">En fonction de la distance</string>
<string name="settings_chart_by_time">En fonction de la durée</string>
<string name="settings_chart_x_axis">Axe X</string>
<!-- Settings Recording -->
<string name="settings_recording_show_on_lockscreen_while_recording_title">Afficher les statistiques sur l\'écran de verrouillage</string>
<string name="settings_recording_show_on_lockscreen_while_recording_summary">Pendant l\'enregistrement, afficher les statistiques sans déverrouiller l\'appareil.</string>
<string name="settings_recording_keep_screen_on_while_recording_title">Garder l\'écran allumé</string>
<string name="settings_recording_keep_screen_on_while_recording_summary">Pendant l\'enregistrement, garder l\'écran allumé.</string>
<string name="settings_recording">Enregistrement</string>
<string name="settings_recording_auto_resume_track_timeout_always_summary">Toujours reprendre l\'enregistrement après un redémarrage.</string>
<string name="settings_recording_auto_resume_track_timeout_never_summary">Ne jamais reprendre l\'enregistrement après un redémarrage.</string>
@@ -229,21 +352,32 @@
<string name="settings_recording_track_name_date_local_option">Date (format local)</string>
<string name="settings_recording_track_name_number_option">Numéro</string>
<string name="settings_recording_track_name_title">Nom parcours par défaut</string>
<!-- Settings Reset -->
<string name="settings_reset">Réinitialiser</string>
<string name="settings_reset_confirm_message">Les valeurs par défaut seront rétablies pour tous les paramètres.</string>
<string name="settings_reset_confirm_title">Réinitialiser maintenant ?</string>
<string name="settings_reset_done">Les valeurs par défaut de tous les paramètres ont bien été rétablies.</string>
<string name="settings_error_initial_values_restored">Désolé, mais une erreur sest produite. Tous les paramètres ont été rétablis aux valeurs par défaut.</string>
<!-- Settings Sensor -->
<string name="settings_sensor">Dispositif de mesure</string>
<string name="settings_sensor_bluetooth">Bluetooth</string>
<string name="settings_sensor_bluetooth_sensor">Capteur Bluetooth</string>
<string name="settings_sensor_connecting">Connecté à %1$s</string>
<string name="settings_sensor_disconnected">Déconnecté de %1$s</string>
<string name="settings_sensor_wheel_circumference">Taille des roues (ETRTO)</string>
<!-- Settings Stats -->
<string name="settings_stats_coordinate">Latitude/Longitude</string>
<string name="settings_stats_rate_title">Mode de calcul souhaité</string>
<string name="settings_stats_units_imperial">Unités impériales (mi, pi)</string>
<string name="settings_stats_units_metric">Unités métriques (km, m)</string>
<string name="settings_stats_units_title">Unités souhaitées</string>
<!-- Share Track -->
<string name="share_track_share_file">Partager le fichier du parcours</string>
<string name="share_track_share_file_body">Je pense que ce parcours peut t\'intéresser.\n\nPour l\'afficher, utilise Google Earth ou un autre logiciel de cartographie.\n\n%1$s</string>
<string name="share_track_subject">J\'aimerais partager un parcours avec toi</string>
<!-- Stats -->
<string name="stats_average_moving_pace">Allure moyenne de déplacement</string>
<string name="stats_average_moving_speed">Vitesse moyenne de déplacement</string>
<string name="stats_average_pace">Allure moyenne</string>
@@ -262,25 +396,36 @@
<string name="stats_speed">Vitesse</string>
<string name="stats_distance">Distance</string>
<string name="stats_total_time">Durée totale</string>
<!-- Track -->
<string name="track_name_format">Parcours %1$d</string>
<string name="track_record_notification">Enregistrement du parcours…</string>
<string name="track_recording_notification_accuracy">Précision de la localisation: %1$s</string>
<string name="track_markers">Repères pour le parcours %1$s</string>
<!-- Track Delete -->
<string name="track_delete_one_confirm_message">Ce parcours et ses repères vont être définitivement effacés de l\'appareil.</string>
<string name="track_delete_one_confirm_title">Supprimer le parcours ?</string>
<string name="track_delete_multiple_confirm_message">Les parcours sélectionnés et leurs repères respectifs vont être définitivement effacés de l\'appareil.</string>
<string name="track_delete_progress_message">Suppression en cours…</string>
<!-- Track Detail -->
<string name="track_detail_chart_tab">Graphique</string>
<string name="track_detail_stats_tab">Statistiques</string>
<string name="track_detail_record_success">Enregistrement d\'un nouveau parcours en cours…</string>
<string name="track_detail_resume_success">Reprenons maintenant la piste</string>
<!-- Track Edit -->
<string name="track_edit_activity_type">Type d\'activité (exemple : randonnée)</string>
<string name="track_edit_activity_type_hint">Type d\'activité</string>
<string name="track_edit_new_track_title">Nouveau parcours</string>
<!-- Track List -->
<string name="track_list_empty_message">Commencez à enregistrer votre prochaine aventure ici.</string>
<!-- Track Widget -->
<!-- Tracks -->
<string name="tracks_few">%1$d parcours</string>
<string name="tracks_many">%1$d parcours</string>
<string name="tracks_one">1 parcours</string>
<string name="tracks_other">%1$d parcours</string>
<string name="tracks_two">2 parcours</string>
<string name="tracks_zero">Aucun parcours</string>
<!-- Unit -->
<string name="unit_feet">pied(s)</string>
<string name="unit_kilometer">km</string>
<string name="unit_kilometer_per_hour">km/h</string>
@@ -289,6 +434,7 @@
<string name="unit_mile_per_hour">miles/h</string>
<string name="unit_minute_per_kilometer">min/km</string>
<string name="unit_minute_per_mile">min/mi</string>
<!-- Value -->
<string name="value_adapt_accuracy">Adapter pour la précision</string>
<string name="value_adapt_battery_life">Économiser la batterie</string>
<string name="value_always">Toujours</string>
@@ -315,6 +461,7 @@
<string name="value_off">Désactiver</string>
<string name="value_smallest">La plus faible</string>
<string name="value_smallest_recommended">La plus petite (recommandé)</string>
<!-- Voice -->
<string name="voice_hours_few">%1$d heures</string>
<string name="voice_hours_many">%1$d heures</string>
<string name="voice_hours_one">1 heure</string>
@@ -361,6 +508,7 @@
<string name="voice_total_distance_miles_two">distance totale 2 miles</string>
<string name="voice_total_distance_miles_zero">distance totale 0 mile</string>
<string name="voice_total_distance_zero">Mes parcours, distance nulle.</string>
<!-- Waypoint Type -->
<string name="waypoint_type_atm">distributeur de billets</string>
<string name="waypoint_type_bank">banque</string>
<string name="waypoint_type_bar">bar</string>
@@ -412,11 +560,7 @@
<string name="waypoint_type_waterfall">cascade</string>
<string name="waypoint_type_water_fountain">fontaine</string>
<string name="waypoint_type_waypoint">point de cheminement</string>
<string name="settings_recording_show_on_lockscreen_while_recording_title">Afficher les statistiques sur l\'écran de verrouillage</string>
<string name="settings_recording_show_on_lockscreen_while_recording_summary">Pendant l\'enregistrement, afficher les statistiques sans déverrouiller l\'appareil.</string>
<string name="settings_recording_keep_screen_on_while_recording_title">Garder l\'écran allumé</string>
<string name="settings_recording_keep_screen_on_while_recording_summary">Pendant l\'enregistrement, garder l\'écran allumé.</string>
<string name="about_preference_title">À propos d\'OpenTracks</string>
<string name="export_with_photos">avec les photos</string>
<string name="export_without_photos">sans les photos</string>
<!-- Share image -->
<string name="share_image_subject">J aimerais partager une image avec vous</string>
<string name="share_image_body">Je pense que cette image pourrait vous intéresser.</string>
</resources>
+155 -1
View File
@@ -1,5 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?><!--
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<string name="about_preference_title">Acerca de OpenTracks</string>
<string name="about_description"><i>OpenTracks</i> é unha aplicación para rexistrar as túas actividades deportivas que respecta a túa privacidade.</string>
<string name="about_url">Web do proxecto: <a href="%1$s">%1$s</a></string>
<string name="about_version_name">Nome da versión: %1$s</string>
<string name="about_version_code">Código da versión: %1$d</string>
<string name="about_history">OpenTracks é un fork de <a href="https://code.google.com/archive/p/mytracks/"><i>MyTracks de Google</i></a>(abandonado en 2016)</string>
<string name="about_libraries">Bibliotecas</string>
<!-- Help -->
<string name="help_feature_title">OpenTracks ten as seguintes funcións:</string>
<string name="help_feature_content">
* crea rutas GPS\n
* usa monitor do pulso cardíaco (só Bluetooth LE)\n
* anuncios por voz (de tempo ou distancia)\n
* crea marcadores (incl. fotos)\n
* funciona sen precisar conexión a rede\n
* mostra as rutas nun mapa (ver abaixo)
</string>
<string name="help_track_map_title">¿Como mostrar a ruta nun mapa?</string>
<string name="help_track_map_content">
Usa a pequena icona do mapa.\n
a)
(actualizacións ao vivo) Instala <a href="https://github.com/OpenTracksApp/OSMDashboard">OSMDashboard</a>.
Proporciona mapas con e sen conexión desde <a href="https://www.openstreetmap.org">OpenStreetMap</a>.\n
En realidade, OSMDashboard é unha aplicación independiente que só pode acceder (temporalmente) aos datos das rutas que se van a mostrar.
\n
b)\n
A través dunha app \"normal\" como <a href="http://osmand.net/">OsmAnd</a> ou <a href="https://maps.me/">MAPS.ME</a>.
Estas apps deben soportar o formato de ficheiros KMZ (só localización e marcas de tempo).
De feito, unha app precisa soportar <i>ACTION_VIEW</i> con MIME <i>application/vnd.google-earth.kmz</i>.</string>
<string name="help_storage_pictures_title">¿De que xeito garda OpenTracks as fotos tomadas como marcadores?</string>
<string name="help_storage_pictures_content">Gárdanse no almacenaxe interno da app (só accesible desde OpenTracks)</string>
<string name="help_track_export_title">¿De que xeito exporto/importo as rutas gravadas?</string>
<string name="help_track_export_content">
As rutas pódense exportar desde os Axustes como KMZ, KML ou GPX.
KMZ/KML inclúe todos os datos; GPX só localización.
As ruts compartidas a través da <i>icona compartir</i> compártense como KMZ.</string>
<string name="help_data_google_play_services_title">¿OpenTracks precisa/utiliza Google Play Service?</string>
<string name="help_recording_title">¿Como grava OpenTracks os datos?</string>
<string name="help_recording_content">
OpenTracks usa o sensor GPS do dispositivo para gravar a localización de xeito contínuo.
Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, elevación), OpenTracks non pode facer nada ao respecto.</string>
<string name="help_data_storage_title">¿De que xeito garda OpenTracks os datos dos sensores?</string>
<string name="help_data_storage_content">
Os datos dos sensores gárdanse asociados a localización.
Respecto do ritmo cardíaco, significa que só se garda <i>unha medida</i> asociada a unha localización.
Se te detés ou estás en interiores, non se gardarán datos do sensor (mais serán mostrados). </string>
<string name="help_gps_troubleshooting_title">¿OpenTracks non grava as localizacións?</string>
<string name="help_gps_troubleshooting_content">
Podes intentar:\n
a) mira que o GPS esté activado,\n
b) mellora a recepción GPS (ex. sae ao exterior),\n
c) comprobaa coa app <a href="https://f-droid.org/en/packages/com.android.gpstest.osmdroid/">GPSTest</a>, e\n
d) desactiva o aforro de enerxía.\n
\n
Se isto non axuda, infórmanos do problema.</string>
<string name="help_support_title">¿Como podo axudar a OpenTracks?</string>
<string name="help_support_content">
Podes:\n
a) falar de OpenTracks coas túas amizades,\n
b) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">propoñer novas funcións</a>,\n
c) <a href="https://github.com/OpenTracksApp/OpenTracks/issues">informar de fallos</a>,\n
d) axudar no desenvolvemento,\n
e) mercar a app na <a href="https://play.google.com/store/apps/details?id=de.dennisguse.opentracks.playstore">Google Play Store</a>,\n
f) facer unha doazón, e\n
g) calquera cousa que estimes axuda a OpenTracks.</string>
<!-- Activity Type -->
<string name="activity_type_airplane">avión</string>
<string name="activity_type_atv">Quad</string>
<string name="activity_type_biking">ciclismo</string>
@@ -66,6 +156,8 @@
<string name="activity_type_walking">camiñar</string>
<string name="activity_type_water_skiing">esquí acuático</string>
<string name="activity_type_wind_surfing">windsurf</string>
<string name="app_not_installed_show_on_map">Por favor instala unha app que mostre datos nun mapa.</string>
<!-- Description -->
<string name="description_activity_type">Tipo de actividade: %1$s</string>
<string name="description_average_moving_pace_in_minute">Ritmo de desprazamento medio: %1$s min/km (%2$s min/mi)</string>
<string name="description_average_moving_speed">Velocidade de desprazamento media: %1$.2f km/h (%2$.1f mi/h)</string>
@@ -93,6 +185,7 @@
<string name="description_sensor_cadence">Cadencia (rpm)</string>
<string name="description_sensor_heart_rate">Frecuencia cardíaca (pulsacións por minuto)</string>
<string name="description_sensor_power">Potencia (W)</string>
<string name="description_default_speed_or_pace">Por tipo de actividade</string>
<string name="description_speed_imperial">Velocidade (mi/h)</string>
<string name="description_speed_metric">Velocidade (km/h)</string>
<string name="description_time">Tempo</string>
@@ -100,19 +193,25 @@
<string name="description_total_time">Tempo total: %1$s</string>
<string name="description_track_point">Punto</string>
<string name="description_track_segment">Segmento</string>
<!-- Export -->
<string name="export_all_option">Todas como %1$s</string>
<string name="export_all_title">Exportar a almacenamento externo</string>
<string name="export_with_photos">con fotos</string>
<string name="export_without_photos">sen fotos</string>
<string name="export_error">Exportados: %1$d de %2$s a %3$s</string>
<string name="export_option">como %1$s a %2$s</string>
<string name="export_progress_message">Exportando a %1$s…</string>
<string name="export_success">Exportados: %1$s a %2$s</string>
<string name="export_title">Exportar a…</string>
<!-- External Storage -->
<!-- Files -->
<string name="files_few">%1$d ficheiros</string>
<string name="files_many">%1$d ficheiros</string>
<string name="files_one">1 ficheiro</string>
<string name="files_other">%1$d ficheiros</string>
<string name="files_two">2 ficheiros</string>
<string name="files_zero">0 ficheiros</string>
<!-- Generic -->
<string name="generic_add">Engadir</string>
<string name="generic_cancel">Cancelar</string>
<string name="generic_delete_all_confirm_title">Eliminar todos?</string>
@@ -131,6 +230,7 @@
<string name="generic_success_title">Correcto</string>
<string name="generic_tracks">Traxectos</string>
<string name="generic_yes">Si</string>
<!-- Gps -->
<string name="gps_location_access">\"Acceso á situación\"</string>
<string name="gps_disabled">O GPS non está habilitado. Toca aquí para ir a %1$s. Preme o botón Atrás para volver atrás.</string>
<string name="gps_disabled_my_location">Non se pode determinar a situación actual. Habilita máis fontes de localización en %1$s e asegúrate de que o ceo estea visible. Téntao de novo máis tarde.</string>
@@ -138,6 +238,7 @@
<string name="gps_starting">Iniciando o GPS</string>
<string name="gps_wait_for_better_signal">Agardando un mellor sinal de GPS</string>
<string name="gps_wait_for_signal">Agardando un sinal de GPS</string>
<!-- Image -->
<string name="image_application">Aplicación</string>
<string name="image_arrow">Frecha</string>
<string name="image_marker">Marcador</string>
@@ -148,6 +249,7 @@
<string name="image_record">Gravar</string>
<string name="image_stop">Deter</string>
<string name="image_track">Traxecto</string>
<!-- Import -->
<string name="import_error">Importados: %1$d de %2$s de %3$s</string>
<string name="import_no_directory">%1$s non existe</string>
<string name="import_no_file">Non hai ficheiros para importar. Conecta o teu dispositivo ao ordenador e coloca os ficheiros para importar en %1$s.</string>
@@ -156,9 +258,11 @@
<string name="import_selection_option">Todos os %1$s</string>
<string name="import_selection_title">Importar desde almacenamento externo</string>
<string name="import_success">Importados: %1$s de %2$s</string>
<!-- Marker -->
<string name="marker_add_error">Non se pode inserir un marcador. Non hai sinal de GPS. Téntao de novo.</string>
<string name="marker_add_success">Inseriuse un marcador</string>
<string name="marker_add_canceled">Cancelouse a operación. Non se engadiu ningún marcador</string>
<string name="marker_add_photo_canceled">Cancelado, non se engadiu marcador con foto</string>
<string name="marker_delete_all_confirm_message">Todos os marcadores para este traxecto eliminaranse permanentemente do dispositivo</string>
<string name="marker_delete_one_confirm_message">O marcador eliminarase permanentemente do dispositivo</string>
<string name="marker_delete_one_confirm_title">Eliminar o marcador?</string>
@@ -168,10 +272,14 @@
<string name="marker_name_format">Marcador %1$d</string>
<string name="marker_split_name_format">División %1$d</string>
<string name="marker_title">%1$d de %2$d</string>
<!-- Marker Detail -->
<string name="marker_detail_statistics_title">Desde o último marcador de división:</string>
<!-- Marker Edit -->
<string name="marker_edit_marker_type">Tipo de marcador (por exemplo, fervenza)</string>
<string name="marker_edit_marker_type_hint">Tipo de marcador</string>
<!-- Marker List -->
<string name="marker_list_empty_message">Non tes marcadores</string>
<!-- Menu -->
<string name="menu_aggregated_statistics">Estatísticas globais</string>
<string name="menu_delete">Eliminar</string>
<string name="menu_edit">Editar</string>
@@ -180,6 +288,7 @@
<string name="menu_import_all">Importar todo</string>
<string name="menu_insert_marker">Inserir marcador</string>
<string name="menu_insert_photo">Inserir foto</string>
<string name="menu_insert_gallery_img">Insertar imaxe da galería</string>
<string name="menu_markers">Marcadores</string>
<string name="menu_search">Busca</string>
<string name="menu_select_all">Seleccionar todo</string>
@@ -192,11 +301,17 @@
<string name="menu_stop_gps">Desactivar GPS</string>
<string name="menu_view_photo">Ver foto</string>
<string name="menu_voice_frequency">Frecuencia da voz</string>
<string name="menu_resume_track">Retomar gravación</string>
<!-- OpenTracks -->
<string name="app_name">OpenTracks</string>
<string name="recording_service">Servizo de gravación</string>
<!-- Permission -->
<string name="permission_gps_failed">OpenTracks precisa permiso para usar o GPS</string>
<!-- Search -->
<string name="search_global_description">Traxectos e marcadores</string>
<string name="search_list_empty_message">Non hai traxectos ou marcadores que coincidan coa túa busca</string>
<string name="search_list_marker_track_location">Na ruta \"%1$s\".</string>
<!-- Sensor State -->
<string name="sensor_state_battery">Nivel da batería</string>
<string name="sensor_state_cadence">Cadencia</string>
<string name="sensor_state_cadence_value">%1$d rpm</string>
@@ -205,12 +320,23 @@
<string name="sensor_state_heart_rate_value">%1$d bpm</string>
<string name="sensor_state_power">Potencia</string>
<string name="sensor_state_power_value">%1$d W</string>
<string name="sensor_not_known">Sensor %1$s descoñecido. Comproba os axustes.</string>
<string name="sensor_could_not_scan">Non se escanearon dispositivos Bluetooth (fallo %1$i).</string>
<string name="bluetooth_disabled">Activa o Bluetooth</string>
<string name="settings_not_while_recording">Non dispoñible durante a gravación dunha ruta</string>
<string name="settings_show">Mostrar</string>
<!-- Settings Advanced -->
<string name="settings_advanced">Opcións avanzadas</string>
<string name="settings_default_trackfileformat">Formato do ficheiro a exportar/compartir</string>
<!-- Settings Chart -->
<string name="settings_chart_by_distance">Por distancia</string>
<string name="settings_chart_by_time">Por duración</string>
<string name="settings_chart_x_axis">Eixo X</string>
<!-- Settings Recording -->
<string name="settings_recording_show_on_lockscreen_while_recording_title">Mostrar estatísticas na pantalla de bloqueo</string>
<string name="settings_recording_show_on_lockscreen_while_recording_summary">Ao gravar, mostra estatísticas sen desbloquear o dispositivo.</string>
<string name="settings_recording_keep_screen_on_while_recording_title">Manter pantalla acesa</string>
<string name="settings_recording_keep_screen_on_while_recording_summary">Ao gravar, manten a pantalla acesa.</string>
<string name="settings_recording">Gravación</string>
<string name="settings_recording_auto_resume_track_timeout_always_summary">Retomar sempre unha gravación ao reiniciar o sistema</string>
<string name="settings_recording_auto_resume_track_timeout_never_summary">Non retomar nunca unha gravación ao reiniciar o sistema</string>
@@ -228,21 +354,32 @@
<string name="settings_recording_track_name_date_local_option">Data (local)</string>
<string name="settings_recording_track_name_number_option">Número</string>
<string name="settings_recording_track_name_title">Nome de ruta predeterm.</string>
<!-- Settings Reset -->
<string name="settings_reset">Restablecer</string>
<string name="settings_reset_confirm_message">Toda a configuración volverá aos valores predeterminados.</string>
<string name="settings_reset_confirm_title">Restablecer agora?</string>
<string name="settings_reset_done">Restablecéronse todos os valores predeterminados da configuración</string>
<string name="settings_error_initial_values_restored">Sentímolo, pero algo fallou. Os axustes foron restablecidos aos valores por omisión.</string>
<!-- Settings Sensor -->
<string name="settings_sensor">Sensor</string>
<string name="settings_sensor_bluetooth">Bluetooth</string>
<string name="settings_sensor_bluetooth_sensor">Sensor de Bluetooth</string>
<string name="settings_sensor_connecting">Conectado a %1$s</string>
<string name="settings_sensor_disconnected">Desconectado de %1$s</string>
<string name="settings_sensor_wheel_circumference">Tamaño da roda (ETRTO)</string>
<!-- Settings Stats -->
<string name="settings_stats_coordinate">Latitude/lonxitude</string>
<string name="settings_stats_rate_title">Indicador preferido</string>
<string name="settings_stats_units_imperial">Imperiais (millas, pés)</string>
<string name="settings_stats_units_metric">Métricas (km, m)</string>
<string name="settings_stats_units_title">Unidades preferidas</string>
<!-- Share Track -->
<string name="share_track_share_file">Compartir ficheiro de traxecto</string>
<string name="share_track_share_file_body">Creo que che pode interesar este traxecto.\n\nPara velo, utiliza Google Earth ou outro programa de mapas.\n\n%1$s</string>
<string name="share_track_subject">Gustaríame compartir un traxecto contigo</string>
<!-- Stats -->
<string name="stats_average_moving_pace">Ritmo de desprazamento medio</string>
<string name="stats_average_moving_speed">Velocidade de desprazamento media</string>
<string name="stats_average_pace">Ritmo medio</string>
@@ -261,25 +398,36 @@
<string name="stats_speed">Velocidade</string>
<string name="stats_distance">Distancia</string>
<string name="stats_total_time">Tempo total</string>
<!-- Track -->
<string name="track_name_format">Pista %1$d</string>
<string name="track_record_notification">Gravando o teu traxecto…</string>
<string name="track_recording_notification_accuracy">Precisión da localización: %1$s</string>
<string name="track_markers">Marcadores de %1$s</string>
<!-- Track Delete -->
<string name="track_delete_one_confirm_message">A ruta e os seus marcadores eliminaranse permanentemente do dispositivo.</string>
<string name="track_delete_one_confirm_title">Eliminar o traxecto?</string>
<string name="track_delete_multiple_confirm_message">As rutas seleccionadas e os seus marcadores eliminaranse permanentemente do dispositivo.</string>
<string name="track_delete_progress_message">Eliminando…</string>
<!-- Track Detail -->
<string name="track_detail_chart_tab">Táboa</string>
<string name="track_detail_stats_tab">Estatísticas</string>
<string name="track_detail_record_success">Estase a gravar un novo traxecto</string>
<string name="track_detail_resume_success">Retomando a ruta</string>
<!-- Track Edit -->
<string name="track_edit_activity_type">Tipo de actividade (por exemplo, sendeirismo)</string>
<string name="track_edit_activity_type_hint">Tipo de actividade</string>
<string name="track_edit_new_track_title">Novo traxecto</string>
<!-- Track List -->
<string name="track_list_empty_message">Empeza a gravar aquí a túa próxima aventura</string>
<!-- Track Widget -->
<!-- Tracks -->
<string name="tracks_few">%1$d rutas</string>
<string name="tracks_many">%1$d rutas</string>
<string name="tracks_one">1 ruta</string>
<string name="tracks_other">%1$d rutas</string>
<string name="tracks_two">2 rutas</string>
<string name="tracks_zero">0 rutas</string>
<!-- Unit -->
<string name="unit_feet">m</string>
<string name="unit_kilometer">km</string>
<string name="unit_kilometer_per_hour">km/h</string>
@@ -288,6 +436,7 @@
<string name="unit_mile_per_hour">km/h</string>
<string name="unit_minute_per_kilometer">min/km</string>
<string name="unit_minute_per_mile">min/km</string>
<!-- Value -->
<string name="value_adapt_accuracy">Adaptar para obter precisión</string>
<string name="value_adapt_battery_life">Adaptar á duración restante da batería</string>
<string name="value_always">Sempre</string>
@@ -314,6 +463,7 @@
<string name="value_off">Desactivar</string>
<string name="value_smallest">Máis pequeno</string>
<string name="value_smallest_recommended">O máis pequeno (recomendado)</string>
<!-- Voice -->
<string name="voice_hours_few">%1$d horas</string>
<string name="voice_hours_many">%1$d horas</string>
<string name="voice_hours_one">1 hora</string>
@@ -360,6 +510,7 @@
<string name="voice_total_distance_miles_two">distancia total 2 quilómetros</string>
<string name="voice_total_distance_miles_zero">distancia total 0 quilómetros</string>
<string name="voice_total_distance_zero">A distancia total de As miñas rutas é cero.</string>
<!-- Waypoint Type -->
<string name="waypoint_type_atm">Caixeiro automático</string>
<string name="waypoint_type_bank">banco</string>
<string name="waypoint_type_bar">bar</string>
@@ -411,4 +562,7 @@
<string name="waypoint_type_waterfall">fervenza</string>
<string name="waypoint_type_water_fountain">fonte de auga</string>
<string name="waypoint_type_waypoint">Punto de referencia</string>
<!-- Share image -->
<string name="share_image_subject">Quixera compartir unha imaxe contigo</string>
<string name="share_image_body">Creo que che podería gustar esta imaxe.</string>
</resources>

Some files were not shown because too many files have changed in this diff Show More