forked from upstream-mirrors/OpenTracks
Compare commits
72 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e0b84df8f6 | |||
| a431850793 | |||
| 5c17c8ffcf | |||
| 0691524dfa | |||
| 1c825b052e | |||
| eb8d28dfc1 | |||
| 9f1ea4d745 | |||
| dbd64a8075 | |||
| 75a88ca0e1 | |||
| 2af19b00e5 | |||
| 11db0d1a4f | |||
| 6a4adee2f2 | |||
| d04b420c56 | |||
| 0122539d0f | |||
| 0b8c2a0ef6 | |||
| cef363562c | |||
| e681531186 | |||
| 65c4b85bc4 | |||
| d7022a07a2 | |||
| 81b090893b | |||
| 3dca9304c4 | |||
| 7fc6492e13 | |||
| 80d968dd0f | |||
| f336a1da12 | |||
| 1657bf5ec1 | |||
| d046ca1cd8 | |||
| 6c852091ce | |||
| 4906d3f942 | |||
| 0395394e26 | |||
| ebcc7dc46e | |||
| 1e4246e73a | |||
| 3ca8381504 | |||
| b92457cb23 | |||
| 4baec1e672 | |||
| c702c5a4a0 | |||
| 24c2c5fc1b | |||
| 8797b033a3 | |||
| b2c4cdc4aa | |||
| ae37ec9c33 | |||
| ccc18739a4 | |||
| f48c4806b5 | |||
| 3aadf6d5d8 | |||
| 2f2679c775 | |||
| 442e23b106 | |||
| 65249f11b2 | |||
| 34b5c2ab18 | |||
| 93919183be | |||
| 1915641d78 | |||
| c2c7cad3eb | |||
| e4957e45af | |||
| 77bce9a5eb | |||
| 6538f0d587 | |||
| 5b0e7e5065 | |||
| d6cf8ba90e | |||
| 29568d0e3f | |||
| a571c4c93b | |||
| 073fed6c84 | |||
| 6847e1f899 | |||
| 427048ac11 | |||
| 240ea47c43 | |||
| 068d56a850 | |||
| 76d02da973 | |||
| 4664895ae3 | |||
| 5235c8cf83 | |||
| 4f040ac79f | |||
| dcad42bf4d | |||
| 2eea93a4a3 | |||
| 55e419475a | |||
| 9225d90e45 | |||
| 32dc6fef82 | |||
| 96fb65b6b0 | |||
| 936bbf9264 |
+8
-10
@@ -6,7 +6,7 @@ buildscript {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:8.1.0-beta01'
|
classpath 'com.android.tools.build:gradle:8.1.0-beta04'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ android {
|
|||||||
targetCompatibility JavaVersion.VERSION_17
|
targetCompatibility JavaVersion.VERSION_17
|
||||||
}
|
}
|
||||||
|
|
||||||
compileSdk 33
|
compileSdk 34
|
||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding true
|
viewBinding true
|
||||||
@@ -64,18 +64,16 @@ android {
|
|||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "de.dennisguse.opentracks"
|
applicationId "de.dennisguse.opentracks"
|
||||||
versionCode 5189
|
versionCode 5262
|
||||||
versionName "v4.3.2"
|
versionName "v4.5.0"
|
||||||
|
|
||||||
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
||||||
|
|
||||||
minSdk 21
|
minSdk 21
|
||||||
targetSdk 33
|
targetSdk 34
|
||||||
|
|
||||||
testInstrumentationRunner "de.dennisguse.opentracks.TestRunner"
|
testInstrumentationRunner "de.dennisguse.opentracks.TestRunner"
|
||||||
testInstrumentationRunnerArguments clearPackageData: 'true'
|
testInstrumentationRunnerArguments clearPackageData: 'true'
|
||||||
|
|
||||||
resConfigs "ar", "bg", "ca", "cs", "da", "de", "el", "en", "es", "et", "eu", "fa", "fi", "fr", "ga", "gl", "hi", "hr", "hu", "in", "it", "iw", "ja", "ko", "lt", "lv", "mo", "ms", "nb", "nl", "pl", "pt", "ro", "ru", "sk", "sl", "sr", "sv", "th", "tl", "tr", "uk", "vi", "zh"
|
|
||||||
}
|
}
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
nightly {
|
nightly {
|
||||||
@@ -133,17 +131,17 @@ dependencies {
|
|||||||
implementation 'androidx.preference:preference:1.2.0'
|
implementation 'androidx.preference:preference:1.2.0'
|
||||||
implementation 'androidx.documentfile:documentfile:1.0.1'
|
implementation 'androidx.documentfile:documentfile:1.0.1'
|
||||||
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.9.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
implementation 'androidx.core:core-splashscreen:1.0.1'
|
implementation 'androidx.core:core-splashscreen:1.0.1'
|
||||||
implementation 'androidx.mediarouter:mediarouter:1.3.1'
|
implementation 'androidx.mediarouter:mediarouter:1.4.0'
|
||||||
|
|
||||||
androidTestImplementation 'androidx.test:core:1.5.0'
|
androidTestImplementation 'androidx.test:core:1.5.0'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||||
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.3.0'
|
androidTestImplementation 'org.mockito:mockito-android:5.4.0'
|
||||||
|
|
||||||
androidTestUtil 'androidx.test:orchestrator:1.4.2'
|
androidTestUtil 'androidx.test:orchestrator:1.4.2'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,4 @@
|
|||||||
|
v4.3.4: OpenTracks
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Android 13: regional locales could not be selected (introduced in v4.3.0)
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
v4.3.5: OpenTracks
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- Sensor parsing had a overflow one off bug (#1559)
|
||||||
|
- Insert marker: don't crash if no camera app is installed (#1561)
|
||||||
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
v4.4.0: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Export all tracks as one file
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
v4.5.0: OpenTracks
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
- Material3
|
||||||
|
- App shortcut for aggregated statistics
|
||||||
|
- New Unit: miles with meters
|
||||||
|
|
||||||
|
Bugfix:
|
||||||
|
- GPS disabled warnings shown repeatedly (#1429)
|
||||||
|
|
||||||
|
Developer:
|
||||||
|
- Use Java Records
|
||||||
|
- Target API34
|
||||||
|
- Refactor sensor handling
|
||||||
@@ -2,13 +2,13 @@ v3.19.0: OpenTracks
|
|||||||
|
|
||||||
Wijzigingen:
|
Wijzigingen:
|
||||||
- Ondersteuning voor Running Speed en Cadence (RCS) sensoren
|
- Ondersteuning voor Running Speed en Cadence (RCS) sensoren
|
||||||
- Bij het delen van een track wordt een afbeelding gemaakt
|
- Bij delen van een track: maakt een deel-afbeelding
|
||||||
- Niet trillen bij starten van opnemen
|
- Niet trillen bij starten van opnemen
|
||||||
- Bij opnemen: UI toont recentst ontvangen hoogte, snelheid en richting (ook al is deze niet opgeslagen)
|
- Bij opnemen: UI toont meest recente hoogte, snelheid en richting (zelfs als niet opgeslagen)
|
||||||
|
|
||||||
Bugfix:
|
Bugfix:
|
||||||
- Berekening van totale tijd mag geen pauze-tijd bevatten (vereist opnieuw importeren)
|
- Berekening totale tijd mag geen pauze-tijd bevatten (vereist opnieuw importeren)
|
||||||
- (opnieuw) Gebruik aangepaste overzichten indien aanwezig
|
- (opnieuw) Gebruik aanwezige aangepaste overzichten
|
||||||
|
|
||||||
Ontwikkelaar:
|
Ontwikkelaar:
|
||||||
- Upgrade naar SDK31 en AGP7.0.0 (vereist JDK11)
|
- Upgrade naar SDK31 en AGP7.0.0 (vereist JDK11)
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ Wijzigingen:
|
|||||||
- Gesproken meldingen van hartslag
|
- Gesproken meldingen van hartslag
|
||||||
|
|
||||||
Bugfix:
|
Bugfix:
|
||||||
- Crash due to race condition
|
- Vastloper door race condities
|
||||||
|
|||||||
Vendored
BIN
Binary file not shown.
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-all.zip
|
||||||
|
networkTimeout=10000
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (3) This script is generated from the Groovy template
|
||||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
# within the Gradle project.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
@@ -80,10 +80,10 @@ do
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
# This is normally unused
|
||||||
|
# shellcheck disable=SC2034
|
||||||
APP_NAME="Gradle"
|
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
@@ -143,12 +143,16 @@ fi
|
|||||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
max*)
|
max*)
|
||||||
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC3045
|
||||||
MAX_FD=$( ulimit -H -n ) ||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
warn "Could not query maximum file descriptor limit"
|
warn "Could not query maximum file descriptor limit"
|
||||||
esac
|
esac
|
||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
'' | soft) :;; #(
|
'' | soft) :;; #(
|
||||||
*)
|
*)
|
||||||
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC3045
|
||||||
ulimit -n "$MAX_FD" ||
|
ulimit -n "$MAX_FD" ||
|
||||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
esac
|
esac
|
||||||
|
|||||||
Vendored
+1
@@ -26,6 +26,7 @@ if "%OS%"=="Windows_NT" setlocal
|
|||||||
|
|
||||||
set DIRNAME=%~dp0
|
set DIRNAME=%~dp0
|
||||||
if "%DIRNAME%"=="" set DIRNAME=.
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
|
@rem This is normally unused
|
||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import static androidx.test.espresso.matcher.ViewMatchers.withId;
|
|||||||
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
||||||
import static org.hamcrest.Matchers.allOf;
|
import static org.hamcrest.Matchers.allOf;
|
||||||
import static org.hamcrest.Matchers.anything;
|
import static org.hamcrest.Matchers.anything;
|
||||||
|
import static de.dennisguse.opentracks.util.EspressoUtils.childAtPosition;
|
||||||
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
||||||
|
|
||||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||||
@@ -30,7 +31,7 @@ public class EspressoDeleteTrackTest {
|
|||||||
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public GrantPermissionRule mGrantPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
public GrantPermissionRule mGrantPermissionRule = TestUtil.createGrantPermissionRule();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void espressoDeleteTrackTest() {
|
public void espressoDeleteTrackTest() {
|
||||||
@@ -51,7 +52,15 @@ public class EspressoDeleteTrackTest {
|
|||||||
onData(anything()).inAdapterView(withId(R.id.track_list)).atPosition(0).perform(longClick());
|
onData(anything()).inAdapterView(withId(R.id.track_list)).atPosition(0).perform(longClick());
|
||||||
|
|
||||||
// open menu and delete selected track
|
// open menu and delete selected track
|
||||||
onView(allOf(withContentDescription("More options"), isDisplayed()))
|
//TODO openActionBarOverflowOrOptionsMenu(); doesn't work
|
||||||
|
onView(
|
||||||
|
allOf(withContentDescription("More options"),
|
||||||
|
childAtPosition(
|
||||||
|
childAtPosition(
|
||||||
|
withId(androidx.appcompat.R.id.action_mode_bar),
|
||||||
|
1),
|
||||||
|
2),
|
||||||
|
isDisplayed()))
|
||||||
.perform(click());
|
.perform(click());
|
||||||
|
|
||||||
onView(withText("Delete")).perform(click());
|
onView(withText("Delete")).perform(click());
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ import static androidx.test.espresso.matcher.ViewMatchers.withText;
|
|||||||
import static org.hamcrest.Matchers.allOf;
|
import static org.hamcrest.Matchers.allOf;
|
||||||
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
|
||||||
import androidx.test.espresso.ViewInteraction;
|
import androidx.test.espresso.ViewInteraction;
|
||||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
@@ -38,7 +40,7 @@ public class EspressoEditTrackRecordingTest {
|
|||||||
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public GrantPermissionRule mGrantPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
public GrantPermissionRule mGrantPermissionRule = TestUtil.createGrantPermissionRule();
|
||||||
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import static org.hamcrest.Matchers.anything;
|
|||||||
import static de.dennisguse.opentracks.util.EspressoUtils.selectTabAtIndex;
|
import static de.dennisguse.opentracks.util.EspressoUtils.selectTabAtIndex;
|
||||||
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
import static de.dennisguse.opentracks.util.EspressoUtils.waitFor;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
|
||||||
import androidx.test.espresso.ViewInteraction;
|
import androidx.test.espresso.ViewInteraction;
|
||||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
@@ -31,8 +33,7 @@ public class EspressoUITest {
|
|||||||
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public GrantPermissionRule mGrantPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
|
public GrantPermissionRule mGrantPermissionRule = TestUtil.createGrantPermissionRule();
|
||||||
|
|
||||||
@LargeTest
|
@LargeTest
|
||||||
@Test
|
@Test
|
||||||
public void record_stop_resume_stop_finish() {
|
public void record_stop_resume_stop_finish() {
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package de.dennisguse.opentracks;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import androidx.test.rule.GrantPermissionRule;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.test.BuildConfig;
|
||||||
|
|
||||||
|
public class TestUtil {
|
||||||
|
|
||||||
|
public static GrantPermissionRule createGrantPermissionRule() {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
return GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.BLUETOOTH_CONNECT, Manifest.permission.POST_NOTIFICATIONS);
|
||||||
|
}
|
||||||
|
return GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.BLUETOOTH_CONNECT);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,7 +27,7 @@ public class ChartPointTest {
|
|||||||
statistics.setTotalTime(Duration.ofSeconds(1000));
|
statistics.setTotalTime(Duration.ofSeconds(1000));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), false, UnitSystem.IMPERIAL);
|
ChartPoint point = new ChartPoint(statistics, TrackStubUtils.createDefaultTrackPoint(), Speed.of(0), false, UnitSystem.IMPERIAL_FEET);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(1000000, (long) point.getTimeOrDistance());
|
assertEquals(1000000, (long) point.getTimeOrDistance());
|
||||||
|
|||||||
@@ -96,16 +96,7 @@ public class TestDataUtil {
|
|||||||
return new TrackData(track, trackPoints, markers);
|
return new TrackData(track, trackPoints, markers);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class TrackData {
|
public record TrackData(Track track, List<TrackPoint> trackPoints, List<Marker> markers) {
|
||||||
public final Track track;
|
|
||||||
public final List<TrackPoint> trackPoints;
|
|
||||||
public final List<Marker> markers;
|
|
||||||
|
|
||||||
public TrackData(Track track, List<TrackPoint> trackPoints, List<Marker> markers) {
|
|
||||||
this.track = track;
|
|
||||||
this.trackPoints = trackPoints;
|
|
||||||
this.markers = markers;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -424,7 +424,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
Marker.Id markerId = new Marker.Id(System.currentTimeMillis());
|
Marker.Id markerId = new Marker.Id(System.currentTimeMillis());
|
||||||
marker.setId(markerId);
|
marker.setId(markerId);
|
||||||
ContentValues contentValues = contentProviderUtils.createContentValues(marker);
|
ContentValues contentValues = contentProviderUtils.createContentValues(marker);
|
||||||
assertEquals(markerId.getId(), contentValues.get(MarkerColumns._ID));
|
assertEquals(markerId.id(), contentValues.get(MarkerColumns._ID));
|
||||||
assertEquals((int) (TestDataUtil.INITIAL_LONGITUDE * 1000000), contentValues.get(MarkerColumns.LONGITUDE));
|
assertEquals((int) (TestDataUtil.INITIAL_LONGITUDE * 1000000), contentValues.get(MarkerColumns.LONGITUDE));
|
||||||
assertEquals(TEST_DESC, contentValues.get(MarkerColumns.DESCRIPTION));
|
assertEquals(TEST_DESC, contentValues.get(MarkerColumns.DESCRIPTION));
|
||||||
}
|
}
|
||||||
@@ -458,7 +458,7 @@ public class CustomContentProviderUtilsTest {
|
|||||||
when(cursorMock.getLong(columnIndex++)).thenReturn(trackId);
|
when(cursorMock.getLong(columnIndex++)).thenReturn(trackId);
|
||||||
|
|
||||||
Marker marker = contentProviderUtils.createMarker(cursorMock);
|
Marker marker = contentProviderUtils.createMarker(cursorMock);
|
||||||
assertEquals(id, marker.getId().getId());
|
assertEquals(id, marker.getId().id());
|
||||||
assertEquals(name, marker.getName());
|
assertEquals(name, marker.getName());
|
||||||
assertEquals(trackId, marker.getTrackId().getId());
|
assertEquals(trackId, marker.getTrackId().getId());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ public class TrackSelectionTest extends TestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testFilterBuildSelection_onlyOneCategory() {
|
public void testFilterBuildSelection_onlyOneCategory() {
|
||||||
// given
|
// given
|
||||||
TrackSelection filter = new TrackSelection().addCategory("running");
|
TrackSelection filter = new TrackSelection().addActivityType("running");
|
||||||
|
|
||||||
// when
|
// when
|
||||||
SelectionData selection = filter.buildSelection();
|
SelectionData selection = filter.buildSelection();
|
||||||
@@ -82,10 +82,10 @@ public class TrackSelectionTest extends TestCase {
|
|||||||
public void testFilterBuildSelection_severalCategories() {
|
public void testFilterBuildSelection_severalCategories() {
|
||||||
// given
|
// given
|
||||||
TrackSelection filter = new TrackSelection()
|
TrackSelection filter = new TrackSelection()
|
||||||
.addCategory("running")
|
.addActivityType("running")
|
||||||
.addCategory("road biking")
|
.addActivityType("road biking")
|
||||||
.addCategory("mountain biking")
|
.addActivityType("mountain biking")
|
||||||
.addCategory("trail walking");
|
.addActivityType("trail walking");
|
||||||
|
|
||||||
// when
|
// when
|
||||||
SelectionData selection = filter.buildSelection();
|
SelectionData selection = filter.buildSelection();
|
||||||
@@ -136,8 +136,8 @@ public class TrackSelectionTest extends TestCase {
|
|||||||
.addTrackId(trackId1)
|
.addTrackId(trackId1)
|
||||||
.addTrackId(trackId2)
|
.addTrackId(trackId2)
|
||||||
.addTrackId(trackId3)
|
.addTrackId(trackId3)
|
||||||
.addCategory("running")
|
.addActivityType("running")
|
||||||
.addCategory("road biking");
|
.addActivityType("road biking");
|
||||||
|
|
||||||
// when
|
// when
|
||||||
SelectionData selection = filter.buildSelection();
|
SelectionData selection = filter.buildSelection();
|
||||||
@@ -188,8 +188,8 @@ public class TrackSelectionTest extends TestCase {
|
|||||||
long oneDay = 24 * 60 * 60 * 1000;
|
long oneDay = 24 * 60 * 60 * 1000;
|
||||||
|
|
||||||
TrackSelection filter = new TrackSelection()
|
TrackSelection filter = new TrackSelection()
|
||||||
.addCategory("running")
|
.addActivityType("running")
|
||||||
.addCategory("road biking")
|
.addActivityType("road biking")
|
||||||
.addDateRange(instant, instant.plusMillis(oneDay));
|
.addDateRange(instant, instant.plusMillis(oneDay));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
|
|||||||
+1
-1
@@ -35,7 +35,7 @@ public class DistanceFormatterTest {
|
|||||||
public void testFormatDistance_imperial() {
|
public void testFormatDistance_imperial() {
|
||||||
DistanceFormatter formatter = DistanceFormatter.Builder()
|
DistanceFormatter formatter = DistanceFormatter.Builder()
|
||||||
.setDecimalCount(2)
|
.setDecimalCount(2)
|
||||||
.setUnit(UnitSystem.IMPERIAL)
|
.setUnit(UnitSystem.IMPERIAL_FEET)
|
||||||
.build(context);
|
.build(context);
|
||||||
|
|
||||||
// A large number in imperial
|
// A large number in imperial
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public class SpeedFormatterTest {
|
|||||||
public void testGetSpeedParts_pace_imperial() {
|
public void testGetSpeedParts_pace_imperial() {
|
||||||
SpeedFormatter formatter = SpeedFormatter.Builder()
|
SpeedFormatter formatter = SpeedFormatter.Builder()
|
||||||
.setDecimalCount(2)
|
.setDecimalCount(2)
|
||||||
.setUnit(UnitSystem.IMPERIAL)
|
.setUnit(UnitSystem.IMPERIAL_FEET)
|
||||||
.setReportSpeedOrPace(false)
|
.setReportSpeedOrPace(false)
|
||||||
.build(context);
|
.build(context);
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ public class SpeedFormatterTest {
|
|||||||
public void testGetSpeedParts_speed_imperial() {
|
public void testGetSpeedParts_speed_imperial() {
|
||||||
SpeedFormatter formatter = SpeedFormatter.Builder()
|
SpeedFormatter formatter = SpeedFormatter.Builder()
|
||||||
.setDecimalCount(2)
|
.setDecimalCount(2)
|
||||||
.setUnit(UnitSystem.IMPERIAL)
|
.setUnit(UnitSystem.IMPERIAL_FEET)
|
||||||
.setReportSpeedOrPace(true)
|
.setReportSpeedOrPace(true)
|
||||||
.build(context);
|
.build(context);
|
||||||
|
|
||||||
|
|||||||
+15
-22
@@ -57,7 +57,6 @@ import de.dennisguse.opentracks.data.models.TrackPoint;
|
|||||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||||
import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
|
import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
|
||||||
import de.dennisguse.opentracks.sensors.AltitudeSumManager;
|
import de.dennisguse.opentracks.sensors.AltitudeSumManager;
|
||||||
import de.dennisguse.opentracks.sensors.BluetoothRemoteSensorManager;
|
|
||||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadence;
|
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadence;
|
||||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingDistanceSpeed;
|
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingDistanceSpeed;
|
||||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower;
|
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingPower;
|
||||||
@@ -97,7 +96,7 @@ public class ExportImportTest {
|
|||||||
private final ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
|
private final ContentProviderUtils contentProviderUtils = new ContentProviderUtils(context);
|
||||||
|
|
||||||
private static final String TRACK_ICON = "the track icon";
|
private static final String TRACK_ICON = "the track icon";
|
||||||
private static final String TRACK_CATEGORY = "the category";
|
private static final String TRACK_ACTIVITY_TYPE = "the activity type";
|
||||||
private static final String TRACK_DESCRIPTION = "the description";
|
private static final String TRACK_DESCRIPTION = "the description";
|
||||||
|
|
||||||
private File tmpFile;
|
private File tmpFile;
|
||||||
@@ -165,8 +164,7 @@ public class ExportImportTest {
|
|||||||
service.insertMarker("Marker 2", "Marker 2 category", "Marker 2 desc", null);
|
service.insertMarker("Marker 2", "Marker 2 category", "Marker 2 desc", null);
|
||||||
|
|
||||||
trackPointCreator.setClock("2020-02-02T02:02:18Z");
|
trackPointCreator.setClock("2020-02-02T02:02:18Z");
|
||||||
BluetoothRemoteSensorManager mockRemoteSensorManager = Mockito.mock(BluetoothRemoteSensorManager.class);
|
trackPointCreator.getSensorManager().sensorDataSet = new SensorDataSet();
|
||||||
trackPointCreator.getSensorManager().setBluetoothSensorManager(mockRemoteSensorManager);
|
|
||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
trackPointCreator.setClock("2020-02-02T02:03:20Z");
|
trackPointCreator.setClock("2020-02-02T02:03:20Z");
|
||||||
@@ -179,12 +177,11 @@ public class ExportImportTest {
|
|||||||
sendLocation(trackPointCreator, "2020-02-02T02:03:23Z", 3, 16.001, 10, 27, 15, 10, 0);
|
sendLocation(trackPointCreator, "2020-02-02T02:03:23Z", 3, 16.001, 10, 27, 15, 10, 0);
|
||||||
|
|
||||||
trackPointCreator.setClock("2020-02-02T02:03:24Z");
|
trackPointCreator.setClock("2020-02-02T02:03:24Z");
|
||||||
trackPointCreator.getSensorManager().setBluetoothSensorManager(mockRemoteSensorManager);
|
|
||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
track.setIcon(TRACK_ICON);
|
track.setIcon(TRACK_ICON);
|
||||||
track.setCategory(TRACK_CATEGORY);
|
track.setActivityType(TRACK_ACTIVITY_TYPE);
|
||||||
track.setDescription(TRACK_DESCRIPTION);
|
track.setDescription(TRACK_DESCRIPTION);
|
||||||
contentProviderUtils.updateTrack(track);
|
contentProviderUtils.updateTrack(track);
|
||||||
|
|
||||||
@@ -208,7 +205,7 @@ public class ExportImportTest {
|
|||||||
|
|
||||||
// when
|
// when
|
||||||
// 1. export
|
// 1. export
|
||||||
trackExporter.writeTrack(track, context.getContentResolver().openOutputStream(tmpFileUri));
|
trackExporter.writeTrack(List.of(track), context.getContentResolver().openOutputStream(tmpFileUri));
|
||||||
contentProviderUtils.deleteTrack(context, trackId);
|
contentProviderUtils.deleteTrack(context, trackId);
|
||||||
|
|
||||||
// 2. import
|
// 2. import
|
||||||
@@ -219,7 +216,7 @@ public class ExportImportTest {
|
|||||||
// 1. track
|
// 1. track
|
||||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||||
assertNotNull(importedTrack);
|
assertNotNull(importedTrack);
|
||||||
assertEquals(track.getCategory(), importedTrack.getCategory());
|
assertEquals(track.getActivityType(), importedTrack.getActivityType());
|
||||||
assertEquals(track.getDescription(), importedTrack.getDescription());
|
assertEquals(track.getDescription(), importedTrack.getDescription());
|
||||||
assertEquals(track.getName(), importedTrack.getName());
|
assertEquals(track.getName(), importedTrack.getName());
|
||||||
assertEquals(track.getIcon(), importedTrack.getIcon());
|
assertEquals(track.getIcon(), importedTrack.getIcon());
|
||||||
@@ -290,7 +287,7 @@ public class ExportImportTest {
|
|||||||
TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context, contentProviderUtils);
|
TrackExporter trackExporter = TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.createTrackExporter(context, contentProviderUtils);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
trackExporter.writeTrack(track, context.getContentResolver().openOutputStream(tmpFileUri));
|
trackExporter.writeTrack(List.of(track), context.getContentResolver().openOutputStream(tmpFileUri));
|
||||||
|
|
||||||
// 2. import
|
// 2. import
|
||||||
InputStream inputStream = context.getContentResolver().openInputStream(tmpFileUri);
|
InputStream inputStream = context.getContentResolver().openInputStream(tmpFileUri);
|
||||||
@@ -314,7 +311,7 @@ public class ExportImportTest {
|
|||||||
|
|
||||||
// when
|
// when
|
||||||
// 1. export
|
// 1. export
|
||||||
trackExporter.writeTrack(track, context.getContentResolver().openOutputStream(tmpFileUri));
|
trackExporter.writeTrack(List.of(track), context.getContentResolver().openOutputStream(tmpFileUri));
|
||||||
contentProviderUtils.deleteTrack(context, trackId);
|
contentProviderUtils.deleteTrack(context, trackId);
|
||||||
|
|
||||||
// 2. import
|
// 2. import
|
||||||
@@ -326,7 +323,7 @@ public class ExportImportTest {
|
|||||||
// 1. track
|
// 1. track
|
||||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||||
assertNotNull(importedTrack);
|
assertNotNull(importedTrack);
|
||||||
assertEquals(track.getCategory(), importedTrack.getCategory());
|
assertEquals(track.getActivityType(), importedTrack.getActivityType());
|
||||||
assertEquals(track.getDescription(), importedTrack.getDescription());
|
assertEquals(track.getDescription(), importedTrack.getDescription());
|
||||||
assertEquals(track.getName(), importedTrack.getName());
|
assertEquals(track.getName(), importedTrack.getName());
|
||||||
|
|
||||||
@@ -432,7 +429,7 @@ public class ExportImportTest {
|
|||||||
|
|
||||||
// when
|
// when
|
||||||
// 1. export
|
// 1. export
|
||||||
trackExporter.writeTrack(track, context.getContentResolver().openOutputStream(tmpFileUri));
|
trackExporter.writeTrack(List.of(track), context.getContentResolver().openOutputStream(tmpFileUri));
|
||||||
|
|
||||||
// 2. import
|
// 2. import
|
||||||
InputStream inputStream = context.getContentResolver().openInputStream(tmpFileUri);
|
InputStream inputStream = context.getContentResolver().openInputStream(tmpFileUri);
|
||||||
@@ -457,7 +454,7 @@ public class ExportImportTest {
|
|||||||
|
|
||||||
// when
|
// when
|
||||||
// 1. export
|
// 1. export
|
||||||
trackExporter.writeTrack(track, context.getContentResolver().openOutputStream(tmpFileUri));
|
trackExporter.writeTrack(List.of(track), context.getContentResolver().openOutputStream(tmpFileUri));
|
||||||
contentProviderUtils.deleteTrack(context, trackId);
|
contentProviderUtils.deleteTrack(context, trackId);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
@@ -495,9 +492,6 @@ public class ExportImportTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void mockBLESensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power) {
|
private void mockBLESensorData(TrackPointCreator trackPointCreator, Float speed, Distance distance, float heartRate, float cadence, Float power) {
|
||||||
BluetoothRemoteSensorManager remoteSensorManager = Mockito.mock(BluetoothRemoteSensorManager.class);
|
|
||||||
Mockito.when(remoteSensorManager.fill(Mockito.any())).thenAnswer(invocation -> {
|
|
||||||
TrackPoint trackPoint = invocation.getArgument(0);
|
|
||||||
|
|
||||||
SensorDataSet sensorDataSet = new SensorDataSet();
|
SensorDataSet sensorDataSet = new SensorDataSet();
|
||||||
sensorDataSet.set(new SensorDataCyclingPower("power", "power", Power.of(power)));
|
sensorDataSet.set(new SensorDataCyclingPower("power", "power", Power.of(power)));
|
||||||
@@ -518,11 +512,10 @@ public class ExportImportTest {
|
|||||||
sensorDataSet.set(distanceSpeed);
|
sensorDataSet.set(distanceSpeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
sensorDataSet.fillTrackPoint(trackPoint);
|
trackPointCreator.getSensorManager().sensorDataSet = sensorDataSet;
|
||||||
return sensorDataSet;
|
trackPointCreator.onChange(sensorDataSet);
|
||||||
});
|
|
||||||
trackPointCreator.getSensorManager().setBluetoothSensorManager(remoteSensorManager);
|
//TODO Might require: getSensorManager().sensorDataSet = new SensorDataSet()
|
||||||
trackPointCreator.onChange(new SensorDataSet());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void mockAltitudeChange(TrackPointCreator trackPointCreator, float altitudeGain) {
|
private void mockAltitudeChange(TrackPointCreator trackPointCreator, float altitudeGain) {
|
||||||
@@ -545,6 +538,6 @@ public class ExportImportTest {
|
|||||||
mockAltitudeChange(trackPointCreator, altitudeGain);
|
mockAltitudeChange(trackPointCreator, altitudeGain);
|
||||||
|
|
||||||
trackPointCreator.setClock(time);
|
trackPointCreator.setClock(time);
|
||||||
trackPointCreator.getGpsHandler().onLocationChanged(location);
|
trackPointCreator.onChange(location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+4
-4
@@ -79,7 +79,7 @@ public class GPXTrackImporterTest {
|
|||||||
// 2. track
|
// 2. track
|
||||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||||
assertNotNull(importedTrack);
|
assertNotNull(importedTrack);
|
||||||
assertEquals("the category", importedTrack.getCategory());
|
assertEquals("the category", importedTrack.getActivityType());
|
||||||
assertEquals("the description", importedTrack.getDescription());
|
assertEquals("the description", importedTrack.getDescription());
|
||||||
assertEquals("2021-01-07 22:51", importedTrack.getName());
|
assertEquals("2021-01-07 22:51", importedTrack.getName());
|
||||||
assertEquals("UNKNOWN", importedTrack.getIcon());
|
assertEquals("UNKNOWN", importedTrack.getIcon());
|
||||||
@@ -133,7 +133,7 @@ public class GPXTrackImporterTest {
|
|||||||
// 2. track
|
// 2. track
|
||||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||||
assertNotNull(importedTrack);
|
assertNotNull(importedTrack);
|
||||||
assertEquals("", importedTrack.getCategory());
|
assertEquals("", importedTrack.getActivityType());
|
||||||
assertEquals("", importedTrack.getDescription());
|
assertEquals("", importedTrack.getDescription());
|
||||||
assertEquals("20210907_213924.gpx", importedTrack.getName());
|
assertEquals("20210907_213924.gpx", importedTrack.getName());
|
||||||
assertEquals("", importedTrack.getIcon());
|
assertEquals("", importedTrack.getIcon());
|
||||||
@@ -181,7 +181,7 @@ public class GPXTrackImporterTest {
|
|||||||
// 2. track
|
// 2. track
|
||||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||||
assertNotNull(importedTrack);
|
assertNotNull(importedTrack);
|
||||||
assertEquals("", importedTrack.getCategory());
|
assertEquals("", importedTrack.getActivityType());
|
||||||
assertEquals("", importedTrack.getDescription());
|
assertEquals("", importedTrack.getDescription());
|
||||||
assertEquals("20210907_213924.gpx", importedTrack.getName());
|
assertEquals("20210907_213924.gpx", importedTrack.getName());
|
||||||
assertEquals("", importedTrack.getIcon());
|
assertEquals("", importedTrack.getIcon());
|
||||||
@@ -232,7 +232,7 @@ public class GPXTrackImporterTest {
|
|||||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||||
|
|
||||||
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context, contentProviderUtils);
|
TrackExporter trackExporter = TrackFileFormat.GPX.createTrackExporter(context, contentProviderUtils);
|
||||||
trackExporter.writeTrack(importedTrack, outputStream);
|
trackExporter.writeTrack(List.of(importedTrack), outputStream);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
String expected = new BufferedReader(
|
String expected = new BufferedReader(
|
||||||
|
|||||||
+3
-3
@@ -70,7 +70,7 @@ public class KMLTrackImporterTest {
|
|||||||
// 1. track
|
// 1. track
|
||||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||||
assertNotNull(importedTrack);
|
assertNotNull(importedTrack);
|
||||||
assertEquals("", importedTrack.getCategory());
|
assertEquals("", importedTrack.getActivityType());
|
||||||
assertEquals("", importedTrack.getDescription());
|
assertEquals("", importedTrack.getDescription());
|
||||||
assertEquals("", importedTrack.getName());
|
assertEquals("", importedTrack.getName());
|
||||||
assertEquals("", importedTrack.getIcon());
|
assertEquals("", importedTrack.getIcon());
|
||||||
@@ -119,7 +119,7 @@ public class KMLTrackImporterTest {
|
|||||||
// 1. track
|
// 1. track
|
||||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||||
assertNotNull(importedTrack);
|
assertNotNull(importedTrack);
|
||||||
assertEquals("", importedTrack.getCategory());
|
assertEquals("", importedTrack.getActivityType());
|
||||||
assertEquals("", importedTrack.getDescription());
|
assertEquals("", importedTrack.getDescription());
|
||||||
assertEquals("", importedTrack.getName());
|
assertEquals("", importedTrack.getName());
|
||||||
assertEquals("", importedTrack.getIcon());
|
assertEquals("", importedTrack.getIcon());
|
||||||
@@ -159,7 +159,7 @@ public class KMLTrackImporterTest {
|
|||||||
// 1. track
|
// 1. track
|
||||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||||
assertNotNull(importedTrack);
|
assertNotNull(importedTrack);
|
||||||
assertEquals("unknown", importedTrack.getCategory());
|
assertEquals("unknown", importedTrack.getActivityType());
|
||||||
assertEquals("Test Track", importedTrack.getDescription());
|
assertEquals("Test Track", importedTrack.getDescription());
|
||||||
assertEquals("2020-11-28 18:06", importedTrack.getName());
|
assertEquals("2020-11-28 18:06", importedTrack.getName());
|
||||||
assertEquals("UNKNOWN", importedTrack.getIcon());
|
assertEquals("UNKNOWN", importedTrack.getIcon());
|
||||||
|
|||||||
@@ -1,47 +1,55 @@
|
|||||||
package de.dennisguse.opentracks.sensors;
|
package de.dennisguse.opentracks.sensors;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.data.models.AtmosphericPressure;
|
||||||
|
import de.dennisguse.opentracks.sensors.driver.BarometerInternal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NOTE: Test data is completely artificial.
|
* NOTE: Test data is completely artificial.
|
||||||
*/
|
*/
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
public class AltitudeSumManagerTest {
|
public class AltitudeSumManagerTest {
|
||||||
|
|
||||||
private final AltitudeSumManager subject = new AltitudeSumManager();
|
|
||||||
|
|
||||||
private static void addSensorValue(AltitudeSumManager altitudeSumManager, float[] values) {
|
private static void addSensorValue(AltitudeSumManager altitudeSumManager, float[] values) {
|
||||||
for (float f : values) {
|
for (float f : values) {
|
||||||
altitudeSumManager.onSensorValueChanged(f);
|
altitudeSumManager.onSensorValueChanged(AtmosphericPressure.ofHPA(f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Mock
|
||||||
public void setUp() {
|
BarometerInternal driver;
|
||||||
subject.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getAltitudeGainLoss_downhill() {
|
public void getAltitudeGainLoss_downhill() {
|
||||||
// given
|
// given
|
||||||
subject.setConnected(true);
|
AltitudeSumManager subject = new AltitudeSumManager(driver);
|
||||||
|
|
||||||
|
when(driver.isConnected()).thenReturn(true);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
addSensorValue(subject, new float[]{1015f, 1015.01f, 1015.02f, 1015.03f, 1015.04f, 1015.05f, 1015.06f, 1015.07f, 1015.08f, 1015.09f, 1015.10f, 1015.11f, 1015.12f, 1015.13f, 1015.14f, 1015.15f});
|
addSensorValue(subject, new float[]{1015f, 1015.01f, 1015.02f, 1015.03f, 1015.04f, 1015.05f, 1015.06f, 1015.07f, 1015.08f, 1015.09f, 1015.10f, 1015.11f, 1015.12f, 1015.13f, 1018f, 1018.1f, 1018.1f, 1018.1f, 1018.1f});
|
||||||
|
|
||||||
// then
|
// then
|
||||||
Assert.assertEquals(0f, subject.getAltitudeGain_m(), 0.01);
|
Assert.assertEquals(0f, subject.getAltitudeGain_m(), 0.01);
|
||||||
Assert.assertEquals(48.0, subject.getAltitudeLoss_m(), 0.01);
|
Assert.assertEquals(15f, subject.getAltitudeLoss_m(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void sensorUnavailable() {
|
public void sensorUnavailable() {
|
||||||
// given
|
// given
|
||||||
subject.setConnected(false);
|
AltitudeSumManager subject = new AltitudeSumManager(driver);
|
||||||
|
|
||||||
|
when(driver.isConnected()).thenReturn(true);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
subject.onSensorValueChanged(999f);
|
subject.onSensorValueChanged(AtmosphericPressure.ofHPA(999f));
|
||||||
|
|
||||||
// then
|
// then
|
||||||
Assert.assertNull(subject.getAltitudeGain_m());
|
Assert.assertNull(subject.getAltitudeGain_m());
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import android.bluetooth.BluetoothGattCharacteristic;
|
|||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.data.models.AtmosphericPressure;
|
||||||
import de.dennisguse.opentracks.data.models.Cadence;
|
import de.dennisguse.opentracks.data.models.Cadence;
|
||||||
import de.dennisguse.opentracks.data.models.Distance;
|
import de.dennisguse.opentracks.data.models.Distance;
|
||||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||||
@@ -20,7 +21,7 @@ public class BluetoothUtilsTest {
|
|||||||
@Test
|
@Test
|
||||||
public void parseHeartRate_uint8() {
|
public void parseHeartRate_uint8() {
|
||||||
// given
|
// given
|
||||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.HEARTRATE.getServiceUUID(), 0, 0);
|
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.HEARTRATE.serviceUUID(), 0, 0);
|
||||||
characteristic.setValue(new byte[]{0x02, 0x3C});
|
characteristic.setValue(new byte[]{0x02, 0x3C});
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@@ -33,7 +34,7 @@ public class BluetoothUtilsTest {
|
|||||||
@Test
|
@Test
|
||||||
public void parseHeartRate_uint16() {
|
public void parseHeartRate_uint16() {
|
||||||
// given
|
// given
|
||||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.HEARTRATE.getServiceUUID(), 0, 0);
|
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.HEARTRATE.serviceUUID(), 0, 0);
|
||||||
characteristic.setValue(new byte[]{0x01, 0x01, 0x01});
|
characteristic.setValue(new byte[]{0x01, 0x01, 0x01});
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@@ -43,9 +44,22 @@ public class BluetoothUtilsTest {
|
|||||||
assertEquals(HeartRate.of(257), heartRate);
|
assertEquals(HeartRate.of(257), heartRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parseEnvironmentalSensing_Pa() {
|
||||||
|
// given
|
||||||
|
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.BAROMETRIC_PRESSURE.serviceUUID(), 0, 0);
|
||||||
|
characteristic.setValue(new byte[]{(byte) 0xB2, (byte) 0x48, (byte) 0x0F, (byte) 0x00});
|
||||||
|
|
||||||
|
// when
|
||||||
|
AtmosphericPressure pressure = BluetoothUtils.parseEnvironmentalSensing(characteristic);
|
||||||
|
|
||||||
|
// then
|
||||||
|
assertEquals(AtmosphericPressure.ofPA(100165), pressure);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void parseCyclingSpeedCadence_crankOnly() {
|
public void parseCyclingSpeedCadence_crankOnly() {
|
||||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.getServiceUUID(), 0, 0);
|
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0);
|
||||||
characteristic.setValue(new byte[]{0x02, (byte) 0xC8, 0x00, 0x00, 0x00, 0x06, (byte) 0x99});
|
characteristic.setValue(new byte[]{0x02, (byte) 0xC8, 0x00, 0x00, 0x00, 0x06, (byte) 0x99});
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@@ -58,7 +72,7 @@ public class BluetoothUtilsTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void parseCyclingSpeedCadence_wheelOnly() {
|
public void parseCyclingSpeedCadence_wheelOnly() {
|
||||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.getServiceUUID(), 0, 0);
|
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0);
|
||||||
characteristic.setValue(new byte[]{0x01, (byte) 0xFF, (byte) 0xFF, 0, 1, 0x45, (byte) 0x99});
|
characteristic.setValue(new byte[]{0x01, (byte) 0xFF, (byte) 0xFF, 0, 1, 0x45, (byte) 0x99});
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@@ -71,7 +85,7 @@ public class BluetoothUtilsTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void parseCyclingSpeedCadence_crankWheel() {
|
public void parseCyclingSpeedCadence_crankWheel() {
|
||||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.getServiceUUID(), 0, 0);
|
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_SPEED_CADENCE.serviceUUID(), 0, 0);
|
||||||
characteristic.setValue(new byte[]{0x03, (byte) 0xC8, 0x00, 0x00, 0x01, 0x06, (byte) 0x99, (byte) 0xE1, 0x00, 0x45, (byte) 0x99});
|
characteristic.setValue(new byte[]{0x03, (byte) 0xC8, 0x00, 0x00, 0x01, 0x06, (byte) 0x99, (byte) 0xE1, 0x00, 0x45, (byte) 0x99});
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@@ -84,34 +98,34 @@ public class BluetoothUtilsTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void parseCyclingPower_power() {
|
public void parseCyclingPower_power() {
|
||||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_POWER.getServiceUUID(), 0, 0);
|
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_POWER.serviceUUID(), 0, 0);
|
||||||
characteristic.setValue(new byte[]{0, 0, 40, 0});
|
characteristic.setValue(new byte[]{0, 0, 40, 0});
|
||||||
|
|
||||||
// when
|
// when
|
||||||
SensorDataCyclingPower.Data powerCadence = BluetoothUtils.parseCyclingPower("", "", characteristic);
|
SensorDataCyclingPower.Data powerCadence = BluetoothUtils.parseCyclingPower("", "", characteristic);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(40, powerCadence.getPower().getValue().getW(), 0.01);
|
assertEquals(40, powerCadence.power().getValue().getW(), 0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void parseCyclingPower_power_with_cadence() {
|
public void parseCyclingPower_power_with_cadence() {
|
||||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_POWER.getServiceUUID(), 0, 0);
|
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.CYCLING_POWER.serviceUUID(), 0, 0);
|
||||||
characteristic.setValue(new byte[]{0x2C, 0x00, 0x00, 0x00, (byte) 0x9F, 0x00, 0x0C, 0x00, (byte) 0xE5, 0x42});
|
characteristic.setValue(new byte[]{0x2C, 0x00, 0x00, 0x00, (byte) 0x9F, 0x00, 0x0C, 0x00, (byte) 0xE5, 0x42});
|
||||||
|
|
||||||
// when
|
// when
|
||||||
SensorDataCyclingPower.Data powerCadence = BluetoothUtils.parseCyclingPower("", "", characteristic);
|
SensorDataCyclingPower.Data powerCadence = BluetoothUtils.parseCyclingPower("", "", characteristic);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(0, powerCadence.getPower().getValue().getW(), 0.01);
|
assertEquals(0, powerCadence.power().getValue().getW(), 0.01);
|
||||||
|
|
||||||
assertEquals(12, powerCadence.getCadence().getCrankRevolutionsCount());
|
assertEquals(12, powerCadence.cadence().getCrankRevolutionsCount());
|
||||||
assertEquals(17125, powerCadence.getCadence().getCrankRevolutionsTime());
|
assertEquals(17125, powerCadence.cadence().getCrankRevolutionsTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void parseRunningSpeedAndCadence_with_distance() {
|
public void parseRunningSpeedAndCadence_with_distance() {
|
||||||
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.RUNNING_SPEED_CADENCE.getServiceUUID(), 0, 0);
|
BluetoothGattCharacteristic characteristic = new BluetoothGattCharacteristic(BluetoothUtils.RUNNING_SPEED_CADENCE.serviceUUID(), 0, 0);
|
||||||
characteristic.setValue(new byte[]{2, 0, 5, 80, (byte) 0xFF, (byte) 0xFF, 0, 1});
|
characteristic.setValue(new byte[]{2, 0, 5, 80, (byte) 0xFF, (byte) 0xFF, 0, 1});
|
||||||
|
|
||||||
// when
|
// when
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import static org.junit.Assert.assertNull;
|
|||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.data.models.AtmosphericPressure;
|
||||||
|
|
||||||
public class PressureSensorUtilsTest {
|
public class PressureSensorUtilsTest {
|
||||||
|
|
||||||
// real data at 500Hz stationary
|
// real data at 500Hz stationary
|
||||||
@@ -12,11 +14,11 @@ public class PressureSensorUtilsTest {
|
|||||||
public void altitudeChanges_none() {
|
public void altitudeChanges_none() {
|
||||||
// given
|
// given
|
||||||
float[] sensorValues_hPa = new float[]{1015.6876f, 1015.699f, 1015.70905f, 1015.71075f, 1015.7224f, 1015.72f, 1015.7373f, 1015.7481f, 1015.74133f, 1015.73553f, 1015.7462f, 1015.74896f, 1015.752f, 1015.754f, 1015.7456f, 1015.73303f, 1015.7288f, 1015.7239f, 1015.7298f, 1015.7387f, 1015.72534f, 1015.72577f, 1015.72797f, 1015.71796f, 1015.7113f, 1015.7097f, 1015.7096f, 1015.7013f, 1015.7072f, 1015.71063f, 1015.7088f, 1015.7115f, 1015.713f, 1015.7121f, 1015.7062f, 1015.7062f, 1015.6994f, 1015.69617f, 1015.68945f, 1015.698f, 1015.6913f, 1015.69696f, 1015.69293f, 1015.6955f, 1015.6928f, 1015.6939f, 1015.68787f, 1015.6881f, 1015.68536f, 1015.68726f, 1015.6879f, 1015.6904f, 1015.6937f, 1015.69476f, 1015.7046f, 1015.7019f, 1015.7079f, 1015.71124f, 1015.71216f, 1015.71436f, 1015.7171f, 1015.7186f, 1015.7186f, 1015.7246f, 1015.72046f, 1015.7211f, 1015.72375f, 1015.7194f, 1015.72125f, 1015.7161f, 1015.71185f, 1015.7188f, 1015.721f, 1015.7252f, 1015.72266f, 1015.7236f, 1015.72614f, 1015.7266f, 1015.7344f, 1015.7376f, 1015.7337f, 1015.7253f, 1015.71875f, 1015.7219f, 1015.7219f, 1015.7317f, 1015.7284f, 1015.73444f, 1015.73584f, 1015.7369f, 1015.73254f, 1015.7369f, 1015.7336f, 1015.73254f, 1015.73676f, 1015.74176f, 1015.74506f, 1015.7478f, 1015.755f, 1015.7515f, 1015.7543f, 1015.7459f, 1015.7468f, 1015.7492f, 1015.74585f, 1015.74756f, 1015.74756f, 1015.75085f, 1015.754f, 1015.75336f, 1015.7568f, 1015.75165f, 1015.761f, 1015.7527f, 1015.75684f, 1015.7635f, 1015.7585f, 1015.7552f, 1015.7475f, 1015.74506f, 1015.7542f, 1015.75586f, 1015.74176f, 1015.74414f, 1015.7501f, 1015.751f, 1015.7478f, 1015.755f, 1015.75836f, 1015.7592f, 1015.75256f, 1015.7559f, 1015.76654f, 1015.76154f, 1015.766f, 1015.77014f, 1015.76904f, 1015.76013f, 1015.7674f, 1015.76514f, 1015.7674f, 1015.7635f, 1015.76404f, 1015.7607f, 1015.754f, 1015.7574f, 1015.7607f, 1015.7607f, 1015.7607f, 1015.754f, 1015.7567f, 1015.7616f, 1015.7643f, 1015.7666f, 1015.7609f, 1015.766f, 1015.76764f, 1015.755f, 1015.75757f, 1015.74927f, 1015.74927f, 1015.7426f, 1015.745f, 1015.745f, 1015.7409f, 1015.7376f, 1015.7326f, 1015.74164f, 1015.74664f, 1015.745f, 1015.7459f, 1015.7502f, 1015.7493f, 1015.7476f, 1015.7493f, 1015.751f, 1015.7601f, 1015.76434f, 1015.766f, 1015.756f, 1015.7617f, 1015.7623f, 1015.76404f, 1015.76733f, 1015.7607f, 1015.7623f, 1015.76337f, 1015.7584f, 1015.7573f, 1015.7502f, 1015.7534f, 1015.7534f, 1015.7601f, 1015.75684f, 1015.7518f, 1015.75574f, 1015.7524f, 1015.7507f, 1015.7484f, 1015.7502f, 1015.7534f, 1015.7623f, 1015.75903f, 1015.75574f, 1015.75574f, 1015.7573f, 1015.7524f, 1015.75574f, 1015.75574f, 1015.7607f, 1015.7573f, 1015.754f, 1015.74677f, 1015.754f, 1015.75574f, 1015.75903f, 1015.7573f, 1015.7551f, 1015.7573f, 1015.754f, 1015.7507f, 1015.7374f, 1015.739f, 1015.73566f, 1015.73566f, 1015.7367f, 1015.7417f, 1015.7417f, 1015.739f, 1015.74835f, 1015.74567f, 1015.7517f, 1015.74066f, 1015.7374f, 1015.739f, 1015.7417f, 1015.73065f, 1015.72894f, 1015.73505f, 1015.73334f, 1015.74005f, 1015.74005f, 1015.7367f, 1015.7434f, 1015.744f, 1015.7423f, 1015.7384f, 1015.739f, 1015.7374f, 1015.7374f, 1015.7374f, 1015.7341f, 1015.73505f, 1015.7341f, 1015.7267f, 1015.72833f, 1015.7234f, 1015.7167f, 1015.725f, 1015.7217f, 1015.7234f, 1015.73004f, 1015.7324f, 1015.7423f, 1015.74005f, 1015.73505f, 1015.73004f, 1015.739f, 1015.7417f, 1015.74567f, 1015.74066f, 1015.74146f, 1015.74146f, 1015.73816f, 1015.7274f, 1015.72906f, 1015.7274f, 1015.7341f, 1015.739f, 1015.744f, 1015.7417f, 1015.7374f, 1015.7324f, 1015.73816f, 1015.74146f, 1015.7374f, 1015.7341f, 1015.7365f, 1015.7365f, 1015.74146f, 1015.74414f, 1015.7475f, 1015.7448f, 1015.74146f, 1015.74585f, 1015.7475f, 1015.75146f, 1015.75146f, 1015.7591f, 1015.75476f, 1015.7614f, 1015.75476f, 1015.7492f, 1015.74817f, 1015.7498f, 1015.7515f, 1015.75586f, 1015.7509f, 1015.7448f, 1015.7525f, 1015.7581f, 1015.7592f};
|
float[] sensorValues_hPa = new float[]{1015.6876f, 1015.699f, 1015.70905f, 1015.71075f, 1015.7224f, 1015.72f, 1015.7373f, 1015.7481f, 1015.74133f, 1015.73553f, 1015.7462f, 1015.74896f, 1015.752f, 1015.754f, 1015.7456f, 1015.73303f, 1015.7288f, 1015.7239f, 1015.7298f, 1015.7387f, 1015.72534f, 1015.72577f, 1015.72797f, 1015.71796f, 1015.7113f, 1015.7097f, 1015.7096f, 1015.7013f, 1015.7072f, 1015.71063f, 1015.7088f, 1015.7115f, 1015.713f, 1015.7121f, 1015.7062f, 1015.7062f, 1015.6994f, 1015.69617f, 1015.68945f, 1015.698f, 1015.6913f, 1015.69696f, 1015.69293f, 1015.6955f, 1015.6928f, 1015.6939f, 1015.68787f, 1015.6881f, 1015.68536f, 1015.68726f, 1015.6879f, 1015.6904f, 1015.6937f, 1015.69476f, 1015.7046f, 1015.7019f, 1015.7079f, 1015.71124f, 1015.71216f, 1015.71436f, 1015.7171f, 1015.7186f, 1015.7186f, 1015.7246f, 1015.72046f, 1015.7211f, 1015.72375f, 1015.7194f, 1015.72125f, 1015.7161f, 1015.71185f, 1015.7188f, 1015.721f, 1015.7252f, 1015.72266f, 1015.7236f, 1015.72614f, 1015.7266f, 1015.7344f, 1015.7376f, 1015.7337f, 1015.7253f, 1015.71875f, 1015.7219f, 1015.7219f, 1015.7317f, 1015.7284f, 1015.73444f, 1015.73584f, 1015.7369f, 1015.73254f, 1015.7369f, 1015.7336f, 1015.73254f, 1015.73676f, 1015.74176f, 1015.74506f, 1015.7478f, 1015.755f, 1015.7515f, 1015.7543f, 1015.7459f, 1015.7468f, 1015.7492f, 1015.74585f, 1015.74756f, 1015.74756f, 1015.75085f, 1015.754f, 1015.75336f, 1015.7568f, 1015.75165f, 1015.761f, 1015.7527f, 1015.75684f, 1015.7635f, 1015.7585f, 1015.7552f, 1015.7475f, 1015.74506f, 1015.7542f, 1015.75586f, 1015.74176f, 1015.74414f, 1015.7501f, 1015.751f, 1015.7478f, 1015.755f, 1015.75836f, 1015.7592f, 1015.75256f, 1015.7559f, 1015.76654f, 1015.76154f, 1015.766f, 1015.77014f, 1015.76904f, 1015.76013f, 1015.7674f, 1015.76514f, 1015.7674f, 1015.7635f, 1015.76404f, 1015.7607f, 1015.754f, 1015.7574f, 1015.7607f, 1015.7607f, 1015.7607f, 1015.754f, 1015.7567f, 1015.7616f, 1015.7643f, 1015.7666f, 1015.7609f, 1015.766f, 1015.76764f, 1015.755f, 1015.75757f, 1015.74927f, 1015.74927f, 1015.7426f, 1015.745f, 1015.745f, 1015.7409f, 1015.7376f, 1015.7326f, 1015.74164f, 1015.74664f, 1015.745f, 1015.7459f, 1015.7502f, 1015.7493f, 1015.7476f, 1015.7493f, 1015.751f, 1015.7601f, 1015.76434f, 1015.766f, 1015.756f, 1015.7617f, 1015.7623f, 1015.76404f, 1015.76733f, 1015.7607f, 1015.7623f, 1015.76337f, 1015.7584f, 1015.7573f, 1015.7502f, 1015.7534f, 1015.7534f, 1015.7601f, 1015.75684f, 1015.7518f, 1015.75574f, 1015.7524f, 1015.7507f, 1015.7484f, 1015.7502f, 1015.7534f, 1015.7623f, 1015.75903f, 1015.75574f, 1015.75574f, 1015.7573f, 1015.7524f, 1015.75574f, 1015.75574f, 1015.7607f, 1015.7573f, 1015.754f, 1015.74677f, 1015.754f, 1015.75574f, 1015.75903f, 1015.7573f, 1015.7551f, 1015.7573f, 1015.754f, 1015.7507f, 1015.7374f, 1015.739f, 1015.73566f, 1015.73566f, 1015.7367f, 1015.7417f, 1015.7417f, 1015.739f, 1015.74835f, 1015.74567f, 1015.7517f, 1015.74066f, 1015.7374f, 1015.739f, 1015.7417f, 1015.73065f, 1015.72894f, 1015.73505f, 1015.73334f, 1015.74005f, 1015.74005f, 1015.7367f, 1015.7434f, 1015.744f, 1015.7423f, 1015.7384f, 1015.739f, 1015.7374f, 1015.7374f, 1015.7374f, 1015.7341f, 1015.73505f, 1015.7341f, 1015.7267f, 1015.72833f, 1015.7234f, 1015.7167f, 1015.725f, 1015.7217f, 1015.7234f, 1015.73004f, 1015.7324f, 1015.7423f, 1015.74005f, 1015.73505f, 1015.73004f, 1015.739f, 1015.7417f, 1015.74567f, 1015.74066f, 1015.74146f, 1015.74146f, 1015.73816f, 1015.7274f, 1015.72906f, 1015.7274f, 1015.7341f, 1015.739f, 1015.744f, 1015.7417f, 1015.7374f, 1015.7324f, 1015.73816f, 1015.74146f, 1015.7374f, 1015.7341f, 1015.7365f, 1015.7365f, 1015.74146f, 1015.74414f, 1015.7475f, 1015.7448f, 1015.74146f, 1015.74585f, 1015.7475f, 1015.75146f, 1015.75146f, 1015.7591f, 1015.75476f, 1015.7614f, 1015.75476f, 1015.7492f, 1015.74817f, 1015.7498f, 1015.7515f, 1015.75586f, 1015.7509f, 1015.7448f, 1015.7525f, 1015.7581f, 1015.7592f};
|
||||||
float firstSensorValue = sensorValues_hPa[0];
|
AtmosphericPressure firstSensorValue = AtmosphericPressure.ofHPA(sensorValues_hPa[0]);
|
||||||
|
|
||||||
// when // then
|
// when // then
|
||||||
for (float v : sensorValues_hPa) {
|
for (float v : sensorValues_hPa) {
|
||||||
assertNull(PressureSensorUtils.computeChanges(firstSensorValue, v));
|
assertNull(PressureSensorUtils.computeChanges(firstSensorValue, AtmosphericPressure.ofHPA(v)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,25 +27,25 @@ public class PressureSensorUtilsTest {
|
|||||||
@Test
|
@Test
|
||||||
public void computeChanges_downhill() {
|
public void computeChanges_downhill() {
|
||||||
float[] sensorValues_hPa = new float[]{1015f, 1015.01f, 1015.02f, 1015.03f, 1015.04f, 1015.05f, 1015.06f, 1015.07f, 1015.08f, 1015.09f, 1015.10f, 1015.11f, 1015.12f, 1015.13f, 1018f, 1018.1f, 1018.1f, 1018.1f, 1018.1f};
|
float[] sensorValues_hPa = new float[]{1015f, 1015.01f, 1015.02f, 1015.03f, 1015.04f, 1015.05f, 1015.06f, 1015.07f, 1015.08f, 1015.09f, 1015.10f, 1015.11f, 1015.12f, 1015.13f, 1018f, 1018.1f, 1018.1f, 1018.1f, 1018.1f};
|
||||||
float firstSensorValue = sensorValues_hPa[0];
|
AtmosphericPressure firstSensorValue = AtmosphericPressure.ofHPA(sensorValues_hPa[0]);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
float altitudeGain_m = 0;
|
float altitudeGain_m = 0;
|
||||||
float altitudeLoss = 0;
|
float altitudeLoss = 0;
|
||||||
float lastUsedPressureValue_hPa = firstSensorValue;
|
AtmosphericPressure lastUsedPressureValue = firstSensorValue;
|
||||||
|
|
||||||
for (float v : sensorValues_hPa) {
|
for (float v : sensorValues_hPa) {
|
||||||
PressureSensorUtils.AltitudeChange altitudeChange = PressureSensorUtils.computeChanges(lastUsedPressureValue_hPa, v);
|
PressureSensorUtils.AltitudeChange altitudeChange = PressureSensorUtils.computeChanges(lastUsedPressureValue, AtmosphericPressure.ofHPA(v));
|
||||||
if (altitudeChange != null) {
|
if (altitudeChange != null) {
|
||||||
altitudeGain_m += altitudeChange.getAltitudeGain_m();
|
altitudeGain_m += altitudeChange.getAltitudeGain_m();
|
||||||
altitudeLoss += altitudeChange.getAltitudeLoss_m();
|
altitudeLoss += altitudeChange.getAltitudeLoss_m();
|
||||||
lastUsedPressureValue_hPa = altitudeChange.getCurrentSensorValue_hPa();
|
lastUsedPressureValue = altitudeChange.currentSensorValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(0f, altitudeGain_m, 0.01);
|
assertEquals(0f, altitudeGain_m, 0.01);
|
||||||
assertEquals(15.0f, altitudeLoss, 0.01);
|
assertEquals(15.0f, altitudeLoss, 0.01);
|
||||||
assertEquals(1016.80f, lastUsedPressureValue_hPa, 0.01); //Expect exponential smoothing
|
assertEquals(1016.80f, lastUsedPressureValue.getHPA(), 0.01); //Expect exponential smoothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,9 +11,40 @@ public class UintUtilsTest {
|
|||||||
assertEquals(0, UintUtils.diff(1, 1, UintUtils.UINT16_MAX));
|
assertEquals(0, UintUtils.diff(1, 1, UintUtils.UINT16_MAX));
|
||||||
assertEquals(1, UintUtils.diff(2, 1, UintUtils.UINT16_MAX));
|
assertEquals(1, UintUtils.diff(2, 1, UintUtils.UINT16_MAX));
|
||||||
assertEquals(3, UintUtils.diff(5, 2, UintUtils.UINT16_MAX));
|
assertEquals(3, UintUtils.diff(5, 2, UintUtils.UINT16_MAX));
|
||||||
assertEquals(65534, UintUtils.diff(1, 2, UintUtils.UINT16_MAX));
|
assertEquals(65535, UintUtils.diff(1, 2, UintUtils.UINT16_MAX)); /* unsigned 16 arithmetic is modulo UINT16_MAX + 1, not modulo UINT16_MAX */
|
||||||
|
|
||||||
assertEquals(65530, UintUtils.diff(UintUtils.UINT16_MAX, 5, UintUtils.UINT16_MAX));
|
assertEquals(65530, UintUtils.diff(UintUtils.UINT16_MAX, 5, UintUtils.UINT16_MAX));
|
||||||
|
|
||||||
|
/* Test modulo arithmetic for arguments that are out of range */
|
||||||
|
|
||||||
|
if (false) { /* false means UintUtils.diff() throws if arguments out of range */
|
||||||
|
assertEquals(0, UintUtils.diff(65537, 1, UintUtils.UINT16_MAX));
|
||||||
|
assertEquals(0, UintUtils.diff(1, 65537, UintUtils.UINT16_MAX));
|
||||||
|
assertEquals(0, UintUtils.diff(65537, 65537, UintUtils.UINT16_MAX));
|
||||||
|
assertEquals(0, UintUtils.diff(-65535, 1, UintUtils.UINT16_MAX));
|
||||||
|
assertEquals(0, UintUtils.diff(1, -65535, UintUtils.UINT16_MAX));
|
||||||
|
assertEquals(0, UintUtils.diff(-65535, -65535, UintUtils.UINT16_MAX));
|
||||||
|
assertEquals(0, UintUtils.diff(-65535, 65537, UintUtils.UINT16_MAX));
|
||||||
|
assertEquals(0, UintUtils.diff(65537, -65535, UintUtils.UINT16_MAX));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The following tests are the above, but for 32-bit unsigned. */
|
||||||
|
|
||||||
|
assertEquals(0, UintUtils.diff(1, 1, UintUtils.UINT32_MAX));
|
||||||
|
assertEquals(1, UintUtils.diff(2, 1, UintUtils.UINT32_MAX));
|
||||||
|
assertEquals(3, UintUtils.diff(5, 2, UintUtils.UINT32_MAX));
|
||||||
|
assertEquals(4294967295L, UintUtils.diff(1, 2, UintUtils.UINT32_MAX));
|
||||||
|
assertEquals(4294967290L, UintUtils.diff(UintUtils.UINT32_MAX, 5, UintUtils.UINT32_MAX));
|
||||||
|
|
||||||
|
if (false) { /* false means UintUtils.diff() throws if arguments out of range */
|
||||||
|
assertEquals(0, UintUtils.diff(4294967297L, 1, UintUtils.UINT32_MAX));
|
||||||
|
assertEquals(0, UintUtils.diff(1, 4294967297L, UintUtils.UINT32_MAX));
|
||||||
|
assertEquals(0, UintUtils.diff(4294967297L, 4294967297L, UintUtils.UINT32_MAX));
|
||||||
|
assertEquals(0, UintUtils.diff(-4294967295L, 1, UintUtils.UINT32_MAX));
|
||||||
|
assertEquals(0, UintUtils.diff(1, -4294967295L, UintUtils.UINT32_MAX));
|
||||||
|
assertEquals(0, UintUtils.diff(-4294967295L, -4294967295L, UintUtils.UINT32_MAX));
|
||||||
|
assertEquals(0, UintUtils.diff(-4294967295L, 4294967297L, UintUtils.UINT32_MAX));
|
||||||
|
assertEquals(0, UintUtils.diff(65537, -4294967297L, UintUtils.UINT32_MAX));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
+1
-1
@@ -30,6 +30,6 @@ public class TrackRecordingServiceTestUtils {
|
|||||||
location.setSpeed(speed);
|
location.setSpeed(speed);
|
||||||
|
|
||||||
trackPointCreator.setClock(time);
|
trackPointCreator.setClock(time);
|
||||||
trackPointCreator.getGpsHandler().onLocationChanged(location);
|
trackPointCreator.getSensorManager().getGpsManager().onLocationChanged(location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -185,7 +185,7 @@ public class VoiceAnnouncementUtilsTest {
|
|||||||
stats.setTotalAltitudeGain(6000f);
|
stats.setTotalAltitudeGain(6000f);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL, true, null, null).toString();
|
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_FEET, true, null, null).toString();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals("Total distance 12.4 miles. 1 hour 5 minutes 10 seconds. Average moving speed 11.4 miles per hour.", announcement);
|
assertEquals("Total distance 12.4 miles. 1 hour 5 minutes 10 seconds. Average moving speed 11.4 miles per hour.", announcement);
|
||||||
@@ -207,7 +207,7 @@ public class VoiceAnnouncementUtilsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL, true, lastInterval, null).toString();
|
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_FEET, true, lastInterval, null).toString();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals("Total distance 8.8 miles. 16 minutes 39 seconds. Average moving speed 31.8 miles per hour. Lap speed 31.8 miles per hour.", announcement);
|
assertEquals("Total distance 8.8 miles. 16 minutes 39 seconds. Average moving speed 31.8 miles per hour. Lap speed 31.8 miles per hour.", announcement);
|
||||||
@@ -223,7 +223,7 @@ public class VoiceAnnouncementUtilsTest {
|
|||||||
stats.setTotalAltitudeGain(6000f);
|
stats.setTotalAltitudeGain(6000f);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL, false, null, null).toString();
|
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_FEET, false, null, null).toString();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals("Total distance 12.4 miles. 1 hour 5 minutes 10 seconds. Pace 5 minutes 15 seconds per mile.", announcement);
|
assertEquals("Total distance 12.4 miles. 1 hour 5 minutes 10 seconds. Pace 5 minutes 15 seconds per mile.", announcement);
|
||||||
@@ -245,7 +245,7 @@ public class VoiceAnnouncementUtilsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// when
|
// when
|
||||||
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL, false, lastInterval, null).toString();
|
String announcement = VoiceAnnouncementUtils.getAnnouncement(context, stats, UnitSystem.IMPERIAL_FEET, false, lastInterval, null).toString();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals("Total distance 8.8 miles. 16 minutes 39 seconds. Pace 1 minute 53 seconds per mile. Lap time 1 minute 53 seconds per mile.", announcement);
|
assertEquals("Total distance 8.8 miles. 16 minutes 39 seconds. Pace 1 minute 53 seconds per mile. Lap time 1 minute 53 seconds per mile.", announcement);
|
||||||
|
|||||||
@@ -0,0 +1,146 @@
|
|||||||
|
package de.dennisguse.opentracks.services.handlers;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static de.dennisguse.opentracks.services.handlers.GpsStatusValue.GPS_DISABLED;
|
||||||
|
import static de.dennisguse.opentracks.services.handlers.GpsStatusValue.GPS_ENABLED;
|
||||||
|
import static de.dennisguse.opentracks.services.handlers.GpsStatusValue.GPS_NONE;
|
||||||
|
import static de.dennisguse.opentracks.services.handlers.GpsStatusValue.GPS_SIGNAL_BAD;
|
||||||
|
import static de.dennisguse.opentracks.services.handlers.GpsStatusValue.GPS_SIGNAL_FIX;
|
||||||
|
import static de.dennisguse.opentracks.services.handlers.GpsStatusValue.GPS_SIGNAL_LOST;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.location.Location;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.HandlerThread;
|
||||||
|
import android.os.Looper;
|
||||||
|
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.data.models.Distance;
|
||||||
|
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class GpsStatusTest {
|
||||||
|
|
||||||
|
private final Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void preSetUp() {
|
||||||
|
// Prepare looper for Android's message queue
|
||||||
|
if (Looper.myLooper() == null) Looper.prepare();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static Location badFix = new Location("gps");
|
||||||
|
private final static Location ok = new Location("gps");
|
||||||
|
|
||||||
|
static {
|
||||||
|
badFix.setAccuracy(50);
|
||||||
|
|
||||||
|
ok.setAccuracy(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStartDisabledEnabledStop() {
|
||||||
|
ArrayList<GpsStatusValue> statusList = new ArrayList<>();
|
||||||
|
|
||||||
|
// given
|
||||||
|
GpsStatusManager subject = new GpsStatusManager(context, statusList::add, new Handler());
|
||||||
|
|
||||||
|
// when / then
|
||||||
|
subject.start();
|
||||||
|
assertEquals(List.of(GPS_ENABLED), statusList);
|
||||||
|
|
||||||
|
subject.onGpsDisabled();
|
||||||
|
assertEquals(List.of(GPS_ENABLED, GPS_DISABLED), statusList);
|
||||||
|
|
||||||
|
subject.onGpsEnabled();
|
||||||
|
assertEquals(List.of(GPS_ENABLED, GPS_DISABLED, GPS_ENABLED), statusList);
|
||||||
|
|
||||||
|
subject.stop();
|
||||||
|
assertEquals(List.of(GPS_ENABLED, GPS_DISABLED, GPS_ENABLED, GPS_NONE), statusList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStartBadfixOk() {
|
||||||
|
ArrayList<GpsStatusValue> statusList = new ArrayList<>();
|
||||||
|
|
||||||
|
// given
|
||||||
|
GpsStatusManager subject = new GpsStatusManager(context, statusList::add, new Handler());
|
||||||
|
subject.onRecordingDistanceChanged(Distance.of(10));
|
||||||
|
|
||||||
|
// when / then
|
||||||
|
subject.start();
|
||||||
|
subject.onNewTrackPoint(new TrackPoint(badFix, Instant.now()));
|
||||||
|
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_BAD), statusList);
|
||||||
|
|
||||||
|
subject.onNewTrackPoint(new TrackPoint(ok, Instant.now()));
|
||||||
|
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_BAD, GPS_SIGNAL_FIX), statusList);
|
||||||
|
|
||||||
|
subject.onNewTrackPoint(new TrackPoint(ok, Instant.now()));
|
||||||
|
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_BAD, GPS_SIGNAL_FIX), statusList);
|
||||||
|
|
||||||
|
subject.onNewTrackPoint(new TrackPoint(badFix, Instant.now()));
|
||||||
|
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_BAD, GPS_SIGNAL_FIX, GPS_SIGNAL_BAD), statusList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStartSignalLost() {
|
||||||
|
ArrayList<GpsStatusValue> statusList = new ArrayList<>();
|
||||||
|
|
||||||
|
// given
|
||||||
|
GpsStatusManager subject = new GpsStatusManager(context, statusList::add, new Handler());
|
||||||
|
subject.onRecordingDistanceChanged(Distance.of(10));
|
||||||
|
subject.onMinRecordingIntervalChanged(GpsStatusManager.SIGNAL_LOST_THRESHOLD.multipliedBy(-1));
|
||||||
|
|
||||||
|
// when / then
|
||||||
|
subject.start();
|
||||||
|
subject.onNewTrackPoint(new TrackPoint(ok, Instant.now().minusMillis(1000)));
|
||||||
|
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_FIX), statusList);
|
||||||
|
|
||||||
|
subject.determineGpsStatusByTime(Instant.now());
|
||||||
|
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_FIX, GPS_SIGNAL_LOST), statusList);
|
||||||
|
|
||||||
|
subject.onNewTrackPoint(new TrackPoint(ok, Instant.now()));
|
||||||
|
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_FIX, GPS_SIGNAL_LOST, GPS_SIGNAL_FIX), statusList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStartSignalLostByTimer() throws InterruptedException {
|
||||||
|
ArrayList<GpsStatusValue> statusList = new ArrayList<>();
|
||||||
|
|
||||||
|
final HandlerThread handlerThread = new HandlerThread("solution!");
|
||||||
|
handlerThread.start();
|
||||||
|
|
||||||
|
// given
|
||||||
|
GpsStatusManager subject = new GpsStatusManager(context, statusList::add, new Handler(handlerThread.getLooper()));
|
||||||
|
subject.onRecordingDistanceChanged(Distance.of(10));
|
||||||
|
subject.onMinRecordingIntervalChanged(GpsStatusManager.SIGNAL_LOST_THRESHOLD.multipliedBy(-1).plus(Duration.ofMillis(10)));
|
||||||
|
|
||||||
|
// when / then
|
||||||
|
subject.start();
|
||||||
|
Thread.sleep(100);
|
||||||
|
assertEquals(List.of(GPS_ENABLED), statusList);
|
||||||
|
|
||||||
|
subject.onNewTrackPoint(new TrackPoint(ok, Instant.now()));
|
||||||
|
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_FIX), statusList);
|
||||||
|
|
||||||
|
Thread.sleep(100);
|
||||||
|
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_FIX, GPS_SIGNAL_LOST), statusList);
|
||||||
|
|
||||||
|
subject.onNewTrackPoint(new TrackPoint(badFix, Instant.now()));
|
||||||
|
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_FIX, GPS_SIGNAL_LOST, GPS_SIGNAL_BAD), statusList);
|
||||||
|
|
||||||
|
Thread.sleep(100);
|
||||||
|
assertEquals(List.of(GPS_ENABLED, GPS_SIGNAL_FIX, GPS_SIGNAL_LOST, GPS_SIGNAL_BAD, GPS_SIGNAL_LOST), statusList);
|
||||||
|
}
|
||||||
|
}
|
||||||
-57
@@ -1,57 +0,0 @@
|
|||||||
package de.dennisguse.opentracks.services.handlers;
|
|
||||||
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.junit.MockitoJUnitRunner;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.data.models.Distance;
|
|
||||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
|
||||||
|
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
|
||||||
public class TrackPointCreatorTest {
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private Context context;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private TrackPointCreator.Callback server;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private GPSManager locationHandler;
|
|
||||||
|
|
||||||
private TrackPointCreator subject;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
subject = new TrackPointCreator(locationHandler, server);
|
|
||||||
subject.start(context, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void tearDown() {
|
|
||||||
subject.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Ignore("ServiceExecutor disabled for #822")
|
|
||||||
@Test
|
|
||||||
public void sendTrackPoint() throws InterruptedException {
|
|
||||||
// given
|
|
||||||
TrackPoint trackPoint = new TrackPoint(TrackPoint.Type.TRACKPOINT, null);
|
|
||||||
Distance horizontalAccuracyThreshold = Distance.of(50);
|
|
||||||
|
|
||||||
// when
|
|
||||||
subject.onNewTrackPoint(trackPoint);
|
|
||||||
|
|
||||||
// then
|
|
||||||
Thread.sleep(10); // Wait for executor service
|
|
||||||
verify(server).newTrackPoint(trackPoint, horizontalAccuracyThreshold);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -80,7 +80,7 @@ public class DescriptionGeneratorTest {
|
|||||||
stats.setTotalAltitudeLoss(6000f);
|
stats.setTotalAltitudeLoss(6000f);
|
||||||
stats.setStartTime(START_TIME);
|
stats.setStartTime(START_TIME);
|
||||||
track.setTrackStatistics(stats);
|
track.setTrackStatistics(stats);
|
||||||
track.setCategory("hiking");
|
track.setActivityType("hiking");
|
||||||
String expected = //"Created by"
|
String expected = //"Created by"
|
||||||
"<a href='https://github.com/OpenTracksApp/OpenTracks'>OpenTracks (Debug)</a><p>"
|
"<a href='https://github.com/OpenTracksApp/OpenTracks'>OpenTracks (Debug)</a><p>"
|
||||||
+ "Name: -<br>"
|
+ "Name: -<br>"
|
||||||
@@ -118,7 +118,7 @@ public class DescriptionGeneratorTest {
|
|||||||
stats.setTotalAltitudeLoss(6000f);
|
stats.setTotalAltitudeLoss(6000f);
|
||||||
stats.setStartTime(START_TIME);
|
stats.setStartTime(START_TIME);
|
||||||
track.setTrackStatistics(stats);
|
track.setTrackStatistics(stats);
|
||||||
track.setCategory("hiking");
|
track.setActivityType("hiking");
|
||||||
String expected = //"Created by"
|
String expected = //"Created by"
|
||||||
"<a href='https://github.com/OpenTracksApp/OpenTracks'>OpenTracks (Debug)</a><p>"
|
"<a href='https://github.com/OpenTracksApp/OpenTracks'>OpenTracks (Debug)</a><p>"
|
||||||
+ "Name: -<br>"
|
+ "Name: -<br>"
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ public class TrackStatisticsUpdaterTest {
|
|||||||
|
|
||||||
// when
|
// when
|
||||||
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
|
TrackStatisticsUpdater subject = new TrackStatisticsUpdater();
|
||||||
data.trackPoints.forEach(subject::addTrackPoint);
|
data.trackPoints().forEach(subject::addTrackPoint);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
TrackStatistics statistics = subject.getTrackStatistics();
|
TrackStatistics statistics = subject.getTrackStatistics();
|
||||||
|
|||||||
+3
-3
@@ -41,7 +41,7 @@ public class AggregatedStatisticsTest {
|
|||||||
|
|
||||||
Track track = new Track();
|
Track track = new Track();
|
||||||
track.setIcon(TrackIconUtils.getIconValue(context, category));
|
track.setIcon(TrackIconUtils.getIconValue(context, category));
|
||||||
track.setCategory(category);
|
track.setActivityType(category);
|
||||||
track.setTrackStatistics(statistics);
|
track.setTrackStatistics(statistics);
|
||||||
return track;
|
return track;
|
||||||
}
|
}
|
||||||
@@ -243,8 +243,8 @@ public class AggregatedStatisticsTest {
|
|||||||
// Check order
|
// Check order
|
||||||
|
|
||||||
{
|
{
|
||||||
assertEquals(biking, aggregatedStatistics.getItem(0).getCategory());
|
assertEquals(biking, aggregatedStatistics.getItem(0).getActivityType());
|
||||||
assertEquals(driving, aggregatedStatistics.getItem(3).getCategory());
|
assertEquals(driving, aggregatedStatistics.getItem(3).getActivityType());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -51,7 +51,7 @@ public class EspressoAggregatedFilterTest {
|
|||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
Pair<Track, List<TrackPoint>> pair = TestDataUtil.createTrack(new Track.Id(System.currentTimeMillis()), 20);
|
Pair<Track, List<TrackPoint>> pair = TestDataUtil.createTrack(new Track.Id(System.currentTimeMillis()), 20);
|
||||||
pair.first.setCategory(CATEGORY);
|
pair.first.setActivityType(CATEGORY);
|
||||||
TestDataUtil.insertTrackWithLocations(new ContentProviderUtils(ApplicationProvider.getApplicationContext()), pair.first, pair.second);
|
TestDataUtil.insertTrackWithLocations(new ContentProviderUtils(ApplicationProvider.getApplicationContext()), pair.first, pair.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,8 +59,8 @@ public class EspressoAggregatedFilterTest {
|
|||||||
public void espressoAggregatedFilterTest() {
|
public void espressoAggregatedFilterTest() {
|
||||||
// open AggregatedStatisticsActivity through toolbar's menu item
|
// open AggregatedStatisticsActivity through toolbar's menu item
|
||||||
onView(ViewMatchers.withId(R.id.bottom_app_bar)).check(matches(isDisplayed()));
|
onView(ViewMatchers.withId(R.id.bottom_app_bar)).check(matches(isDisplayed()));
|
||||||
onView(withId(R.id.bottom_app_bar)).check(matches(hasDescendant(withId(R.id.track_list_aggregated_stats))));
|
onView(withId(R.id.bottom_app_bar)).check(matches(hasDescendant(withId(R.id.aggregated_stats_button))));
|
||||||
onView(withId(R.id.track_list_aggregated_stats)).perform(click());
|
onView(withId(R.id.aggregated_stats_button)).perform(click());
|
||||||
|
|
||||||
// open FilterDialogFragment through toolbar's menu item
|
// open FilterDialogFragment through toolbar's menu item
|
||||||
onView(withId(R.id.bottom_app_bar)).check(matches(isDisplayed()));
|
onView(withId(R.id.bottom_app_bar)).check(matches(isDisplayed()));
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ public class EGM2008UtilsTest {
|
|||||||
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(-14.8980, altitude_egm2008.correctAltitude(trackPoint.getLocation()), MAX_BILINEAR_ERROR);
|
assertEquals(-14.8980, altitude_egm2008.correctAltitude(trackPoint.getLocation()).toM(), MAX_BILINEAR_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -94,7 +94,7 @@ public class EGM2008UtilsTest {
|
|||||||
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(30.15, altitude_egm2008.correctAltitude(trackPoint.getLocation()), MAX_BILINEAR_ERROR);
|
assertEquals(30.15, altitude_egm2008.correctAltitude(trackPoint.getLocation()).toM(), MAX_BILINEAR_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -109,7 +109,7 @@ public class EGM2008UtilsTest {
|
|||||||
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(30.15, altitude_egm2008.correctAltitude(trackPoint.getLocation()), MAX_BILINEAR_ERROR);
|
assertEquals(30.15, altitude_egm2008.correctAltitude(trackPoint.getLocation()).toM(), MAX_BILINEAR_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -124,7 +124,7 @@ public class EGM2008UtilsTest {
|
|||||||
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(30.15, altitude_egm2008.correctAltitude(trackPoint.getLocation()), MAX_BILINEAR_ERROR);
|
assertEquals(30.15, altitude_egm2008.correctAltitude(trackPoint.getLocation()).toM(), MAX_BILINEAR_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -139,7 +139,7 @@ public class EGM2008UtilsTest {
|
|||||||
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(-17.2260, altitude_egm2008.correctAltitude(trackPoint.getLocation()), MAX_BILINEAR_ERROR);
|
assertEquals(-17.2260, altitude_egm2008.correctAltitude(trackPoint.getLocation()).toM(), MAX_BILINEAR_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -154,7 +154,7 @@ public class EGM2008UtilsTest {
|
|||||||
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(-39.4865, altitude_egm2008.correctAltitude(trackPoint.getLocation()), MAX_BILINEAR_ERROR);
|
assertEquals(-39.4865, altitude_egm2008.correctAltitude(trackPoint.getLocation()).toM(), MAX_BILINEAR_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -174,7 +174,7 @@ public class EGM2008UtilsTest {
|
|||||||
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint1.getLocation());
|
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint1.getLocation());
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertNotEquals(altitude_egm2008.correctAltitude(trackPoint1.getLocation()), altitude_egm2008.correctAltitude(trackPoint2.getLocation()), 0.0001);
|
assertNotEquals(altitude_egm2008.correctAltitude(trackPoint1.getLocation()).toM(), altitude_egm2008.correctAltitude(trackPoint2.getLocation()).toM(), 0.0001);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -190,7 +190,7 @@ public class EGM2008UtilsTest {
|
|||||||
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(22.99, altitude_egm2008.correctAltitude(trackPoint.getLocation()), MAX_BILINEAR_ERROR);
|
assertEquals(22.99, altitude_egm2008.correctAltitude(trackPoint.getLocation()).toM(), MAX_BILINEAR_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -205,7 +205,7 @@ public class EGM2008UtilsTest {
|
|||||||
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
EGM2008Utils.EGM2008Correction altitude_egm2008 = EGM2008Utils.createCorrection(context, trackPoint.getLocation());
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertEquals(-85.824, altitude_egm2008.correctAltitude(trackPoint.getLocation()), MAX_BILINEAR_ERROR);
|
assertEquals(-85.824, altitude_egm2008.correctAltitude(trackPoint.getLocation()).toM(), MAX_BILINEAR_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,11 +17,14 @@ package de.dennisguse.opentracks.util;
|
|||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.junit.runners.JUnit4;
|
import org.junit.runners.JUnit4;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -37,7 +40,7 @@ public class FileUtilsTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testBuildUniqueFileName_new() {
|
public void testBuildUniqueFileName_new() {
|
||||||
String filename = FileUtils.buildUniqueFileName(new File("/dir"), "Filename", "ext");
|
String filename = FileUtils.buildUniqueFileName(FileUtils.getPhotoDir(ApplicationProvider.getApplicationContext()), "Filename", "ext");
|
||||||
assertEquals("Filename.ext", filename);
|
assertEquals("Filename.ext", filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,9 +48,14 @@ public class FileUtilsTest {
|
|||||||
* Tests {@link FileUtils#buildUniqueFileName(File, String, String)} when the file exists already.
|
* Tests {@link FileUtils#buildUniqueFileName(File, String, String)} when the file exists already.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testBuildUniqueFileName_exist() {
|
public void testBuildUniqueFileName_exist() throws IOException {
|
||||||
// Expect "/default.prop" to exist on the phone/emulator
|
// given
|
||||||
String filename = FileUtils.buildUniqueFileName(new File("/"), "default", "prop");
|
new File(FileUtils.getPhotoDir(ApplicationProvider.getApplicationContext()), "default.prop").createNewFile();
|
||||||
|
|
||||||
|
//when
|
||||||
|
String filename = FileUtils.buildUniqueFileName(FileUtils.getPhotoDir(ApplicationProvider.getApplicationContext()), "default", "prop");
|
||||||
|
|
||||||
|
// then
|
||||||
assertEquals("default(1).prop", filename);
|
assertEquals("default(1).prop", filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<shortcut
|
||||||
|
android:enabled="true"
|
||||||
|
android:icon="@drawable/ic_statistics_24dp"
|
||||||
|
android:shortcutId="stats"
|
||||||
|
android:shortcutLongLabel="@string/menu_aggregated_statistics"
|
||||||
|
android:shortcutShortLabel="@string/menu_aggregated_statistics">
|
||||||
|
|
||||||
|
<intent
|
||||||
|
android:action="android.intent.action.VIEW"
|
||||||
|
android:targetClass="de.dennisguse.opentracks.ui.aggregatedStatistics.AggregatedStatisticsActivity"
|
||||||
|
android:targetPackage="de.dennisguse.opentracks.debug" />
|
||||||
|
</shortcut>
|
||||||
|
</shortcuts>
|
||||||
@@ -89,6 +89,7 @@ limitations under the License.
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<!-- NOTE: the name of the MAIN/LAUNCHER is used by icon packs; if this is changed, icon packs break.-->
|
||||||
<activity
|
<activity
|
||||||
android:name=".introduction.IntroductionActivity"
|
android:name=".introduction.IntroductionActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
@@ -98,6 +99,11 @@ limitations under the License.
|
|||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="android.app.shortcuts"
|
||||||
|
android:resource="@xml/shortcuts"
|
||||||
|
/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity android:name=".AboutActivity" />
|
<activity android:name=".AboutActivity" />
|
||||||
@@ -348,6 +354,7 @@ limitations under the License.
|
|||||||
<activity android:name=".ui.aggregatedStatistics.AggregatedStatisticsActivity" />
|
<activity android:name=".ui.aggregatedStatistics.AggregatedStatisticsActivity" />
|
||||||
|
|
||||||
<activity android:name=".io.file.importer.DirectoryChooserActivity$ExportDirectoryChooserActivity" />
|
<activity android:name=".io.file.importer.DirectoryChooserActivity$ExportDirectoryChooserActivity" />
|
||||||
|
<activity android:name=".io.file.importer.DirectoryChooserActivity$ExportDirectoryChooserOneFileActivity" />
|
||||||
<activity android:name=".io.file.importer.DirectoryChooserActivity$DefaultTrackExportDirectoryChooserActivity" />
|
<activity android:name=".io.file.importer.DirectoryChooserActivity$DefaultTrackExportDirectoryChooserActivity" />
|
||||||
|
|
||||||
<activity android:name=".io.file.exporter.ExportActivity" />
|
<activity android:name=".io.file.exporter.ExportActivity" />
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public class ShowErrorActivity extends AbstractActivity {
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
viewBinding.textViewError.setText(getIntent().getStringExtra(EXTRA_ERROR_TEXT));
|
viewBinding.textViewError.setText(getIntent().getStringExtra(EXTRA_ERROR_TEXT));
|
||||||
viewBinding.bottomAppBarLayout.bottomAppBarTitle.setText(createErrorTitle());
|
viewBinding.showErrorToolbar.setTitle(createErrorTitle());
|
||||||
setSupportActionBar(viewBinding.bottomAppBarLayout.bottomAppBar);
|
setSupportActionBar(viewBinding.bottomAppBarLayout.bottomAppBar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
|
|||||||
|
|
||||||
viewBinding.fields.trackEditName.setText(track.getName());
|
viewBinding.fields.trackEditName.setText(track.getName());
|
||||||
|
|
||||||
viewBinding.fields.trackEditActivityType.setText(track.getCategory());
|
viewBinding.fields.trackEditActivityType.setText(track.getActivityType());
|
||||||
|
|
||||||
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_dropdown_item_1line, ActivityType.getLocalizedStrings(this));
|
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_dropdown_item_1line, ActivityType.getLocalizedStrings(this));
|
||||||
viewBinding.fields.trackEditActivityType.setAdapter(adapter);
|
viewBinding.fields.trackEditActivityType.setAdapter(adapter);
|
||||||
@@ -109,7 +109,6 @@ public class TrackEditActivity extends AbstractActivity implements ChooseActivit
|
|||||||
viewBinding.trackEditCancel.setOnClickListener(v -> finish());
|
viewBinding.trackEditCancel.setOnClickListener(v -> finish());
|
||||||
viewBinding.trackEditCancel.setVisibility(View.VISIBLE);
|
viewBinding.trackEditCancel.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
viewBinding.bottomAppBarLayout.bottomAppBarTitle.setText(getString(R.string.menu_edit));
|
|
||||||
setSupportActionBar(viewBinding.bottomAppBarLayout.bottomAppBar);
|
setSupportActionBar(viewBinding.bottomAppBarLayout.bottomAppBar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import android.database.Cursor;
|
|||||||
import android.graphics.drawable.AnimatedVectorDrawable;
|
import android.graphics.drawable.AnimatedVectorDrawable;
|
||||||
import android.location.LocationManager;
|
import android.location.LocationManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Vibrator;
|
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
@@ -36,6 +35,7 @@ import android.widget.Toast;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.content.res.AppCompatResources;
|
||||||
import androidx.appcompat.widget.SearchView;
|
import androidx.appcompat.widget.SearchView;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.cursoradapter.widget.ResourceCursorAdapter;
|
import androidx.cursoradapter.widget.ResourceCursorAdapter;
|
||||||
@@ -43,12 +43,12 @@ 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 com.google.android.material.button.MaterialButton;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.time.ZoneOffset;
|
import java.time.ZoneOffset;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.data.models.Distance;
|
import de.dennisguse.opentracks.data.models.Distance;
|
||||||
import de.dennisguse.opentracks.data.models.DistanceFormatter;
|
import de.dennisguse.opentracks.data.models.DistanceFormatter;
|
||||||
@@ -83,7 +83,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
|
|
||||||
private static final String TAG = TrackListActivity.class.getSimpleName();
|
private static final String TAG = TrackListActivity.class.getSimpleName();
|
||||||
|
|
||||||
// The following are setFrequency in onCreate
|
// The following are set in onCreate
|
||||||
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||||
private ResourceCursorAdapter resourceCursorAdapter;
|
private ResourceCursorAdapter resourceCursorAdapter;
|
||||||
|
|
||||||
@@ -137,7 +137,6 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
|
|
||||||
// Menu items
|
// Menu items
|
||||||
private MenuItem searchMenuItem;
|
private MenuItem searchMenuItem;
|
||||||
private MenuItem startGpsMenuItem;
|
|
||||||
|
|
||||||
private final TrackRecordingServiceConnection.Callback bindChangedCallback = (service, unused) -> {
|
private final TrackRecordingServiceConnection.Callback bindChangedCallback = (service, unused) -> {
|
||||||
service.getRecordingStatusObservable()
|
service.getRecordingStatusObservable()
|
||||||
@@ -165,6 +164,25 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
|
|
||||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback);
|
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback);
|
||||||
|
|
||||||
|
viewBinding.aggregatedStatsButton.setOnClickListener((view) -> startActivity(IntentUtils.newIntent(this, AggregatedStatisticsActivity.class)));
|
||||||
|
viewBinding.sensorStartButton.setOnClickListener((view) -> {
|
||||||
|
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
|
||||||
|
if (locationManager != null && !locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
|
||||||
|
startActivity(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));
|
||||||
|
} else {
|
||||||
|
// Invoke trackRecordingService
|
||||||
|
if (!gpsStatusValue.isGpsStarted()) {
|
||||||
|
trackRecordingServiceConnection.startAndBindWithCallback(this);
|
||||||
|
} else {
|
||||||
|
TrackRecordingService trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
||||||
|
if (trackRecordingService != null) {
|
||||||
|
trackRecordingService.stopSensorsAndShutdown(); //TODO Handle this in TrackRecordingServiceConnection
|
||||||
|
}
|
||||||
|
trackRecordingServiceConnection.unbindAndStop(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
viewBinding.trackList.setEmptyView(viewBinding.trackListEmptyView);
|
viewBinding.trackList.setEmptyView(viewBinding.trackListEmptyView);
|
||||||
viewBinding.trackList.setOnItemClickListener((parent, view, position, trackIdId) -> {
|
viewBinding.trackList.setOnItemClickListener((parent, view, position, trackIdId) -> {
|
||||||
Track.Id trackId = new Track.Id(trackIdId);
|
Track.Id trackId = new Track.Id(trackIdId);
|
||||||
@@ -194,7 +212,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
int totalDistanceIndex = cursor.getColumnIndexOrThrow(TracksColumns.TOTALDISTANCE);
|
int totalDistanceIndex = cursor.getColumnIndexOrThrow(TracksColumns.TOTALDISTANCE);
|
||||||
int startTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME);
|
int startTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME);
|
||||||
int startTimeOffsetIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME_OFFSET);
|
int startTimeOffsetIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME_OFFSET);
|
||||||
int categoryIndex = cursor.getColumnIndexOrThrow(TracksColumns.CATEGORY);
|
int activityTypeIndex = cursor.getColumnIndexOrThrow(TracksColumns.ACTIVITY_TYPE);
|
||||||
int descriptionIndex = cursor.getColumnIndexOrThrow(TracksColumns.DESCRIPTION);
|
int descriptionIndex = cursor.getColumnIndexOrThrow(TracksColumns.DESCRIPTION);
|
||||||
int markerCountIndex = cursor.getColumnIndexOrThrow(TracksColumns.MARKER_COUNT);
|
int markerCountIndex = cursor.getColumnIndexOrThrow(TracksColumns.MARKER_COUNT);
|
||||||
|
|
||||||
@@ -210,13 +228,13 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
int markerCount = cursor.getInt(markerCountIndex);
|
int markerCount = cursor.getInt(markerCountIndex);
|
||||||
long startTime = cursor.getLong(startTimeIndex);
|
long startTime = cursor.getLong(startTimeIndex);
|
||||||
int startTimeOffset = cursor.getInt(startTimeOffsetIndex);
|
int startTimeOffset = cursor.getInt(startTimeOffsetIndex);
|
||||||
String category = icon != null && !icon.equals("") ? null : cursor.getString(categoryIndex);
|
String activityType = icon != null && !icon.equals("") ? null : cursor.getString(activityTypeIndex);
|
||||||
String description = cursor.getString(descriptionIndex);
|
String description = cursor.getString(descriptionIndex);
|
||||||
|
|
||||||
ListItemUtils.setListItem(TrackListActivity.this, view, isRecording,
|
ListItemUtils.setListItem(TrackListActivity.this, view, isRecording,
|
||||||
iconId, R.string.image_track, name, totalTime, totalDistance, markerCount,
|
iconId, R.string.image_track, name, totalTime, totalDistance, markerCount,
|
||||||
OffsetDateTime.ofInstant(Instant.ofEpochMilli(startTime), ZoneOffset.ofTotalSeconds(startTimeOffset)),
|
OffsetDateTime.ofInstant(Instant.ofEpochMilli(startTime), ZoneOffset.ofTotalSeconds(startTimeOffset)),
|
||||||
category, description, false);
|
activityType, description, false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
viewBinding.trackList.setAdapter(resourceCursorAdapter);
|
viewBinding.trackList.setAdapter(resourceCursorAdapter);
|
||||||
@@ -246,7 +264,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Recording -> Stop
|
// Recording -> Stop
|
||||||
((Vibrator) getSystemService(Context.VIBRATOR_SERVICE)).vibrate(1000);
|
ActivityUtils.vibrate(this, 1000);
|
||||||
updateGpsMenuItem(false, false);
|
updateGpsMenuItem(false, false);
|
||||||
trackRecordingServiceConnection.stopRecording(TrackListActivity.this);
|
trackRecordingServiceConnection.stopRecording(TrackListActivity.this);
|
||||||
viewBinding.trackListFabAction.setImageResource(R.drawable.ic_baseline_record_24);
|
viewBinding.trackListFabAction.setImageResource(R.drawable.ic_baseline_record_24);
|
||||||
@@ -254,7 +272,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
setSupportActionBar(viewBinding.bottomAppBar);
|
setSupportActionBar(viewBinding.trackListToolbar);
|
||||||
|
|
||||||
loadData(getIntent());
|
loadData(getIntent());
|
||||||
}
|
}
|
||||||
@@ -309,21 +327,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
getMenuInflater().inflate(R.menu.track_list, menu);
|
getMenuInflater().inflate(R.menu.track_list, menu);
|
||||||
|
|
||||||
searchMenuItem = menu.findItem(R.id.track_list_search);
|
searchMenuItem = menu.findItem(R.id.track_list_search);
|
||||||
SearchView searchView = ActivityUtils.configureSearchWidget(this, searchMenuItem);
|
ActivityUtils.configureSearchWidget(this, searchMenuItem);
|
||||||
|
|
||||||
searchView.findViewById(androidx.appcompat.R.id.search_edit_frame).setPadding(0, 0, 48, 0);
|
|
||||||
|
|
||||||
SearchView.SearchAutoComplete searchAutoComplete = searchView.findViewById(androidx.appcompat.R.id.search_src_text);
|
|
||||||
searchAutoComplete.setHintTextColor(ContextCompat.getColor(this, android.R.color.white));
|
|
||||||
searchAutoComplete.setTextColor(ContextCompat.getColor(this, android.R.color.white));
|
|
||||||
|
|
||||||
searchView.setOnCloseListener(() -> {
|
|
||||||
searchView.clearFocus();
|
|
||||||
searchMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
startGpsMenuItem = menu.findItem(R.id.track_list_start_gps);
|
|
||||||
|
|
||||||
return super.onCreateOptionsMenu(menu);
|
return super.onCreateOptionsMenu(menu);
|
||||||
}
|
}
|
||||||
@@ -340,34 +344,6 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
if (item.getItemId() == R.id.track_list_start_gps) {
|
|
||||||
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
|
|
||||||
if (locationManager != null && !locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
|
|
||||||
startActivity(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));
|
|
||||||
} else {
|
|
||||||
// Invoke trackRecordingService
|
|
||||||
if (!gpsStatusValue.isGpsStarted()) {
|
|
||||||
trackRecordingServiceConnection.startAndBindWithCallback(this);
|
|
||||||
} else {
|
|
||||||
TrackRecordingService trackRecordingService = trackRecordingServiceConnection.getServiceIfBound();
|
|
||||||
if (trackRecordingService != null) {
|
|
||||||
trackRecordingService.stopSensorsAndShutdown(); //TODO Handle this in TrackRecordingServiceConnection
|
|
||||||
}
|
|
||||||
trackRecordingServiceConnection.unbindAndStop(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update menu after starting or stopping gps
|
|
||||||
this.invalidateOptionsMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.getItemId() == R.id.track_list_aggregated_stats) {
|
|
||||||
startActivity(IntentUtils.newIntent(this, AggregatedStatisticsActivity.class));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.getItemId() == R.id.track_list_markers) {
|
if (item.getItemId() == R.id.track_list_markers) {
|
||||||
startActivity(IntentUtils.newIntent(this, MarkerListActivity.class));
|
startActivity(IntentUtils.newIntent(this, MarkerListActivity.class));
|
||||||
return true;
|
return true;
|
||||||
@@ -382,6 +358,12 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
SearchView searchView = (SearchView) searchMenuItem.getActionView();
|
SearchView searchView = (SearchView) searchMenuItem.getActionView();
|
||||||
searchView.setIconified(false);
|
searchView.setIconified(false);
|
||||||
searchMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
searchMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.getItemId() == R.id.track_list_help) {
|
||||||
|
startActivity(IntentUtils.newIntent(this, HelpActivity.class));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
@@ -455,17 +437,15 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
*/
|
*/
|
||||||
//TODO Check if if can be avoided to call this outside of onGpsStatusChanged()
|
//TODO Check if if can be avoided to call this outside of onGpsStatusChanged()
|
||||||
private void updateGpsMenuItem(boolean isGpsStarted, boolean isRecording) {
|
private void updateGpsMenuItem(boolean isGpsStarted, boolean isRecording) {
|
||||||
if (startGpsMenuItem != null) {
|
MaterialButton startGpsMenuItem = viewBinding.sensorStartButton;
|
||||||
startGpsMenuItem.setVisible(!isRecording);
|
startGpsMenuItem.setVisibility(!isRecording ? View.VISIBLE : View.INVISIBLE);
|
||||||
if (!isRecording) {
|
if (!isRecording) {
|
||||||
startGpsMenuItem.setTitle(isGpsStarted ? R.string.menu_stop_gps : R.string.menu_start_gps);
|
startGpsMenuItem.setIcon(AppCompatResources.getDrawable(this, isGpsStarted ? gpsStatusValue.icon : R.drawable.ic_gps_off_24dp));
|
||||||
startGpsMenuItem.setIcon(isGpsStarted ? gpsStatusValue.icon : R.drawable.ic_gps_off_24dp);
|
|
||||||
if (startGpsMenuItem.getIcon() instanceof AnimatedVectorDrawable) {
|
if (startGpsMenuItem.getIcon() instanceof AnimatedVectorDrawable) {
|
||||||
((AnimatedVectorDrawable) startGpsMenuItem.getIcon()).start();
|
((AnimatedVectorDrawable) startGpsMenuItem.getIcon()).start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a context item selection.
|
* Handles a context item selection.
|
||||||
@@ -504,13 +484,6 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itemId == R.id.list_context_menu_aggregated_stats) {
|
|
||||||
Intent intent = IntentUtils.newIntent(this, AggregatedStatisticsActivity.class)
|
|
||||||
.putParcelableArrayListExtra(AggregatedStatisticsActivity.EXTRA_TRACK_IDS, new ArrayList<>(Arrays.asList(trackIds)));
|
|
||||||
startActivity(intent);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (itemId == R.id.list_context_menu_select_all) {
|
if (itemId == R.id.list_context_menu_select_all) {
|
||||||
for (int i = 0; i < viewBinding.trackList.getCount(); i++) {
|
for (int i = 0; i < viewBinding.trackList.getCount(); i++) {
|
||||||
viewBinding.trackList.setItemChecked(i, true);
|
viewBinding.trackList.setItemChecked(i, true);
|
||||||
@@ -532,11 +505,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
public void setSearch(String searchQuery) {
|
public void setSearch(String searchQuery) {
|
||||||
this.searchQuery = searchQuery;
|
this.searchQuery = searchQuery;
|
||||||
restart();
|
restart();
|
||||||
if (searchQuery != null) {
|
viewBinding.trackListToolbar.setTitle(searchQuery == null ? getString(R.string.app_name) : searchQuery);
|
||||||
setTitle(searchQuery);
|
|
||||||
} else {
|
|
||||||
setTitle(R.string.app_name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void restart() {
|
public void restart() {
|
||||||
@@ -547,7 +516,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
@Override
|
@Override
|
||||||
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
|
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
|
||||||
final String[] PROJECTION = new String[]{TracksColumns._ID, TracksColumns.NAME,
|
final String[] PROJECTION = new String[]{TracksColumns._ID, TracksColumns.NAME,
|
||||||
TracksColumns.DESCRIPTION, TracksColumns.CATEGORY, TracksColumns.STARTTIME, TracksColumns.STARTTIME_OFFSET,
|
TracksColumns.DESCRIPTION, TracksColumns.ACTIVITY_TYPE, TracksColumns.STARTTIME, TracksColumns.STARTTIME_OFFSET,
|
||||||
TracksColumns.TOTALDISTANCE, TracksColumns.TOTALTIME, TracksColumns.ICON, TracksColumns.MARKER_COUNT};
|
TracksColumns.TOTALDISTANCE, TracksColumns.TOTALTIME, TracksColumns.ICON, TracksColumns.MARKER_COUNT};
|
||||||
|
|
||||||
final String sortOrder = TracksColumns.STARTTIME + " DESC";
|
final String sortOrder = TracksColumns.STARTTIME + " DESC";
|
||||||
@@ -557,7 +526,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
} else {
|
} else {
|
||||||
final String SEARCH_QUERY = TracksColumns.NAME + " LIKE ? OR " +
|
final String SEARCH_QUERY = TracksColumns.NAME + " LIKE ? OR " +
|
||||||
TracksColumns.DESCRIPTION + " LIKE ? OR " +
|
TracksColumns.DESCRIPTION + " LIKE ? OR " +
|
||||||
TracksColumns.CATEGORY + " LIKE ?";
|
TracksColumns.ACTIVITY_TYPE + " LIKE ?";
|
||||||
final String[] selectionArgs = new String[]{"%" + searchQuery + "%", "%" + searchQuery + "%", "%" + searchQuery + "%"};
|
final String[] selectionArgs = new String[]{"%" + searchQuery + "%", "%" + searchQuery + "%", "%" + searchQuery + "%"};
|
||||||
return new CursorLoader(TrackListActivity.this, TracksColumns.CONTENT_URI, PROJECTION, SEARCH_QUERY, selectionArgs, sortOrder);
|
return new CursorLoader(TrackListActivity.this, TracksColumns.CONTENT_URI, PROJECTION, SEARCH_QUERY, selectionArgs, sortOrder);
|
||||||
}
|
}
|
||||||
@@ -581,7 +550,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
|
|
||||||
private void setFloatButton() {
|
private void setFloatButton() {
|
||||||
viewBinding.trackListFabAction.setImageResource(recordingStatus.isRecording() ? R.drawable.ic_baseline_stop_24 : R.drawable.ic_baseline_record_24);
|
viewBinding.trackListFabAction.setImageResource(recordingStatus.isRecording() ? R.drawable.ic_baseline_stop_24 : R.drawable.ic_baseline_record_24);
|
||||||
viewBinding.trackListFabAction.setBackgroundTintList(ContextCompat.getColorStateList(this, recordingStatus.isRecording() ? R.color.opentracks_secondary_color : R.color.red_dark));
|
viewBinding.trackListFabAction.setBackgroundTintList(ContextCompat.getColorStateList(this, recordingStatus.isRecording() ? R.color.opentracks : R.color.red_dark));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onRecordingStatusChanged(RecordingStatus status) {
|
private void onRecordingStatusChanged(RecordingStatus status) {
|
||||||
|
|||||||
@@ -103,7 +103,6 @@ public class TrackRecordedActivity extends AbstractTrackDeleteActivity implement
|
|||||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindCallback);
|
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindCallback);
|
||||||
|
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
viewBinding.bottomAppBarLayout.bottomAppBarTitle.setText(track != null ? track.getName() : "");
|
|
||||||
viewBinding.bottomAppBarLayout.bottomAppBar.replaceMenu(R.menu.track_detail);
|
viewBinding.bottomAppBarLayout.bottomAppBar.replaceMenu(R.menu.track_detail);
|
||||||
setSupportActionBar(viewBinding.bottomAppBarLayout.bottomAppBar);
|
setSupportActionBar(viewBinding.bottomAppBarLayout.bottomAppBar);
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
package de.dennisguse.opentracks;
|
package de.dennisguse.opentracks;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Vibrator;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
@@ -45,6 +43,7 @@ import de.dennisguse.opentracks.settings.SettingsActivity;
|
|||||||
import de.dennisguse.opentracks.ui.intervals.IntervalsFragment;
|
import de.dennisguse.opentracks.ui.intervals.IntervalsFragment;
|
||||||
import de.dennisguse.opentracks.ui.markers.MarkerEditActivity;
|
import de.dennisguse.opentracks.ui.markers.MarkerEditActivity;
|
||||||
import de.dennisguse.opentracks.ui.markers.MarkerListActivity;
|
import de.dennisguse.opentracks.ui.markers.MarkerListActivity;
|
||||||
|
import de.dennisguse.opentracks.ui.util.ActivityUtils;
|
||||||
import de.dennisguse.opentracks.util.IntentDashboardUtils;
|
import de.dennisguse.opentracks.util.IntentDashboardUtils;
|
||||||
import de.dennisguse.opentracks.util.IntentUtils;
|
import de.dennisguse.opentracks.util.IntentUtils;
|
||||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||||
@@ -134,10 +133,10 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
}
|
}
|
||||||
|
|
||||||
viewBinding.trackRecordingFabAction.setImageResource(R.drawable.ic_baseline_stop_24);
|
viewBinding.trackRecordingFabAction.setImageResource(R.drawable.ic_baseline_stop_24);
|
||||||
viewBinding.trackRecordingFabAction.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.opentracks_secondary_color));
|
viewBinding.trackRecordingFabAction.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.opentracks));
|
||||||
viewBinding.trackRecordingFabAction.setBackgroundColor(ContextCompat.getColor(this, R.color.opentracks_secondary_color));
|
viewBinding.trackRecordingFabAction.setBackgroundColor(ContextCompat.getColor(this, R.color.opentracks));
|
||||||
viewBinding.trackRecordingFabAction.setOnLongClickListener((view) -> {
|
viewBinding.trackRecordingFabAction.setOnLongClickListener((view) -> {
|
||||||
((Vibrator) getSystemService(Context.VIBRATOR_SERVICE)).vibrate(1000);
|
ActivityUtils.vibrate(this, 1000);
|
||||||
trackRecordingServiceConnection.stopRecording(TrackRecordingActivity.this);
|
trackRecordingServiceConnection.stopRecording(TrackRecordingActivity.this);
|
||||||
Intent newIntent = IntentUtils.newIntent(TrackRecordingActivity.this, TrackStoppedActivity.class)
|
Intent newIntent = IntentUtils.newIntent(TrackRecordingActivity.this, TrackStoppedActivity.class)
|
||||||
.putExtra(TrackStoppedActivity.EXTRA_TRACK_ID, trackId);
|
.putExtra(TrackStoppedActivity.EXTRA_TRACK_ID, trackId);
|
||||||
@@ -320,8 +319,8 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
@Override
|
@Override
|
||||||
public void onChooseActivityTypeDone(String iconValue) {
|
public void onChooseActivityTypeDone(String iconValue) {
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
Track track = contentProviderUtils.getTrack(trackId);
|
||||||
String category = getString(TrackIconUtils.getIconActivityType(iconValue));
|
String activityType = getString(TrackIconUtils.getIconActivityType(iconValue));
|
||||||
TrackUtils.updateTrack(this, track, null, category, null, contentProviderUtils);
|
TrackUtils.updateTrack(this, track, null, activityType, null, contentProviderUtils);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CustomFragmentPagerAdapter extends FragmentStateAdapter {
|
private class CustomFragmentPagerAdapter extends FragmentStateAdapter {
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ public class TrackStoppedActivity extends AbstractTrackDeleteActivity implements
|
|||||||
|
|
||||||
viewBinding.trackEditName.setText(track.getName());
|
viewBinding.trackEditName.setText(track.getName());
|
||||||
|
|
||||||
viewBinding.trackEditActivityType.setText(track.getCategory());
|
viewBinding.trackEditActivityType.setText(track.getActivityType());
|
||||||
|
|
||||||
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_dropdown_item_1line, ActivityType.getLocalizedStrings(this));
|
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_dropdown_item_1line, ActivityType.getLocalizedStrings(this));
|
||||||
viewBinding.trackEditActivityType.setAdapter(adapter);
|
viewBinding.trackEditActivityType.setAdapter(adapter);
|
||||||
@@ -74,7 +74,7 @@ public class TrackStoppedActivity extends AbstractTrackDeleteActivity implements
|
|||||||
{
|
{
|
||||||
Pair<String, String> parts = SpeedFormatter.Builder()
|
Pair<String, String> parts = SpeedFormatter.Builder()
|
||||||
.setUnit(PreferencesUtils.getUnitSystem())
|
.setUnit(PreferencesUtils.getUnitSystem())
|
||||||
.setReportSpeedOrPace(PreferencesUtils.isReportSpeed(track.getCategory()))
|
.setReportSpeedOrPace(PreferencesUtils.isReportSpeed(track.getActivityType()))
|
||||||
.build(this)
|
.build(this)
|
||||||
.getSpeedParts(track.getTrackStatistics().getAverageMovingSpeed());
|
.getSpeedParts(track.getTrackStatistics().getAverageMovingSpeed());
|
||||||
viewBinding.speed.setText(parts.first);
|
viewBinding.speed.setText(parts.first);
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ public class ChartFragment extends Fragment implements TrackDataHub.Listener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
category = track.getCategory();
|
category = track.getActivityType();
|
||||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(category);
|
boolean reportSpeed = PreferencesUtils.isReportSpeed(category);
|
||||||
if (reportSpeed != viewBinding.chartView.getReportSpeed()) {
|
if (reportSpeed != viewBinding.chartView.getReportSpeed()) {
|
||||||
viewBinding.chartView.setReportSpeed(reportSpeed);
|
viewBinding.chartView.setReportSpeed(reportSpeed);
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ abstract class ChartValueSeries {
|
|||||||
int getTitleId(UnitSystem unitSystem) {
|
int getTitleId(UnitSystem unitSystem) {
|
||||||
return switch (unitSystem) {
|
return switch (unitSystem) {
|
||||||
case METRIC -> metricTitleId;
|
case METRIC -> metricTitleId;
|
||||||
case IMPERIAL -> imperialTitleId;
|
case IMPERIAL_FEET, IMPERIAL_METER -> imperialTitleId;
|
||||||
case NAUTICAL_IMPERIAL -> nauticalTitleId;
|
case NAUTICAL_IMPERIAL -> nauticalTitleId;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -735,7 +735,7 @@ public class ChartView extends View {
|
|||||||
if (chartByDistance) {
|
if (chartByDistance) {
|
||||||
return switch (unitSystem) {
|
return switch (unitSystem) {
|
||||||
case METRIC -> context.getString(R.string.unit_kilometer);
|
case METRIC -> context.getString(R.string.unit_kilometer);
|
||||||
case IMPERIAL -> context.getString(R.string.unit_mile);
|
case IMPERIAL_FEET, IMPERIAL_METER -> context.getString(R.string.unit_mile);
|
||||||
case NAUTICAL_IMPERIAL -> context.getString(R.string.unit_nautical_mile);
|
case NAUTICAL_IMPERIAL -> context.getString(R.string.unit_nautical_mile);
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ public class ContentProviderUtils {
|
|||||||
int uuidIndex = cursor.getColumnIndexOrThrow(TracksColumns.UUID);
|
int uuidIndex = cursor.getColumnIndexOrThrow(TracksColumns.UUID);
|
||||||
int nameIndex = cursor.getColumnIndexOrThrow(TracksColumns.NAME);
|
int nameIndex = cursor.getColumnIndexOrThrow(TracksColumns.NAME);
|
||||||
int descriptionIndex = cursor.getColumnIndexOrThrow(TracksColumns.DESCRIPTION);
|
int descriptionIndex = cursor.getColumnIndexOrThrow(TracksColumns.DESCRIPTION);
|
||||||
int categoryIndex = cursor.getColumnIndexOrThrow(TracksColumns.CATEGORY);
|
int categoryIndex = cursor.getColumnIndexOrThrow(TracksColumns.ACTIVITY_TYPE);
|
||||||
int startTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME);
|
int startTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME);
|
||||||
int startTimeOffsetIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME_OFFSET);
|
int startTimeOffsetIndex = cursor.getColumnIndexOrThrow(TracksColumns.STARTTIME_OFFSET);
|
||||||
int stopTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.STOPTIME);
|
int stopTimeIndex = cursor.getColumnIndexOrThrow(TracksColumns.STOPTIME);
|
||||||
@@ -129,7 +129,7 @@ public class ContentProviderUtils {
|
|||||||
track.setDescription(cursor.getString(descriptionIndex));
|
track.setDescription(cursor.getString(descriptionIndex));
|
||||||
}
|
}
|
||||||
if (!cursor.isNull(categoryIndex)) {
|
if (!cursor.isNull(categoryIndex)) {
|
||||||
track.setCategory(cursor.getString(categoryIndex));
|
track.setActivityType(cursor.getString(categoryIndex));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cursor.isNull(startTimeIndex)) {
|
if (!cursor.isNull(startTimeIndex)) {
|
||||||
@@ -290,7 +290,7 @@ public class ContentProviderUtils {
|
|||||||
values.put(TracksColumns.UUID, UUIDUtils.toBytes(track.getUuid()));
|
values.put(TracksColumns.UUID, UUIDUtils.toBytes(track.getUuid()));
|
||||||
values.put(TracksColumns.NAME, track.getName());
|
values.put(TracksColumns.NAME, track.getName());
|
||||||
values.put(TracksColumns.DESCRIPTION, track.getDescription());
|
values.put(TracksColumns.DESCRIPTION, track.getDescription());
|
||||||
values.put(TracksColumns.CATEGORY, track.getCategory());
|
values.put(TracksColumns.ACTIVITY_TYPE, track.getActivityType());
|
||||||
values.put(TracksColumns.STARTTIME_OFFSET, track.getZoneOffset().getTotalSeconds());
|
values.put(TracksColumns.STARTTIME_OFFSET, track.getZoneOffset().getTotalSeconds());
|
||||||
if (trackStatistics.getStartTime() != null) {
|
if (trackStatistics.getStartTime() != null) {
|
||||||
values.put(TracksColumns.STARTTIME, trackStatistics.getStartTime().toEpochMilli());
|
values.put(TracksColumns.STARTTIME, trackStatistics.getStartTime().toEpochMilli());
|
||||||
@@ -403,7 +403,7 @@ public class ContentProviderUtils {
|
|||||||
public void deleteMarker(Context context, Marker.Id markerId) {
|
public void deleteMarker(Context context, Marker.Id markerId) {
|
||||||
final Marker marker = getMarker(markerId);
|
final Marker marker = getMarker(markerId);
|
||||||
deleteMarkerPhoto(context, marker);
|
deleteMarkerPhoto(context, marker);
|
||||||
contentResolver.delete(MarkerColumns.CONTENT_URI, MarkerColumns._ID + "=?", new String[]{Long.toString(markerId.getId())});
|
contentResolver.delete(MarkerColumns.CONTENT_URI, MarkerColumns._ID + "=?", new String[]{Long.toString(markerId.id())});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -422,7 +422,7 @@ public class ContentProviderUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Marker getMarker(@NonNull Marker.Id markerId) {
|
public Marker getMarker(@NonNull Marker.Id markerId) {
|
||||||
try (Cursor cursor = getMarkerCursor(null, MarkerColumns._ID + "=?", new String[]{Long.toString(markerId.getId())}, MarkerColumns._ID, 1)) {
|
try (Cursor cursor = getMarkerCursor(null, MarkerColumns._ID + "=?", new String[]{Long.toString(markerId.id())}, MarkerColumns._ID, 1)) {
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
return createMarker(cursor);
|
return createMarker(cursor);
|
||||||
}
|
}
|
||||||
@@ -442,7 +442,7 @@ public class ContentProviderUtils {
|
|||||||
String[] selectionArgs;
|
String[] selectionArgs;
|
||||||
if (minMarkerId != null) {
|
if (minMarkerId != null) {
|
||||||
selection = MarkerColumns.TRACKID + "=? AND " + MarkerColumns._ID + ">=?";
|
selection = MarkerColumns.TRACKID + "=? AND " + MarkerColumns._ID + ">=?";
|
||||||
selectionArgs = new String[]{Long.toString(trackId.getId()), Long.toString(minMarkerId.getId())};
|
selectionArgs = new String[]{Long.toString(trackId.getId()), Long.toString(minMarkerId.id())};
|
||||||
} else {
|
} else {
|
||||||
selection = MarkerColumns.TRACKID + "=?";
|
selection = MarkerColumns.TRACKID + "=?";
|
||||||
selectionArgs = new String[]{Long.toString(trackId.getId())};
|
selectionArgs = new String[]{Long.toString(trackId.getId())};
|
||||||
@@ -495,7 +495,7 @@ public class ContentProviderUtils {
|
|||||||
if (!updateMarker.hasPhoto()) {
|
if (!updateMarker.hasPhoto()) {
|
||||||
deleteMarkerPhoto(context, savedMarker);
|
deleteMarkerPhoto(context, savedMarker);
|
||||||
}
|
}
|
||||||
int rows = contentResolver.update(MarkerColumns.CONTENT_URI, createContentValues(updateMarker), MarkerColumns._ID + "=?", new String[]{Long.toString(updateMarker.getId().getId())});
|
int rows = contentResolver.update(MarkerColumns.CONTENT_URI, createContentValues(updateMarker), MarkerColumns._ID + "=?", new String[]{Long.toString(updateMarker.getId().id())});
|
||||||
return rows == 1;
|
return rows == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,7 +503,7 @@ public class ContentProviderUtils {
|
|||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
|
|
||||||
if (marker.getId() != null) {
|
if (marker.getId() != null) {
|
||||||
values.put(MarkerColumns._ID, marker.getId().getId());
|
values.put(MarkerColumns._ID, marker.getId().id());
|
||||||
}
|
}
|
||||||
values.put(MarkerColumns.NAME, marker.getName());
|
values.put(MarkerColumns.NAME, marker.getName());
|
||||||
values.put(MarkerColumns.DESCRIPTION, marker.getDescription());
|
values.put(MarkerColumns.DESCRIPTION, marker.getDescription());
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ public class ShareContentProvider extends CustomContentProvider {
|
|||||||
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())) {
|
||||||
// TODO handle failure (i.e., do not export an empty file)
|
// TODO handle failure (i.e., do not export an empty file)
|
||||||
trackExporter.writeTrack(tracks.toArray(new Track[0]), fileOutputStream);
|
trackExporter.writeTrack(tracks, fileOutputStream);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.w(TAG, "there occurred an error while sharing a file: " + e);
|
Log.w(TAG, "there occurred an error while sharing a file: " + e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ public class TrackSelection implements ContentProviderUtils.ContentProviderSelec
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TrackSelection addCategory(String category) {
|
public TrackSelection addActivityType(String activityType) {
|
||||||
if (!this.categories.contains(category)) {
|
if (!this.categories.contains(activityType)) {
|
||||||
this.categories.add(category);
|
this.categories.add(activityType);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -53,7 +53,7 @@ public class TrackSelection implements ContentProviderUtils.ContentProviderSelec
|
|||||||
}
|
}
|
||||||
if (!categories.isEmpty()) {
|
if (!categories.isEmpty()) {
|
||||||
selection += selection.isEmpty() ? "" : " AND ";
|
selection += selection.isEmpty() ? "" : " AND ";
|
||||||
selection += String.format(TracksColumns.CATEGORY + " IN (%s)", TextUtils.join(",", Collections.nCopies(categories.size(), "?")));
|
selection += String.format(TracksColumns.ACTIVITY_TYPE + " IN (%s)", TextUtils.join(",", Collections.nCopies(categories.size(), "?")));
|
||||||
}
|
}
|
||||||
if (from != null && to != null) {
|
if (from != null && to != null) {
|
||||||
selection += selection.isEmpty() ? "" : " AND ";
|
selection += selection.isEmpty() ? "" : " AND ";
|
||||||
|
|||||||
@@ -16,8 +16,6 @@ public abstract class Altitude {
|
|||||||
return altitude_m;
|
return altitude_m;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract Altitude replace(double altitude_m);
|
|
||||||
|
|
||||||
public abstract int getLabelId();
|
public abstract int getLabelId();
|
||||||
|
|
||||||
public static class WGS84 extends Altitude {
|
public static class WGS84 extends Altitude {
|
||||||
@@ -31,12 +29,7 @@ public abstract class Altitude {
|
|||||||
return R.string.wgs84;
|
return R.string.wgs84;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Altitude of(double altitude_m) {
|
public static WGS84 of(double altitude_m) {
|
||||||
return new WGS84(altitude_m);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Altitude replace(double altitude_m) {
|
|
||||||
return new WGS84(altitude_m);
|
return new WGS84(altitude_m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,12 +45,7 @@ public abstract class Altitude {
|
|||||||
return R.string.egm2008;
|
return R.string.egm2008;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Altitude of(double altitude_m) {
|
public static EGM2008 of(double altitude_m) {
|
||||||
return new EGM2008(altitude_m);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Altitude replace(double altitude_m) {
|
|
||||||
return new EGM2008(altitude_m);
|
return new EGM2008(altitude_m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package de.dennisguse.opentracks.data.models;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public record AtmosphericPressure(float value) {
|
||||||
|
|
||||||
|
public static AtmosphericPressure ofPA(float value_Pa) {
|
||||||
|
return new AtmosphericPressure(value_Pa * 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AtmosphericPressure ofHPA(float value_hPa) {
|
||||||
|
return new AtmosphericPressure(value_hPa);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getPA() {
|
||||||
|
return value * 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getHPA() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package de.dennisguse.opentracks.data.models;
|
||||||
|
|
||||||
|
public record BatteryLevel(int percentage) {
|
||||||
|
|
||||||
|
public static BatteryLevel of(int percentage) {
|
||||||
|
return new BatteryLevel(percentage);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ import androidx.annotation.NonNull;
|
|||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public class Cadence {
|
public record Cadence(float value_rpm) {
|
||||||
|
|
||||||
public static Cadence of(float value, Duration duration) {
|
public static Cadence of(float value, Duration duration) {
|
||||||
if (duration.isZero()) {
|
if (duration.isZero()) {
|
||||||
@@ -23,34 +23,7 @@ public class Cadence {
|
|||||||
return of(0.0f);
|
return of(0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final float value_rpm;
|
|
||||||
|
|
||||||
private Cadence(float value) {
|
|
||||||
this.value_rpm = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getRPM() {
|
public float getRPM() {
|
||||||
return value_rpm;
|
return value_rpm;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
|
||||||
Cadence cadence = (Cadence) o;
|
|
||||||
return Float.compare(cadence.value_rpm, value_rpm) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(value_rpm);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "Cadence{" +
|
|
||||||
"value=" + value_rpm + " rpm" +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,16 +3,13 @@ package de.dennisguse.opentracks.data.models;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||||
|
|
||||||
public class Distance {
|
public record Distance(double distance_m) {
|
||||||
|
|
||||||
public static Distance of(double distance_m) {
|
public static Distance of(double distance_m) {
|
||||||
return new Distance(distance_m);
|
return new Distance(distance_m);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Distance of(Double distance_m) {
|
public static Distance of(Double distance_m) {
|
||||||
if (distance_m == null) {
|
if (distance_m == null) {
|
||||||
return Distance.of(Double.NaN);
|
return Distance.of(Double.NaN);
|
||||||
@@ -20,11 +17,9 @@ public class Distance {
|
|||||||
return Distance.of((double) distance_m);
|
return Distance.of((double) distance_m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Distance of(String distance_m) {
|
public static Distance of(String distance_m) {
|
||||||
return of(Float.parseFloat(distance_m));
|
return of(Float.parseFloat(distance_m));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static Distance ofOrNull(Double distance_m) {
|
public static Distance ofOrNull(Double distance_m) {
|
||||||
if (distance_m == null) {
|
if (distance_m == null) {
|
||||||
@@ -60,17 +55,11 @@ public class Distance {
|
|||||||
public static Distance one(UnitSystem unitSystem) {
|
public static Distance one(UnitSystem unitSystem) {
|
||||||
return switch (unitSystem) {
|
return switch (unitSystem) {
|
||||||
case METRIC -> Distance.ofKilometer(1);
|
case METRIC -> Distance.ofKilometer(1);
|
||||||
case IMPERIAL -> Distance.ofMile(1);
|
case IMPERIAL_FEET, IMPERIAL_METER -> Distance.ofMile(1);
|
||||||
case NAUTICAL_IMPERIAL -> Distance.ofNauticalMile(1);
|
case NAUTICAL_IMPERIAL -> Distance.ofNauticalMile(1);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private final double distance_m;
|
|
||||||
|
|
||||||
private Distance(double distance_m) {
|
|
||||||
this.distance_m = distance_m;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Distance plus(@NonNull Distance distance) {
|
public Distance plus(@NonNull Distance distance) {
|
||||||
return new Distance(distance_m + distance.distance_m);
|
return new Distance(distance_m + distance.distance_m);
|
||||||
}
|
}
|
||||||
@@ -130,39 +119,18 @@ public class Distance {
|
|||||||
public double toKM_Miles(UnitSystem unitSystem) {
|
public double toKM_Miles(UnitSystem unitSystem) {
|
||||||
return switch (unitSystem) {
|
return switch (unitSystem) {
|
||||||
case METRIC -> toKM();
|
case METRIC -> toKM();
|
||||||
case IMPERIAL -> toMI();
|
case IMPERIAL_FEET, IMPERIAL_METER -> toMI();
|
||||||
case NAUTICAL_IMPERIAL -> toNauticalMiles();
|
case NAUTICAL_IMPERIAL -> toNauticalMiles();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public double toM_FT(UnitSystem unitSystem) {
|
public double toM_FT(UnitSystem unitSystem) {
|
||||||
return switch (unitSystem) {
|
return switch (unitSystem) {
|
||||||
case METRIC -> toM();
|
case METRIC, IMPERIAL_METER -> toM();
|
||||||
case NAUTICAL_IMPERIAL, IMPERIAL -> toFT();
|
case NAUTICAL_IMPERIAL, IMPERIAL_FEET -> toFT();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
|
||||||
Distance distance = (Distance) o;
|
|
||||||
return Double.compare(distance.distance_m, distance_m) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(distance_m);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "Distance{" +
|
|
||||||
"distance_m=" + distance_m +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
|
|
||||||
// multiplication factors for conversion
|
// multiplication factors for conversion
|
||||||
private static final double KM_TO_M = 1000.0;
|
private static final double KM_TO_M = 1000.0;
|
||||||
private static final double M_TO_KM = 1 / KM_TO_M;
|
private static final double M_TO_KM = 1 / KM_TO_M;
|
||||||
|
|||||||
@@ -47,8 +47,9 @@ public class DistanceFormatter {
|
|||||||
if (distance.isInvalid()) {
|
if (distance.isInvalid()) {
|
||||||
String valueUnknown = resources.getString(R.string.value_unknown);
|
String valueUnknown = resources.getString(R.string.value_unknown);
|
||||||
return switch (unitSystem) {
|
return switch (unitSystem) {
|
||||||
case METRIC -> new Pair<>(valueUnknown, resources.getString(R.string.unit_meter));
|
case METRIC, IMPERIAL_METER ->
|
||||||
case IMPERIAL, NAUTICAL_IMPERIAL ->
|
new Pair<>(valueUnknown, resources.getString(R.string.unit_meter));
|
||||||
|
case IMPERIAL_FEET, NAUTICAL_IMPERIAL ->
|
||||||
new Pair<>(valueUnknown, resources.getString(R.string.unit_feet));
|
new Pair<>(valueUnknown, resources.getString(R.string.unit_feet));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -61,13 +62,20 @@ public class DistanceFormatter {
|
|||||||
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_FEET -> {
|
||||||
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 IMPERIAL_METER -> {
|
||||||
|
if (distance.greaterThan(Distance.ofMile(threshold))) {
|
||||||
|
return new Pair<>(StringUtils.formatDecimal(distance.toMI(), decimalCount), resources.getString(R.string.unit_mile));
|
||||||
|
} else {
|
||||||
|
return new Pair<>(StringUtils.formatDecimal(distance.toM(), decimalCount), resources.getString(R.string.unit_meter));
|
||||||
|
}
|
||||||
|
}
|
||||||
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));
|
||||||
|
|||||||
@@ -4,40 +4,13 @@ import androidx.annotation.NonNull;
|
|||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public class HeartRate {
|
public record HeartRate(float value) {
|
||||||
|
|
||||||
public static HeartRate of(float value) {
|
public static HeartRate of(float value) {
|
||||||
return new HeartRate(value);
|
return new HeartRate(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final float value;
|
|
||||||
|
|
||||||
private HeartRate(float value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getBPM() {
|
public float getBPM() {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
|
||||||
HeartRate heartRate = (HeartRate) o;
|
|
||||||
return Float.compare(heartRate.value, value) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "HeartRate{" +
|
|
||||||
"value=" + value + " bpm" +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import androidx.annotation.Nullable;
|
|||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||||
|
|
||||||
@@ -267,31 +266,7 @@ public final class Marker {
|
|||||||
return photoUrl != null && !"".equals(photoUrl);
|
return photoUrl != null && !"".equals(photoUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Id implements Parcelable {
|
public record Id(long id) implements Parcelable {
|
||||||
|
|
||||||
private final long id;
|
|
||||||
|
|
||||||
public Id(long id) {
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
//TOOD Limit visibility to TrackRecordingService / ContentProvider
|
|
||||||
public long getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
|
||||||
Id id1 = (Id) o;
|
|
||||||
return id == id1.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
@@ -309,7 +284,7 @@ public final class Marker {
|
|||||||
parcel.writeLong(id);
|
parcel.writeLong(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Parcelable.Creator<Id> CREATOR = new Parcelable.Creator<>() {
|
public static final Creator<Id> CREATOR = new Creator<>() {
|
||||||
public Id createFromParcel(Parcel in) {
|
public Id createFromParcel(Parcel in) {
|
||||||
return new Id(in.readLong());
|
return new Id(in.readLong());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,40 +4,13 @@ import androidx.annotation.NonNull;
|
|||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public class Power {
|
public record Power(float value) {
|
||||||
|
|
||||||
public static Power of(float value) {
|
public static Power of(float value) {
|
||||||
return new Power(value);
|
return new Power(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final float value;
|
|
||||||
|
|
||||||
private Power(float value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getW() {
|
public float getW() {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
|
||||||
Power power = (Power) o;
|
|
||||||
return Float.compare(power.value, value) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "Power{" +
|
|
||||||
"value=" + value + " W" +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
package de.dennisguse.opentracks.data.models;
|
package de.dennisguse.opentracks.data.models;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||||
|
|
||||||
public class Speed {
|
public record Speed(double speed_mps) {
|
||||||
|
|
||||||
public static Speed of(Distance distance, Duration duration) {
|
public static Speed of(Distance distance, Duration duration) {
|
||||||
if (duration.isZero()) {
|
if (duration.isZero()) {
|
||||||
@@ -47,12 +44,6 @@ public class Speed {
|
|||||||
return Speed.of(Math.abs(speed1.speed_mps - speed2.speed_mps));
|
return Speed.of(Math.abs(speed1.speed_mps - speed2.speed_mps));
|
||||||
}
|
}
|
||||||
|
|
||||||
private final double speed_mps;
|
|
||||||
|
|
||||||
private Speed(double speed_mps) {
|
|
||||||
this.speed_mps = speed_mps;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Speed mul(double factor) {
|
public Speed mul(double factor) {
|
||||||
return new Speed(factor * speed_mps);
|
return new Speed(factor * speed_mps);
|
||||||
}
|
}
|
||||||
@@ -117,29 +108,8 @@ public class Speed {
|
|||||||
public double to(UnitSystem unitSystem) {
|
public double to(UnitSystem unitSystem) {
|
||||||
return switch (unitSystem) {
|
return switch (unitSystem) {
|
||||||
case METRIC -> toKMH();
|
case METRIC -> toKMH();
|
||||||
case IMPERIAL -> toMPH();
|
case IMPERIAL_FEET, IMPERIAL_METER -> toMPH();
|
||||||
case NAUTICAL_IMPERIAL -> toKnots();
|
case NAUTICAL_IMPERIAL -> toKnots();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
|
||||||
Speed speed = (Speed) o;
|
|
||||||
return Double.compare(speed.speed_mps, speed_mps) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(speed_mps);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "Speed{" +
|
|
||||||
"speed_mps=" + speed_mps +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ public class SpeedFormatter {
|
|||||||
int unitId = switch (unitSystem) {
|
int unitId = switch (unitSystem) {
|
||||||
case METRIC ->
|
case METRIC ->
|
||||||
reportSpeedOrPace ? R.string.unit_kilometer_per_hour : R.string.unit_minute_per_kilometer;
|
reportSpeedOrPace ? R.string.unit_kilometer_per_hour : R.string.unit_minute_per_kilometer;
|
||||||
case IMPERIAL ->
|
case IMPERIAL_FEET, IMPERIAL_METER ->
|
||||||
reportSpeedOrPace ? R.string.unit_mile_per_hour : R.string.unit_minute_per_mile;
|
reportSpeedOrPace ? R.string.unit_mile_per_hour : R.string.unit_minute_per_mile;
|
||||||
case NAUTICAL_IMPERIAL ->
|
case NAUTICAL_IMPERIAL ->
|
||||||
reportSpeedOrPace ? R.string.unit_knots : R.string.unit_minute_per_nautical_mile;
|
reportSpeedOrPace ? R.string.unit_knots : R.string.unit_minute_per_nautical_mile;
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ public class Track {
|
|||||||
|
|
||||||
private String name = "";
|
private String name = "";
|
||||||
private String description = "";
|
private String description = "";
|
||||||
private String category = "";
|
private String activityType = "";
|
||||||
|
|
||||||
private String icon = "";
|
private String icon = "";
|
||||||
|
|
||||||
@@ -97,12 +97,12 @@ public class Track {
|
|||||||
this.description = description;
|
this.description = description;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCategory() {
|
public String getActivityType() {
|
||||||
return category;
|
return activityType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCategory(String category) {
|
public void setActivityType(String activityType) {
|
||||||
this.category = category;
|
this.activityType = activityType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getIcon() {
|
public String getIcon() {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public interface TracksColumns extends BaseColumns {
|
|||||||
String UUID = "uuid"; // identifier to make tracks globally unique (prevent re-import)
|
String UUID = "uuid"; // identifier to make tracks globally unique (prevent re-import)
|
||||||
String NAME = "name"; // track name
|
String NAME = "name"; // track name
|
||||||
String DESCRIPTION = "description"; // track description
|
String DESCRIPTION = "description"; // track description
|
||||||
String CATEGORY = "category"; // track activity type
|
String ACTIVITY_TYPE = "category"; // track activity type
|
||||||
String STARTTIME = "starttime"; // track start time
|
String STARTTIME = "starttime"; // track start time
|
||||||
String STARTTIME_OFFSET = "starttime_offset"; // in plus/minus in seconds
|
String STARTTIME_OFFSET = "starttime_offset"; // in plus/minus in seconds
|
||||||
String STOPTIME = "stoptime"; // track stop time
|
String STOPTIME = "stoptime"; // track stop time
|
||||||
@@ -63,7 +63,7 @@ public interface TracksColumns extends BaseColumns {
|
|||||||
+ _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
+ _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||||
+ NAME + " TEXT, "
|
+ NAME + " TEXT, "
|
||||||
+ DESCRIPTION + " TEXT, "
|
+ DESCRIPTION + " TEXT, "
|
||||||
+ CATEGORY + " TEXT, "
|
+ ACTIVITY_TYPE + " TEXT, "
|
||||||
+ STARTTIME + " INTEGER, "
|
+ STARTTIME + " INTEGER, "
|
||||||
+ STOPTIME + " INTEGER, "
|
+ STOPTIME + " INTEGER, "
|
||||||
+ NUMPOINTS + " INTEGER, "
|
+ NUMPOINTS + " INTEGER, "
|
||||||
|
|||||||
+1
-1
@@ -78,7 +78,7 @@ class ChooseActivityTypeImageAdapter extends BaseAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (position == selected) {
|
if (position == selected) {
|
||||||
imageView.setBackgroundColor(ContextCompat.getColor(parent.getContext(), R.color.opentracks_secondary_color));
|
imageView.setBackgroundColor(ContextCompat.getColor(parent.getContext(), R.color.opentracks));
|
||||||
} else {
|
} else {
|
||||||
imageView.setBackgroundColor(Color.TRANSPARENT);
|
imageView.setBackgroundColor(Color.TRANSPARENT);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ public class StatisticsRecordedFragment extends Fragment {
|
|||||||
|
|
||||||
if (PreferencesUtils.isKey(R.string.stats_rate_key, key) && track != null) {
|
if (PreferencesUtils.isKey(R.string.stats_rate_key, key) && track != null) {
|
||||||
updateUInecessary = true;
|
updateUInecessary = true;
|
||||||
preferenceReportSpeed = PreferencesUtils.isReportSpeed(track.getCategory());
|
preferenceReportSpeed = PreferencesUtils.isReportSpeed(track.getActivityType());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key != null && updateUInecessary && isResumed()) {
|
if (key != null && updateUInecessary && isResumed()) {
|
||||||
@@ -155,7 +155,7 @@ public class StatisticsRecordedFragment extends Fragment {
|
|||||||
|
|
||||||
sensorStatistics = contentProviderUtils.getSensorStats(trackId);
|
sensorStatistics = contentProviderUtils.getSensorStats(trackId);
|
||||||
|
|
||||||
boolean prefsChanged = this.track == null || (!this.track.getCategory().equals(track.getCategory()));
|
boolean prefsChanged = this.track == null || (!this.track.getActivityType().equals(track.getActivityType()));
|
||||||
this.track = track;
|
this.track = track;
|
||||||
if (prefsChanged) {
|
if (prefsChanged) {
|
||||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, getString(R.string.stats_rate_key));
|
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, getString(R.string.stats_rate_key));
|
||||||
@@ -191,7 +191,7 @@ public class StatisticsRecordedFragment extends Fragment {
|
|||||||
|
|
||||||
// Set activity type
|
// Set activity type
|
||||||
{
|
{
|
||||||
String trackIconValue = TrackIconUtils.getIconValue(getContext(), track.getCategory());
|
String trackIconValue = TrackIconUtils.getIconValue(getContext(), track.getActivityType());
|
||||||
viewBinding.statsActivityTypeIcon.setImageDrawable(ContextCompat.getDrawable(getContext(), TrackIconUtils.getIconDrawable(trackIconValue)));
|
viewBinding.statsActivityTypeIcon.setImageDrawable(ContextCompat.getDrawable(getContext(), TrackIconUtils.getIconDrawable(trackIconValue)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -122,17 +122,15 @@ public class StatisticsRecordingFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
recordingLayout = newRecordingLayout;
|
recordingLayout = newRecordingLayout;
|
||||||
|
|
||||||
viewBinding.statsLayout.setColumnCount(recordingLayout.getColumnsPerRow());
|
|
||||||
|
|
||||||
viewBinding.statsLayout.removeAllViews(); //Let's start from scratch
|
viewBinding.statsLayout.removeAllViews(); //Let's start from scratch
|
||||||
|
viewBinding.statsLayout.setColumnCount(recordingLayout.getColumnsPerRow());
|
||||||
viewHolders.clear();
|
viewHolders.clear();
|
||||||
|
|
||||||
Map<String, Callable<StatisticViewHolder<?>>> m = Mapping.create(getContext());
|
Map<String, Callable<StatisticViewHolder<?>>> m = Mapping.create(getContext());
|
||||||
|
|
||||||
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;
|
||||||
|
|||||||
@@ -20,15 +20,19 @@ public class TrackFilenameGenerator {
|
|||||||
|
|
||||||
public static final String UUID_KEY = "{uuid}";
|
public static final String UUID_KEY = "{uuid}";
|
||||||
public static final String TRACKNAME_KEY = "{name}";
|
public static final String TRACKNAME_KEY = "{name}";
|
||||||
public static final String CATEGORY_KEY = "{category}";
|
public static final String ACTIVITY_TYPE_KEY = "{category}";
|
||||||
public static final String STARTTIME_TIME_KEY = "{time}";
|
public static final String STARTTIME_TIME_KEY = "{time}";
|
||||||
public static final String STARTTIME_DATE_KEY = "{date}";
|
public static final String STARTTIME_DATE_KEY = "{date}";
|
||||||
|
|
||||||
public static String getAllOptions() {
|
public static String getAllOptions() {
|
||||||
return Stream.of(UUID_KEY, TRACKNAME_KEY, CATEGORY_KEY, STARTTIME_TIME_KEY, STARTTIME_DATE_KEY)
|
return Stream.of(UUID_KEY, TRACKNAME_KEY, ACTIVITY_TYPE_KEY, STARTTIME_TIME_KEY, STARTTIME_DATE_KEY)
|
||||||
.collect(Collectors.joining(", "));
|
.collect(Collectors.joining(", "));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String format(@NonNull String name, @NonNull TrackFileFormat trackFileFormat) {
|
||||||
|
return FileUtils.sanitizeFileName(name + "." + trackFileFormat.getExtension());
|
||||||
|
}
|
||||||
|
|
||||||
private final String template;
|
private final String template;
|
||||||
|
|
||||||
public TrackFilenameGenerator(@NonNull String template) {
|
public TrackFilenameGenerator(@NonNull String template) {
|
||||||
@@ -40,11 +44,11 @@ public class TrackFilenameGenerator {
|
|||||||
|
|
||||||
values.put(UUID_KEY, track.getUuid().toString().substring(0, 8));
|
values.put(UUID_KEY, track.getUuid().toString().substring(0, 8));
|
||||||
values.put(TRACKNAME_KEY, track.getName());
|
values.put(TRACKNAME_KEY, track.getName());
|
||||||
values.put(CATEGORY_KEY, track.getCategory());
|
values.put(ACTIVITY_TYPE_KEY, track.getActivityType());
|
||||||
values.put(STARTTIME_TIME_KEY, track.getStartTime().toLocalTime().toString());
|
values.put(STARTTIME_TIME_KEY, track.getStartTime().toLocalTime().toString());
|
||||||
values.put(STARTTIME_DATE_KEY, track.getStartTime().toLocalDate().toString());
|
values.put(STARTTIME_DATE_KEY, track.getStartTime().toLocalDate().toString());
|
||||||
|
|
||||||
return FileUtils.sanitizeFileName(format(template, values)) + "." + trackFileFormat.getExtension();
|
return format(format(template, values), trackFileFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String format(String template, Map<String, String> values) {
|
private static String format(String template, Map<String, String> values) {
|
||||||
@@ -77,10 +81,6 @@ public class TrackFilenameGenerator {
|
|||||||
return String.format(templateCompiled, valueList.toArray());
|
return String.format(templateCompiled, valueList.toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTemplate() {
|
|
||||||
return template;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isValid() {
|
public boolean isValid() {
|
||||||
try {
|
try {
|
||||||
getExample();
|
getExample();
|
||||||
|
|||||||
@@ -23,8 +23,6 @@ import androidx.annotation.NonNull;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
@@ -87,15 +85,23 @@ public class CSVTrackExporter implements TrackExporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean writeTrack(Track track, @NonNull OutputStream outputStream) {
|
public boolean writeTrack(@NonNull List<Track> tracks, @NonNull OutputStream outputStream) {
|
||||||
return writeTrack(new Track[]{track}, outputStream);
|
List<Column> columns = List.of(
|
||||||
}
|
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()) : ""),
|
||||||
|
|
||||||
@Override
|
new Column("speed", t -> t.hasSpeed() ? SPEED_FORMAT.format(t.getSpeed().toKMH()) : ""),
|
||||||
public boolean writeTrack(Track[] tracks, @NonNull OutputStream outputStream) {
|
new Column("altitude_gain", t -> t.hasAltitudeGain() ? DISTANCE_FORMAT.format(t.getAltitudeGain()) : ""),
|
||||||
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("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("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("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);
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package de.dennisguse.opentracks.io.file.exporter;
|
package de.dennisguse.opentracks.io.file.exporter;
|
||||||
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@@ -24,6 +23,7 @@ import android.view.View;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.documentfile.provider.DocumentFile;
|
import androidx.documentfile.provider.DocumentFile;
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
@@ -31,14 +31,15 @@ import androidx.fragment.app.FragmentActivity;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
import de.dennisguse.opentracks.data.tables.TracksColumns;
|
|
||||||
import de.dennisguse.opentracks.databinding.ExportActivityBinding;
|
import de.dennisguse.opentracks.databinding.ExportActivityBinding;
|
||||||
import de.dennisguse.opentracks.io.file.ErrorListDialog;
|
import de.dennisguse.opentracks.io.file.ErrorListDialog;
|
||||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||||
|
import de.dennisguse.opentracks.io.file.TrackFilenameGenerator;
|
||||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||||
import de.dennisguse.opentracks.util.ExportUtils;
|
import de.dennisguse.opentracks.util.ExportUtils;
|
||||||
import de.dennisguse.opentracks.util.FileUtils;
|
import de.dennisguse.opentracks.util.FileUtils;
|
||||||
@@ -47,12 +48,20 @@ import de.dennisguse.opentracks.util.FileUtils;
|
|||||||
* An activity for saving tracks to the external storage.
|
* An activity for saving tracks to the external storage.
|
||||||
*
|
*
|
||||||
* @author Rodrigo Damazio
|
* @author Rodrigo Damazio
|
||||||
|
* TODO: This class needs some refactoring.
|
||||||
|
* * It pushes each export job (usually one Track) for export; although export could be run in parallel.
|
||||||
|
* Also this requires that the ExportActivity stays in foreground, so the user has to activily wait.
|
||||||
|
* It would be better to let the ExportService handle this and let it report progress / conflicts to ExportActivity
|
||||||
|
* * File name conflicts are checked in this class instead of the ExportService.
|
||||||
|
* So, for this check actually a different file name might be used than in the ExportService.
|
||||||
|
* * Saved state as an object instead of individual values.
|
||||||
*/
|
*/
|
||||||
public class ExportActivity extends FragmentActivity implements ExportServiceResultReceiver.Receiver {
|
public class ExportActivity extends FragmentActivity implements ExportServiceResultReceiver.Receiver {
|
||||||
|
|
||||||
private static final String TAG = ExportActivity.class.getSimpleName();
|
private static final String TAG = ExportActivity.class.getSimpleName();
|
||||||
|
|
||||||
public static final String EXTRA_DIRECTORY_URI_KEY = "directory_uri";
|
public static final String EXTRA_DIRECTORY_URI_KEY = "directory_uri";
|
||||||
|
public static final String EXTRA_ONE_FILE_KEY = "one_file";
|
||||||
public static final String EXTRA_TRACKFILEFORMAT_KEY = "trackfileformat";
|
public static final String EXTRA_TRACKFILEFORMAT_KEY = "trackfileformat";
|
||||||
|
|
||||||
private static final String BUNDLE_AUTO_CONFLICT = "auto_conflict";
|
private static final String BUNDLE_AUTO_CONFLICT = "auto_conflict";
|
||||||
@@ -64,9 +73,13 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
|||||||
private static final String BUNDLE_DIRECTORY_FILES = "track_directory_files";
|
private static final String BUNDLE_DIRECTORY_FILES = "track_directory_files";
|
||||||
private static final String BUNDLE_TRACK_ERRORS = "track_errors";
|
private static final String BUNDLE_TRACK_ERRORS = "track_errors";
|
||||||
|
|
||||||
private static final int CONFLICT_NONE = 0;
|
private static final String BUNDLE_EXPORT_TASKS = "export_tasks";
|
||||||
private static final int CONFLICT_OVERWRITE = 1;
|
|
||||||
private static final int CONFLICT_SKIP = 2;
|
private enum ConflictResolutionStrategy {
|
||||||
|
CONFLICT_NONE,
|
||||||
|
CONFLICT_OVERWRITE,
|
||||||
|
CONFLICT_SKIP
|
||||||
|
}
|
||||||
|
|
||||||
private TrackFileFormat trackFileFormat;
|
private TrackFileFormat trackFileFormat;
|
||||||
private Uri directoryUri;
|
private Uri directoryUri;
|
||||||
@@ -87,12 +100,12 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
|||||||
|
|
||||||
private ArrayList<String> trackErrors = new ArrayList<>();
|
private ArrayList<String> trackErrors = new ArrayList<>();
|
||||||
|
|
||||||
private int autoConflict;
|
private ConflictResolutionStrategy autoConflict;
|
||||||
|
|
||||||
private ContentProviderUtils contentProviderUtils;
|
private ContentProviderUtils contentProviderUtils;
|
||||||
|
|
||||||
// List of tracks to be exported.
|
// List of tracks to be exported.
|
||||||
private final ArrayList<Track> tracks = new ArrayList<>();
|
private ArrayList<ExportTask> exportTasks;
|
||||||
|
|
||||||
private final LinkedBlockingQueue<PendingConflict> conflictsQueue = new LinkedBlockingQueue<>();
|
private final LinkedBlockingQueue<PendingConflict> conflictsQueue = new LinkedBlockingQueue<>();
|
||||||
private final Handler conflictsHandler = new Handler();
|
private final Handler conflictsHandler = new Handler();
|
||||||
@@ -100,7 +113,10 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
|||||||
private final Runnable conflictsRunnable = new Runnable() {
|
private final Runnable conflictsRunnable = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (conflictsQueue.size() > 0) {
|
if (conflictsQueue.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
PendingConflict conflict = conflictsQueue.peek();
|
PendingConflict conflict = conflictsQueue.peek();
|
||||||
if (conflict.resolve()) {
|
if (conflict.resolve()) {
|
||||||
conflictsQueue.remove(conflict);
|
conflictsQueue.remove(conflict);
|
||||||
@@ -128,7 +144,6 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -139,6 +154,7 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
|||||||
|
|
||||||
directoryUri = getIntent().getParcelableExtra(EXTRA_DIRECTORY_URI_KEY);
|
directoryUri = getIntent().getParcelableExtra(EXTRA_DIRECTORY_URI_KEY);
|
||||||
trackFileFormat = (TrackFileFormat) getIntent().getSerializableExtra(EXTRA_TRACKFILEFORMAT_KEY);
|
trackFileFormat = (TrackFileFormat) getIntent().getSerializableExtra(EXTRA_TRACKFILEFORMAT_KEY);
|
||||||
|
boolean allInOneFile = getIntent().getBooleanExtra(EXTRA_ONE_FILE_KEY, false);
|
||||||
|
|
||||||
contentProviderUtils = new ContentProviderUtils(this);
|
contentProviderUtils = new ContentProviderUtils(this);
|
||||||
|
|
||||||
@@ -148,14 +164,17 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
|||||||
resultReceiver = new ExportServiceResultReceiver(new Handler(), this);
|
resultReceiver = new ExportServiceResultReceiver(new Handler(), this);
|
||||||
|
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
autoConflict = CONFLICT_NONE;
|
autoConflict = ConflictResolutionStrategy.CONFLICT_NONE;
|
||||||
setProgress();
|
setProgress();
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
directoryFiles = ExportUtils.getAllFiles(ExportActivity.this, documentFile.getUri());
|
directoryFiles = ExportUtils.getAllFiles(ExportActivity.this, documentFile.getUri());
|
||||||
runOnUiThread(() -> initExport(0));
|
runOnUiThread(() -> {
|
||||||
|
createExportTasks(allInOneFile);
|
||||||
|
nextExport(null);
|
||||||
|
});
|
||||||
}).start();
|
}).start();
|
||||||
} else {
|
} else {
|
||||||
autoConflict = savedInstanceState.getInt(BUNDLE_AUTO_CONFLICT);
|
autoConflict = ConflictResolutionStrategy.valueOf(savedInstanceState.getString(BUNDLE_AUTO_CONFLICT));
|
||||||
trackExportSuccessCount = savedInstanceState.getInt(BUNDLE_SUCCESS_COUNT);
|
trackExportSuccessCount = savedInstanceState.getInt(BUNDLE_SUCCESS_COUNT);
|
||||||
trackExportErrorCount = savedInstanceState.getInt(BUNDLE_ERROR_COUNT);
|
trackExportErrorCount = savedInstanceState.getInt(BUNDLE_ERROR_COUNT);
|
||||||
trackExportOverwrittenCount = savedInstanceState.getInt(BUNDLE_OVERWRITTEN_COUNT);
|
trackExportOverwrittenCount = savedInstanceState.getInt(BUNDLE_OVERWRITTEN_COUNT);
|
||||||
@@ -163,19 +182,19 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
|||||||
trackExportTotalCount = savedInstanceState.getInt(BUNDLE_TOTAL_COUNT);
|
trackExportTotalCount = savedInstanceState.getInt(BUNDLE_TOTAL_COUNT);
|
||||||
directoryFiles = savedInstanceState.getStringArrayList(BUNDLE_DIRECTORY_FILES);
|
directoryFiles = savedInstanceState.getStringArrayList(BUNDLE_DIRECTORY_FILES);
|
||||||
trackErrors = savedInstanceState.getStringArrayList(BUNDLE_TRACK_ERRORS);
|
trackErrors = savedInstanceState.getStringArrayList(BUNDLE_TRACK_ERRORS);
|
||||||
|
exportTasks = new ArrayList<>(savedInstanceState.getParcelableArrayList(BUNDLE_EXPORT_TASKS));
|
||||||
setProgress();
|
setProgress();
|
||||||
initExport(getTotalDone());
|
nextExport(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
viewBinding.bottomAppBarLayout.bottomAppBarTitle.setText(getString(R.string.export_progress_message, directoryDisplayName));
|
viewBinding.exportActivityToolbar.setTitle(getString(R.string.export_progress_message, directoryDisplayName));
|
||||||
viewBinding.bottomAppBarLayout.bottomAppBar.setNavigationIcon(R.drawable.ic_logo_color_24dp);
|
viewBinding.bottomAppBarLayout.bottomAppBar.setNavigationIcon(R.drawable.ic_logo_color_24dp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
outState.putInt(BUNDLE_AUTO_CONFLICT, autoConflict);
|
outState.putString(BUNDLE_AUTO_CONFLICT, autoConflict.name());
|
||||||
outState.putInt(BUNDLE_SUCCESS_COUNT, trackExportSuccessCount);
|
outState.putInt(BUNDLE_SUCCESS_COUNT, trackExportSuccessCount);
|
||||||
outState.putInt(BUNDLE_ERROR_COUNT, trackExportErrorCount);
|
outState.putInt(BUNDLE_ERROR_COUNT, trackExportErrorCount);
|
||||||
outState.putInt(BUNDLE_OVERWRITTEN_COUNT, trackExportOverwrittenCount);
|
outState.putInt(BUNDLE_OVERWRITTEN_COUNT, trackExportOverwrittenCount);
|
||||||
@@ -183,13 +202,14 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
|||||||
outState.putInt(BUNDLE_TOTAL_COUNT, trackExportTotalCount);
|
outState.putInt(BUNDLE_TOTAL_COUNT, trackExportTotalCount);
|
||||||
outState.putStringArrayList(BUNDLE_DIRECTORY_FILES, (ArrayList<String>) directoryFiles);
|
outState.putStringArrayList(BUNDLE_DIRECTORY_FILES, (ArrayList<String>) directoryFiles);
|
||||||
outState.putStringArrayList(BUNDLE_TRACK_ERRORS, trackErrors);
|
outState.putStringArrayList(BUNDLE_TRACK_ERRORS, trackErrors);
|
||||||
|
outState.putParcelableArrayList(BUNDLE_EXPORT_TASKS, exportTasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
conflictsQueue.clear();
|
conflictsQueue.clear();
|
||||||
tracks.clear();
|
exportTasks.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -202,57 +222,50 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
|||||||
this.doubleBackToCancel = true;
|
this.doubleBackToCancel = true;
|
||||||
Toast.makeText(this, getString(R.string.generic_click_twice_cancel), Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, getString(R.string.generic_click_twice_cancel), Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
new Handler().postDelayed(() -> doubleBackToCancel=false, 2000);
|
new Handler().postDelayed(() -> doubleBackToCancel = false, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initExport(int from) {
|
private void createExportTasks(boolean allInOneFile) {
|
||||||
try (Cursor cursor = contentProviderUtils.getTrackCursor(null, null, TracksColumns._ID)) {
|
List<Track> tracks = contentProviderUtils.getTracks();
|
||||||
if (cursor == null) {
|
exportTasks = new ArrayList<>();
|
||||||
onExportEnded();
|
if (allInOneFile) {
|
||||||
return;
|
String filename = "OpenTracks-Backup";
|
||||||
}
|
exportTasks.add(new ExportTask(filename, trackFileFormat, tracks.stream().map(Track::getId).collect(Collectors.toList())));
|
||||||
|
|
||||||
trackExportTotalCount = cursor.getCount();
|
|
||||||
viewBinding.exportProgressTotal.setText("" + trackExportTotalCount);
|
|
||||||
for (int i = from; i < trackExportTotalCount; i++) {
|
|
||||||
cursor.moveToPosition(i);
|
|
||||||
Track track = ContentProviderUtils.createTrack(cursor);
|
|
||||||
tracks.add(track);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!tracks.isEmpty()) {
|
|
||||||
export(tracks.get(0));
|
|
||||||
} else {
|
} else {
|
||||||
onExportEnded();
|
exportTasks.addAll(tracks.stream().map(it -> new ExportTask(null, trackFileFormat, List.of(it.getId()))).collect(Collectors.toList()));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
trackExportTotalCount = exportTasks.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enqueue track identified by UUID to be exported if not exported already or there is a conflict resolution.
|
* Enqueue track identified by UUID to be exported if not exported already or there is a conflict resolution.
|
||||||
*
|
|
||||||
* @param track Track object.
|
|
||||||
* @param conflictResolution conflict resolution to be applied if needed.
|
|
||||||
*/
|
*/
|
||||||
private void export(Track track, int conflictResolution) {
|
private void export(ExportTask exportTask, ConflictResolutionStrategy conflictResolution) {
|
||||||
boolean fileExists = exportFileExists(track);
|
boolean fileExists = exportFileExists(exportTask);
|
||||||
|
|
||||||
if (fileExists && conflictResolution == CONFLICT_NONE) {
|
if (fileExists && conflictResolution == ConflictResolutionStrategy.CONFLICT_NONE) {
|
||||||
conflict(track);
|
conflict(exportTask);
|
||||||
} else if (fileExists && conflictResolution == CONFLICT_SKIP) {
|
} else if (fileExists && conflictResolution == ConflictResolutionStrategy.CONFLICT_SKIP) {
|
||||||
trackExportSkippedCount++;
|
trackExportSkippedCount++;
|
||||||
onExportCompleted(track);
|
nextExport(exportTask);
|
||||||
} else {
|
} else {
|
||||||
ExportService.enqueue(this, resultReceiver, track.getId(), trackFileFormat, directoryUri);
|
ExportService.enqueue(this, resultReceiver, exportTask, directoryUri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void export(Track track) {
|
private void export(ExportTask exportTask) {
|
||||||
export(track, autoConflict);
|
export(exportTask, autoConflict);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean exportFileExists(Track track) {
|
@Deprecated //TODO Check should be done in ExportService
|
||||||
String filename = PreferencesUtils.getTrackFileformatGenerator().format(track, trackFileFormat);
|
private boolean exportFileExists(ExportTask exportTask) {
|
||||||
|
String filename;
|
||||||
|
if (exportTask.isMultiExport()) {
|
||||||
|
filename = TrackFilenameGenerator.format(exportTask.getFilename(), exportTask.getTrackFileFormat());
|
||||||
|
} else {
|
||||||
|
Track track = contentProviderUtils.getTrack(exportTask.getTrackIds().get(0));
|
||||||
|
filename = PreferencesUtils.getTrackFileformatGenerator().format(track, trackFileFormat);
|
||||||
|
}
|
||||||
return directoryFiles.stream().anyMatch(filename::equals);
|
return directoryFiles.stream().anyMatch(filename::equals);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,31 +282,29 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setProgress() {
|
private void setProgress() {
|
||||||
int done = getTotalDone();
|
viewBinding.exportProgressDone.setText("" + getTotalDone());
|
||||||
|
|
||||||
viewBinding.exportProgressDone.setText("" + done);
|
|
||||||
viewBinding.exportProgressTotal.setText("" + trackExportTotalCount);
|
viewBinding.exportProgressTotal.setText("" + trackExportTotalCount);
|
||||||
|
|
||||||
viewBinding.exportProgressBar.setProgress((int) ((float) done / (float) trackExportTotalCount * 100f));
|
viewBinding.exportProgressBar.setProgress((int) ((float) getTotalDone() / (float) trackExportTotalCount * 100f));
|
||||||
viewBinding.exportProgressSummaryNew.setText(String.valueOf(trackExportSuccessCount));
|
viewBinding.exportProgressSummaryNew.setText("" + trackExportSuccessCount);
|
||||||
viewBinding.exportProgressSummaryOverwrite.setText(String.valueOf(trackExportOverwrittenCount));
|
viewBinding.exportProgressSummaryOverwrite.setText("" + trackExportOverwrittenCount);
|
||||||
viewBinding.exportProgressSummarySkip.setText(String.valueOf(trackExportSkippedCount));
|
viewBinding.exportProgressSummarySkip.setText("" + trackExportSkippedCount);
|
||||||
viewBinding.exportProgressSummaryErrors.setText(String.valueOf(trackExportErrorCount));
|
viewBinding.exportProgressSummaryErrors.setText("" + trackExportErrorCount);
|
||||||
viewBinding.exportProgressSummaryNewGroup.setVisibility(trackExportSuccessCount > 0 ? View.VISIBLE : View.GONE);
|
viewBinding.exportProgressSummaryNewGroup.setVisibility(trackExportSuccessCount > 0 ? View.VISIBLE : View.GONE);
|
||||||
viewBinding.exportProgressSummaryOverwriteGroup.setVisibility(trackExportOverwrittenCount > 0 ? View.VISIBLE : View.GONE);
|
viewBinding.exportProgressSummaryOverwriteGroup.setVisibility(trackExportOverwrittenCount > 0 ? View.VISIBLE : View.GONE);
|
||||||
viewBinding.exportProgressSummarySkipGroup.setVisibility(trackExportSkippedCount > 0 ? View.VISIBLE : View.GONE);
|
viewBinding.exportProgressSummarySkipGroup.setVisibility(trackExportSkippedCount > 0 ? View.VISIBLE : View.GONE);
|
||||||
viewBinding.exportProgressSummaryErrorsGroup.setVisibility(trackExportErrorCount > 0 ? View.VISIBLE : View.GONE);
|
viewBinding.exportProgressSummaryErrorsGroup.setVisibility(trackExportErrorCount > 0 ? View.VISIBLE : View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onExportCompleted(Track track) {
|
private void nextExport(@Nullable ExportTask exportTask) {
|
||||||
tracks.remove(track);
|
exportTasks.remove(exportTask);
|
||||||
|
|
||||||
setProgress();
|
setProgress();
|
||||||
if (tracks.isEmpty()) {
|
if (exportTasks.isEmpty()) {
|
||||||
onExportEnded();
|
onExportEnded();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
export(tracks.get(0));
|
export(exportTasks.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onExportEnded() {
|
private void onExportEnded() {
|
||||||
@@ -318,30 +329,32 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onExportSuccess(Track.Id trackId) {
|
public void onExportSuccess(ExportTask exportTask) {
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
if (exportFileExists(exportTask)) {
|
||||||
|
|
||||||
if (exportFileExists(track)) {
|
|
||||||
trackExportOverwrittenCount++;
|
trackExportOverwrittenCount++;
|
||||||
} else {
|
} else {
|
||||||
trackExportSuccessCount++;
|
trackExportSuccessCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
onExportCompleted(track);
|
nextExport(exportTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onExportError(Track.Id trackId) {
|
public void onExportError(ExportTask exportTask) {
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
|
||||||
|
|
||||||
trackExportErrorCount++;
|
trackExportErrorCount++;
|
||||||
trackErrors.add(track.getName());
|
String name;
|
||||||
|
if (exportTask.isMultiExport()) {
|
||||||
|
name = exportTask.getFilename();
|
||||||
|
} else {
|
||||||
|
name = contentProviderUtils.getTrack(exportTask.getTrackIds().get(0)).getName();
|
||||||
|
}
|
||||||
|
trackErrors.add(name);
|
||||||
|
|
||||||
onExportCompleted(track);
|
nextExport(exportTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void conflict(Track track) {
|
private void conflict(ExportTask exportTask) {
|
||||||
PendingConflict newConflict = new PendingConflict(track);
|
PendingConflict newConflict = new PendingConflict(exportTask);
|
||||||
conflictsQueue.add(newConflict);
|
conflictsQueue.add(newConflict);
|
||||||
|
|
||||||
if (conflictsQueue.size() == 1) {
|
if (conflictsQueue.size() == 1) {
|
||||||
@@ -353,10 +366,10 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
|||||||
* Handle conflicts (exporting file already exists).
|
* Handle conflicts (exporting file already exists).
|
||||||
*/
|
*/
|
||||||
private class PendingConflict {
|
private class PendingConflict {
|
||||||
private final Track track;
|
private final ExportTask exportTask;
|
||||||
|
|
||||||
public PendingConflict(Track track) {
|
public PendingConflict(ExportTask exportTask) {
|
||||||
this.track = track;
|
this.exportTask = exportTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -366,14 +379,20 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
|||||||
* @return true if it could resolve the conflict or false otherwise.
|
* @return true if it could resolve the conflict or false otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean resolve() {
|
public boolean resolve() {
|
||||||
if (autoConflict == CONFLICT_NONE) {
|
if (autoConflict == ConflictResolutionStrategy.CONFLICT_NONE) {
|
||||||
viewBinding.exportProgressAlertIcon.setImageDrawable(ContextCompat.getDrawable(ExportActivity.this, R.drawable.ic_report_problem_24));
|
viewBinding.exportProgressAlertIcon.setImageDrawable(ContextCompat.getDrawable(ExportActivity.this, R.drawable.ic_report_problem_24));
|
||||||
viewBinding.exportProgressAlertMsg.setText(getString(R.string.export_track_already_exists_msg, track.getName()));
|
String name;
|
||||||
|
if (exportTask.isMultiExport()) {
|
||||||
|
name = exportTask.getFilename();
|
||||||
|
} else {
|
||||||
|
name = contentProviderUtils.getTrack(exportTask.getTrackIds().get(0)).getName();
|
||||||
|
}
|
||||||
|
viewBinding.exportProgressAlertMsg.setText(getString(R.string.export_track_already_exists_msg, name));
|
||||||
setConflictVisibility(View.VISIBLE);
|
setConflictVisibility(View.VISIBLE);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export(track);
|
export(exportTask);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,10 +400,10 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
|||||||
* Overwrite the export file and set the autoConflict if user set the "do it for all" switch button.
|
* Overwrite the export file and set the autoConflict if user set the "do it for all" switch button.
|
||||||
*/
|
*/
|
||||||
public void overwrite() {
|
public void overwrite() {
|
||||||
export(track, CONFLICT_OVERWRITE);
|
export(exportTask, ConflictResolutionStrategy.CONFLICT_OVERWRITE);
|
||||||
|
|
||||||
if (viewBinding.exportProgressApplyToAll.isChecked()) {
|
if (viewBinding.exportProgressApplyToAll.isChecked()) {
|
||||||
autoConflict = CONFLICT_OVERWRITE;
|
autoConflict = ConflictResolutionStrategy.CONFLICT_OVERWRITE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,10 +411,10 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
|
|||||||
* Skip the export file and set the autoConflict if user set the "do it for all" switch button.
|
* Skip the export file and set the autoConflict if user set the "do it for all" switch button.
|
||||||
*/
|
*/
|
||||||
public void skip() {
|
public void skip() {
|
||||||
export(track, CONFLICT_SKIP);
|
export(exportTask, ConflictResolutionStrategy.CONFLICT_SKIP);
|
||||||
|
|
||||||
if (viewBinding.exportProgressApplyToAll.isChecked()) {
|
if (viewBinding.exportProgressApplyToAll.isChecked()) {
|
||||||
autoConflict = CONFLICT_SKIP;
|
autoConflict = ConflictResolutionStrategy.CONFLICT_SKIP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,9 +12,6 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.core.app.JobIntentService;
|
import androidx.core.app.JobIntentService;
|
||||||
import androidx.documentfile.provider.DocumentFile;
|
import androidx.documentfile.provider.DocumentFile;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
|
||||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
|
||||||
import de.dennisguse.opentracks.util.ExportUtils;
|
import de.dennisguse.opentracks.util.ExportUtils;
|
||||||
|
|
||||||
public class ExportService extends JobIntentService {
|
public class ExportService extends JobIntentService {
|
||||||
@@ -22,16 +19,14 @@ public class ExportService extends JobIntentService {
|
|||||||
private static final int JOB_ID = 1;
|
private static final int JOB_ID = 1;
|
||||||
|
|
||||||
private static final String EXTRA_RECEIVER = "extra_receiver";
|
private static final String EXTRA_RECEIVER = "extra_receiver";
|
||||||
private static final String EXTRA_TRACK_ID = "extra_track_id";
|
private static final String EXTRA_EXPORT_TASK = "export_task";
|
||||||
private static final String EXTRA_TRACK_FILE_FORMAT = "extra_track_file_format";
|
|
||||||
private static final String EXTRA_DIRECTORY_URI = "extra_directory_uri";
|
private static final String EXTRA_DIRECTORY_URI = "extra_directory_uri";
|
||||||
private static final String TAG = ExportService.class.getSimpleName();
|
private static final String TAG = ExportService.class.getSimpleName();
|
||||||
|
|
||||||
public static void enqueue(Context context, ExportServiceResultReceiver receiver, Track.Id trackId, TrackFileFormat trackFileFormat, Uri directoryUri) {
|
public static void enqueue(Context context, ExportServiceResultReceiver receiver, ExportTask exportTask, Uri directoryUri) {
|
||||||
Intent intent = new Intent(context, JobService.class);
|
Intent intent = new Intent(context, JobService.class);
|
||||||
intent.putExtra(EXTRA_RECEIVER, receiver);
|
intent.putExtra(EXTRA_RECEIVER, receiver);
|
||||||
intent.putExtra(EXTRA_TRACK_ID, trackId);
|
intent.putExtra(EXTRA_EXPORT_TASK, exportTask);
|
||||||
intent.putExtra(EXTRA_TRACK_FILE_FORMAT, trackFileFormat);
|
|
||||||
intent.putExtra(EXTRA_DIRECTORY_URI, directoryUri);
|
intent.putExtra(EXTRA_DIRECTORY_URI, directoryUri);
|
||||||
enqueueWork(context, ExportService.class, JOB_ID, intent);
|
enqueueWork(context, ExportService.class, JOB_ID, intent);
|
||||||
}
|
}
|
||||||
@@ -40,13 +35,12 @@ public class ExportService extends JobIntentService {
|
|||||||
protected void onHandleWork(@NonNull Intent intent) {
|
protected void onHandleWork(@NonNull Intent intent) {
|
||||||
// Get all data.
|
// Get all data.
|
||||||
ResultReceiver resultReceiver = intent.getParcelableExtra(EXTRA_RECEIVER);
|
ResultReceiver resultReceiver = intent.getParcelableExtra(EXTRA_RECEIVER);
|
||||||
Track.Id trackId = intent.getParcelableExtra(EXTRA_TRACK_ID);
|
ExportTask exportTask = intent.getParcelableExtra(EXTRA_EXPORT_TASK);
|
||||||
TrackFileFormat trackFileFormat = (TrackFileFormat) intent.getSerializableExtra(EXTRA_TRACK_FILE_FORMAT);
|
|
||||||
Uri directoryUri = intent.getParcelableExtra(EXTRA_DIRECTORY_URI);
|
Uri directoryUri = intent.getParcelableExtra(EXTRA_DIRECTORY_URI);
|
||||||
|
|
||||||
// Prepare resultCode and bundle to send to the receiver.
|
// Prepare resultCode and bundle to send to the receiver.
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putParcelable(ExportServiceResultReceiver.RESULT_EXTRA_TRACK_ID, trackId);
|
bundle.putParcelable(ExportServiceResultReceiver.RESULT_EXTRA_EXPORT_TASK, exportTask);
|
||||||
|
|
||||||
// Build directory file.
|
// Build directory file.
|
||||||
DocumentFile directoryFile = DocumentFile.fromTreeUri(this, directoryUri);
|
DocumentFile directoryFile = DocumentFile.fromTreeUri(this, directoryUri);
|
||||||
@@ -57,9 +51,7 @@ public class ExportService extends JobIntentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Export.
|
// Export.
|
||||||
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(this);
|
boolean success = ExportUtils.exportTrack(this, directoryFile, exportTask);
|
||||||
Track track = contentProviderUtils.getTrack(trackId);
|
|
||||||
boolean success = ExportUtils.exportTrack(this, trackFileFormat, directoryFile, track);
|
|
||||||
|
|
||||||
// Send result to the receiver.
|
// Send result to the receiver.
|
||||||
int resultCode = success ? ExportServiceResultReceiver.RESULT_CODE_SUCCESS : ExportServiceResultReceiver.RESULT_CODE_ERROR;
|
int resultCode = success ? ExportServiceResultReceiver.RESULT_CODE_SUCCESS : ExportServiceResultReceiver.RESULT_CODE_ERROR;
|
||||||
|
|||||||
+6
-8
@@ -6,8 +6,6 @@ import android.os.ResultReceiver;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new ResultReceive to receive results.
|
* Create a new ResultReceive to receive results.
|
||||||
* Your {@link #onReceiveResult} method will be called from the thread running <var>handler</var> if given, or from an arbitrary thread if null.
|
* Your {@link #onReceiveResult} method will be called from the thread running <var>handler</var> if given, or from an arbitrary thread if null.
|
||||||
@@ -17,7 +15,7 @@ public class ExportServiceResultReceiver extends ResultReceiver {
|
|||||||
public static final int RESULT_CODE_SUCCESS = 1;
|
public static final int RESULT_CODE_SUCCESS = 1;
|
||||||
public static final int RESULT_CODE_ERROR = 0;
|
public static final int RESULT_CODE_ERROR = 0;
|
||||||
|
|
||||||
public static final String RESULT_EXTRA_TRACK_ID = "result_extra_track_id";
|
public static final String RESULT_EXTRA_EXPORT_TASK = "result_extra_export_task";
|
||||||
|
|
||||||
private final Receiver receiver;
|
private final Receiver receiver;
|
||||||
|
|
||||||
@@ -28,19 +26,19 @@ public class ExportServiceResultReceiver extends ResultReceiver {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onReceiveResult(int resultCode, Bundle resultData) {
|
protected void onReceiveResult(int resultCode, Bundle resultData) {
|
||||||
Track.Id trackId = resultData.getParcelable(ExportServiceResultReceiver.RESULT_EXTRA_TRACK_ID);
|
ExportTask exportTask = resultData.getParcelable(ExportServiceResultReceiver.RESULT_EXTRA_EXPORT_TASK);
|
||||||
switch (resultCode) {
|
switch (resultCode) {
|
||||||
case RESULT_CODE_SUCCESS -> receiver.onExportSuccess(trackId);
|
case RESULT_CODE_SUCCESS -> receiver.onExportSuccess(exportTask);
|
||||||
case RESULT_CODE_ERROR -> receiver.onExportError(trackId);
|
case RESULT_CODE_ERROR -> receiver.onExportError(exportTask);
|
||||||
default -> throw new RuntimeException("Unknown resultCode.");
|
default -> throw new RuntimeException("Unknown resultCode.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface Receiver {
|
public interface Receiver {
|
||||||
default void onExportSuccess(Track.Id trackId) {
|
default void onExportSuccess(ExportTask exportTask) {
|
||||||
}
|
}
|
||||||
|
|
||||||
default void onExportError(Track.Id trackId) {
|
default void onExportError(ExportTask exportTask) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package de.dennisguse.opentracks.io.file.exporter;
|
||||||
|
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
|
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||||
|
|
||||||
|
public class ExportTask implements Parcelable {
|
||||||
|
|
||||||
|
private final String filename; //TODO Name will only be used when exporting multiple tracks
|
||||||
|
|
||||||
|
private final TrackFileFormat trackFileFormat;
|
||||||
|
|
||||||
|
private final List<Track.Id> trackIds;
|
||||||
|
|
||||||
|
public ExportTask(@Nullable String filename, @NonNull TrackFileFormat trackFileFormat, @NonNull List<Track.Id> trackIds) {
|
||||||
|
this.filename = filename;
|
||||||
|
this.trackFileFormat = trackFileFormat;
|
||||||
|
this.trackIds = trackIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ExportTask(Parcel in) {
|
||||||
|
filename = in.readString();
|
||||||
|
trackFileFormat = TrackFileFormat.valueOf(in.readString());
|
||||||
|
trackIds = in.createTypedArrayList(Track.Id.CREATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFilename() {
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TrackFileFormat getTrackFileFormat() {
|
||||||
|
return trackFileFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Track.Id> getTrackIds() {
|
||||||
|
return trackIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMultiExport() {
|
||||||
|
return trackIds.size() > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
ExportTask that = (ExportTask) o;
|
||||||
|
return Objects.equals(filename, that.filename) && trackFileFormat == that.trackFileFormat && Objects.equals(trackIds, that.trackIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(filename, trackFileFormat, trackIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
dest.writeString(filename);
|
||||||
|
dest.writeString(trackFileFormat.name());
|
||||||
|
dest.writeTypedList(trackIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Creator<ExportTask> CREATOR = new Creator<>() {
|
||||||
|
@Override
|
||||||
|
public ExportTask createFromParcel(Parcel in) {
|
||||||
|
return new ExportTask(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExportTask[] newArray(int size) {
|
||||||
|
return new ExportTask[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -98,12 +98,7 @@ public class GPXTrackExporter implements TrackExporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean writeTrack(Track track, @NonNull OutputStream outputStream) {
|
public boolean writeTrack(List<Track> tracks, @NonNull OutputStream outputStream) {
|
||||||
return writeTrack(new Track[]{track}, outputStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean writeTrack(Track[] tracks, @NonNull OutputStream outputStream) {
|
|
||||||
try {
|
try {
|
||||||
prepare(outputStream);
|
prepare(outputStream);
|
||||||
writeHeader();
|
writeHeader();
|
||||||
@@ -273,7 +268,7 @@ public class GPXTrackExporter implements TrackExporter {
|
|||||||
printWriter.println("<trk>");
|
printWriter.println("<trk>");
|
||||||
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
||||||
printWriter.println("<desc>" + StringUtils.formatCData(track.getDescription()) + "</desc>");
|
printWriter.println("<desc>" + StringUtils.formatCData(track.getDescription()) + "</desc>");
|
||||||
printWriter.println("<type>" + StringUtils.formatCData(track.getCategory()) + "</type>");
|
printWriter.println("<type>" + StringUtils.formatCData(track.getActivityType()) + "</type>");
|
||||||
|
|
||||||
printWriter.println("<extensions>");
|
printWriter.println("<extensions>");
|
||||||
printWriter.println("<topografix:color>c0c0c0</topografix:color>");
|
printWriter.println("<topografix:color>c0c0c0</topografix:color>");
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
private static final String TRACK_STYLE = "track";
|
private static final String TRACK_STYLE = "track";
|
||||||
private static final String SCHEMA_ID = "schema";
|
private static final String SCHEMA_ID = "schema";
|
||||||
|
|
||||||
public static final String EXTENDED_DATA_TYPE_CATEGORY = "type";
|
public static final String EXTENDED_DATA_TYPE_ACTIVITYTYPE = "type";
|
||||||
|
|
||||||
public static final String EXTENDED_DATA_TYPE_SPEED = "speed";
|
public static final String EXTENDED_DATA_TYPE_SPEED = "speed";
|
||||||
public static final String EXTENDED_DATA_TYPE_DISTANCE = "distance";
|
public static final String EXTENDED_DATA_TYPE_DISTANCE = "distance";
|
||||||
@@ -99,18 +99,14 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
this.contentProviderUtils = contentProviderUtils;
|
this.contentProviderUtils = contentProviderUtils;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean writeTrack(Track track, @NonNull OutputStream outputStream) {
|
public boolean writeTrack(List<Track> tracks, @NonNull OutputStream outputStream) {
|
||||||
return writeTrack(new Track[]{track}, outputStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean writeTrack(Track[] tracks, @NonNull OutputStream outputStream) {
|
|
||||||
try {
|
try {
|
||||||
prepare(outputStream);
|
prepare(outputStream);
|
||||||
writeHeader(tracks);
|
writeHeader(tracks);
|
||||||
for (Track track : tracks) {
|
for (Track track : tracks) {
|
||||||
writeMarkers(track);
|
writeMarkers(track);
|
||||||
}
|
}
|
||||||
boolean hasMultipleTracks = tracks.length > 1;
|
boolean hasMultipleTracks = tracks.size() > 1;
|
||||||
if (hasMultipleTracks) {
|
if (hasMultipleTracks) {
|
||||||
writeMultiTrackBegin();
|
writeMultiTrackBegin();
|
||||||
}
|
}
|
||||||
@@ -222,7 +218,7 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeHeader(Track[] tracks) {
|
private void writeHeader(List<Track> tracks) {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.println(
|
printWriter.println(
|
||||||
"""
|
"""
|
||||||
@@ -241,7 +237,7 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
printWriter.println("<open>1</open>");
|
printWriter.println("<open>1</open>");
|
||||||
printWriter.println("<visibility>1</visibility>");
|
printWriter.println("<visibility>1</visibility>");
|
||||||
|
|
||||||
Track track = tracks[0];
|
Track track = tracks.get(0);
|
||||||
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
printWriter.println("<name>" + StringUtils.formatCData(track.getName()) + "</name>");
|
||||||
printWriter.println("<atom:generator>" + StringUtils.formatCData(context.getString(R.string.app_name)) + "</atom:generator>");
|
printWriter.println("<atom:generator>" + StringUtils.formatCData(context.getString(R.string.app_name)) + "</atom:generator>");
|
||||||
|
|
||||||
@@ -293,7 +289,7 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
|
|
||||||
private void writeMultiTrackBegin() {
|
private void writeMultiTrackBegin() {
|
||||||
if (printWriter != null) {
|
if (printWriter != null) {
|
||||||
printWriter.println("<Folder id=tour>");
|
printWriter.println("<Folder id=\"tracks\">");
|
||||||
printWriter.println("<name>" + context.getString(R.string.generic_tracks) + "</name>");
|
printWriter.println("<name>" + context.getString(R.string.generic_tracks) + "</name>");
|
||||||
printWriter.println("<open>1</open>");
|
printWriter.println("<open>1</open>");
|
||||||
}
|
}
|
||||||
@@ -315,7 +311,7 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
printWriter.println("<opentracks:trackid>" + track.getUuid() + "</opentracks:trackid>");
|
printWriter.println("<opentracks:trackid>" + track.getUuid() + "</opentracks:trackid>");
|
||||||
|
|
||||||
printWriter.println("<styleUrl>#" + TRACK_STYLE + "</styleUrl>");
|
printWriter.println("<styleUrl>#" + TRACK_STYLE + "</styleUrl>");
|
||||||
writeCategory(track.getCategory());
|
writeActivityType(track.getActivityType());
|
||||||
printWriter.println("<MultiTrack>");
|
printWriter.println("<MultiTrack>");
|
||||||
printWriter.println("<altitudeMode>absolute</altitudeMode>");
|
printWriter.println("<altitudeMode>absolute</altitudeMode>");
|
||||||
printWriter.println("<interpolate>1</interpolate>");
|
printWriter.println("<interpolate>1</interpolate>");
|
||||||
@@ -431,18 +427,18 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
* Writes a placemark.
|
* Writes a placemark.
|
||||||
*
|
*
|
||||||
* @param name the name
|
* @param name the name
|
||||||
* @param category the category
|
* @param activityType the activityType
|
||||||
* @param description the description
|
* @param description the description
|
||||||
* @param location the location
|
* @param location the location
|
||||||
*/
|
*/
|
||||||
private void writePlacemark(String name, String category, String description, Location location, ZoneOffset zoneOffset) {
|
private void writePlacemark(String name, String activityType, String description, Location location, ZoneOffset zoneOffset) {
|
||||||
if (location != null) {
|
if (location != null) {
|
||||||
printWriter.println("<Placemark>");
|
printWriter.println("<Placemark>");
|
||||||
printWriter.println("<name>" + StringUtils.formatCData(name) + "</name>");
|
printWriter.println("<name>" + StringUtils.formatCData(name) + "</name>");
|
||||||
printWriter.println("<description>" + StringUtils.formatCData(description) + "</description>");
|
printWriter.println("<description>" + StringUtils.formatCData(description) + "</description>");
|
||||||
printWriter.println("<TimeStamp><when>" + getTime(zoneOffset, location) + "</when></TimeStamp>");
|
printWriter.println("<TimeStamp><when>" + getTime(zoneOffset, location) + "</when></TimeStamp>");
|
||||||
printWriter.println("<styleUrl>#" + KMLTrackExporter.MARKER_STYLE + "</styleUrl>");
|
printWriter.println("<styleUrl>#" + KMLTrackExporter.MARKER_STYLE + "</styleUrl>");
|
||||||
writeCategory(category);
|
writeActivityType(activityType);
|
||||||
printWriter.println("<Point>");
|
printWriter.println("<Point>");
|
||||||
printWriter.println("<coordinates>" + getCoordinates(location, ",") + "</coordinates>");
|
printWriter.println("<coordinates>" + getCoordinates(location, ",") + "</coordinates>");
|
||||||
printWriter.println("</Point>");
|
printWriter.println("</Point>");
|
||||||
@@ -463,7 +459,7 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
printWriter.println("</Camera>");
|
printWriter.println("</Camera>");
|
||||||
printWriter.println("<TimeStamp><when>" + getTime(zoneOffset, marker.getLocation()) + "</when></TimeStamp>");
|
printWriter.println("<TimeStamp><when>" + getTime(zoneOffset, marker.getLocation()) + "</when></TimeStamp>");
|
||||||
printWriter.println("<styleUrl>#" + MARKER_STYLE + "</styleUrl>");
|
printWriter.println("<styleUrl>#" + MARKER_STYLE + "</styleUrl>");
|
||||||
writeCategory(marker.getCategory());
|
writeActivityType(marker.getCategory());
|
||||||
|
|
||||||
if (exportPhotos) {
|
if (exportPhotos) {
|
||||||
printWriter.println("<Icon><href>" + KmzTrackExporter.buildKmzImageFilePath(marker) + "</href></Icon>");
|
printWriter.println("<Icon><href>" + KmzTrackExporter.buildKmzImageFilePath(marker) + "</href></Icon>");
|
||||||
@@ -518,23 +514,15 @@ public class KMLTrackExporter implements TrackExporter {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void writeActivityType(String actiivtyType) {
|
||||||
* Writes the category.
|
if (actiivtyType == null || actiivtyType.equals("")) {
|
||||||
*
|
|
||||||
* @param category the category
|
|
||||||
*/
|
|
||||||
private void writeCategory(String category) {
|
|
||||||
if (category == null || category.equals("")) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
printWriter.println("<ExtendedData>");
|
printWriter.println("<ExtendedData>");
|
||||||
printWriter.println("<Data name=\"" + EXTENDED_DATA_TYPE_CATEGORY + "\"><value>" + StringUtils.formatCData(category) + "</value></Data>");
|
printWriter.println("<Data name=\"" + EXTENDED_DATA_TYPE_ACTIVITYTYPE + "\"><value>" + StringUtils.formatCData(actiivtyType) + "</value></Data>");
|
||||||
printWriter.println("</ExtendedData>");
|
printWriter.println("</ExtendedData>");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes the track style.
|
|
||||||
*/
|
|
||||||
private void writeTrackStyle() {
|
private void writeTrackStyle() {
|
||||||
printWriter.println("<Style id=\"" + TRACK_STYLE + "\">");
|
printWriter.println("<Style id=\"" + TRACK_STYLE + "\">");
|
||||||
printWriter.println("<LineStyle><color>7f0000ff</color><width>4</width></LineStyle>");
|
printWriter.println("<LineStyle><color>7f0000ff</color><width>4</width></LineStyle>");
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import java.io.FileNotFoundException;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.util.List;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
@@ -64,12 +65,7 @@ public class KmzTrackExporter implements TrackExporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean writeTrack(Track track, @NonNull OutputStream outputStream) {
|
public boolean writeTrack(List<Track> tracks, @NonNull OutputStream outputStream) {
|
||||||
return writeTrack(new Track[]{track}, outputStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean writeTrack(Track[] tracks, @NonNull OutputStream outputStream) {
|
|
||||||
try (ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream)) {
|
try (ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream)) {
|
||||||
// Add kml file
|
// Add kml file
|
||||||
ZipEntry zipEntry = new ZipEntry(KMZ_KML_FILE);
|
ZipEntry zipEntry = new ZipEntry(KMZ_KML_FILE);
|
||||||
@@ -91,7 +87,7 @@ public class KmzTrackExporter implements TrackExporter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addImages(Context context, Track[] tracks, ZipOutputStream zipOutputStream) throws InterruptedException, IOException {
|
private void addImages(Context context, List<Track> tracks, ZipOutputStream zipOutputStream) throws InterruptedException, IOException {
|
||||||
for (Track track : tracks) {
|
for (Track track : tracks) {
|
||||||
try (Cursor cursor = contentProviderUtils.getMarkerCursor(track.getId(), null, -1)) {
|
try (Cursor cursor = contentProviderUtils.getMarkerCursor(track.getId(), null, -1)) {
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
@@ -145,6 +141,6 @@ public class KmzTrackExporter implements TrackExporter {
|
|||||||
public static String buildKmzImageFilePath(Marker marker) {
|
public static String buildKmzImageFilePath(Marker marker) {
|
||||||
String ext = FileUtils.getExtension(marker.getPhotoUrl());
|
String ext = FileUtils.getExtension(marker.getPhotoUrl());
|
||||||
ext = ext == null ? "" : "." + ext;
|
ext = ext == null ? "" : "." + ext;
|
||||||
return KMZ_IMAGES_DIR + File.separatorChar + FileUtils.sanitizeFileName(marker.getId().getId() + ext);
|
return KMZ_IMAGES_DIR + File.separatorChar + FileUtils.sanitizeFileName(marker.getId().id() + ext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -19,6 +19,7 @@ package de.dennisguse.opentracks.io.file.exporter;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
|
|
||||||
@@ -29,7 +30,5 @@ import de.dennisguse.opentracks.data.models.Track;
|
|||||||
*/
|
*/
|
||||||
public interface TrackExporter {
|
public interface TrackExporter {
|
||||||
|
|
||||||
boolean writeTrack(Track[] tracks, @NonNull OutputStream outputStream);
|
boolean writeTrack(List<Track> tracks, @NonNull OutputStream outputStream);
|
||||||
|
|
||||||
boolean writeTrack(Track track, @NonNull OutputStream outputStream);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,6 +101,30 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class ExportDirectoryChooserOneFileActivity extends DirectoryChooserActivity {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DocumentFile configureDirectoryChooserIntent(Intent intent) {
|
||||||
|
super.configureDirectoryChooserIntent(intent);
|
||||||
|
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||||
|
return IntentUtils.toDocumentFile(this, PreferencesUtils.getDefaultExportDirectoryUri());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isDirectoryValid(final DocumentFile directoryUri) {
|
||||||
|
return super.isDirectoryValid(directoryUri) && directoryUri.canWrite();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Intent createNextActivityIntent(Uri directoryUri) {
|
||||||
|
Intent intent = IntentUtils.newIntent(this, ExportActivity.class);
|
||||||
|
intent.putExtra(ExportActivity.EXTRA_DIRECTORY_URI_KEY, directoryUri);
|
||||||
|
intent.putExtra(ExportActivity.EXTRA_ONE_FILE_KEY, true);
|
||||||
|
intent.putExtra(ExportActivity.EXTRA_TRACKFILEFORMAT_KEY, PreferencesUtils.getExportTrackFileFormat());
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class DefaultTrackExportDirectoryChooserActivity extends DirectoryChooserActivity {
|
public static class DefaultTrackExportDirectoryChooserActivity extends DirectoryChooserActivity {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ public class GpxTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
|||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
private String description;
|
private String description;
|
||||||
private String category;
|
private String activityType;
|
||||||
private String latitude;
|
private String latitude;
|
||||||
private String longitude;
|
private String longitude;
|
||||||
private String altitude;
|
private String altitude;
|
||||||
@@ -159,7 +159,7 @@ public class GpxTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
|||||||
case TAG_GPX -> onFileEnd();
|
case TAG_GPX -> onFileEnd();
|
||||||
case TAG_MARKER -> onMarkerEnd();
|
case TAG_MARKER -> onMarkerEnd();
|
||||||
case TAG_TRACK -> {
|
case TAG_TRACK -> {
|
||||||
trackImporter.setTrack(context, name, uuid, description, category, null, zoneOffset);
|
trackImporter.setTrack(context, name, uuid, description, activityType, null, zoneOffset);
|
||||||
zoneOffset = null;
|
zoneOffset = null;
|
||||||
}
|
}
|
||||||
case TAG_TRACK_SEGMENT -> onTrackSegmentEnd();
|
case TAG_TRACK_SEGMENT -> onTrackSegmentEnd();
|
||||||
@@ -176,7 +176,7 @@ public class GpxTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
|||||||
}
|
}
|
||||||
case TAG_TYPE -> {
|
case TAG_TYPE -> {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
category = content.trim();
|
activityType = content.trim();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case TAG_TIME -> {
|
case TAG_TIME -> {
|
||||||
@@ -385,7 +385,7 @@ public class GpxTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
|||||||
private void onMarkerStart(Attributes attributes) {
|
private void onMarkerStart(Attributes attributes) {
|
||||||
name = null;
|
name = null;
|
||||||
description = null;
|
description = null;
|
||||||
category = null;
|
activityType = null;
|
||||||
photoUrl = null;
|
photoUrl = null;
|
||||||
latitude = attributes.getValue(ATTRIBUTE_LAT);
|
latitude = attributes.getValue(ATTRIBUTE_LAT);
|
||||||
longitude = attributes.getValue(ATTRIBUTE_LON);
|
longitude = attributes.getValue(ATTRIBUTE_LON);
|
||||||
@@ -415,8 +415,8 @@ public class GpxTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
|||||||
if (description != null) {
|
if (description != null) {
|
||||||
marker.setDescription(description);
|
marker.setDescription(description);
|
||||||
}
|
}
|
||||||
if (category != null) {
|
if (activityType != null) {
|
||||||
marker.setCategory(category);
|
marker.setCategory(activityType);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (photoUrl != null) {
|
if (photoUrl != null) {
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ public class ImportActivity extends FragmentActivity {
|
|||||||
});
|
});
|
||||||
|
|
||||||
//Works for a directory, but we might have received multiple files via SEND_MULTIPLE.
|
//Works for a directory, but we might have received multiple files via SEND_MULTIPLE.
|
||||||
viewBinding.bottomAppBarLayout.bottomAppBarTitle.setText(getString(R.string.import_progress_message, documentFiles.get(0).getName()));
|
viewBinding.importActivityToolbar.setTitle(getString(R.string.import_progress_message, documentFiles.get(0).getName()));
|
||||||
viewBinding.bottomAppBarLayout.bottomAppBar.setNavigationIcon(R.drawable.ic_logo_color_24dp);
|
viewBinding.bottomAppBarLayout.bottomAppBar.setNavigationIcon(R.drawable.ic_logo_color_24dp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
|||||||
private static final String TAG_MULTI_TRACK = "MultiTrack";
|
private static final String TAG_MULTI_TRACK = "MultiTrack";
|
||||||
private static final String TAG_KML22_MULTI_TRACK = "gx:MultiTrack";
|
private static final String TAG_KML22_MULTI_TRACK = "gx:MultiTrack";
|
||||||
|
|
||||||
private static final String TAG_DATA_CATEGORY = "Data"; //used for Track.category
|
private static final String TAG_DATA_ACTIVITYTYPE = "Data";
|
||||||
|
|
||||||
private static final String TAG_SIMPLE_ARRAY_DATA = "SimpleArrayData";
|
private static final String TAG_SIMPLE_ARRAY_DATA = "SimpleArrayData";
|
||||||
private static final String TAG_KML22_SIMPLE_ARRAY_DATA = "gx:SimpleArrayData";
|
private static final String TAG_KML22_SIMPLE_ARRAY_DATA = "gx:SimpleArrayData";
|
||||||
@@ -113,7 +113,7 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
|||||||
private String icon;
|
private String icon;
|
||||||
private String name;
|
private String name;
|
||||||
private String description;
|
private String description;
|
||||||
private String category;
|
private String activityType;
|
||||||
private String latitude;
|
private String latitude;
|
||||||
private String longitude;
|
private String longitude;
|
||||||
private String altitude;
|
private String altitude;
|
||||||
@@ -146,7 +146,7 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
|||||||
}
|
}
|
||||||
onTrackSegmentStart();
|
onTrackSegmentStart();
|
||||||
}
|
}
|
||||||
case TAG_DATA_CATEGORY, TAG_SIMPLE_ARRAY_DATA, TAG_KML22_SIMPLE_ARRAY_DATA ->
|
case TAG_DATA_ACTIVITYTYPE, TAG_SIMPLE_ARRAY_DATA, TAG_KML22_SIMPLE_ARRAY_DATA ->
|
||||||
dataType = attributes.getValue(ATTRIBUTE_NAME);
|
dataType = attributes.getValue(ATTRIBUTE_NAME);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,15 +165,15 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
|||||||
onMarkerEnd();
|
onMarkerEnd();
|
||||||
case TAG_COORDINATES -> onMarkerLocationEnd();
|
case TAG_COORDINATES -> onMarkerLocationEnd();
|
||||||
case TAG_MULTI_TRACK, TAG_KML22_MULTI_TRACK -> {
|
case TAG_MULTI_TRACK, TAG_KML22_MULTI_TRACK -> {
|
||||||
trackImporter.setTrack(context, name, uuid, description, category, icon, zoneOffset);
|
trackImporter.setTrack(context, name, uuid, description, activityType, icon, zoneOffset);
|
||||||
zoneOffset = null;
|
zoneOffset = null;
|
||||||
}
|
}
|
||||||
case TAG_TRACK, TAG_KML22_TRACK -> onTrackSegmentEnd();
|
case TAG_TRACK, TAG_KML22_TRACK -> onTrackSegmentEnd();
|
||||||
case TAG_COORD, TAG_KML22_COORD -> onCoordEnded();
|
case TAG_COORD, TAG_KML22_COORD -> onCoordEnded();
|
||||||
case TAG_VALUE, TAG_KML22_VALUE -> {
|
case TAG_VALUE, TAG_KML22_VALUE -> {
|
||||||
if (KMLTrackExporter.EXTENDED_DATA_TYPE_CATEGORY.equals(dataType)) {
|
if (KMLTrackExporter.EXTENDED_DATA_TYPE_ACTIVITYTYPE.equals(dataType)) {
|
||||||
if (content != null) {
|
if (content != null) {
|
||||||
category = content.trim();
|
activityType = content.trim();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
onExtendedDataValueEnd();
|
onExtendedDataValueEnd();
|
||||||
@@ -233,7 +233,7 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
|||||||
name = null;
|
name = null;
|
||||||
icon = null;
|
icon = null;
|
||||||
description = null;
|
description = null;
|
||||||
category = null;
|
activityType = null;
|
||||||
photoUrl = null;
|
photoUrl = null;
|
||||||
latitude = null;
|
latitude = null;
|
||||||
longitude = null;
|
longitude = null;
|
||||||
@@ -260,13 +260,13 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
|||||||
Marker marker = new Marker(null, new TrackPoint(TrackPoint.Type.TRACKPOINT, location, whenList.get(0))); //TODO Creating marker without need
|
Marker marker = new Marker(null, new TrackPoint(TrackPoint.Type.TRACKPOINT, location, whenList.get(0))); //TODO Creating marker without need
|
||||||
marker.setName(name != null ? name : "");
|
marker.setName(name != null ? name : "");
|
||||||
marker.setDescription(description != null ? description : "");
|
marker.setDescription(description != null ? description : "");
|
||||||
marker.setCategory(category != null ? category : "");
|
marker.setCategory(activityType != null ? activityType : "");
|
||||||
marker.setPhotoUrl(photoUrl);
|
marker.setPhotoUrl(photoUrl);
|
||||||
markers.add(marker);
|
markers.add(marker);
|
||||||
|
|
||||||
name = null;
|
name = null;
|
||||||
description = null;
|
description = null;
|
||||||
category = null;
|
activityType = null;
|
||||||
photoUrl = null;
|
photoUrl = null;
|
||||||
whenList.clear();
|
whenList.clear();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ public class TrackImporter {
|
|||||||
this.markers.addAll(markers);
|
this.markers.addAll(markers);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setTrack(Context context, String name, String uuid, String description, String category, String icon, @Nullable ZoneOffset zoneOffset) {
|
void setTrack(Context context, String name, String uuid, String description, String activityType, String icon, @Nullable ZoneOffset zoneOffset) {
|
||||||
track = new Track(zoneOffset != null ? zoneOffset : ZoneOffset.UTC);
|
track = new Track(zoneOffset != null ? zoneOffset : ZoneOffset.UTC);
|
||||||
track.setName(name != null ? name : "");
|
track.setName(name != null ? name : "");
|
||||||
|
|
||||||
@@ -103,11 +103,11 @@ public class TrackImporter {
|
|||||||
|
|
||||||
track.setDescription(description != null ? description : "");
|
track.setDescription(description != null ? description : "");
|
||||||
|
|
||||||
if (category != null) {
|
if (activityType != null) {
|
||||||
track.setCategory(category);
|
track.setActivityType(activityType);
|
||||||
|
|
||||||
if (icon == null) {
|
if (icon == null) {
|
||||||
icon = TrackIconUtils.getIconValue(context, category);
|
icon = TrackIconUtils.getIconValue(context, activityType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ public class StopRecording extends AbstractAPIActivity {
|
|||||||
protected void execute(TrackRecordingService service) {
|
protected void execute(TrackRecordingService service) {
|
||||||
RecordingData recordingData = service.getRecordingDataObservable().getValue();
|
RecordingData recordingData = service.getRecordingDataObservable().getValue();
|
||||||
Track.Id trackId = null;
|
Track.Id trackId = null;
|
||||||
if (recordingData != null && recordingData.getTrack() != null) {
|
if (recordingData != null && recordingData.track() != null) {
|
||||||
trackId = recordingData.getTrack().getId();
|
trackId = recordingData.track().getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
service.endCurrentTrack();
|
service.endCurrentTrack();
|
||||||
|
|||||||
+5
-4
@@ -77,7 +77,7 @@ public abstract class AbstractBluetoothConnectionManager<DataType> {
|
|||||||
BluetoothGattService gattService = null;
|
BluetoothGattService gattService = null;
|
||||||
ServiceMeasurementUUID serviceMeasurement = null;
|
ServiceMeasurementUUID serviceMeasurement = null;
|
||||||
for (ServiceMeasurementUUID s : serviceMeasurementUUIDs) {
|
for (ServiceMeasurementUUID s : serviceMeasurementUUIDs) {
|
||||||
gattService = gatt.getService(s.getServiceUUID());
|
gattService = gatt.getService(s.serviceUUID());
|
||||||
if (gattService != null) {
|
if (gattService != null) {
|
||||||
serviceMeasurement = s;
|
serviceMeasurement = s;
|
||||||
break;
|
break;
|
||||||
@@ -89,9 +89,9 @@ public abstract class AbstractBluetoothConnectionManager<DataType> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BluetoothGattCharacteristic characteristic = gattService.getCharacteristic(serviceMeasurement.getMeasurementUUID());
|
BluetoothGattCharacteristic characteristic = gattService.getCharacteristic(serviceMeasurement.measurementUUID());
|
||||||
if (characteristic == null) {
|
if (characteristic == null) {
|
||||||
Log.e(TAG, "Could not get BluetoothCharacteristic for address=" + gatt.getDevice().getAddress() + " serviceUUID=" + serviceMeasurement.getServiceUUID() + " characteristicUUID=" + serviceMeasurement.getMeasurementUUID());
|
Log.e(TAG, "Could not get BluetoothCharacteristic for address=" + gatt.getDevice().getAddress() + " serviceUUID=" + serviceMeasurement.serviceUUID() + " characteristicUUID=" + serviceMeasurement.measurementUUID());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
gatt.setCharacteristicNotification(characteristic, true);
|
gatt.setCharacteristicNotification(characteristic, true);
|
||||||
@@ -113,7 +113,7 @@ public abstract class AbstractBluetoothConnectionManager<DataType> {
|
|||||||
UUID serviceUUID = characteristic.getService().getUuid();
|
UUID serviceUUID = characteristic.getService().getUuid();
|
||||||
Log.d(TAG, "Received data from " + gatt.getDevice().getAddress() + " with service " + serviceUUID + " and characteristics " + characteristic.getUuid());
|
Log.d(TAG, "Received data from " + gatt.getDevice().getAddress() + " with service " + serviceUUID + " and characteristics " + characteristic.getUuid());
|
||||||
Optional<ServiceMeasurementUUID> serviceMeasurementUUID = serviceMeasurementUUIDs.stream()
|
Optional<ServiceMeasurementUUID> serviceMeasurementUUID = serviceMeasurementUUIDs.stream()
|
||||||
.filter(s -> s.getServiceUUID().equals(characteristic.getService().getUuid())).findFirst();
|
.filter(s -> s.serviceUUID().equals(characteristic.getService().getUuid())).findFirst();
|
||||||
if (serviceMeasurementUUID.isEmpty()) {
|
if (serviceMeasurementUUID.isEmpty()) {
|
||||||
Log.e(TAG, "Unknown service UUID; not supported?");
|
Log.e(TAG, "Unknown service UUID; not supported?");
|
||||||
return;
|
return;
|
||||||
@@ -189,6 +189,7 @@ public abstract class AbstractBluetoothConnectionManager<DataType> {
|
|||||||
*/
|
*/
|
||||||
protected abstract SensorData<DataType> parsePayload(@NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, @NonNull BluetoothGattCharacteristic characteristic);
|
protected abstract SensorData<DataType> parsePayload(@NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, @NonNull BluetoothGattCharacteristic characteristic);
|
||||||
|
|
||||||
|
@Deprecated //TODO REMOVE
|
||||||
interface SensorDataObserver {
|
interface SensorDataObserver {
|
||||||
|
|
||||||
void onChanged(SensorData<?> sensorData);
|
void onChanged(SensorData<?> sensorData);
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
package de.dennisguse.opentracks.sensors;
|
package de.dennisguse.opentracks.sensors;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.hardware.Sensor;
|
|
||||||
import android.hardware.SensorEvent;
|
|
||||||
import android.hardware.SensorEventListener;
|
|
||||||
import android.hardware.SensorManager;
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
@@ -12,56 +8,49 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import de.dennisguse.opentracks.data.models.AtmosphericPressure;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||||
|
import de.dennisguse.opentracks.sensors.driver.BarometerInternal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Estimates the altitude gain and altitude loss using the device's pressure sensor (i.e., barometer).
|
* Estimates the altitude gain and altitude loss using the device's pressure sensor (i.e., barometer).
|
||||||
*/
|
*/
|
||||||
public class AltitudeSumManager implements SensorConnector, SensorEventListener {
|
public class AltitudeSumManager implements SensorConnector {
|
||||||
|
|
||||||
private static final String TAG = AltitudeSumManager.class.getSimpleName();
|
private static final String TAG = AltitudeSumManager.class.getSimpleName();
|
||||||
|
|
||||||
private boolean isConnected = false;
|
private final BarometerInternal driver;
|
||||||
|
|
||||||
private float lastAcceptedPressureValue_hPa;
|
private AtmosphericPressure lastAcceptedSensorValue;
|
||||||
|
|
||||||
private float lastSeenSensorValue_hPa;
|
private AtmosphericPressure lastSeenSensorValue;
|
||||||
|
|
||||||
private Float altitudeGain_m;
|
private Float altitudeGain_m;
|
||||||
private Float altitudeLoss_m;
|
private Float altitudeLoss_m;
|
||||||
|
|
||||||
public void start(Context context, Handler handler) {
|
public AltitudeSumManager() {
|
||||||
SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
|
driver = new BarometerInternal();
|
||||||
|
|
||||||
Sensor pressureSensor = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE);
|
|
||||||
if (pressureSensor == null) {
|
|
||||||
Log.w(TAG, "No pressure sensor available.");
|
|
||||||
isConnected = false;
|
|
||||||
} else {
|
|
||||||
isConnected = sensorManager.registerListener(this, pressureSensor, (int) TimeUnit.SECONDS.toMicros(5), handler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lastAcceptedPressureValue_hPa = Float.NaN;
|
@VisibleForTesting
|
||||||
|
public AltitudeSumManager(BarometerInternal mock) {
|
||||||
|
this.driver = mock;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start(Context context, Handler handler) {
|
||||||
|
driver.connect(context, handler, this);
|
||||||
|
|
||||||
|
lastAcceptedSensorValue = null;
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stop(Context context) {
|
public void stop(Context context) {
|
||||||
Log.d(TAG, "Stop");
|
Log.d(TAG, "Stop");
|
||||||
|
|
||||||
SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
|
driver.disconnect(context);
|
||||||
sensorManager.unregisterListener(this);
|
|
||||||
|
|
||||||
isConnected = false;
|
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public void setConnected(boolean isConnected) {
|
|
||||||
this.isConnected = isConnected;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void fill(@NonNull TrackPoint trackPoint) {
|
public void fill(@NonNull TrackPoint trackPoint) {
|
||||||
trackPoint.setAltitudeGain(altitudeGain_m);
|
trackPoint.setAltitudeGain(altitudeGain_m);
|
||||||
trackPoint.setAltitudeLoss(altitudeLoss_m);
|
trackPoint.setAltitudeLoss(altitudeLoss_m);
|
||||||
@@ -69,7 +58,7 @@ public class AltitudeSumManager implements SensorConnector, SensorEventListener
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public Float getAltitudeGain_m() {
|
public Float getAltitudeGain_m() {
|
||||||
return isConnected ? altitudeGain_m : null;
|
return driver.isConnected() ? altitudeGain_m : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -77,21 +66,9 @@ public class AltitudeSumManager implements SensorConnector, SensorEventListener
|
|||||||
this.altitudeGain_m = altitudeGain_m;
|
this.altitudeGain_m = altitudeGain_m;
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public void addAltitudeGain_m(float altitudeGain_m) {
|
|
||||||
this.altitudeGain_m = this.altitudeGain_m == null ? 0f : this.altitudeGain_m;
|
|
||||||
this.altitudeGain_m += altitudeGain_m;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
public void addAltitudeLoss_m(Float altitudeLoss_m) {
|
|
||||||
this.altitudeLoss_m = this.altitudeLoss_m == null ? 0f : this.altitudeLoss_m;
|
|
||||||
this.altitudeLoss_m += altitudeLoss_m;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public Float getAltitudeLoss_m() {
|
public Float getAltitudeLoss_m() {
|
||||||
return isConnected ? altitudeLoss_m : null;
|
return driver.isConnected() ? altitudeLoss_m : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -105,41 +82,26 @@ public class AltitudeSumManager implements SensorConnector, SensorEventListener
|
|||||||
altitudeLoss_m = null;
|
altitudeLoss_m = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void onSensorValueChanged(AtmosphericPressure currentSensorValue) {
|
||||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
if (lastAcceptedSensorValue == null) {
|
||||||
Log.w(TAG, "Sensor accuracy changes are (currently) ignored.");
|
lastAcceptedSensorValue = currentSensorValue;
|
||||||
}
|
lastSeenSensorValue = currentSensorValue;
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSensorChanged(SensorEvent event) {
|
|
||||||
if (!isConnected) {
|
|
||||||
Log.w(TAG, "Not connected to sensor, cannot process data.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onSensorValueChanged(event.values[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
void onSensorValueChanged(float value_hPa) {
|
|
||||||
if (Float.isNaN(lastAcceptedPressureValue_hPa)) {
|
|
||||||
lastAcceptedPressureValue_hPa = value_hPa;
|
|
||||||
lastSeenSensorValue_hPa = value_hPa;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
altitudeGain_m = altitudeGain_m != null ? altitudeGain_m : 0;
|
altitudeGain_m = altitudeGain_m != null ? altitudeGain_m : 0;
|
||||||
altitudeLoss_m = altitudeLoss_m != null ? altitudeLoss_m : 0;
|
altitudeLoss_m = altitudeLoss_m != null ? altitudeLoss_m : 0;
|
||||||
|
|
||||||
PressureSensorUtils.AltitudeChange altitudeChange = PressureSensorUtils.computeChangesWithSmoothing_m(lastAcceptedPressureValue_hPa, lastSeenSensorValue_hPa, value_hPa);
|
PressureSensorUtils.AltitudeChange altitudeChange = PressureSensorUtils.computeChangesWithSmoothing_m(lastAcceptedSensorValue, lastSeenSensorValue, currentSensorValue);
|
||||||
if (altitudeChange != null) {
|
if (altitudeChange != null) {
|
||||||
altitudeGain_m += altitudeChange.getAltitudeGain_m();
|
altitudeGain_m += altitudeChange.getAltitudeGain_m();
|
||||||
|
|
||||||
altitudeLoss_m += altitudeChange.getAltitudeLoss_m();
|
altitudeLoss_m += altitudeChange.getAltitudeLoss_m();
|
||||||
|
|
||||||
lastAcceptedPressureValue_hPa = altitudeChange.getCurrentSensorValue_hPa();
|
lastAcceptedSensorValue = altitudeChange.currentSensorValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
lastSeenSensorValue_hPa = value_hPa;
|
lastSeenSensorValue = currentSensorValue;
|
||||||
|
|
||||||
Log.v(TAG, "altitude gain: " + altitudeGain_m + ", altitude loss: " + altitudeLoss_m);
|
Log.v(TAG, "altitude gain: " + altitudeGain_m + ", altitude loss: " + altitudeLoss_m);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -28,7 +28,7 @@ public class BluetoothConnectionManagerCyclingCadence extends AbstractBluetoothC
|
|||||||
if (serviceMeasurementUUID.equals(BluetoothUtils.CYCLING_POWER)) {
|
if (serviceMeasurementUUID.equals(BluetoothUtils.CYCLING_POWER)) {
|
||||||
SensorDataCyclingPower.Data data = BluetoothUtils.parseCyclingPower(address, sensorName, characteristic);
|
SensorDataCyclingPower.Data data = BluetoothUtils.parseCyclingPower(address, sensorName, characteristic);
|
||||||
if (data!= null) {
|
if (data!= null) {
|
||||||
return data.getCadence();
|
return data.cadence();
|
||||||
}
|
}
|
||||||
} else if (serviceMeasurementUUID.equals(BluetoothUtils.CYCLING_SPEED_CADENCE)) {
|
} else if (serviceMeasurementUUID.equals(BluetoothUtils.CYCLING_SPEED_CADENCE)) {
|
||||||
SensorDataCyclingCadenceAndDistanceSpeed cadenceAndSpeed = BluetoothUtils.parseCyclingCrankAndWheel(address, sensorName, characteristic);
|
SensorDataCyclingCadenceAndDistanceSpeed cadenceAndSpeed = BluetoothUtils.parseCyclingCrankAndWheel(address, sensorName, characteristic);
|
||||||
|
|||||||
+1
-1
@@ -22,6 +22,6 @@ public class BluetoothConnectionManagerCyclingPower extends AbstractBluetoothCon
|
|||||||
protected SensorDataCyclingPower parsePayload(@NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) {
|
protected SensorDataCyclingPower parsePayload(@NonNull ServiceMeasurementUUID serviceMeasurementUUID, String sensorName, String address, BluetoothGattCharacteristic characteristic) {
|
||||||
SensorDataCyclingPower.Data cyclingPower = BluetoothUtils.parseCyclingPower(address, sensorName, characteristic);
|
SensorDataCyclingPower.Data cyclingPower = BluetoothUtils.parseCyclingPower(address, sensorName, characteristic);
|
||||||
|
|
||||||
return cyclingPower != null ? cyclingPower.getPower() : null;
|
return cyclingPower != null ? cyclingPower.power() : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,17 +24,12 @@ import android.os.Handler;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.R;
|
import de.dennisguse.opentracks.R;
|
||||||
import de.dennisguse.opentracks.data.models.Distance;
|
|
||||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
|
||||||
import de.dennisguse.opentracks.sensors.sensorData.SensorData;
|
import de.dennisguse.opentracks.sensors.sensorData.SensorData;
|
||||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingCadence;
|
|
||||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataCyclingDistanceSpeed;
|
|
||||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataRunning;
|
|
||||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
|
|
||||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||||
import de.dennisguse.opentracks.util.PermissionRequester;
|
import de.dennisguse.opentracks.util.PermissionRequester;
|
||||||
|
|
||||||
@@ -61,20 +56,15 @@ public class BluetoothRemoteSensorManager implements SensorConnector, AbstractBl
|
|||||||
private final BluetoothAdapter bluetoothAdapter;
|
private final BluetoothAdapter bluetoothAdapter;
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final Handler handler;
|
private final Handler handler;
|
||||||
|
private final SensorManager.SensorDataChangedObserver observer;
|
||||||
private boolean started = false;
|
private boolean started = false;
|
||||||
|
|
||||||
private Distance preferenceWheelCircumference;
|
|
||||||
|
|
||||||
private final BluetoothConnectionManagerHeartRate heartRate = new BluetoothConnectionManagerHeartRate(this);
|
private final BluetoothConnectionManagerHeartRate heartRate = new BluetoothConnectionManagerHeartRate(this);
|
||||||
private final BluetoothConnectionManagerCyclingCadence cyclingCadence = new BluetoothConnectionManagerCyclingCadence(this);
|
private final BluetoothConnectionManagerCyclingCadence cyclingCadence = new BluetoothConnectionManagerCyclingCadence(this);
|
||||||
private final BluetoothConnectionManagerCyclingDistanceSpeed cyclingSpeed = new BluetoothConnectionManagerCyclingDistanceSpeed(this);
|
private final BluetoothConnectionManagerCyclingDistanceSpeed cyclingSpeed = new BluetoothConnectionManagerCyclingDistanceSpeed(this);
|
||||||
private final BluetoothConnectionManagerCyclingPower cyclingPower = new BluetoothConnectionManagerCyclingPower(this);
|
private final BluetoothConnectionManagerCyclingPower cyclingPower = new BluetoothConnectionManagerCyclingPower(this);
|
||||||
private final BluetoothConnectionRunningSpeedAndCadence runningSpeedAndCadence = new BluetoothConnectionRunningSpeedAndCadence(this);
|
private final BluetoothConnectionRunningSpeedAndCadence runningSpeedAndCadence = new BluetoothConnectionRunningSpeedAndCadence(this);
|
||||||
|
|
||||||
private final SensorDataSet sensorDataSet = new SensorDataSet();
|
|
||||||
|
|
||||||
private final SensorManager.SensorDataSetChangeObserver observer;
|
|
||||||
|
|
||||||
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
|
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||||
@@ -95,9 +85,6 @@ public class BluetoothRemoteSensorManager implements SensorConnector, AbstractBl
|
|||||||
|
|
||||||
connect(cyclingSpeed, address);
|
connect(cyclingSpeed, address);
|
||||||
}
|
}
|
||||||
if (PreferencesUtils.isKey(R.string.settings_sensor_bluetooth_cycling_speed_wheel_circumference_key, key)) {
|
|
||||||
preferenceWheelCircumference = PreferencesUtils.getWheelCircumference();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PreferencesUtils.isKey(R.string.settings_sensor_bluetooth_cycling_power_key, key)) {
|
if (PreferencesUtils.isKey(R.string.settings_sensor_bluetooth_cycling_power_key, key)) {
|
||||||
String address = PreferencesUtils.getBluetoothCyclingPowerSensorAddress();
|
String address = PreferencesUtils.getBluetoothCyclingPowerSensorAddress();
|
||||||
@@ -114,7 +101,7 @@ public class BluetoothRemoteSensorManager implements SensorConnector, AbstractBl
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public BluetoothRemoteSensorManager(@NonNull Context context, @NonNull Handler handler, @NonNull SensorManager.SensorDataSetChangeObserver observer) {
|
public BluetoothRemoteSensorManager(@NonNull Context context, @NonNull Handler handler, @Nullable SensorManager.SensorDataChangedObserver observer) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.handler = handler;
|
this.handler = handler;
|
||||||
this.observer = observer;
|
this.observer = observer;
|
||||||
@@ -137,8 +124,6 @@ public class BluetoothRemoteSensorManager implements SensorConnector, AbstractBl
|
|||||||
cyclingPower.disconnect();
|
cyclingPower.disconnect();
|
||||||
runningSpeedAndCadence.disconnect();
|
runningSpeedAndCadence.disconnect();
|
||||||
|
|
||||||
sensorDataSet.clear();
|
|
||||||
|
|
||||||
PreferencesUtils.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
PreferencesUtils.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||||
started = false;
|
started = false;
|
||||||
}
|
}
|
||||||
@@ -178,53 +163,14 @@ public class BluetoothRemoteSensorManager implements SensorConnector, AbstractBl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public SensorDataSet fill(@NonNull TrackPoint trackPoint) {
|
|
||||||
sensorDataSet.fillTrackPoint(trackPoint);
|
|
||||||
return new SensorDataSet(sensorDataSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void reset() {
|
|
||||||
sensorDataSet.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void onChanged(SensorData<?> sensorData) {
|
public synchronized void onChanged(SensorData<?> sensorData) {
|
||||||
if (sensorData instanceof SensorDataCyclingCadence) {
|
observer.onChange(sensorData);
|
||||||
SensorDataCyclingCadence previous = sensorDataSet.getCyclingCadence();
|
|
||||||
Log.d(TAG, "Previous: " + previous + "; current: " + sensorData);
|
|
||||||
|
|
||||||
if (sensorData.equals(previous)) {
|
|
||||||
Log.d(TAG, "onChanged: cadence data repeated.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
((SensorDataCyclingCadence) sensorData).compute(previous);
|
|
||||||
}
|
|
||||||
if (sensorData instanceof SensorDataCyclingDistanceSpeed) {
|
|
||||||
SensorDataCyclingDistanceSpeed previous = sensorDataSet.getCyclingDistanceSpeed();
|
|
||||||
Log.d(TAG, "Previous: " + previous + "; Current" + sensorData);
|
|
||||||
if (sensorData.equals(previous)) {
|
|
||||||
Log.d(TAG, "onChanged: cycling speed data repeated.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
((SensorDataCyclingDistanceSpeed) sensorData).compute(previous, preferenceWheelCircumference);
|
|
||||||
}
|
|
||||||
if (sensorData instanceof SensorDataRunning) {
|
|
||||||
SensorDataRunning previous = sensorDataSet.getRunningDistanceSpeedCadence();
|
|
||||||
Log.d(TAG, "Previous: " + previous + "; Current" + sensorData);
|
|
||||||
if (sensorData.equals(previous)) {
|
|
||||||
Log.d(TAG, "onChanged: running speed data repeated.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
((SensorDataRunning) sensorData).compute(previous);
|
|
||||||
}
|
|
||||||
|
|
||||||
sensorDataSet.set(sensorData);
|
|
||||||
observer.onChange(new SensorDataSet(sensorDataSet));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisconnecting(SensorData<?> sensorData) {
|
public void onDisconnecting(SensorData<?> sensorData) {
|
||||||
sensorDataSet.remove(sensorData);
|
observer.onDisconnect(sensorData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.data.models.AtmosphericPressure;
|
||||||
|
import de.dennisguse.opentracks.data.models.BatteryLevel;
|
||||||
import de.dennisguse.opentracks.data.models.Cadence;
|
import de.dennisguse.opentracks.data.models.Cadence;
|
||||||
import de.dennisguse.opentracks.data.models.Distance;
|
import de.dennisguse.opentracks.data.models.Distance;
|
||||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||||
@@ -48,19 +50,30 @@ public class BluetoothUtils {
|
|||||||
|
|
||||||
public static final UUID CLIENT_CHARACTERISTIC_CONFIG_UUID = new UUID(0x290200001000L, 0x800000805f9b34fbL);
|
public static final UUID CLIENT_CHARACTERISTIC_CONFIG_UUID = new UUID(0x290200001000L, 0x800000805f9b34fbL);
|
||||||
|
|
||||||
|
public static final ServiceMeasurementUUID BATTERY = new ServiceMeasurementUUID(
|
||||||
|
new UUID(0x180F00001000L, 0x800000805f9b34fbL),
|
||||||
|
new UUID(0x2A1900001000L, 0x800000805f9b34fbL)
|
||||||
|
);
|
||||||
|
|
||||||
public static final ServiceMeasurementUUID HEARTRATE = new ServiceMeasurementUUID(
|
public static final ServiceMeasurementUUID HEARTRATE = new ServiceMeasurementUUID(
|
||||||
new UUID(0x180D00001000L, 0x800000805f9b34fbL),
|
new UUID(0x180D00001000L, 0x800000805f9b34fbL),
|
||||||
new UUID(0x2A3700001000L, 0x800000805f9b34fbL)
|
new UUID(0x2A3700001000L, 0x800000805f9b34fbL)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Used for device discovery in preferences
|
// Used for device discovery in preferences
|
||||||
public static final List<ServiceMeasurementUUID> HEART_RATE_SUPPORTING_DEVICES = Collections.unmodifiableList(Arrays.asList(
|
public static final List<ServiceMeasurementUUID> HEART_RATE_SUPPORTING_DEVICES = List.of(
|
||||||
HEARTRATE,
|
HEARTRATE,
|
||||||
//Devices that support HEART_RATE_SERVICE_UUID, but do not announce HEART_RATE_SERVICE_UUID in there BLE announcement messages (during device discovery).
|
//Devices that support HEART_RATE_SERVICE_UUID, but do not announce HEART_RATE_SERVICE_UUID in there BLE announcement messages (during device discovery).
|
||||||
new ServiceMeasurementUUID(
|
new ServiceMeasurementUUID(
|
||||||
UUID.fromString("0000fee0-0000-1000-8000-00805f9b34fb"), //Miband3
|
UUID.fromString("0000fee0-0000-1000-8000-00805f9b34fb"), //Miband3
|
||||||
HEARTRATE.getMeasurementUUID()
|
HEARTRATE.measurementUUID()
|
||||||
))
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
private static final UUID ENVIRONMENTAL_SENSING_SERVICE = new UUID(0x181A00001000L, 0x800000805f9b34fbL);
|
||||||
|
public static final ServiceMeasurementUUID BAROMETRIC_PRESSURE = new ServiceMeasurementUUID(
|
||||||
|
ENVIRONMENTAL_SENSING_SERVICE,
|
||||||
|
new UUID(0x2A6D00001000L, 0x800000805f9b34fbL)
|
||||||
);
|
);
|
||||||
|
|
||||||
public static final ServiceMeasurementUUID CYCLING_POWER = new ServiceMeasurementUUID(
|
public static final ServiceMeasurementUUID CYCLING_POWER = new ServiceMeasurementUUID(
|
||||||
@@ -102,6 +115,17 @@ public class BluetoothUtils {
|
|||||||
return BluetoothUtils.getAdapter(context) != null;
|
return BluetoothUtils.getAdapter(context) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static BatteryLevel parseBatteryLevel(BluetoothGattCharacteristic characteristic) {
|
||||||
|
// DOCUMENTATION org.bluetooth.characteristic.battery_level.xml
|
||||||
|
byte[] raw = characteristic.getValue();
|
||||||
|
if (raw.length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int batteryLevel = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0);
|
||||||
|
return BatteryLevel.of(batteryLevel);
|
||||||
|
}
|
||||||
|
|
||||||
public static HeartRate parseHeartRate(BluetoothGattCharacteristic characteristic) {
|
public static HeartRate parseHeartRate(BluetoothGattCharacteristic characteristic) {
|
||||||
//DOCUMENTATION https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.heart_rate_measurement.xml
|
//DOCUMENTATION https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.heart_rate_measurement.xml
|
||||||
byte[] raw = characteristic.getValue();
|
byte[] raw = characteristic.getValue();
|
||||||
@@ -120,6 +144,17 @@ public class BluetoothUtils {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static AtmosphericPressure parseEnvironmentalSensing(BluetoothGattCharacteristic characteristic) {
|
||||||
|
byte[] raw = characteristic.getValue();
|
||||||
|
|
||||||
|
if (raw.length < 4) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer pressure = characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, 0);
|
||||||
|
return AtmosphericPressure.ofPA(pressure / 10f);
|
||||||
|
}
|
||||||
|
|
||||||
public static SensorDataCyclingPower.Data parseCyclingPower(String address, String sensorName, BluetoothGattCharacteristic characteristic) {
|
public static SensorDataCyclingPower.Data parseCyclingPower(String address, String sensorName, BluetoothGattCharacteristic characteristic) {
|
||||||
// DOCUMENTATION https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.cycling_power_measurement.xml
|
// DOCUMENTATION https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.cycling_power_measurement.xml
|
||||||
int valueLength = characteristic.getValue().length;
|
int valueLength = characteristic.getValue().length;
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import android.hardware.SensorManager;
|
|||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.data.models.AtmosphericPressure;
|
||||||
|
|
||||||
public class PressureSensorUtils {
|
public class PressureSensorUtils {
|
||||||
|
|
||||||
//Everything above is considered a meaningful change in altitude.
|
//Everything above is considered a meaningful change in altitude.
|
||||||
@@ -16,24 +18,7 @@ public class PressureSensorUtils {
|
|||||||
private PressureSensorUtils() {
|
private PressureSensorUtils() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class AltitudeChange {
|
public record AltitudeChange(AtmosphericPressure currentSensorValue, float altitudeChange_m) {
|
||||||
|
|
||||||
private final float currentSensorValue_hPa;
|
|
||||||
|
|
||||||
private final float altitudeChange_m;
|
|
||||||
|
|
||||||
public AltitudeChange(float currentSensorValue_hPa, float altitudeChange_m) {
|
|
||||||
this.currentSensorValue_hPa = currentSensorValue_hPa;
|
|
||||||
this.altitudeChange_m = altitudeChange_m;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getCurrentSensorValue_hPa() {
|
|
||||||
return currentSensorValue_hPa;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getAltitudeChange_m() {
|
|
||||||
return altitudeChange_m;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getAltitudeGain_m() {
|
public float getAltitudeGain_m() {
|
||||||
return altitudeChange_m > 0 ? altitudeChange_m : 0;
|
return altitudeChange_m > 0 ? altitudeChange_m : 0;
|
||||||
@@ -47,16 +32,16 @@ public class PressureSensorUtils {
|
|||||||
/**
|
/**
|
||||||
* Applies exponential smoothing to sensor value before computation.
|
* Applies exponential smoothing to sensor value before computation.
|
||||||
*/
|
*/
|
||||||
public static AltitudeChange computeChangesWithSmoothing_m(float lastAcceptedSensorValue_hPa, float lastSeenSensorValue_hPa, float currentSensorValue_hPa) {
|
public static AltitudeChange computeChangesWithSmoothing_m(AtmosphericPressure lastAcceptedSensorValue, AtmosphericPressure lastSeenSensorValue, AtmosphericPressure currentSensorValue) {
|
||||||
float nextSensorValue_hPa = EXPONENTIAL_SMOOTHING * currentSensorValue_hPa + (1 - EXPONENTIAL_SMOOTHING) * lastSeenSensorValue_hPa;
|
AtmosphericPressure nextSensorValue = AtmosphericPressure.ofHPA(EXPONENTIAL_SMOOTHING * currentSensorValue.getHPA() + (1 - EXPONENTIAL_SMOOTHING) * lastSeenSensorValue.getHPA());
|
||||||
|
|
||||||
return computeChanges(lastAcceptedSensorValue_hPa, nextSensorValue_hPa);
|
return computeChanges(lastAcceptedSensorValue, nextSensorValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public static AltitudeChange computeChanges(float lastAcceptedSensorValue_hPa, float currentSensorValue_hPa) {
|
public static AltitudeChange computeChanges(AtmosphericPressure lastAcceptedSensorValue, AtmosphericPressure currentSensorValue) {
|
||||||
float lastSensorValue_m = SensorManager.getAltitude(p0, lastAcceptedSensorValue_hPa);
|
float lastSensorValue_m = SensorManager.getAltitude(p0, lastAcceptedSensorValue.getHPA());
|
||||||
float currentSensorValue_m = SensorManager.getAltitude(p0, currentSensorValue_hPa);
|
float currentSensorValue_m = SensorManager.getAltitude(p0, currentSensorValue.getHPA());
|
||||||
|
|
||||||
float altitudeChange_m = currentSensorValue_m - lastSensorValue_m;
|
float altitudeChange_m = currentSensorValue_m - lastSensorValue_m;
|
||||||
if (Math.abs(altitudeChange_m) < ALTITUDE_CHANGE_DIFF_M) {
|
if (Math.abs(altitudeChange_m) < ALTITUDE_CHANGE_DIFF_M) {
|
||||||
@@ -64,8 +49,8 @@ public class PressureSensorUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Limit altitudeC change by ALTITUDE_CHANGE_DIFF and computes pressure value accordingly.
|
// Limit altitudeC change by ALTITUDE_CHANGE_DIFF and computes pressure value accordingly.
|
||||||
AltitudeChange altitudeChange = new AltitudeChange(currentSensorValue_hPa, altitudeChange_m);
|
AltitudeChange altitudeChange = new AltitudeChange(currentSensorValue, altitudeChange_m);
|
||||||
if (altitudeChange.getAltitudeChange_m() > 0) {
|
if (altitudeChange.altitudeChange_m() > 0) {
|
||||||
return new AltitudeChange(getBarometricPressure(lastSensorValue_m + ALTITUDE_CHANGE_DIFF_M), ALTITUDE_CHANGE_DIFF_M);
|
return new AltitudeChange(getBarometricPressure(lastSensorValue_m + ALTITUDE_CHANGE_DIFF_M), ALTITUDE_CHANGE_DIFF_M);
|
||||||
} else {
|
} else {
|
||||||
return new AltitudeChange(getBarometricPressure(lastSensorValue_m - ALTITUDE_CHANGE_DIFF_M), -1 * ALTITUDE_CHANGE_DIFF_M);
|
return new AltitudeChange(getBarometricPressure(lastSensorValue_m - ALTITUDE_CHANGE_DIFF_M), -1 * ALTITUDE_CHANGE_DIFF_M);
|
||||||
@@ -78,7 +63,7 @@ public class PressureSensorUtils {
|
|||||||
* {\color{White} p(h)} = p_0 \cdot \left( 1 - \frac{0{,}0065 \frac{\mathrm K}{\mathrm m} \cdot h}{T_0\ } \right)^{5{,}255}
|
* {\color{White} p(h)} = p_0 \cdot \left( 1 - \frac{0{,}0065 \frac{\mathrm K}{\mathrm m} \cdot h}{T_0\ } \right)^{5{,}255}
|
||||||
*/
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public static float getBarometricPressure(float altitude_m) {
|
public static AtmosphericPressure getBarometricPressure(float altitude_m) {
|
||||||
return (float) (p0 * Math.pow(1.0 - 0.0065 * altitude_m / 288.15, 5.255f));
|
return AtmosphericPressure.ofHPA((float) (p0 * Math.pow(1.0 - 0.0065 * altitude_m / 288.15, 5.255f)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,23 +9,53 @@ import androidx.annotation.VisibleForTesting;
|
|||||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||||
import de.dennisguse.opentracks.sensors.sensorData.SensorData;
|
import de.dennisguse.opentracks.sensors.sensorData.SensorData;
|
||||||
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
|
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
|
||||||
|
import de.dennisguse.opentracks.services.handlers.GPSManager;
|
||||||
|
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
||||||
|
|
||||||
public class SensorManager {
|
public class SensorManager {
|
||||||
|
|
||||||
private static final String TAG = SensorManager.class.getSimpleName();
|
private static final String TAG = SensorManager.class.getSimpleName();
|
||||||
|
|
||||||
|
//TODO Should be final and not be visible for testing
|
||||||
|
@VisibleForTesting
|
||||||
|
public SensorDataSet sensorDataSet = new SensorDataSet();
|
||||||
|
|
||||||
|
private final TrackPointCreator observer;
|
||||||
|
|
||||||
|
private final SensorDataChangedObserver listener = new SensorDataChangedObserver() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChange(SensorData<?> sensorData) {
|
||||||
|
sensorDataSet.set(sensorData);
|
||||||
|
observer.onChange(new SensorDataSet(sensorDataSet));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisconnect(SensorData<?> sensorData) {
|
||||||
|
sensorDataSet.remove(sensorData);
|
||||||
|
observer.onChange(new SensorDataSet(sensorDataSet));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private BluetoothRemoteSensorManager bluetoothSensorManager;
|
private BluetoothRemoteSensorManager bluetoothSensorManager;
|
||||||
|
|
||||||
private AltitudeSumManager altitudeSumManager;
|
private AltitudeSumManager altitudeSumManager;
|
||||||
|
|
||||||
public SensorManager(Context context, Handler handler, SensorDataSetChangeObserver observer) {
|
private GPSManager gpsManager;
|
||||||
bluetoothSensorManager = new BluetoothRemoteSensorManager(context, handler, observer);
|
|
||||||
altitudeSumManager = new AltitudeSumManager();
|
public SensorManager(TrackPointCreator observer) {
|
||||||
|
this.observer = observer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start(Context context, Handler handler) {
|
public void start(Context context, Handler handler) {
|
||||||
bluetoothSensorManager.start(context, handler);
|
gpsManager = new GPSManager(observer); //TODO Pass listener
|
||||||
|
gpsManager.start(context, handler);
|
||||||
|
|
||||||
|
altitudeSumManager = new AltitudeSumManager();
|
||||||
|
bluetoothSensorManager = new BluetoothRemoteSensorManager(context, handler, listener);
|
||||||
|
|
||||||
altitudeSumManager.start(context, handler);
|
altitudeSumManager.start(context, handler);
|
||||||
|
bluetoothSensorManager.start(context, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stop(Context context) {
|
public void stop(Context context) {
|
||||||
@@ -38,11 +68,19 @@ public class SensorManager {
|
|||||||
altitudeSumManager.stop(context);
|
altitudeSumManager.stop(context);
|
||||||
altitudeSumManager = null;
|
altitudeSumManager = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gpsManager != null) {
|
||||||
|
gpsManager.stop(context);
|
||||||
|
gpsManager = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
sensorDataSet.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public SensorDataSet fill(TrackPoint trackPoint) {
|
public SensorDataSet fill(TrackPoint trackPoint) {
|
||||||
altitudeSumManager.fill(trackPoint);
|
altitudeSumManager.fill(trackPoint);
|
||||||
return bluetoothSensorManager.fill(trackPoint);
|
sensorDataSet.fillTrackPoint(trackPoint);
|
||||||
|
return new SensorDataSet(sensorDataSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reset() {
|
public void reset() {
|
||||||
@@ -50,7 +88,7 @@ public class SensorManager {
|
|||||||
Log.d(TAG, "No recording running and no reset necessary.");
|
Log.d(TAG, "No recording running and no reset necessary.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bluetoothSensorManager.reset();
|
sensorDataSet.reset();
|
||||||
altitudeSumManager.reset();
|
altitudeSumManager.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,10 +98,8 @@ public class SensorManager {
|
|||||||
return bluetoothSensorManager;
|
return bluetoothSensorManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
public GPSManager getGpsManager() {
|
||||||
@VisibleForTesting
|
return gpsManager;
|
||||||
public void setBluetoothSensorManager(BluetoothRemoteSensorManager remoteSensorManager) {
|
|
||||||
this.bluetoothSensorManager = remoteSensorManager;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@@ -78,8 +114,9 @@ public class SensorManager {
|
|||||||
this.altitudeSumManager = altitudeSumManager;
|
this.altitudeSumManager = altitudeSumManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
public interface SensorDataChangedObserver {
|
||||||
public interface SensorDataSetChangeObserver {
|
void onChange(SensorData<?> sensorData);
|
||||||
void onChange(SensorDataSet sensorDataSet);
|
|
||||||
|
void onDisconnect(SensorData<?> sensorData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,43 +5,5 @@ import androidx.annotation.NonNull;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class ServiceMeasurementUUID {
|
public record ServiceMeasurementUUID(UUID serviceUUID, UUID measurementUUID) {
|
||||||
|
|
||||||
private final UUID serviceUUID;
|
|
||||||
private final UUID measurementUUID;
|
|
||||||
|
|
||||||
public ServiceMeasurementUUID(UUID serviceUUID, UUID measurementUUID) {
|
|
||||||
this.serviceUUID = serviceUUID;
|
|
||||||
this.measurementUUID = measurementUUID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UUID getServiceUUID() {
|
|
||||||
return serviceUUID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UUID getMeasurementUUID() {
|
|
||||||
return measurementUUID;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "ServiceMeasurementUUID{" +
|
|
||||||
"serviceUUID=" + serviceUUID +
|
|
||||||
", measurementUUID=" + measurementUUID +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
|
||||||
ServiceMeasurementUUID that = (ServiceMeasurementUUID) o;
|
|
||||||
return Objects.equals(serviceUUID, that.serviceUUID) && Objects.equals(measurementUUID, that.measurementUUID);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(serviceUUID, measurementUUID);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ public class UintUtils {
|
|||||||
* @return diff
|
* @return diff
|
||||||
*/
|
*/
|
||||||
public static long diff(long a, long b, final long UINT_MAX) {
|
public static long diff(long a, long b, final long UINT_MAX) {
|
||||||
|
|
||||||
if (a < 0 || b < 0) {
|
if (a < 0 || b < 0) {
|
||||||
throw new RuntimeException("a or b cannot be less than zero.");
|
throw new RuntimeException("a or b cannot be less than zero.");
|
||||||
}
|
}
|
||||||
@@ -25,6 +26,6 @@ public class UintUtils {
|
|||||||
return a - b;
|
return a - b;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (UINT_MAX - b) + a;
|
return (UINT_MAX + 1 - b) + a;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package de.dennisguse.opentracks.sensors.driver;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.hardware.Sensor;
|
||||||
|
import android.hardware.SensorEvent;
|
||||||
|
import android.hardware.SensorEventListener;
|
||||||
|
import android.hardware.SensorManager;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.data.models.AtmosphericPressure;
|
||||||
|
import de.dennisguse.opentracks.sensors.AltitudeSumManager;
|
||||||
|
|
||||||
|
public class BarometerInternal {
|
||||||
|
|
||||||
|
private static final String TAG = BarometerInternal.class.getSimpleName();
|
||||||
|
|
||||||
|
private static final int SAMPLING_PERIOD = (int) TimeUnit.SECONDS.toMicros(5);
|
||||||
|
|
||||||
|
private AltitudeSumManager observer;
|
||||||
|
|
||||||
|
private final SensorEventListener listener = new SensorEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onSensorChanged(SensorEvent event) {
|
||||||
|
if (!isConnected()) {
|
||||||
|
Log.w(TAG, "Not connected to sensor, cannot process data.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
observer.onSensorValueChanged(AtmosphericPressure.ofHPA(event.values[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||||
|
Log.w(TAG, "Sensor accuracy changes are (currently) ignored.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public void connect(Context context, Handler handler, AltitudeSumManager observer) {
|
||||||
|
this.observer = observer;
|
||||||
|
SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
|
||||||
|
Sensor pressureSensor = sensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE);
|
||||||
|
if (pressureSensor == null) {
|
||||||
|
Log.w(TAG, "No pressure sensor available.");
|
||||||
|
this.observer = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sensorManager.registerListener(listener, pressureSensor, SAMPLING_PERIOD, handler)) {
|
||||||
|
this.observer = observer;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnect(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disconnect(Context context) {
|
||||||
|
SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
|
||||||
|
sensorManager.unregisterListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isConnected() {
|
||||||
|
return observer != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-12
@@ -28,23 +28,12 @@ public class SensorDataCyclingPower extends SensorData<Power> {
|
|||||||
return super.toString() + " data=" + value;
|
return super.toString() + " data=" + value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Data {
|
public record Data(SensorDataCyclingPower power, SensorDataCyclingCadence cadence) {
|
||||||
private final SensorDataCyclingPower power;
|
|
||||||
private final SensorDataCyclingCadence cadence;
|
|
||||||
|
|
||||||
public Data(SensorDataCyclingPower power, @Nullable SensorDataCyclingCadence cadence) {
|
public Data(SensorDataCyclingPower power, @Nullable SensorDataCyclingCadence cadence) {
|
||||||
this.power = power;
|
this.power = power;
|
||||||
this.cadence = cadence;
|
this.cadence = cadence;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SensorDataCyclingPower getPower() {
|
|
||||||
return power;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SensorDataCyclingCadence getCadence() {
|
|
||||||
return cadence;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
|||||||
@@ -68,8 +68,8 @@ public final class SensorDataRunning extends SensorData<SensorDataRunning.Data>
|
|||||||
Distance overallDistance = null;
|
Distance overallDistance = null;
|
||||||
if (previous != null && previous.hasTotalDistance()) {
|
if (previous != null && previous.hasTotalDistance()) {
|
||||||
overallDistance = this.totalDistance.minus(previous.totalDistance);
|
overallDistance = this.totalDistance.minus(previous.totalDistance);
|
||||||
if (previous.hasValue() && previous.getValue().getDistance() != null) {
|
if (previous.hasValue() && previous.getValue().distance() != null) {
|
||||||
overallDistance = overallDistance.plus(previous.getValue().getDistance());
|
overallDistance = overallDistance.plus(previous.getValue().distance());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,41 +84,6 @@ public final class SensorDataRunning extends SensorData<SensorDataRunning.Data>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Data {
|
public record Data(Speed speed, Cadence cadence, @Nullable Distance distance) {}
|
||||||
private final Speed speed;
|
|
||||||
private final Cadence cadence;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private final Distance distance;
|
|
||||||
|
|
||||||
public Data(Speed speed, Cadence cadence, @Nullable Distance distance) {
|
|
||||||
this.speed = speed;
|
|
||||||
this.cadence = cadence;
|
|
||||||
this.distance = distance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Speed getSpeed() {
|
|
||||||
return speed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Cadence getCadence() {
|
|
||||||
return cadence;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public Distance getDistance() {
|
|
||||||
return distance;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "Data{" +
|
|
||||||
"speed=" + speed +
|
|
||||||
", cadence=" + cadence +
|
|
||||||
", distance=" + distance +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,22 @@
|
|||||||
package de.dennisguse.opentracks.sensors.sensorData;
|
package de.dennisguse.opentracks.sensors.sensorData;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.data.models.Cadence;
|
import de.dennisguse.opentracks.data.models.Cadence;
|
||||||
|
import de.dennisguse.opentracks.data.models.Distance;
|
||||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||||
import de.dennisguse.opentracks.data.models.Speed;
|
import de.dennisguse.opentracks.data.models.Speed;
|
||||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||||
|
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||||
|
|
||||||
public final class SensorDataSet {
|
public final class SensorDataSet {
|
||||||
|
|
||||||
|
private static final String TAG = SensorDataSet.class.getSimpleName();
|
||||||
|
|
||||||
private SensorDataHeartRate heartRate;
|
private SensorDataHeartRate heartRate;
|
||||||
|
|
||||||
private SensorDataCyclingCadence cyclingCadence;
|
private SensorDataCyclingCadence cyclingCadence;
|
||||||
@@ -57,7 +63,7 @@ public final class SensorDataSet {
|
|||||||
return new Pair<>(cyclingDistanceSpeed.getValue().getSpeed(), cyclingDistanceSpeed.getSensorNameOrAddress());
|
return new Pair<>(cyclingDistanceSpeed.getValue().getSpeed(), cyclingDistanceSpeed.getSensorNameOrAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasValue() && runningDistanceSpeedCadence.getValue().getSpeed() != null) {
|
if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasValue() && runningDistanceSpeedCadence.getValue().speed() != null) {
|
||||||
return new Pair<>(runningDistanceSpeedCadence.getSpeed(), runningDistanceSpeedCadence.getSensorNameOrAddress());
|
return new Pair<>(runningDistanceSpeedCadence.getSpeed(), runningDistanceSpeedCadence.getSensorNameOrAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +124,7 @@ public final class SensorDataSet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasValue()) {
|
if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasValue()) {
|
||||||
trackPoint.setSensorDistance(runningDistanceSpeedCadence.getValue().getDistance());
|
trackPoint.setSensorDistance(runningDistanceSpeedCadence.getValue().distance());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,28 +146,61 @@ public final class SensorDataSet {
|
|||||||
+ (runningDistanceSpeedCadence != null ? " " + runningDistanceSpeedCadence : "");
|
+ (runningDistanceSpeedCadence != null ? " " + runningDistanceSpeedCadence : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void set(@NonNull SensorData<?> type, SensorData<?> data) {
|
private void set(@NonNull SensorData<?> type, @Nullable SensorData<?> sensorData) {
|
||||||
if (type instanceof SensorDataHeartRate) {
|
if (type instanceof SensorDataHeartRate) {
|
||||||
this.heartRate = (SensorDataHeartRate) data;
|
this.heartRate = (SensorDataHeartRate) sensorData;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type instanceof SensorDataCyclingCadence) {
|
if (type instanceof SensorDataCyclingCadence) {
|
||||||
this.cyclingCadence = (SensorDataCyclingCadence) data;
|
SensorDataCyclingCadence previous = getCyclingCadence();
|
||||||
|
Log.d(TAG, "Previous: " + previous + "; current: " + sensorData);
|
||||||
|
|
||||||
|
if (sensorData != null && sensorData.equals(previous)) {
|
||||||
|
Log.d(TAG, "onChanged: cadence data repeated.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.cyclingCadence = (SensorDataCyclingCadence) sensorData;
|
||||||
|
if (this.cyclingCadence != null) {
|
||||||
|
this.cyclingCadence.compute(previous);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (type instanceof SensorDataCyclingDistanceSpeed) {
|
if (type instanceof SensorDataCyclingDistanceSpeed) {
|
||||||
this.cyclingDistanceSpeed = (SensorDataCyclingDistanceSpeed) data;
|
SensorDataCyclingDistanceSpeed previous = getCyclingDistanceSpeed();
|
||||||
|
Log.d(TAG, "Previous: " + previous + "; Current" + sensorData);
|
||||||
|
if (sensorData != null && sensorData.equals(previous)) {
|
||||||
|
Log.d(TAG, "onChanged: cycling speed data repeated.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Distance preferenceWheelCircumference = PreferencesUtils.getWheelCircumference(); //TODO Fetch once and then listen for changes.
|
||||||
|
|
||||||
|
this.cyclingDistanceSpeed = (SensorDataCyclingDistanceSpeed) sensorData;
|
||||||
|
if (this.cyclingDistanceSpeed != null) {
|
||||||
|
this.cyclingDistanceSpeed.compute(previous, preferenceWheelCircumference);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type instanceof SensorDataCyclingPower) {
|
if (type instanceof SensorDataCyclingPower) {
|
||||||
this.cyclingPower = (SensorDataCyclingPower) data;
|
this.cyclingPower = (SensorDataCyclingPower) sensorData;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type instanceof SensorDataRunning) {
|
if (type instanceof SensorDataRunning) {
|
||||||
this.runningDistanceSpeedCadence = (SensorDataRunning) data;
|
SensorDataRunning previous = getRunningDistanceSpeedCadence();
|
||||||
|
Log.d(TAG, "Previous: " + previous + "; Current" + sensorData);
|
||||||
|
if (sensorData != null && sensorData.equals(previous)) {
|
||||||
|
Log.d(TAG, "onChanged: running speed data repeated.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.runningDistanceSpeedCadence = (SensorDataRunning) sensorData;
|
||||||
|
if (this.runningDistanceSpeedCadence != null) {
|
||||||
|
this.runningDistanceSpeedCadence.compute(previous);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user