forked from upstream-mirrors/OpenTracks
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9225d90e45 | |||
| 32dc6fef82 | |||
| 96fb65b6b0 | |||
| 936bbf9264 | |||
| 241a52ee7c | |||
| c66e285bf5 | |||
| 8c1ddc077d | |||
| 61c18af639 | |||
| b83df3ac4b | |||
| 53f1c4b7f3 | |||
| 6290bdbab0 | |||
| bd0d85cd37 | |||
| ba08417236 | |||
| 63281a3ca8 | |||
| fae0cf8454 | |||
| efddb31391 | |||
| 3a8e547e10 | |||
| fe09d18197 | |||
| 93bcf8d494 | |||
| 819f454bd8 | |||
| 7d58014b15 | |||
| 9dac95569c | |||
| 96c98ad275 | |||
| 7dcc0ec8bc | |||
| 09ecc03be5 | |||
| d7a5ffb938 |
@@ -13,7 +13,7 @@ jobs:
|
|||||||
- uses: actions/setup-java@v3
|
- uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: 'adopt'
|
distribution: 'adopt'
|
||||||
java-version: '11'
|
java-version: '17'
|
||||||
|
|
||||||
- name: Assemble APK
|
- name: Assemble APK
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|||||||
@@ -16,6 +16,6 @@ jobs:
|
|||||||
- uses: actions/setup-java@v3
|
- uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: 'zulu'
|
distribution: 'zulu'
|
||||||
java-version: '11'
|
java-version: '17'
|
||||||
|
|
||||||
- run: ./gradlew lint
|
- run: ./gradlew lint
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
_OpenTracks_ is a sport tracking application that completely respects your privacy.
|
_OpenTracks_ is a sport tracking application that completely respects your privacy.
|
||||||
|
|
||||||
[](https://github.com/humanetech-community/awesome-humane-tech)
|
[](https://delightful.club/delightful-humane-design/#health)
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -40,8 +40,10 @@ _OpenTracks_ is a sport tracking application that completely respects your priva
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
Translations are hosted on <a href="https://hosted.weblate.org/engage/opentracks/">
|
||||||
|
hosted.weblate.org</a>.
|
||||||
<a href="https://hosted.weblate.org/engage/opentracks/">
|
<a href="https://hosted.weblate.org/engage/opentracks/">
|
||||||
<img src="https://hosted.weblate.org/widgets/opentracks/-/horizontal-auto.svg" alt="Translation status" />
|
<img src="https://hosted.weblate.org/widgets/opentracks/-/horizontal-auto.svg" alt="Translation status" />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|||||||
+14
-9
@@ -6,7 +6,7 @@ buildscript {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:7.4.2'
|
classpath 'com.android.tools.build:gradle:8.1.0-beta01'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,20 +47,25 @@ android {
|
|||||||
compileOptions {
|
compileOptions {
|
||||||
coreLibraryDesugaringEnabled true
|
coreLibraryDesugaringEnabled true
|
||||||
|
|
||||||
sourceCompatibility JavaVersion.VERSION_11
|
sourceCompatibility JavaVersion.VERSION_17
|
||||||
targetCompatibility JavaVersion.VERSION_11
|
targetCompatibility JavaVersion.VERSION_17
|
||||||
}
|
}
|
||||||
|
|
||||||
compileSdk 33
|
compileSdk 33
|
||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding true
|
viewBinding true
|
||||||
|
buildConfig true
|
||||||
|
}
|
||||||
|
|
||||||
|
androidResources {
|
||||||
|
generateLocaleConfig = true
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "de.dennisguse.opentracks"
|
applicationId "de.dennisguse.opentracks"
|
||||||
versionCode 5168
|
versionCode 5194
|
||||||
versionName "v4.3.1"
|
versionName "v4.3.3"
|
||||||
|
|
||||||
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
||||||
|
|
||||||
@@ -108,11 +113,11 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
applicationVariants.all { variant ->
|
applicationVariants.configureEach { variant ->
|
||||||
variant.resValue "string", "applicationId", variant.applicationId
|
variant.resValue "string", "applicationId", variant.applicationId
|
||||||
|
|
||||||
if (variant.buildType.name == 'nightly') {
|
if (variant.buildType.name == 'nightly') {
|
||||||
variant.outputs.all {
|
variant.outputs.configureEach {
|
||||||
setVersionCodeOverride(getVersionCode())
|
setVersionCodeOverride(getVersionCode())
|
||||||
setVersionNameOverride(getVersionName())
|
setVersionNameOverride(getVersionName())
|
||||||
outputFileName = "${applicationId}_${variant.versionCode}.apk"
|
outputFileName = "${applicationId}_${variant.versionCode}.apk"
|
||||||
@@ -130,7 +135,7 @@ dependencies {
|
|||||||
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
||||||
implementation 'com.google.android.material:material:1.8.0'
|
implementation 'com.google.android.material:material:1.8.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
implementation 'androidx.core:core-splashscreen:1.0.0'
|
implementation 'androidx.core:core-splashscreen:1.0.1'
|
||||||
implementation 'androidx.mediarouter:mediarouter:1.3.1'
|
implementation 'androidx.mediarouter:mediarouter:1.3.1'
|
||||||
|
|
||||||
androidTestImplementation 'androidx.test:core:1.5.0'
|
androidTestImplementation 'androidx.test:core:1.5.0'
|
||||||
@@ -138,7 +143,7 @@ dependencies {
|
|||||||
androidTestImplementation 'androidx.test:rules:1.5.0'
|
androidTestImplementation 'androidx.test:rules:1.5.0'
|
||||||
androidTestImplementation 'androidx.test:runner:1.5.2'
|
androidTestImplementation 'androidx.test:runner:1.5.2'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||||
androidTestImplementation 'org.mockito:mockito-android:5.2.0'
|
androidTestImplementation 'org.mockito:mockito-android:5.3.1'
|
||||||
|
|
||||||
androidTestUtil 'androidx.test:orchestrator:1.4.2'
|
androidTestUtil 'androidx.test:orchestrator:1.4.2'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
v4.3.2: OpenTracks
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- KML header was misformed (#1522)
|
||||||
|
|
||||||
|
Developer:
|
||||||
|
- Use Java 17 (multi-line text, switch)
|
||||||
|
- API33: locale config is generated
|
||||||
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
v4.3.3: OpenTracks
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
- Hidden fields were shown (introduced in v4.2.0)
|
||||||
|
- Speed was shown instead of pace (introduced in v4.2.0)
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
一個尊重您的隱私的運動蹤跡紀錄器。
|
||||||
|
|
||||||
|
運動與戶外活動賦予幸福、技能與自信。
|
||||||
|
|
||||||
|
由保持追蹤您的訓練來重視您的健康。
|
||||||
|
紀錄您的跑步或健行,並且在您騎自行車時給您一台具有大螢幕的自行車電腦。
|
||||||
|
用照片標記您一路上有趣的地點。
|
||||||
|
保持紀錄非常詳細的統計資料以供分析。
|
||||||
|
僅分享資料給您想要分享的其他人。
|
||||||
|
|
||||||
|
* 語音播報。
|
||||||
|
* 支援藍芽 LE 感測器:心律、速度與距離 (騎自行車)、踏頻 (騎自行車)、功率計 (騎自行車)。
|
||||||
|
* 海拔上升與下降:通過氣壓感測器。
|
||||||
|
* EGM2008 海拔顯示 (平均海平面以上);匯出為 WGS84。
|
||||||
|
* 匯出蹤跡資料為 KMZ (包含照片)、KML、GPX。
|
||||||
|
* 沒有網路存取或額外的權限需求。
|
||||||
|
* 深色與淺色主題,取決於系統設定。
|
||||||
|
* 沒有廣告。
|
||||||
|
|
||||||
|
此為 自由軟體 / 免費軟體 / 開放原始碼
|
||||||
|
這意味您可以使用、學習、修改與分享此專案的原始碼。
|
||||||
|
授權條款:Apache 2.0
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
一個尊重您的隱私的運動蹤跡紀錄器。
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
OpenTracks
|
||||||
+1
-1
@@ -1,2 +1,2 @@
|
|||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
org.gradle.jvmargs=-Xmx1024m -XX:MaxPermSize=256m
|
org.gradle.jvmargs=-Xmx1024m
|
||||||
|
|||||||
+12
-10
@@ -27,21 +27,23 @@ public class KmlTrackExporterTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void writeCloseSegment_only_write_sensordata_if_present() {
|
public void writeCloseSegment_only_write_sensordata_if_present() {
|
||||||
String expected = "<when>1970-01-01T00:00:00Z</when>\n" +
|
String expected = """
|
||||||
"<coord/>\n" +
|
<when>1970-01-01T00:00:00Z</when>
|
||||||
"<when>1970-01-01T01:00:00+01:00</when>\n" +
|
<coord/>
|
||||||
"<coord/>\n" +
|
<when>1970-01-01T01:00:00+01:00</when>
|
||||||
"<ExtendedData>\n" +
|
<coord/>
|
||||||
"<SchemaData schemaUrl=\"#schema\">\n" +
|
<ExtendedData>
|
||||||
"</SchemaData>\n" +
|
<SchemaData schemaUrl="#schema">
|
||||||
"</ExtendedData>\n" +
|
</SchemaData>
|
||||||
"</Track>\n";
|
</ExtendedData>
|
||||||
|
</Track>
|
||||||
|
""";
|
||||||
|
|
||||||
// given
|
// given
|
||||||
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0));
|
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, Instant.ofEpochSecond(0));
|
||||||
|
|
||||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
KMLTrackExporter kmlTrackWriter = (KMLTrackExporter) TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context);
|
KMLTrackExporter kmlTrackWriter = (KMLTrackExporter) TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context, null);
|
||||||
kmlTrackWriter.prepare(outputStream);
|
kmlTrackWriter.prepare(outputStream);
|
||||||
|
|
||||||
kmlTrackWriter.writeTrackPoint(ZoneOffset.UTC, trackPoint);
|
kmlTrackWriter.writeTrackPoint(ZoneOffset.UTC, trackPoint);
|
||||||
|
|||||||
+5
-5
@@ -204,7 +204,7 @@ public class ExportImportTest {
|
|||||||
// given
|
// given
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
|
|
||||||
TrackExporter trackExporter = TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context);
|
TrackExporter trackExporter = TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context, contentProviderUtils);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
// 1. export
|
// 1. export
|
||||||
@@ -287,7 +287,7 @@ public class ExportImportTest {
|
|||||||
editor.commit();
|
editor.commit();
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
|
|
||||||
TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context);
|
TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context, contentProviderUtils);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
trackExporter.writeTrack(track, context.getContentResolver().openOutputStream(tmpFileUri));
|
trackExporter.writeTrack(track, context.getContentResolver().openOutputStream(tmpFileUri));
|
||||||
@@ -310,7 +310,7 @@ public class ExportImportTest {
|
|||||||
// given
|
// given
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
|
|
||||||
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context);
|
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context, contentProviderUtils);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
// 1. export
|
// 1. export
|
||||||
@@ -428,7 +428,7 @@ public class ExportImportTest {
|
|||||||
editor.commit();
|
editor.commit();
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
|
|
||||||
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context);
|
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context, contentProviderUtils);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
// 1. export
|
// 1. export
|
||||||
@@ -453,7 +453,7 @@ public class ExportImportTest {
|
|||||||
// given
|
// given
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
|
|
||||||
TrackExporter trackExporter = TrackFileFormat.CSV.createTrackExporter(context);
|
TrackExporter trackExporter = TrackFileFormat.CSV.createTrackExporter(context, contentProviderUtils);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
// 1. export
|
// 1. export
|
||||||
|
|||||||
+1
-1
@@ -231,7 +231,7 @@ public class GPXTrackImporterTest {
|
|||||||
importTrackId = importer.importFile(inputStream).get(0);
|
importTrackId = importer.importFile(inputStream).get(0);
|
||||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||||
|
|
||||||
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context);
|
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context, contentProviderUtils);
|
||||||
trackExporter.writeTrack(importedTrack, outputStream);
|
trackExporter.writeTrack(importedTrack, outputStream);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
|
|||||||
@@ -34,23 +34,24 @@ public class EGM2008UtilsTest {
|
|||||||
int expectedLength = 18671444;
|
int expectedLength = 18671444;
|
||||||
int expectedHeaderLength = 404;
|
int expectedHeaderLength = 404;
|
||||||
|
|
||||||
String expectedHeader = "P5\n" +
|
String expectedHeader = """
|
||||||
"# Geoid file in PGM format for the GeographicLib::Geoid class\n" +
|
P5
|
||||||
"# Description WGS84 EGM2008, 5-minute grid\n" +
|
# Geoid file in PGM format for the GeographicLib::Geoid class
|
||||||
"# URL http://earth-info.nga.mil/GandG/wgs84/gravitymod/egm2008\n" +
|
# Description WGS84 EGM2008, 5-minute grid
|
||||||
"# DateTime 2009-08-29 18:45:00\n" +
|
# URL http://earth-info.nga.mil/GandG/wgs84/gravitymod/egm2008
|
||||||
"# MaxBilinearError 0.478\n" +
|
# DateTime 2009-08-29 18:45:00
|
||||||
"# RMSBilinearError 0.012\n" +
|
# MaxBilinearError 0.478
|
||||||
"# MaxCubicError 0.294\n" +
|
# RMSBilinearError 0.012
|
||||||
"# RMSCubicError 0.005\n" +
|
# MaxCubicError 0.294
|
||||||
"# Offset -108\n" +
|
# RMSCubicError 0.005
|
||||||
"# Scale 0.003\n" +
|
# Offset -108
|
||||||
"# Origin 90N 0E\n" +
|
# Scale 0.003
|
||||||
"# AREA_OR_POINT Point\n" +
|
# Origin 90N 0E
|
||||||
"# Vertical_Datum WGS84\n" +
|
# AREA_OR_POINT Point
|
||||||
"4320 2161\n" +
|
# Vertical_Datum WGS84
|
||||||
"65535" +
|
4320 2161
|
||||||
"\n";
|
65535
|
||||||
|
""";
|
||||||
|
|
||||||
// when
|
// when
|
||||||
try (InputStream inputStream = context.getResources().openRawResource(EGM2008Utils.EGM2008_5_DATA)) {
|
try (InputStream inputStream = context.getResources().openRawResource(EGM2008Utils.EGM2008_5_DATA)) {
|
||||||
|
|||||||
@@ -52,8 +52,8 @@ public class StatisticsUtilsTest extends TestCase {
|
|||||||
recordingLayout.addField(new DataField("key5", true, true, false));
|
recordingLayout.addField(new DataField("key5", true, true, false));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
RecordingLayout resultTrue = StatisticsUtils.filterVisible(recordingLayout, true);
|
RecordingLayout resultTrue = recordingLayout.toRecordingLayout(true);
|
||||||
RecordingLayout resultFalse = StatisticsUtils.filterVisible(recordingLayout, false);
|
RecordingLayout resultFalse = recordingLayout.toRecordingLayout(false);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(resultTrue.getFields().size(), 3);
|
assertEquals(resultTrue.getFields().size(), 3);
|
||||||
|
|||||||
@@ -70,8 +70,7 @@ limitations under the License.
|
|||||||
android:icon="@drawable/ic_launcher"
|
android:icon="@drawable/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/ThemeCustom"
|
android:theme="@style/ThemeCustom">
|
||||||
android:localeConfig="@xml/locales_config">
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".publicapi.StartRecording"
|
android:name=".publicapi.StartRecording"
|
||||||
|
|||||||
@@ -311,9 +311,9 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
searchMenuItem = menu.findItem(R.id.track_list_search);
|
searchMenuItem = menu.findItem(R.id.track_list_search);
|
||||||
SearchView searchView = ActivityUtils.configureSearchWidget(this, searchMenuItem);
|
SearchView searchView = ActivityUtils.configureSearchWidget(this, searchMenuItem);
|
||||||
|
|
||||||
searchView.findViewById(R.id.search_edit_frame).setPadding(0, 0, 48, 0);
|
searchView.findViewById(androidx.appcompat.R.id.search_edit_frame).setPadding(0, 0, 48, 0);
|
||||||
|
|
||||||
SearchView.SearchAutoComplete searchAutoComplete = searchView.findViewById(R.id.search_src_text);
|
SearchView.SearchAutoComplete searchAutoComplete = searchView.findViewById(androidx.appcompat.R.id.search_src_text);
|
||||||
searchAutoComplete.setHintTextColor(ContextCompat.getColor(this, android.R.color.white));
|
searchAutoComplete.setHintTextColor(ContextCompat.getColor(this, android.R.color.white));
|
||||||
searchAutoComplete.setTextColor(ContextCompat.getColor(this, android.R.color.white));
|
searchAutoComplete.setTextColor(ContextCompat.getColor(this, android.R.color.white));
|
||||||
|
|
||||||
|
|||||||
@@ -274,18 +274,14 @@ public class TrackRecordedActivity extends AbstractTrackDeleteActivity implement
|
|||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Fragment createFragment(int position) {
|
public Fragment createFragment(int position) {
|
||||||
switch (position) {
|
return switch (position) {
|
||||||
case 0:
|
case 0 -> StatisticsRecordedFragment.newInstance(trackId);
|
||||||
return StatisticsRecordedFragment.newInstance(trackId);
|
case 1 -> IntervalsFragment.newInstance(trackId, true);
|
||||||
case 1:
|
case 2 -> ChartFragment.newInstance(false);
|
||||||
return IntervalsFragment.newInstance(trackId, true);
|
case 3 -> ChartFragment.newInstance(true);
|
||||||
case 2:
|
default ->
|
||||||
return ChartFragment.newInstance(false);
|
|
||||||
case 3:
|
|
||||||
return ChartFragment.newInstance(true);
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
|
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -294,18 +290,14 @@ public class TrackRecordedActivity extends AbstractTrackDeleteActivity implement
|
|||||||
}
|
}
|
||||||
|
|
||||||
public CharSequence getPageTitle(int position) {
|
public CharSequence getPageTitle(int position) {
|
||||||
switch (position) {
|
return switch (position) {
|
||||||
case 0:
|
case 0 -> getString(R.string.track_detail_stats_tab);
|
||||||
return getString(R.string.track_detail_stats_tab);
|
case 1 -> getString(R.string.track_detail_intervals_tab);
|
||||||
case 1:
|
case 2 -> getString(R.string.settings_chart_by_time);
|
||||||
return getString(R.string.track_detail_intervals_tab);
|
case 3 -> getString(R.string.settings_chart_by_distance);
|
||||||
case 2:
|
default ->
|
||||||
return getString(R.string.settings_chart_by_time);
|
|
||||||
case 3:
|
|
||||||
return getString(R.string.settings_chart_by_distance);
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
|
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -333,18 +333,14 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Fragment createFragment(int position) {
|
public Fragment createFragment(int position) {
|
||||||
switch (position) {
|
return switch (position) {
|
||||||
case 0:
|
case 0 -> StatisticsRecordingFragment.newInstance();
|
||||||
return StatisticsRecordingFragment.newInstance();
|
case 1 -> IntervalsFragment.newInstance(trackId, false);
|
||||||
case 1:
|
case 2 -> ChartFragment.newInstance(false);
|
||||||
return IntervalsFragment.newInstance(trackId, false);
|
case 3 -> ChartFragment.newInstance(true);
|
||||||
case 2:
|
default ->
|
||||||
return ChartFragment.newInstance(false);
|
|
||||||
case 3:
|
|
||||||
return ChartFragment.newInstance(true);
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
|
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -353,18 +349,14 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
}
|
}
|
||||||
|
|
||||||
public CharSequence getPageTitle(int position) {
|
public CharSequence getPageTitle(int position) {
|
||||||
switch (position) {
|
return switch (position) {
|
||||||
case 0:
|
case 0 -> getString(R.string.track_detail_stats_tab);
|
||||||
return getString(R.string.track_detail_stats_tab);
|
case 1 -> getString(R.string.track_detail_intervals_tab);
|
||||||
case 1:
|
case 2 -> getString(R.string.settings_chart_by_time);
|
||||||
return getString(R.string.track_detail_intervals_tab);
|
case 3 -> getString(R.string.settings_chart_by_distance);
|
||||||
case 2:
|
default ->
|
||||||
return getString(R.string.settings_chart_by_time);
|
|
||||||
case 3:
|
|
||||||
return getString(R.string.settings_chart_by_distance);
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
|
throw new RuntimeException("There isn't Fragment associated with the position: " + position);
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -210,16 +210,11 @@ abstract class ChartValueSeries {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int getTitleId(UnitSystem unitSystem) {
|
int getTitleId(UnitSystem unitSystem) {
|
||||||
switch (unitSystem) {
|
return switch (unitSystem) {
|
||||||
case METRIC:
|
case METRIC -> metricTitleId;
|
||||||
return metricTitleId;
|
case IMPERIAL -> imperialTitleId;
|
||||||
case IMPERIAL:
|
case NAUTICAL_IMPERIAL -> nauticalTitleId;
|
||||||
return imperialTitleId;
|
};
|
||||||
case NAUTICAL_IMPERIAL:
|
|
||||||
return nauticalTitleId;
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Not implemented");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Paint getTitlePaint() {
|
Paint getTitlePaint() {
|
||||||
|
|||||||
@@ -733,16 +733,11 @@ public class ChartView extends View {
|
|||||||
private String getXAxisLabel() {
|
private String getXAxisLabel() {
|
||||||
Context context = getContext();
|
Context context = getContext();
|
||||||
if (chartByDistance) {
|
if (chartByDistance) {
|
||||||
switch (unitSystem) {
|
return switch (unitSystem) {
|
||||||
case METRIC:
|
case METRIC -> context.getString(R.string.unit_kilometer);
|
||||||
return context.getString(R.string.unit_kilometer);
|
case IMPERIAL -> context.getString(R.string.unit_mile);
|
||||||
case IMPERIAL:
|
case NAUTICAL_IMPERIAL -> context.getString(R.string.unit_nautical_mile);
|
||||||
return context.getString(R.string.unit_mile);
|
};
|
||||||
case NAUTICAL_IMPERIAL:
|
|
||||||
return context.getString(R.string.unit_nautical_mile);
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Not implemented");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return context.getString(R.string.description_time);
|
return context.getString(R.string.description_time);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,20 +133,12 @@ public class CustomContentProvider extends ContentProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int delete(@NonNull Uri url, String where, String[] selectionArgs) {
|
public int delete(@NonNull Uri url, String where, String[] selectionArgs) {
|
||||||
String table;
|
String table = switch (getUrlType(url)) {
|
||||||
switch (getUrlType(url)) {
|
case TRACKPOINTS -> TrackPointsColumns.TABLE_NAME;
|
||||||
case TRACKPOINTS:
|
case TRACKS -> TracksColumns.TABLE_NAME;
|
||||||
table = TrackPointsColumns.TABLE_NAME;
|
case MARKERS -> MarkerColumns.TABLE_NAME;
|
||||||
break;
|
default -> throw new IllegalArgumentException("Unknown URL " + url);
|
||||||
case TRACKS:
|
};
|
||||||
table = TracksColumns.TABLE_NAME;
|
|
||||||
break;
|
|
||||||
case MARKERS:
|
|
||||||
table = MarkerColumns.TABLE_NAME;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Unknown URL " + url);
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.w(TAG, "Deleting from table " + table);
|
Log.w(TAG, "Deleting from table " + table);
|
||||||
int totalChangesBefore = getTotalChanges();
|
int totalChangesBefore = getTotalChanges();
|
||||||
@@ -186,24 +178,15 @@ public class CustomContentProvider extends ContentProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getType(@NonNull Uri url) {
|
public String getType(@NonNull Uri url) {
|
||||||
switch (getUrlType(url)) {
|
return switch (getUrlType(url)) {
|
||||||
case TRACKPOINTS:
|
case TRACKPOINTS -> TrackPointsColumns.CONTENT_TYPE;
|
||||||
return TrackPointsColumns.CONTENT_TYPE;
|
case TRACKPOINTS_BY_ID, TRACKPOINTS_BY_TRACKID -> TrackPointsColumns.CONTENT_ITEMTYPE;
|
||||||
case TRACKPOINTS_BY_ID:
|
case TRACKS -> TracksColumns.CONTENT_TYPE;
|
||||||
case TRACKPOINTS_BY_TRACKID:
|
case TRACKS_BY_ID -> TracksColumns.CONTENT_ITEMTYPE;
|
||||||
return TrackPointsColumns.CONTENT_ITEMTYPE;
|
case MARKERS -> MarkerColumns.CONTENT_TYPE;
|
||||||
case TRACKS:
|
case MARKERS_BY_ID, MARKERS_BY_TRACKID -> MarkerColumns.CONTENT_ITEMTYPE;
|
||||||
return TracksColumns.CONTENT_TYPE;
|
default -> throw new IllegalArgumentException("Unknown URL " + url);
|
||||||
case TRACKS_BY_ID:
|
};
|
||||||
return TracksColumns.CONTENT_ITEMTYPE;
|
|
||||||
case MARKERS:
|
|
||||||
return MarkerColumns.CONTENT_TYPE;
|
|
||||||
case MARKERS_BY_ID:
|
|
||||||
case MARKERS_BY_TRACKID:
|
|
||||||
return MarkerColumns.CONTENT_ITEMTYPE;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Unknown URL " + url);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -251,47 +234,47 @@ public class CustomContentProvider extends ContentProvider {
|
|||||||
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
|
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
|
||||||
String sortOrder = null;
|
String sortOrder = null;
|
||||||
switch (getUrlType(url)) {
|
switch (getUrlType(url)) {
|
||||||
case TRACKPOINTS:
|
case TRACKPOINTS -> {
|
||||||
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
||||||
sortOrder = sort != null ? sort : TrackPointsColumns.DEFAULT_SORT_ORDER;
|
sortOrder = sort != null ? sort : TrackPointsColumns.DEFAULT_SORT_ORDER;
|
||||||
break;
|
}
|
||||||
case TRACKPOINTS_BY_ID:
|
case TRACKPOINTS_BY_ID -> {
|
||||||
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
||||||
queryBuilder.appendWhere(TrackPointsColumns._ID + "=" + ContentUris.parseId(url));
|
queryBuilder.appendWhere(TrackPointsColumns._ID + "=" + ContentUris.parseId(url));
|
||||||
break;
|
}
|
||||||
case TRACKPOINTS_BY_TRACKID:
|
case TRACKPOINTS_BY_TRACKID -> {
|
||||||
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
queryBuilder.setTables(TrackPointsColumns.TABLE_NAME);
|
||||||
queryBuilder.appendWhere(TrackPointsColumns.TRACKID + " IN (" + TextUtils.join(SQL_LIST_DELIMITER, ContentProviderUtils.parseTrackIdsFromUri(url)) + ")");
|
queryBuilder.appendWhere(TrackPointsColumns.TRACKID + " IN (" + TextUtils.join(SQL_LIST_DELIMITER, ContentProviderUtils.parseTrackIdsFromUri(url)) + ")");
|
||||||
break;
|
}
|
||||||
case TRACKS:
|
case TRACKS -> {
|
||||||
if (projection != null && Arrays.asList(projection).contains(TracksColumns.MARKER_COUNT)) {
|
if (projection != null && Arrays.asList(projection).contains(TracksColumns.MARKER_COUNT)) {
|
||||||
queryBuilder.setTables(TracksColumns.TABLE_NAME + " LEFT OUTER JOIN (SELECT " + MarkerColumns.TRACKID + " AS markerTrackId, COUNT(*) AS " + TracksColumns.MARKER_COUNT + " FROM " + MarkerColumns.TABLE_NAME + " GROUP BY " + MarkerColumns.TRACKID + ") ON (" + TracksColumns.TABLE_NAME + "." + TracksColumns._ID + "= markerTrackId)");
|
queryBuilder.setTables(TracksColumns.TABLE_NAME + " LEFT OUTER JOIN (SELECT " + MarkerColumns.TRACKID + " AS markerTrackId, COUNT(*) AS " + TracksColumns.MARKER_COUNT + " FROM " + MarkerColumns.TABLE_NAME + " GROUP BY " + MarkerColumns.TRACKID + ") ON (" + TracksColumns.TABLE_NAME + "." + TracksColumns._ID + "= markerTrackId)");
|
||||||
} else {
|
} else {
|
||||||
queryBuilder.setTables(TracksColumns.TABLE_NAME);
|
queryBuilder.setTables(TracksColumns.TABLE_NAME);
|
||||||
}
|
}
|
||||||
sortOrder = sort != null ? sort : TracksColumns.DEFAULT_SORT_ORDER;
|
sortOrder = sort != null ? sort : TracksColumns.DEFAULT_SORT_ORDER;
|
||||||
break;
|
}
|
||||||
case TRACKS_BY_ID:
|
case TRACKS_BY_ID -> {
|
||||||
queryBuilder.setTables(TracksColumns.TABLE_NAME);
|
queryBuilder.setTables(TracksColumns.TABLE_NAME);
|
||||||
queryBuilder.appendWhere(TracksColumns._ID + " IN (" + TextUtils.join(SQL_LIST_DELIMITER, ContentProviderUtils.parseTrackIdsFromUri(url)) + ")");
|
queryBuilder.appendWhere(TracksColumns._ID + " IN (" + TextUtils.join(SQL_LIST_DELIMITER, ContentProviderUtils.parseTrackIdsFromUri(url)) + ")");
|
||||||
break;
|
}
|
||||||
case TRACKS_SENSOR_STATS:
|
case TRACKS_SENSOR_STATS -> {
|
||||||
long trackId = ContentUris.parseId(url);
|
long trackId = ContentUris.parseId(url);
|
||||||
return db.rawQuery(SENSOR_STATS_QUERY, new String[]{String.valueOf(trackId), String.valueOf(trackId)});
|
return db.rawQuery(SENSOR_STATS_QUERY, new String[]{String.valueOf(trackId), String.valueOf(trackId)});
|
||||||
case MARKERS:
|
}
|
||||||
|
case MARKERS -> {
|
||||||
queryBuilder.setTables(MarkerColumns.TABLE_NAME);
|
queryBuilder.setTables(MarkerColumns.TABLE_NAME);
|
||||||
sortOrder = sort != null ? sort : MarkerColumns.DEFAULT_SORT_ORDER;
|
sortOrder = sort != null ? sort : MarkerColumns.DEFAULT_SORT_ORDER;
|
||||||
break;
|
}
|
||||||
case MARKERS_BY_ID:
|
case MARKERS_BY_ID -> {
|
||||||
queryBuilder.setTables(MarkerColumns.TABLE_NAME);
|
queryBuilder.setTables(MarkerColumns.TABLE_NAME);
|
||||||
queryBuilder.appendWhere(MarkerColumns._ID + "=" + ContentUris.parseId(url));
|
queryBuilder.appendWhere(MarkerColumns._ID + "=" + ContentUris.parseId(url));
|
||||||
break;
|
}
|
||||||
case MARKERS_BY_TRACKID:
|
case MARKERS_BY_TRACKID -> {
|
||||||
queryBuilder.setTables(MarkerColumns.TABLE_NAME);
|
queryBuilder.setTables(MarkerColumns.TABLE_NAME);
|
||||||
queryBuilder.appendWhere(MarkerColumns.TRACKID + " IN (" + TextUtils.join(SQL_LIST_DELIMITER, ContentProviderUtils.parseTrackIdsFromUri(url)) + ")");
|
queryBuilder.appendWhere(MarkerColumns.TRACKID + " IN (" + TextUtils.join(SQL_LIST_DELIMITER, ContentProviderUtils.parseTrackIdsFromUri(url)) + ")");
|
||||||
break;
|
}
|
||||||
default:
|
default -> throw new IllegalArgumentException("Unknown url " + url);
|
||||||
throw new IllegalArgumentException("Unknown url " + url);
|
|
||||||
}
|
}
|
||||||
Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
|
Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
|
||||||
cursor.setNotificationUri(getContext().getContentResolver(), url);
|
cursor.setNotificationUri(getContext().getContentResolver(), url);
|
||||||
@@ -304,41 +287,40 @@ public class CustomContentProvider extends ContentProvider {
|
|||||||
String table;
|
String table;
|
||||||
String whereClause;
|
String whereClause;
|
||||||
switch (getUrlType(url)) {
|
switch (getUrlType(url)) {
|
||||||
case TRACKPOINTS:
|
case TRACKPOINTS -> {
|
||||||
table = TrackPointsColumns.TABLE_NAME;
|
table = TrackPointsColumns.TABLE_NAME;
|
||||||
whereClause = where;
|
whereClause = where;
|
||||||
break;
|
}
|
||||||
case TRACKPOINTS_BY_ID:
|
case TRACKPOINTS_BY_ID -> {
|
||||||
table = TrackPointsColumns.TABLE_NAME;
|
table = TrackPointsColumns.TABLE_NAME;
|
||||||
whereClause = TrackPointsColumns._ID + "=" + ContentUris.parseId(url);
|
whereClause = TrackPointsColumns._ID + "=" + ContentUris.parseId(url);
|
||||||
if (!TextUtils.isEmpty(where)) {
|
if (!TextUtils.isEmpty(where)) {
|
||||||
whereClause += " AND (" + where + ")";
|
whereClause += " AND (" + where + ")";
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TRACKS:
|
case TRACKS -> {
|
||||||
table = TracksColumns.TABLE_NAME;
|
table = TracksColumns.TABLE_NAME;
|
||||||
whereClause = where;
|
whereClause = where;
|
||||||
break;
|
}
|
||||||
case TRACKS_BY_ID:
|
case TRACKS_BY_ID -> {
|
||||||
table = TracksColumns.TABLE_NAME;
|
table = TracksColumns.TABLE_NAME;
|
||||||
whereClause = TracksColumns._ID + "=" + ContentUris.parseId(url);
|
whereClause = TracksColumns._ID + "=" + ContentUris.parseId(url);
|
||||||
if (!TextUtils.isEmpty(where)) {
|
if (!TextUtils.isEmpty(where)) {
|
||||||
whereClause += " AND (" + where + ")";
|
whereClause += " AND (" + where + ")";
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case MARKERS:
|
case MARKERS -> {
|
||||||
table = MarkerColumns.TABLE_NAME;
|
table = MarkerColumns.TABLE_NAME;
|
||||||
whereClause = where;
|
whereClause = where;
|
||||||
break;
|
}
|
||||||
case MARKERS_BY_ID:
|
case MARKERS_BY_ID -> {
|
||||||
table = MarkerColumns.TABLE_NAME;
|
table = MarkerColumns.TABLE_NAME;
|
||||||
whereClause = MarkerColumns._ID + "=" + ContentUris.parseId(url);
|
whereClause = MarkerColumns._ID + "=" + ContentUris.parseId(url);
|
||||||
if (!TextUtils.isEmpty(where)) {
|
if (!TextUtils.isEmpty(where)) {
|
||||||
whereClause += " AND (" + where + ")";
|
whereClause += " AND (" + where + ")";
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
default:
|
default -> throw new IllegalArgumentException("Unknown url " + url);
|
||||||
throw new IllegalArgumentException("Unknown url " + url);
|
|
||||||
}
|
}
|
||||||
int count;
|
int count;
|
||||||
try {
|
try {
|
||||||
@@ -371,16 +353,12 @@ public class CustomContentProvider extends ContentProvider {
|
|||||||
* @param contentValues the content values
|
* @param contentValues the content values
|
||||||
*/
|
*/
|
||||||
private Uri insertContentValues(Uri url, UrlType urlType, ContentValues contentValues) {
|
private Uri insertContentValues(Uri url, UrlType urlType, ContentValues contentValues) {
|
||||||
switch (urlType) {
|
return switch (urlType) {
|
||||||
case TRACKPOINTS:
|
case TRACKPOINTS -> insertTrackPoint(url, contentValues);
|
||||||
return insertTrackPoint(url, contentValues);
|
case TRACKS -> insertTrack(url, contentValues);
|
||||||
case TRACKS:
|
case MARKERS -> insertMarker(url, contentValues);
|
||||||
return insertTrack(url, contentValues);
|
default -> throw new IllegalArgumentException("Unknown url " + url);
|
||||||
case MARKERS:
|
};
|
||||||
return insertMarker(url, contentValues);
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("Unknown url " + url);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Uri insertTrackPoint(Uri url, ContentValues values) {
|
private Uri insertTrackPoint(Uri url, ContentValues values) {
|
||||||
|
|||||||
@@ -61,42 +61,18 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
|||||||
for (int toVersion = oldVersion + 1; toVersion <= newVersion; toVersion++) {
|
for (int toVersion = oldVersion + 1; toVersion <= newVersion; toVersion++) {
|
||||||
Log.i(TAG, "Upgrade from " + oldVersion + " to " + toVersion);
|
Log.i(TAG, "Upgrade from " + oldVersion + " to " + toVersion);
|
||||||
switch (toVersion) {
|
switch (toVersion) {
|
||||||
case 24:
|
case 24 -> upgradeFrom23to24(db);
|
||||||
upgradeFrom23to24(db);
|
case 25 -> upgradeFrom24to25(db);
|
||||||
break;
|
case 26 -> upgradeFrom25to26(db);
|
||||||
case 25:
|
case 27 -> upgradeFrom26to27(db);
|
||||||
upgradeFrom24to25(db);
|
case 28 -> upgradeFrom27to28(db);
|
||||||
break;
|
case 29 -> upgradeFrom28to29(db);
|
||||||
case 26:
|
case 30 -> upgradeFrom29to30(db);
|
||||||
upgradeFrom25to26(db);
|
case 31 -> upgradeFrom30to31(db);
|
||||||
break;
|
case 32 -> upgradeFrom31to32(db);
|
||||||
case 27:
|
case 33 -> upgradeFrom32to33(db);
|
||||||
upgradeFrom26to27(db);
|
case 34 -> upgradeFrom33to34(db);
|
||||||
break;
|
default -> throw new RuntimeException("Not implemented: upgrade to " + toVersion);
|
||||||
case 28:
|
|
||||||
upgradeFrom27to28(db);
|
|
||||||
break;
|
|
||||||
case 29:
|
|
||||||
upgradeFrom28to29(db);
|
|
||||||
break;
|
|
||||||
case 30:
|
|
||||||
upgradeFrom29to30(db);
|
|
||||||
break;
|
|
||||||
case 31:
|
|
||||||
upgradeFrom30to31(db);
|
|
||||||
break;
|
|
||||||
case 32:
|
|
||||||
upgradeFrom31to32(db);
|
|
||||||
break;
|
|
||||||
case 33:
|
|
||||||
upgradeFrom32to33(db);
|
|
||||||
break;
|
|
||||||
case 34:
|
|
||||||
upgradeFrom33to34(db);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Not implemented: upgrade to " + toVersion);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,41 +82,18 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
|||||||
for (int toVersion = oldVersion - 1; toVersion >= newVersion; toVersion--) {
|
for (int toVersion = oldVersion - 1; toVersion >= newVersion; toVersion--) {
|
||||||
Log.i(TAG, "Downgrade from " + oldVersion + " to " + toVersion);
|
Log.i(TAG, "Downgrade from " + oldVersion + " to " + toVersion);
|
||||||
switch (toVersion) {
|
switch (toVersion) {
|
||||||
case 23:
|
case 23 -> downgradeFrom24to23(db);
|
||||||
downgradeFrom24to23(db);
|
case 24 -> downgradeFrom25to24(db);
|
||||||
break;
|
case 25 -> downgradeFrom26to25(db);
|
||||||
case 24:
|
case 26 -> downgradeFrom27to26(db);
|
||||||
downgradeFrom25to24(db);
|
case 27 -> downgradeFrom28to27(db);
|
||||||
break;
|
case 28 -> downgradeFrom29to28(db);
|
||||||
case 25:
|
case 29 -> downgradeFrom30to29(db);
|
||||||
downgradeFrom26to25(db);
|
case 30 -> downgradeFrom31to30(db);
|
||||||
break;
|
case 31 -> downgradeFrom32to31(db);
|
||||||
case 26:
|
case 32 -> downgradeFrom33to32(db);
|
||||||
downgradeFrom27to26(db);
|
case 33 -> downgradeFrom34to33(db);
|
||||||
break;
|
default -> throw new RuntimeException("Not implemented: downgrade to " + toVersion);
|
||||||
case 27:
|
|
||||||
downgradeFrom28to27(db);
|
|
||||||
break;
|
|
||||||
case 28:
|
|
||||||
downgradeFrom29to28(db);
|
|
||||||
break;
|
|
||||||
case 29:
|
|
||||||
downgradeFrom30to29(db);
|
|
||||||
break;
|
|
||||||
case 30:
|
|
||||||
downgradeFrom31to30(db);
|
|
||||||
break;
|
|
||||||
case 31:
|
|
||||||
downgradeFrom32to31(db);
|
|
||||||
break;
|
|
||||||
case 32:
|
|
||||||
downgradeFrom33to32(db);
|
|
||||||
break;
|
|
||||||
case 33:
|
|
||||||
downgradeFrom34to33(db);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Not implemented: downgrade to " + toVersion);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,24 +129,18 @@ public class ShareContentProvider extends CustomContentProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static TrackFileFormat getTrackFileFormat(@NonNull Uri uri) {
|
private static TrackFileFormat getTrackFileFormat(@NonNull Uri uri) {
|
||||||
switch (uriMatcher.match(uri)) {
|
return switch (uriMatcher.match(uri)) {
|
||||||
case URI_GPX:
|
case URI_GPX -> TrackFileFormat.GPX;
|
||||||
return TrackFileFormat.GPX;
|
case URI_KML_WITH_TRACKDETAIL_SENSORDATA ->
|
||||||
|
TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA;
|
||||||
case URI_KML_WITH_TRACKDETAIL_SENSORDATA:
|
case URI_KMZ_WITH_TRACKDETAIL_AND_SENSORDATA ->
|
||||||
return TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA;
|
TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA;
|
||||||
|
case URI_KMZ_WITH_TRACKDETAIL_SENSORDATA_AND_PICTURES ->
|
||||||
case URI_KMZ_WITH_TRACKDETAIL_AND_SENSORDATA:
|
TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES;
|
||||||
return TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA;
|
case URI_CSV -> TrackFileFormat.CSV;
|
||||||
case URI_KMZ_WITH_TRACKDETAIL_SENSORDATA_AND_PICTURES:
|
default ->
|
||||||
return TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES;
|
|
||||||
|
|
||||||
case URI_CSV:
|
|
||||||
return TrackFileFormat.CSV;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Could not derive TrackFileFormat from Uri " + uri);
|
throw new RuntimeException("Could not derive TrackFileFormat from Uri " + uri);
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -170,14 +164,14 @@ public class ShareContentProvider extends CustomContentProvider {
|
|||||||
int i = 0;
|
int i = 0;
|
||||||
for (String col : projection) {
|
for (String col : projection) {
|
||||||
switch (col) {
|
switch (col) {
|
||||||
case OpenableColumns.DISPLAY_NAME:
|
case OpenableColumns.DISPLAY_NAME -> {
|
||||||
cols[i] = OpenableColumns.DISPLAY_NAME;
|
cols[i] = OpenableColumns.DISPLAY_NAME;
|
||||||
values[i++] = uri.getLastPathSegment();
|
values[i++] = uri.getLastPathSegment();
|
||||||
break;
|
}
|
||||||
case OpenableColumns.SIZE:
|
case OpenableColumns.SIZE -> {
|
||||||
cols[i] = OpenableColumns.SIZE;
|
cols[i] = OpenableColumns.SIZE;
|
||||||
values[i++] = -1; //Report unknown size; if applications need to know, one need to generate the file here also (count bytes that are written to OutputStream.
|
values[i++] = -1; //Report unknown size; if applications need to know, one need to generate the file here also (count bytes that are written to OutputStream.
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,7 +208,7 @@ public class ShareContentProvider extends CustomContentProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final TrackExporter trackExporter = getTrackFileFormat(uri).createTrackExporter(getContext());
|
final TrackExporter trackExporter = getTrackFileFormat(uri).createTrackExporter(getContext(), new ContentProviderUtils(getContext()));
|
||||||
|
|
||||||
PipeDataWriter<String> pipeDataWriter = (output, uri1, mimeType, opts, args) -> {
|
PipeDataWriter<String> pipeDataWriter = (output, uri1, mimeType, opts, args) -> {
|
||||||
try (FileOutputStream fileOutputStream = new FileOutputStream(output.getFileDescriptor())) {
|
try (FileOutputStream fileOutputStream = new FileOutputStream(output.getFileDescriptor())) {
|
||||||
|
|||||||
@@ -58,16 +58,11 @@ public class Distance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Distance one(UnitSystem unitSystem) {
|
public static Distance one(UnitSystem unitSystem) {
|
||||||
switch (unitSystem) {
|
return switch (unitSystem) {
|
||||||
case METRIC:
|
case METRIC -> Distance.ofKilometer(1);
|
||||||
return Distance.ofKilometer(1);
|
case IMPERIAL -> Distance.ofMile(1);
|
||||||
case IMPERIAL:
|
case NAUTICAL_IMPERIAL -> Distance.ofNauticalMile(1);
|
||||||
return Distance.ofMile(1);
|
};
|
||||||
case NAUTICAL_IMPERIAL:
|
|
||||||
return Distance.ofNauticalMile(1);
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Not implemented");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final double distance_m;
|
private final double distance_m;
|
||||||
@@ -133,28 +128,18 @@ public class Distance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public double toKM_Miles(UnitSystem unitSystem) {
|
public double toKM_Miles(UnitSystem unitSystem) {
|
||||||
switch (unitSystem) {
|
return switch (unitSystem) {
|
||||||
case METRIC:
|
case METRIC -> toKM();
|
||||||
return toKM();
|
case IMPERIAL -> toMI();
|
||||||
case IMPERIAL:
|
case NAUTICAL_IMPERIAL -> toNauticalMiles();
|
||||||
return toMI();
|
};
|
||||||
case NAUTICAL_IMPERIAL:
|
|
||||||
return toNauticalMiles();
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Not implemented");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public double toM_FT(UnitSystem unitSystem) {
|
public double toM_FT(UnitSystem unitSystem) {
|
||||||
switch (unitSystem) {
|
return switch (unitSystem) {
|
||||||
case METRIC:
|
case METRIC -> toM();
|
||||||
return toM();
|
case NAUTICAL_IMPERIAL, IMPERIAL -> toFT();
|
||||||
case NAUTICAL_IMPERIAL:
|
};
|
||||||
case IMPERIAL:
|
|
||||||
return toFT();
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Not implemented");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -46,38 +46,36 @@ public class DistanceFormatter {
|
|||||||
public Pair<String, String> getDistanceParts(Distance distance) {
|
public Pair<String, String> getDistanceParts(Distance distance) {
|
||||||
if (distance.isInvalid()) {
|
if (distance.isInvalid()) {
|
||||||
String valueUnknown = resources.getString(R.string.value_unknown);
|
String valueUnknown = resources.getString(R.string.value_unknown);
|
||||||
switch (unitSystem) {
|
return switch (unitSystem) {
|
||||||
case METRIC:
|
case METRIC -> new Pair<>(valueUnknown, resources.getString(R.string.unit_meter));
|
||||||
return new Pair<>(valueUnknown, resources.getString(R.string.unit_meter));
|
case IMPERIAL, NAUTICAL_IMPERIAL ->
|
||||||
case IMPERIAL:
|
new Pair<>(valueUnknown, resources.getString(R.string.unit_feet));
|
||||||
case NAUTICAL_IMPERIAL:
|
};
|
||||||
return new Pair<>(valueUnknown, resources.getString(R.string.unit_feet));
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Not implemented");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (unitSystem) {
|
switch (unitSystem) {
|
||||||
case METRIC:
|
case METRIC -> {
|
||||||
if (distance.greaterThan(Distance.ofKilometer(threshold))) {
|
if (distance.greaterThan(Distance.ofKilometer(threshold))) {
|
||||||
return new Pair<>(StringUtils.formatDecimal(distance.toKM(), decimalCount), resources.getString(R.string.unit_kilometer));
|
return new Pair<>(StringUtils.formatDecimal(distance.toKM(), decimalCount), resources.getString(R.string.unit_kilometer));
|
||||||
} else {
|
} else {
|
||||||
return new Pair<>(StringUtils.formatDecimal(distance.toM(), decimalCount), resources.getString(R.string.unit_meter));
|
return new Pair<>(StringUtils.formatDecimal(distance.toM(), decimalCount), resources.getString(R.string.unit_meter));
|
||||||
}
|
}
|
||||||
case IMPERIAL:
|
}
|
||||||
|
case IMPERIAL -> {
|
||||||
if (distance.greaterThan(Distance.ofMile(threshold))) {
|
if (distance.greaterThan(Distance.ofMile(threshold))) {
|
||||||
return new Pair<>(StringUtils.formatDecimal(distance.toMI(), decimalCount), resources.getString(R.string.unit_mile));
|
return new Pair<>(StringUtils.formatDecimal(distance.toMI(), decimalCount), resources.getString(R.string.unit_mile));
|
||||||
} else {
|
} else {
|
||||||
return new Pair<>(StringUtils.formatDecimal(distance.toFT(), decimalCount), resources.getString(R.string.unit_feet));
|
return new Pair<>(StringUtils.formatDecimal(distance.toFT(), decimalCount), resources.getString(R.string.unit_feet));
|
||||||
}
|
}
|
||||||
case NAUTICAL_IMPERIAL:
|
}
|
||||||
|
case NAUTICAL_IMPERIAL -> {
|
||||||
if (distance.greaterThan(Distance.ofNauticalMile(threshold))) {
|
if (distance.greaterThan(Distance.ofNauticalMile(threshold))) {
|
||||||
return new Pair<>(StringUtils.formatDecimal(distance.toNauticalMiles(), decimalCount), resources.getString(R.string.unit_nautical_mile));
|
return new Pair<>(StringUtils.formatDecimal(distance.toNauticalMiles(), decimalCount), resources.getString(R.string.unit_nautical_mile));
|
||||||
} else {
|
} else {
|
||||||
return new Pair<>(StringUtils.formatDecimal(distance.toFT(), decimalCount), resources.getString(R.string.unit_feet));
|
return new Pair<>(StringUtils.formatDecimal(distance.toFT(), decimalCount), resources.getString(R.string.unit_feet));
|
||||||
}
|
}
|
||||||
default:
|
}
|
||||||
throw new RuntimeException("Not implemented");
|
default -> throw new RuntimeException("Not implemented");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -115,16 +115,11 @@ public class Speed {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public double to(UnitSystem unitSystem) {
|
public double to(UnitSystem unitSystem) {
|
||||||
switch (unitSystem) {
|
return switch (unitSystem) {
|
||||||
case METRIC:
|
case METRIC -> toKMH();
|
||||||
return toKMH();
|
case IMPERIAL -> toMPH();
|
||||||
case IMPERIAL:
|
case NAUTICAL_IMPERIAL -> toKnots();
|
||||||
return toMPH();
|
};
|
||||||
case NAUTICAL_IMPERIAL:
|
|
||||||
return toKnots();
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Not implemented");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -39,20 +39,14 @@ public class SpeedFormatter {
|
|||||||
* @return the formatted speed (or null) and it's unit as {@link Pair}
|
* @return the formatted speed (or null) and it's unit as {@link Pair}
|
||||||
*/
|
*/
|
||||||
public Pair<String, String> getSpeedParts(Speed speed) {
|
public Pair<String, String> getSpeedParts(Speed speed) {
|
||||||
int unitId;
|
int unitId = switch (unitSystem) {
|
||||||
switch (unitSystem) {
|
case METRIC ->
|
||||||
case METRIC:
|
reportSpeedOrPace ? R.string.unit_kilometer_per_hour : R.string.unit_minute_per_kilometer;
|
||||||
unitId = reportSpeedOrPace ? R.string.unit_kilometer_per_hour : R.string.unit_minute_per_kilometer;
|
case IMPERIAL ->
|
||||||
break;
|
reportSpeedOrPace ? R.string.unit_mile_per_hour : R.string.unit_minute_per_mile;
|
||||||
case IMPERIAL:
|
case NAUTICAL_IMPERIAL ->
|
||||||
unitId = reportSpeedOrPace ? R.string.unit_mile_per_hour : R.string.unit_minute_per_mile;
|
reportSpeedOrPace ? R.string.unit_knots : R.string.unit_minute_per_nautical_mile;
|
||||||
break;
|
};
|
||||||
case NAUTICAL_IMPERIAL:
|
|
||||||
unitId = reportSpeedOrPace ? R.string.unit_knots : R.string.unit_minute_per_nautical_mile;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
String unitString = resources.getString(unitId);
|
String unitString = resources.getString(unitId);
|
||||||
|
|
||||||
|
|||||||
@@ -131,8 +131,7 @@ public class StatisticsRecordingFragment extends Fragment {
|
|||||||
|
|
||||||
int rowIndex = 0;
|
int rowIndex = 0;
|
||||||
int columnIndex = 0;
|
int columnIndex = 0;
|
||||||
for (int i = 0; i < recordingLayout.getFields().size(); i++) {
|
for (DataField dataField : recordingLayout.toRecordingLayout(true).getFields()) {
|
||||||
DataField dataField = recordingLayout.getFields().get(i);
|
|
||||||
GridLayout.LayoutParams param = new GridLayout.LayoutParams();
|
GridLayout.LayoutParams param = new GridLayout.LayoutParams();
|
||||||
param.setGravity(Gravity.FILL_HORIZONTAL);
|
param.setGravity(Gravity.FILL_HORIZONTAL);
|
||||||
param.width = 0;
|
param.width = 0;
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package de.dennisguse.opentracks.io.file;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@@ -25,8 +27,8 @@ public enum TrackFileFormat {
|
|||||||
|
|
||||||
KML_WITH_TRACKDETAIL_AND_SENSORDATA("KML_WITH_TRACKDETAIL_AND_SENSORDATA") {
|
KML_WITH_TRACKDETAIL_AND_SENSORDATA("KML_WITH_TRACKDETAIL_AND_SENSORDATA") {
|
||||||
@Override
|
@Override
|
||||||
public TrackExporter createTrackExporter(Context context) {
|
public TrackExporter createTrackExporter(@NonNull Context context, @NonNull ContentProviderUtils contentProviderUtils) {
|
||||||
return new KMLTrackExporter(context, false);
|
return new KMLTrackExporter(context, contentProviderUtils, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -45,9 +47,9 @@ public enum TrackFileFormat {
|
|||||||
private static final boolean exportPhotos = false;
|
private static final boolean exportPhotos = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TrackExporter createTrackExporter(Context context) {
|
public TrackExporter createTrackExporter(@NonNull Context context, @NonNull ContentProviderUtils contentProviderUtils) {
|
||||||
KMLTrackExporter exporter = new KMLTrackExporter(context, exportPhotos);
|
KMLTrackExporter exporter = new KMLTrackExporter(context, contentProviderUtils, exportPhotos);
|
||||||
return new KmzTrackExporter(context, new ContentProviderUtils(context), exporter, exportPhotos);
|
return new KmzTrackExporter(context, contentProviderUtils, exporter, exportPhotos);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -70,9 +72,9 @@ public enum TrackFileFormat {
|
|||||||
private static final boolean exportPhotos = true;
|
private static final boolean exportPhotos = true;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TrackExporter createTrackExporter(Context context) {
|
public TrackExporter createTrackExporter(@NonNull Context context, @NonNull ContentProviderUtils contentProviderUtils) {
|
||||||
KMLTrackExporter exporter = new KMLTrackExporter(context, exportPhotos);
|
KMLTrackExporter exporter = new KMLTrackExporter(context, contentProviderUtils, exportPhotos);
|
||||||
return new KmzTrackExporter(context, new ContentProviderUtils(context), exporter, exportPhotos);
|
return new KmzTrackExporter(context, contentProviderUtils, exporter, exportPhotos);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -93,8 +95,8 @@ public enum TrackFileFormat {
|
|||||||
|
|
||||||
GPX("GPX") {
|
GPX("GPX") {
|
||||||
@Override
|
@Override
|
||||||
public TrackExporter createTrackExporter(Context context) {
|
public TrackExporter createTrackExporter(@NonNull Context context, @NonNull ContentProviderUtils contentProviderUtils) {
|
||||||
return new GPXTrackExporter(new ContentProviderUtils(context), context.getString(R.string.app_name));
|
return new GPXTrackExporter(contentProviderUtils, context.getString(R.string.app_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -109,8 +111,8 @@ public enum TrackFileFormat {
|
|||||||
|
|
||||||
CSV("CSV") {
|
CSV("CSV") {
|
||||||
@Override
|
@Override
|
||||||
public TrackExporter createTrackExporter(Context context) {
|
public TrackExporter createTrackExporter(@NonNull Context context, @NonNull ContentProviderUtils contentProviderUtils) {
|
||||||
return new CSVTrackExporter(new ContentProviderUtils(context));
|
return new CSVTrackExporter(contentProviderUtils);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -161,7 +163,7 @@ public enum TrackFileFormat {
|
|||||||
*
|
*
|
||||||
* @param context the context
|
* @param context the context
|
||||||
*/
|
*/
|
||||||
public abstract TrackExporter createTrackExporter(Context context);
|
public abstract TrackExporter createTrackExporter(@NonNull Context context, @NonNull ContentProviderUtils contentProviderUtils);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the file extension for each format.
|
* Returns the file extension for each format.
|
||||||
|
|||||||
@@ -93,23 +93,9 @@ public class CSVTrackExporter implements TrackExporter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean writeTrack(Track[] tracks, @NonNull OutputStream outputStream) {
|
public boolean writeTrack(Track[] tracks, @NonNull OutputStream outputStream) {
|
||||||
List<Column> columns = Collections.unmodifiableList(Arrays.asList(
|
List<Column> columns = Collections.unmodifiableList(Arrays.asList(new Column("time", null), new Column("trackpoint_type", t -> quote(t.getType().name())), new Column("latitude", t -> t.hasLocation() ? COORDINATE_FORMAT.format(t.getLatitude()) : ""), new Column("longitude", t -> t.hasLocation() ? COORDINATE_FORMAT.format(t.getLongitude()) : ""), new Column("altitude", t -> t.hasAltitude() ? COORDINATE_FORMAT.format(t.getAltitude().toM()) : ""), new Column("accuracy_horizontal", t -> t.hasHorizontalAccuracy() ? DISTANCE_FORMAT.format(t.getHorizontalAccuracy().toM()) : ""), new Column("accuracy_vertical", t -> t.hasVerticalAccuracy() ? DISTANCE_FORMAT.format(t.getVerticalAccuracy().toM()) : ""),
|
||||||
new Column("time", null),
|
|
||||||
new Column("trackpoint_type", t -> quote(t.getType().name())),
|
|
||||||
new Column("latitude", t -> t.hasLocation() ? COORDINATE_FORMAT.format(t.getLatitude()) : ""),
|
|
||||||
new Column("longitude", t -> t.hasLocation() ? COORDINATE_FORMAT.format(t.getLongitude()) : ""),
|
|
||||||
new Column("altitude", t -> t.hasAltitude() ? COORDINATE_FORMAT.format(t.getAltitude().toM()) : ""),
|
|
||||||
new Column("accuracy_horizontal", t -> t.hasHorizontalAccuracy() ? DISTANCE_FORMAT.format(t.getHorizontalAccuracy().toM()) : ""),
|
|
||||||
new Column("accuracy_vertical", t -> t.hasVerticalAccuracy() ? DISTANCE_FORMAT.format(t.getVerticalAccuracy().toM()) : ""),
|
|
||||||
|
|
||||||
new Column("speed", t -> t.hasSpeed() ? SPEED_FORMAT.format(t.getSpeed().toKMH()) : ""),
|
new Column("speed", t -> t.hasSpeed() ? SPEED_FORMAT.format(t.getSpeed().toKMH()) : ""), new Column("altitude_gain", t -> t.hasAltitudeGain() ? DISTANCE_FORMAT.format(t.getAltitudeGain()) : ""), new Column("altitude_loss", t -> t.hasAltitudeLoss() ? DISTANCE_FORMAT.format(t.getAltitudeLoss()) : ""), new Column("sensor_distance", t -> t.hasSensorDistance() ? DISTANCE_FORMAT.format(t.getSensorDistance().toM()) : ""), new Column("heartrate", t -> t.hasHeartRate() ? HEARTRATE_FORMAT.format(t.getHeartRate().getBPM()) : ""), new Column("cadence", t -> t.hasCadence() ? CADENCE_FORMAT.format(t.getCadence().getRPM()) : ""), new Column("power", t -> t.hasPower() ? ALTITUDE_FORMAT.format(t.getPower().getW()) : "")));
|
||||||
new Column("altitude_gain", t -> t.hasAltitudeGain() ? DISTANCE_FORMAT.format(t.getAltitudeGain()) : ""),
|
|
||||||
new Column("altitude_loss", t -> t.hasAltitudeLoss() ? DISTANCE_FORMAT.format(t.getAltitudeLoss()) : ""),
|
|
||||||
new Column("sensor_distance", t -> t.hasSensorDistance() ? DISTANCE_FORMAT.format(t.getSensorDistance().toM()) : ""),
|
|
||||||
new Column("heartrate", t -> t.hasHeartRate() ? HEARTRATE_FORMAT.format(t.getHeartRate().getBPM()) : ""),
|
|
||||||
new Column("cadence", t -> t.hasCadence() ? CADENCE_FORMAT.format(t.getCadence().getRPM()) : ""),
|
|
||||||
new Column("power", t -> t.hasPower() ? ALTITUDE_FORMAT.format(t.getPower().getW()) : "")
|
|
||||||
));
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
prepare(outputStream);
|
prepare(outputStream);
|
||||||
@@ -144,14 +130,9 @@ public class CSVTrackExporter implements TrackExporter {
|
|||||||
TrackPoint trackPoint = trackPointIterator.next();
|
TrackPoint trackPoint = trackPointIterator.next();
|
||||||
|
|
||||||
switch (trackPoint.getType()) {
|
switch (trackPoint.getType()) {
|
||||||
case SEGMENT_START_MANUAL:
|
case SEGMENT_START_MANUAL, SEGMENT_END_MANUAL, SEGMENT_START_AUTOMATIC, SENSORPOINT, TRACKPOINT ->
|
||||||
case SEGMENT_END_MANUAL:
|
|
||||||
case SEGMENT_START_AUTOMATIC:
|
|
||||||
case SENSORPOINT:
|
|
||||||
case TRACKPOINT:
|
|
||||||
writeTrackPoint(columns, trackPoint);
|
writeTrackPoint(columns, trackPoint);
|
||||||
break;
|
default ->
|
||||||
default:
|
|
||||||
throw new RuntimeException("Exporting this TrackPoint type is not implemented: " + trackPoint.getType());
|
throw new RuntimeException("Exporting this TrackPoint type is not implemented: " + trackPoint.getType());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -171,18 +152,14 @@ public class CSVTrackExporter implements TrackExporter {
|
|||||||
|
|
||||||
public void writeHeader(List<Column> columns) {
|
public void writeHeader(List<Column> columns) {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
String columnNames = columns.stream().map(c -> c.columnName)
|
String columnNames = columns.stream().map(c -> c.columnName).reduce((s, s2) -> s + "," + s2).orElseThrow(() -> new RuntimeException("No columns defined"));
|
||||||
.reduce((s, s2) -> s + "," + s2)
|
|
||||||
.orElseThrow(() -> new RuntimeException("No columns defined"));
|
|
||||||
printWriter.println("#" + columnNames);
|
printWriter.println("#" + columnNames);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeTrackPoint(List<Column> columns, TrackPoint trackPoint) {
|
public void writeTrackPoint(List<Column> columns, TrackPoint trackPoint) {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
String columnNames = columns.stream().map(c -> c.extractor.apply(trackPoint))
|
String columnNames = columns.stream().map(c -> c.extractor.apply(trackPoint)).reduce((s, s2) -> s + "," + s2).orElseThrow(() -> new RuntimeException("No columns defined"));
|
||||||
.reduce((s, s2) -> s + "," + s2)
|
|
||||||
.orElseThrow(() -> new RuntimeException("No columns defined"));
|
|
||||||
printWriter.println(columnNames);
|
printWriter.println(columnNames);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-8
@@ -30,14 +30,9 @@ public class ExportServiceResultReceiver extends ResultReceiver {
|
|||||||
protected void onReceiveResult(int resultCode, Bundle resultData) {
|
protected void onReceiveResult(int resultCode, Bundle resultData) {
|
||||||
Track.Id trackId = resultData.getParcelable(ExportServiceResultReceiver.RESULT_EXTRA_TRACK_ID);
|
Track.Id trackId = resultData.getParcelable(ExportServiceResultReceiver.RESULT_EXTRA_TRACK_ID);
|
||||||
switch (resultCode) {
|
switch (resultCode) {
|
||||||
case RESULT_CODE_SUCCESS:
|
case RESULT_CODE_SUCCESS -> receiver.onExportSuccess(trackId);
|
||||||
receiver.onExportSuccess(trackId);
|
case RESULT_CODE_ERROR -> receiver.onExportError(trackId);
|
||||||
break;
|
default -> throw new RuntimeException("Unknown resultCode.");
|
||||||
case RESULT_CODE_ERROR:
|
|
||||||
receiver.onExportError(trackId);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Unknown resultCode.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -145,36 +145,31 @@ public class GPXTrackExporter implements TrackExporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (trackPoint.getType()) {
|
switch (trackPoint.getType()) {
|
||||||
case SEGMENT_START_MANUAL:
|
case SEGMENT_START_MANUAL ->
|
||||||
Log.i(TAG, "Exporting " + TrackPoint.Type.SEGMENT_START_MANUAL.name() + " is not supported.");
|
Log.i(TAG, "Exporting " + TrackPoint.Type.SEGMENT_START_MANUAL.name() + " is not supported.");
|
||||||
break;
|
case SEGMENT_END_MANUAL -> {
|
||||||
case SEGMENT_END_MANUAL:
|
|
||||||
if (wroteSegment) writeCloseSegment();
|
if (wroteSegment) writeCloseSegment();
|
||||||
wroteSegment = false;
|
wroteSegment = false;
|
||||||
Log.i(TAG, "Exporting " + TrackPoint.Type.SEGMENT_END_MANUAL.name() + " is not supported.");
|
Log.i(TAG, "Exporting " + TrackPoint.Type.SEGMENT_END_MANUAL.name() + " is not supported.");
|
||||||
break;
|
}
|
||||||
case SEGMENT_START_AUTOMATIC:
|
case SEGMENT_START_AUTOMATIC -> {
|
||||||
if (wroteSegment) writeCloseSegment();
|
if (wroteSegment) writeCloseSegment();
|
||||||
writeOpenSegment();
|
writeOpenSegment();
|
||||||
wroteSegment = true;
|
wroteSegment = true;
|
||||||
|
|
||||||
trackDistance = trackDistance.plus(writeTrackPoint(track.getZoneOffset(), trackPoint, sensorPoints, trackDistance));
|
trackDistance = trackDistance.plus(writeTrackPoint(track.getZoneOffset(), trackPoint, sensorPoints, trackDistance));
|
||||||
sensorPoints.clear();
|
sensorPoints.clear();
|
||||||
break;
|
}
|
||||||
case SENSORPOINT:
|
case SENSORPOINT -> sensorPoints.add(trackPoint);
|
||||||
sensorPoints.add(trackPoint);
|
case TRACKPOINT -> {
|
||||||
break;
|
|
||||||
case TRACKPOINT:
|
|
||||||
if (!wroteSegment) {
|
if (!wroteSegment) {
|
||||||
// Might happen for older data (pre v3.15.0)
|
// Might happen for older data (pre v3.15.0)
|
||||||
writeOpenSegment();
|
writeOpenSegment();
|
||||||
wroteSegment = true;
|
wroteSegment = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
trackDistance = trackDistance.plus(writeTrackPoint(track.getZoneOffset(), trackPoint, sensorPoints, trackDistance));
|
trackDistance = trackDistance.plus(writeTrackPoint(track.getZoneOffset(), trackPoint, sensorPoints, trackDistance));
|
||||||
sensorPoints.clear();
|
sensorPoints.clear();
|
||||||
break;
|
}
|
||||||
default:
|
default ->
|
||||||
throw new RuntimeException("Exporting this TrackPoint type is not implemented: " + trackPoint.getType());
|
throw new RuntimeException("Exporting this TrackPoint type is not implemented: " + trackPoint.getType());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,10 +93,10 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
private final List<Float> accuracyHorizontal = new ArrayList<>();
|
private final List<Float> accuracyHorizontal = new ArrayList<>();
|
||||||
private final List<Float> accuracyVertical = new ArrayList<>();
|
private final List<Float> accuracyVertical = new ArrayList<>();
|
||||||
|
|
||||||
public KMLTrackExporter(Context context, boolean exportPhotos) {
|
public KMLTrackExporter(Context context, ContentProviderUtils contentProviderUtils, boolean exportPhotos) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.exportPhotos = exportPhotos;
|
this.exportPhotos = exportPhotos;
|
||||||
this.contentProviderUtils = new ContentProviderUtils(context);
|
this.contentProviderUtils = contentProviderUtils;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean writeTrack(Track track, @NonNull OutputStream outputStream) {
|
public boolean writeTrack(Track track, @NonNull OutputStream outputStream) {
|
||||||
@@ -169,29 +169,27 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (trackPoint.getType()) {
|
switch (trackPoint.getType()) {
|
||||||
case SEGMENT_START_MANUAL:
|
case SEGMENT_START_MANUAL, SEGMENT_START_AUTOMATIC -> {
|
||||||
case SEGMENT_START_AUTOMATIC:
|
|
||||||
if (wroteSegment) writeCloseSegment();
|
if (wroteSegment) writeCloseSegment();
|
||||||
writeOpenSegment();
|
writeOpenSegment();
|
||||||
writeTrackPoint(track.getZoneOffset(), trackPoint);
|
writeTrackPoint(track.getZoneOffset(), trackPoint);
|
||||||
wroteSegment = true;
|
wroteSegment = true;
|
||||||
break;
|
}
|
||||||
case SEGMENT_END_MANUAL:
|
case SEGMENT_END_MANUAL -> {
|
||||||
if (!wroteSegment) writeOpenSegment();
|
if (!wroteSegment) writeOpenSegment();
|
||||||
writeTrackPoint(track.getZoneOffset(), trackPoint);
|
writeTrackPoint(track.getZoneOffset(), trackPoint);
|
||||||
writeCloseSegment();
|
writeCloseSegment();
|
||||||
wroteSegment = false;
|
wroteSegment = false;
|
||||||
break;
|
}
|
||||||
case SENSORPOINT:
|
case SENSORPOINT, TRACKPOINT -> {
|
||||||
case TRACKPOINT:
|
|
||||||
if (!wroteSegment) {
|
if (!wroteSegment) {
|
||||||
// Might happen for older data (pre v3.15.0)
|
// Might happen for older data (pre v3.15.0)
|
||||||
writeOpenSegment();
|
writeOpenSegment();
|
||||||
wroteSegment = true;
|
wroteSegment = true;
|
||||||
}
|
}
|
||||||
writeTrackPoint(track.getZoneOffset(), trackPoint);
|
writeTrackPoint(track.getZoneOffset(), trackPoint);
|
||||||
break;
|
}
|
||||||
default:
|
default ->
|
||||||
throw new RuntimeException("Exporting this TrackPoint type is not implemented: " + trackPoint.getType());
|
throw new RuntimeException("Exporting this TrackPoint type is not implemented: " + trackPoint.getType());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,15 +224,19 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
|
|
||||||
private void writeHeader(Track[] tracks) {
|
private void writeHeader(Track[] tracks) {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
printWriter.println(
|
||||||
printWriter.println("<kml xmlns=\"http://www.opengis.net/kml/2.3\"");
|
"""
|
||||||
printWriter.println("xmlns:atom=\"http://www.w3.org/2005/Atom\"");
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
printWriter.println("xmlns:opentracks=\"http://opentracksapp.com/xmlschemas/v1\">");
|
""");
|
||||||
//TODO ADD xsi:schemaLocation for atom
|
printWriter.println(
|
||||||
printWriter.println("xsi:schemaLocation=" +
|
"""
|
||||||
"\"http://www.opengis.net/kml/2.3 http://schemas.opengis.net/kml/2.3/ogckml23.xsd"
|
<kml xmlns="http://www.opengis.net/kml/2.3"
|
||||||
+ " http://opentracksapp.com/xmlschemas/v1 http://opentracksapp.com/xmlschemas/OpenTracks_v1.xsd\">");
|
xmlns:atom="http://www.w3.org/2005/Atom"
|
||||||
|
xmlns:opentracks="http://opentracksapp.com/xmlschemas/v1"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.opengis.net/kml/2.3 http://schemas.opengis.net/kml/2.3/ogckml23.xsd
|
||||||
|
http://opentracksapp.com/xmlschemas/v1 http://opentracksapp.com/xmlschemas/OpenTracks_v1.xsd">
|
||||||
|
"""); //TODO ADD xsi:schemaLocation for atom
|
||||||
printWriter.println("<Document>");
|
printWriter.println("<Document>");
|
||||||
printWriter.println("<open>1</open>");
|
printWriter.println("<open>1</open>");
|
||||||
printWriter.println("<visibility>1</visibility>");
|
printWriter.println("<visibility>1</visibility>");
|
||||||
|
|||||||
@@ -156,103 +156,94 @@ public class GpxTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
|||||||
@Override
|
@Override
|
||||||
public void endElement(String uri, String localName, String tag) {
|
public void endElement(String uri, String localName, String tag) {
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case TAG_GPX:
|
case TAG_GPX -> onFileEnd();
|
||||||
onFileEnd();
|
case TAG_MARKER -> onMarkerEnd();
|
||||||
break;
|
case TAG_TRACK -> {
|
||||||
case TAG_MARKER:
|
|
||||||
onMarkerEnd();
|
|
||||||
break;
|
|
||||||
case TAG_TRACK:
|
|
||||||
trackImporter.setTrack(context, name, uuid, description, category, null, zoneOffset);
|
trackImporter.setTrack(context, name, uuid, description, category, null, zoneOffset);
|
||||||
zoneOffset = null;
|
zoneOffset = null;
|
||||||
break;
|
}
|
||||||
case TAG_TRACK_SEGMENT:
|
case TAG_TRACK_SEGMENT -> onTrackSegmentEnd();
|
||||||
onTrackSegmentEnd();
|
case TAG_TRACK_POINT -> currentSegment.add(createTrackPoint());
|
||||||
break;
|
case TAG_NAME -> {
|
||||||
case TAG_TRACK_POINT:
|
|
||||||
currentSegment.add(createTrackPoint());
|
|
||||||
break;
|
|
||||||
case TAG_NAME:
|
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
name = content.trim();
|
name = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_DESCRIPTION:
|
case TAG_DESCRIPTION -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
description = content.trim();
|
description = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_TYPE:
|
case TAG_TYPE -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
category = content.trim();
|
category = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_TIME:
|
case TAG_TIME -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
time = content.trim();
|
time = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_ALTITUDE:
|
case TAG_ALTITUDE -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
altitude = content.trim();
|
altitude = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_COMMENT:
|
case TAG_COMMENT -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
markerType = content.trim();
|
markerType = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_EXTENSION_SPEED:
|
case TAG_EXTENSION_SPEED, TAG_EXTENSION_SPEED_COMPAT -> {
|
||||||
case TAG_EXTENSION_SPEED_COMPAT:
|
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
speed = content.trim();
|
speed = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_EXTENSION_HEARTRATE:
|
case TAG_EXTENSION_HEARTRATE -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
heartrate = content.trim();
|
heartrate = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_EXTENSION_CADENCE:
|
case TAG_EXTENSION_CADENCE -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
cadence = content.trim();
|
cadence = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_EXTENSION_POWER:
|
case TAG_EXTENSION_POWER -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
power = content.trim();
|
power = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_ID:
|
case TAG_ID -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
uuid = content.trim();
|
uuid = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_EXTENSION_GAIN:
|
case TAG_EXTENSION_GAIN -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
gain = content.trim();
|
gain = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_EXTENSION_LOSS:
|
case TAG_EXTENSION_LOSS -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
loss = content.trim();
|
loss = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_EXTENSION_DISTANCE:
|
case TAG_EXTENSION_DISTANCE -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
sensorDistance = content.trim();
|
sensorDistance = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_EXTENSION_ACCURACY_HORIZONTAL:
|
case TAG_EXTENSION_ACCURACY_HORIZONTAL -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
accuracyHorizontal = content.trim();
|
accuracyHorizontal = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_EXTENSION_ACCURACY_VERTICAL:
|
case TAG_EXTENSION_ACCURACY_VERTICAL -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
accuracyVertical = content.trim();
|
accuracyVertical = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
content = "";
|
content = "";
|
||||||
|
|||||||
@@ -79,18 +79,16 @@ public class ImportViewModel extends AndroidViewModel implements ImportServiceRe
|
|||||||
String message = resultData.getString(ImportServiceResultReceiver.RESULT_EXTRA_MESSAGE);
|
String message = resultData.getString(ImportServiceResultReceiver.RESULT_EXTRA_MESSAGE);
|
||||||
|
|
||||||
switch (resultCode) {
|
switch (resultCode) {
|
||||||
case ImportServiceResultReceiver.RESULT_CODE_ERROR:
|
case ImportServiceResultReceiver.RESULT_CODE_ERROR -> {
|
||||||
summary.errorCount++;
|
summary.errorCount++;
|
||||||
summary.fileErrors.add(getApplication().getString(R.string.import_error_info, fileName, message));
|
summary.fileErrors.add(getApplication().getString(R.string.import_error_info, fileName, message));
|
||||||
break;
|
}
|
||||||
case ImportServiceResultReceiver.RESULT_CODE_IMPORTED:
|
case ImportServiceResultReceiver.RESULT_CODE_IMPORTED -> {
|
||||||
summary.importedTrackIds.addAll(trackIds);
|
summary.importedTrackIds.addAll(trackIds);
|
||||||
summary.successCount++;
|
summary.successCount++;
|
||||||
break;
|
}
|
||||||
case ImportServiceResultReceiver.RESULT_CODE_ALREADY_EXISTS:
|
case ImportServiceResultReceiver.RESULT_CODE_ALREADY_EXISTS -> summary.existsCount++;
|
||||||
summary.existsCount++;
|
default ->
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new RuntimeException(TAG + ": import service result code invalid: " + resultCode);
|
throw new RuntimeException(TAG + ": import service result code invalid: " + resultCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -136,27 +136,18 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
|||||||
@Override
|
@Override
|
||||||
public void startElement(String uri, String localName, String tag, Attributes attributes) throws SAXException {
|
public void startElement(String uri, String localName, String tag, Attributes attributes) throws SAXException {
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case TAG_PLACEMARK:
|
case TAG_PLACEMARK, TAG_PHOTO_OVERLAY ->
|
||||||
case TAG_PHOTO_OVERLAY:
|
|
||||||
// Note that a track is contained in a Placemark, calling onMarkerStart will clear various track variables like name, category, and description.
|
// Note that a track is contained in a Placemark, calling onMarkerStart will clear various track variables like name, category, and description.
|
||||||
onMarkerStart();
|
onMarkerStart();
|
||||||
break;
|
case TAG_MULTI_TRACK, TAG_KML22_MULTI_TRACK -> trackImporter.newTrack();
|
||||||
case TAG_MULTI_TRACK:
|
case TAG_TRACK, TAG_KML22_TRACK -> {
|
||||||
case TAG_KML22_MULTI_TRACK:
|
|
||||||
trackImporter.newTrack();
|
|
||||||
break;
|
|
||||||
case TAG_TRACK:
|
|
||||||
case TAG_KML22_TRACK:
|
|
||||||
if (trackImporter == null) {
|
if (trackImporter == null) {
|
||||||
throw new SAXException("Missing " + TAG_MULTI_TRACK);
|
throw new SAXException("Missing " + TAG_MULTI_TRACK);
|
||||||
}
|
}
|
||||||
onTrackSegmentStart();
|
onTrackSegmentStart();
|
||||||
break;
|
}
|
||||||
case TAG_DATA_CATEGORY:
|
case TAG_DATA_CATEGORY, TAG_SIMPLE_ARRAY_DATA, TAG_KML22_SIMPLE_ARRAY_DATA ->
|
||||||
case TAG_SIMPLE_ARRAY_DATA:
|
|
||||||
case TAG_KML22_SIMPLE_ARRAY_DATA:
|
|
||||||
dataType = attributes.getValue(ATTRIBUTE_NAME);
|
dataType = attributes.getValue(ATTRIBUTE_NAME);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,32 +159,18 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
|||||||
@Override
|
@Override
|
||||||
public void endElement(String uri, String localName, String tag) throws SAXException {
|
public void endElement(String uri, String localName, String tag) throws SAXException {
|
||||||
switch (tag) {
|
switch (tag) {
|
||||||
case TAG_KML:
|
case TAG_KML -> onFileEnd();
|
||||||
onFileEnd();
|
case TAG_PLACEMARK, TAG_PHOTO_OVERLAY ->
|
||||||
break;
|
|
||||||
case TAG_PLACEMARK:
|
|
||||||
case TAG_PHOTO_OVERLAY:
|
|
||||||
// Note that a track is contained in a Placemark, calling onMarkerEnd is save since markerType is not set for a track.
|
// Note that a track is contained in a Placemark, calling onMarkerEnd is save since markerType is not set for a track.
|
||||||
onMarkerEnd();
|
onMarkerEnd();
|
||||||
break;
|
case TAG_COORDINATES -> onMarkerLocationEnd();
|
||||||
case TAG_COORDINATES:
|
case TAG_MULTI_TRACK, TAG_KML22_MULTI_TRACK -> {
|
||||||
onMarkerLocationEnd();
|
|
||||||
break;
|
|
||||||
case TAG_MULTI_TRACK:
|
|
||||||
case TAG_KML22_MULTI_TRACK:
|
|
||||||
trackImporter.setTrack(context, name, uuid, description, category, icon, zoneOffset);
|
trackImporter.setTrack(context, name, uuid, description, category, icon, zoneOffset);
|
||||||
zoneOffset = null;
|
zoneOffset = null;
|
||||||
break;
|
}
|
||||||
case TAG_TRACK:
|
case TAG_TRACK, TAG_KML22_TRACK -> onTrackSegmentEnd();
|
||||||
case TAG_KML22_TRACK:
|
case TAG_COORD, TAG_KML22_COORD -> onCoordEnded();
|
||||||
onTrackSegmentEnd();
|
case TAG_VALUE, TAG_KML22_VALUE -> {
|
||||||
break;
|
|
||||||
case TAG_COORD:
|
|
||||||
case TAG_KML22_COORD:
|
|
||||||
onCoordEnded();
|
|
||||||
break;
|
|
||||||
case TAG_VALUE:
|
|
||||||
case TAG_KML22_VALUE:
|
|
||||||
if (KMLTrackExporter.EXTENDED_DATA_TYPE_CATEGORY.equals(dataType)) {
|
if (KMLTrackExporter.EXTENDED_DATA_TYPE_CATEGORY.equals(dataType)) {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
category = content.trim();
|
category = content.trim();
|
||||||
@@ -201,28 +178,28 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
|||||||
} else {
|
} else {
|
||||||
onExtendedDataValueEnd();
|
onExtendedDataValueEnd();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_NAME:
|
case TAG_NAME -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
name = content.trim();
|
name = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_UUID:
|
case TAG_UUID -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
uuid = content.trim();
|
uuid = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_DESCRIPTION:
|
case TAG_DESCRIPTION -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
description = content.trim();
|
description = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_ICON:
|
case TAG_ICON -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
icon = content.trim();
|
icon = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_WHEN:
|
case TAG_WHEN -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
try {
|
try {
|
||||||
OffsetDateTime time = StringUtils.parseTime(content.trim());
|
OffsetDateTime time = StringUtils.parseTime(content.trim());
|
||||||
@@ -234,18 +211,17 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
|||||||
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse time: %s", content.trim())), e);
|
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse time: %s", content.trim())), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
case TAG_STYLE_URL -> {
|
||||||
case TAG_STYLE_URL:
|
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
markerType = content.trim();
|
markerType = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case TAG_HREF:
|
case TAG_HREF -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
photoUrl = content.trim();
|
photoUrl = content.trim();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset element content
|
// Reset element content
|
||||||
@@ -436,34 +412,18 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch (dataType) {
|
switch (dataType) {
|
||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_SPEED:
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_SPEED -> sensorSpeedList.add(value);
|
||||||
sensorSpeedList.add(value);
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_DISTANCE -> sensorDistanceList.add(value);
|
||||||
break;
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_POWER -> sensorPowerList.add(value);
|
||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_DISTANCE:
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_HEART_RATE -> sensorHeartRateList.add(value);
|
||||||
sensorDistanceList.add(value);
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_CADENCE -> sensorCadenceList.add(value);
|
||||||
break;
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_ALTITUDE_GAIN -> altitudeGainList.add(value);
|
||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_POWER:
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_ALTITUDE_LOSS -> altitudeLossList.add(value);
|
||||||
sensorPowerList.add(value);
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_ACCURACY_HORIZONTAL ->
|
||||||
break;
|
|
||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_HEART_RATE:
|
|
||||||
sensorHeartRateList.add(value);
|
|
||||||
break;
|
|
||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_CADENCE:
|
|
||||||
sensorCadenceList.add(value);
|
|
||||||
break;
|
|
||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_ALTITUDE_GAIN:
|
|
||||||
altitudeGainList.add(value);
|
|
||||||
break;
|
|
||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_ALTITUDE_LOSS:
|
|
||||||
altitudeLossList.add(value);
|
|
||||||
break;
|
|
||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_ACCURACY_HORIZONTAL:
|
|
||||||
accuracyHorizontal.add(value);
|
accuracyHorizontal.add(value);
|
||||||
break;
|
case KMLTrackExporter.EXTENDED_DATA_TYPE_ACCURACY_VERTICAL ->
|
||||||
case KMLTrackExporter.EXTENDED_DATA_TYPE_ACCURACY_VERTICAL:
|
|
||||||
accuracyVertical.add(value);
|
accuracyVertical.add(value);
|
||||||
break;
|
default ->
|
||||||
default:
|
|
||||||
Log.w(TAG, "Data from extended data " + dataType + " is not (yet) supported.");
|
Log.w(TAG, "Data from extended data " + dataType + " is not (yet) supported.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-10
@@ -53,26 +53,22 @@ public abstract class AbstractBluetoothConnectionManager<DataType> {
|
|||||||
@Override
|
@Override
|
||||||
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
|
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
|
||||||
switch (newState) {
|
switch (newState) {
|
||||||
case BluetoothProfile.STATE_CONNECTING:
|
case BluetoothProfile.STATE_CONNECTING ->
|
||||||
Log.i(TAG, "Connecting to sensor: " + gatt.getDevice());
|
Log.i(TAG, "Connecting to sensor: " + gatt.getDevice());
|
||||||
break;
|
case BluetoothProfile.STATE_CONNECTED -> {
|
||||||
case BluetoothProfile.STATE_CONNECTED:
|
|
||||||
Log.i(TAG, "Connected to sensor: " + gatt.getDevice() + "; discovering services.");
|
Log.i(TAG, "Connected to sensor: " + gatt.getDevice() + "; discovering services.");
|
||||||
|
|
||||||
gatt.discoverServices();
|
gatt.discoverServices();
|
||||||
break;
|
}
|
||||||
case BluetoothProfile.STATE_DISCONNECTING:
|
case BluetoothProfile.STATE_DISCONNECTING ->
|
||||||
Log.i(TAG, "Disconnecting from sensor: " + gatt.getDevice());
|
Log.i(TAG, "Disconnecting from sensor: " + gatt.getDevice());
|
||||||
break;
|
case BluetoothProfile.STATE_DISCONNECTED -> {
|
||||||
|
|
||||||
case BluetoothProfile.STATE_DISCONNECTED:
|
|
||||||
//This is also triggered, if no connection was established (ca. 30s)
|
//This is also triggered, if no connection was established (ca. 30s)
|
||||||
Log.i(TAG, "Disconnected from sensor: " + gatt.getDevice() + "; trying to reconnect");
|
Log.i(TAG, "Disconnected from sensor: " + gatt.getDevice() + "; trying to reconnect");
|
||||||
if (gatt.connect()) {
|
if (gatt.connect()) {
|
||||||
Log.e(TAG, "Could not trigger reconnect for sensor: " + gatt.getDevice());
|
Log.e(TAG, "Could not trigger reconnect for sensor: " + gatt.getDevice());
|
||||||
}
|
}
|
||||||
clearData();
|
clearData();
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -76,9 +76,8 @@ public class SensorDataCyclingCadence extends SensorData<Cadence> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(@Nullable Object obj) {
|
public boolean equals(@Nullable Object obj) {
|
||||||
if (!(obj instanceof SensorDataCyclingCadence)) return false;
|
if (!(obj instanceof SensorDataCyclingCadence comp)) return false;
|
||||||
|
|
||||||
SensorDataCyclingCadence comp = (SensorDataCyclingCadence) obj;
|
|
||||||
if (hasData() && comp.hasData() == hasData()) {
|
if (hasData() && comp.hasData() == hasData()) {
|
||||||
return getCrankRevolutionsCount() == comp.getCrankRevolutionsCount() && getCrankRevolutionsTime() == comp.getCrankRevolutionsTime();
|
return getCrankRevolutionsCount() == comp.getCrankRevolutionsCount() && getCrankRevolutionsTime() == comp.getCrankRevolutionsTime();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+1
-2
@@ -93,9 +93,8 @@ public class SensorDataCyclingDistanceSpeed extends SensorData<SensorDataCycling
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(@Nullable Object obj) {
|
public boolean equals(@Nullable Object obj) {
|
||||||
if (!(obj instanceof SensorDataCyclingDistanceSpeed)) return false;
|
if (!(obj instanceof SensorDataCyclingDistanceSpeed comp)) return false;
|
||||||
|
|
||||||
SensorDataCyclingDistanceSpeed comp = (SensorDataCyclingDistanceSpeed) obj;
|
|
||||||
if (!(hasData() && comp.hasData())) {
|
if (!(hasData() && comp.hasData())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-11
@@ -28,6 +28,7 @@ import android.util.Log;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
@@ -67,17 +68,8 @@ public class VoiceAnnouncement {
|
|||||||
public void onAudioFocusChange(int focusChange) {
|
public void onAudioFocusChange(int focusChange) {
|
||||||
Log.d(TAG, "Audio focus changed to " + focusChange);
|
Log.d(TAG, "Audio focus changed to " + focusChange);
|
||||||
|
|
||||||
boolean stop = false;
|
boolean stop = List.of(AudioManager.AUDIOFOCUS_LOSS, AudioManager.AUDIOFOCUS_LOSS_TRANSIENT, AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK)
|
||||||
switch (focusChange) {
|
.contains(focusChange);
|
||||||
case AudioManager.AUDIOFOCUS_GAIN:
|
|
||||||
stop = false;
|
|
||||||
break;
|
|
||||||
case AudioManager.AUDIOFOCUS_LOSS:
|
|
||||||
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
|
|
||||||
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
|
|
||||||
stop = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stop && tts != null && tts.isSpeaking()) {
|
if (stop && tts != null && tts.isSpeaking()) {
|
||||||
tts.stop();
|
tts.stop();
|
||||||
|
|||||||
+7
-8
@@ -43,29 +43,28 @@ class VoiceAnnouncementUtils {
|
|||||||
String unitDistanceTTS;
|
String unitDistanceTTS;
|
||||||
String unitSpeedTTS;
|
String unitSpeedTTS;
|
||||||
switch (unitSystem) {
|
switch (unitSystem) {
|
||||||
case METRIC:
|
case METRIC -> {
|
||||||
perUnitStringId = R.string.voice_per_kilometer;
|
perUnitStringId = R.string.voice_per_kilometer;
|
||||||
distanceId = R.plurals.voiceDistanceKilometers;
|
distanceId = R.plurals.voiceDistanceKilometers;
|
||||||
speedId = R.plurals.voiceSpeedKilometersPerHour;
|
speedId = R.plurals.voiceSpeedKilometersPerHour;
|
||||||
unitDistanceTTS = "kilometer";
|
unitDistanceTTS = "kilometer";
|
||||||
unitSpeedTTS = "kilometer per hour";
|
unitSpeedTTS = "kilometer per hour";
|
||||||
break;
|
}
|
||||||
case IMPERIAL:
|
case IMPERIAL -> {
|
||||||
perUnitStringId = R.string.voice_per_mile;
|
perUnitStringId = R.string.voice_per_mile;
|
||||||
distanceId = R.plurals.voiceDistanceMiles;
|
distanceId = R.plurals.voiceDistanceMiles;
|
||||||
speedId = R.plurals.voiceSpeedMilesPerHour;
|
speedId = R.plurals.voiceSpeedMilesPerHour;
|
||||||
unitDistanceTTS = "mile";
|
unitDistanceTTS = "mile";
|
||||||
unitSpeedTTS = "mile per hour";
|
unitSpeedTTS = "mile per hour";
|
||||||
break;
|
}
|
||||||
case NAUTICAL_IMPERIAL:
|
case NAUTICAL_IMPERIAL -> {
|
||||||
perUnitStringId = R.string.voice_per_nautical_mile;
|
perUnitStringId = R.string.voice_per_nautical_mile;
|
||||||
distanceId = R.plurals.voiceDistanceNauticalMiles;
|
distanceId = R.plurals.voiceDistanceNauticalMiles;
|
||||||
speedId = R.plurals.voiceSpeedMKnots;
|
speedId = R.plurals.voiceSpeedMKnots;
|
||||||
unitDistanceTTS = "nautical mile";
|
unitDistanceTTS = "nautical mile";
|
||||||
unitSpeedTTS = "knots";
|
unitSpeedTTS = "knots";
|
||||||
break;
|
}
|
||||||
default:
|
default -> throw new RuntimeException("Not implemented");
|
||||||
throw new RuntimeException("Not implemented");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double distanceInUnit = totalDistance.toKM_Miles(unitSystem);
|
double distanceInUnit = totalDistance.toKM_Miles(unitSystem);
|
||||||
|
|||||||
@@ -68,18 +68,10 @@ public class DefaultsSettingsFragment extends PreferenceFragmentCompat implement
|
|||||||
|
|
||||||
ListPreference statsRatePreferences = findPreference(getString(R.string.stats_rate_key));
|
ListPreference statsRatePreferences = findPreference(getString(R.string.stats_rate_key));
|
||||||
|
|
||||||
int entriesId;
|
int entriesId = switch (unitSystem) {
|
||||||
switch (unitSystem) {
|
case METRIC -> R.array.stats_rate_metric_options;
|
||||||
case METRIC:
|
case IMPERIAL, NAUTICAL_IMPERIAL -> R.array.stats_rate_imperial_options;
|
||||||
entriesId = R.array.stats_rate_metric_options;
|
};
|
||||||
break;
|
|
||||||
case IMPERIAL:
|
|
||||||
case NAUTICAL_IMPERIAL:
|
|
||||||
entriesId = R.array.stats_rate_imperial_options;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] entries = getResources().getStringArray(entriesId);
|
String[] entries = getResources().getStringArray(entriesId);
|
||||||
statsRatePreferences.setEntries(entries);
|
statsRatePreferences.setEntries(entries);
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ public class ImportExportSettingsFragment extends PreferenceFragmentCompat {
|
|||||||
DocumentFile directory = IntentUtils.toDocumentFile(getContext(), directoryUri);
|
DocumentFile directory = IntentUtils.toDocumentFile(getContext(), directoryUri);
|
||||||
//Use same value for not set as Androidx ListPreference and EditTextPreference
|
//Use same value for not set as Androidx ListPreference and EditTextPreference
|
||||||
if (directory == null) {
|
if (directory == null) {
|
||||||
return getString(R.string.not_set);
|
return getString(androidx.preference.R.string.not_set);
|
||||||
}
|
}
|
||||||
|
|
||||||
return directoryUri + (directory.canWrite() ? "" : getString(R.string.export_dir_not_writable));
|
return directoryUri + (directory.canWrite() ? "" : getString(R.string.export_dir_not_writable));
|
||||||
|
|||||||
@@ -32,14 +32,9 @@ class PreferencesOpenHelper {
|
|||||||
PreferencesUtils.setInt(R.string.prefs_last_version_key, version);
|
PreferencesUtils.setInt(R.string.prefs_last_version_key, version);
|
||||||
for (int i = 1; i <= version; i++) {
|
for (int i = 1; i <= version; i++) {
|
||||||
switch (i) {
|
switch (i) {
|
||||||
case 1:
|
case 1 -> upgradeFrom0to1();
|
||||||
upgradeFrom0to1();
|
case 2 -> upgradeFrom1to2();
|
||||||
break;
|
default -> throw new RuntimeException("Not implemented: upgrade to " + version);
|
||||||
case 2:
|
|
||||||
upgradeFrom1to2();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Not implemented: upgrade to " + version);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -448,23 +448,21 @@ public class PreferencesUtils {
|
|||||||
|
|
||||||
String displayValue = formatter.formatDistance(distance);
|
String displayValue = formatter.formatDistance(distance);
|
||||||
switch (unitSystem) {
|
switch (unitSystem) {
|
||||||
case METRIC:
|
case METRIC -> {
|
||||||
if (value == recordingDistanceIntervalDefault) {
|
if (value == recordingDistanceIntervalDefault) {
|
||||||
entries[i] = resources.getString(R.string.value_integer_meter_recommended, value);
|
entries[i] = resources.getString(R.string.value_integer_meter_recommended, value);
|
||||||
} else {
|
} else {
|
||||||
entries[i] = displayValue;
|
entries[i] = displayValue;
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case IMPERIAL:
|
case IMPERIAL, NAUTICAL_IMPERIAL -> {
|
||||||
case NAUTICAL_IMPERIAL:
|
|
||||||
if (value == recordingDistanceIntervalDefault) {
|
if (value == recordingDistanceIntervalDefault) {
|
||||||
entries[i] = resources.getString(R.string.value_integer_feet_recommended, (int) distance.toFT());
|
entries[i] = resources.getString(R.string.value_integer_feet_recommended, (int) distance.toFT());
|
||||||
} else {
|
} else {
|
||||||
entries[i] = displayValue;
|
entries[i] = displayValue;
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
default:
|
default -> throw new RuntimeException("Not implemented");
|
||||||
throw new RuntimeException("Not implemented");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -494,23 +492,21 @@ public class PreferencesUtils {
|
|||||||
|
|
||||||
String displayValue = formatter.formatDistance(distance);
|
String displayValue = formatter.formatDistance(distance);
|
||||||
switch (unitSystem) {
|
switch (unitSystem) {
|
||||||
case METRIC:
|
case METRIC -> {
|
||||||
if (value == maxRecordingDistanceDefault) {
|
if (value == maxRecordingDistanceDefault) {
|
||||||
entries[i] = resources.getString(R.string.value_integer_meter_recommended, value);
|
entries[i] = resources.getString(R.string.value_integer_meter_recommended, value);
|
||||||
} else {
|
} else {
|
||||||
entries[i] = displayValue;
|
entries[i] = displayValue;
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case IMPERIAL:
|
case IMPERIAL, NAUTICAL_IMPERIAL -> {
|
||||||
case NAUTICAL_IMPERIAL:
|
|
||||||
if (value == maxRecordingDistanceDefault) {
|
if (value == maxRecordingDistanceDefault) {
|
||||||
entries[i] = resources.getString(R.string.value_integer_feet_recommended, (int) distance.toFT());
|
entries[i] = resources.getString(R.string.value_integer_feet_recommended, (int) distance.toFT());
|
||||||
} else {
|
} else {
|
||||||
entries[i] = displayValue;
|
entries[i] = displayValue;
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
default:
|
default -> throw new RuntimeException("Not implemented");
|
||||||
throw new RuntimeException("Not implemented");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -569,7 +565,7 @@ public class PreferencesUtils {
|
|||||||
|
|
||||||
String displayValue = formatter.formatDistance(distance);
|
String displayValue = formatter.formatDistance(distance);
|
||||||
switch (unitSystem) {
|
switch (unitSystem) {
|
||||||
case METRIC:
|
case METRIC -> {
|
||||||
if (value == recordingGPSAccuracyDefault) {
|
if (value == recordingGPSAccuracyDefault) {
|
||||||
entries[i] = resources.getString(R.string.value_integer_meter_recommended, value);
|
entries[i] = resources.getString(R.string.value_integer_meter_recommended, value);
|
||||||
} else if (value == recordingGPSAccuracyExcellent) {
|
} else if (value == recordingGPSAccuracyExcellent) {
|
||||||
@@ -579,9 +575,8 @@ public class PreferencesUtils {
|
|||||||
} else {
|
} else {
|
||||||
entries[i] = displayValue;
|
entries[i] = displayValue;
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case IMPERIAL:
|
case IMPERIAL, NAUTICAL_IMPERIAL -> {
|
||||||
case NAUTICAL_IMPERIAL:
|
|
||||||
if (value == recordingGPSAccuracyDefault) {
|
if (value == recordingGPSAccuracyDefault) {
|
||||||
entries[i] = resources.getString(R.string.value_integer_feet_recommended, (int) distance.toFT());
|
entries[i] = resources.getString(R.string.value_integer_feet_recommended, (int) distance.toFT());
|
||||||
} else if (value == recordingGPSAccuracyExcellent) {
|
} else if (value == recordingGPSAccuracyExcellent) {
|
||||||
@@ -589,9 +584,8 @@ public class PreferencesUtils {
|
|||||||
} else {
|
} else {
|
||||||
entries[i] = displayValue;
|
entries[i] = displayValue;
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
default:
|
default -> throw new RuntimeException("Not implemented");
|
||||||
throw new RuntimeException("Not implemented");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -617,32 +611,30 @@ public class PreferencesUtils {
|
|||||||
float value = Float.parseFloat(entryValues[i]);
|
float value = Float.parseFloat(entryValues[i]);
|
||||||
|
|
||||||
switch (unitSystem) {
|
switch (unitSystem) {
|
||||||
case METRIC:
|
case METRIC -> {
|
||||||
if (value == idleSpeedDefault) {
|
if (value == idleSpeedDefault) {
|
||||||
entries[i] = resources.getString(R.string.value_float_kilometer_hour_recommended, value);
|
entries[i] = resources.getString(R.string.value_float_kilometer_hour_recommended, value);
|
||||||
} else {
|
} else {
|
||||||
entries[i] = resources.getString(R.string.value_float_kilometer_hour, value);
|
entries[i] = resources.getString(R.string.value_float_kilometer_hour, value);
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case IMPERIAL:
|
case IMPERIAL -> {
|
||||||
double valueMPH = Speed.ofKMH(value).toMPH();
|
double valueMPH = Speed.ofKMH(value).toMPH();
|
||||||
|
|
||||||
if (value == idleSpeedDefault) {
|
if (value == idleSpeedDefault) {
|
||||||
entries[i] = resources.getString(R.string.value_float_mile_hour_recommended, valueMPH);
|
entries[i] = resources.getString(R.string.value_float_mile_hour_recommended, valueMPH);
|
||||||
} else {
|
} else {
|
||||||
entries[i] = resources.getString(R.string.value_float_mile_hour, valueMPH);
|
entries[i] = resources.getString(R.string.value_float_mile_hour, valueMPH);
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case NAUTICAL_IMPERIAL:
|
case NAUTICAL_IMPERIAL -> {
|
||||||
double valueKnots = Speed.ofKMH(value).toKnots();
|
double valueKnots = Speed.ofKMH(value).toKnots();
|
||||||
if (value == idleSpeedDefault) {
|
if (value == idleSpeedDefault) {
|
||||||
entries[i] = resources.getString(R.string.value_float_knots_recommended, valueKnots);
|
entries[i] = resources.getString(R.string.value_float_knots_recommended, valueKnots);
|
||||||
} else {
|
} else {
|
||||||
entries[i] = resources.getString(R.string.value_float_knots, valueKnots);
|
entries[i] = resources.getString(R.string.value_float_knots, valueKnots);
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
default:
|
default -> throw new RuntimeException("Not implemented");
|
||||||
throw new RuntimeException("Not implemented");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-3
@@ -20,7 +20,6 @@ import de.dennisguse.opentracks.ui.customRecordingLayout.DataField;
|
|||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayout;
|
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayout;
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.SettingsCustomLayoutEditAdapter;
|
import de.dennisguse.opentracks.ui.customRecordingLayout.SettingsCustomLayoutEditAdapter;
|
||||||
import de.dennisguse.opentracks.ui.util.ArrayAdapterFilterDisabled;
|
import de.dennisguse.opentracks.ui.util.ArrayAdapterFilterDisabled;
|
||||||
import de.dennisguse.opentracks.util.StatisticsUtils;
|
|
||||||
|
|
||||||
public class SettingsCustomLayoutEditActivity extends AbstractActivity implements SettingsCustomLayoutEditAdapter.SettingsCustomLayoutItemClickListener {
|
public class SettingsCustomLayoutEditActivity extends AbstractActivity implements SettingsCustomLayoutEditAdapter.SettingsCustomLayoutItemClickListener {
|
||||||
|
|
||||||
@@ -41,7 +40,7 @@ public class SettingsCustomLayoutEditActivity extends AbstractActivity implement
|
|||||||
// Recycler view with visible stats.
|
// Recycler view with visible stats.
|
||||||
RecordingLayout recordingLayout = getIntent().getParcelableExtra(EXTRA_LAYOUT);
|
RecordingLayout recordingLayout = getIntent().getParcelableExtra(EXTRA_LAYOUT);
|
||||||
profile = recordingLayout.getName();
|
profile = recordingLayout.getName();
|
||||||
recordingLayoutFieldsVisible = StatisticsUtils.filterVisible(recordingLayout, true);
|
recordingLayoutFieldsVisible = recordingLayout.toRecordingLayout(true);
|
||||||
adapterFieldsVisible = new SettingsCustomLayoutEditAdapter(this, this, recordingLayoutFieldsVisible);
|
adapterFieldsVisible = new SettingsCustomLayoutEditAdapter(this, this, recordingLayoutFieldsVisible);
|
||||||
|
|
||||||
numColumns = recordingLayout.getColumnsPerRow();
|
numColumns = recordingLayout.getColumnsPerRow();
|
||||||
@@ -90,7 +89,7 @@ public class SettingsCustomLayoutEditActivity extends AbstractActivity implement
|
|||||||
viewBinding.rowsOptions.setText(rowsOptionAdapter.getItem(numColumns - 1).toString(), false);
|
viewBinding.rowsOptions.setText(rowsOptionAdapter.getItem(numColumns - 1).toString(), false);
|
||||||
|
|
||||||
// Recycler view with not visible stats.
|
// Recycler view with not visible stats.
|
||||||
recordingLayoutFieldsHidden = StatisticsUtils.filterVisible(recordingLayout, false);
|
recordingLayoutFieldsHidden = recordingLayout.toRecordingLayout(false);
|
||||||
adapterFieldsHidden = new SettingsCustomLayoutEditAdapter(this, this, recordingLayoutFieldsHidden);
|
adapterFieldsHidden = new SettingsCustomLayoutEditAdapter(this, this, recordingLayoutFieldsHidden);
|
||||||
RecyclerView recyclerViewNotVisible = viewBinding.recyclerViewNotVisible;
|
RecyclerView recyclerViewNotVisible = viewBinding.recyclerViewNotVisible;
|
||||||
recyclerViewNotVisible.setLayoutManager(new LinearLayoutManager(this));
|
recyclerViewNotVisible.setLayoutManager(new LinearLayoutManager(this));
|
||||||
|
|||||||
@@ -96,8 +96,7 @@ public class BluetoothLeAdapter extends BaseAdapter {
|
|||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
if (!(o instanceof Device)) return false;
|
if (!(o instanceof Device device)) return false;
|
||||||
Device device = (Device) o;
|
|
||||||
return address.equals(device.address);
|
return address.equals(device.address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,12 @@ public class RecordingLayout implements Parcelable {
|
|||||||
return new ArrayList<>(dataFields);
|
return new ArrayList<>(dataFields);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RecordingLayout toRecordingLayout(boolean visibility) {
|
||||||
|
RecordingLayout result = new RecordingLayout(this.getName());
|
||||||
|
result.addFields(dataFields.stream().filter(f -> f.isVisible() == visibility).collect(Collectors.toList()));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public void moveField(int from, int to) {
|
public void moveField(int from, int to) {
|
||||||
DataField dataFieldToMove = dataFields.remove(from);
|
DataField dataFieldToMove = dataFields.remove(from);
|
||||||
dataFields.add(to, dataFieldToMove);
|
dataFields.add(to, dataFieldToMove);
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ import androidx.loader.app.LoaderManager;
|
|||||||
import androidx.loader.content.CursorLoader;
|
import androidx.loader.content.CursorLoader;
|
||||||
import androidx.loader.content.Loader;
|
import androidx.loader.content.Loader;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.AbstractActivity;
|
import de.dennisguse.opentracks.AbstractActivity;
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||||
@@ -309,11 +311,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
|||||||
public void setSearch(String searchQuery) {
|
public void setSearch(String searchQuery) {
|
||||||
this.searchQuery = searchQuery;
|
this.searchQuery = searchQuery;
|
||||||
restart();
|
restart();
|
||||||
if (searchQuery != null) {
|
viewBinding.bottomAppBarLayout.bottomAppBarTitle.setText(Objects.requireNonNullElseGet(searchQuery, () -> getString(R.string.menu_markers)));
|
||||||
viewBinding.bottomAppBarLayout.bottomAppBarTitle.setText(searchQuery);
|
|
||||||
} else {
|
|
||||||
viewBinding.bottomAppBarLayout.bottomAppBarTitle.setText(getString(R.string.menu_markers));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void restart() {
|
public void restart() {
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ import android.util.TypedValue;
|
|||||||
|
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
|
||||||
|
|
||||||
public class ThemeUtils {
|
public class ThemeUtils {
|
||||||
|
|
||||||
private ThemeUtils() {
|
private ThemeUtils() {
|
||||||
@@ -42,14 +40,14 @@ public class ThemeUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static int getFontSizeSmallInPx(Context context) {
|
public static int getFontSizeSmallInPx(Context context) {
|
||||||
TypedArray typedArray = context.obtainStyledAttributes(R.style.TextAppearance_MaterialComponents_Body2, new int[]{android.R.attr.textSize});
|
TypedArray typedArray = context.obtainStyledAttributes(com.google.android.material.R.style.TextAppearance_MaterialComponents_Body2, new int[]{android.R.attr.textSize});
|
||||||
int fontSize = typedArray.getDimensionPixelSize(0, 12);
|
int fontSize = typedArray.getDimensionPixelSize(0, 12);
|
||||||
typedArray.recycle();
|
typedArray.recycle();
|
||||||
return fontSize;
|
return fontSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getFontSizeMediumInPx(Context context) {
|
public static int getFontSizeMediumInPx(Context context) {
|
||||||
TypedArray typedArray = context.obtainStyledAttributes(R.style.TextAppearance_MaterialComponents_Body1, new int[]{android.R.attr.textSize});
|
TypedArray typedArray = context.obtainStyledAttributes(com.google.android.material.R.style.TextAppearance_MaterialComponents_Body1, new int[]{android.R.attr.textSize});
|
||||||
int fontSize = typedArray.getDimensionPixelSize(0, 15);
|
int fontSize = typedArray.getDimensionPixelSize(0, 15);
|
||||||
typedArray.recycle();
|
typedArray.recycle();
|
||||||
return fontSize;
|
return fontSize;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
|
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||||
import de.dennisguse.opentracks.io.file.exporter.ExportService;
|
import de.dennisguse.opentracks.io.file.exporter.ExportService;
|
||||||
@@ -56,7 +57,7 @@ public class ExportUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean exportTrack(Context context, TrackFileFormat trackFileFormat, DocumentFile directory, Track track) {
|
public static boolean exportTrack(Context context, TrackFileFormat trackFileFormat, DocumentFile directory, Track track) {
|
||||||
TrackExporter trackExporter = trackFileFormat.createTrackExporter(context);
|
TrackExporter trackExporter = trackFileFormat.createTrackExporter(context, new ContentProviderUtils(context));
|
||||||
|
|
||||||
Uri exportDocumentFileUri = getExportDocumentFileUri(context, track, trackFileFormat, directory);
|
Uri exportDocumentFileUri = getExportDocumentFileUri(context, track, trackFileFormat, directory);
|
||||||
if (exportDocumentFileUri == null) {
|
if (exportDocumentFileUri == null) {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import java.io.FileInputStream;
|
|||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.nio.channels.FileChannel;
|
import java.nio.channels.FileChannel;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.BuildConfig;
|
import de.dennisguse.opentracks.BuildConfig;
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
@@ -162,34 +163,8 @@ public class FileUtils {
|
|||||||
* @param character the character
|
* @param character the character
|
||||||
*/
|
*/
|
||||||
private static boolean isSpecialFat32(char character) {
|
private static boolean isSpecialFat32(char character) {
|
||||||
switch (character) {
|
return List.of('$', '%', '\'', '-', '_', '@', '~', '`', '!', '(', ')', '{', '}', '^', '#', '&', '+', ',', ';', '=', '[', ']', ' ')
|
||||||
case '$':
|
.contains(character);
|
||||||
case '%':
|
|
||||||
case '\'':
|
|
||||||
case '-':
|
|
||||||
case '_':
|
|
||||||
case '@':
|
|
||||||
case '~':
|
|
||||||
case '`':
|
|
||||||
case '!':
|
|
||||||
case '(':
|
|
||||||
case ')':
|
|
||||||
case '{':
|
|
||||||
case '}':
|
|
||||||
case '^':
|
|
||||||
case '#':
|
|
||||||
case '&':
|
|
||||||
case '+':
|
|
||||||
case ',':
|
|
||||||
case ';':
|
|
||||||
case '=':
|
|
||||||
case '[':
|
|
||||||
case ']':
|
|
||||||
case ' ':
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,11 +4,8 @@ import android.content.Context;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||||
import de.dennisguse.opentracks.ui.customRecordingLayout.RecordingLayout;
|
|
||||||
|
|
||||||
public class StatisticsUtils {
|
public class StatisticsUtils {
|
||||||
|
|
||||||
@@ -24,11 +21,4 @@ public class StatisticsUtils {
|
|||||||
return context.getString(R.string.stats_empty_value_integer);
|
return context.getString(R.string.stats_empty_value_integer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated //Add to Layout?
|
|
||||||
public static RecordingLayout filterVisible(RecordingLayout recordingLayout, boolean visible) {
|
|
||||||
RecordingLayout result = new RecordingLayout(recordingLayout.getName());
|
|
||||||
result.addFields(recordingLayout.getFields().stream().filter(f -> f.isVisible() == visible).collect(Collectors.toList()));
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,11 +40,9 @@ public class TrackIconUtils {
|
|||||||
it -> it.getId().equals(activityTypeId)
|
it -> it.getId().equals(activityTypeId)
|
||||||
).findFirst();
|
).findFirst();
|
||||||
|
|
||||||
if (found.isEmpty()) {
|
return found.map(ActivityType::getIconId)
|
||||||
return ActivityType.UNKNOWN.getIconId();
|
.orElseGet(ActivityType.UNKNOWN::getIconId);
|
||||||
}
|
|
||||||
|
|
||||||
return found.get().getIconId();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int getIconActivityType(String activityTypeId) {
|
public static int getIconActivityType(String activityTypeId) {
|
||||||
@@ -52,11 +50,9 @@ public class TrackIconUtils {
|
|||||||
it -> it.getId().equals(activityTypeId)
|
it -> it.getId().equals(activityTypeId)
|
||||||
).findFirst();
|
).findFirst();
|
||||||
|
|
||||||
if (found.isEmpty()) {
|
return found.map(ActivityType::getFirstLocalizedStringId)
|
||||||
return ActivityType.UNKNOWN.getFirstLocalizedStringId();
|
.orElseGet(ActivityType.UNKNOWN::getFirstLocalizedStringId);
|
||||||
}
|
|
||||||
|
|
||||||
return found.get().getFirstLocalizedStringId();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -96,10 +92,8 @@ public class TrackIconUtils {
|
|||||||
)
|
)
|
||||||
.findFirst();
|
.findFirst();
|
||||||
|
|
||||||
if (selected.isEmpty()) {
|
return selected.map(ActivityType::isShowSpeedPreferred)
|
||||||
return false;
|
.orElse(false);
|
||||||
}
|
|
||||||
|
|
||||||
return selected.get().isShowSpeedPreferred();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,15 +65,20 @@ public abstract class GenericStatisticsViewHolder extends StatisticViewHolder<St
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class SpeedOrPace extends GenericStatisticsViewHolder {
|
public abstract static class SpeedOrPace extends GenericStatisticsViewHolder {
|
||||||
|
|
||||||
|
private final boolean reportSpeed;
|
||||||
|
|
||||||
|
public SpeedOrPace(boolean reportSpeed) {
|
||||||
|
this.reportSpeed = reportSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
||||||
// TODO Pace wont work for now
|
|
||||||
boolean reportSpeed = true;
|
|
||||||
SpeedFormatter localSpeedFormatter = SpeedFormatter.Builder()
|
SpeedFormatter localSpeedFormatter = SpeedFormatter.Builder()
|
||||||
.setUnit(unitSystem)
|
.setUnit(unitSystem)
|
||||||
.setReportSpeedOrPace(reportSpeed).build(getContext());
|
.setReportSpeedOrPace(reportSpeed)
|
||||||
|
.build(getContext());
|
||||||
|
|
||||||
Pair<String, String> valueAndUnit;
|
Pair<String, String> valueAndUnit;
|
||||||
|
|
||||||
@@ -85,7 +90,9 @@ public abstract class GenericStatisticsViewHolder extends StatisticViewHolder<St
|
|||||||
} else {
|
} else {
|
||||||
Speed speed = latestTrackPoint != null && latestTrackPoint.hasSpeed() ? latestTrackPoint.getSpeed() : null;
|
Speed speed = latestTrackPoint != null && latestTrackPoint.hasSpeed() ? latestTrackPoint.getSpeed() : null;
|
||||||
valueAndUnit = localSpeedFormatter.getSpeedParts(speed);
|
valueAndUnit = localSpeedFormatter.getSpeedParts(speed);
|
||||||
getBinding().statsDescriptionMain.setText(getContext().getString(R.string.description_speed_source_gps));
|
|
||||||
|
String title = reportSpeed ? getContext().getString(R.string.stats_speed) : getContext().getString(R.string.stats_pace);
|
||||||
|
getBinding().statsDescriptionMain.setText(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
getBinding().statsValue.setText(valueAndUnit.first);
|
getBinding().statsValue.setText(valueAndUnit.first);
|
||||||
@@ -93,6 +100,20 @@ public abstract class GenericStatisticsViewHolder extends StatisticViewHolder<St
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class SpeedVH extends SpeedOrPace {
|
||||||
|
|
||||||
|
public SpeedVH() {
|
||||||
|
super(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PaceVH extends SpeedOrPace {
|
||||||
|
|
||||||
|
public PaceVH() {
|
||||||
|
super(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class AverageMovingSpeed extends GenericStatisticsViewHolder {
|
public static class AverageMovingSpeed extends GenericStatisticsViewHolder {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ public class Mapping {
|
|||||||
|
|
||||||
m.put(context.getString(R.string.stats_custom_layout_distance_key), GenericStatisticsViewHolder.Distance::new);
|
m.put(context.getString(R.string.stats_custom_layout_distance_key), GenericStatisticsViewHolder.Distance::new);
|
||||||
|
|
||||||
m.put(context.getString(R.string.stats_custom_layout_speed_key), GenericStatisticsViewHolder.SpeedOrPace::new);
|
m.put(context.getString(R.string.stats_custom_layout_speed_key), GenericStatisticsViewHolder.SpeedVH::new);
|
||||||
m.put(context.getString(R.string.stats_custom_layout_pace_key), GenericStatisticsViewHolder.SpeedOrPace::new);
|
m.put(context.getString(R.string.stats_custom_layout_pace_key), GenericStatisticsViewHolder.PaceVH::new);
|
||||||
m.put(context.getString(R.string.stats_custom_layout_average_moving_speed_key), GenericStatisticsViewHolder.AverageMovingSpeed::new);
|
m.put(context.getString(R.string.stats_custom_layout_average_moving_speed_key), GenericStatisticsViewHolder.AverageMovingSpeed::new);
|
||||||
m.put(context.getString(R.string.stats_custom_layout_average_speed_key), GenericStatisticsViewHolder.AverageSpeed::new);
|
m.put(context.getString(R.string.stats_custom_layout_average_speed_key), GenericStatisticsViewHolder.AverageSpeed::new);
|
||||||
m.put(context.getString(R.string.stats_custom_layout_max_speed_key), GenericStatisticsViewHolder.MaxSpeed::new);
|
m.put(context.getString(R.string.stats_custom_layout_max_speed_key), GenericStatisticsViewHolder.MaxSpeed::new);
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
unqualifiedResLocale=en-US
|
||||||
@@ -612,4 +612,5 @@ Se o GPS informa sobre datos pouco precisos (ex.: localización, velocidade, ele
|
|||||||
<string name="help_data_google_play_services_content">Non</string>
|
<string name="help_data_google_play_services_content">Non</string>
|
||||||
<string name="settings_sensor_heart_rate_max">Zonas: Pulso máximo (lpm)</string>
|
<string name="settings_sensor_heart_rate_max">Zonas: Pulso máximo (lpm)</string>
|
||||||
<string name="settings_api_summary">Control remoto e API de estatísticas para outras apps</string>
|
<string name="settings_api_summary">Control remoto e API de estatísticas para outras apps</string>
|
||||||
|
<string name="voice_on_device_speaker_title">Usar o altofalante do dispositivo</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
Copyright 2010 Google Inc.
|
Copyright 2010 Google Inc.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -12,8 +13,7 @@ distributed under the License is distributed on an "AS IS" BASIS,
|
|||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
--><resources>
|
||||||
<resources>
|
|
||||||
<string name="about_preference_title">關於 OpenTracks</string>
|
<string name="about_preference_title">關於 OpenTracks</string>
|
||||||
<string name="about_description"><i>OpenTracks</i> 是一個完全尊重您的隱私的運動蹤跡應用程式。</string>
|
<string name="about_description"><i>OpenTracks</i> 是一個完全尊重您的隱私的運動蹤跡應用程式。</string>
|
||||||
<string name="about_url">專案網頁:<a href="%1$s">%1$s</a></string>
|
<string name="about_url">專案網頁:<a href="%1$s">%1$s</a></string>
|
||||||
@@ -23,16 +23,13 @@ limitations under the License.
|
|||||||
<string name="about_third_party_data">第三方資料</string>
|
<string name="about_third_party_data">第三方資料</string>
|
||||||
<string name="about_libraries">函式庫</string>
|
<string name="about_libraries">函式庫</string>
|
||||||
<!-- Help -->
|
<!-- Help -->
|
||||||
<string name="help_feature_title">OpenTracks 擁有以下特點:</string>
|
<string name="help_feature_title">OpenTracks 擁有以下功能:</string>
|
||||||
<string name="help_feature_content">
|
<string name="help_feature_content">* 建立 GPS 蹤跡
|
||||||
* 建立 GPS 蹤跡\n
|
\n * 使用一個心率感測器 (僅限藍牙 LE)
|
||||||
* 使用一個心率感測器 (僅限藍牙 LE)\n
|
\n * 語音播報 (依時間或距離)
|
||||||
* 語音通知 (依時間或距離)\n
|
\n * 建立標記 (包含圖片)
|
||||||
* 建立標記 (包含圖片)\n
|
\n * 完全離線工作
|
||||||
* 完全離線工作\n
|
\n * 在地圖上顯示蹤跡 (見下文)</string>
|
||||||
* 在地圖上顯示蹤跡 (見下文)
|
|
||||||
</string>
|
|
||||||
|
|
||||||
<string name="help_track_map_title">如何在地圖顯示一個蹤跡?</string>
|
<string name="help_track_map_title">如何在地圖顯示一個蹤跡?</string>
|
||||||
<string name="help_track_map_content">
|
<string name="help_track_map_content">
|
||||||
使用小的地圖圖示。\n
|
使用小的地圖圖示。\n
|
||||||
@@ -47,33 +44,27 @@ limitations under the License.
|
|||||||
這些 APP 需要支援 KMZ 檔案格式 (只有位置與時間戳記)。
|
這些 APP 需要支援 KMZ 檔案格式 (只有位置與時間戳記)。
|
||||||
事實上,APP 需要支援 <i>ACTION_VIEW</i> 與 MIME <i>application/vnd.google-earth.kmz</i>。
|
事實上,APP 需要支援 <i>ACTION_VIEW</i> 與 MIME <i>application/vnd.google-earth.kmz</i>。
|
||||||
</string>
|
</string>
|
||||||
|
|
||||||
<string name="help_storage_pictures_title">OpenTracks 如何存儲作為標記拍攝的照片?</string>
|
<string name="help_storage_pictures_title">OpenTracks 如何存儲作為標記拍攝的照片?</string>
|
||||||
<string name="help_storage_pictures_content">這些儲存在應用程序內部儲存區中 (即,只能在 OpenTracks 中存取)。</string>
|
<string name="help_storage_pictures_content">這些儲存在應用程序內部儲存區中 (即,只能在 OpenTracks 中存取)。</string>
|
||||||
|
|
||||||
<string name="help_track_export_title">如何匯出/分享 紀錄的蹤跡?</string>
|
<string name="help_track_export_title">如何匯出/分享 紀錄的蹤跡?</string>
|
||||||
<string name="help_track_export_content">
|
<string name="help_track_export_content">
|
||||||
可以從設定匯出蹤跡為 KMZ、KML 或 GPX。
|
可以從設定匯出蹤跡為 KMZ、KML 或 GPX。
|
||||||
KMZ/KML 全部資料;GPX 只有位置。
|
KMZ/KML 全部資料;GPX 只有位置。
|
||||||
通過<i>分享圖示</i>以分享蹤跡為 KMZ。
|
通過<i>分享圖示</i>以分享蹤跡為 KMZ。
|
||||||
</string>
|
</string>
|
||||||
|
|
||||||
<string name="help_data_google_play_services_title">OpenTracks 是否需要/使用 Google Play 服務?</string>
|
<string name="help_data_google_play_services_title">OpenTracks 是否需要/使用 Google Play 服務?</string>
|
||||||
<string name="help_data_google_play_services_content">否</string>
|
<string name="help_data_google_play_services_content">否</string>
|
||||||
|
|
||||||
<string name="help_recording_title">OpenTracks 是如何記錄資料的?</string>
|
<string name="help_recording_title">OpenTracks 是如何記錄資料的?</string>
|
||||||
<string name="help_recording_content">
|
<string name="help_recording_content">
|
||||||
OpenTracks 使用裝置的 GPS 感測器以連續紀錄位置。
|
OpenTracks 使用裝置的 GPS 感測器以連續紀錄位置。
|
||||||
如果 GPS 裝置回報的資料不准確 (例如,位置、速度、海拔),OpenTracks 對此無能為力。
|
如果 GPS 裝置回報的資料不准確 (例如,位置、速度、海拔),OpenTracks 對此無能為力。
|
||||||
</string>
|
</string>
|
||||||
|
|
||||||
<string name="help_data_storage_title">OpenTracks 如何儲存感測器資料?</string>
|
<string name="help_data_storage_title">OpenTracks 如何儲存感測器資料?</string>
|
||||||
<string name="help_data_storage_content">
|
<string name="help_data_storage_content">
|
||||||
感測器資料儲存在每個記錄的位置。
|
感測器資料儲存在每個記錄的位置。
|
||||||
就心率感測器而言,這意味著只有<i>一個</i>心率測量值被依照位置儲存。
|
就心率感測器而言,這意味著只有<i>一個</i>心率測量值被依照位置儲存。
|
||||||
如果您是站著不動或在室內,沒有感測器資料會被記錄 (但它會被顯示)。
|
如果您是站著不動或在室內,沒有感測器資料會被記錄 (但它會被顯示)。
|
||||||
</string>
|
</string>
|
||||||
|
|
||||||
<string name="help_gps_troubleshooting_title">OpenTracks 沒有紀錄位置?</string>
|
<string name="help_gps_troubleshooting_title">OpenTracks 沒有紀錄位置?</string>
|
||||||
<string name="help_gps_troubleshooting_content">
|
<string name="help_gps_troubleshooting_content">
|
||||||
您能做什麼:\n
|
您能做什麼:\n
|
||||||
@@ -84,7 +75,6 @@ limitations under the License.
|
|||||||
\n
|
\n
|
||||||
如果一切都沒有幫助,請回報 bug。
|
如果一切都沒有幫助,請回報 bug。
|
||||||
</string>
|
</string>
|
||||||
|
|
||||||
<string name="help_support_title">我如何支持 OpenTracks?</string>
|
<string name="help_support_title">我如何支持 OpenTracks?</string>
|
||||||
<string name="help_support_content">
|
<string name="help_support_content">
|
||||||
您可以:\n
|
您可以:\n
|
||||||
@@ -96,11 +86,9 @@ limitations under the License.
|
|||||||
f) 贊助,以及\n
|
f) 贊助,以及\n
|
||||||
g) 做任何您認為對 OpenTracks 有幫助的事情。
|
g) 做任何您認為對 OpenTracks 有幫助的事情。
|
||||||
</string>
|
</string>
|
||||||
|
|
||||||
<string name="open_track_as_KML">開啟為 KML</string>
|
<string name="open_track_as_KML">開啟為 KML</string>
|
||||||
<string name="open_track_as_GPX">開啟為 GPX</string>
|
<string name="open_track_as_GPX">開啟為 GPX</string>
|
||||||
<string name="open_track_as_trackfileformat">開啟為 %1$s</string>
|
<string name="open_track_as_trackfileformat">開啟為 %1$s</string>
|
||||||
|
|
||||||
<!-- Activity Type -->
|
<!-- Activity Type -->
|
||||||
<string name="activity_type_airplane">飛機</string>
|
<string name="activity_type_airplane">飛機</string>
|
||||||
<string name="activity_type_atv">越野車</string>
|
<string name="activity_type_atv">越野車</string>
|
||||||
@@ -335,34 +323,23 @@ limitations under the License.
|
|||||||
<string name="sensor_state_power_avg">平均功率</string>
|
<string name="sensor_state_power_avg">平均功率</string>
|
||||||
<string name="sensor_could_not_scan">無法掃描藍牙裝置 (錯誤 %1$i).</string>
|
<string name="sensor_could_not_scan">無法掃描藍牙裝置 (錯誤 %1$i).</string>
|
||||||
<string name="bluetooth_disabled">請啟用藍芽。</string>
|
<string name="bluetooth_disabled">請啟用藍芽。</string>
|
||||||
|
|
||||||
<string name="settings_open_tracks_title">開啟蹤跡</string>
|
<string name="settings_open_tracks_title">開啟蹤跡</string>
|
||||||
<string name="settings_open_tracks_summary">所有關於 OpenTracks 專案</string>
|
<string name="settings_open_tracks_summary">所有關於 OpenTracks 專案</string>
|
||||||
|
|
||||||
<string name="settings_defaults_title">預設單位與活動</string>
|
<string name="settings_defaults_title">預設單位與活動</string>
|
||||||
<string name="settings_defaults_summary">首選單位、預設活動、蹤跡預設名稱</string>
|
<string name="settings_defaults_summary">首選單位、預設活動、蹤跡預設名稱</string>
|
||||||
|
|
||||||
<string name="settings_ui_title">使用者介面</string>
|
<string name="settings_ui_title">使用者介面</string>
|
||||||
<string name="settings_ui_summary">外觀、紀錄布局、主題</string>
|
<string name="settings_ui_summary">外觀、紀錄布局、主題</string>
|
||||||
|
|
||||||
<string name="settings_gps_title">GPS</string>
|
<string name="settings_gps_title">GPS</string>
|
||||||
<string name="settings_gps_summary">時間間隔、距離間隔、最大距離、精度</string>
|
<string name="settings_gps_summary">時間間隔、距離間隔、最大距離、精度</string>
|
||||||
|
|
||||||
<string name="settings_sensors_title">藍芽感測器</string>
|
<string name="settings_sensors_title">藍芽感測器</string>
|
||||||
<string name="settings_sensors_summary">自行車感測器、輪周、跑步感測器</string>
|
<string name="settings_sensors_summary">自行車感測器、輪周、跑步感測器</string>
|
||||||
|
<string name="settings_announcements_title">語音播報</string>
|
||||||
<string name="settings_announcements_title">語音通知</string>
|
|
||||||
<string name="settings_announcements_summary">時間、距離、語速</string>
|
<string name="settings_announcements_summary">時間、距離、語速</string>
|
||||||
|
|
||||||
<string name="settings_import_export_title">匯入/匯出</string>
|
<string name="settings_import_export_title">匯入/匯出</string>
|
||||||
<string name="settings_import_export_summary">匯入、匯出、自動匯出</string>
|
<string name="settings_import_export_summary">匯入、匯出、自動匯出</string>
|
||||||
|
|
||||||
<string name="settings_units_category">單位</string>
|
<string name="settings_units_category">單位</string>
|
||||||
<string name="settings_activity_defaults_category">活動的預設值</string>
|
<string name="settings_activity_defaults_category">活動的預設值</string>
|
||||||
|
|
||||||
<string name="settings_reset_summary">重置設定為預設值</string>
|
<string name="settings_reset_summary">重置設定為預設值</string>
|
||||||
<string name="settings_layout_reset_key" translatable="false">設定布局重置</string>
|
|
||||||
|
|
||||||
<!-- Settings Advanced -->
|
<!-- Settings Advanced -->
|
||||||
<string name="settings_advanced">進階</string>
|
<string name="settings_advanced">進階</string>
|
||||||
<string name="settings_default_trackfileformat">匯出/分享檔案格式</string>
|
<string name="settings_default_trackfileformat">匯出/分享檔案格式</string>
|
||||||
@@ -396,14 +373,12 @@ limitations under the License.
|
|||||||
<string name="settings_night_mode_option_system">系統</string>
|
<string name="settings_night_mode_option_system">系統</string>
|
||||||
<string name="settings_night_mode_option_day">日</string>
|
<string name="settings_night_mode_option_day">日</string>
|
||||||
<string name="settings_night_mode_option_night">夜</string>
|
<string name="settings_night_mode_option_night">夜</string>
|
||||||
|
|
||||||
<string name="settings_announcements_totaltime_frequency">時間間隔</string>
|
<string name="settings_announcements_totaltime_frequency">時間間隔</string>
|
||||||
<string name="settings_announcements_distance_frequency">距離間隔</string>
|
<string name="settings_announcements_distance_frequency">距離間隔</string>
|
||||||
<string name="settings_announcements_average_heart_rate">平均心率</string>
|
<string name="settings_announcements_average_heart_rate">平均心率</string>
|
||||||
<string name="settings_announcements_lap_heart_rate">單圈心率</string>
|
<string name="settings_announcements_lap_heart_rate">單圈心率</string>
|
||||||
<string name="settings_announcements_average_speed_pace">平均速度/步速</string>
|
<string name="settings_announcements_average_speed_pace">平均速度/步速</string>
|
||||||
<string name="settings_announcements_lap_speed_pace">單圈速度/步速</string>
|
<string name="settings_announcements_lap_speed_pace">單圈速度/步速</string>
|
||||||
|
|
||||||
<!-- Custom Layout -->
|
<!-- Custom Layout -->
|
||||||
<string name="custom_layout_list_title">布局</string>
|
<string name="custom_layout_list_title">布局</string>
|
||||||
<string name="custom_layout_list_edit_name">輸入要新增的佈局名稱</string>
|
<string name="custom_layout_list_edit_name">輸入要新增的佈局名稱</string>
|
||||||
@@ -425,24 +400,19 @@ limitations under the License.
|
|||||||
<string name="settings_generic_sensor">通用</string>
|
<string name="settings_generic_sensor">通用</string>
|
||||||
<string name="settings_cycling_sensor">騎自行車</string>
|
<string name="settings_cycling_sensor">騎自行車</string>
|
||||||
<string name="settings_running_sensor">跑步</string>
|
<string name="settings_running_sensor">跑步</string>
|
||||||
|
|
||||||
<string name="settings_sensor_bluetooth">藍牙</string>
|
<string name="settings_sensor_bluetooth">藍牙</string>
|
||||||
|
|
||||||
<string name="settings_sensor_cycling_distance_speed">距離與速度</string>
|
<string name="settings_sensor_cycling_distance_speed">距離與速度</string>
|
||||||
<string name="settings_sensor_cycling_cadence">踏頻</string>
|
<string name="settings_sensor_cycling_cadence">踏頻</string>
|
||||||
<string name="settings_sensor_running_speed_and_cadence">距離、速度與踏頻</string>
|
<string name="settings_sensor_running_speed_and_cadence">距離、速度與踏頻</string>
|
||||||
<string name="settings_sensor_wheel_circumference">輪周 (公釐)</string>
|
<string name="settings_sensor_wheel_circumference">輪周 (公釐)</string>
|
||||||
<string name="settings_sensor_bluetooth_service_filter_enabled">僅顯示宣告所需服務的藍芽裝置</string>
|
<string name="settings_sensor_bluetooth_service_filter_enabled">僅顯示宣告所需服務的藍芽裝置</string>
|
||||||
|
|
||||||
<string name="settings_sensor_heart_rate_max">區間:最大心率 (bpm)</string>
|
<string name="settings_sensor_heart_rate_max">區間:最大心率 (bpm)</string>
|
||||||
|
|
||||||
<!-- Settings Stats -->
|
<!-- Settings Stats -->
|
||||||
<string name="settings_stats_rate_title">偏好速率</string>
|
<string name="settings_stats_rate_title">偏好速率</string>
|
||||||
<string name="settings_stats_units_imperial">英制 (英里、英尺)</string>
|
<string name="settings_stats_units_imperial">英制 (英里、英尺)</string>
|
||||||
<string name="settings_stats_units_metric">公制 (公里、公尺)</string>
|
<string name="settings_stats_units_metric">公制 (公里、公尺)</string>
|
||||||
<string name="settings_stats_units_nautical">航海 (海里、英尺)</string>
|
<string name="settings_stats_units_nautical">航海 (海里、英尺)</string>
|
||||||
<string name="settings_stats_units_title">慣用單位</string>
|
<string name="settings_stats_units_title">慣用單位</string>
|
||||||
|
|
||||||
<!-- Settings public api -->
|
<!-- Settings public api -->
|
||||||
<string name="settings_api_title">公開 API</string>
|
<string name="settings_api_title">公開 API</string>
|
||||||
<string name="settings_api_summary">其他 APP 的遠端控制與統計資料 API</string>
|
<string name="settings_api_summary">其他 APP 的遠端控制與統計資料 API</string>
|
||||||
@@ -454,11 +424,9 @@ limitations under the License.
|
|||||||
<string name="settings_public_api_dashboard_enabled_title">自動資料傳輸</string>
|
<string name="settings_public_api_dashboard_enabled_title">自動資料傳輸</string>
|
||||||
<string name="settings_public_api_dashboard_enabled_summary_on">開始紀錄的應用程式也可以存取紀錄的資料。</string>
|
<string name="settings_public_api_dashboard_enabled_summary_on">開始紀錄的應用程式也可以存取紀錄的資料。</string>
|
||||||
<string name="settings_public_api_dashboard_enabled_summary_off">記錄的資料將不會自動與其他應用程式共用。</string>
|
<string name="settings_public_api_dashboard_enabled_summary_off">記錄的資料將不會自動與其他應用程式共用。</string>
|
||||||
|
|
||||||
<!-- Settings import/export -->
|
<!-- Settings import/export -->
|
||||||
<string name="settings_import_section">匯入</string>
|
<string name="settings_import_section">匯入</string>
|
||||||
<string name="settings_export_section">匯出</string>
|
<string name="settings_export_section">匯出</string>
|
||||||
|
|
||||||
<string name="settings_prevent_reimport_tracks_title">防止重新匯入蹤跡</string>
|
<string name="settings_prevent_reimport_tracks_title">防止重新匯入蹤跡</string>
|
||||||
<!-- Share Track -->
|
<!-- Share Track -->
|
||||||
<string name="share_track_share_file_body">你應該會喜歡這個蹤跡。\n\n請使用 Google 地球或其他地圖繪製軟體查看蹤跡。\n\n%1$s</string>
|
<string name="share_track_share_file_body">你應該會喜歡這個蹤跡。\n\n請使用 Google 地球或其他地圖繪製軟體查看蹤跡。\n\n%1$s</string>
|
||||||
@@ -513,7 +481,7 @@ limitations under the License.
|
|||||||
<string name="aggregated_stats_empty_message">紀錄您的第一個蹤跡以檢視綜合統計資料</string>
|
<string name="aggregated_stats_empty_message">紀錄您的第一個蹤跡以檢視綜合統計資料</string>
|
||||||
<string name="aggregated_stats_filter_no_results">沒有指定篩選的活動</string>
|
<string name="aggregated_stats_filter_no_results">沒有指定篩選的活動</string>
|
||||||
<!-- Interval List -->
|
<!-- Interval List -->
|
||||||
<string name="interval_list_empty_message">還有有任何間格</string>
|
<string name="interval_list_empty_message">還沒有任何間格</string>
|
||||||
<!-- Unit -->
|
<!-- Unit -->
|
||||||
<string name="unit_feet">英尺</string>
|
<string name="unit_feet">英尺</string>
|
||||||
<string name="unit_kilometer">公里</string>
|
<string name="unit_kilometer">公里</string>
|
||||||
@@ -530,7 +498,6 @@ limitations under the License.
|
|||||||
<string name="location_coordinate">%1$s°</string>
|
<string name="location_coordinate">%1$s°</string>
|
||||||
<string name="location_latitude_longitude">%1$s, %2$s</string>
|
<string name="location_latitude_longitude">%1$s, %2$s</string>
|
||||||
<!-- Value -->
|
<!-- Value -->
|
||||||
|
|
||||||
<string name="value_integer_feet_excellent_gps">%1$d 英尺 (GPS 訊號佳)</string>
|
<string name="value_integer_feet_excellent_gps">%1$d 英尺 (GPS 訊號佳)</string>
|
||||||
<string name="value_integer_feet_recommended">%1$d 英尺 (建議值)</string>
|
<string name="value_integer_feet_recommended">%1$d 英尺 (建議值)</string>
|
||||||
<string name="value_integer_kilometer_hour">%1$d 公里/小時</string>
|
<string name="value_integer_kilometer_hour">%1$d 公里/小時</string>
|
||||||
@@ -652,12 +619,9 @@ limitations under the License.
|
|||||||
<!-- Share image -->
|
<!-- Share image -->
|
||||||
<string name="share_image_subject">我想與你分享一張圖片</string>
|
<string name="share_image_subject">我想與你分享一張圖片</string>
|
||||||
<string name="share_image_body">我認為你可能會對這張圖片感興趣。</string>
|
<string name="share_image_body">我認為你可能會對這張圖片感興趣。</string>
|
||||||
|
|
||||||
<string name="hold_to_stop">按住以停止</string>
|
<string name="hold_to_stop">按住以停止</string>
|
||||||
|
|
||||||
<string name="instant_export_enabled_summary">在紀錄完成後匯出蹤跡至儲存區</string>
|
<string name="instant_export_enabled_summary">在紀錄完成後匯出蹤跡至儲存區</string>
|
||||||
<string name="instant_export_enabled_title">即時運動後匯出</string>
|
<string name="instant_export_enabled_title">即時運動後匯出</string>
|
||||||
|
|
||||||
<string name="settings_default_export_uri_title">蹤跡匯出目錄</string>
|
<string name="settings_default_export_uri_title">蹤跡匯出目錄</string>
|
||||||
<!-- Fields -->
|
<!-- Fields -->
|
||||||
<string name="field_set_primary">主要的</string>
|
<string name="field_set_primary">主要的</string>
|
||||||
@@ -667,12 +631,11 @@ limitations under the License.
|
|||||||
<string name="error_crash_title">%1$s 已崩潰</string>
|
<string name="error_crash_title">%1$s 已崩潰</string>
|
||||||
<string name="share_crash_report">分享</string>
|
<string name="share_crash_report">分享</string>
|
||||||
<string name="report_bug">回報 bug</string>
|
<string name="report_bug">回報 bug</string>
|
||||||
|
|
||||||
<string name="select_show_on_map_behavior">選擇 \"在地圖上顯示\" 行為</string>
|
<string name="select_show_on_map_behavior">選擇 \"在地圖上顯示\" 行為</string>
|
||||||
<string name="show_on_map_format_ask">詢問</string>
|
<string name="show_on_map_format_ask">詢問</string>
|
||||||
<string name="show_on_dashboard">OpenTracks 儀表板 API</string>
|
<string name="show_on_dashboard">OpenTracks 儀表板 API</string>
|
||||||
<string name="show_on_dashboard_not_installed">沒有安裝支援 OpenTracks 儀表板 API 的應用程式。</string>
|
<string name="show_on_dashboard_not_installed">沒有安裝支援 OpenTracks 儀表板 API 的應用程式。</string>
|
||||||
<string name="always">總是</string>
|
<string name="always">總是</string>
|
||||||
|
|
||||||
<string name="introduction_osm_dashboard">OpenTracks 本身不提供地圖。請安裝 OSMDashboard 以在地圖上檢視您的紀錄。</string>
|
<string name="introduction_osm_dashboard">OpenTracks 本身不提供地圖。請安裝 OSMDashboard 以在地圖上檢視您的紀錄。</string>
|
||||||
|
<string name="voice_on_device_speaker_title">使用裝置的揚聲器</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -62,6 +62,8 @@
|
|||||||
<string name="settings_export" translatable="false">settingsExport</string>
|
<string name="settings_export" translatable="false">settingsExport</string>
|
||||||
|
|
||||||
<!-- Stats layout customization default layout -->
|
<!-- Stats layout customization default layout -->
|
||||||
|
<string name="settings_layout_reset_key" translatable="false">settingsLayoutReset</string>
|
||||||
|
|
||||||
<string name="default_activity_key" translatable="false">defaultActivity</string>
|
<string name="default_activity_key" translatable="false">defaultActivity</string>
|
||||||
<string name="default_activity_default" translatable="false">@string/activity_type_unknown</string>
|
<string name="default_activity_default" translatable="false">@string/activity_type_unknown</string>
|
||||||
|
|
||||||
|
|||||||
@@ -361,7 +361,6 @@ limitations under the License.
|
|||||||
<string name="settings_activity_defaults_category">Activity\'s Defaults</string>
|
<string name="settings_activity_defaults_category">Activity\'s Defaults</string>
|
||||||
|
|
||||||
<string name="settings_reset_summary">Reset Settings to Default Values</string>
|
<string name="settings_reset_summary">Reset Settings to Default Values</string>
|
||||||
<string name="settings_layout_reset_key" translatable="false">settingsLayoutReset</string>
|
|
||||||
|
|
||||||
<!-- Settings Advanced -->
|
<!-- Settings Advanced -->
|
||||||
<string name="settings_advanced">Advanced</string>
|
<string name="settings_advanced">Advanced</string>
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<locale android:name="ar"/>
|
|
||||||
<locale android:name="bg"/>
|
|
||||||
<locale android:name="ca"/>
|
|
||||||
<locale android:name="cs"/>
|
|
||||||
<locale android:name="da"/>
|
|
||||||
<locale android:name="de"/>
|
|
||||||
<locale android:name="el"/>
|
|
||||||
<locale android:name="en"/>
|
|
||||||
<locale android:name="es"/>
|
|
||||||
<locale android:name="et"/>
|
|
||||||
<locale android:name="eu"/>
|
|
||||||
<locale android:name="fa"/>
|
|
||||||
<locale android:name="fi"/>
|
|
||||||
<locale android:name="fr"/>
|
|
||||||
<locale android:name="ga"/>
|
|
||||||
<locale android:name="gl"/>
|
|
||||||
<locale android:name="hi"/>
|
|
||||||
<locale android:name="hr"/>
|
|
||||||
<locale android:name="hu"/>
|
|
||||||
<locale android:name="in"/>
|
|
||||||
<locale android:name="it"/>
|
|
||||||
<locale android:name="iw"/>
|
|
||||||
<locale android:name="ja"/>
|
|
||||||
<locale android:name="ko"/>
|
|
||||||
<locale android:name="lt"/>
|
|
||||||
<locale android:name="lv"/>
|
|
||||||
<locale android:name="mo"/>
|
|
||||||
<locale android:name="ms"/>
|
|
||||||
<locale android:name="nb"/>
|
|
||||||
<locale android:name="nl"/>
|
|
||||||
<locale android:name="pl"/>
|
|
||||||
<locale android:name="pt"/>
|
|
||||||
<locale android:name="ro"/>
|
|
||||||
<locale android:name="ru"/>
|
|
||||||
<locale android:name="sk"/>
|
|
||||||
<locale android:name="sl"/>
|
|
||||||
<locale android:name="sr"/>
|
|
||||||
<locale android:name="sv"/>
|
|
||||||
<locale android:name="th"/>
|
|
||||||
<locale android:name="tl"/>
|
|
||||||
<locale android:name="tr"/>
|
|
||||||
<locale android:name="uk"/>
|
|
||||||
<locale android:name="vi"/>
|
|
||||||
<locale android:name="zh"/>
|
|
||||||
</locale-config>
|
|
||||||
Reference in New Issue
Block a user