forked from upstream-mirrors/OpenTracks
Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9f858168f8 | |||
| cfdcea2d88 | |||
| 2d2db41c5d | |||
| 2c73d0645a | |||
| ed1027eccc | |||
| 0dec17eba5 | |||
| 774d701a09 | |||
| 23bcbf09a5 | |||
| 115966cbdf | |||
| 1862bcc6f4 | |||
| 900910e9f7 | |||
| 45571c68c9 | |||
| 083b620e41 | |||
| 2f7a56bb17 | |||
| d8988663cb | |||
| c742dae6e0 | |||
| 292437e6bd | |||
| 8e9cde7222 | |||
| d07ab3cc22 | |||
| 90493ae498 | |||
| f6b994572a | |||
| 24826ff442 | |||
| d97e5ab1ef | |||
| 52a110d998 | |||
| 12e18b0bcd | |||
| 66dac4b0ca | |||
| 1bb8cbb82a | |||
| 2f5b7b63e4 | |||
| 58ca78b976 | |||
| 8079a25a42 | |||
| 0527e4ab14 | |||
| b6b4c5aa39 | |||
| 8c5c9d7947 | |||
| eac3d4b3db | |||
| d7bc550b29 | |||
| d76d5673a8 | |||
| 847be686ea | |||
| 1e3445cfa2 | |||
| d0f7939f1a | |||
| 000b8149f8 | |||
| 05ffa5a373 | |||
| e92f1107aa | |||
| 2ad1fab670 | |||
| 8c04129ef9 | |||
| 400d9fe948 | |||
| 5d79510750 | |||
| 18bb64580f | |||
| 7adebfc0d1 | |||
| 2178ff97d3 | |||
| f070495fb4 | |||
| d99fb5ea47 | |||
| d69a726e0e | |||
| e0111c2ffd | |||
| 487bd6c3a0 | |||
| 40aab9444a |
@@ -7,7 +7,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
|
||||
- uses: actions/setup-java@v2
|
||||
with:
|
||||
|
||||
@@ -9,7 +9,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
|
||||
- uses: actions/setup-java@v2
|
||||
with:
|
||||
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
target: [ default ]
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
|
||||
- uses: actions/setup-java@v2
|
||||
with:
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
java-version: '11'
|
||||
|
||||
- name: run tests
|
||||
uses: reactivecircus/android-emulator-runner@v2.20.0
|
||||
uses: reactivecircus/android-emulator-runner@v2.21.0
|
||||
with:
|
||||
api-level: ${{ matrix.api-level }}
|
||||
target: ${{ matrix.target }}
|
||||
|
||||
Generated
+1
-1
@@ -6,4 +6,4 @@
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
||||
</project>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Overview of tested Bluetooth LE sensors
|
||||
|
||||
## HRS: Heartrate Service
|
||||
## 0x180D: Heartrate Service
|
||||
|
||||
* Decathlon|Geonaute|Kalenji Dual HRM Belt
|
||||
* moofit heart rate monitor
|
||||
@@ -13,7 +13,7 @@
|
||||
* Amazfit Neo. But first you have to enable "Discoverable" and "Activity heart rate sharing" in the official Zepp app. (after pairing with OpenTracks you can disable them again)
|
||||
* Garmin HRM-Dual (Reference: 010-12883-00)
|
||||
|
||||
## CSCP: Cycling Cadence and Speed Service
|
||||
## 0x1816: Cycling Cadence and Speed Service
|
||||
|
||||
Please note that the according to the specification these sensors _may_ provide speed as well as cadence.
|
||||
However, often only one value is provided.
|
||||
@@ -36,7 +36,7 @@ However, often only one value is provided.
|
||||
This sensor reports cadence data as speed.
|
||||
A workaround is in place.
|
||||
|
||||
## Cycling Power Measurement
|
||||
## 0x1818: Power Measurement
|
||||
|
||||
We only support _Instantaneous Power_.
|
||||
|
||||
@@ -44,6 +44,7 @@ _WARNING_: we only support physically movements with valid GPS coordinates.
|
||||
Using a device indoors (no GPS available) will only show the current power values, but won't store anything.
|
||||
Also the distance is not computed.
|
||||
|
||||
For cycling:
|
||||
* Assioma Duo Powermeter
|
||||
* Elite drivo II
|
||||
* QUARQ Red DZero Powermeter
|
||||
@@ -51,8 +52,28 @@ Also the distance is not computed.
|
||||
* Wahoo Kickr v4.0
|
||||
* Rotor 2INpower DM Road (only supports power measurement, cadence measurement is propietary @ firmware v1.061)
|
||||
|
||||
## RSC: running cadence and speed
|
||||
For running:
|
||||
* Stryd
|
||||
|
||||
## 0x1814: running cadence and speed
|
||||
|
||||
We do not support type of movement (i.e., walking vs running).
|
||||
|
||||
* Polar Stride
|
||||
* Stryd (incl. power)
|
||||
|
||||
## Tested Barometric Sensor Smartphones
|
||||
|
||||
There are smartphones that have a barometric sensor that OpenTracks uses to compute altitude gain and loss.
|
||||
|
||||
In our tests there are smartphones whose barometric sensor works well and others don't. Here is a list of the smartphones tested:
|
||||
|
||||
|Brand|Model|Android Version|Tests|
|
||||
|-----|-----|---------------|-----------|
|
||||
|Google|Pixel|LineageOS 17.1|Ok|
|
||||
|Google|Pixel 2 XL|LineageOS 17.1|Ok on the bike handlebars but it fails when I carry it in a pocket or backpack|
|
||||
|Google|Pixel 3|Android 11|Failure|
|
||||
|Google|Pixel 3a|Android 11|Ok|
|
||||
|Google|Pixel 3a|Lineage 18.1|Ok|
|
||||
|Google|Pixel 4a|Android 11|Ok|
|
||||
|Sony|Xperia XZ1 Compact|LineageOS 16.1|Failure|
|
||||
|
||||
+5
-5
@@ -6,7 +6,7 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.0.2'
|
||||
classpath 'com.android.tools.build:gradle:7.0.3'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,8 +50,8 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId "de.dennisguse.opentracks"
|
||||
versionCode 4441
|
||||
versionName "v3.20.1"
|
||||
versionCode 4496
|
||||
versionName "v3.21.0"
|
||||
|
||||
buildConfigField "String", "VERSION_NAME_FULL", "\"${getVersionName()}\""
|
||||
|
||||
@@ -59,7 +59,7 @@ android {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 31
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
testInstrumentationRunner "de.dennisguse.opentracks.TestRunner"
|
||||
testInstrumentationRunnerArguments clearPackageData: 'true'
|
||||
}
|
||||
signingConfigs {
|
||||
@@ -114,7 +114,7 @@ dependencies {
|
||||
androidTestImplementation 'androidx.test:rules:1.4.0'
|
||||
androidTestImplementation 'androidx.test:runner:1.4.0'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
androidTestImplementation 'org.mockito:mockito-android:3.12.4'
|
||||
androidTestImplementation 'org.mockito:mockito-android:4.0.0'
|
||||
|
||||
androidTestUtil 'androidx.test:orchestrator:1.4.0'
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
v3.20.2: OpenTracks
|
||||
|
||||
Changes:
|
||||
- Elevation gain and loss are integers instead of floats
|
||||
- Ask for permission before using Bluetooth by a Toast message
|
||||
|
||||
Bugfix:
|
||||
- Set FLAG_IMMUTABLE for PendingIntents to avoid crash on Android 12
|
||||
- Cadence sensor returns 0 instead of None when user is not pedaling
|
||||
- Speed sensor returns 0 instead of the last speed when it's stopped
|
||||
- With sensors, moving time stops when is not moving
|
||||
@@ -0,0 +1,19 @@
|
||||
v3.21.0: OpenTracks
|
||||
|
||||
Changes:
|
||||
- While recording: remove idle/non-idle creation of TrackPoints
|
||||
- Settings are now in separate folders
|
||||
- Opening OpenTracks from notification (while recording) and pressing back opens track list
|
||||
- GPX compatibilty: support <speed> for import
|
||||
|
||||
Bugfixes:
|
||||
- Altitude and distance (from BlE sensor): only reset counters if data was actually stored
|
||||
- Import without speed, requires computing speed
|
||||
- BLE running sensor not connected, should not be reported as present
|
||||
- BLE running sensor: show data while recording
|
||||
- Activity type icon was not updated while changing activity
|
||||
|
||||
Developer:
|
||||
- SharedPrefences via static reference
|
||||
- Tests are executed against in-memory database
|
||||
- TrackRecordingManager keeps last TrackPoint in memory
|
||||
@@ -1,6 +1,6 @@
|
||||
OpenTracks estas aplikaĵo por registri vian sportan aktivecon, kiu plene respektas vian privatecon.
|
||||
|
||||
Eblaĵoj:
|
||||
Ebloj:
|
||||
• registri vian sportan kaj alian aktivecon eksterdome
|
||||
• fotoj kaj markoj: marki interesajn lokojn dum registri aktivecon
|
||||
• registri ŝanĝon de altitudo uzante barometron
|
||||
|
||||
@@ -15,7 +15,6 @@ import androidx.test.rule.ActivityTestRule;
|
||||
import androidx.test.rule.GrantPermissionRule;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -27,7 +26,7 @@ import static androidx.test.espresso.matcher.ViewMatchers.*;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.DataField;
|
||||
import de.dennisguse.opentracks.content.data.Layout;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.Matcher;
|
||||
@@ -59,8 +58,7 @@ public class EspressoCustomLayoutTest {
|
||||
trackControllerRecordButton.perform(click());
|
||||
|
||||
// Get custom layout preferences and check all data fields are showed.
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
Layout layout = PreferencesUtils.getCustomLayout(sharedPreferences, context);
|
||||
Layout layout = PreferencesUtils.getCustomLayout();
|
||||
|
||||
onView(withId(R.id.stats_recycler_view)).check(new RecyclerViewItemCountAssertion((int) layout.getFields().stream().filter(DataField::isVisible).count()));
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
@@ -9,7 +8,7 @@ import org.junit.rules.TestRule;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
public class PreferenceMetricUnitRule implements TestRule {
|
||||
private final Context context;
|
||||
@@ -25,14 +24,13 @@ public class PreferenceMetricUnitRule implements TestRule {
|
||||
return new Statement() {
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
final boolean previousMetricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, context);
|
||||
final boolean previousMetricUnits = PreferencesUtils.isMetricUnits();
|
||||
|
||||
try {
|
||||
PreferencesUtils.setMetricUnits(sharedPreferences, context, metricUnits);
|
||||
PreferencesUtils.setMetricUnits(metricUnits);
|
||||
base.evaluate();
|
||||
} finally {
|
||||
PreferencesUtils.setMetricUnits(sharedPreferences, context, previousMetricUnits);
|
||||
PreferencesUtils.setMetricUnits(previousMetricUnits);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
public class TestApplication extends Startup {
|
||||
@Override
|
||||
public String getDatabaseName() {
|
||||
// null will make SQLiteOpenHelper create an in-memory database
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.runner.AndroidJUnitRunner;
|
||||
|
||||
public class TestRunner extends AndroidJUnitRunner {
|
||||
@Override
|
||||
public Application newApplication(ClassLoader cl, String className, Context context) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
|
||||
return super.newApplication(cl, TestApplication.class.getName(), context);
|
||||
}
|
||||
}
|
||||
@@ -78,7 +78,7 @@ public class ChartPointTest {
|
||||
// given
|
||||
TrackPoint trackPoint = TrackStubUtils.createDefaultTrackPoint();
|
||||
trackPoint.setHeartRate_bpm(100f);
|
||||
trackPoint.setCyclingCadence_rpm(101f);
|
||||
trackPoint.setCadence_rpm(101f);
|
||||
trackPoint.setPower(102f);
|
||||
|
||||
TrackStatistics statistics = new TrackStatistics();
|
||||
|
||||
@@ -120,7 +120,7 @@ public class TestDataUtil {
|
||||
trackPoint.setSpeed(Speed.of(5f + (i / 10f)));
|
||||
|
||||
trackPoint.setHeartRate_bpm(100f + i);
|
||||
trackPoint.setCyclingCadence_rpm(300f + i);
|
||||
trackPoint.setCadence_rpm(300f + i);
|
||||
trackPoint.setPower(400f + i);
|
||||
trackPoint.setAltitudeGain(ALTITUDE_GAIN);
|
||||
trackPoint.setAltitudeLoss(ALTITUDE_LOSS);
|
||||
|
||||
@@ -17,7 +17,7 @@ public class TestSensorDataUtil {
|
||||
tp.setLatitude(TestDataUtil.INITIAL_LATITUDE + (double) i / 10000.0);
|
||||
tp.setLongitude(TestDataUtil.INITIAL_LONGITUDE - (double) i / 10000.0);
|
||||
tp.setHeartRate_bpm(hr);
|
||||
tp.setCyclingCadence_rpm(cadence);
|
||||
tp.setCadence_rpm(cadence);
|
||||
tp.setPower(power);
|
||||
tp.setHorizontalAccuracy(Distance.of(1f));
|
||||
tp.setAltitude(1f);
|
||||
|
||||
+3
-3
@@ -835,7 +835,7 @@ public class CustomContentProviderUtilsTest {
|
||||
|
||||
TrackPoint trackPoint = TestDataUtil.createTrackPoint(5);
|
||||
trackPoint.setHeartRate_bpm(1F);
|
||||
trackPoint.setCyclingCadence_rpm(2F);
|
||||
trackPoint.setCadence_rpm(2F);
|
||||
trackPoint.setPower(3F);
|
||||
|
||||
// when
|
||||
@@ -845,7 +845,7 @@ public class CustomContentProviderUtilsTest {
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
assertTrue(trackPoints.get(10).hasHeartRate());
|
||||
assertEquals(trackPoint.getHeartRate_bpm(), trackPoints.get(10).getHeartRate_bpm(), 0.01);
|
||||
assertEquals(trackPoint.getCyclingCadence_rpm(), trackPoints.get(10).getCyclingCadence_rpm(), 0.01);
|
||||
assertEquals(trackPoint.getCadence_rpm(), trackPoints.get(10).getCadence_rpm(), 0.01);
|
||||
assertEquals(trackPoint.getPower(), trackPoints.get(10).getPower(), 0.01);
|
||||
}
|
||||
|
||||
@@ -934,7 +934,7 @@ public class CustomContentProviderUtilsTest {
|
||||
TrackPoint trackPoint = TestDataUtil.createTrackPoint(1);
|
||||
trackPoint.setType(TrackPoint.Type.TRACKPOINT);
|
||||
trackPoint.setPower(null);
|
||||
trackPoint.setCyclingCadence_rpm(null);
|
||||
trackPoint.setCadence_rpm(null);
|
||||
trackPoint.setHeartRate_bpm(null);
|
||||
trackPointList.add(trackPoint);
|
||||
Track.Id trackId = new Track.Id(System.currentTimeMillis());
|
||||
|
||||
+32
@@ -2,6 +2,7 @@ package de.dennisguse.opentracks.content.sensor;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@@ -11,6 +12,7 @@ import de.dennisguse.opentracks.util.UintUtils;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class SensorDataCyclingTest {
|
||||
@@ -81,7 +83,9 @@ public class SensorDataCyclingTest {
|
||||
assertEquals(60, current.getValue(), 0.01);
|
||||
}
|
||||
|
||||
@Ignore("Disabled from #953")
|
||||
@Test
|
||||
@Deprecated
|
||||
public void compute_cadence_rollOverCount() {
|
||||
// given
|
||||
SensorDataCycling.Cadence previous = new SensorDataCycling.Cadence("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
|
||||
@@ -94,6 +98,19 @@ public class SensorDataCyclingTest {
|
||||
assertEquals(60, current.getValue(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compute_cadence_overflow() {
|
||||
// given
|
||||
SensorDataCycling.Cadence previous = new SensorDataCycling.Cadence("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
|
||||
SensorDataCycling.Cadence current = new SensorDataCycling.Cadence("sensorAddress", "sensorName", 0, 2048);
|
||||
|
||||
// when
|
||||
current.compute(previous);
|
||||
|
||||
// then
|
||||
assertNull(current.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compute_speed() {
|
||||
// given
|
||||
@@ -108,7 +125,9 @@ public class SensorDataCyclingTest {
|
||||
assertEquals(1.20, current.getValue().getSpeed().toMPS(), 0.01);
|
||||
}
|
||||
|
||||
@Ignore("Disabled from #953")
|
||||
@Test
|
||||
@Deprecated
|
||||
public void compute_speed_rollOverCount() {
|
||||
// given
|
||||
SensorDataCycling.DistanceSpeed previous = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
|
||||
@@ -122,6 +141,19 @@ public class SensorDataCyclingTest {
|
||||
assertEquals(2, current.getValue().getSpeed().toMPS(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compute_speed_overflow() {
|
||||
// given
|
||||
SensorDataCycling.DistanceSpeed previous = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", UintUtils.UINT32_MAX - 1, 1024);
|
||||
SensorDataCycling.DistanceSpeed current = new SensorDataCycling.DistanceSpeed("sensorAddress", "sensorName", 0, 2048);
|
||||
|
||||
// when
|
||||
current.compute(previous, Distance.ofMM(2000));
|
||||
|
||||
// then
|
||||
assertNull(current.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_speed_with_no_data() {
|
||||
// given
|
||||
|
||||
@@ -14,6 +14,7 @@ import org.junit.runners.JUnit4;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
@@ -23,6 +24,7 @@ import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
@@ -67,7 +69,7 @@ public class GPXImportTest {
|
||||
importTrackId = importer.importFile(inputStream).get(0);
|
||||
|
||||
// then
|
||||
// 1. track
|
||||
// 2. track
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(importedTrack);
|
||||
assertEquals("the category", importedTrack.getCategory());
|
||||
@@ -75,6 +77,8 @@ public class GPXImportTest {
|
||||
assertEquals("2021-01-07 22:51", importedTrack.getName());
|
||||
assertEquals("UNKNOWN", importedTrack.getIcon());
|
||||
|
||||
//TODO Check trackstatistics
|
||||
|
||||
// 3. trackpoints
|
||||
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
||||
assertEquals(6, importedTrackPoints.size());
|
||||
@@ -106,4 +110,102 @@ public class GPXImportTest {
|
||||
.setAltitude(10)
|
||||
), importedTrackPoints);
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
public void gpx_without_speed() throws IOException {
|
||||
// given
|
||||
XMLImporter importer = new XMLImporter(new GpxTrackImporter(context, trackImporter));
|
||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.gpx11_without_speed);
|
||||
|
||||
// when
|
||||
// 1. import
|
||||
importTrackId = importer.importFile(inputStream).get(0);
|
||||
|
||||
// then
|
||||
// 2. track
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(importedTrack);
|
||||
assertEquals("", importedTrack.getCategory());
|
||||
assertEquals("", importedTrack.getDescription());
|
||||
assertEquals("20210907_213924.gpx", importedTrack.getName());
|
||||
assertEquals("", importedTrack.getIcon());
|
||||
|
||||
// 3. trackstatistics
|
||||
TrackStatistics trackStatistics = importedTrack.getTrackStatistics();
|
||||
assertEquals(1.44, trackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(Duration.ofSeconds(53), trackStatistics.getMovingTime());
|
||||
|
||||
// 4. trackpoints
|
||||
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
||||
assertEquals(3, importedTrackPoints.size());
|
||||
|
||||
TrackPointAssert a = new TrackPointAssert();
|
||||
a.assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse("2021-09-07T22:10:19Z"))
|
||||
.setLatitude(30.14185982)
|
||||
.setLongitude(-40.3863038)
|
||||
.setAltitude(-5),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2021-09-07T22:11:07Z"))
|
||||
.setLatitude(30.14184657)
|
||||
.setLongitude(-40.38670089)
|
||||
.setAltitude(-5)
|
||||
.setSpeed(Speed.of(0.7976524233818054)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2021-09-07T22:12:00Z"))
|
||||
.setLatitude(30.14185982)
|
||||
.setLongitude(-40.3863038)
|
||||
.setAltitude(-5)
|
||||
.setSpeed(Speed.of(0.7224021553993225))
|
||||
), importedTrackPoints);
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@Test
|
||||
public void gpx_speed_no_namespace() throws IOException {
|
||||
// given
|
||||
XMLImporter importer = new XMLImporter(new GpxTrackImporter(context, trackImporter));
|
||||
InputStream inputStream = InstrumentationRegistry.getInstrumentation().getContext().getResources().openRawResource(de.dennisguse.opentracks.debug.test.R.raw.gpx11_with_speed_no_namespace);
|
||||
|
||||
// when
|
||||
// 1. import
|
||||
importTrackId = importer.importFile(inputStream).get(0);
|
||||
|
||||
// then
|
||||
// 2. track
|
||||
Track importedTrack = contentProviderUtils.getTrack(importTrackId);
|
||||
assertNotNull(importedTrack);
|
||||
assertEquals("", importedTrack.getCategory());
|
||||
assertEquals("", importedTrack.getDescription());
|
||||
assertEquals("20210907_213924.gpx", importedTrack.getName());
|
||||
assertEquals("", importedTrack.getIcon());
|
||||
|
||||
// 3. trackstatistics
|
||||
TrackStatistics trackStatistics = importedTrack.getTrackStatistics();
|
||||
assertEquals(4, trackStatistics.getMaxSpeed().toMPS(), 0.01);
|
||||
assertEquals(Duration.ofSeconds(101), trackStatistics.getMovingTime());
|
||||
|
||||
// 4. trackpoints
|
||||
List<TrackPoint> importedTrackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, importTrackId);
|
||||
assertEquals(3, importedTrackPoints.size());
|
||||
|
||||
TrackPointAssert a = new TrackPointAssert();
|
||||
a.assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_AUTOMATIC, Instant.parse("2021-09-07T22:10:19Z"))
|
||||
.setLatitude(30.14185982)
|
||||
.setLongitude(-40.3863038)
|
||||
.setAltitude(-5)
|
||||
.setSpeed(Speed.of(5)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2021-09-07T22:11:07Z"))
|
||||
.setLatitude(30.14184657)
|
||||
.setLongitude(-40.38670089)
|
||||
.setAltitude(-5)
|
||||
.setSpeed(Speed.of(0.7976524233818054))
|
||||
.setSpeed(Speed.of(4)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, Instant.parse("2021-09-07T22:12:00Z"))
|
||||
.setLatitude(30.14185982)
|
||||
.setLongitude(-40.3863038)
|
||||
.setAltitude(-5)
|
||||
.setSpeed(Speed.of(3))
|
||||
), importedTrackPoints);
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -73,9 +73,9 @@ public class TrackPointAssert {
|
||||
Assert.assertEquals(expected.getPower(), actual.getPower(), delta);
|
||||
}
|
||||
|
||||
Assert.assertEquals(expected.hasCyclingCadence(), actual.hasCyclingCadence());
|
||||
if (expected.hasCyclingCadence()) {
|
||||
Assert.assertEquals(expected.getCyclingCadence_rpm(), actual.getCyclingCadence_rpm(), delta);
|
||||
Assert.assertEquals(expected.hasCadence(), actual.hasCadence());
|
||||
if (expected.hasCadence()) {
|
||||
Assert.assertEquals(expected.getCadence_rpm(), actual.getCadence_rpm(), delta);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,14 +83,14 @@ public class TrackPointAssert {
|
||||
try {
|
||||
Assert.assertEquals(expected.size(), actual.size());
|
||||
} catch (AssertionError e) {
|
||||
throw new AssertionError("Expected: " + expected + " actual: " + actual);
|
||||
throw new AssertionError("Expected: " + expected + "\n actual: " + actual);
|
||||
}
|
||||
|
||||
for (int i = 0; i < expected.size(); i++) {
|
||||
try {
|
||||
assertEquals(expected.get(i), actual.get(i));
|
||||
} catch (AssertionError e) {
|
||||
throw new AssertionError("Expected: " + expected.get(i) + " actual: " + actual.get(i), e);
|
||||
throw new AssertionError("Expected: " + expected.get(i) + "\n actual: " + actual.get(i), e);
|
||||
}
|
||||
}
|
||||
Assert.assertEquals(expected.size(), actual.size());
|
||||
|
||||
+2
-7
@@ -25,7 +25,6 @@ import static org.junit.Assert.assertTrue;
|
||||
import android.content.ContentProvider;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
|
||||
@@ -66,15 +65,12 @@ import de.dennisguse.opentracks.io.file.importer.TrackPointAssert;
|
||||
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
||||
import de.dennisguse.opentracks.services.sensors.AltitudeSumManager;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
/**
|
||||
* Tests for the track recording service.
|
||||
*
|
||||
* @author Bartlomiej Niechwiej
|
||||
* <p>
|
||||
* //TODO ATTENTION: This tests deletes all stored tracks in the database.
|
||||
* So, if it is executed on a real device, data might be lost.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TrackRecordingServiceTest {
|
||||
@@ -97,7 +93,6 @@ public class TrackRecordingServiceTest {
|
||||
}
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
private final SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
|
||||
private final Track.Id trackId = new Track.Id(Math.abs(new Random().nextLong()));
|
||||
@@ -124,7 +119,7 @@ public class TrackRecordingServiceTest {
|
||||
contentProviderUtils = new ContentProviderUtils(context);
|
||||
|
||||
// Let's use default values.
|
||||
sharedPreferences.edit().clear().commit();
|
||||
PreferencesUtils.clear();
|
||||
|
||||
// Ensure that the database is empty before every test
|
||||
contentProviderUtils.deleteAllTracks(context);
|
||||
|
||||
+88
-30
@@ -1,8 +1,9 @@
|
||||
package de.dennisguse.opentracks.services;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -32,19 +33,16 @@ import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.provider.CustomContentProvider;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataHeartRate;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataRunning;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.io.file.importer.TrackPointAssert;
|
||||
import de.dennisguse.opentracks.services.sensors.AltitudeSumManager;
|
||||
import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.util.PressureSensorUtils;
|
||||
|
||||
/**
|
||||
* Tests insert location.
|
||||
* <p>
|
||||
* //TODO ATTENTION: This tests deletes all stored tracks in the database.
|
||||
* So, if it is executed on a real device, data might be lost.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
//TODO Implement as mock test; no need to store data in database
|
||||
@@ -91,8 +89,7 @@ public class TrackRecordingServiceTestLocation {
|
||||
tearDown();
|
||||
|
||||
// Let's use default values.
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
sharedPreferences.edit().clear().commit();
|
||||
PreferencesUtils.clear();
|
||||
|
||||
service = ((TrackRecordingService.Binder) mServiceRule.bindService(TrackRecordingServiceTest.createStartIntent(context)))
|
||||
.getService();
|
||||
@@ -255,13 +252,6 @@ public class TrackRecordingServiceTestLocation {
|
||||
.setSpeed(Speed.of(0))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, null) //TODO Why is this added? Systems is idle and not moving at all.
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setHorizontalAccuracy(Distance.of(2))
|
||||
.setSpeed(Speed.of(0))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, null)
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
@@ -308,20 +298,6 @@ public class TrackRecordingServiceTestLocation {
|
||||
.setSpeed(Speed.of(15))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, null) //TODO Check why this trackPoint is inserted.
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setHorizontalAccuracy(Distance.of(2))
|
||||
.setSpeed(Speed.of(0))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, null) //TODO Check why this trackPoint is inserted.
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setHorizontalAccuracy(Distance.of(5))
|
||||
.setSpeed(Speed.of(0))
|
||||
.setAltitudeGain(0f)
|
||||
.setAltitudeLoss(0f),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, null)
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
@@ -430,6 +406,88 @@ public class TrackRecordingServiceTestLocation {
|
||||
), trackPoints);
|
||||
}
|
||||
|
||||
@Deprecated // Will be superseded when fixing #500
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testOnLocationChangedAsync_idle_withSensorDistance() {
|
||||
BluetoothRemoteSensorManager remoteSensorManager = new BluetoothRemoteSensorManager(context) {
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
AltitudeSumManager altitudeSumManager = new AltitudeSumManager();
|
||||
|
||||
// given
|
||||
Track.Id trackId = service.startNewTrack();
|
||||
service.getTrackPointCreator().setRemoteSensorManager(remoteSensorManager);
|
||||
service.getTrackPointCreator().setAltitudeSumManager(altitudeSumManager);
|
||||
altitudeSumManager.stop(service);
|
||||
altitudeSumManager.setConnected(true);
|
||||
|
||||
// when
|
||||
altitudeSumManager.addAltitudeGain_m(6f);
|
||||
altitudeSumManager.addAltitudeLoss_m(6f);
|
||||
remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(0)));
|
||||
remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(2)));
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 1, 15);
|
||||
|
||||
remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(12)));
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 2, 15);
|
||||
|
||||
remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(13)));
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 3, 15);
|
||||
altitudeSumManager.addAltitudeGain_m(6f);
|
||||
altitudeSumManager.addAltitudeLoss_m(6f);
|
||||
remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(14)));
|
||||
TrackRecordingServiceTest.newTrackPoint(service, 45.0, 35.0, 4, 15);
|
||||
|
||||
altitudeSumManager.addAltitudeGain_m(6f);
|
||||
altitudeSumManager.addAltitudeLoss_m(6f);
|
||||
remoteSensorManager.onChanged(new SensorDataRunning("", "", Speed.of(5), null, Distance.of(16)));
|
||||
service.endCurrentTrack();
|
||||
|
||||
// then
|
||||
assertFalse(service.isRecording());
|
||||
|
||||
List<TrackPoint> trackPoints = TestDataUtil.getTrackPoints(contentProviderUtils, trackId);
|
||||
TrackPointAssert a = new TrackPointAssert()
|
||||
.ignoreTime();
|
||||
a.assertEquals(List.of(
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_START_MANUAL, null),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, null)
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setHorizontalAccuracy(Distance.of(1))
|
||||
.setSpeed(Speed.of(5))
|
||||
.setAltitudeGain(6f)
|
||||
.setAltitudeLoss(6f)
|
||||
.setSensorDistance(Distance.of(2)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, null)
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setHorizontalAccuracy(Distance.of(2))
|
||||
.setSpeed(Speed.of(5))
|
||||
.setSensorDistance(Distance.of(10)),
|
||||
new TrackPoint(TrackPoint.Type.TRACKPOINT, null)
|
||||
.setLatitude(45)
|
||||
.setLongitude(35)
|
||||
.setHorizontalAccuracy(Distance.of(4))
|
||||
.setSpeed(Speed.of(5))
|
||||
.setAltitudeGain(6f)
|
||||
.setAltitudeLoss(6f)
|
||||
.setSensorDistance(Distance.of(2)),
|
||||
new TrackPoint(TrackPoint.Type.SEGMENT_END_MANUAL, null)
|
||||
.setSensorDistance(Distance.of(11))
|
||||
.setSpeed(Speed.of(5))
|
||||
.setAltitudeGain(6f)
|
||||
.setAltitudeLoss(6f)
|
||||
.setSensorDistance(Distance.of(2))
|
||||
), trackPoints);
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testOnLocationChangedAsync_segment() {
|
||||
|
||||
+5
-9
@@ -32,7 +32,7 @@ import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.provider.CustomContentProvider;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
@@ -43,9 +43,6 @@ import static org.junit.Assert.assertTrue;
|
||||
* Tests for the track recording service, which require a {@link Looper}.
|
||||
*
|
||||
* @author Bartlomiej Niechwiej
|
||||
* <p>
|
||||
* ATTENTION: This tests deletes all stored tracks in the database.
|
||||
* So, if it is executed on a real device, data might be lost.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TrackRecordingServiceTestLooper {
|
||||
@@ -54,7 +51,6 @@ public class TrackRecordingServiceTestLooper {
|
||||
public final ServiceTestRule mServiceRule = ServiceTestRule.withTimeout(5, TimeUnit.SECONDS);
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
private final SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
|
||||
@@ -79,7 +75,7 @@ public class TrackRecordingServiceTestLooper {
|
||||
contentProviderUtils = new ContentProviderUtils(context);
|
||||
|
||||
// Let's use default values.
|
||||
sharedPreferences.edit().clear().apply();
|
||||
PreferencesUtils.clear();
|
||||
|
||||
// Ensure that the database is empty before every test
|
||||
contentProviderUtils.deleteAllTracks(context);
|
||||
@@ -146,14 +142,14 @@ public class TrackRecordingServiceTestLooper {
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_metricUnitsDefault() throws TimeoutException {
|
||||
PreferencesUtils.setString(sharedPreferences, context, R.string.stats_units_key, context.getString(R.string.stats_units_default));
|
||||
PreferencesUtils.setString(R.string.stats_units_key, context.getString(R.string.stats_units_default));
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@MediumTest
|
||||
@Test
|
||||
public void testWithProperties_metricUnitsDisabled() throws TimeoutException {
|
||||
PreferencesUtils.setString(sharedPreferences, context, R.string.stats_units_key, context.getString(R.string.stats_units_imperial));
|
||||
PreferencesUtils.setString(R.string.stats_units_key, context.getString(R.string.stats_units_imperial));
|
||||
fullRecordingSession();
|
||||
}
|
||||
|
||||
@@ -215,7 +211,7 @@ public class TrackRecordingServiceTestLooper {
|
||||
.setSpeed(Speed.of(10))
|
||||
.setBearing(3.0f);
|
||||
|
||||
Distance prefAccuracy = PreferencesUtils.getThresholdHorizontalAccuracy(sharedPreferences, context);
|
||||
Distance prefAccuracy = PreferencesUtils.getThresholdHorizontalAccuracy();
|
||||
service.getTrackPointCreator().onNewTrackPoint(trackPoint, prefAccuracy);
|
||||
|
||||
if (i % 7 == 0) {
|
||||
|
||||
+4
-9
@@ -5,7 +5,6 @@ import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.location.Location;
|
||||
import android.os.Looper;
|
||||
|
||||
@@ -24,13 +23,12 @@ import java.time.Instant;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.Distance;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class LocationHandlerTest {
|
||||
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
private final SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
|
||||
@Mock
|
||||
private TrackPointCreator trackPointCreator;
|
||||
@@ -47,15 +45,12 @@ public class LocationHandlerTest {
|
||||
@Before
|
||||
public void setUp() {
|
||||
// Let's use default values.
|
||||
sharedPreferences.edit().clear().commit();
|
||||
PreferencesUtils.clear();
|
||||
|
||||
Mockito.when(trackPointCreator.createNow())
|
||||
.thenReturn(Instant.now());
|
||||
|
||||
//TODO REMOVE
|
||||
// locationHandler.onSharedPreferenceChanged(context, sharedPreferences, context.getString(R.string.recording_gps_accuracy_key));
|
||||
// locationHandler.onSharedPreferenceChanged(context, sharedPreferences, context.getString(R.string.min_recording_interval_key));
|
||||
locationHandler.onStart(context, sharedPreferences);
|
||||
locationHandler.onStart(context);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,7 +87,7 @@ public class LocationHandlerTest {
|
||||
@Test
|
||||
public void testOnLocationChanged_poorAccuracy() {
|
||||
// given
|
||||
Distance prefAccuracy = PreferencesUtils.getThresholdHorizontalAccuracy(sharedPreferences, context);
|
||||
Distance prefAccuracy = PreferencesUtils.getThresholdHorizontalAccuracy();
|
||||
|
||||
// when
|
||||
locationHandler.onLocationChanged(createLocation(45f, 35f, (float) (prefAccuracy.toM() + 1), 5, System.currentTimeMillis()));
|
||||
|
||||
+5
-2
@@ -1,4 +1,4 @@
|
||||
package de.dennisguse.opentracks.util;
|
||||
package de.dennisguse.opentracks.services.tasks;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Pair;
|
||||
@@ -6,6 +6,7 @@ import android.util.Pair;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -18,7 +19,9 @@ import de.dennisguse.opentracks.content.data.TestDataUtil;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
|
||||
import de.dennisguse.opentracks.services.tasks.AnnouncementUtils;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
import de.dennisguse.opentracks.viewmodels.IntervalStatistics;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -68,7 +71,7 @@ public class AnnouncementUtilsTest {
|
||||
String announcement = AnnouncementUtils.getAnnouncement(context, stats, true, true, lastInterval);
|
||||
|
||||
// then
|
||||
assertEquals(
|
||||
Assert.assertEquals(
|
||||
"total distance " +
|
||||
StringUtils.getDistanceParts(context, stats.getTotalDistance(), true).first +
|
||||
" kilometers in " + buildAndGetTimeText(stats.getTotalTime(), false) + " at " +
|
||||
@@ -3,6 +3,7 @@ package de.dennisguse.opentracks.util;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
@@ -13,6 +14,7 @@ import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.data.DataField;
|
||||
import de.dennisguse.opentracks.content.data.Layout;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
@@ -27,14 +29,14 @@ public class PreferencesUtilsTest {
|
||||
@Test
|
||||
public void ExportTrackFileFormat_ok() {
|
||||
// given
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
|
||||
editor.putString(context.getString(R.string.export_trackfileformat_key), TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.name());
|
||||
editor.commit();
|
||||
|
||||
// when
|
||||
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(sharedPreferences, context);
|
||||
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat();
|
||||
|
||||
// then
|
||||
assertEquals(TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA, trackFileFormat);
|
||||
@@ -43,13 +45,13 @@ public class PreferencesUtilsTest {
|
||||
@Test
|
||||
public void ExportTrackFileFormat_invalid() {
|
||||
// given
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.putString(context.getString(R.string.export_trackfileformat_key), "invalid");
|
||||
editor.commit();
|
||||
|
||||
// when
|
||||
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(sharedPreferences, context);
|
||||
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat();
|
||||
|
||||
// then
|
||||
assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, trackFileFormat);
|
||||
@@ -58,13 +60,13 @@ public class PreferencesUtilsTest {
|
||||
@Test
|
||||
public void ExportTrackFileFormat_noValue() {
|
||||
// given
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.clear();
|
||||
editor.commit();
|
||||
|
||||
// when
|
||||
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(sharedPreferences, context);
|
||||
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat();
|
||||
|
||||
// then
|
||||
assertEquals(TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA, trackFileFormat);
|
||||
@@ -73,13 +75,13 @@ public class PreferencesUtilsTest {
|
||||
@Test
|
||||
public void testGetCustomLayout_default() {
|
||||
// given
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.clear();
|
||||
editor.commit();
|
||||
|
||||
// when
|
||||
Layout layout = PreferencesUtils.getCustomLayout(sharedPreferences, context);
|
||||
Layout layout = PreferencesUtils.getCustomLayout();
|
||||
|
||||
// then
|
||||
assertTrue(layout.getFields().size() > 0);
|
||||
@@ -90,7 +92,7 @@ public class PreferencesUtilsTest {
|
||||
@Test
|
||||
public void testGetCustomLayout_1() {
|
||||
// given
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.putString(
|
||||
context.getString(R.string.stats_custom_layout_fields_key),
|
||||
@@ -102,7 +104,7 @@ public class PreferencesUtilsTest {
|
||||
editor.apply();
|
||||
|
||||
// when
|
||||
Layout layout = PreferencesUtils.getCustomLayout(sharedPreferences, context);
|
||||
Layout layout = PreferencesUtils.getCustomLayout();
|
||||
|
||||
// then
|
||||
assertEquals(layout.getFields().size(), 4);
|
||||
@@ -128,7 +130,7 @@ public class PreferencesUtilsTest {
|
||||
@Test
|
||||
public void testGetCustomLayout_coordinatesIsWide() {
|
||||
// given
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.putString(
|
||||
context.getString(R.string.stats_custom_layout_fields_key),
|
||||
@@ -140,7 +142,7 @@ public class PreferencesUtilsTest {
|
||||
editor.apply();
|
||||
|
||||
// when
|
||||
Layout layout = PreferencesUtils.getCustomLayout(sharedPreferences, context);
|
||||
Layout layout = PreferencesUtils.getCustomLayout();
|
||||
|
||||
// then
|
||||
assertEquals(layout.getFields().size(), 4);
|
||||
@@ -167,7 +169,7 @@ public class PreferencesUtilsTest {
|
||||
@Test
|
||||
public void testSetCustomLayout() {
|
||||
// given
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
Layout layoutSrc = new Layout("road cycling");
|
||||
layoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_moving_time_key), context.getString(R.string.stats_moving_time), true, true, false));
|
||||
layoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_distance_key), context.getString(R.string.stats_distance), true, false, false));
|
||||
@@ -175,7 +177,7 @@ public class PreferencesUtilsTest {
|
||||
layoutSrc.addField(new DataField(context.getString(R.string.stats_custom_layout_speed_key), context.getString(R.string.stats_speed), false, false, false));
|
||||
|
||||
// when
|
||||
PreferencesUtils.setCustomLayout(sharedPreferences, context, layoutSrc);
|
||||
PreferencesUtils.setCustomLayout(layoutSrc);
|
||||
|
||||
// then
|
||||
String csv = sharedPreferences.getString(context.getString(R.string.stats_custom_layout_fields_key), null);
|
||||
@@ -187,7 +189,7 @@ public class PreferencesUtilsTest {
|
||||
+ context.getString(R.string.stats_custom_layout_average_moving_speed_key) + ",0,1;"
|
||||
+ context.getString(R.string.stats_custom_layout_speed_key) + ",0,0;");
|
||||
|
||||
Layout layoutDst = PreferencesUtils.getCustomLayout(sharedPreferences, context);
|
||||
Layout layoutDst = PreferencesUtils.getCustomLayout();
|
||||
assertEquals(layoutSrc.getProfile(), layoutDst.getProfile());
|
||||
assertEquals(layoutSrc.getFields().size(), layoutDst.getFields().size());
|
||||
for (int i = 0; i < layoutSrc.getFields().size(); i++) {
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package de.dennisguse.opentracks.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
@@ -31,6 +30,7 @@ import java.util.Locale;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@@ -47,14 +47,12 @@ public class TrackNameUtilsTest {
|
||||
|
||||
private static final Context CONTEXT = ApplicationProvider.getApplicationContext();
|
||||
|
||||
private static final SharedPreferences SHARED_PREFERENCES = PreferencesUtils.getSharedPreferences(CONTEXT);
|
||||
|
||||
/**
|
||||
* Tests when the track_name_key is settings_recording_track_name_date_local_value.
|
||||
*/
|
||||
@Test
|
||||
public void testTrackName_date_local() {
|
||||
PreferencesUtils.setString(SHARED_PREFERENCES, CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_date_local_value));
|
||||
PreferencesUtils.setString(R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_date_local_value));
|
||||
assertEquals(StringUtils.formatDateTime(CONTEXT, START_TIME), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
|
||||
}
|
||||
|
||||
@@ -63,7 +61,7 @@ public class TrackNameUtilsTest {
|
||||
*/
|
||||
@Test
|
||||
public void testTrackName_date_iso_8601() {
|
||||
PreferencesUtils.setString(SHARED_PREFERENCES, CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_date_iso_8601_value));
|
||||
PreferencesUtils.setString(R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_date_iso_8601_value));
|
||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(TrackNameUtils.ISO_8601_FORMAT, Locale.US);
|
||||
assertEquals(simpleDateFormat.format(START_TIME.toEpochMilli()), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
|
||||
}
|
||||
@@ -73,7 +71,7 @@ public class TrackNameUtilsTest {
|
||||
*/
|
||||
@Test
|
||||
public void testTrackName_number() {
|
||||
PreferencesUtils.setString(SHARED_PREFERENCES, CONTEXT, R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_number_value));
|
||||
PreferencesUtils.setString(R.string.track_name_key, CONTEXT.getString(R.string.settings_recording_track_name_number_value));
|
||||
assertEquals("Track " + TRACK_ID.getId(), TrackNameUtils.getTrackName(CONTEXT, TRACK_ID, START_TIME));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
|
||||
<gpx version="1.1" creator="Cruiser" xmlns="http://www.topografix.com/GPX/1/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
|
||||
<trk>
|
||||
<name>20210907_213924.gpx</name>
|
||||
<trkseg>
|
||||
<trkpt lat="30.14185982" lon="-40.3863038">
|
||||
<ele>-5</ele>
|
||||
<time>2021-09-07T22:10:19Z</time>
|
||||
<hdop>4</hdop>
|
||||
<speed>5</speed>
|
||||
</trkpt>
|
||||
<trkpt lat="30.14184657" lon="-40.38670089">
|
||||
<ele>-5</ele>
|
||||
<time>2021-09-07T22:11:07Z</time>
|
||||
<hdop>4</hdop>
|
||||
<speed>4</speed>
|
||||
</trkpt>
|
||||
<trkpt lat="30.14185982" lon="-40.3863038">
|
||||
<ele>-5</ele>
|
||||
<time>2021-09-07T22:12:00Z</time>
|
||||
<hdop>4</hdop>
|
||||
<speed>3</speed>
|
||||
</trkpt>
|
||||
</trkseg>
|
||||
</trk>
|
||||
</gpx>
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
|
||||
<gpx version="1.1" creator="Cruiser" xmlns="http://www.topografix.com/GPX/1/1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
|
||||
<trk>
|
||||
<name>20210907_213924.gpx</name>
|
||||
<trkseg>
|
||||
<trkpt lat="30.14185982" lon="-40.3863038">
|
||||
<ele>-5</ele>
|
||||
<time>2021-09-07T22:10:19Z</time>
|
||||
<hdop>4</hdop>
|
||||
</trkpt>
|
||||
<trkpt lat="30.14184657" lon="-40.38670089">
|
||||
<ele>-5</ele>
|
||||
<time>2021-09-07T22:11:07Z</time>
|
||||
<hdop>4</hdop>
|
||||
</trkpt>
|
||||
<trkpt lat="30.14185982" lon="-40.3863038">
|
||||
<ele>-5</ele>
|
||||
<time>2021-09-07T22:12:00Z</time>
|
||||
<hdop>4</hdop>
|
||||
</trkpt>
|
||||
</trkseg>
|
||||
</trk>
|
||||
</gpx>
|
||||
@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="de.dennisguse.opentracks"
|
||||
android:installLocation="auto">
|
||||
|
||||
@@ -52,7 +53,10 @@ limitations under the License.
|
||||
<uses-permission
|
||||
android:name="android.permission.BLUETOOTH_ADMIN"
|
||||
android:maxSdkVersion="30" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
|
||||
<uses-permission
|
||||
android:name="android.permission.BLUETOOTH_SCAN"
|
||||
android:usesPermissionFlags="neverForLocation"
|
||||
tools:targetApi="s" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
|
||||
<application
|
||||
@@ -304,7 +308,11 @@ limitations under the License.
|
||||
|
||||
<activity android:name=".TrackRecordedActivity" />
|
||||
|
||||
<activity android:name=".TrackRecordingActivity" />
|
||||
<activity android:name=".TrackRecordingActivity" android:parentActivityName=".TrackListActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".TrackListActivity" />
|
||||
</activity>
|
||||
|
||||
<activity android:name=".TrackEditActivity" />
|
||||
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
package de.dennisguse.opentracks;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.StrictMode;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import de.dennisguse.opentracks.util.ActivityUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
/**
|
||||
* Code that is executed when the application starts.
|
||||
@@ -27,13 +25,14 @@ public class Startup extends Application {
|
||||
// Include version information into stack traces.
|
||||
Log.i(TAG, BuildConfig.APPLICATION_ID + "; BuildType: " + BuildConfig.BUILD_TYPE + "; VersionName: " + BuildConfig.VERSION_NAME + "/" + BuildConfig.VERSION_NAME_FULL + " VersionCode: " + BuildConfig.VERSION_CODE);
|
||||
|
||||
PreferencesUtils.initPreferences(this, getResources());
|
||||
// Set default values of preferences on first start.
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.resetPreferences(this, false);
|
||||
if (PreferencesUtils.getString(sharedPreferences, this, R.string.stats_units_key, "").equals("")) {
|
||||
PreferencesUtils.setMetricUnits(sharedPreferences, this, !Locale.US.equals(Locale.getDefault()));
|
||||
PreferencesUtils.resetPreferences(this, false);
|
||||
if (PreferencesUtils.getString( R.string.stats_units_key, "").equals("")) {
|
||||
PreferencesUtils.setMetricUnits(!Locale.US.equals(Locale.getDefault()));
|
||||
}
|
||||
PreferencesUtils.applyNightMode();
|
||||
|
||||
ActivityUtils.applyNightMode(sharedPreferences, this);
|
||||
|
||||
// In debug builds: show thread and VM warnings.
|
||||
if (BuildConfig.DEBUG) {
|
||||
@@ -41,4 +40,13 @@ public class Startup extends Application {
|
||||
StrictMode.enableDefaults();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the database used by SQLiteOpenHelper.
|
||||
* See {@link android.database.sqlite.SQLiteOpenHelper} for details.
|
||||
* @return SQLite database name.
|
||||
*/
|
||||
public String getDatabaseName() {
|
||||
return "database.db";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import android.app.ActivityOptions;
|
||||
import android.app.SearchManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
@@ -64,7 +63,7 @@ import de.dennisguse.opentracks.util.ActivityUtils;
|
||||
import de.dennisguse.opentracks.util.IntentDashboardUtils;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
import de.dennisguse.opentracks.util.ListItemUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
|
||||
@@ -78,7 +77,6 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
private static final String TAG = TrackListActivity.class.getSimpleName();
|
||||
|
||||
// The following are setFrequency in onCreate
|
||||
private SharedPreferences sharedPreferences;
|
||||
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||
private ResourceCursorAdapter resourceCursorAdapter;
|
||||
|
||||
@@ -110,8 +108,8 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
};
|
||||
|
||||
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
|
||||
if (PreferencesUtils.isKey(TrackListActivity.this, R.string.stats_units_key, key)) {
|
||||
metricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, TrackListActivity.this);
|
||||
if (PreferencesUtils.isKey(R.string.stats_units_key, key)) {
|
||||
metricUnits = PreferencesUtils.isMetricUnits();
|
||||
}
|
||||
if (key != null) {
|
||||
runOnUiThread(() -> {
|
||||
@@ -161,8 +159,6 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
super.onCreate(savedInstanceState);
|
||||
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
|
||||
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
||||
|
||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback);
|
||||
|
||||
viewBinding.trackList.setEmptyView(viewBinding.trackListEmptyView);
|
||||
@@ -231,8 +227,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
||||
PreferencesUtils.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
trackRecordingServiceConnection.startConnection(this);
|
||||
}
|
||||
|
||||
@@ -249,7 +244,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
PreferencesUtils.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
trackRecordingServiceConnection.unbind(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||
import de.dennisguse.opentracks.settings.SettingsActivity;
|
||||
import de.dennisguse.opentracks.util.IntentDashboardUtils;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
/**
|
||||
* An activity to show the track detail, record a new track or resumes an existing one.
|
||||
@@ -68,7 +68,6 @@ public class TrackRecordedActivity extends AbstractTrackDeleteActivity implement
|
||||
|
||||
// The following are setFrequency in onCreate.
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
private SharedPreferences sharedPreferences;
|
||||
private TrackDataHub trackDataHub;
|
||||
|
||||
private TrackRecordedBinding viewBinding;
|
||||
@@ -96,8 +95,8 @@ public class TrackRecordedActivity extends AbstractTrackDeleteActivity implement
|
||||
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
if (PreferencesUtils.isKey(TrackRecordedActivity.this, R.string.recording_distance_interval_key, key)) {
|
||||
trackDataHub.setRecordingDistanceInterval(PreferencesUtils.getRecordingDistanceInterval(sharedPreferences, TrackRecordedActivity.this));
|
||||
if (PreferencesUtils.isKey(R.string.recording_distance_interval_key, key)) {
|
||||
trackDataHub.setRecordingDistanceInterval(PreferencesUtils.getRecordingDistanceInterval());
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -107,7 +106,6 @@ public class TrackRecordedActivity extends AbstractTrackDeleteActivity implement
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
contentProviderUtils = new ContentProviderUtils(this);
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
||||
|
||||
handleIntent(getIntent());
|
||||
|
||||
@@ -130,8 +128,7 @@ public class TrackRecordedActivity extends AbstractTrackDeleteActivity implement
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
||||
PreferencesUtils.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
|
||||
trackDataHub.start();
|
||||
}
|
||||
@@ -155,7 +152,7 @@ public class TrackRecordedActivity extends AbstractTrackDeleteActivity implement
|
||||
super.onStop();
|
||||
trackRecordingServiceConnection.unbind(this);
|
||||
trackDataHub.stop();
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
PreferencesUtils.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -31,7 +31,7 @@ import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||
import de.dennisguse.opentracks.settings.SettingsActivity;
|
||||
import de.dennisguse.opentracks.util.IntentDashboardUtils;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
import de.dennisguse.opentracks.util.TrackUtils;
|
||||
|
||||
@@ -53,7 +53,6 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
||||
|
||||
// The following are setFrequency in onCreate
|
||||
private ContentProviderUtils contentProviderUtils;
|
||||
private SharedPreferences sharedPreferences;
|
||||
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||
private TrackDataHub trackDataHub;
|
||||
|
||||
@@ -96,17 +95,17 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
||||
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
if (PreferencesUtils.isKey(TrackRecordingActivity.this, R.string.stats_show_on_lockscreen_while_recording_key, key)) {
|
||||
if (PreferencesUtils.isKey(R.string.stats_show_on_lockscreen_while_recording_key, key)) {
|
||||
setLockscreenPolicy();
|
||||
}
|
||||
if (PreferencesUtils.isKey(TrackRecordingActivity.this, R.string.stats_keep_screen_on_while_recording_key, key)) {
|
||||
if (PreferencesUtils.isKey(R.string.stats_keep_screen_on_while_recording_key, key)) {
|
||||
setScreenOnPolicy();
|
||||
}
|
||||
if (PreferencesUtils.isKey(TrackRecordingActivity.this, R.string.stats_fullscreen_while_recording_key, key)) {
|
||||
if (PreferencesUtils.isKey(R.string.stats_fullscreen_while_recording_key, key)) {
|
||||
setFullscreenPolicy();
|
||||
}
|
||||
if (PreferencesUtils.isKey(TrackRecordingActivity.this, R.string.recording_distance_interval_key, key)) {
|
||||
trackDataHub.setRecordingDistanceInterval(PreferencesUtils.getRecordingDistanceInterval(sharedPreferences, TrackRecordingActivity.this));
|
||||
if (PreferencesUtils.isKey(R.string.recording_distance_interval_key, key)) {
|
||||
trackDataHub.setRecordingDistanceInterval(PreferencesUtils.getRecordingDistanceInterval());
|
||||
}
|
||||
if (key == null) return;
|
||||
|
||||
@@ -121,7 +120,6 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
contentProviderUtils = new ContentProviderUtils(this);
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
||||
trackId = null;
|
||||
if (savedInstanceState != null) {
|
||||
//Activity was recreated.
|
||||
@@ -156,7 +154,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
||||
}
|
||||
|
||||
private void setLockscreenPolicy() {
|
||||
boolean showOnLockScreen = PreferencesUtils.shouldShowStatsOnLockscreen(sharedPreferences, TrackRecordingActivity.this);
|
||||
boolean showOnLockScreen = PreferencesUtils.shouldShowStatsOnLockscreen();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||
setShowWhenLocked(showOnLockScreen);
|
||||
@@ -168,7 +166,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
||||
}
|
||||
|
||||
private void setScreenOnPolicy() {
|
||||
boolean keepScreenOn = PreferencesUtils.shouldKeepScreenOn(sharedPreferences, TrackRecordingActivity.this);
|
||||
boolean keepScreenOn = PreferencesUtils.shouldKeepScreenOn();
|
||||
|
||||
if (keepScreenOn) {
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
@@ -178,7 +176,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
||||
}
|
||||
|
||||
private void setFullscreenPolicy() {
|
||||
boolean keepScreenOn = PreferencesUtils.shouldUseFullscreen(sharedPreferences, TrackRecordingActivity.this);
|
||||
boolean keepScreenOn = PreferencesUtils.shouldUseFullscreen();
|
||||
|
||||
if (keepScreenOn) {
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
@@ -191,8 +189,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
||||
PreferencesUtils.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
|
||||
trackRecordingServiceConnection.startConnection(this);
|
||||
trackDataHub.start();
|
||||
@@ -230,7 +227,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
PreferencesUtils.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
trackRecordingServiceConnection.unbind(this);
|
||||
trackDataHub.stop();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package de.dennisguse.opentracks.adapters;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Pair;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -11,7 +10,7 @@ import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
import de.dennisguse.opentracks.viewmodels.AggregatedStatistics;
|
||||
@@ -58,7 +57,7 @@ public class AggregatedStatisticsAdapter extends BaseAdapter {
|
||||
viewHolder = (ViewHolder) convertView.getTag();
|
||||
}
|
||||
|
||||
if (TrackIconUtils.isSpeedIcon(context, aggregatedStatistic.getCategory())) {
|
||||
if (TrackIconUtils.isSpeedIcon(context.getResources(), aggregatedStatistic.getCategory())) {
|
||||
viewHolder.setSpeed(aggregatedStatistic);
|
||||
} else {
|
||||
viewHolder.setPace(aggregatedStatistic);
|
||||
@@ -139,9 +138,8 @@ public class AggregatedStatisticsAdapter extends BaseAdapter {
|
||||
private void setCommonValues(AggregatedStatistics.AggregatedStatistic aggregatedStatistic) {
|
||||
String category = aggregatedStatistic.getCategory();
|
||||
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
reportSpeed = PreferencesUtils.isReportSpeed(sharedPreferences, context, category);
|
||||
metricsUnits = PreferencesUtils.isMetricUnits(sharedPreferences, context);
|
||||
reportSpeed = PreferencesUtils.isReportSpeed(category);
|
||||
metricsUnits = PreferencesUtils.isMetricUnits();
|
||||
|
||||
sportIcon.setImageResource(getIcon(aggregatedStatistic));
|
||||
typeLabel.setText(category);
|
||||
|
||||
@@ -59,8 +59,8 @@ public class IntervalStatisticsAdapter extends RecyclerView.Adapter<RecyclerView
|
||||
|
||||
viewHolder.rate.setText(StringUtils.formatSpeed(context, interval.getSpeed(), metricUnits, isReportSpeed));
|
||||
|
||||
viewHolder.gain.setText(StringUtils.formatAltitude(context, interval.getGain_m(), metricUnits));
|
||||
viewHolder.loss.setText(StringUtils.formatAltitude(context, interval.getLoss_m(), metricUnits));
|
||||
viewHolder.gain.setText(StringUtils.formatAltitudeChange(context, interval.getGain_m(), metricUnits));
|
||||
viewHolder.loss.setText(StringUtils.formatAltitudeChange(context, interval.getLoss_m(), metricUnits));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -41,8 +41,8 @@ public class ChartPoint {
|
||||
if (trackPoint.hasHeartRate()) {
|
||||
heartRate = (double) trackPoint.getHeartRate_bpm();
|
||||
}
|
||||
if (trackPoint.hasCyclingCadence()) {
|
||||
cadence = (double) trackPoint.getCyclingCadence_rpm();
|
||||
if (trackPoint.hasCadence()) {
|
||||
cadence = (double) trackPoint.getCadence_rpm();
|
||||
}
|
||||
if (trackPoint.hasPower()) {
|
||||
power = (double) trackPoint.getPower();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package de.dennisguse.opentracks.content.data;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.util.CsvConstants;
|
||||
@@ -57,43 +58,43 @@ public class DataField {
|
||||
return key + CsvConstants.ITEM_SEPARATOR + visible + CsvConstants.ITEM_SEPARATOR + primary;
|
||||
}
|
||||
|
||||
public static String getTitleByKey(Context context, String key) {
|
||||
if (key.equals(context.getString(R.string.stats_custom_layout_total_time_key))) {
|
||||
return context.getString(R.string.stats_total_time);
|
||||
} else if (key.equals(context.getString(R.string.stats_custom_layout_moving_time_key))) {
|
||||
return context.getString(R.string.stats_moving_time);
|
||||
} else if (key.equals(context.getString(R.string.stats_custom_layout_distance_key))) {
|
||||
return context.getString(R.string.stats_distance);
|
||||
} else if (key.equals(context.getString(R.string.stats_custom_layout_speed_key))) {
|
||||
return context.getString(R.string.stats_speed);
|
||||
} else if (key.equals(context.getString(R.string.stats_custom_layout_pace_key))) {
|
||||
return context.getString(R.string.stats_pace);
|
||||
} else if (key.equals(context.getString(R.string.stats_custom_layout_average_moving_speed_key))) {
|
||||
return context.getString(R.string.stats_average_moving_speed);
|
||||
} else if (key.equals(context.getString(R.string.stats_custom_layout_average_speed_key))) {
|
||||
return context.getString(R.string.stats_average_speed);
|
||||
} else if (key.equals(context.getString(R.string.stats_custom_layout_max_speed_key))) {
|
||||
return context.getString(R.string.stats_max_speed);
|
||||
} else if (key.equals(context.getString(R.string.stats_custom_layout_average_moving_pace_key))) {
|
||||
return context.getString(R.string.stats_average_moving_pace);
|
||||
} else if (key.equals(context.getString(R.string.stats_custom_layout_average_pace_key))) {
|
||||
return context.getString(R.string.stats_average_pace);
|
||||
} else if (key.equals(context.getString(R.string.stats_custom_layout_fastest_pace_key))) {
|
||||
return context.getString(R.string.stats_fastest_pace);
|
||||
} else if (key.equals(context.getString(R.string.stats_custom_layout_altitude_key))) {
|
||||
return context.getString(R.string.stats_altitude);
|
||||
} else if (key.equals(context.getString(R.string.stats_custom_layout_gain_key))) {
|
||||
return context.getString(R.string.stats_gain);
|
||||
} else if (key.equals(context.getString(R.string.stats_custom_layout_loss_key))) {
|
||||
return context.getString(R.string.stats_loss);
|
||||
} else if (key.equals(context.getString(R.string.stats_custom_layout_coordinates_key))) {
|
||||
return context.getString(R.string.stats_coordinates);
|
||||
} else if (key.equals(context.getString(R.string.stats_custom_layout_heart_rate_key))) {
|
||||
return context.getString(R.string.stats_sensors_heart_rate);
|
||||
} else if (key.equals(context.getString(R.string.stats_custom_layout_cadence_key))) {
|
||||
return context.getString(R.string.stats_sensors_cadence);
|
||||
} else if (key.equals(context.getString(R.string.stats_custom_layout_power_key))) {
|
||||
return context.getString(R.string.stats_sensors_power);
|
||||
public static String getTitleByKey(Resources resources, String key) {
|
||||
if (key.equals(resources.getString(R.string.stats_custom_layout_total_time_key))) {
|
||||
return resources.getString(R.string.stats_total_time);
|
||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_moving_time_key))) {
|
||||
return resources.getString(R.string.stats_moving_time);
|
||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_distance_key))) {
|
||||
return resources.getString(R.string.stats_distance);
|
||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_speed_key))) {
|
||||
return resources.getString(R.string.stats_speed);
|
||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_pace_key))) {
|
||||
return resources.getString(R.string.stats_pace);
|
||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_average_moving_speed_key))) {
|
||||
return resources.getString(R.string.stats_average_moving_speed);
|
||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_average_speed_key))) {
|
||||
return resources.getString(R.string.stats_average_speed);
|
||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_max_speed_key))) {
|
||||
return resources.getString(R.string.stats_max_speed);
|
||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_average_moving_pace_key))) {
|
||||
return resources.getString(R.string.stats_average_moving_pace);
|
||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_average_pace_key))) {
|
||||
return resources.getString(R.string.stats_average_pace);
|
||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_fastest_pace_key))) {
|
||||
return resources.getString(R.string.stats_fastest_pace);
|
||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_altitude_key))) {
|
||||
return resources.getString(R.string.stats_altitude);
|
||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_gain_key))) {
|
||||
return resources.getString(R.string.stats_gain);
|
||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_loss_key))) {
|
||||
return resources.getString(R.string.stats_loss);
|
||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_coordinates_key))) {
|
||||
return resources.getString(R.string.stats_coordinates);
|
||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_heart_rate_key))) {
|
||||
return resources.getString(R.string.stats_sensors_heart_rate);
|
||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_cadence_key))) {
|
||||
return resources.getString(R.string.stats_sensors_cadence);
|
||||
} else if (key.equals(resources.getString(R.string.stats_custom_layout_power_key))) {
|
||||
return resources.getString(R.string.stats_sensors_power);
|
||||
} else {
|
||||
throw new RuntimeException("It doesn't exists a field with key: " + key);
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ public class TrackPoint {
|
||||
private Type type;
|
||||
|
||||
private Float heartRate_bpm = null;
|
||||
private Float cyclingCadence_rpm = null;
|
||||
private Float cadence_rpm = null;
|
||||
private Float power = null;
|
||||
private Float altitudeGain_m = null;
|
||||
private Float altitudeLoss_m = null;
|
||||
@@ -349,8 +349,21 @@ public class TrackPoint {
|
||||
return this;
|
||||
}
|
||||
|
||||
public TrackPoint minusCumulativeSensorData(@NonNull TrackPoint lastTrackPoint) {
|
||||
if (hasSensorDistance() && lastTrackPoint.hasSensorDistance()) {
|
||||
sensorDistance = sensorDistance.minus(lastTrackPoint.getSensorDistance());
|
||||
}
|
||||
if (hasAltitudeGain() && lastTrackPoint.hasAltitudeGain()) {
|
||||
altitudeGain_m -= lastTrackPoint.altitudeGain_m;
|
||||
}
|
||||
if (hasAltitudeLoss() && lastTrackPoint.hasAltitudeLoss()) {
|
||||
altitudeLoss_m -= lastTrackPoint.altitudeLoss_m;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean hasSensorData() {
|
||||
return hasHeartRate() || hasCyclingCadence() || hasPower();
|
||||
return hasHeartRate() || hasCadence() || hasPower();
|
||||
}
|
||||
|
||||
public boolean hasHeartRate() {
|
||||
@@ -366,16 +379,16 @@ public class TrackPoint {
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean hasCyclingCadence() {
|
||||
return cyclingCadence_rpm != null;
|
||||
public boolean hasCadence() {
|
||||
return cadence_rpm != null;
|
||||
}
|
||||
|
||||
public float getCyclingCadence_rpm() {
|
||||
return cyclingCadence_rpm;
|
||||
public float getCadence_rpm() {
|
||||
return cadence_rpm;
|
||||
}
|
||||
|
||||
public TrackPoint setCyclingCadence_rpm(Float cyclingCadence_rpm) {
|
||||
this.cyclingCadence_rpm = cyclingCadence_rpm;
|
||||
public TrackPoint setCadence_rpm(Float cadence_rpm) {
|
||||
this.cadence_rpm = cadence_rpm;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -396,15 +409,17 @@ public class TrackPoint {
|
||||
@Override
|
||||
public String toString() {
|
||||
String result = "time=" + getTime() + " (type=" + getType() + ")";
|
||||
if (!hasLocation()) {
|
||||
return result;
|
||||
if (hasLocation()) {
|
||||
result += ": lat=" + getLatitude() + " lng=" + getLongitude();
|
||||
}
|
||||
result += ": lat=" + getLatitude() + " lng=" + getLongitude();
|
||||
if (!hasHorizontalAccuracy()) {
|
||||
return result;
|
||||
if (hasHorizontalAccuracy()) {
|
||||
result += " acc=" + getHorizontalAccuracy();
|
||||
}
|
||||
if (hasSensorDistance()) {
|
||||
result += " distance=" + getSensorDistance();
|
||||
}
|
||||
|
||||
return result + " acc=" + getHorizontalAccuracy();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static class Id {
|
||||
|
||||
@@ -558,7 +558,7 @@ public class ContentProviderUtils {
|
||||
trackPoint.setHeartRate_bpm(cursor.getFloat(indexes.sensorHeartRateIndex));
|
||||
}
|
||||
if (!cursor.isNull(indexes.sensorCadenceIndex)) {
|
||||
trackPoint.setCyclingCadence_rpm(cursor.getFloat(indexes.sensorCadenceIndex));
|
||||
trackPoint.setCadence_rpm(cursor.getFloat(indexes.sensorCadenceIndex));
|
||||
}
|
||||
if (!cursor.isNull(indexes.sensorDistanceIndex)) {
|
||||
trackPoint.setSensorDistance(Distance.of(cursor.getFloat(indexes.sensorDistanceIndex)));
|
||||
@@ -719,8 +719,8 @@ public class ContentProviderUtils {
|
||||
if (trackPoint.hasHeartRate()) {
|
||||
values.put(TrackPointsColumns.SENSOR_HEARTRATE, trackPoint.getHeartRate_bpm());
|
||||
}
|
||||
if (trackPoint.hasCyclingCadence()) {
|
||||
values.put(TrackPointsColumns.SENSOR_CADENCE, trackPoint.getCyclingCadence_rpm());
|
||||
if (trackPoint.hasCadence()) {
|
||||
values.put(TrackPointsColumns.SENSOR_CADENCE, trackPoint.getCadence_rpm());
|
||||
}
|
||||
if (trackPoint.hasSensorDistance()) {
|
||||
values.put(TrackPointsColumns.SENSOR_DISTANCE, trackPoint.getSensorDistance().toM());
|
||||
|
||||
@@ -11,6 +11,7 @@ import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import de.dennisguse.opentracks.Startup;
|
||||
import de.dennisguse.opentracks.content.data.MarkerColumns;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
|
||||
@@ -27,11 +28,8 @@ public class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
|
||||
|
||||
private static final int DATABASE_VERSION = 31;
|
||||
|
||||
@VisibleForTesting
|
||||
public static final String DATABASE_NAME = "database.db";
|
||||
|
||||
public CustomSQLiteOpenHelper(Context context) {
|
||||
this(context, DATABASE_NAME);
|
||||
this(context, ((Startup) context.getApplicationContext()).getDatabaseName());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
||||
@@ -50,8 +50,17 @@ public abstract class SensorData<T> {
|
||||
return value != null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
protected abstract T getNoneValue();
|
||||
|
||||
public T getValue() {
|
||||
return value;
|
||||
if (!hasValue()) {
|
||||
return null;
|
||||
}
|
||||
if (isRecent()) {
|
||||
return value;
|
||||
}
|
||||
return getNoneValue();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,7 +72,7 @@ public abstract class SensorData<T> {
|
||||
/**
|
||||
* Is the data recent considering the current time.
|
||||
*/
|
||||
public boolean isRecent() {
|
||||
private boolean isRecent() {
|
||||
return Instant.now()
|
||||
.isBefore(time.plus(BluetoothRemoteSensorManager.MAX_SENSOR_DATE_SET_AGE_MS));
|
||||
}
|
||||
|
||||
@@ -59,17 +59,30 @@ public final class SensorDataCycling {
|
||||
return crankRevolutionsTime;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected Float getNoneValue() {
|
||||
return 0f;
|
||||
}
|
||||
|
||||
public void compute(Cadence previous) {
|
||||
if (hasData() && previous != null && previous.hasData()) {
|
||||
float timeDiff_ms = UintUtils.diff(crankRevolutionsTime, previous.crankRevolutionsTime, UintUtils.UINT16_MAX) / 1024f * UnitConversions.S_TO_MS;
|
||||
if (timeDiff_ms <= 0) {
|
||||
Log.e(TAG, "Timestamps difference is invalid: cannot compute cadence.");
|
||||
value = null;
|
||||
} else {
|
||||
long crankDiff = UintUtils.diff(crankRevolutionsCount, previous.crankRevolutionsCount, UintUtils.UINT32_MAX);
|
||||
float cadence_ms = crankDiff / timeDiff_ms;
|
||||
value = (float) (cadence_ms / UnitConversions.MS_TO_S / UnitConversions.S_TO_MIN);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO We have to treat with overflow according to the documentation: read https://github.com/OpenTracksApp/OpenTracks/pull/953#discussion_r711625268
|
||||
if (crankRevolutionsCount < previous.crankRevolutionsCount) {
|
||||
Log.e(TAG, "Crank revolutions count difference is invalid: cannot compute cadence.");
|
||||
return;
|
||||
}
|
||||
|
||||
long crankDiff = UintUtils.diff(crankRevolutionsCount, previous.crankRevolutionsCount, UintUtils.UINT32_MAX);
|
||||
float cadence_ms = crankDiff / timeDiff_ms;
|
||||
value = (float) (cadence_ms / UnitConversions.MS_TO_S / UnitConversions.S_TO_MIN);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,6 +134,16 @@ public final class SensorDataCycling {
|
||||
return wheelRevolutionsTime;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected Data getNoneValue() {
|
||||
if (value != null) {
|
||||
return new Data(value.distance, value.distanceOverall, Speed.zero());
|
||||
} else {
|
||||
return new Data(Distance.of(0), Distance.of(0), Speed.zero());
|
||||
}
|
||||
}
|
||||
|
||||
public void compute(DistanceSpeed previous, Distance wheelCircumference) {
|
||||
if (hasData() && previous != null && previous.hasData()) {
|
||||
float timeDiff_ms = UintUtils.diff(wheelRevolutionsTime, previous.wheelRevolutionsTime, UintUtils.UINT16_MAX) / 1024f * UnitConversions.S_TO_MS;
|
||||
@@ -131,6 +154,10 @@ public final class SensorDataCycling {
|
||||
return;
|
||||
}
|
||||
|
||||
if (wheelRevolutionsCount < previous.wheelRevolutionsCount) {
|
||||
Log.e(TAG, "Wheel revolutions count difference is invalid: cannot compute speed.");
|
||||
return;
|
||||
}
|
||||
long wheelDiff = UintUtils.diff(wheelRevolutionsCount, previous.wheelRevolutionsCount, UintUtils.UINT32_MAX);
|
||||
|
||||
Distance distance = wheelCircumference.multipliedBy(wheelDiff);
|
||||
@@ -217,6 +244,12 @@ public final class SensorDataCycling {
|
||||
public DistanceSpeed getDistanceSpeed() {
|
||||
return this.value != null ? this.value.second : null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected Pair<Cadence, DistanceSpeed> getNoneValue() {
|
||||
return new Pair<>(null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,4 +18,10 @@ public class SensorDataCyclingPower extends SensorData<Float> {
|
||||
public String toString() {
|
||||
return super.toString() + " power=" + value;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected Float getNoneValue() {
|
||||
return 0f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,4 +18,10 @@ public class SensorDataHeartRate extends SensorData<Float> {
|
||||
public String toString() {
|
||||
return super.toString() + " heart=" + value;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected Float getNoneValue() {
|
||||
return 0f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ public final class SensorDataRunning extends SensorData<SensorDataRunning.Data>
|
||||
return cadence;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public Speed getSpeed() {
|
||||
return speed;
|
||||
}
|
||||
@@ -53,16 +52,28 @@ public final class SensorDataRunning extends SensorData<SensorDataRunning.Data>
|
||||
return totalDistance;
|
||||
}
|
||||
|
||||
public void compute(SensorDataRunning previous) {
|
||||
Distance overallDistance = null;
|
||||
if (hasTotalDistance() && previous != null && previous.hasTotalDistance()) {
|
||||
overallDistance = this.totalDistance.minus(previous.totalDistance);
|
||||
if (previous.hasValue() && previous.getValue().getDistance() != null) {
|
||||
overallDistance = overallDistance.plus(previous.getValue().getDistance());
|
||||
}
|
||||
@NonNull
|
||||
@Override
|
||||
protected Data getNoneValue() {
|
||||
if (value != null) {
|
||||
return new Data(Speed.zero(), 0f, value.distance);
|
||||
} else {
|
||||
return new Data(Speed.zero(), 0f, Distance.of(0));
|
||||
}
|
||||
}
|
||||
|
||||
value = new Data(speed, cadence, overallDistance);
|
||||
public void compute(SensorDataRunning previous) {
|
||||
if (speed != null && hasTotalDistance()) {
|
||||
Distance overallDistance = null;
|
||||
if (previous != null && previous.hasTotalDistance()) {
|
||||
overallDistance = this.totalDistance.minus(previous.totalDistance);
|
||||
if (previous.hasValue() && previous.getValue().getDistance() != null) {
|
||||
overallDistance = overallDistance.plus(previous.getValue().getDistance());
|
||||
}
|
||||
}
|
||||
|
||||
value = new Data(speed, cadence, overallDistance);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package de.dennisguse.opentracks.content.sensor;
|
||||
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.Speed;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
|
||||
public final class SensorDataSet {
|
||||
@@ -27,8 +30,36 @@ public final class SensorDataSet {
|
||||
this.runningDistanceSpeedCadence = toCopy.runningDistanceSpeedCadence;
|
||||
}
|
||||
|
||||
public SensorDataHeartRate getHeartRate() {
|
||||
return heartRate;
|
||||
public Pair<Float, String> getHeartRate() {
|
||||
if (heartRate != null) {
|
||||
return new Pair<>(heartRate.getValue(), heartRate.getSensorNameOrAddress());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Pair<Float, String> getCadence() {
|
||||
if (cyclingCadence != null) {
|
||||
return new Pair<>(cyclingCadence.getValue(), cyclingCadence.getSensorNameOrAddress());
|
||||
}
|
||||
|
||||
if (runningDistanceSpeedCadence != null) {
|
||||
return new Pair<>(runningDistanceSpeedCadence.getCadence(), runningDistanceSpeedCadence.getSensorNameOrAddress());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Pair<Speed, String> getSpeed() {
|
||||
if (cyclingDistanceSpeed != null && cyclingDistanceSpeed.hasValue() && cyclingDistanceSpeed.getValue().getSpeed() != null) {
|
||||
return new Pair<>(cyclingDistanceSpeed.getValue().getSpeed(), cyclingDistanceSpeed.getSensorNameOrAddress());
|
||||
}
|
||||
|
||||
if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasValue() && runningDistanceSpeedCadence.getValue().getSpeed() != null) {
|
||||
return new Pair<>(runningDistanceSpeedCadence.getSpeed(), runningDistanceSpeedCadence.getSensorNameOrAddress());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public SensorDataCycling.Cadence getCyclingCadence() {
|
||||
@@ -64,17 +95,20 @@ public final class SensorDataSet {
|
||||
}
|
||||
|
||||
public void fillTrackPoint(TrackPoint trackPoint) {
|
||||
if (heartRate != null) {
|
||||
trackPoint.setHeartRate_bpm(heartRate.getValue());
|
||||
if (getHeartRate() != null) {
|
||||
trackPoint.setHeartRate_bpm(getHeartRate().first);
|
||||
}
|
||||
|
||||
if (cyclingCadence != null && cyclingCadence.hasValue()) {
|
||||
trackPoint.setCyclingCadence_rpm(cyclingCadence.getValue());
|
||||
if (getCadence() != null) {
|
||||
trackPoint.setCadence_rpm(getCadence().first);
|
||||
}
|
||||
|
||||
if (getSpeed() != null) {
|
||||
trackPoint.setSpeed(getSpeed().first);
|
||||
}
|
||||
|
||||
if (cyclingDistanceSpeed != null && cyclingDistanceSpeed.hasValue()) {
|
||||
trackPoint.setSensorDistance(cyclingDistanceSpeed.getValue().getDistanceOverall());
|
||||
trackPoint.setSpeed(cyclingDistanceSpeed.getValue().getSpeed());
|
||||
}
|
||||
|
||||
if (cyclingPower != null && cyclingPower.hasValue()) {
|
||||
@@ -83,8 +117,6 @@ public final class SensorDataSet {
|
||||
|
||||
if (runningDistanceSpeedCadence != null && runningDistanceSpeedCadence.hasValue()) {
|
||||
trackPoint.setSensorDistance(runningDistanceSpeedCadence.getValue().getDistance());
|
||||
trackPoint.setSpeed(runningDistanceSpeedCadence.getValue().getSpeed());
|
||||
trackPoint.setCyclingCadence_rpm(runningDistanceSpeedCadence.getValue().getCadence());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,18 +125,17 @@ public final class SensorDataSet {
|
||||
if (cyclingCadence != null) cyclingCadence.reset();
|
||||
if (cyclingDistanceSpeed != null) cyclingDistanceSpeed.reset();
|
||||
if (cyclingPower != null) cyclingPower.reset();
|
||||
|
||||
if (runningDistanceSpeedCadence != null) runningDistanceSpeedCadence.reset();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return (getHeartRate() != null ? "" + getHeartRate() : "")
|
||||
+ (getCyclingCadence() != null ? " " + getCyclingCadence() : "")
|
||||
+ (getCyclingDistanceSpeed() != null ? " " + getCyclingDistanceSpeed() : "")
|
||||
+ (getCyclingPower() != null ? " " + getCyclingPower() : "")
|
||||
+ (getRunningDistanceSpeedCadence() != null ? " " + getRunningDistanceSpeedCadence() : "");
|
||||
return (heartRate != null ? "" + heartRate : "")
|
||||
+ (cyclingCadence != null ? " " + cyclingCadence : "")
|
||||
+ (cyclingDistanceSpeed != null ? " " + cyclingDistanceSpeed : "")
|
||||
+ (cyclingPower != null ? " " + cyclingPower : "")
|
||||
+ (runningDistanceSpeedCadence != null ? " " + runningDistanceSpeedCadence : "");
|
||||
}
|
||||
|
||||
private void set(@NonNull SensorData<?> type, SensorData<?> data) {
|
||||
|
||||
@@ -42,7 +42,7 @@ import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.databinding.ChartBinding;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
/**
|
||||
* A fragment to display track chart to the user.
|
||||
@@ -64,8 +64,6 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
return chartFragment;
|
||||
}
|
||||
|
||||
private SharedPreferences sharedPreferences;
|
||||
|
||||
private TrackDataHub trackDataHub;
|
||||
|
||||
// Stats gathered from the received data
|
||||
@@ -80,8 +78,8 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
if (PreferencesUtils.isKey(getContext(), R.string.stats_units_key, key)) {
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, getContext());
|
||||
if (PreferencesUtils.isKey(R.string.stats_units_key, key)) {
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits();
|
||||
if (metricUnits != viewBinding.chartView.getMetricUnits()) {
|
||||
viewBinding.chartView.setMetricUnits(metricUnits);
|
||||
runOnUiThread(() -> {
|
||||
@@ -91,8 +89,8 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
});
|
||||
}
|
||||
}
|
||||
if (PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key)) {
|
||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(sharedPreferences, getContext(), category);
|
||||
if (PreferencesUtils.isKey(R.string.stats_rate_key, key)) {
|
||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(category);
|
||||
if (reportSpeed != viewBinding.chartView.getReportSpeed()) {
|
||||
viewBinding.chartView.setReportSpeed(reportSpeed);
|
||||
viewBinding.chartView.applyReportSpeed();
|
||||
@@ -141,9 +139,8 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
resumeTrackDataHub();
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(getContext());
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
||||
|
||||
PreferencesUtils.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
|
||||
checkChartSettings();
|
||||
getActivity().runOnUiThread(updateChart);
|
||||
@@ -153,7 +150,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
pauseTrackDataHub();
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
PreferencesUtils.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -162,12 +159,6 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
viewBinding = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
sharedPreferences = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrackUpdated(Track track) {
|
||||
if (isResumed()) {
|
||||
@@ -177,7 +168,7 @@ public class ChartFragment extends Fragment implements TrackDataListener {
|
||||
}
|
||||
|
||||
category = track.getCategory();
|
||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(PreferencesUtils.getSharedPreferences(getContext()), getContext(), category);
|
||||
boolean reportSpeed = PreferencesUtils.isReportSpeed(category);
|
||||
if (reportSpeed != viewBinding.chartView.getReportSpeed()) {
|
||||
viewBinding.chartView.setReportSpeed(reportSpeed);
|
||||
viewBinding.chartView.applyReportSpeed();
|
||||
|
||||
@@ -23,7 +23,7 @@ import de.dennisguse.opentracks.adapters.IntervalStatisticsAdapter;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.databinding.IntervalListViewBinding;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.viewmodels.IntervalStatistics;
|
||||
import de.dennisguse.opentracks.viewmodels.IntervalStatisticsModel;
|
||||
|
||||
@@ -46,15 +46,13 @@ public class IntervalsFragment extends Fragment {
|
||||
private IntervalStatisticsAdapter adapter;
|
||||
private ArrayAdapter<IntervalStatisticsModel.IntervalOption> spinnerAdapter;
|
||||
|
||||
private SharedPreferences sharedPreferences;
|
||||
|
||||
private boolean isReportSpeed;
|
||||
|
||||
private IntervalListViewBinding viewBinding;
|
||||
|
||||
protected final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
|
||||
if (PreferencesUtils.isKey(getContext(), R.string.stats_units_key, key) || PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key)) {
|
||||
updateIntervals(PreferencesUtils.isMetricUnits(sharedPreferences, getContext()), selectedInterval);
|
||||
if (PreferencesUtils.isKey(R.string.stats_units_key, key) || PreferencesUtils.isKey(R.string.stats_rate_key, key)) {
|
||||
updateIntervals(PreferencesUtils.isMetricUnits(), selectedInterval);
|
||||
if (spinnerAdapter != null) {
|
||||
spinnerAdapter.notifyDataSetChanged();
|
||||
}
|
||||
@@ -93,8 +91,6 @@ public class IntervalsFragment extends Fragment {
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(getContext());
|
||||
|
||||
adapter = new IntervalStatisticsAdapter(getContext(), stackModeListView, metricUnits, isReportSpeed);
|
||||
viewBinding.intervalList.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
// TODO handle empty view: before we did viewBinding.intervalList.setEmptyView(viewBinding.intervalListEmptyView);
|
||||
@@ -136,13 +132,12 @@ public class IntervalsFragment extends Fragment {
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
||||
PreferencesUtils.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
|
||||
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(getContext());
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
if (track != null) {
|
||||
isReportSpeed = PreferencesUtils.isReportSpeed(sharedPreferences, getContext(), track.getCategory());
|
||||
isReportSpeed = PreferencesUtils.isReportSpeed(track.getCategory());
|
||||
}
|
||||
|
||||
viewModel = new ViewModelProvider(getActivity()).get(IntervalStatisticsModel.class);
|
||||
@@ -152,7 +147,9 @@ public class IntervalsFragment extends Fragment {
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
|
||||
PreferencesUtils.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
|
||||
if (viewModel != null) {
|
||||
viewModel.onPause();
|
||||
}
|
||||
@@ -168,8 +165,6 @@ public class IntervalsFragment extends Fragment {
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
sharedPreferences = null;
|
||||
|
||||
adapter = null;
|
||||
viewModel = null;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.databinding.StatisticsRecordedBinding;
|
||||
import de.dennisguse.opentracks.stats.SensorStatistics;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
import de.dennisguse.opentracks.viewmodels.StatisticData;
|
||||
@@ -79,21 +79,20 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
|
||||
private StatisticsRecordedBinding viewBinding;
|
||||
|
||||
private SharedPreferences sharedPreferences;
|
||||
private boolean preferenceMetricUnits;
|
||||
private boolean preferenceReportSpeed;
|
||||
|
||||
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
|
||||
boolean updateUInecessary = false;
|
||||
|
||||
if (PreferencesUtils.isKey(getContext(), R.string.stats_units_key, key)) {
|
||||
if (PreferencesUtils.isKey(R.string.stats_units_key, key)) {
|
||||
updateUInecessary = true;
|
||||
preferenceMetricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, getContext());
|
||||
preferenceMetricUnits = PreferencesUtils.isMetricUnits();
|
||||
}
|
||||
|
||||
if (PreferencesUtils.isKey(getContext(), R.string.stats_rate_key, key) && track != null) {
|
||||
if (PreferencesUtils.isKey(R.string.stats_rate_key, key) && track != null) {
|
||||
updateUInecessary = true;
|
||||
preferenceReportSpeed = PreferencesUtils.isReportSpeed(sharedPreferences, getContext(), track.getCategory());
|
||||
preferenceReportSpeed = PreferencesUtils.isReportSpeed(track.getCategory());
|
||||
}
|
||||
|
||||
if (key != null && updateUInecessary && isResumed()) {
|
||||
@@ -112,8 +111,6 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
trackId = getArguments().getParcelable(TRACK_ID_KEY);
|
||||
contentProviderUtils = new ContentProviderUtils(getContext());
|
||||
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(getContext());
|
||||
|
||||
sensorsAdapter = new StatisticsAdapter.WithRecordedLayout(getContext());
|
||||
}
|
||||
|
||||
@@ -132,24 +129,22 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
||||
PreferencesUtils.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
|
||||
loadStatistics();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
|
||||
PreferencesUtils.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
viewBinding = null;
|
||||
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
sharedPreferences = null;
|
||||
}
|
||||
|
||||
public void loadStatistics() {
|
||||
@@ -168,7 +163,7 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
boolean prefsChanged = this.track == null || (!this.track.getCategory().equals(track.getCategory()));
|
||||
this.track = track;
|
||||
if (prefsChanged) {
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, getString(R.string.stats_rate_key));
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, getString(R.string.stats_rate_key));
|
||||
}
|
||||
|
||||
loadTrackDescription(track);
|
||||
@@ -243,11 +238,11 @@ public class StatisticsRecordedFragment extends Fragment {
|
||||
|
||||
Pair<String, String> parts;
|
||||
|
||||
parts = StringUtils.getAltitudeParts(getContext(), altitudeGain_m, preferenceMetricUnits);
|
||||
parts = StringUtils.getAltitudeChangeParts(getContext(), altitudeGain_m, preferenceMetricUnits);
|
||||
viewBinding.statsAltitudeGainValue.setText(parts.first);
|
||||
viewBinding.statsAltitudeGainUnit.setText(parts.second);
|
||||
|
||||
parts = StringUtils.getAltitudeParts(getContext(), altitudeLoss_m, preferenceMetricUnits);
|
||||
parts = StringUtils.getAltitudeChangeParts(getContext(), altitudeLoss_m, preferenceMetricUnits);
|
||||
viewBinding.statsAltitudeLossValue.setText(parts.first);
|
||||
viewBinding.statsAltitudeLossUnit.setText(parts.second);
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.databinding.StatisticsRecordingBinding;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.viewmodels.StatisticData;
|
||||
import de.dennisguse.opentracks.viewmodels.StatisticsDataModel;
|
||||
|
||||
@@ -55,24 +55,23 @@ public class StatisticsRecordingFragment extends Fragment {
|
||||
private StatisticsDataModel viewModel;
|
||||
private LiveData<List<StatisticData>> statisticsLiveData;
|
||||
|
||||
private SharedPreferences sharedPreferences;
|
||||
private boolean preferenceMetricUnits;
|
||||
|
||||
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
|
||||
boolean updateUInecessary = false;
|
||||
|
||||
if (PreferencesUtils.isKey(getContext(), R.string.stats_units_key, key)) {
|
||||
if (PreferencesUtils.isKey(R.string.stats_units_key, key)) {
|
||||
updateUInecessary = true;
|
||||
preferenceMetricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, getContext());
|
||||
preferenceMetricUnits = PreferencesUtils.isMetricUnits();
|
||||
}
|
||||
|
||||
if (PreferencesUtils.isKey(getContext(), R.string.stats_custom_layout_fields_key, key)) {
|
||||
if (PreferencesUtils.isKey(R.string.stats_custom_layout_fields_key, key)) {
|
||||
updateUInecessary = true;
|
||||
layout = PreferencesUtils.getCustomLayout(sharedPreferences, getContext());
|
||||
layout = PreferencesUtils.getCustomLayout();
|
||||
}
|
||||
|
||||
if (PreferencesUtils.isKey(getContext(), R.string.stats_custom_layout_columns_key, key)) {
|
||||
gridLayoutManager.setSpanCount(PreferencesUtils.getLayoutColumns(sharedPreferences, getContext()));
|
||||
if (PreferencesUtils.isKey(R.string.stats_custom_layout_columns_key, key)) {
|
||||
gridLayoutManager.setSpanCount(PreferencesUtils.getLayoutColumns());
|
||||
}
|
||||
|
||||
if (key != null && updateUInecessary && isResumed()) {
|
||||
@@ -97,7 +96,6 @@ public class StatisticsRecordingFragment extends Fragment {
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(getContext());
|
||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindChangedCallback);
|
||||
|
||||
statisticsAdapter = new StatisticsAdapter(getContext());
|
||||
@@ -109,7 +107,7 @@ public class StatisticsRecordingFragment extends Fragment {
|
||||
|
||||
RecyclerView recyclerView = viewBinding.statsRecyclerView;
|
||||
recyclerView.addItemDecoration(new DividerItemDecoration(getContext(), RecyclerView.VERTICAL));
|
||||
final int numColumns = PreferencesUtils.getLayoutColumns(sharedPreferences, getContext());
|
||||
final int numColumns = PreferencesUtils.getLayoutColumns();
|
||||
gridLayoutManager = new GridLayoutManager(getContext(), numColumns);
|
||||
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
|
||||
@Override
|
||||
@@ -130,8 +128,7 @@ public class StatisticsRecordingFragment extends Fragment {
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
||||
PreferencesUtils.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
|
||||
viewModel = new ViewModelProvider(getActivity()).get(StatisticsDataModel.class);
|
||||
statisticsLiveData = viewModel.getStatsData();
|
||||
@@ -143,7 +140,7 @@ public class StatisticsRecordingFragment extends Fragment {
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
PreferencesUtils.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -162,9 +159,12 @@ public class StatisticsRecordingFragment extends Fragment {
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
trackRecordingServiceConnection = null;
|
||||
sharedPreferences = null;
|
||||
viewModel = null;
|
||||
statisticsLiveData.removeObservers(getActivity());
|
||||
|
||||
if (statisticsLiveData != null) {
|
||||
statisticsLiveData.removeObservers(getActivity());
|
||||
}
|
||||
statisticsLiveData = null;
|
||||
}
|
||||
|
||||
private void updateUI() {
|
||||
@@ -179,7 +179,7 @@ public class StatisticsRecordingFragment extends Fragment {
|
||||
this.recordingData = recordingData;
|
||||
|
||||
if (!oldCategory.equals(newCategory)) {
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, getString(R.string.stats_rate_key));
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, getString(R.string.stats_rate_key));
|
||||
}
|
||||
|
||||
latestTrackPoint = recordingData.getLatestTrackPoint();
|
||||
|
||||
@@ -285,7 +285,7 @@ public class GPXTrackExporter implements TrackExporter {
|
||||
|
||||
printWriter.println("<time>" + StringUtils.formatDateTimeIso8601(trackPoint.getTime()) + "</time>");
|
||||
|
||||
if (trackPoint.hasSpeed() || trackPoint.hasHeartRate() || trackPoint.hasCyclingCadence() || trackPoint.hasAltitudeGain() || trackPoint.hasAltitudeLoss()) {
|
||||
if (trackPoint.hasSpeed() || trackPoint.hasHeartRate() || trackPoint.hasCadence() || trackPoint.hasAltitudeGain() || trackPoint.hasAltitudeLoss()) {
|
||||
printWriter.println("<extensions><gpxtpx:TrackPointExtension>");
|
||||
|
||||
if (trackPoint.hasSpeed()) {
|
||||
@@ -296,8 +296,8 @@ public class GPXTrackExporter implements TrackExporter {
|
||||
printWriter.println("<gpxtpx:hr>" + HEARTRATE_FORMAT.format(trackPoint.getHeartRate_bpm()) + "</gpxtpx:hr>");
|
||||
}
|
||||
|
||||
if (trackPoint.hasCyclingCadence()) {
|
||||
printWriter.println("<gpxtpx:cad>" + CADENCE_FORMAT.format(trackPoint.getCyclingCadence_rpm()) + "</gpxtpx:cad>");
|
||||
if (trackPoint.hasCadence()) {
|
||||
printWriter.println("<gpxtpx:cad>" + CADENCE_FORMAT.format(trackPoint.getCadence_rpm()) + "</gpxtpx:cad>");
|
||||
}
|
||||
|
||||
if (trackPoint.hasPower()) {
|
||||
|
||||
@@ -382,7 +382,7 @@ public class KMLTrackExporter implements TrackExporter {
|
||||
|
||||
distanceList.add(trackPoint.hasSensorDistance() ? (float) trackPoint.getSensorDistance().toM() : null);
|
||||
heartRateList.add(trackPoint.hasHeartRate() ? trackPoint.getHeartRate_bpm() : null);
|
||||
cadenceList.add(trackPoint.hasCyclingCadence() ? trackPoint.getCyclingCadence_rpm() : null);
|
||||
cadenceList.add(trackPoint.hasCadence() ? trackPoint.getCadence_rpm() : null);
|
||||
powerList.add(trackPoint.hasPower() ? trackPoint.getPower() : null);
|
||||
|
||||
altitudeGainList.add(trackPoint.hasAltitudeGain() ? trackPoint.getAltitudeGain() : null);
|
||||
|
||||
+3
-3
@@ -14,7 +14,7 @@ import java.io.OutputStream;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.databinding.TrackSharePictureBinding;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -61,12 +61,12 @@ public class SharePictureTrackExporter implements TrackExporter {
|
||||
TrackSharePictureBinding viewBinding = TrackSharePictureBinding.inflate(LayoutInflater.from(context));
|
||||
viewBinding.sharePictureCategory.setText(track.getCategory());
|
||||
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(PreferencesUtils.getSharedPreferences(context), context);
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits();
|
||||
TrackStatistics trackStatistics = track.getTrackStatistics();
|
||||
|
||||
viewBinding.sharePictureTotalTime.setText(StringUtils.formatElapsedTimeWithHour(track.getTrackStatistics().getTotalTime()));
|
||||
viewBinding.sharePictureTotalDistance.setText(StringUtils.formatDistance(context, track.getTrackStatistics().getTotalDistance(), metricUnits));
|
||||
viewBinding.sharePictureGain.setText(StringUtils.formatAltitude(context, trackStatistics.getTotalAltitudeGain(), metricUnits));
|
||||
viewBinding.sharePictureGain.setText(StringUtils.formatAltitudeChange(context, trackStatistics.getTotalAltitudeGain(), metricUnits));
|
||||
|
||||
// prepare rendering
|
||||
View view = viewBinding.getRoot();
|
||||
|
||||
+7
-13
@@ -2,7 +2,6 @@ package de.dennisguse.opentracks.io.file.importer;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.provider.DocumentsContract;
|
||||
@@ -13,20 +12,16 @@ import androidx.documentfile.provider.DocumentFile;
|
||||
|
||||
import de.dennisguse.opentracks.io.file.exporter.ExportActivity;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
||||
|
||||
private static final int DIRECTORY_PICKER_REQUEST_CODE = 6;
|
||||
|
||||
protected SharedPreferences sharedPreferences;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||
DocumentFile directoryUri = configureDirectoryChooserIntent(intent);
|
||||
if (directoryUri == null) {
|
||||
@@ -46,7 +41,6 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
sharedPreferences = null;
|
||||
}
|
||||
|
||||
protected void onActivityResultCustom(int requestCode, int resultCode, @Nullable Intent resultData) {
|
||||
@@ -91,14 +85,14 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
||||
super.configureDirectoryChooserIntent(intent);
|
||||
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
|
||||
|
||||
return PreferencesUtils.getDefaultExportDirectoryUri(sharedPreferences, this);
|
||||
return PreferencesUtils.getDefaultExportDirectoryUri(this);
|
||||
}
|
||||
|
||||
@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_TRACKFILEFORMAT_KEY, PreferencesUtils.getExportTrackFileFormat(sharedPreferences, this));
|
||||
intent.putExtra(ExportActivity.EXTRA_TRACKFILEFORMAT_KEY, PreferencesUtils.getExportTrackFileFormat());
|
||||
return intent;
|
||||
}
|
||||
}
|
||||
@@ -112,11 +106,11 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
||||
case RESULT_OK:
|
||||
Uri directoryUri = resultData.getData();
|
||||
|
||||
PreferencesUtils.setDefaultExportDirectoryUri(sharedPreferences, this, directoryUri);
|
||||
PreferencesUtils.setDefaultExportDirectoryUri(directoryUri);
|
||||
IntentUtils.persistDirectoryAccessPermission(getApplicationContext(), directoryUri);
|
||||
break;
|
||||
case RESULT_CANCELED:
|
||||
PreferencesUtils.setDefaultExportDirectoryUri(sharedPreferences, this, null);
|
||||
PreferencesUtils.setDefaultExportDirectoryUri(null);
|
||||
//TODO Remove stored permission contentResolver.releasePersistableUriPermission
|
||||
break;
|
||||
}
|
||||
@@ -129,8 +123,8 @@ public abstract class DirectoryChooserActivity extends AppCompatActivity {
|
||||
protected DocumentFile configureDirectoryChooserIntent(Intent intent) {
|
||||
super.configureDirectoryChooserIntent(intent);
|
||||
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
if (PreferencesUtils.isDefaultExportDirectoryUri(sharedPreferences, this)) {
|
||||
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, PreferencesUtils.getDefaultExportDirectoryUri(sharedPreferences, this).getUri());
|
||||
if (PreferencesUtils.isDefaultExportDirectoryUri(this)) {
|
||||
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, PreferencesUtils.getDefaultExportDirectoryUri(this).getUri());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -70,6 +70,10 @@ public class GpxTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
||||
private static final String ATTRIBUTE_LON = "lon";
|
||||
|
||||
private static final String TAG_EXTENSION_SPEED = "gpxtpx:speed";
|
||||
/**
|
||||
* Often speed is exported without the proper namespace.
|
||||
*/
|
||||
private static final String TAG_EXTENSION_SPEED_COMPAT = "speed";
|
||||
private static final String TAG_EXTENSION_HEARTRATE = "gpxtpx:hr";
|
||||
private static final String TAG_EXTENSION_CADENCE = "gpxtpx:cad";
|
||||
private static final String TAG_EXTENSION_POWER = "pwr:PowerInWatts";
|
||||
@@ -192,6 +196,7 @@ public class GpxTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
||||
}
|
||||
break;
|
||||
case TAG_EXTENSION_SPEED:
|
||||
case TAG_EXTENSION_SPEED_COMPAT:
|
||||
if (content != null) {
|
||||
speed = content.trim();
|
||||
}
|
||||
@@ -295,7 +300,7 @@ public class GpxTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
||||
|
||||
if (cadence != null) {
|
||||
try {
|
||||
trackPoint.setCyclingCadence_rpm(Float.parseFloat(cadence));
|
||||
trackPoint.setCadence_rpm(Float.parseFloat(cadence));
|
||||
} catch (Exception e) {
|
||||
throw new ParsingException(createErrorMessage(String.format(Locale.US, "Unable to parse cadence: %s", cadence)), e);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package de.dennisguse.opentracks.io.file.importer;
|
||||
import android.app.job.JobService;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.ResultReceiver;
|
||||
@@ -22,7 +21,7 @@ import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.util.FileUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
public class ImportService extends JobIntentService {
|
||||
|
||||
@@ -54,10 +53,9 @@ public class ImportService extends JobIntentService {
|
||||
|
||||
String fileExtension = FileUtils.getExtension(file);
|
||||
try {
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
||||
Distance maxRecordingDistance = PreferencesUtils.getMaxRecordingDistance(sharedPreferences, this);
|
||||
Distance recordingDistanceInterval = PreferencesUtils.getRecordingDistanceInterval(sharedPreferences, this);
|
||||
boolean preventReimport = PreferencesUtils.getPreventReimportTracks(sharedPreferences, this);
|
||||
Distance maxRecordingDistance = PreferencesUtils.getMaxRecordingDistance();
|
||||
Distance recordingDistanceInterval = PreferencesUtils.getRecordingDistanceInterval();
|
||||
boolean preventReimport = PreferencesUtils.getPreventReimportTracks();
|
||||
|
||||
TrackImporter trackImporter = new TrackImporter(this, new ContentProviderUtils(this), recordingDistanceInterval, maxRecordingDistance, preventReimport);
|
||||
|
||||
|
||||
@@ -316,7 +316,7 @@ public class KmlTrackImporter extends DefaultHandler implements XMLImporter.Trac
|
||||
trackPoint.setHeartRate_bpm(sensorHeartRateList.get(i));
|
||||
}
|
||||
if (i < sensorCadenceList.size()) {
|
||||
trackPoint.setCyclingCadence_rpm(sensorCadenceList.get(i));
|
||||
trackPoint.setCadence_rpm(sensorCadenceList.get(i));
|
||||
}
|
||||
if (i < sensorPowerList.size()) {
|
||||
trackPoint.setPower(sensorPowerList.get(i));
|
||||
|
||||
@@ -144,6 +144,8 @@ public class TrackImporter {
|
||||
return 0;
|
||||
});
|
||||
|
||||
adjustTrackPoints();
|
||||
|
||||
TrackStatisticsUpdater updater = new TrackStatisticsUpdater();
|
||||
updater.addTrackPoints(trackPoints, recordingDistanceInterval);
|
||||
track.setTrackStatistics(updater.getTrackStatistics());
|
||||
@@ -151,7 +153,6 @@ public class TrackImporter {
|
||||
Track.Id trackId = contentProviderUtils.insertTrack(track);
|
||||
|
||||
// Store TrackPoints
|
||||
adjustTrackPoints();
|
||||
contentProviderUtils.bulkInsertTrackPoint(trackPoints, trackId);
|
||||
|
||||
// Store Markers
|
||||
|
||||
@@ -2,7 +2,6 @@ package de.dennisguse.opentracks.services;
|
||||
|
||||
import android.content.ContentUris;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
@@ -20,7 +19,7 @@ import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.stats.TrackStatisticsUpdater;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
import de.dennisguse.opentracks.util.TrackNameUtils;
|
||||
|
||||
@@ -37,9 +36,9 @@ class TrackRecordingManager {
|
||||
private Track.Id trackId;
|
||||
private TrackStatisticsUpdater trackStatisticsUpdater;
|
||||
|
||||
private boolean currentSegmentHasTrackPoint;
|
||||
private TrackPoint lastTrackPoint;
|
||||
private TrackPoint lastValidTrackPoint;
|
||||
private boolean isIdle;
|
||||
private TrackPoint lastStoredTrackPoint;
|
||||
|
||||
TrackRecordingManager(Context context) {
|
||||
this.context = context;
|
||||
@@ -59,12 +58,14 @@ class TrackRecordingManager {
|
||||
//TODO Pass TrackPoint
|
||||
track.setName(TrackNameUtils.getTrackName(context, trackId, segmentStartTrackPoint.getTime()));
|
||||
|
||||
String category = PreferencesUtils.getDefaultActivity(PreferencesUtils.getSharedPreferences(context), context); //TODO Re-use sharedpreferences
|
||||
String category = PreferencesUtils.getDefaultActivity();
|
||||
track.setCategory(category);
|
||||
track.setIcon(TrackIconUtils.getIconValue(context, category));
|
||||
track.setTrackStatistics(trackStatisticsUpdater.getTrackStatistics());
|
||||
contentProviderUtils.updateTrack(track);
|
||||
|
||||
currentSegmentHasTrackPoint = false;
|
||||
|
||||
return trackId;
|
||||
}
|
||||
|
||||
@@ -79,40 +80,38 @@ class TrackRecordingManager {
|
||||
|
||||
trackStatisticsUpdater = new TrackStatisticsUpdater(track.getTrackStatistics());
|
||||
insertTrackPoint(trackId, segmentStartTrackPoint);
|
||||
}
|
||||
currentSegmentHasTrackPoint = false;
|
||||
lastTrackPoint = null;
|
||||
lastStoredTrackPoint = null; }
|
||||
|
||||
void pause(TrackPointCreator trackPointCreator) {
|
||||
if (lastTrackPoint != null) {
|
||||
insertTrackPointIfNewer(trackId, lastTrackPoint);
|
||||
}
|
||||
insertTrackPoint(trackId, trackPointCreator.createSegmentEnd());
|
||||
currentSegmentHasTrackPoint = false;
|
||||
lastTrackPoint = null;
|
||||
lastStoredTrackPoint = null;
|
||||
}
|
||||
|
||||
void end(TrackPointCreator trackPointCreator) {
|
||||
if (lastTrackPoint != null) {
|
||||
insertTrackPointIfNewer(trackId, lastTrackPoint);
|
||||
}
|
||||
|
||||
TrackPoint segmentEnd = trackPointCreator.createSegmentEnd();
|
||||
insertTrackPoint(trackId, segmentEnd);
|
||||
|
||||
trackId = null;
|
||||
trackStatisticsUpdater = null;
|
||||
lastTrackPoint = null;
|
||||
lastValidTrackPoint = null;
|
||||
isIdle = false;
|
||||
lastStoredTrackPoint = null;
|
||||
currentSegmentHasTrackPoint = false;
|
||||
}
|
||||
|
||||
Pair<Track, Pair<TrackPoint, SensorDataSet>> get(TrackPointCreator trackPointCreator) {
|
||||
if (trackPointCreator == null) {
|
||||
return null;
|
||||
}
|
||||
TrackStatisticsUpdater tmpTrackStatisticsUpdater = getTrackStatisticsUpdater();
|
||||
TrackStatisticsUpdater tmpTrackStatisticsUpdater = new TrackStatisticsUpdater(trackStatisticsUpdater);
|
||||
Pair<TrackPoint, SensorDataSet> current = trackPointCreator.createCurrentTrackPoint(lastTrackPoint);
|
||||
|
||||
tmpTrackStatisticsUpdater.addTrackPoint(current.first, recordingDistanceInterval);
|
||||
|
||||
Track track = getTrack(); //Get copy
|
||||
Track track = contentProviderUtils.getTrack(trackId); //Get copy
|
||||
if (track == null) {
|
||||
Log.w(TAG, "Requesting data if not recording is taking place, should not be done.");
|
||||
return null;
|
||||
@@ -132,8 +131,7 @@ class TrackRecordingManager {
|
||||
name = context.getString(R.string.marker_name_format, nextMarkerNumber + 1);
|
||||
}
|
||||
|
||||
TrackPoint trackPoint = getLastValidTrackPointInCurrentSegment(trackId);
|
||||
if (trackPoint == null) {
|
||||
if (lastStoredTrackPoint == null) {
|
||||
Log.i(TAG, "Could not create a marker as trackPoint is unknown.");
|
||||
return null;
|
||||
}
|
||||
@@ -143,130 +141,68 @@ class TrackRecordingManager {
|
||||
String icon = context.getString(R.string.marker_icon_url);
|
||||
photoUrl = photoUrl != null ? photoUrl : "";
|
||||
|
||||
|
||||
// Insert marker
|
||||
Marker marker = new Marker(name, description, category, icon, trackId, getTrackStatistics(), trackPoint, photoUrl);
|
||||
Marker marker = new Marker(name, description, category, icon, trackId, getTrackStatistics(), lastStoredTrackPoint, photoUrl);
|
||||
Uri uri = contentProviderUtils.insertMarker(marker);
|
||||
return new Marker.Id(ContentUris.parseId(uri));
|
||||
}
|
||||
|
||||
void onNewTrackPoint(TrackPoint trackPoint, Distance thresholdHorizontalAccuracy) {
|
||||
//TODO Figure out how to avoid loading the lastValidTrackPoint from the database
|
||||
TrackPoint lastValidTrackPoint = getLastValidTrackPointInCurrentSegment(trackId);
|
||||
|
||||
boolean onNewTrackPoint(TrackPoint trackPoint) {
|
||||
//Storing trackPoint
|
||||
|
||||
// Always insert the first segment location
|
||||
if (!currentSegmentHasTrackPoint()) {
|
||||
if (!currentSegmentHasTrackPoint) {
|
||||
insertTrackPoint(trackId, trackPoint);
|
||||
lastTrackPoint = trackPoint;
|
||||
return;
|
||||
currentSegmentHasTrackPoint = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
Distance distanceToLastTrackLocation = trackPoint.distanceToPrevious(lastValidTrackPoint);
|
||||
Distance distanceToLastTrackLocation = trackPoint.distanceToPrevious(lastStoredTrackPoint);
|
||||
if (distanceToLastTrackLocation != null) {
|
||||
if (distanceToLastTrackLocation.greaterThan(maxRecordingDistance)) {
|
||||
insertTrackPointIfNewer(trackId, lastTrackPoint);
|
||||
|
||||
trackPoint.setType(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
||||
insertTrackPoint(trackId, trackPoint);
|
||||
|
||||
isIdle = false;
|
||||
lastTrackPoint = trackPoint;
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (trackPoint.hasSensorData() || distanceToLastTrackLocation.greaterOrEqualThan(recordingDistanceInterval)) {
|
||||
insertTrackPointIfNewer(trackId, lastTrackPoint);
|
||||
|
||||
if (trackPoint.hasSensorData() || (distanceToLastTrackLocation.greaterOrEqualThan(recordingDistanceInterval) && trackPoint.isMoving())) {
|
||||
insertTrackPoint(trackId, trackPoint);
|
||||
|
||||
isIdle = false;
|
||||
|
||||
lastTrackPoint = trackPoint;
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isIdle && !trackPoint.isMoving()) {
|
||||
insertTrackPointIfNewer(trackId, lastTrackPoint);
|
||||
|
||||
insertTrackPoint(trackId, trackPoint);
|
||||
|
||||
isIdle = true;
|
||||
|
||||
lastTrackPoint = trackPoint;
|
||||
return;
|
||||
}
|
||||
|
||||
if (isIdle && trackPoint.isMoving()) {
|
||||
insertTrackPointIfNewer(trackId, lastTrackPoint);
|
||||
|
||||
insertTrackPoint(trackId, trackPoint);
|
||||
|
||||
isIdle = false;
|
||||
|
||||
lastTrackPoint = trackPoint;
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d(TAG, "Not recording TrackPoint, idle");
|
||||
lastTrackPoint = trackPoint;
|
||||
}
|
||||
|
||||
Track getTrack() {
|
||||
return contentProviderUtils.getTrack(trackId);
|
||||
}
|
||||
|
||||
//Functionality that uses this method should happen here.
|
||||
@Deprecated
|
||||
TrackStatisticsUpdater getTrackStatisticsUpdater() {
|
||||
return new TrackStatisticsUpdater(trackStatisticsUpdater);
|
||||
return false;
|
||||
}
|
||||
|
||||
TrackStatistics getTrackStatistics() {
|
||||
return trackStatisticsUpdater.getTrackStatistics();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the last valid track point in the current segment.
|
||||
*
|
||||
* @param trackId the track id
|
||||
* @return the location or null
|
||||
*/
|
||||
@Deprecated
|
||||
//Use lastValidTrackPoint
|
||||
private TrackPoint getLastValidTrackPointInCurrentSegment(Track.Id trackId) {
|
||||
if (!currentSegmentHasTrackPoint()) {
|
||||
return null;
|
||||
}
|
||||
return contentProviderUtils.getLastValidTrackPoint(trackId);
|
||||
}
|
||||
|
||||
private boolean currentSegmentHasTrackPoint() {
|
||||
return lastTrackPoint != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a trackPoint if this trackPoint is different than lastValidTrackPoint.
|
||||
*/
|
||||
private void insertTrackPointIfNewer(@NonNull Track.Id trackId, @NonNull TrackPoint trackPoint) {
|
||||
TrackPoint lastValidTrackPoint = getLastValidTrackPointInCurrentSegment(trackId);
|
||||
if (lastValidTrackPoint != null && trackPoint.getTime().equals(lastValidTrackPoint.getTime())) {
|
||||
// Do not insert if inserted already
|
||||
Log.w(TAG, "Ignore insertTrackPoint. trackPoint time same as last valid trackId point time.");
|
||||
return;
|
||||
}
|
||||
|
||||
insertTrackPoint(trackId, trackPoint);
|
||||
}
|
||||
|
||||
private void insertTrackPoint(@NonNull Track.Id trackId, @NonNull TrackPoint trackPoint) {
|
||||
if (lastTrackPoint != null) {
|
||||
if (lastStoredTrackPoint != null && lastTrackPoint.getTime().equals(lastStoredTrackPoint.getTime())) {
|
||||
// Do not insert if inserted already
|
||||
Log.w(TAG, "Ignore insertTrackPoint. trackPoint time same as last valid trackId point time.");
|
||||
} else {
|
||||
insertTrackPointHelper(trackId, lastTrackPoint);
|
||||
// Remove the sensorDistance from trackPoint that is already going be stored with lastTrackPoint.
|
||||
trackPoint.minusCumulativeSensorData(lastTrackPoint);
|
||||
}
|
||||
lastTrackPoint = null;
|
||||
}
|
||||
|
||||
insertTrackPointHelper(trackId, trackPoint);
|
||||
}
|
||||
|
||||
private void insertTrackPointHelper(@NonNull Track.Id trackId, @NonNull TrackPoint trackPoint) {
|
||||
try {
|
||||
contentProviderUtils.insertTrackPoint(trackPoint, trackId);
|
||||
trackStatisticsUpdater.addTrackPoint(trackPoint, recordingDistanceInterval);
|
||||
|
||||
contentProviderUtils.updateTrackStatistics(trackId, trackStatisticsUpdater.getTrackStatistics());
|
||||
lastStoredTrackPoint = trackPoint;
|
||||
} catch (SQLiteException e) {
|
||||
/*
|
||||
* Insert failed, most likely because of SqlLite error code 5 (SQLite_BUSY).
|
||||
@@ -276,12 +212,12 @@ class TrackRecordingManager {
|
||||
}
|
||||
}
|
||||
|
||||
public void onSharedPreferenceChanged(@NonNull SharedPreferences sharedPreferences, String key) {
|
||||
if (PreferencesUtils.isKey(context, R.string.recording_distance_interval_key, key)) {
|
||||
recordingDistanceInterval = PreferencesUtils.getRecordingDistanceInterval(sharedPreferences, context);
|
||||
public void onSharedPreferenceChanged(String key) {
|
||||
if (PreferencesUtils.isKey(R.string.recording_distance_interval_key, key)) {
|
||||
recordingDistanceInterval = PreferencesUtils.getRecordingDistanceInterval();
|
||||
}
|
||||
if (PreferencesUtils.isKey(context, R.string.max_recording_distance_key, key)) {
|
||||
maxRecordingDistance = PreferencesUtils.getMaxRecordingDistance(sharedPreferences, context);
|
||||
if (PreferencesUtils.isKey(R.string.max_recording_distance_key, key)) {
|
||||
maxRecordingDistance = PreferencesUtils.getMaxRecordingDistance();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ import de.dennisguse.opentracks.settings.SettingsActivity;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.util.ExportUtils;
|
||||
import de.dennisguse.opentracks.util.IntentUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.util.SystemUtils;
|
||||
|
||||
/**
|
||||
@@ -96,25 +96,23 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
||||
}
|
||||
};
|
||||
|
||||
private SharedPreferences sharedPreferences;
|
||||
|
||||
private final OnSharedPreferenceChangeListener sharedPreferenceChangeListener = new OnSharedPreferenceChangeListener() {
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
Context context = TrackRecordingService.this;
|
||||
if (PreferencesUtils.isKey(context, R.string.stats_units_key, key)) {
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, context);
|
||||
if (PreferencesUtils.isKey(R.string.stats_units_key, key)) {
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits();
|
||||
notificationManager.setMetricUnits(metricUnits);
|
||||
}
|
||||
if (PreferencesUtils.isKey(context, R.string.voice_announcement_frequency_key, key)) {
|
||||
voiceAnnouncementManager.setFrequency(PreferencesUtils.getVoiceAnnouncementFrequency(sharedPreferences, context));
|
||||
if (PreferencesUtils.isKey(R.string.voice_announcement_frequency_key, key)) {
|
||||
voiceAnnouncementManager.setFrequency(PreferencesUtils.getVoiceAnnouncementFrequency());
|
||||
}
|
||||
if (PreferencesUtils.isKey(context, new int[]{R.string.voice_announcement_distance_key, R.string.stats_units_key}, key)) {
|
||||
voiceAnnouncementManager.setFrequency(PreferencesUtils.getVoiceAnnouncementDistance(sharedPreferences, context));
|
||||
if (PreferencesUtils.isKey(new int[]{R.string.voice_announcement_distance_key, R.string.stats_units_key}, key)) {
|
||||
voiceAnnouncementManager.setFrequency(PreferencesUtils.getVoiceAnnouncementDistance());
|
||||
}
|
||||
|
||||
trackPointCreator.onSharedPreferenceChanged(sharedPreferences, key);
|
||||
trackRecordingManager.onSharedPreferenceChanged(sharedPreferences, key);
|
||||
trackPointCreator.onSharedPreferenceChanged(key);
|
||||
trackRecordingManager.onSharedPreferenceChanged(key);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -148,9 +146,7 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
||||
|
||||
notificationManager = new TrackRecordingServiceNotificationManager(this);
|
||||
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
||||
PreferencesUtils.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -173,8 +169,7 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
||||
// Reverse order from onCreate
|
||||
showNotification(false); //TODO Why?
|
||||
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
sharedPreferences = null;
|
||||
PreferencesUtils.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
|
||||
try {
|
||||
voiceAnnouncementManager.shutdown();
|
||||
@@ -360,14 +355,14 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void newTrackPoint(TrackPoint trackPoint, Distance thresholdHorizontalAccuracy) {
|
||||
public boolean newTrackPoint(TrackPoint trackPoint, Distance thresholdHorizontalAccuracy) {
|
||||
if (!isRecording() || isPaused()) {
|
||||
Log.w(TAG, "Ignore newTrackPoint. Not recording or paused.");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
trackRecordingManager.onNewTrackPoint(trackPoint, thresholdHorizontalAccuracy);
|
||||
notificationManager.updateTrackPoint(this, trackRecordingManager.getTrackStatistics(), trackPoint, thresholdHorizontalAccuracy);
|
||||
return trackRecordingManager.onNewTrackPoint(trackPoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -382,12 +377,16 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
||||
|
||||
private void showNotification(boolean isGpsStarted) {
|
||||
if (isRecording()) {
|
||||
Intent intent = IntentUtils.newIntent(this, TrackRecordingActivity.class)
|
||||
.putExtra(TrackRecordingActivity.EXTRA_TRACK_ID, recordingStatus.getTrackId());
|
||||
Intent intent = IntentUtils.newIntent(this, TrackRecordingActivity.class);
|
||||
|
||||
int pendingIntentFlags = PendingIntent.FLAG_UPDATE_CURRENT;
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
|
||||
pendingIntentFlags |= PendingIntent.FLAG_IMMUTABLE;
|
||||
}
|
||||
|
||||
PendingIntent pendingIntent = TaskStackBuilder.create(this)
|
||||
.addParentStack(TrackRecordingActivity.class)
|
||||
.addNextIntent(intent)
|
||||
.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
.addNextIntentWithParentStack(intent)
|
||||
.getPendingIntent(0, pendingIntentFlags);
|
||||
|
||||
notificationManager.updatePendingIntent(pendingIntent);
|
||||
notificationManager.updateContent(getString(R.string.gps_starting));
|
||||
@@ -395,9 +394,15 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
||||
}
|
||||
if (!isRecording() && isGpsStarted) {
|
||||
Intent intent = IntentUtils.newIntent(this, TrackListActivity.class);
|
||||
|
||||
int pendingIntentFlags = 0;
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
|
||||
pendingIntentFlags = PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT;
|
||||
}
|
||||
PendingIntent pendingIntent = TaskStackBuilder.create(this)
|
||||
.addParentStack(TrackListActivity.class)
|
||||
.addNextIntent(intent)
|
||||
.getPendingIntent(0, 0);
|
||||
.getPendingIntent(0, pendingIntentFlags);
|
||||
|
||||
notificationManager.updatePendingIntent(pendingIntent);
|
||||
notificationManager.updateContent(getString(R.string.gps_starting));
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package de.dennisguse.opentracks.services.handlers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Handler;
|
||||
|
||||
@@ -13,7 +12,7 @@ import java.time.Instant;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.Distance;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
/**
|
||||
* This class handle GPS status according to received locations` and some thresholds.
|
||||
@@ -68,10 +67,9 @@ class GpsStatus {
|
||||
this.client = client;
|
||||
this.context = context;
|
||||
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
thresholdHorizontalAccuracy = PreferencesUtils.getRecordingDistanceInterval(sharedPreferences, context);
|
||||
thresholdHorizontalAccuracy = PreferencesUtils.getRecordingDistanceInterval();
|
||||
|
||||
Duration minRecordingInterval = PreferencesUtils.getMinRecordingInterval(sharedPreferences, context);
|
||||
Duration minRecordingInterval = PreferencesUtils.getMinRecordingInterval();
|
||||
signalLostThreshold = SIGNAL_LOST_THRESHOLD.plus(minRecordingInterval);
|
||||
|
||||
gpsStatusHandler = new Handler();
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package de.dennisguse.opentracks.services.handlers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.location.Location;
|
||||
import android.location.LocationListener;
|
||||
import android.location.LocationManager;
|
||||
@@ -17,7 +16,7 @@ import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.data.Distance;
|
||||
import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.util.LocationUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
|
||||
public class LocationHandler implements LocationListener, GpsStatus.GpsStatusListener {
|
||||
@@ -35,8 +34,8 @@ public class LocationHandler implements LocationListener, GpsStatus.GpsStatusLis
|
||||
this.trackPointCreator = trackPointCreator;
|
||||
}
|
||||
|
||||
public void onStart(@NonNull Context context, SharedPreferences sharedPreferences) {
|
||||
onSharedPreferenceChanged(context, sharedPreferences, null);
|
||||
public void onStart(@NonNull Context context) {
|
||||
onSharedPreferenceChanged(null);
|
||||
gpsStatus = new GpsStatus(context, this);
|
||||
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
|
||||
registerLocationListener();
|
||||
@@ -62,26 +61,26 @@ public class LocationHandler implements LocationListener, GpsStatus.GpsStatusLis
|
||||
}
|
||||
}
|
||||
|
||||
public void onSharedPreferenceChanged(@NonNull Context context, @NonNull SharedPreferences sharedPreferences, String key) {
|
||||
public void onSharedPreferenceChanged(String key) {
|
||||
boolean registerListener = false;
|
||||
|
||||
if (PreferencesUtils.isKey(context, R.string.min_recording_interval_key, key)) {
|
||||
if (PreferencesUtils.isKey(R.string.min_recording_interval_key, key)) {
|
||||
registerListener = true;
|
||||
|
||||
gpsInterval = PreferencesUtils.getMinRecordingInterval(sharedPreferences, context);
|
||||
gpsInterval = PreferencesUtils.getMinRecordingInterval();
|
||||
|
||||
if (gpsStatus != null) {
|
||||
gpsStatus.onMinRecordingIntervalChanged(gpsInterval);
|
||||
}
|
||||
}
|
||||
if (PreferencesUtils.isKey(context, R.string.recording_gps_accuracy_key, key)) {
|
||||
thresholdHorizontalAccuracy = PreferencesUtils.getThresholdHorizontalAccuracy(sharedPreferences, context);
|
||||
if (PreferencesUtils.isKey(R.string.recording_gps_accuracy_key, key)) {
|
||||
thresholdHorizontalAccuracy = PreferencesUtils.getThresholdHorizontalAccuracy();
|
||||
}
|
||||
if (PreferencesUtils.isKey(context, R.string.recording_distance_interval_key, key)) {
|
||||
if (PreferencesUtils.isKey(R.string.recording_distance_interval_key, key)) {
|
||||
registerListener = true;
|
||||
|
||||
if (gpsStatus != null) {
|
||||
Distance gpsMinDistance = PreferencesUtils.getRecordingDistanceInterval(sharedPreferences, context);
|
||||
Distance gpsMinDistance = PreferencesUtils.getRecordingDistanceInterval();
|
||||
gpsStatus.onRecordingDistanceChanged(gpsMinDistance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package de.dennisguse.opentracks.services.handlers;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
@@ -17,7 +16,6 @@ import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.services.sensors.AltitudeSumManager;
|
||||
import de.dennisguse.opentracks.services.sensors.BluetoothRemoteSensorManager;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
|
||||
/**
|
||||
* Creates TrackPoints while recording by fusing data from different sensors (e.g., GNSS, barometer, BLE sensors).
|
||||
@@ -51,8 +49,7 @@ public class TrackPointCreator {
|
||||
public void start(@NonNull Context context) {
|
||||
this.context = context;
|
||||
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
locationHandler.onStart(context, sharedPreferences);
|
||||
locationHandler.onStart(context);
|
||||
|
||||
remoteSensorManager = new BluetoothRemoteSensorManager(context);
|
||||
remoteSensorManager.start();
|
||||
@@ -86,14 +83,6 @@ public class TrackPointCreator {
|
||||
return sensorDataSet;
|
||||
}
|
||||
|
||||
public SensorDataSet fillAndReset(TrackPoint trackPoint) {
|
||||
SensorDataSet sensorDataSet = fill(trackPoint);
|
||||
resetSensorData();
|
||||
|
||||
return sensorDataSet;
|
||||
}
|
||||
|
||||
|
||||
public void stop() {
|
||||
locationHandler.onStop();
|
||||
|
||||
@@ -110,19 +99,17 @@ public class TrackPointCreator {
|
||||
this.context = null;
|
||||
}
|
||||
|
||||
public void onSharedPreferenceChanged(@NonNull SharedPreferences preferences, String key) {
|
||||
if (context == null) {
|
||||
Log.w(TAG, "not started yet.");
|
||||
return;
|
||||
}
|
||||
|
||||
locationHandler.onSharedPreferenceChanged(context, preferences, key);
|
||||
public void onSharedPreferenceChanged(String key) {
|
||||
locationHandler.onSharedPreferenceChanged(key);
|
||||
}
|
||||
|
||||
public void onNewTrackPoint(TrackPoint trackPoint, Distance thresholdHorizontalAccuracy) {
|
||||
fillAndReset(trackPoint);
|
||||
fill(trackPoint);
|
||||
|
||||
service.newTrackPoint(trackPoint, thresholdHorizontalAccuracy);
|
||||
boolean stored = service.newTrackPoint(trackPoint, thresholdHorizontalAccuracy);
|
||||
if (stored) {
|
||||
resetSensorData();
|
||||
}
|
||||
}
|
||||
|
||||
public TrackPoint createSegmentStartManual() {
|
||||
@@ -131,7 +118,8 @@ public class TrackPointCreator {
|
||||
|
||||
public TrackPoint createSegmentEnd() {
|
||||
TrackPoint segmentEnd = TrackPoint.createSegmentEndWithTime(createNow());
|
||||
fillAndReset(segmentEnd);
|
||||
fill(segmentEnd);
|
||||
resetSensorData();
|
||||
return segmentEnd;
|
||||
}
|
||||
|
||||
@@ -189,7 +177,10 @@ public class TrackPointCreator {
|
||||
}
|
||||
|
||||
public interface Callback {
|
||||
void newTrackPoint(TrackPoint trackPoint, Distance thresholdHorizontalAccuracy);
|
||||
/**
|
||||
* @return Was TrackPoint stored (not discarded)?
|
||||
*/
|
||||
boolean newTrackPoint(TrackPoint trackPoint, Distance thresholdHorizontalAccuracy);
|
||||
|
||||
void newGpsStatus(GpsStatusValue gpsStatusValue);
|
||||
}
|
||||
|
||||
@@ -70,6 +70,19 @@ public class AltitudeSumManager implements SensorEventListener {
|
||||
return isConnected ? altitudeGain_m : null;
|
||||
}
|
||||
|
||||
|
||||
@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 ;
|
||||
}
|
||||
|
||||
public @Nullable
|
||||
Float getAltitudeLoss_m() {
|
||||
return isConnected ? altitudeLoss_m : null;
|
||||
|
||||
+17
-20
@@ -34,7 +34,7 @@ import de.dennisguse.opentracks.content.sensor.SensorDataCycling;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataRunning;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.util.BluetoothUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
/**
|
||||
* Bluetooth LE sensor manager: manages connections to Bluetooth LE sensors.
|
||||
@@ -61,7 +61,6 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager.
|
||||
|
||||
private boolean started = false;
|
||||
|
||||
private final SharedPreferences sharedPreferences;
|
||||
private Distance preferenceWheelCircumference;
|
||||
|
||||
private final BluetoothConnectionManager.HeartRate heartRate = new BluetoothConnectionManager.HeartRate(this);
|
||||
@@ -77,34 +76,34 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager.
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
if (!started) return;
|
||||
|
||||
if (PreferencesUtils.isKey(context, R.string.settings_sensor_bluetooth_heart_rate_key, key)) {
|
||||
String address = PreferencesUtils.getBluetoothHeartRateSensorAddress(sharedPreferences, context);
|
||||
if (PreferencesUtils.isKey(R.string.settings_sensor_bluetooth_heart_rate_key, key)) {
|
||||
String address = PreferencesUtils.getBluetoothHeartRateSensorAddress();
|
||||
connect(heartRate, address);
|
||||
}
|
||||
|
||||
if (PreferencesUtils.isKey(context, R.string.settings_sensor_bluetooth_cycling_cadence_key, key)) {
|
||||
String address = PreferencesUtils.getBluetoothCyclingCadenceSensorAddress(sharedPreferences, context);
|
||||
if (PreferencesUtils.isKey(R.string.settings_sensor_bluetooth_cycling_cadence_key, key)) {
|
||||
String address = PreferencesUtils.getBluetoothCyclingCadenceSensorAddress();
|
||||
connect(cyclingCadence, address);
|
||||
}
|
||||
|
||||
if (PreferencesUtils.isKey(context, R.string.settings_sensor_bluetooth_cycling_speed_key, key)) {
|
||||
String address = PreferencesUtils.getBluetoothCyclingSpeedSensorAddress(sharedPreferences, context);
|
||||
if (PreferencesUtils.isKey(R.string.settings_sensor_bluetooth_cycling_speed_key, key)) {
|
||||
String address = PreferencesUtils.getBluetoothCyclingSpeedSensorAddress();
|
||||
|
||||
connect(cyclingSpeed, address);
|
||||
}
|
||||
if (PreferencesUtils.isKey(context, R.string.settings_sensor_bluetooth_cycling_speed_wheel_circumference_key, key)) {
|
||||
preferenceWheelCircumference = PreferencesUtils.getWheelCircumference(sharedPreferences, context);
|
||||
if (PreferencesUtils.isKey(R.string.settings_sensor_bluetooth_cycling_speed_wheel_circumference_key, key)) {
|
||||
preferenceWheelCircumference = PreferencesUtils.getWheelCircumference();
|
||||
}
|
||||
|
||||
if (PreferencesUtils.isKey(context, R.string.settings_sensor_bluetooth_cycling_power_key, key)) {
|
||||
String address = PreferencesUtils.getBluetoothCyclingPowerSensorAddress(sharedPreferences, context);
|
||||
if (PreferencesUtils.isKey(R.string.settings_sensor_bluetooth_cycling_power_key, key)) {
|
||||
String address = PreferencesUtils.getBluetoothCyclingPowerSensorAddress();
|
||||
|
||||
connect(cyclingPower, address);
|
||||
}
|
||||
|
||||
|
||||
if (PreferencesUtils.isKey(context, R.string.settings_sensor_bluetooth_running_speed_and_cadence_key, key)) {
|
||||
String address = PreferencesUtils.getBluetoothRunningSpeedAndCadenceAddress(sharedPreferences, context);
|
||||
if (PreferencesUtils.isKey(R.string.settings_sensor_bluetooth_running_speed_and_cadence_key, key)) {
|
||||
String address = PreferencesUtils.getBluetoothRunningSpeedAndCadenceAddress();
|
||||
|
||||
connect(runningSpeedAndCadence, address);
|
||||
}
|
||||
@@ -113,16 +112,14 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager.
|
||||
|
||||
public BluetoothRemoteSensorManager(Context context) {
|
||||
this.context = context;
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
bluetoothAdapter = BluetoothUtils.getAdapter(context);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
started = true;
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
|
||||
//Trigger connection startup
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
||||
//Registering triggers connection startup
|
||||
PreferencesUtils.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
}
|
||||
|
||||
public synchronized void stop() {
|
||||
@@ -134,7 +131,7 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager.
|
||||
|
||||
sensorDataSet.clear();
|
||||
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
PreferencesUtils.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
started = false;
|
||||
}
|
||||
|
||||
@@ -148,7 +145,7 @@ public class BluetoothRemoteSensorManager implements BluetoothConnectionManager.
|
||||
return;
|
||||
}
|
||||
|
||||
if (PreferencesUtils.isBluetoothSensorAddressNone(context, address)) {
|
||||
if (PreferencesUtils.isBluetoothSensorAddressNone(address)) {
|
||||
Log.w(TAG, "No Bluetooth address.");
|
||||
connectionManager.disconnect();
|
||||
return;
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
package de.dennisguse.opentracks.util;
|
||||
package de.dennisguse.opentracks.services.tasks;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
@@ -12,12 +12,12 @@ import de.dennisguse.opentracks.content.data.Speed;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.viewmodels.IntervalStatistics;
|
||||
|
||||
public class AnnouncementUtils {
|
||||
class AnnouncementUtils {
|
||||
|
||||
private AnnouncementUtils() {
|
||||
}
|
||||
|
||||
public static String getAnnouncement(Context context, TrackStatistics trackStatistics, boolean isMetricUnits, boolean isReportSpeed, @Nullable IntervalStatistics.Interval currentInterval) {
|
||||
static String getAnnouncement(Context context, TrackStatistics trackStatistics, boolean isMetricUnits, boolean isReportSpeed, @Nullable IntervalStatistics.Interval currentInterval) {
|
||||
Distance distance = trackStatistics.getTotalDistance();
|
||||
Speed distancePerTime = trackStatistics.getAverageMovingSpeed();
|
||||
Speed currentDistancePerTime = currentInterval != null ? currentInterval.getSpeed() : null;
|
||||
@@ -17,7 +17,6 @@
|
||||
package de.dennisguse.opentracks.services.tasks;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.speech.tts.TextToSpeech;
|
||||
@@ -35,8 +34,7 @@ import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.util.AnnouncementUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.viewmodels.IntervalStatistics;
|
||||
|
||||
/**
|
||||
@@ -103,8 +101,6 @@ public class VoiceAnnouncement {
|
||||
}
|
||||
};
|
||||
|
||||
private SharedPreferences sharedPreferences;
|
||||
|
||||
private TextToSpeech tts;
|
||||
// Response from TTS after its initialization
|
||||
private int ttsInitStatus = TextToSpeech.ERROR;
|
||||
@@ -122,8 +118,6 @@ public class VoiceAnnouncement {
|
||||
public void start() {
|
||||
Log.d(TAG, "Start");
|
||||
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
|
||||
if (tts == null) {
|
||||
tts = new TextToSpeech(context, status -> {
|
||||
Log.i(TAG, "TextToSpeech initialized with status " + status);
|
||||
@@ -161,9 +155,9 @@ public class VoiceAnnouncement {
|
||||
}
|
||||
|
||||
|
||||
boolean isMetricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, context);
|
||||
boolean isReportSpeed = PreferencesUtils.isReportSpeed(sharedPreferences, context, track.getCategory());
|
||||
Distance minGPSDistance = PreferencesUtils.getRecordingDistanceInterval(sharedPreferences, context);
|
||||
boolean isMetricUnits = PreferencesUtils.isMetricUnits();
|
||||
boolean isReportSpeed = PreferencesUtils.isReportSpeed(track.getCategory());
|
||||
Distance minGPSDistance = PreferencesUtils.getRecordingDistanceInterval();
|
||||
|
||||
//TODO Do not load all trackpoints for every announcement
|
||||
TrackPointIterator trackPointIterator = contentProviderUtils.getTrackPointLocationIterator(track.getId(), null);
|
||||
@@ -187,8 +181,6 @@ public class VoiceAnnouncement {
|
||||
ttsFallback.release();
|
||||
ttsFallback = null;
|
||||
}
|
||||
|
||||
sharedPreferences = null;
|
||||
}
|
||||
|
||||
private void onTtsReady() {
|
||||
@@ -203,7 +195,7 @@ public class VoiceAnnouncement {
|
||||
*/
|
||||
}
|
||||
tts.setLanguage(locale);
|
||||
tts.setSpeechRate(PreferencesUtils.getVoiceSpeedRate(PreferencesUtils.getSharedPreferences(context), context));
|
||||
tts.setSpeechRate(PreferencesUtils.getVoiceSpeedRate());
|
||||
tts.setOnUtteranceProgressListener(utteranceListener);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package de.dennisguse.opentracks.settings;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
@@ -31,7 +30,6 @@ import androidx.preference.PreferenceDialogFragmentCompat;
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
|
||||
import de.dennisguse.opentracks.util.HackUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
|
||||
/**
|
||||
@@ -49,7 +47,7 @@ public class ActivityTypePreference extends DialogPreference {
|
||||
setDialogIcon(null);
|
||||
setPersistent(true);
|
||||
|
||||
SummaryProvider<DialogPreference> summaryProvider = preference -> PreferencesUtils.getDefaultActivity(PreferencesUtils.getSharedPreferences(context), ActivityTypePreference.this.getContext());
|
||||
SummaryProvider<DialogPreference> summaryProvider = preference -> PreferencesUtils.getDefaultActivity();
|
||||
setSummaryProvider(summaryProvider);
|
||||
}
|
||||
|
||||
@@ -61,8 +59,6 @@ public class ActivityTypePreference extends DialogPreference {
|
||||
|
||||
public static class ActivityPreferenceDialog extends PreferenceDialogFragmentCompat {
|
||||
|
||||
private SharedPreferences sharedPreferences;
|
||||
|
||||
private AutoCompleteTextView textView;
|
||||
private ImageView iconView;
|
||||
|
||||
@@ -80,11 +76,9 @@ public class ActivityTypePreference extends DialogPreference {
|
||||
super.onBindDialogView(view);
|
||||
|
||||
final Context context = getActivity();
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
|
||||
|
||||
textView = view.findViewById(R.id.activity_type_preference_text_view);
|
||||
String category = PreferencesUtils.getDefaultActivity(sharedPreferences, context);
|
||||
String category = PreferencesUtils.getDefaultActivity();
|
||||
textView.setText(category);
|
||||
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(context, R.array.activity_types, android.R.layout.simple_dropdown_item_1line);
|
||||
textView.setAdapter(adapter);
|
||||
@@ -105,14 +99,8 @@ public class ActivityTypePreference extends DialogPreference {
|
||||
updateIcon(TrackIconUtils.getIconValue(context, category));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
sharedPreferences = null;
|
||||
}
|
||||
|
||||
private void showIconSelectDialog() {
|
||||
String category = PreferencesUtils.getDefaultActivity(sharedPreferences, getActivity());
|
||||
String category = PreferencesUtils.getDefaultActivity();
|
||||
ChooseActivityTypeDialogFragment.showDialog(getActivity().getSupportFragmentManager(), category);
|
||||
}
|
||||
|
||||
@@ -121,13 +109,14 @@ public class ActivityTypePreference extends DialogPreference {
|
||||
if (positiveResult) {
|
||||
String newDefaultActivity = textView.getText().toString();
|
||||
if (getPreference().callChangeListener(newDefaultActivity)) {
|
||||
PreferencesUtils.setDefaultActivity(sharedPreferences, getActivity(), newDefaultActivity);
|
||||
PreferencesUtils.setDefaultActivity(newDefaultActivity);
|
||||
HackUtils.invalidatePreference(getPreference());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void updateUI(String iconValue) {
|
||||
updateIcon(iconValue);
|
||||
textView.setText(getActivity().getString(TrackIconUtils.getIconActivityType(iconValue)));
|
||||
textView.clearFocus();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package de.dennisguse.opentracks.settings;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
public class AnnouncementsSettingsFragment extends PreferenceFragmentCompat {
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
addPreferencesFromResource(R.xml.settings_announcements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_announcements_title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
ListPreference voiceFrequency = findPreference(getString(R.string.voice_announcement_frequency_key));
|
||||
voiceFrequency.setEntries(PreferencesUtils.getVoiceAnnouncementFrequencyEntries());
|
||||
|
||||
ListPreference voiceDistance = findPreference(getString(R.string.voice_announcement_distance_key));
|
||||
voiceDistance.setEntries(PreferencesUtils.getVoiceAnnouncementDistanceEntries());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package de.dennisguse.opentracks.settings;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
|
||||
import de.dennisguse.opentracks.util.HackUtils;
|
||||
|
||||
public class DefaultsSettingsFragment extends PreferenceFragmentCompat implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller {
|
||||
|
||||
// Used to forward update from ChooseActivityTypeDialogFragment; TODO Could be replaced with LiveData.
|
||||
private ActivityTypePreference.ActivityPreferenceDialog activityPreferenceDialog;
|
||||
|
||||
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
|
||||
if (PreferencesUtils.isKey(R.string.stats_units_key, key)) {
|
||||
getActivity().runOnUiThread(this::updateUnits);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
addPreferencesFromResource(R.xml.settings_defaults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_defaults_title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
PreferencesUtils.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
updateUnits();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
PreferencesUtils.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayPreferenceDialog(Preference preference) {
|
||||
DialogFragment dialogFragment = null;
|
||||
if (preference instanceof ActivityTypePreference) {
|
||||
activityPreferenceDialog = ActivityTypePreference.ActivityPreferenceDialog.newInstance(preference.getKey());
|
||||
dialogFragment = activityPreferenceDialog;
|
||||
}
|
||||
|
||||
if (dialogFragment != null) {
|
||||
dialogFragment.setTargetFragment(this, 0);
|
||||
dialogFragment.show(getParentFragmentManager(), getClass().getSimpleName());
|
||||
return;
|
||||
}
|
||||
|
||||
super.onDisplayPreferenceDialog(preference);
|
||||
}
|
||||
|
||||
private void updateUnits() {
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits();
|
||||
|
||||
ListPreference statsRatePreferences = findPreference(getString(R.string.stats_rate_key));
|
||||
String[] entries = getResources().getStringArray(metricUnits ? R.array.stats_rate_metric_options : R.array.stats_rate_imperial_options);
|
||||
statsRatePreferences.setEntries(entries);
|
||||
|
||||
HackUtils.invalidatePreference(statsRatePreferences);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChooseActivityTypeDone(String iconValue) {
|
||||
if (activityPreferenceDialog != null) {
|
||||
activityPreferenceDialog.updateUI(iconValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package de.dennisguse.opentracks.settings;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.data.Distance;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
|
||||
public class GpsSettingsFragment extends PreferenceFragmentCompat {
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
addPreferencesFromResource(R.xml.settings_gps);
|
||||
|
||||
findPreference(getString(R.string.recording_distance_interval_key))
|
||||
.setSummaryProvider(
|
||||
preference -> {
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits();
|
||||
Distance distance = PreferencesUtils.getRecordingDistanceInterval();
|
||||
return getString(R.string.settings_recording_location_frequency_summary, StringUtils.formatDistance(getContext(), distance, metricUnits));
|
||||
}
|
||||
);
|
||||
|
||||
findPreference(getString(R.string.max_recording_distance_key))
|
||||
.setSummaryProvider(
|
||||
preference -> {
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits();
|
||||
Distance distance = PreferencesUtils.getMaxRecordingDistance();
|
||||
return getString(R.string.settings_recording_max_recording_distance_summary, StringUtils.formatDistance(getContext(), distance, metricUnits));
|
||||
}
|
||||
);
|
||||
|
||||
findPreference(getString(R.string.recording_gps_accuracy_key))
|
||||
.setSummaryProvider(
|
||||
preference -> {
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits();
|
||||
Distance distance = PreferencesUtils.getThresholdHorizontalAccuracy();
|
||||
return getString(R.string.settings_recording_min_required_accuracy_summary, StringUtils.formatDistance(getContext(), distance, metricUnits));
|
||||
}
|
||||
);
|
||||
|
||||
findPreference(getString(R.string.min_recording_interval_key))
|
||||
.setSummaryProvider(
|
||||
preference -> {
|
||||
Duration interval = PreferencesUtils.getMinRecordingInterval();
|
||||
return getString(R.string.settings_recording_location_frequency_summary, getString(R.string.value_integer_second, interval.getSeconds()));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_gps_title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
ListPreference minRecordingInterval = findPreference(getString(R.string.min_recording_interval_key));
|
||||
minRecordingInterval.setEntries(PreferencesUtils.getMinRecordingIntervalEntries());
|
||||
|
||||
ListPreference recordingDistanceInterval = findPreference(getString(R.string.recording_distance_interval_key));
|
||||
recordingDistanceInterval.setEntries(PreferencesUtils.getRecordingDistanceIntervalEntries());
|
||||
|
||||
ListPreference maxRecordingDistance = findPreference(getString(R.string.max_recording_distance_key));
|
||||
maxRecordingDistance.setEntries(PreferencesUtils.getMaxRecordingDistanceEntries());
|
||||
|
||||
ListPreference recordingGpsAccuracy = findPreference(getString(R.string.recording_gps_accuracy_key));
|
||||
recordingGpsAccuracy.setEntries(PreferencesUtils.getThresholdHorizontalAccuracyEntries());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package de.dennisguse.opentracks.settings;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.documentfile.provider.DocumentFile;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
|
||||
public class ImportExportSettingsFragment extends PreferenceFragmentCompat {
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
addPreferencesFromResource(R.xml.settings_import_export);
|
||||
|
||||
setExportTrackFileFormatOptions();
|
||||
setExportDirectorySummary();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_import_export_title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
setExportDirectorySummary();
|
||||
|
||||
Preference instantExportEnabledPreference = findPreference(getString(R.string.post_workout_export_enabled_key));
|
||||
instantExportEnabledPreference.setEnabled(PreferencesUtils.isDefaultExportDirectoryUri(getContext()));
|
||||
}
|
||||
|
||||
private void setExportTrackFileFormatOptions() {
|
||||
final TrackFileFormat[] trackFileFormats = {
|
||||
TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES,
|
||||
TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA,
|
||||
TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA,
|
||||
TrackFileFormat.GPX
|
||||
};
|
||||
String[] entries = new String[trackFileFormats.length];
|
||||
String[] entryValues = new String[trackFileFormats.length];
|
||||
|
||||
for (int i = 0; i < entries.length; i++) {
|
||||
TrackFileFormat trackFileFormat = trackFileFormats[i];
|
||||
String trackFileFormatUpperCase = trackFileFormat.getExtension().toUpperCase(Locale.US); //ASCII upper case
|
||||
int photoMessageId = trackFileFormat.includesPhotos() ? R.string.export_with_photos : R.string.export_without_photos;
|
||||
entries[i] = String.format("%s (%s)", trackFileFormatUpperCase, getString(photoMessageId));
|
||||
entryValues[i] = trackFileFormat.name();
|
||||
}
|
||||
|
||||
ListPreference listPreference = findPreference(getString(R.string.export_trackfileformat_key));
|
||||
listPreference.setEntries(entries);
|
||||
listPreference.setEntryValues(entryValues);
|
||||
}
|
||||
|
||||
private void setExportDirectorySummary() {
|
||||
Preference instantExportDirectoryPreference = findPreference(getString(R.string.settings_default_export_directory_key));
|
||||
instantExportDirectoryPreference.setSummaryProvider(preference -> {
|
||||
DocumentFile directory = PreferencesUtils.getDefaultExportDirectoryUri(getContext());
|
||||
//Use same value for not set as Androidx ListPreference and EditTextPreference
|
||||
return directory != null ? directory.getName() : getString(R.string.not_set);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package de.dennisguse.opentracks.settings;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||
|
||||
public class MainSettingsFragment extends PreferenceFragmentCompat {
|
||||
|
||||
private static final String TAG = MainSettingsFragment.class.getSimpleName();
|
||||
|
||||
private TrackRecordingService.RecordingStatus recordingStatus = TrackRecordingService.STATUS_DEFAULT;
|
||||
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||
private final Runnable bindServiceCallback = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
TrackRecordingService service = trackRecordingServiceConnection.getServiceIfBound();
|
||||
if (service == null) {
|
||||
Log.w(TAG, "could not get TrackRecordingService");
|
||||
return;
|
||||
}
|
||||
|
||||
service.getRecordingStatusObservable()
|
||||
.observe(MainSettingsFragment.this, status -> onRecordingStatusChanged(status));
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
addPreferencesFromResource(R.xml.settings);
|
||||
|
||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindServiceCallback);
|
||||
|
||||
findPreference(getString(R.string.settings_defaults_key)).setOnPreferenceClickListener(preference -> {
|
||||
((SettingsActivity) getActivity()).openScreen(getString(R.string.settings_defaults_key));
|
||||
return true;
|
||||
});
|
||||
|
||||
findPreference(getString(R.string.settings_ui_key)).setOnPreferenceClickListener(preference -> {
|
||||
((SettingsActivity) getActivity()).openScreen(getString(R.string.settings_ui_key));
|
||||
return true;
|
||||
});
|
||||
|
||||
findPreference(getString(R.string.settings_gps_key)).setOnPreferenceClickListener(preference -> {
|
||||
((SettingsActivity) getActivity()).openScreen(getString(R.string.settings_gps_key));
|
||||
return true;
|
||||
});
|
||||
|
||||
findPreference(getString(R.string.settings_sensors_key)).setOnPreferenceClickListener(preference -> {
|
||||
((SettingsActivity) getActivity()).openScreen(getString(R.string.settings_sensors_key));
|
||||
return true;
|
||||
});
|
||||
|
||||
findPreference(getString(R.string.settings_announcements_key)).setOnPreferenceClickListener(preference -> {
|
||||
((SettingsActivity) getActivity()).openScreen(getString(R.string.settings_announcements_key));
|
||||
return true;
|
||||
});
|
||||
|
||||
findPreference(getString(R.string.settings_import_export_key)).setOnPreferenceClickListener(preference -> {
|
||||
((SettingsActivity) getActivity()).openScreen(getString(R.string.settings_import_export_key));
|
||||
return true;
|
||||
});
|
||||
|
||||
findPreference(getString(R.string.settings_open_tracks_key)).setOnPreferenceClickListener(preference -> {
|
||||
((SettingsActivity) getActivity()).openScreen(getString(R.string.settings_open_tracks_key));
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.menu_settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
trackRecordingServiceConnection.bind(getContext());
|
||||
updatePrefsDependOnRecording();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
trackRecordingServiceConnection.unbind(getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayPreferenceDialog(Preference preference) {
|
||||
DialogFragment dialogFragment = null;
|
||||
|
||||
if (preference instanceof ResetDialogPreference) {
|
||||
dialogFragment = ResetDialogPreference.ResetPreferenceDialog.newInstance(preference.getKey());
|
||||
}
|
||||
|
||||
if (dialogFragment != null) {
|
||||
dialogFragment.setTargetFragment(this, 0);
|
||||
dialogFragment.show(getParentFragmentManager(), getClass().getSimpleName());
|
||||
return;
|
||||
}
|
||||
|
||||
super.onDisplayPreferenceDialog(preference);
|
||||
}
|
||||
|
||||
private void onRecordingStatusChanged(TrackRecordingService.RecordingStatus status) {
|
||||
this.recordingStatus = status;
|
||||
if (isAdded()) {
|
||||
updatePrefsDependOnRecording();
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePrefsDependOnRecording() {
|
||||
Preference importExportPreference = findPreference(getString(R.string.settings_import_export_key));
|
||||
Preference resetPreference = findPreference(getString(R.string.settings_reset_key));
|
||||
|
||||
boolean isRecording = recordingStatus.isRecording();
|
||||
|
||||
importExportPreference.setSummary(isRecording ? getString(R.string.settings_not_while_recording) : getString(R.string.settings_import_export_summary));
|
||||
importExportPreference.setEnabled(!isRecording);
|
||||
|
||||
resetPreference.setSummary(isRecording ? getString(R.string.settings_not_while_recording) : getString(R.string.settings_reset_summary));
|
||||
resetPreference.setEnabled(!isRecording);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package de.dennisguse.opentracks.settings;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
public class OpenTracksSettingsFragment extends PreferenceFragmentCompat {
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
addPreferencesFromResource(R.xml.settings_open_tracks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_open_tracks_title);
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
package de.dennisguse.opentracks.settings;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.util.UnitConversions;
|
||||
|
||||
final class PreferenceHelper {
|
||||
|
||||
static String[] getMinRecordingIntervalEntries(Context context) {
|
||||
String[] entryValues = context.getResources().getStringArray(R.array.min_recording_interval_values);
|
||||
String[] entries = new String[entryValues.length];
|
||||
for (int i = 0; i < entryValues.length; i++) {
|
||||
int value = Integer.parseInt(entryValues[i]);
|
||||
|
||||
if (value == PreferencesUtils.getMinRecordingIntervalDefault(context).getSeconds()) {
|
||||
entries[i] = context.getString(R.string.value_smallest_recommended);
|
||||
} else {
|
||||
entries[i] = value < 60 ? context.getString(R.string.value_integer_second, value) : context.getString(R.string.value_integer_minute, value / 60);
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
static String[] getRecordingDistanceIntervalEntries(Context context, boolean metricUnits) {
|
||||
String[] entryValues = context.getResources().getStringArray(R.array.recording_distance_interval_values);
|
||||
String[] entries = new String[entryValues.length];
|
||||
|
||||
final int recordingDistanceIntervalDefault = (int) PreferencesUtils.getRecordingDistanceIntervalDefault(context).toM();
|
||||
|
||||
for (int i = 0; i < entryValues.length; i++) {
|
||||
int value = Integer.parseInt(entryValues[i]);
|
||||
String displayValue;
|
||||
if (metricUnits) {
|
||||
displayValue = context.getString(R.string.value_integer_meter, value);
|
||||
if (value == recordingDistanceIntervalDefault) {
|
||||
entries[i] = context.getString(R.string.value_integer_meter_recommended, value);
|
||||
} else {
|
||||
entries[i] = displayValue;
|
||||
}
|
||||
} else {
|
||||
int feet = (int) (value * UnitConversions.M_TO_FT);
|
||||
displayValue = context.getString(R.string.value_integer_feet, feet);
|
||||
if (value == recordingDistanceIntervalDefault) {
|
||||
entries[i] = context.getString(R.string.value_integer_feet_recommended, feet);
|
||||
} else {
|
||||
entries[i] = displayValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
static String[] getMaxRecordingDistanceEntries(Context context, boolean metricUnits) {
|
||||
String[] entryValues = context.getResources().getStringArray(R.array.max_recording_distance_values);
|
||||
String[] entries = new String[entryValues.length];
|
||||
|
||||
final int maxRecordingDistanceDefault = Integer.parseInt(context.getResources().getString(R.string.max_recording_distance_default));
|
||||
|
||||
for (int i = 0; i < entryValues.length; i++) {
|
||||
int value = Integer.parseInt(entryValues[i]);
|
||||
String displayValue;
|
||||
if (metricUnits) {
|
||||
displayValue = context.getString(R.string.value_integer_meter, value);
|
||||
if (value == maxRecordingDistanceDefault) {
|
||||
entries[i] = context.getString(R.string.value_integer_meter_recommended, value);
|
||||
} else {
|
||||
entries[i] = displayValue;
|
||||
}
|
||||
} else {
|
||||
int feet = (int) (value * UnitConversions.M_TO_FT);
|
||||
displayValue = context.getString(R.string.value_integer_feet, feet);
|
||||
if (value == maxRecordingDistanceDefault) {
|
||||
entries[i] = context.getString(R.string.value_integer_feet_recommended, feet);
|
||||
} else {
|
||||
entries[i] = displayValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
static String[] getRecordingGpsAccuracyEntries(Context context, boolean metricUnits) {
|
||||
String[] entryValues = context.getResources().getStringArray(R.array.recording_gps_accuracy_values);
|
||||
String[] entries = new String[entryValues.length];
|
||||
|
||||
final int recordingGPSAccuracyDefault = Integer.parseInt(context.getResources().getString(R.string.recording_gps_accuracy_default));
|
||||
final int recordingGPSAccuracyExcellent = Integer.parseInt(context.getResources().getString(R.string.recording_gps_accuracy_excellent));
|
||||
final int recordingGPSAccuracyPoor = Integer.parseInt(context.getResources().getString(R.string.recording_gps_accuracy_poor));
|
||||
|
||||
for (int i = 0; i < entryValues.length; i++) {
|
||||
int value = Integer.parseInt(entryValues[i]);
|
||||
String displayValue;
|
||||
if (metricUnits) {
|
||||
displayValue = context.getString(R.string.value_integer_meter, value);
|
||||
if (value == recordingGPSAccuracyDefault) {
|
||||
entries[i] = context.getString(R.string.value_integer_meter_recommended, value);
|
||||
} else if (value == recordingGPSAccuracyExcellent) {
|
||||
entries[i] = context.getString(R.string.value_integer_meter_excellent_gps, value);
|
||||
} else if (value == recordingGPSAccuracyPoor) {
|
||||
entries[i] = context.getString(R.string.value_integer_meter_poor_gps, value);
|
||||
} else {
|
||||
entries[i] = displayValue;
|
||||
}
|
||||
} else {
|
||||
int feet = (int) (value * UnitConversions.M_TO_FT);
|
||||
displayValue = context.getString(R.string.value_integer_feet, feet);
|
||||
|
||||
if (value == recordingGPSAccuracyDefault) {
|
||||
entries[i] = context.getString(R.string.value_integer_feet_recommended, feet);
|
||||
} else if (value == recordingGPSAccuracyExcellent) {
|
||||
entries[i] = context.getString(R.string.value_integer_feet_excellent_gps, feet);
|
||||
} else {
|
||||
entries[i] = displayValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package de.dennisguse.opentracks.settings;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
class PreferencesOpenHelper {
|
||||
|
||||
private static final int PREFERENCES_VERSION = 1;
|
||||
|
||||
private PreferencesOpenHelper() {
|
||||
}
|
||||
|
||||
static PreferencesOpenHelper newInstance() {
|
||||
return new PreferencesOpenHelper();
|
||||
}
|
||||
|
||||
void checkForUpgrade() {
|
||||
int lastVersion = PreferencesUtils.getInt(R.string.prefs_last_version_key, 0);
|
||||
if (PREFERENCES_VERSION > lastVersion) {
|
||||
onUpgrade();
|
||||
}
|
||||
}
|
||||
|
||||
private void onUpgrade() {
|
||||
PreferencesUtils.setInt(R.string.prefs_last_version_key, PREFERENCES_VERSION);
|
||||
switch (PREFERENCES_VERSION) {
|
||||
case 1:
|
||||
upgradeFrom0to1();
|
||||
}
|
||||
}
|
||||
|
||||
private void upgradeFrom0to1() {
|
||||
PreferencesUtils.setString(R.string.stats_custom_layout_fields_key, PreferencesUtils.buildDefaultLayout());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,595 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package de.dennisguse.opentracks.settings;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.documentfile.provider.DocumentFile;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.data.DataField;
|
||||
import de.dennisguse.opentracks.content.data.Distance;
|
||||
import de.dennisguse.opentracks.content.data.Layout;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.util.CsvConstants;
|
||||
import de.dennisguse.opentracks.util.TrackIconUtils;
|
||||
import de.dennisguse.opentracks.util.UnitConversions;
|
||||
|
||||
/**
|
||||
* Utilities to access preferences stored in {@link SharedPreferences}.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class PreferencesUtils {
|
||||
|
||||
private final static String TAG = PreferencesUtils.class.getSimpleName();
|
||||
|
||||
private PreferencesUtils() {
|
||||
}
|
||||
|
||||
private static SharedPreferences sharedPreferences;
|
||||
|
||||
private static Resources resources;
|
||||
|
||||
/**
|
||||
* Must be called during application startup.
|
||||
*/
|
||||
public static void initPreferences(final Context context, final Resources resources) {
|
||||
PreferencesUtils.resources = resources;
|
||||
PreferencesUtils.sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
PreferencesOpenHelper.newInstance().checkForUpgrade();
|
||||
}
|
||||
|
||||
public static void registerOnSharedPreferenceChangeListener(SharedPreferences.OnSharedPreferenceChangeListener changeListener) {
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(changeListener);
|
||||
changeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
||||
}
|
||||
|
||||
public static void unregisterOnSharedPreferenceChangeListener(SharedPreferences.OnSharedPreferenceChangeListener changeListener) {
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(changeListener);
|
||||
}
|
||||
|
||||
public static String getDefaultActivity() {
|
||||
return getString(R.string.default_activity_key, resources.getString(R.string.default_activity_default));
|
||||
}
|
||||
|
||||
public static void setDefaultActivity(String newDefaultActivity) {
|
||||
setString(R.string.default_activity_key, newDefaultActivity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a preference key
|
||||
*
|
||||
* @param keyId the key id
|
||||
*/
|
||||
private static String getKey(int keyId) {
|
||||
return resources.getString(keyId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares if keyId and key belong to the same shared preference key.
|
||||
*
|
||||
* @param keyId The resource id of the key
|
||||
* @param key The key of the preference
|
||||
* @return true if key == null or key belongs to keyId
|
||||
*/
|
||||
public static boolean isKey(int keyId, String key) {
|
||||
return key == null || key.equals(getKey(keyId));
|
||||
}
|
||||
|
||||
public static boolean isKey(int[] keyIds, String key) {
|
||||
for(int keyId : keyIds) {
|
||||
if (isKey(keyId, key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean getBoolean(int keyId, boolean defaultValue) {
|
||||
return sharedPreferences.getBoolean(getKey(keyId), defaultValue);
|
||||
}
|
||||
|
||||
static int getInt(int keyId, int defaultValue) {
|
||||
try {
|
||||
return sharedPreferences.getInt(getKey(keyId), defaultValue);
|
||||
} catch (ClassCastException e) {
|
||||
//Ignore
|
||||
}
|
||||
|
||||
//NOTE: We assume that the data was stored as String due to use of ListPreference.
|
||||
try {
|
||||
String stringValue = sharedPreferences.getString(getKey(keyId), null);
|
||||
return Integer.parseInt(stringValue);
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
private static float getFloat(int keyId, float defaultValue) {
|
||||
try {
|
||||
return sharedPreferences.getFloat(getKey(keyId), defaultValue);
|
||||
} catch (ClassCastException e) {
|
||||
//Ignore
|
||||
}
|
||||
|
||||
//NOTE: We assume that the data was stored as String due to use of ListPreference.
|
||||
try {
|
||||
String stringValue = sharedPreferences.getString(getKey(keyId), null);
|
||||
return Float.parseFloat(stringValue);
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getString(int keyId, String defaultValue) {
|
||||
return sharedPreferences.getString(getKey(keyId), defaultValue);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static void setString(int keyId, String value) {
|
||||
Editor editor = sharedPreferences.edit();
|
||||
editor.putString(getKey(keyId), value);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
static void setInt(int keyId, int value) {
|
||||
Editor editor = sharedPreferences.edit();
|
||||
editor.putInt(getKey(keyId), value);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public static boolean isMetricUnits() {
|
||||
final String STATS_UNIT = resources.getString(R.string.stats_units_default);
|
||||
return STATS_UNIT.equals(getString(R.string.stats_units_key, STATS_UNIT));
|
||||
}
|
||||
|
||||
public static void setMetricUnits(boolean metricUnits) {
|
||||
String unit;
|
||||
if (metricUnits) {
|
||||
unit = resources.getString(R.string.stats_units_metric);
|
||||
} else {
|
||||
unit = resources.getString(R.string.stats_units_imperial);
|
||||
}
|
||||
setString( R.string.stats_units_key, unit);
|
||||
}
|
||||
|
||||
public static boolean isReportSpeed(String category) {
|
||||
final String STATS_RATE_DEFAULT = resources.getString(R.string.stats_rate_default);
|
||||
String currentStatsRate = getString(R.string.stats_rate_key, STATS_RATE_DEFAULT);
|
||||
if (currentStatsRate.equals(getString(R.string.stats_rate_speed_or_pace_default, STATS_RATE_DEFAULT))) {
|
||||
return TrackIconUtils.isSpeedIcon(resources, category);
|
||||
}
|
||||
|
||||
return currentStatsRate.equals(resources.getString(R.string.stats_rate_speed));
|
||||
}
|
||||
|
||||
private static String getBluetoothSensorAddressNone() {
|
||||
return resources.getString(R.string.sensor_type_value_none);
|
||||
}
|
||||
|
||||
public static boolean isBluetoothSensorAddressNone(String currentValue) {
|
||||
return getBluetoothSensorAddressNone().equals(currentValue);
|
||||
}
|
||||
|
||||
public static String getBluetoothHeartRateSensorAddress() {
|
||||
return getString(R.string.settings_sensor_bluetooth_heart_rate_key, getBluetoothSensorAddressNone());
|
||||
}
|
||||
|
||||
public static String getBluetoothCyclingCadenceSensorAddress() {
|
||||
return getString(R.string.settings_sensor_bluetooth_cycling_cadence_key, getBluetoothSensorAddressNone());
|
||||
}
|
||||
|
||||
public static String getBluetoothCyclingSpeedSensorAddress() {
|
||||
return getString(R.string.settings_sensor_bluetooth_cycling_speed_key, getBluetoothSensorAddressNone());
|
||||
}
|
||||
|
||||
public static Distance getWheelCircumference() {
|
||||
final int DEFAULT = Integer.parseInt(resources.getString(R.string.settings_sensor_bluetooth_cycling_speed_wheel_circumference_default));
|
||||
return Distance.ofMM(getInt(R.string.settings_sensor_bluetooth_cycling_speed_wheel_circumference_key, DEFAULT));
|
||||
}
|
||||
|
||||
public static String getBluetoothCyclingPowerSensorAddress() {
|
||||
return getString(R.string.settings_sensor_bluetooth_cycling_power_key, getBluetoothSensorAddressNone());
|
||||
}
|
||||
|
||||
public static String getBluetoothRunningSpeedAndCadenceAddress() {
|
||||
return getString(R.string.settings_sensor_bluetooth_running_speed_and_cadence_key, getBluetoothSensorAddressNone());
|
||||
}
|
||||
|
||||
public static boolean shouldShowStatsOnLockscreen() {
|
||||
final boolean STATS_SHOW_ON_LOCKSCREEN_DEFAULT = resources.getBoolean(R.bool.stats_show_on_lockscreen_while_recording_default);
|
||||
return getBoolean(R.string.stats_show_on_lockscreen_while_recording_key, STATS_SHOW_ON_LOCKSCREEN_DEFAULT);
|
||||
}
|
||||
|
||||
public static boolean shouldKeepScreenOn() {
|
||||
final boolean DEFAULT = resources.getBoolean(R.bool.stats_keep_screen_on_while_recording_default);
|
||||
return getBoolean(R.string.stats_keep_screen_on_while_recording_key, DEFAULT);
|
||||
}
|
||||
|
||||
public static boolean shouldUseFullscreen() {
|
||||
final boolean DEFAULT = resources.getBoolean(R.bool.stats_fullscreen_while_recording_default);
|
||||
return getBoolean(R.string.stats_fullscreen_while_recording_key, DEFAULT);
|
||||
}
|
||||
|
||||
public static Duration getVoiceAnnouncementFrequency() {
|
||||
final int DEFAULT = Integer.parseInt(resources.getString(R.string.voice_announcement_frequency_default));
|
||||
int value = getInt(R.string.voice_announcement_frequency_key, DEFAULT);
|
||||
return Duration.ofSeconds(value);
|
||||
}
|
||||
|
||||
static String[] getVoiceAnnouncementFrequencyEntries() {
|
||||
String[] values = resources.getStringArray(R.array.voice_announcement_frequency_values);
|
||||
String[] options = new String[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (resources.getString(R.string.announcement_off).equals(values[i])) {
|
||||
options[i] = resources.getString(R.string.value_off);
|
||||
} else {
|
||||
int value = Integer.parseInt(values[i]);
|
||||
options[i] = resources.getString(R.string.value_integer_minute, Duration.ofSeconds(value).toMinutes());
|
||||
}
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Result depends on isMetricUnits
|
||||
*/
|
||||
public static Distance getVoiceAnnouncementDistance() {
|
||||
final float DEFAULT = Integer.parseInt(resources.getString(R.string.voice_announcement_distance_default));
|
||||
float value = getFloat(R.string.voice_announcement_distance_key, DEFAULT);
|
||||
return Distance.one(isMetricUnits()).multipliedBy(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Result depends on isMetricUnits
|
||||
*/
|
||||
static String[] getVoiceAnnouncementDistanceEntries() {
|
||||
String[] values = resources.getStringArray(R.array.voice_announcement_distance_values);
|
||||
String[] options = new String[values.length];
|
||||
boolean metricUnits = isMetricUnits();
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (resources.getString(R.string.announcement_off).equals(values[i])) {
|
||||
options[i] = resources.getString(R.string.value_off);
|
||||
} else {
|
||||
int value = Integer.parseInt(values[i]);
|
||||
options[i] = resources.getString(metricUnits ? R.string.value_integer_kilometer : R.string.value_integer_mile, value);
|
||||
}
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
public static float getVoiceSpeedRate() {
|
||||
final float DEFAULT = Float.parseFloat(resources.getString(R.string.voice_speed_rate_default));
|
||||
return getFloat(R.string.voice_speed_rate_key, DEFAULT);
|
||||
}
|
||||
|
||||
public static Distance getRecordingDistanceInterval() {
|
||||
return Distance.of(getInt(R.string.recording_distance_interval_key, getRecordingDistanceIntervalDefaultInternal()));
|
||||
}
|
||||
|
||||
public static Distance getRecordingDistanceIntervalDefault() {
|
||||
return Distance.of(getRecordingDistanceIntervalDefaultInternal());
|
||||
}
|
||||
|
||||
private static int getRecordingDistanceIntervalDefaultInternal() {
|
||||
return Integer.parseInt(resources.getString(R.string.recording_distance_interval_default));
|
||||
}
|
||||
|
||||
static String[] getRecordingDistanceIntervalEntries() {
|
||||
String[] entryValues = resources.getStringArray(R.array.recording_distance_interval_values);
|
||||
String[] entries = new String[entryValues.length];
|
||||
|
||||
final int recordingDistanceIntervalDefault = (int) getRecordingDistanceIntervalDefault().toM();
|
||||
boolean metricUnits = isMetricUnits();
|
||||
|
||||
for (int i = 0; i < entryValues.length; i++) {
|
||||
int value = Integer.parseInt(entryValues[i]);
|
||||
String displayValue;
|
||||
if (metricUnits) {
|
||||
displayValue = resources.getString(R.string.value_integer_meter, value);
|
||||
if (value == recordingDistanceIntervalDefault) {
|
||||
entries[i] = resources.getString(R.string.value_integer_meter_recommended, value);
|
||||
} else {
|
||||
entries[i] = displayValue;
|
||||
}
|
||||
} else {
|
||||
int feet = (int) (value * UnitConversions.M_TO_FT);
|
||||
displayValue = resources.getString(R.string.value_integer_feet, feet);
|
||||
if (value == recordingDistanceIntervalDefault) {
|
||||
entries[i] = resources.getString(R.string.value_integer_feet_recommended, feet);
|
||||
} else {
|
||||
entries[i] = displayValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
public static Distance getMaxRecordingDistance() {
|
||||
final int MAX_RECORDING_DISTANCE = Integer.parseInt(resources.getString(R.string.max_recording_distance_default));
|
||||
return Distance.of(getInt(R.string.max_recording_distance_key, MAX_RECORDING_DISTANCE));
|
||||
}
|
||||
|
||||
static String[] getMaxRecordingDistanceEntries() {
|
||||
String[] entryValues = resources.getStringArray(R.array.max_recording_distance_values);
|
||||
String[] entries = new String[entryValues.length];
|
||||
|
||||
final int maxRecordingDistanceDefault = Integer.parseInt(resources.getString(R.string.max_recording_distance_default));
|
||||
boolean metricUnits = isMetricUnits();
|
||||
|
||||
for (int i = 0; i < entryValues.length; i++) {
|
||||
int value = Integer.parseInt(entryValues[i]);
|
||||
String displayValue;
|
||||
if (metricUnits) {
|
||||
displayValue = resources.getString(R.string.value_integer_meter, value);
|
||||
if (value == maxRecordingDistanceDefault) {
|
||||
entries[i] = resources.getString(R.string.value_integer_meter_recommended, value);
|
||||
} else {
|
||||
entries[i] = displayValue;
|
||||
}
|
||||
} else {
|
||||
int feet = (int) (value * UnitConversions.M_TO_FT);
|
||||
displayValue = resources.getString(R.string.value_integer_feet, feet);
|
||||
if (value == maxRecordingDistanceDefault) {
|
||||
entries[i] = resources.getString(R.string.value_integer_feet_recommended, feet);
|
||||
} else {
|
||||
entries[i] = displayValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
public static Duration getMinRecordingInterval() {
|
||||
final Duration MIN_RECORDING_INTERVAL = getMinRecordingIntervalDefault();
|
||||
return Duration.ofSeconds(getInt(R.string.min_recording_interval_key, (int) MIN_RECORDING_INTERVAL.getSeconds()));
|
||||
}
|
||||
|
||||
public static Duration getMinRecordingIntervalDefault() {
|
||||
return Duration.ofSeconds(Integer.parseInt(resources.getString(R.string.min_recording_interval_default)));
|
||||
}
|
||||
|
||||
static String[] getMinRecordingIntervalEntries() {
|
||||
String[] entryValues = resources.getStringArray(R.array.min_recording_interval_values);
|
||||
String[] entries = new String[entryValues.length];
|
||||
for (int i = 0; i < entryValues.length; i++) {
|
||||
int value = Integer.parseInt(entryValues[i]);
|
||||
|
||||
if (value == PreferencesUtils.getMinRecordingIntervalDefault().getSeconds()) {
|
||||
entries[i] = resources.getString(R.string.value_smallest_recommended);
|
||||
} else {
|
||||
entries[i] = value < 60 ? resources.getString(R.string.value_integer_second, value) : resources.getString(R.string.value_integer_minute, value / 60);
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
public static Distance getThresholdHorizontalAccuracy() {
|
||||
final int RECORDING_GPS_ACCURACY = Integer.parseInt(resources.getString(R.string.recording_gps_accuracy_default));
|
||||
return Distance.of(getInt(R.string.recording_gps_accuracy_key, RECORDING_GPS_ACCURACY));
|
||||
}
|
||||
|
||||
static String[] getThresholdHorizontalAccuracyEntries() {
|
||||
String[] entryValues = resources.getStringArray(R.array.recording_gps_accuracy_values);
|
||||
String[] entries = new String[entryValues.length];
|
||||
|
||||
final int recordingGPSAccuracyDefault = Integer.parseInt(resources.getString(R.string.recording_gps_accuracy_default));
|
||||
final int recordingGPSAccuracyExcellent = Integer.parseInt(resources.getString(R.string.recording_gps_accuracy_excellent));
|
||||
final int recordingGPSAccuracyPoor = Integer.parseInt(resources.getString(R.string.recording_gps_accuracy_poor));
|
||||
|
||||
boolean metricUnits = isMetricUnits();
|
||||
|
||||
for (int i = 0; i < entryValues.length; i++) {
|
||||
int value = Integer.parseInt(entryValues[i]);
|
||||
String displayValue;
|
||||
if (metricUnits) {
|
||||
displayValue = resources.getString(R.string.value_integer_meter, value);
|
||||
if (value == recordingGPSAccuracyDefault) {
|
||||
entries[i] = resources.getString(R.string.value_integer_meter_recommended, value);
|
||||
} else if (value == recordingGPSAccuracyExcellent) {
|
||||
entries[i] = resources.getString(R.string.value_integer_meter_excellent_gps, value);
|
||||
} else if (value == recordingGPSAccuracyPoor) {
|
||||
entries[i] = resources.getString(R.string.value_integer_meter_poor_gps, value);
|
||||
} else {
|
||||
entries[i] = displayValue;
|
||||
}
|
||||
} else {
|
||||
int feet = (int) (value * UnitConversions.M_TO_FT);
|
||||
displayValue = resources.getString(R.string.value_integer_feet, feet);
|
||||
|
||||
if (value == recordingGPSAccuracyDefault) {
|
||||
entries[i] = resources.getString(R.string.value_integer_feet_recommended, feet);
|
||||
} else if (value == recordingGPSAccuracyExcellent) {
|
||||
entries[i] = resources.getString(R.string.value_integer_feet_excellent_gps, feet);
|
||||
} else {
|
||||
entries[i] = displayValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
|
||||
public static boolean shouldInstantExportAfterWorkout(Context context) {
|
||||
final boolean INSTANT_POST_WORKOUT_EXPORT_DEFAULT = resources.getBoolean(R.bool.post_workout_export_enabled_default);
|
||||
return getBoolean(R.string.post_workout_export_enabled_key, INSTANT_POST_WORKOUT_EXPORT_DEFAULT) && isDefaultExportDirectoryUri(context);
|
||||
}
|
||||
|
||||
public static TrackFileFormat getExportTrackFileFormat() {
|
||||
final String TRACKFILEFORMAT_NAME_DEFAULT = getString(R.string.export_trackfileformat_default, null);
|
||||
String trackFileFormatName = getString(R.string.export_trackfileformat_key, TRACKFILEFORMAT_NAME_DEFAULT);
|
||||
try {
|
||||
return TrackFileFormat.valueOf(trackFileFormatName);
|
||||
} catch (Exception e) {
|
||||
return TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean getPreventReimportTracks() {
|
||||
final boolean defaultValue = getBoolean(R.bool.import_prevent_reimport_default, false);
|
||||
return getBoolean(R.string.import_prevent_reimport_key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link androidx.appcompat.app.AppCompatDelegate}.MODE_*
|
||||
*/
|
||||
public static int getDefaultNightMode() {
|
||||
final String defaultValue = getKey(R.string.night_mode_default);
|
||||
final String value = getString(R.string.night_mode_key, defaultValue);
|
||||
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
|
||||
public static void resetPreferences(Context context, boolean readAgain) {
|
||||
if (readAgain) {
|
||||
// We want to really clear settings now.
|
||||
sharedPreferences.edit().clear().commit();
|
||||
}
|
||||
PreferenceManager.setDefaultValues(context, R.xml.settings, readAgain);
|
||||
}
|
||||
|
||||
public static DocumentFile getDefaultExportDirectoryUri(Context context) {
|
||||
String singleExportDirectorySettingsKey = getString(R.string.settings_default_export_directory_key, null);
|
||||
if (singleExportDirectorySettingsKey == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return DocumentFile.fromTreeUri(context, Uri.parse(singleExportDirectorySettingsKey));
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Could not decode default export directory: " + e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void setDefaultExportDirectoryUri(Uri directoryUri) {
|
||||
String value = directoryUri != null ? directoryUri.toString() : null;
|
||||
setString(R.string.settings_default_export_directory_key, value);
|
||||
}
|
||||
|
||||
public static boolean isDefaultExportDirectoryUri(Context context) {
|
||||
return getDefaultExportDirectoryUri(context) != null;
|
||||
}
|
||||
|
||||
public static int getLayoutColumns() {
|
||||
return getInt(R.string.stats_custom_layout_columns_key, resources.getInteger(R.integer.stats_custom_layout_columns_default));
|
||||
}
|
||||
|
||||
public static void setLayoutColumns(int columns) {
|
||||
setInt(R.string.stats_custom_layout_columns_key, columns);
|
||||
}
|
||||
|
||||
private static List<TypedArray> getMultiTypedArray(String key) {
|
||||
List<TypedArray> typedArrays = new ArrayList<>();
|
||||
|
||||
try {
|
||||
Class<R.array> resource = R.array.class;
|
||||
Field field;
|
||||
int i = 0;
|
||||
|
||||
do {
|
||||
field = resource.getField(key + resources.getString(R.string.stats_custom_layout_fields_default_value_separator) + i);
|
||||
typedArrays.add(resources.obtainTypedArray(field.getInt(null)));
|
||||
i++;
|
||||
} while (field != null); //TODO Catch no such field exception instead of using an endless loop.
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, e.getMessage());
|
||||
}
|
||||
|
||||
return typedArrays;
|
||||
}
|
||||
|
||||
@SuppressLint("ResourceType")
|
||||
static String buildDefaultLayout() {
|
||||
List<TypedArray> fieldsArrays = getMultiTypedArray("stats_custom_layout_fields_default_value");
|
||||
return resources.getString(R.string.default_activity_default) + CsvConstants.LINE_SEPARATOR
|
||||
+ fieldsArrays.stream().map(i -> i.getString(0) + CsvConstants.ITEM_SEPARATOR + i.getString(1)).collect(Collectors.joining(CsvConstants.LINE_SEPARATOR))
|
||||
+ CsvConstants.LINE_SEPARATOR;
|
||||
}
|
||||
|
||||
public static Layout getCustomLayout() {
|
||||
String csvCustomLayout = getString(R.string.stats_custom_layout_fields_key, buildDefaultLayout());
|
||||
List<String> csvParts = Arrays.asList(csvCustomLayout.split(CsvConstants.LINE_SEPARATOR));
|
||||
Layout layout = new Layout(csvParts.get(0));
|
||||
for (int i = 1; i < csvParts.size(); i++) {
|
||||
String[] fieldParts = csvParts.get(i).split(CsvConstants.ITEM_SEPARATOR);
|
||||
layout.addField(fieldParts[0], DataField.getTitleByKey(resources, fieldParts[0]), fieldParts[1].equals(DataField.YES_VALUE), fieldParts[2].equals(DataField.YES_VALUE), fieldParts[0].equals(resources.getString(R.string.stats_custom_layout_coordinates_key)));
|
||||
}
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
public static void setCustomLayout(Layout layout) {
|
||||
List<DataField> fields = layout.getFields();
|
||||
if (fields.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String csv = layout.getProfile() + CsvConstants.LINE_SEPARATOR
|
||||
+ fields.stream().map(DataField::toCsv).collect(Collectors.joining(CsvConstants.LINE_SEPARATOR))
|
||||
+ CsvConstants.LINE_SEPARATOR;
|
||||
setString(R.string.stats_custom_layout_fields_key, csv);
|
||||
}
|
||||
|
||||
public static void resetCustomLayoutPreferences() {
|
||||
if (sharedPreferences.contains(resources.getString(R.string.stats_custom_layout_fields_key))) {
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.remove(resources.getString(R.string.stats_custom_layout_fields_key));
|
||||
editor.commit();
|
||||
}
|
||||
if (sharedPreferences.contains(resources.getString(R.string.stats_custom_layout_columns_key))) {
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.remove(resources.getString(R.string.stats_custom_layout_columns_key));
|
||||
editor.commit();
|
||||
}
|
||||
}
|
||||
|
||||
public static void applyNightMode() {
|
||||
AppCompatDelegate.setDefaultNightMode(PreferencesUtils.getDefaultNightMode());
|
||||
}
|
||||
|
||||
//TODO Check if resetPreferences can be used instead.
|
||||
@Deprecated
|
||||
@VisibleForTesting
|
||||
public static void clear() {
|
||||
sharedPreferences.edit().clear().commit();
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import androidx.preference.DialogPreference;
|
||||
import androidx.preference.PreferenceDialogFragmentCompat;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
|
||||
public class ResetDialogPreference extends DialogPreference {
|
||||
|
||||
@@ -46,7 +45,7 @@ public class ResetDialogPreference extends DialogPreference {
|
||||
PreferencesUtils.resetPreferences(activity, true);
|
||||
Toast.makeText(activity, R.string.settings_reset_done, Toast.LENGTH_SHORT).show();
|
||||
} else if (preferenceKey.equals(getString(R.string.settings_layout_reset_key))) {
|
||||
PreferencesUtils.resetCustomLayoutPreferences(activity);
|
||||
PreferencesUtils.resetCustomLayoutPreferences();
|
||||
Toast.makeText(activity, R.string.settings_layout_reset_done, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package de.dennisguse.opentracks.settings;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.preference.EditTextPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.settings.bluetooth.BluetoothLeCyclingCadenceAndSpeedPreference;
|
||||
import de.dennisguse.opentracks.settings.bluetooth.BluetoothLeCyclingPowerPreference;
|
||||
import de.dennisguse.opentracks.settings.bluetooth.BluetoothLeHeartRatePreference;
|
||||
import de.dennisguse.opentracks.settings.bluetooth.BluetoothLeRunningSpeedAndCadencePreference;
|
||||
import de.dennisguse.opentracks.settings.bluetooth.BluetoothLeSensorPreference;
|
||||
import de.dennisguse.opentracks.util.BluetoothUtils;
|
||||
|
||||
public class SensorsSettingsFragment extends PreferenceFragmentCompat {
|
||||
|
||||
private final static String TAG = SensorsSettingsFragment.class.getSimpleName();
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
addPreferencesFromResource(R.xml.settings_sensors);
|
||||
setWheelCircumferenceInputFilter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_sensors_title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayPreferenceDialog(Preference preference) {
|
||||
DialogFragment dialogFragment = null;
|
||||
if (preference instanceof BluetoothLeHeartRatePreference) {
|
||||
dialogFragment = BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(preference.getKey(), BluetoothUtils.HEART_RATE_SUPPORTING_DEVICES);
|
||||
} else if (preference instanceof BluetoothLeCyclingCadenceAndSpeedPreference) {
|
||||
dialogFragment = BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(preference.getKey(), BluetoothUtils.CYCLING_SPEED_CADENCE_SERVICE_UUID);
|
||||
} else if (preference instanceof BluetoothLeCyclingPowerPreference) {
|
||||
dialogFragment = BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(preference.getKey(), BluetoothUtils.CYCLING_POWER_UUID);
|
||||
} else if (preference instanceof BluetoothLeRunningSpeedAndCadencePreference) {
|
||||
dialogFragment = BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(preference.getKey(), BluetoothUtils.RUNNING_RUNNING_SPEED_CADENCE_UUID);
|
||||
}
|
||||
|
||||
if (dialogFragment != null) {
|
||||
dialogFragment.setTargetFragment(this, 0);
|
||||
dialogFragment.show(getParentFragmentManager(), getClass().getSimpleName());
|
||||
return;
|
||||
}
|
||||
|
||||
super.onDisplayPreferenceDialog(preference);
|
||||
}
|
||||
|
||||
private void setWheelCircumferenceInputFilter() {
|
||||
EditTextPreference wheelPreference = findPreference(getString(R.string.settings_sensor_bluetooth_cycling_speed_wheel_circumference_key));
|
||||
wheelPreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
if (newValue instanceof String) {
|
||||
try {
|
||||
int newValueInt = Integer.parseInt((String) newValue);
|
||||
return newValueInt > 500 && newValueInt < 4000;
|
||||
} catch (NumberFormatException e) {
|
||||
Log.w(TAG, "Entered string is no number.");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,50 +1,25 @@
|
||||
package de.dennisguse.opentracks.settings;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.documentfile.provider.DocumentFile;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.preference.EditTextPreference;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Locale;
|
||||
|
||||
import de.dennisguse.opentracks.AbstractActivity;
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.data.Distance;
|
||||
import de.dennisguse.opentracks.fragments.ChooseActivityTypeDialogFragment;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||
import de.dennisguse.opentracks.settings.bluetooth.BluetoothLeCyclingCadenceAndSpeedPreference;
|
||||
import de.dennisguse.opentracks.settings.bluetooth.BluetoothLeCyclingPowerPreference;
|
||||
import de.dennisguse.opentracks.settings.bluetooth.BluetoothLeHeartRatePreference;
|
||||
import de.dennisguse.opentracks.settings.bluetooth.BluetoothLeRunningSpeedAndCadencePreference;
|
||||
import de.dennisguse.opentracks.settings.bluetooth.BluetoothLeSensorPreference;
|
||||
import de.dennisguse.opentracks.util.ActivityUtils;
|
||||
import de.dennisguse.opentracks.util.BluetoothUtils;
|
||||
import de.dennisguse.opentracks.util.HackUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
|
||||
public class SettingsActivity extends AbstractActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller, ResetDialogPreference.ResetCallback {
|
||||
public class SettingsActivity extends AbstractActivity implements ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller {
|
||||
|
||||
private static final String TAG = SettingsActivity.class.getSimpleName();
|
||||
public static final String EXTRAS_CHECK_EXPORT_DIRECTORY = "Check Export Directory";
|
||||
|
||||
private PrefsFragment prefsFragment;
|
||||
private boolean checkExportDirectory = false;
|
||||
|
||||
public static final String FRAGMENT_KEY = "fragmentKey";
|
||||
|
||||
private PreferenceFragmentCompat fragment = null;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -53,18 +28,14 @@ public class SettingsActivity extends AbstractActivity implements ChooseActivity
|
||||
if (intent != null && intent.hasExtra(EXTRAS_CHECK_EXPORT_DIRECTORY)) {
|
||||
checkExportDirectory = true;
|
||||
}
|
||||
onReset();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View getRootView() {
|
||||
return getLayoutInflater().inflate(R.layout.settings, null);
|
||||
}
|
||||
if (savedInstanceState != null) {
|
||||
fragment = (PreferenceFragmentCompat) getSupportFragmentManager().getFragment(savedInstanceState, FRAGMENT_KEY);
|
||||
} else {
|
||||
fragment = new MainSettingsFragment();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setupActionBarBack(Toolbar toolbar) {
|
||||
super.setupActionBarBack(toolbar);
|
||||
toolbar.setTitle(R.string.menu_settings);
|
||||
getSupportFragmentManager().beginTransaction().replace(R.id.settings_fragment, fragment).commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -84,275 +55,50 @@ public class SettingsActivity extends AbstractActivity implements ChooseActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChooseActivityTypeDone(String iconValue) {
|
||||
prefsFragment.setDefaultActivity(iconValue);
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
getSupportFragmentManager().putFragment(outState, FRAGMENT_KEY, fragment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReset() {
|
||||
prefsFragment = new PrefsFragment();
|
||||
getSupportFragmentManager().beginTransaction().replace(R.id.settings_fragment, prefsFragment).commit();
|
||||
protected View getRootView() {
|
||||
return getLayoutInflater().inflate(R.layout.settings, null);
|
||||
}
|
||||
|
||||
public static class PrefsFragment extends PreferenceFragmentCompat {
|
||||
private PreferenceFragmentCompat getPreferenceScreen(String key) {
|
||||
PreferenceFragmentCompat fragment = null;
|
||||
|
||||
private SharedPreferences sharedPreferences;
|
||||
|
||||
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
|
||||
if (PreferencesUtils.isKey(getActivity(), R.string.stats_units_key, key)) {
|
||||
getActivity().runOnUiThread(this::updateUnits);
|
||||
}
|
||||
if (PreferencesUtils.isKey(getActivity(), R.string.night_mode_key, key)) {
|
||||
getActivity().runOnUiThread(() -> ActivityUtils.applyNightMode(sharedPreferences, getContext()));
|
||||
}
|
||||
};
|
||||
|
||||
private TrackRecordingServiceConnection trackRecordingServiceConnection;
|
||||
private TrackRecordingService.RecordingStatus recordingStatus = TrackRecordingService.STATUS_DEFAULT;
|
||||
|
||||
private final Runnable bindServiceCallback = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
TrackRecordingService service = trackRecordingServiceConnection.getServiceIfBound();
|
||||
if (service == null) {
|
||||
Log.w(TAG, "could not get TrackRecordingService");
|
||||
return;
|
||||
}
|
||||
|
||||
service.getRecordingStatusObservable()
|
||||
.observe(PrefsFragment.this, status -> onRecordingStatusChanged(status));
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
// Used to forward update from ChooseActivityTypeDialogFragment; TODO Could be replaced with LiveData.
|
||||
private ActivityTypePreference.ActivityPreferenceDialog activityPreferenceDialog;
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(getContext());
|
||||
|
||||
trackRecordingServiceConnection = new TrackRecordingServiceConnection(bindServiceCallback);
|
||||
|
||||
try {
|
||||
setPreferencesFromResource(R.xml.settings, rootKey);
|
||||
} catch (ClassCastException e) {
|
||||
// Some sharedPreference is broken: delete all and reset them; it is just a last resort...
|
||||
Toast.makeText(getContext(), R.string.settings_error_initial_values_restored, Toast.LENGTH_LONG).show();
|
||||
PreferencesUtils.resetPreferences(getContext(), true);
|
||||
|
||||
setPreferencesFromResource(R.xml.settings, rootKey);
|
||||
}
|
||||
|
||||
setExportTrackFileFormatOptions();
|
||||
setWheelCircumferenceInputFilter();
|
||||
|
||||
Preference instantExportDirectoryPreference = findPreference(getString(R.string.settings_default_export_directory_key));
|
||||
instantExportDirectoryPreference.setSummaryProvider(preference -> {
|
||||
DocumentFile directory = PreferencesUtils.getDefaultExportDirectoryUri(sharedPreferences, getContext());
|
||||
//Use same value for not set as Androidx ListPreference and EditTextPreference
|
||||
return directory != null ? directory.getName() : getString(R.string.not_set);
|
||||
});
|
||||
|
||||
findPreference(getString(R.string.recording_distance_interval_key))
|
||||
.setSummaryProvider(
|
||||
preference -> {
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, getActivity());
|
||||
Distance distance = PreferencesUtils.getRecordingDistanceInterval(sharedPreferences, getContext());
|
||||
return getString(R.string.settings_recording_location_frequency_summary, StringUtils.formatDistance(getContext(), distance, metricUnits));
|
||||
}
|
||||
);
|
||||
findPreference(getString(R.string.max_recording_distance_key))
|
||||
.setSummaryProvider(
|
||||
preference -> {
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, getActivity());
|
||||
Distance distance = PreferencesUtils.getMaxRecordingDistance(sharedPreferences, getContext());
|
||||
return getString(R.string.settings_recording_max_recording_distance_summary, StringUtils.formatDistance(getContext(), distance, metricUnits));
|
||||
}
|
||||
);
|
||||
findPreference(getString(R.string.recording_gps_accuracy_key))
|
||||
.setSummaryProvider(
|
||||
preference -> {
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, getActivity());
|
||||
Distance distance = PreferencesUtils.getThresholdHorizontalAccuracy(sharedPreferences, getContext());
|
||||
return getString(R.string.settings_recording_min_required_accuracy_summary, StringUtils.formatDistance(getContext(), distance, metricUnits));
|
||||
}
|
||||
);
|
||||
findPreference(getString(R.string.min_recording_interval_key))
|
||||
.setSummaryProvider(
|
||||
preference -> {
|
||||
Duration interval = PreferencesUtils.getMinRecordingInterval(sharedPreferences, getContext());
|
||||
return getString(R.string.settings_recording_location_frequency_summary, getString(R.string.value_integer_second, interval.getSeconds()));
|
||||
}
|
||||
);
|
||||
|
||||
Preference customLayoutPreference = findPreference(getString(R.string.stats_custom_layout_key));
|
||||
customLayoutPreference.setOnPreferenceClickListener((preference) -> {
|
||||
Intent intent = new Intent(getContext(), SettingsCustomLayoutActivity.class);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
});
|
||||
if (key.equals(getString(R.string.settings_defaults_key))) {
|
||||
fragment = new DefaultsSettingsFragment();
|
||||
} else if (key.equals(getString(R.string.settings_ui_key))) {
|
||||
fragment = new UserInterfaceSettingsFragment();
|
||||
} else if (key.equals(getString(R.string.settings_gps_key))) {
|
||||
fragment = new GpsSettingsFragment();
|
||||
} else if (key.equals(getString(R.string.settings_sensors_key))) {
|
||||
fragment = new SensorsSettingsFragment();
|
||||
} else if (key.equals(getString(R.string.settings_announcements_key))) {
|
||||
fragment = new AnnouncementsSettingsFragment();
|
||||
} else if (key.equals(getString(R.string.settings_import_export_key))) {
|
||||
fragment = new ImportExportSettingsFragment();
|
||||
} else if (key.equals(getString(R.string.settings_open_tracks_key))) {
|
||||
fragment = new OpenTracksSettingsFragment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
return fragment;
|
||||
}
|
||||
|
||||
trackRecordingServiceConnection.bind(getContext());
|
||||
public PreferenceFragmentCompat openScreen(String key) {
|
||||
fragment = getPreferenceScreen(key);
|
||||
getSupportFragmentManager().beginTransaction().replace(R.id.settings_fragment, fragment).addToBackStack(key).commit();
|
||||
return fragment;
|
||||
}
|
||||
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(sharedPreferences, null);
|
||||
|
||||
updateUnits();
|
||||
updatePrefsDependOnRecording();
|
||||
updateBluetooth();
|
||||
updatePostWorkoutExport();
|
||||
}
|
||||
|
||||
private void updatePostWorkoutExport() {
|
||||
Preference instantExportEnabledPreference = findPreference(getString(R.string.post_workout_export_enabled_key));
|
||||
instantExportEnabledPreference.setEnabled(PreferencesUtils.isDefaultExportDirectoryUri(sharedPreferences, getContext()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayPreferenceDialog(Preference preference) {
|
||||
DialogFragment dialogFragment = null;
|
||||
if (preference instanceof ResetDialogPreference) {
|
||||
dialogFragment = ResetDialogPreference.ResetPreferenceDialog.newInstance(preference.getKey());
|
||||
} else if (preference instanceof ActivityTypePreference) {
|
||||
activityPreferenceDialog = ActivityTypePreference.ActivityPreferenceDialog.newInstance(preference.getKey());
|
||||
dialogFragment = activityPreferenceDialog;
|
||||
} else if (preference instanceof BluetoothLeHeartRatePreference) {
|
||||
dialogFragment = BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(preference.getKey(), BluetoothUtils.HEART_RATE_SUPPORTING_DEVICES);
|
||||
} else if (preference instanceof BluetoothLeCyclingCadenceAndSpeedPreference) {
|
||||
dialogFragment = BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(preference.getKey(), BluetoothUtils.CYCLING_SPEED_CADENCE_SERVICE_UUID);
|
||||
} else if (preference instanceof BluetoothLeCyclingPowerPreference) {
|
||||
dialogFragment = BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(preference.getKey(), BluetoothUtils.CYCLING_POWER_UUID);
|
||||
} else if (preference instanceof BluetoothLeRunningSpeedAndCadencePreference) {
|
||||
dialogFragment = BluetoothLeSensorPreference.BluetoothLeSensorPreferenceDialog.newInstance(preference.getKey(), BluetoothUtils.RUNNING_RUNNING_SPEED_CADENCE_UUID);
|
||||
}
|
||||
|
||||
if (dialogFragment != null) {
|
||||
dialogFragment.setTargetFragment(this, 0);
|
||||
dialogFragment.show(getParentFragmentManager(), getClass().getSimpleName());
|
||||
return;
|
||||
}
|
||||
|
||||
super.onDisplayPreferenceDialog(preference);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
trackRecordingServiceConnection.unbind(getContext());
|
||||
sharedPreferences = null;
|
||||
}
|
||||
|
||||
public void setDefaultActivity(String iconValue) {
|
||||
if (activityPreferenceDialog != null) {
|
||||
activityPreferenceDialog.updateUI(iconValue);
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePrefsDependOnRecording() {
|
||||
Preference resetPreference = findPreference(getString(R.string.settings_reset_key));
|
||||
Preference importPreference = findPreference(getString(R.string.settings_import));
|
||||
Preference exportPreference = findPreference(getString(R.string.settings_export));
|
||||
|
||||
boolean isRecording = recordingStatus.isRecording();
|
||||
resetPreference.setSummary(isRecording ? getString(R.string.settings_not_while_recording) : "");
|
||||
importPreference.setSummary(isRecording ? getString(R.string.settings_not_while_recording) : "");
|
||||
exportPreference.setSummary(isRecording ? getString(R.string.settings_not_while_recording) : "");
|
||||
|
||||
resetPreference.setEnabled(!isRecording);
|
||||
importPreference.setEnabled(!isRecording);
|
||||
exportPreference.setEnabled(!isRecording);
|
||||
}
|
||||
|
||||
private void updateBluetooth() {
|
||||
// Disable Bluetooth preference if device does not have Bluetooth
|
||||
BluetoothLeSensorPreference bluetoothPreference = findPreference(getString(R.string.settings_sensor_bluetooth_heart_rate_key));
|
||||
bluetoothPreference.setVisible(BluetoothUtils.hasBluetooth(getContext()));
|
||||
}
|
||||
|
||||
private void updateUnits() {
|
||||
boolean metricUnits = PreferencesUtils.isMetricUnits(sharedPreferences, getActivity());
|
||||
|
||||
ListPreference voiceFrequency = findPreference(getString(R.string.voice_announcement_frequency_key));
|
||||
voiceFrequency.setEntries(StringUtils.getAnnouncementFrequency(getActivity()));
|
||||
|
||||
ListPreference voiceDistance = findPreference(getString(R.string.voice_announcement_distance_key));
|
||||
voiceDistance.setEntries(StringUtils.getAnnouncementDistance(getActivity(), metricUnits));
|
||||
|
||||
ListPreference minRecordingInterval = findPreference(getString(R.string.min_recording_interval_key));
|
||||
minRecordingInterval.setEntries(PreferenceHelper.getMinRecordingIntervalEntries(getActivity()));
|
||||
|
||||
ListPreference recordingDistanceInterval = findPreference(getString(R.string.recording_distance_interval_key));
|
||||
recordingDistanceInterval.setEntries(PreferenceHelper.getRecordingDistanceIntervalEntries(getActivity(), metricUnits));
|
||||
|
||||
ListPreference maxRecordingDistance = findPreference(getString(R.string.max_recording_distance_key));
|
||||
maxRecordingDistance.setEntries(PreferenceHelper.getMaxRecordingDistanceEntries(getActivity(), metricUnits));
|
||||
|
||||
ListPreference recordingGpsAccuracy = findPreference(getString(R.string.recording_gps_accuracy_key));
|
||||
recordingGpsAccuracy.setEntries(PreferenceHelper.getRecordingGpsAccuracyEntries(getActivity(), metricUnits));
|
||||
|
||||
ListPreference statsRatePreferences = findPreference(getString(R.string.stats_rate_key));
|
||||
String[] entries = getResources().getStringArray(metricUnits ? R.array.stats_rate_metric_options : R.array.stats_rate_imperial_options);
|
||||
statsRatePreferences.setEntries(entries);
|
||||
|
||||
HackUtils.invalidatePreference(statsRatePreferences);
|
||||
}
|
||||
|
||||
|
||||
private void setExportTrackFileFormatOptions() {
|
||||
final TrackFileFormat[] trackFileFormats = {
|
||||
TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES,
|
||||
TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA,
|
||||
TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA,
|
||||
TrackFileFormat.GPX
|
||||
};
|
||||
String[] entries = new String[trackFileFormats.length];
|
||||
String[] entryValues = new String[trackFileFormats.length];
|
||||
|
||||
for (int i = 0; i < entries.length; i++) {
|
||||
TrackFileFormat trackFileFormat = trackFileFormats[i];
|
||||
String trackFileFormatUpperCase = trackFileFormat.getExtension().toUpperCase(Locale.US); //ASCII upper case
|
||||
int photoMessageId = trackFileFormat.includesPhotos() ? R.string.export_with_photos : R.string.export_without_photos;
|
||||
entries[i] = String.format("%s (%s)", trackFileFormatUpperCase, getString(photoMessageId));
|
||||
entryValues[i] = trackFileFormat.name();
|
||||
}
|
||||
|
||||
ListPreference listPreference = findPreference(getString(R.string.export_trackfileformat_key));
|
||||
listPreference.setEntries(entries);
|
||||
listPreference.setEntryValues(entryValues);
|
||||
}
|
||||
|
||||
private void setWheelCircumferenceInputFilter() {
|
||||
EditTextPreference wheelPreference = findPreference(getString(R.string.settings_sensor_bluetooth_cycling_speed_wheel_circumference_key));
|
||||
wheelPreference.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
if (newValue instanceof String) {
|
||||
try {
|
||||
int newValueInt = Integer.parseInt((String) newValue);
|
||||
return newValueInt > 500 && newValueInt < 4000;
|
||||
} catch (NumberFormatException e) {
|
||||
Log.w(TAG, "Entered string is no number.");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
private void onRecordingStatusChanged(TrackRecordingService.RecordingStatus status) {
|
||||
this.recordingStatus = status;
|
||||
if (!status.isRecording() && isAdded()) {
|
||||
updatePrefsDependOnRecording();
|
||||
}
|
||||
@Override
|
||||
public void onChooseActivityTypeDone(String iconValue) {
|
||||
try {
|
||||
((ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller) this.fragment).onChooseActivityTypeDone(iconValue);
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassCastException(this.fragment.getClass().getSimpleName() + " must implement " + ChooseActivityTypeDialogFragment.ChooseActivityTypeCaller.class.getSimpleName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package de.dennisguse.opentracks.settings;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
@@ -23,7 +22,6 @@ import de.dennisguse.opentracks.adapters.SettingsCustomLayoutAdapter;
|
||||
import de.dennisguse.opentracks.content.data.Layout;
|
||||
import de.dennisguse.opentracks.content.data.DataField;
|
||||
import de.dennisguse.opentracks.databinding.ActivitySettingsCustomLayoutBinding;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.util.StatisticsUtils;
|
||||
|
||||
public class SettingsCustomLayoutActivity extends AbstractActivity implements SettingsCustomLayoutAdapter.SettingsCustomLayoutItemClickListener {
|
||||
@@ -34,20 +32,18 @@ public class SettingsCustomLayoutActivity extends AbstractActivity implements Se
|
||||
private SettingsCustomLayoutAdapter adapterFieldsHidden;
|
||||
private Layout layoutFieldsVisible;
|
||||
private Layout layoutFieldsHidden;
|
||||
private SharedPreferences sharedPreferences;
|
||||
private int numColumns;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
||||
|
||||
// Recycler view with visible stats.
|
||||
layoutFieldsVisible = StatisticsUtils.filterVisible(PreferencesUtils.getCustomLayout(sharedPreferences, this), true);
|
||||
layoutFieldsVisible = StatisticsUtils.filterVisible(PreferencesUtils.getCustomLayout(), true);
|
||||
adapterFieldsVisible = new SettingsCustomLayoutAdapter(this, this, layoutFieldsVisible);
|
||||
|
||||
numColumns = PreferencesUtils.getLayoutColumns(sharedPreferences, this);
|
||||
numColumns = PreferencesUtils.getLayoutColumns();
|
||||
RecyclerView recyclerViewVisible = viewBinding.recyclerViewVisible;
|
||||
gridLayoutManager = new GridLayoutManager(this, numColumns);
|
||||
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
|
||||
@@ -90,7 +86,7 @@ public class SettingsCustomLayoutActivity extends AbstractActivity implements Se
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
numColumns = position + 1;
|
||||
gridLayoutManager.setSpanCount(numColumns);
|
||||
PreferencesUtils.setLayoutColumns(sharedPreferences, SettingsCustomLayoutActivity.this, position + 1);
|
||||
PreferencesUtils.setLayoutColumns(position + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -98,10 +94,10 @@ public class SettingsCustomLayoutActivity extends AbstractActivity implements Se
|
||||
|
||||
}
|
||||
});
|
||||
viewBinding.spinnerOptions.setSelection(PreferencesUtils.getLayoutColumns(sharedPreferences, SettingsCustomLayoutActivity.this) - 1);
|
||||
viewBinding.spinnerOptions.setSelection(PreferencesUtils.getLayoutColumns() - 1);
|
||||
|
||||
// Recycler view with not visible stats.
|
||||
layoutFieldsHidden = StatisticsUtils.filterVisible(PreferencesUtils.getCustomLayout(sharedPreferences, this), false);
|
||||
layoutFieldsHidden = StatisticsUtils.filterVisible(PreferencesUtils.getCustomLayout(), false);
|
||||
adapterFieldsHidden = new SettingsCustomLayoutAdapter(this, this, layoutFieldsHidden);
|
||||
RecyclerView recyclerViewNotVisible = viewBinding.recyclerViewNotVisible;
|
||||
recyclerViewNotVisible.setLayoutManager(new LinearLayoutManager(this));
|
||||
@@ -115,14 +111,13 @@ public class SettingsCustomLayoutActivity extends AbstractActivity implements Se
|
||||
Layout newLayout = new Layout(layoutFieldsVisible.getProfile());
|
||||
newLayout.addFields(layoutFieldsVisible.getFields());
|
||||
newLayout.addFields(layoutFieldsHidden.getFields());
|
||||
PreferencesUtils.setCustomLayout(sharedPreferences, this, newLayout);
|
||||
PreferencesUtils.setCustomLayout(newLayout);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
sharedPreferences = null;
|
||||
layoutFieldsVisible = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package de.dennisguse.opentracks.settings;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
public class UserInterfaceSettingsFragment extends PreferenceFragmentCompat {
|
||||
|
||||
private final SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener = (sharedPreferences, key) -> {
|
||||
if (PreferencesUtils.isKey(R.string.night_mode_key, key)) {
|
||||
getActivity().runOnUiThread(PreferencesUtils::applyNightMode);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
addPreferencesFromResource(R.xml.settings_user_interface);
|
||||
|
||||
Preference customLayoutPreference = findPreference(getString(R.string.stats_custom_layout_key));
|
||||
customLayoutPreference.setOnPreferenceClickListener((preference) -> {
|
||||
Intent intent = new Intent(getContext(), SettingsCustomLayoutActivity.class);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
((SettingsActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_ui_title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
PreferencesUtils.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
PreferencesUtils.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisplayPreferenceDialog(Preference preference) {
|
||||
DialogFragment dialogFragment = null;
|
||||
|
||||
if (preference instanceof ResetDialogPreference) {
|
||||
dialogFragment = ResetDialogPreference.ResetPreferenceDialog.newInstance(preference.getKey());
|
||||
}
|
||||
|
||||
if (dialogFragment != null) {
|
||||
dialogFragment.setTargetFragment(this, 0);
|
||||
dialogFragment.show(getParentFragmentManager(), getClass().getSimpleName());
|
||||
return;
|
||||
}
|
||||
|
||||
super.onDisplayPreferenceDialog(preference);
|
||||
}
|
||||
}
|
||||
+36
-4
@@ -1,5 +1,6 @@
|
||||
package de.dennisguse.opentracks.settings.bluetooth;
|
||||
|
||||
import android.Manifest;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.le.BluetoothLeScanner;
|
||||
import android.bluetooth.le.ScanCallback;
|
||||
@@ -8,6 +9,8 @@ import android.bluetooth.le.ScanResult;
|
||||
import android.bluetooth.le.ScanSettings;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.ParcelUuid;
|
||||
import android.text.TextUtils;
|
||||
@@ -15,7 +18,10 @@ import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.preference.DialogPreference;
|
||||
import androidx.preference.PreferenceDialogFragmentCompat;
|
||||
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat;
|
||||
@@ -29,7 +35,7 @@ import java.util.stream.Collectors;
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.settings.BluetoothLeAdapter;
|
||||
import de.dennisguse.opentracks.util.BluetoothUtils;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
/**
|
||||
* Preference to select a discoverable Bluetooth LE device.
|
||||
@@ -85,7 +91,7 @@ public abstract class BluetoothLeSensorPreference extends DialogPreference {
|
||||
|
||||
@Override
|
||||
public CharSequence getSummary() {
|
||||
if (getValue() == null || PreferencesUtils.isBluetoothSensorAddressNone(getContext(), getValue())) {
|
||||
if (getValue() == null || PreferencesUtils.isBluetoothSensorAddressNone(getValue())) {
|
||||
return getContext().getString(DEVICE_NONE_RESOURCEID);
|
||||
}
|
||||
|
||||
@@ -143,13 +149,39 @@ public abstract class BluetoothLeSensorPreference extends DialogPreference {
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
List<ParcelUuid> serviceUUIDs = getArguments().getParcelableArrayList(ARG_BLE_SERVICE_UUIDS);
|
||||
|
||||
// Don't know why: need to load the drawable _twice_, so that animation is actually started.
|
||||
bluetoothIcon = AnimatedVectorDrawableCompat.create(getContext(), R.drawable.ic_bluetooth_searching_animated_24dp);
|
||||
bluetoothIcon = AnimatedVectorDrawableCompat.create(getContext(), R.drawable.ic_bluetooth_searching_animated_24dp);
|
||||
bluetoothIcon.start();
|
||||
|
||||
if (
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && (
|
||||
ContextCompat.checkSelfPermission(getContext(), Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED ||
|
||||
ContextCompat.checkSelfPermission(getContext(), Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED
|
||||
)
|
||||
) {
|
||||
ActivityResultLauncher<String[]> requestPermissionLauncher =
|
||||
registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), granted -> {
|
||||
if (!granted.containsValue(false)) {
|
||||
startBluetoothScan();
|
||||
} else if (shouldShowRequestPermissionRationale(Manifest.permission.BLUETOOTH_SCAN) || shouldShowRequestPermissionRationale(Manifest.permission.BLUETOOTH_CONNECT)) {
|
||||
Toast.makeText(getContext(), R.string.permission_bluetooth_failed_rejected, Toast.LENGTH_LONG).show();
|
||||
dismiss();
|
||||
} else {
|
||||
Toast.makeText(getContext(), R.string.permission_bluetooth_failed, Toast.LENGTH_SHORT).show();
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
requestPermissionLauncher.launch(new String[]{Manifest.permission.BLUETOOTH_CONNECT, Manifest.permission.BLUETOOTH_SCAN});
|
||||
} else {
|
||||
startBluetoothScan();
|
||||
}
|
||||
}
|
||||
|
||||
private void startBluetoothScan() {
|
||||
List<ParcelUuid> serviceUUIDs = getArguments().getParcelableArrayList(ARG_BLE_SERVICE_UUIDS);
|
||||
|
||||
BluetoothAdapter bluetoothAdapter = BluetoothUtils.getAdapter(getContext());
|
||||
if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
|
||||
Log.w(TAG, "Bluetooth adapter is present or not enabled.");
|
||||
|
||||
@@ -178,7 +178,9 @@ public class TrackStatisticsUpdater {
|
||||
}
|
||||
|
||||
// Update moving time
|
||||
currentSegment.addMovingTime(movingTime);
|
||||
if (lastTrackPoint.isMoving()) {
|
||||
currentSegment.addMovingTime(movingTime);
|
||||
}
|
||||
|
||||
// Update max speed
|
||||
if (trackPoint.hasSpeed() && lastTrackPoint.hasSpeed()) {
|
||||
|
||||
@@ -107,8 +107,4 @@ public class ActivityUtils {
|
||||
vibrator.vibrate(milliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
public static void applyNightMode(SharedPreferences sharedPreferences, Context context) {
|
||||
AppCompatDelegate.setDefaultNightMode(PreferencesUtils.getDefaultNightMode(sharedPreferences, context));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package de.dennisguse.opentracks.util;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.DocumentsContract;
|
||||
@@ -23,16 +22,16 @@ import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.io.file.exporter.ExportService;
|
||||
import de.dennisguse.opentracks.io.file.exporter.ExportServiceResultReceiver;
|
||||
import de.dennisguse.opentracks.io.file.exporter.TrackExporter;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
public class ExportUtils {
|
||||
|
||||
private static final String TAG = ExportUtils.class.getSimpleName();
|
||||
|
||||
public static void postWorkoutExport(Context context, Track.Id trackId, ExportServiceResultReceiver resultReceiver) {
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
if (PreferencesUtils.shouldInstantExportAfterWorkout(sharedPreferences, context)) {
|
||||
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat(sharedPreferences, context);
|
||||
DocumentFile directory = PreferencesUtils.getDefaultExportDirectoryUri(sharedPreferences, context);
|
||||
if (PreferencesUtils.shouldInstantExportAfterWorkout(context)) {
|
||||
TrackFileFormat trackFileFormat = PreferencesUtils.getExportTrackFileFormat();
|
||||
DocumentFile directory = PreferencesUtils.getDefaultExportDirectoryUri(context);
|
||||
|
||||
ExportService.enqueue(context, resultReceiver, trackId, trackFileFormat, directory.getUri());
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package de.dennisguse.opentracks.util;
|
||||
import android.content.ClipData;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -17,6 +16,7 @@ import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
|
||||
import de.dennisguse.opentracks.content.data.TracksColumns;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
/**
|
||||
* Create an {@link Intent} to request showing a Dashboard.
|
||||
@@ -74,13 +74,11 @@ public class IntentDashboardUtils {
|
||||
|
||||
intent.putParcelableArrayListExtra(ACTION_DASHBOARD_PAYLOAD, uris);
|
||||
|
||||
SharedPreferences sharedPreferences = PreferencesUtils.getSharedPreferences(context);
|
||||
|
||||
intent.putExtra(EXTRAS_SHOULD_KEEP_SCREEN_ON, PreferencesUtils.shouldKeepScreenOn(sharedPreferences, context));
|
||||
intent.putExtra(EXTRAS_SHOW_WHEN_LOCKED, PreferencesUtils.shouldShowStatsOnLockscreen(sharedPreferences, context));
|
||||
intent.putExtra(EXTRAS_SHOULD_KEEP_SCREEN_ON, PreferencesUtils.shouldKeepScreenOn());
|
||||
intent.putExtra(EXTRAS_SHOW_WHEN_LOCKED, PreferencesUtils.shouldShowStatsOnLockscreen());
|
||||
intent.putExtra(EXTRAS_OPENTRACKS_IS_RECORDING_THIS_TRACK, isRecording);
|
||||
if (isRecording) {
|
||||
intent.putExtra(EXTRAS_SHOW_FULLSCREEN, PreferencesUtils.shouldUseFullscreen(sharedPreferences, context));
|
||||
intent.putExtra(EXTRAS_SHOW_FULLSCREEN, PreferencesUtils.shouldUseFullscreen());
|
||||
}
|
||||
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
|
||||
@@ -39,6 +39,7 @@ import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.provider.ShareContentProvider;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
/**
|
||||
* Utilities for creating intents.
|
||||
@@ -91,7 +92,7 @@ public class IntentUtils {
|
||||
continue;
|
||||
}
|
||||
|
||||
Pair<Uri, String> uriTrackFile = ShareContentProvider.createURI(trackId, track.getName(), PreferencesUtils.getExportTrackFileFormat(PreferencesUtils.getSharedPreferences(context), context));
|
||||
Pair<Uri, String> uriTrackFile = ShareContentProvider.createURI(trackId, track.getName(), PreferencesUtils.getExportTrackFileFormat());
|
||||
Pair<Uri, String> uriSharePicture = ShareContentProvider.createURI(trackId, track.getName(), TrackFileFormat.SHARE_PICTURE_PNG);
|
||||
|
||||
uris.addAll(Arrays.asList(uriSharePicture.first, uriTrackFile.first));
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
package de.dennisguse.opentracks.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
|
||||
public class PreferencesOpenHelper {
|
||||
private final Context context;
|
||||
private final SharedPreferences sharedPreferences;
|
||||
|
||||
private static final int PREFERENCES_VERSION = 1;
|
||||
|
||||
private PreferencesOpenHelper(Context context, SharedPreferences sharedPreferences) {
|
||||
this.context = context;
|
||||
this.sharedPreferences = sharedPreferences;
|
||||
}
|
||||
|
||||
public static PreferencesOpenHelper newInstance(Context context, SharedPreferences sharedPreferences) {
|
||||
return new PreferencesOpenHelper(context, sharedPreferences);
|
||||
}
|
||||
|
||||
public void checkForUpgrade() {
|
||||
int lastVersion = PreferencesUtils.getInt(sharedPreferences, context, R.string.prefs_last_version_key, 0);
|
||||
if (PREFERENCES_VERSION > lastVersion) {
|
||||
onUpgrade();
|
||||
}
|
||||
}
|
||||
|
||||
private void onUpgrade() {
|
||||
PreferencesUtils.setInt(sharedPreferences, context, R.string.prefs_last_version_key, PREFERENCES_VERSION);
|
||||
switch (PREFERENCES_VERSION) {
|
||||
case 1:
|
||||
upgradeFrom0to1();
|
||||
}
|
||||
}
|
||||
|
||||
private void upgradeFrom0to1() {
|
||||
PreferencesUtils.setString(sharedPreferences, context, R.string.stats_custom_layout_fields_key, PreferencesUtils.buildDefaultLayout(context));
|
||||
}
|
||||
}
|
||||
@@ -1,421 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package de.dennisguse.opentracks.util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.content.res.TypedArray;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.documentfile.provider.DocumentFile;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.data.DataField;
|
||||
import de.dennisguse.opentracks.content.data.Distance;
|
||||
import de.dennisguse.opentracks.content.data.Layout;
|
||||
import de.dennisguse.opentracks.io.file.TrackFileFormat;
|
||||
|
||||
/**
|
||||
* Utilities to access preferences stored in {@link SharedPreferences}.
|
||||
*
|
||||
* @author Jimmy Shih
|
||||
*/
|
||||
public class PreferencesUtils {
|
||||
|
||||
private final static String TAG = PreferencesUtils.class.getSimpleName();
|
||||
|
||||
private PreferencesUtils() {
|
||||
}
|
||||
|
||||
@Deprecated //Should only be used to get a sharedPreference for more than one interaction!
|
||||
public static SharedPreferences getSharedPreferences(Context context) {
|
||||
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
PreferencesOpenHelper.newInstance(context, sharedPreferences).checkForUpgrade();
|
||||
return sharedPreferences;
|
||||
}
|
||||
|
||||
public static String getDefaultActivity(SharedPreferences sharedPreferences, Context context) {
|
||||
return getString(sharedPreferences, context, R.string.default_activity_key, context.getString(R.string.default_activity_default));
|
||||
}
|
||||
|
||||
public static void setDefaultActivity(SharedPreferences sharedPreferences, Context context, String newDefaultActivity) {
|
||||
setString(sharedPreferences, context, R.string.default_activity_key, newDefaultActivity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a preference key
|
||||
*
|
||||
* @param context the context
|
||||
* @param keyId the key id
|
||||
*/
|
||||
private static String getKey(Context context, int keyId) {
|
||||
return context.getString(keyId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares if keyId and key belong to the same shared preference key.
|
||||
*
|
||||
* @param keyId The resource id of the key
|
||||
* @param key The key of the preference
|
||||
* @return true if key == null or key belongs to keyId
|
||||
*/
|
||||
public static boolean isKey(Context context, int keyId, String key) {
|
||||
return key == null || key.equals(getKey(context, keyId));
|
||||
}
|
||||
|
||||
public static boolean isKey(Context context, int[] keyIds, String key) {
|
||||
for(int keyId : keyIds) {
|
||||
if (isKey(context, keyId, key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean getBoolean(SharedPreferences sharedPreferences, Context context, int keyId, boolean defaultValue) {
|
||||
return sharedPreferences.getBoolean(getKey(context, keyId), defaultValue);
|
||||
}
|
||||
|
||||
static int getInt(SharedPreferences sharedPreferences, Context context, int keyId, int defaultValue) {
|
||||
try {
|
||||
return sharedPreferences.getInt(getKey(context, keyId), defaultValue);
|
||||
} catch (ClassCastException e) {
|
||||
//Ignore
|
||||
}
|
||||
|
||||
//NOTE: We assume that the data was stored as String due to use of ListPreference.
|
||||
try {
|
||||
String stringValue = sharedPreferences.getString(getKey(context, keyId), null);
|
||||
return Integer.parseInt(stringValue);
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
private static float getFloat(SharedPreferences sharedPreferences, Context context, int keyId, float defaultValue) {
|
||||
try {
|
||||
return sharedPreferences.getFloat(getKey(context, keyId), defaultValue);
|
||||
} catch (ClassCastException e) {
|
||||
//Ignore
|
||||
}
|
||||
|
||||
//NOTE: We assume that the data was stored as String due to use of ListPreference.
|
||||
try {
|
||||
String stringValue = sharedPreferences.getString(getKey(context, keyId), null);
|
||||
return Float.parseFloat(stringValue);
|
||||
} catch (NumberFormatException e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getString(SharedPreferences sharedPreferences, Context context, int keyId, String defaultValue) {
|
||||
return sharedPreferences.getString(getKey(context, keyId), defaultValue);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static void setString(SharedPreferences sharedPreferences, Context context, int keyId, String value) {
|
||||
Editor editor = sharedPreferences.edit();
|
||||
editor.putString(getKey(context, keyId), value);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
static void setInt(SharedPreferences sharedPreferences, Context context, int keyId, int value) {
|
||||
Editor editor = sharedPreferences.edit();
|
||||
editor.putInt(getKey(context, keyId), value);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
public static boolean isMetricUnits(SharedPreferences sharedPreferences, Context context) {
|
||||
final String STATS_UNIT = context.getString(R.string.stats_units_default);
|
||||
return STATS_UNIT.equals(getString(sharedPreferences, context, R.string.stats_units_key, STATS_UNIT));
|
||||
}
|
||||
|
||||
public static void setMetricUnits(SharedPreferences sharedPreferences, Context context, boolean metricUnits) {
|
||||
String unit;
|
||||
if (metricUnits) {
|
||||
unit = context.getString(R.string.stats_units_metric);
|
||||
} else {
|
||||
unit = context.getString(R.string.stats_units_imperial);
|
||||
}
|
||||
setString(sharedPreferences, context, R.string.stats_units_key, unit);
|
||||
}
|
||||
|
||||
public static boolean isReportSpeed(SharedPreferences sharedPreferences, Context context, String category) {
|
||||
final String STATS_RATE_DEFAULT = context.getString(R.string.stats_rate_default);
|
||||
String currentStatsRate = getString(sharedPreferences, context, R.string.stats_rate_key, STATS_RATE_DEFAULT);
|
||||
if (currentStatsRate.equals(getString(sharedPreferences, context, R.string.stats_rate_speed_or_pace_default, STATS_RATE_DEFAULT))) {
|
||||
return TrackIconUtils.isSpeedIcon(context, category);
|
||||
}
|
||||
|
||||
return currentStatsRate.equals(context.getString(R.string.stats_rate_speed));
|
||||
}
|
||||
|
||||
private static String getBluetoothSensorAddressNone(Context context) {
|
||||
return context.getString(R.string.sensor_type_value_none);
|
||||
}
|
||||
|
||||
public static boolean isBluetoothSensorAddressNone(Context context, String currentValue) {
|
||||
return getBluetoothSensorAddressNone(context).equals(currentValue);
|
||||
}
|
||||
|
||||
public static String getBluetoothHeartRateSensorAddress(SharedPreferences sharedPreferences, Context context) {
|
||||
return getString(sharedPreferences, context, R.string.settings_sensor_bluetooth_heart_rate_key, getBluetoothSensorAddressNone(context));
|
||||
}
|
||||
|
||||
public static String getBluetoothCyclingCadenceSensorAddress(SharedPreferences sharedPreferences, Context context) {
|
||||
return getString(sharedPreferences, context, R.string.settings_sensor_bluetooth_cycling_cadence_key, getBluetoothSensorAddressNone(context));
|
||||
}
|
||||
|
||||
public static String getBluetoothCyclingSpeedSensorAddress(SharedPreferences sharedPreferences, Context context) {
|
||||
return getString(sharedPreferences, context, R.string.settings_sensor_bluetooth_cycling_speed_key, getBluetoothSensorAddressNone(context));
|
||||
}
|
||||
|
||||
public static Distance getWheelCircumference(SharedPreferences sharedPreferences, Context context) {
|
||||
final int DEFAULT = Integer.parseInt(context.getResources().getString(R.string.settings_sensor_bluetooth_cycling_speed_wheel_circumference_default));
|
||||
return Distance.ofMM(getInt(sharedPreferences, context, R.string.settings_sensor_bluetooth_cycling_speed_wheel_circumference_key, DEFAULT));
|
||||
}
|
||||
|
||||
public static String getBluetoothCyclingPowerSensorAddress(SharedPreferences sharedPreferences, Context context) {
|
||||
return getString(sharedPreferences, context, R.string.settings_sensor_bluetooth_cycling_power_key, getBluetoothSensorAddressNone(context));
|
||||
}
|
||||
|
||||
public static String getBluetoothRunningSpeedAndCadenceAddress(SharedPreferences sharedPreferences, Context context) {
|
||||
return getString(sharedPreferences, context, R.string.settings_sensor_bluetooth_running_speed_and_cadence_key, getBluetoothSensorAddressNone(context));
|
||||
}
|
||||
|
||||
public static boolean shouldShowStatsOnLockscreen(SharedPreferences sharedPreferences, Context context) {
|
||||
final boolean STATS_SHOW_ON_LOCKSCREEN_DEFAULT = context.getResources().getBoolean(R.bool.stats_show_on_lockscreen_while_recording_default);
|
||||
return getBoolean(sharedPreferences, context, R.string.stats_show_on_lockscreen_while_recording_key, STATS_SHOW_ON_LOCKSCREEN_DEFAULT);
|
||||
}
|
||||
|
||||
public static boolean shouldKeepScreenOn(SharedPreferences sharedPreferences, Context context) {
|
||||
final boolean DEFAULT = context.getResources().getBoolean(R.bool.stats_keep_screen_on_while_recording_default);
|
||||
return getBoolean(sharedPreferences, context, R.string.stats_keep_screen_on_while_recording_key, DEFAULT);
|
||||
}
|
||||
|
||||
public static boolean shouldUseFullscreen(SharedPreferences sharedPreferences, Context context) {
|
||||
final boolean DEFAULT = context.getResources().getBoolean(R.bool.stats_fullscreen_while_recording_default);
|
||||
return getBoolean(sharedPreferences, context, R.string.stats_fullscreen_while_recording_key, DEFAULT);
|
||||
}
|
||||
|
||||
public static Duration getVoiceAnnouncementFrequency(SharedPreferences sharedPreferences, Context context) {
|
||||
final int DEFAULT = Integer.parseInt(context.getResources().getString(R.string.voice_announcement_frequency_default));
|
||||
int value = getInt(sharedPreferences, context, R.string.voice_announcement_frequency_key, DEFAULT);
|
||||
return Duration.ofSeconds(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Result depends on isMetricUnits
|
||||
*/
|
||||
public static Distance getVoiceAnnouncementDistance(SharedPreferences sharedPreferences, Context context) {
|
||||
final float DEFAULT = Integer.parseInt(context.getResources().getString(R.string.voice_announcement_distance_default));
|
||||
float value = getFloat(sharedPreferences, context, R.string.voice_announcement_distance_key, DEFAULT);
|
||||
return Distance.one(isMetricUnits(sharedPreferences, context)).multipliedBy(value);
|
||||
}
|
||||
|
||||
public static float getVoiceSpeedRate(SharedPreferences sharedPreferences, Context context) {
|
||||
final float DEFAULT = Float.parseFloat(context.getResources().getString(R.string.voice_speed_rate_default));
|
||||
return getFloat(sharedPreferences, context, R.string.voice_speed_rate_key, DEFAULT);
|
||||
}
|
||||
|
||||
public static Distance getRecordingDistanceInterval(SharedPreferences sharedPreferences, Context context) {
|
||||
return Distance.of(getInt(sharedPreferences, context, R.string.recording_distance_interval_key, getRecordingDistanceIntervalDefaultInternal(context)));
|
||||
}
|
||||
|
||||
public static Distance getRecordingDistanceIntervalDefault(Context context) {
|
||||
return Distance.of(getRecordingDistanceIntervalDefaultInternal(context));
|
||||
}
|
||||
|
||||
private static int getRecordingDistanceIntervalDefaultInternal(Context context) {
|
||||
return Integer.parseInt(context.getResources().getString(R.string.recording_distance_interval_default));
|
||||
}
|
||||
|
||||
public static Distance getMaxRecordingDistance(SharedPreferences sharedPreferences, Context context) {
|
||||
final int MAX_RECORDING_DISTANCE = Integer.parseInt(context.getResources().getString(R.string.max_recording_distance_default));
|
||||
return Distance.of(getInt(sharedPreferences, context, R.string.max_recording_distance_key, MAX_RECORDING_DISTANCE));
|
||||
}
|
||||
|
||||
public static Duration getMinRecordingInterval(SharedPreferences sharedPreferences, Context context) {
|
||||
final Duration MIN_RECORDING_INTERVAL = getMinRecordingIntervalDefault(context);
|
||||
Duration interval = Duration.ofSeconds(getInt(sharedPreferences, context, R.string.min_recording_interval_key, (int) MIN_RECORDING_INTERVAL.getSeconds()));
|
||||
|
||||
if (interval.isNegative()) {
|
||||
// Due to removal of adaptive listener policy; used -1, and -2
|
||||
interval = getMinRecordingIntervalDefault(context);
|
||||
}
|
||||
return interval;
|
||||
}
|
||||
|
||||
public static Duration getMinRecordingIntervalDefault(Context context) {
|
||||
return Duration.ofSeconds(Integer.parseInt(context.getResources().getString(R.string.min_recording_interval_default)));
|
||||
}
|
||||
|
||||
public static Distance getThresholdHorizontalAccuracy(SharedPreferences sharedPreferences, Context context) {
|
||||
final int RECORDING_GPS_ACCURACY = Integer.parseInt(context.getResources().getString(R.string.recording_gps_accuracy_default));
|
||||
return Distance.of(getInt(sharedPreferences, context, R.string.recording_gps_accuracy_key, RECORDING_GPS_ACCURACY));
|
||||
}
|
||||
|
||||
public static boolean shouldInstantExportAfterWorkout(SharedPreferences sharedPreferences, Context context) {
|
||||
final boolean INSTANT_POST_WORKOUT_EXPORT_DEFAULT = context.getResources().getBoolean(R.bool.post_workout_export_enabled_default);
|
||||
return getBoolean(sharedPreferences, context, R.string.post_workout_export_enabled_key, INSTANT_POST_WORKOUT_EXPORT_DEFAULT) && isDefaultExportDirectoryUri(sharedPreferences, context);
|
||||
}
|
||||
|
||||
public static TrackFileFormat getExportTrackFileFormat(SharedPreferences sharedPreferences, Context context) {
|
||||
final String TRACKFILEFORMAT_NAME_DEFAULT = getString(sharedPreferences, context, R.string.export_trackfileformat_default, null);
|
||||
String trackFileFormatName = getString(sharedPreferences, context, R.string.export_trackfileformat_key, TRACKFILEFORMAT_NAME_DEFAULT);
|
||||
try {
|
||||
return TrackFileFormat.valueOf(trackFileFormatName);
|
||||
} catch (Exception e) {
|
||||
return TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean getPreventReimportTracks(SharedPreferences sharedPreferences, Context context) {
|
||||
final boolean defaultValue = getBoolean(sharedPreferences, context, R.bool.import_prevent_reimport_default, false);
|
||||
return getBoolean(sharedPreferences, context, R.string.import_prevent_reimport_key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link androidx.appcompat.app.AppCompatDelegate}.MODE_*
|
||||
*/
|
||||
public static int getDefaultNightMode(SharedPreferences sharedPreferences, Context context) {
|
||||
final String defaultValue = getKey(context, R.string.night_mode_default);
|
||||
final String value = getString(sharedPreferences, context, R.string.night_mode_key, defaultValue);
|
||||
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
|
||||
public static SharedPreferences resetPreferences(Context context, boolean readAgain) {
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(context);
|
||||
if (readAgain) {
|
||||
// We want to really clear settings now.
|
||||
sharedPreferences.edit().clear().commit();
|
||||
}
|
||||
PreferenceManager.setDefaultValues(context, R.xml.settings, readAgain);
|
||||
|
||||
return sharedPreferences;
|
||||
}
|
||||
|
||||
public static DocumentFile getDefaultExportDirectoryUri(SharedPreferences sharedPreferences, Context context) {
|
||||
String singleExportDirectorySettingsKey = getString(sharedPreferences, context, R.string.settings_default_export_directory_key, null);
|
||||
if (singleExportDirectorySettingsKey == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return DocumentFile.fromTreeUri(context, Uri.parse(singleExportDirectorySettingsKey));
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Could not decode default export directory: " + e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void setDefaultExportDirectoryUri(SharedPreferences sharedPreferences, Context context, Uri directoryUri) {
|
||||
String value = directoryUri != null ? directoryUri.toString() : null;
|
||||
setString(sharedPreferences, context, R.string.settings_default_export_directory_key, value);
|
||||
}
|
||||
|
||||
public static boolean isDefaultExportDirectoryUri(SharedPreferences sharedPreferences, Context context) {
|
||||
return getDefaultExportDirectoryUri(sharedPreferences, context) != null;
|
||||
}
|
||||
|
||||
public static int getLayoutColumns(SharedPreferences sharedPreferences, Context context) {
|
||||
return getInt(sharedPreferences, context, R.string.stats_custom_layout_columns_key, context.getResources().getInteger(R.integer.stats_custom_layout_columns_default));
|
||||
}
|
||||
|
||||
public static void setLayoutColumns(SharedPreferences sharedPreferences, Context context, int columns) {
|
||||
setInt(sharedPreferences, context, R.string.stats_custom_layout_columns_key, columns);
|
||||
}
|
||||
|
||||
private static List<TypedArray> getMultiTypedArray(Context context, String key) {
|
||||
List<TypedArray> typedArrays = new ArrayList<>();
|
||||
|
||||
try {
|
||||
Class<R.array> resource = R.array.class;
|
||||
Field field;
|
||||
int i = 0;
|
||||
|
||||
do {
|
||||
field = resource.getField(key + context.getString(R.string.stats_custom_layout_fields_default_value_separator) + i);
|
||||
typedArrays.add(context.getResources().obtainTypedArray(field.getInt(null)));
|
||||
i++;
|
||||
} while (field != null);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.e(TAG, e.getMessage());
|
||||
}
|
||||
|
||||
return typedArrays;
|
||||
}
|
||||
|
||||
@SuppressLint("ResourceType")
|
||||
static String buildDefaultLayout(Context context) {
|
||||
List<TypedArray> fieldsArrays = getMultiTypedArray(context, "stats_custom_layout_fields_default_value");
|
||||
return context.getString(R.string.default_activity_default) + CsvConstants.LINE_SEPARATOR
|
||||
+ fieldsArrays.stream().map(i -> i.getString(0) + CsvConstants.ITEM_SEPARATOR + i.getString(1)).collect(Collectors.joining(CsvConstants.LINE_SEPARATOR))
|
||||
+ CsvConstants.LINE_SEPARATOR;
|
||||
}
|
||||
|
||||
public static Layout getCustomLayout(SharedPreferences sharedPreferences, Context context) {
|
||||
String csvCustomLayout = getString(sharedPreferences, context, R.string.stats_custom_layout_fields_key, buildDefaultLayout(context));
|
||||
List<String> csvParts = Arrays.asList(csvCustomLayout.split(CsvConstants.LINE_SEPARATOR));
|
||||
Layout layout = new Layout(csvParts.get(0));
|
||||
for (int i = 1; i < csvParts.size(); i++) {
|
||||
String[] fieldParts = csvParts.get(i).split(CsvConstants.ITEM_SEPARATOR);
|
||||
layout.addField(fieldParts[0], DataField.getTitleByKey(context, fieldParts[0]), fieldParts[1].equals(DataField.YES_VALUE), fieldParts[2].equals(DataField.YES_VALUE), fieldParts[0].equals(context.getString(R.string.stats_custom_layout_coordinates_key)));
|
||||
}
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
public static void setCustomLayout(SharedPreferences sharedPreferences, Context context, Layout layout) {
|
||||
List<DataField> fields = layout.getFields();
|
||||
if (fields.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String csv = layout.getProfile() + CsvConstants.LINE_SEPARATOR
|
||||
+ fields.stream().map(DataField::toCsv).collect(Collectors.joining(CsvConstants.LINE_SEPARATOR)) + CsvConstants.LINE_SEPARATOR;
|
||||
setString(sharedPreferences, context, R.string.stats_custom_layout_fields_key, csv);
|
||||
}
|
||||
|
||||
public static void resetCustomLayoutPreferences(Context context) {
|
||||
SharedPreferences settings = getSharedPreferences(context);
|
||||
if (settings.contains(context.getString(R.string.stats_custom_layout_fields_key))) {
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
editor.remove(context.getString(R.string.stats_custom_layout_fields_key));
|
||||
editor.commit();
|
||||
}
|
||||
if (settings.contains(context.getString(R.string.stats_custom_layout_columns_key))) {
|
||||
SharedPreferences.Editor editor = settings.edit();
|
||||
editor.remove(context.getString(R.string.stats_custom_layout_columns_key));
|
||||
editor.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,7 +77,8 @@ public class PressureSensorUtils {
|
||||
* https://de.wikipedia.org/wiki/Barometrische_H%C3%B6henformel#Internationale_H%C3%B6henformel
|
||||
* {\color{White} p(h)} = p_0 \cdot \left( 1 - \frac{0{,}0065 \frac{\mathrm K}{\mathrm m} \cdot h}{T_0\ } \right)^{5{,}255}
|
||||
*/
|
||||
private static float getBarometricPressure(float altitude_m) {
|
||||
@VisibleForTesting
|
||||
public static float getBarometricPressure(float altitude_m) {
|
||||
return (float) (p0 * Math.pow(1.0 - 0.0065 * altitude_m / 288.15, 5.255f));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,15 +8,16 @@ import java.util.stream.Collectors;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.data.Layout;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
public class StatisticsUtils {
|
||||
|
||||
public static String emptyValue(@NonNull Context context, @NonNull String statTitle) {
|
||||
if (PreferencesUtils.isKey(context, R.string.stats_custom_layout_total_time_key, statTitle) || PreferencesUtils.isKey(context, R.string.stats_custom_layout_moving_time_key, statTitle) || PreferencesUtils.isKey(context, R.string.stats_custom_layout_pace_key, statTitle) || PreferencesUtils.isKey(context, R.string.stats_custom_layout_average_moving_pace_key, statTitle) || PreferencesUtils.isKey(context, R.string.stats_custom_layout_average_pace_key, statTitle) || PreferencesUtils.isKey(context, R.string.stats_custom_layout_fastest_pace_key, statTitle)) {
|
||||
if (PreferencesUtils.isKey(R.string.stats_custom_layout_total_time_key, statTitle) || PreferencesUtils.isKey(R.string.stats_custom_layout_moving_time_key, statTitle) || PreferencesUtils.isKey(R.string.stats_custom_layout_pace_key, statTitle) || PreferencesUtils.isKey(R.string.stats_custom_layout_average_moving_pace_key, statTitle) || PreferencesUtils.isKey(R.string.stats_custom_layout_average_pace_key, statTitle) || PreferencesUtils.isKey(R.string.stats_custom_layout_fastest_pace_key, statTitle)) {
|
||||
return context.getString(R.string.stats_empty_value_time);
|
||||
} else if (PreferencesUtils.isKey(context, R.string.stats_custom_layout_distance_key, statTitle) || PreferencesUtils.isKey(context, R.string.stats_custom_layout_speed_key, statTitle) || PreferencesUtils.isKey(context, R.string.stats_custom_layout_average_speed_key, statTitle) || PreferencesUtils.isKey(context, R.string.stats_custom_layout_max_speed_key, statTitle) || PreferencesUtils.isKey(context, R.string.stats_custom_layout_average_moving_speed_key, statTitle)) {
|
||||
} else if (PreferencesUtils.isKey(R.string.stats_custom_layout_distance_key, statTitle) || PreferencesUtils.isKey(R.string.stats_custom_layout_speed_key, statTitle) || PreferencesUtils.isKey(R.string.stats_custom_layout_average_speed_key, statTitle) || PreferencesUtils.isKey(R.string.stats_custom_layout_max_speed_key, statTitle) || PreferencesUtils.isKey(R.string.stats_custom_layout_average_moving_speed_key, statTitle)) {
|
||||
return context.getString(R.string.stats_empty_value_float);
|
||||
} else if (PreferencesUtils.isKey(context, R.string.stats_custom_layout_coordinates_key, statTitle)) {
|
||||
} else if (PreferencesUtils.isKey(R.string.stats_custom_layout_coordinates_key, statTitle)) {
|
||||
return context.getString(R.string.stats_empty_value_coordinates);
|
||||
} else {
|
||||
return context.getString(R.string.stats_empty_value_integer);
|
||||
|
||||
@@ -204,7 +204,7 @@ public class StringUtils {
|
||||
}
|
||||
|
||||
public static Pair<String, String> getHeartRateParts(Context context, Float heartrate_bpm) {
|
||||
String value = context.getString(R.string.value_none);
|
||||
String value = context.getString(R.string.value_unknown);
|
||||
if (heartrate_bpm != null) {
|
||||
value = StringUtils.formatDecimal(heartrate_bpm, 0);
|
||||
}
|
||||
@@ -213,7 +213,7 @@ public class StringUtils {
|
||||
}
|
||||
|
||||
public static Pair<String, String> getCadenceParts(Context context, Float cadence_rpm) {
|
||||
String value = context.getString(R.string.value_none);
|
||||
String value = context.getString(R.string.value_unknown);
|
||||
if (cadence_rpm != null) {
|
||||
value = StringUtils.formatDecimal(cadence_rpm, 0);
|
||||
}
|
||||
@@ -222,7 +222,7 @@ public class StringUtils {
|
||||
}
|
||||
|
||||
public static Pair<String, String> getPowerParts(Context context, Float power_w) {
|
||||
String value = context.getString(R.string.value_none);
|
||||
String value = context.getString(R.string.value_unknown);
|
||||
if (power_w != null) {
|
||||
value = StringUtils.formatDecimal(power_w, 0);
|
||||
}
|
||||
@@ -292,34 +292,6 @@ public class StringUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static String[] getAnnouncementFrequency(Context context) {
|
||||
String[] values = context.getResources().getStringArray(R.array.voice_announcement_frequency_values);
|
||||
String[] options = new String[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (context.getString(R.string.announcement_off).equals(values[i])) {
|
||||
options[i] = context.getString(R.string.value_off);
|
||||
} else {
|
||||
int value = Integer.parseInt(values[i]);
|
||||
options[i] = context.getString(R.string.value_integer_minute, value);
|
||||
}
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
public static String[] getAnnouncementDistance(Context context, boolean metricUnits) {
|
||||
String[] values = context.getResources().getStringArray(R.array.voice_announcement_distance_values);
|
||||
String[] options = new String[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (context.getString(R.string.announcement_off).equals(values[i])) {
|
||||
options[i] = context.getString(R.string.value_off);
|
||||
} else {
|
||||
int value = Integer.parseInt(values[i]);
|
||||
options[i] = context.getString(metricUnits ? R.string.value_integer_kilometer : R.string.value_integer_mile, value);
|
||||
}
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the formatted altitude_m (or null) and it's unit as {@link Pair}
|
||||
*/
|
||||
@@ -336,9 +308,24 @@ public class StringUtils {
|
||||
}
|
||||
|
||||
public static String formatAltitude(Context context, Float altitude_m, boolean metricUnits) {
|
||||
Pair<String, String> distanceParts = getAltitudeParts(context, altitude_m, metricUnits);
|
||||
Pair<String, String> altitudeParts = getAltitudeParts(context, altitude_m, metricUnits);
|
||||
|
||||
return context.getString(R.string.altitude_with_unit, distanceParts.first, distanceParts.second);
|
||||
return context.getString(R.string.altitude_with_unit, altitudeParts.first, altitudeParts.second);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the formatted altitudeDifference_m (or null) and it's unit as {@link Pair}
|
||||
*/
|
||||
public static Pair<String, String> getAltitudeChangeParts(Context context, Float altitudeDifference_m, boolean metricUnits) {
|
||||
String formattedValue = altitudeDifference_m != null ? String.valueOf((int) Distance.of(altitudeDifference_m).toM_FT(metricUnits)) : context.getString(R.string.value_unknown);
|
||||
String unit = context.getString(metricUnits ? R.string.unit_meter : R.string.unit_feet);
|
||||
return new Pair<>(formattedValue, unit);
|
||||
}
|
||||
|
||||
public static String formatAltitudeChange(Context context, Float altitude_m, boolean metricUnits) {
|
||||
Pair<String, String> altitudeParts = getAltitudeChangeParts(context, altitude_m, metricUnits);
|
||||
|
||||
return context.getString(R.string.altitude_with_unit, altitudeParts.first, altitudeParts.second);
|
||||
}
|
||||
|
||||
public static String valueInParentheses(String text) {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package de.dennisguse.opentracks.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -155,55 +156,56 @@ public class TrackIconUtils {
|
||||
*/
|
||||
@NonNull
|
||||
public static String getIconValue(Context context, String activityType) {
|
||||
Resources resources = context.getResources();
|
||||
if (activityType == null || activityType.equals("")) {
|
||||
return UNKNOWN;
|
||||
}
|
||||
if (inList(context, activityType, AIRPLANE_LIST)) {
|
||||
if (inList(resources, activityType, AIRPLANE_LIST)) {
|
||||
return AIRPLANE;
|
||||
}
|
||||
if (inList(context, activityType, BIKE_LIST)) {
|
||||
if (inList(resources, activityType, BIKE_LIST)) {
|
||||
return BIKE;
|
||||
}
|
||||
if (inList(context, activityType, MOUNTAIN_BIKE_LIST)) {
|
||||
if (inList(resources, activityType, MOUNTAIN_BIKE_LIST)) {
|
||||
return MOUNTAIN_BIKE;
|
||||
}
|
||||
if (inList(context, activityType, CLIMBING_LIST)) {
|
||||
if (inList(resources, activityType, CLIMBING_LIST)) {
|
||||
return CLIMBING;
|
||||
}
|
||||
if (inList(context, activityType, MOTOR_BIKE_LIST)) {
|
||||
if (inList(resources, activityType, MOTOR_BIKE_LIST)) {
|
||||
return MOTOR_BIKE;
|
||||
}
|
||||
if (inList(context, activityType, KAYAKING_LIST)) {
|
||||
if (inList(resources, activityType, KAYAKING_LIST)) {
|
||||
return KAYAK;
|
||||
}
|
||||
if (inList(context, activityType, BOAT_LIST)) {
|
||||
if (inList(resources, activityType, BOAT_LIST)) {
|
||||
return BOAT;
|
||||
}
|
||||
if (inList(context, activityType, SAILING_LIST)) {
|
||||
if (inList(resources, activityType, SAILING_LIST)) {
|
||||
return SAILING;
|
||||
}
|
||||
if (inList(context, activityType, DRIVE_LIST)) {
|
||||
if (inList(resources, activityType, DRIVE_LIST)) {
|
||||
return DRIVE;
|
||||
}
|
||||
if (inList(context, activityType, INLINE_SKATING_LIST)) {
|
||||
if (inList(resources, activityType, INLINE_SKATING_LIST)) {
|
||||
return INLINE_SKATING;
|
||||
}
|
||||
if (inList(context, activityType, ESCOOTER_LIST)) {
|
||||
if (inList(resources, activityType, ESCOOTER_LIST)) {
|
||||
return ESCOOTER;
|
||||
}
|
||||
if (inList(context, activityType, RUN_LIST)) {
|
||||
if (inList(resources, activityType, RUN_LIST)) {
|
||||
return RUN;
|
||||
}
|
||||
if (inList(context, activityType, SKI_LIST)) {
|
||||
if (inList(resources, activityType, SKI_LIST)) {
|
||||
return SKI;
|
||||
}
|
||||
if (inList(context, activityType, SNOW_BOARDING_LIST)) {
|
||||
if (inList(resources, activityType, SNOW_BOARDING_LIST)) {
|
||||
return SNOW_BOARDING;
|
||||
}
|
||||
if (inList(context, activityType, SKATE_BOARDING_LIST)) {
|
||||
if (inList(resources, activityType, SKATE_BOARDING_LIST)) {
|
||||
return SKATE_BOARDING;
|
||||
}
|
||||
if (inList(context, activityType, WALK_LIST)) {
|
||||
if (inList(resources, activityType, WALK_LIST)) {
|
||||
return WALK;
|
||||
}
|
||||
return UNKNOWN;
|
||||
@@ -216,7 +218,7 @@ public class TrackIconUtils {
|
||||
* @param activityType the activity type
|
||||
* @param list the list
|
||||
*/
|
||||
private static boolean inList(Context context, String activityType, int[] list) {
|
||||
private static boolean inList(Resources context, String activityType, int[] list) {
|
||||
for (int i : list) {
|
||||
if (context.getString(i).equals(activityType)) {
|
||||
return true;
|
||||
@@ -228,10 +230,9 @@ public class TrackIconUtils {
|
||||
/**
|
||||
* Returns true if category is in the SPEED_ICON array. Otherwise returns false.
|
||||
*
|
||||
* @param context the context.
|
||||
* @param category the name of the category, activity type.
|
||||
*/
|
||||
public static boolean isSpeedIcon(Context context, String category) {
|
||||
return inList(context, category, SPEED_ICON);
|
||||
public static boolean isSpeedIcon(Resources resources, String category) {
|
||||
return inList(resources, category, SPEED_ICON);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.Locale;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
/**
|
||||
* Utilities for track name.
|
||||
@@ -43,7 +44,7 @@ public class TrackNameUtils {
|
||||
|
||||
//TODO Should not access sharedPreferences; trackName should be an ENUM.
|
||||
public static String getTrackName(Context context, Track.Id trackId, Instant startTime) {
|
||||
String trackName = PreferencesUtils.getString(PreferencesUtils.getSharedPreferences(context), context, R.string.track_name_key, context.getString(R.string.track_name_default));
|
||||
String trackName = PreferencesUtils.getString(R.string.track_name_key, context.getString(R.string.track_name_default));
|
||||
|
||||
if (trackName.equals(context.getString(R.string.settings_recording_track_name_date_local_value))) {
|
||||
return StringUtils.formatDateTime(context, startTime);
|
||||
|
||||
@@ -22,7 +22,7 @@ import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.content.data.TrackPointsColumns;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.content.provider.TrackPointIterator;
|
||||
import de.dennisguse.opentracks.util.PreferencesUtils;
|
||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||
|
||||
/**
|
||||
* This model is used to load intervals for a track.
|
||||
@@ -46,7 +46,7 @@ public class IntervalStatisticsModel extends AndroidViewModel {
|
||||
|
||||
public IntervalStatisticsModel(@NonNull Application application) {
|
||||
super(application);
|
||||
minGPSDistance = PreferencesUtils.getRecordingDistanceInterval(PreferencesUtils.getSharedPreferences(application), application);
|
||||
minGPSDistance = PreferencesUtils.getRecordingDistanceInterval();
|
||||
contentResolver = getApplication().getContentResolver();
|
||||
handlerThread = new HandlerThread(TAG);
|
||||
handlerThread.start();
|
||||
|
||||
@@ -18,6 +18,7 @@ import de.dennisguse.opentracks.content.data.TrackPoint;
|
||||
import de.dennisguse.opentracks.content.sensor.SensorDataSet;
|
||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||
import de.dennisguse.opentracks.stats.SensorStatistics;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
import de.dennisguse.opentracks.util.StringUtils;
|
||||
|
||||
public class StatisticDataBuilder {
|
||||
@@ -35,48 +36,53 @@ public class StatisticDataBuilder {
|
||||
private static StatisticData build(@NonNull Context context, @NonNull TrackRecordingService.RecordingData recordingData, @NonNull String fieldKey, boolean isPrimary, boolean metricUnits) {
|
||||
final TrackPoint latestTrackPoint = recordingData.getLatestTrackPoint();
|
||||
final SensorDataSet sensorDataSet = recordingData.getSensorDataSet();
|
||||
final TrackStatistics trackStatistics = recordingData.getTrackStatistics();
|
||||
|
||||
String title = null;
|
||||
String description = null;
|
||||
Pair<String, String> valueAndUnit = null;
|
||||
boolean isWide = false;
|
||||
|
||||
final String sensorUnknown = context.getString(R.string.value_unknown);
|
||||
|
||||
if (fieldKey.equals(context.getString(R.string.stats_custom_layout_total_time_key))) {
|
||||
valueAndUnit = new Pair<>(StringUtils.formatElapsedTime(recordingData.getTrackStatistics().getTotalTime()), null);
|
||||
valueAndUnit = new Pair<>(StringUtils.formatElapsedTime(trackStatistics.getTotalTime()), null);
|
||||
title = context.getString(R.string.stats_total_time);
|
||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_moving_time_key))) {
|
||||
valueAndUnit = new Pair<>(StringUtils.formatElapsedTime(recordingData.getTrackStatistics().getMovingTime()), null);
|
||||
valueAndUnit = new Pair<>(StringUtils.formatElapsedTime(trackStatistics.getMovingTime()), null);
|
||||
title = context.getString(R.string.stats_moving_time);
|
||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_distance_key))) {
|
||||
valueAndUnit = StringUtils.getDistanceParts(context, recordingData.getTrackStatistics().getTotalDistance(), metricUnits);
|
||||
valueAndUnit = StringUtils.getDistanceParts(context, trackStatistics.getTotalDistance(), metricUnits);
|
||||
title = context.getString(R.string.stats_distance);
|
||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_speed_key)) || fieldKey.equals(context.getString(R.string.stats_custom_layout_pace_key))) {
|
||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_speed_key)) || fieldKey.equals(context.getString(R.string.stats_custom_layout_pace_key))) {
|
||||
boolean reportSpeed = fieldKey.equals("speed");
|
||||
title = fieldKey.equals("speed") ? context.getString(R.string.stats_speed) : context.getString(R.string.stats_pace);
|
||||
Speed speed = latestTrackPoint != null && latestTrackPoint.hasSpeed() ? latestTrackPoint.getSpeed() : null;
|
||||
if (sensorDataSet != null && sensorDataSet.getCyclingDistanceSpeed() != null && sensorDataSet.getCyclingDistanceSpeed().hasValue() && sensorDataSet.getCyclingDistanceSpeed().isRecent()) {
|
||||
valueAndUnit = StringUtils.getSpeedParts(context, sensorDataSet.getCyclingDistanceSpeed().getValue().getSpeed(), metricUnits, reportSpeed);
|
||||
description = context.getString(R.string.description_speed_source_sensor, sensorDataSet.getCyclingDistanceSpeed().getSensorNameOrAddress());
|
||||
title = reportSpeed ? context.getString(R.string.stats_speed) : context.getString(R.string.stats_pace);
|
||||
|
||||
if (sensorDataSet != null && sensorDataSet.getSpeed() != null) {
|
||||
valueAndUnit = StringUtils.getSpeedParts(context, sensorDataSet.getSpeed().first, metricUnits, reportSpeed);
|
||||
description = context.getString(R.string.description_speed_source_sensor, sensorDataSet.getSpeed().second);
|
||||
} else {
|
||||
Speed speed = latestTrackPoint != null && latestTrackPoint.hasSpeed() ? latestTrackPoint.getSpeed() : null;
|
||||
valueAndUnit = StringUtils.getSpeedParts(context, speed, metricUnits, reportSpeed);
|
||||
description = context.getString(R.string.description_speed_source_gps);
|
||||
}
|
||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_average_moving_speed_key))) {
|
||||
valueAndUnit = StringUtils.getSpeedParts(context, recordingData.getTrackStatistics().getAverageMovingSpeed(), metricUnits, true);
|
||||
valueAndUnit = StringUtils.getSpeedParts(context, trackStatistics.getAverageMovingSpeed(), metricUnits, true);
|
||||
title = context.getString(R.string.stats_average_moving_speed);
|
||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_average_speed_key))) {
|
||||
valueAndUnit = StringUtils.getSpeedParts(context, recordingData.getTrackStatistics().getAverageSpeed(), metricUnits, true);
|
||||
valueAndUnit = StringUtils.getSpeedParts(context, trackStatistics.getAverageSpeed(), metricUnits, true);
|
||||
title = context.getString(R.string.stats_average_speed);
|
||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_max_speed_key))) {
|
||||
valueAndUnit = StringUtils.getSpeedParts(context, recordingData.getTrackStatistics().getMaxSpeed(), metricUnits, true);
|
||||
valueAndUnit = StringUtils.getSpeedParts(context, trackStatistics.getMaxSpeed(), metricUnits, true);
|
||||
title = context.getString(R.string.stats_max_speed);
|
||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_average_moving_pace_key))) {
|
||||
valueAndUnit = StringUtils.getSpeedParts(context, recordingData.getTrackStatistics().getAverageMovingSpeed(), metricUnits, false);
|
||||
valueAndUnit = StringUtils.getSpeedParts(context, trackStatistics.getAverageMovingSpeed(), metricUnits, false);
|
||||
title = context.getString(R.string.stats_average_moving_pace);
|
||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_average_pace_key))) {
|
||||
valueAndUnit = StringUtils.getSpeedParts(context, recordingData.getTrackStatistics().getAverageSpeed(), metricUnits, false);
|
||||
valueAndUnit = StringUtils.getSpeedParts(context, trackStatistics.getAverageSpeed(), metricUnits, false);
|
||||
title = context.getString(R.string.stats_average_pace);
|
||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_fastest_pace_key))) {
|
||||
valueAndUnit = StringUtils.getSpeedParts(context, recordingData.getTrackStatistics().getMaxSpeed(), metricUnits, true);
|
||||
valueAndUnit = StringUtils.getSpeedParts(context, trackStatistics.getMaxSpeed(), metricUnits, true);
|
||||
title = context.getString(R.string.stats_fastest_pace);
|
||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_altitude_key))) {
|
||||
Float altitude = latestTrackPoint != null && latestTrackPoint.hasAltitude() ? (float) latestTrackPoint.getAltitude().toM() : null;
|
||||
@@ -84,10 +90,10 @@ public class StatisticDataBuilder {
|
||||
description = latestTrackPoint != null && latestTrackPoint.hasAltitude() ? context.getString(latestTrackPoint.getAltitude().getLabelId()) : null;
|
||||
valueAndUnit = StringUtils.getAltitudeParts(context, altitude, metricUnits);
|
||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_gain_key))) {
|
||||
valueAndUnit = StringUtils.getAltitudeParts(context, recordingData.getTrackStatistics().getTotalAltitudeGain(), metricUnits);
|
||||
valueAndUnit = StringUtils.getAltitudeChangeParts(context, trackStatistics.getTotalAltitudeGain(), metricUnits);
|
||||
title = context.getString(R.string.stats_gain);
|
||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_loss_key))) {
|
||||
valueAndUnit = StringUtils.getAltitudeParts(context, recordingData.getTrackStatistics().getTotalAltitudeLoss(), metricUnits);
|
||||
valueAndUnit = StringUtils.getAltitudeChangeParts(context, trackStatistics.getTotalAltitudeLoss(), metricUnits);
|
||||
title = context.getString(R.string.stats_loss);
|
||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_coordinates_key))) {
|
||||
title = context.getString(R.string.stats_coordinates);
|
||||
@@ -95,34 +101,41 @@ public class StatisticDataBuilder {
|
||||
if (latestTrackPoint != null && latestTrackPoint.hasLocation()) {
|
||||
valueAndUnit = new Pair<>(StringUtils.formatCoordinate(context, latestTrackPoint.getLatitude(), latestTrackPoint.getLongitude()), null);
|
||||
} else {
|
||||
valueAndUnit = new Pair<>(context.getString(R.string.value_none), null);
|
||||
valueAndUnit = new Pair<>(context.getString(R.string.value_unknown), null);
|
||||
}
|
||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_heart_rate_key))) {
|
||||
title = context.getString(R.string.stats_sensors_heart_rate);
|
||||
if (sensorDataSet != null && sensorDataSet.getHeartRate() != null && sensorDataSet.getHeartRate().hasValue() && sensorDataSet.getHeartRate().isRecent()) {
|
||||
valueAndUnit = StringUtils.getHeartRateParts(context, sensorDataSet.getHeartRate().getValue());
|
||||
description = sensorDataSet.getHeartRate().getSensorNameOrAddress();
|
||||
if (sensorDataSet != null && sensorDataSet.getHeartRate() != null) {
|
||||
valueAndUnit = StringUtils.getHeartRateParts(context, sensorDataSet.getHeartRate().first);
|
||||
description = sensorDataSet.getHeartRate().second;
|
||||
} else {
|
||||
valueAndUnit = StringUtils.getHeartRateParts(context, null);
|
||||
description = context.getString(R.string.value_none);
|
||||
description = sensorUnknown;
|
||||
}
|
||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_cadence_key))) {
|
||||
title = context.getString(R.string.stats_sensors_cadence);
|
||||
if (sensorDataSet != null && sensorDataSet.getCyclingCadence() != null && sensorDataSet.getCyclingCadence().hasValue() && sensorDataSet.getCyclingCadence().isRecent()) {
|
||||
valueAndUnit = StringUtils.getCadenceParts(context, sensorDataSet.getCyclingCadence().getValue());
|
||||
description = sensorDataSet.getCyclingCadence().getSensorNameOrAddress();
|
||||
} else {
|
||||
valueAndUnit = StringUtils.getCadenceParts(context, null);
|
||||
description = context.getString(R.string.value_none);
|
||||
|
||||
Float cadence = null;
|
||||
if (sensorDataSet != null) {
|
||||
Pair<Float, String> cadenceData = sensorDataSet.getCadence();
|
||||
if (cadenceData != null) {
|
||||
cadence = cadenceData.first;
|
||||
description = cadenceData.second;
|
||||
}
|
||||
}
|
||||
|
||||
valueAndUnit = StringUtils.getCadenceParts(context, cadence);
|
||||
if (description == null) {
|
||||
description = sensorUnknown;
|
||||
}
|
||||
} else if (fieldKey.equals(context.getString(R.string.stats_custom_layout_power_key))) {
|
||||
title = context.getString(R.string.stats_sensors_power);
|
||||
if (sensorDataSet != null && sensorDataSet.getCyclingPower() != null && sensorDataSet.getCyclingPower().hasValue() && sensorDataSet.getCyclingPower().isRecent()) {
|
||||
if (sensorDataSet != null && sensorDataSet.getCyclingPower() != null && sensorDataSet.getCyclingPower().hasValue()) {
|
||||
valueAndUnit = StringUtils.getPowerParts(context, sensorDataSet.getCyclingPower().getValue());
|
||||
description = sensorDataSet.getCyclingPower().getSensorNameOrAddress();
|
||||
} else {
|
||||
valueAndUnit = StringUtils.getPowerParts(context, null);
|
||||
description = context.getString(R.string.value_none);
|
||||
description = sensorUnknown;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +147,7 @@ public class StatisticDataBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a list of StatisticData with sensors connected but not in statisticDataList.
|
||||
* Builds a list of StatisticData with sensors configured but not in statisticDataList.
|
||||
*/
|
||||
private static List<StatisticData> getSensorStatsDataIfNeeded(Context context, TrackRecordingService.RecordingData recordingData, List<StatisticData> statisticDataList, boolean metricUnits) {
|
||||
List<StatisticData> sensorDataList = new ArrayList<>();
|
||||
@@ -142,13 +155,13 @@ public class StatisticDataBuilder {
|
||||
if (sensorDataSet == null) {
|
||||
return sensorDataList;
|
||||
}
|
||||
if (statisticDataList.stream().noneMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_sensors_heart_rate))) && sensorDataSet.getHeartRate() != null && sensorDataSet.getHeartRate().hasValue() && sensorDataSet.getHeartRate().isRecent()) {
|
||||
if (statisticDataList.stream().noneMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_sensors_heart_rate))) && sensorDataSet.getHeartRate() != null) {
|
||||
sensorDataList.add(build(context, recordingData, "heart_rate", true, metricUnits));
|
||||
}
|
||||
if (statisticDataList.stream().noneMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_sensors_cadence))) && sensorDataSet.getCyclingCadence() != null && sensorDataSet.getCyclingCadence().hasValue() && sensorDataSet.getCyclingCadence().isRecent()) {
|
||||
if (statisticDataList.stream().noneMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_sensors_cadence))) && sensorDataSet.getCadence() != null) {
|
||||
sensorDataList.add(build(context, recordingData, "cadence", true, metricUnits));
|
||||
}
|
||||
if (statisticDataList.stream().noneMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_sensors_power))) && sensorDataSet.getCyclingPower() != null && sensorDataSet.getCyclingPower().hasValue() && sensorDataSet.getCyclingPower().isRecent()) {
|
||||
if (statisticDataList.stream().noneMatch(i -> i.getField().getTitle().equals(context.getString(R.string.stats_sensors_power))) && sensorDataSet.getCyclingPower() != null && sensorDataSet.getCyclingPower().hasValue()) {
|
||||
sensorDataList.add(build(context, recordingData, "power", true, metricUnits));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,6v3l4,-4 -4,-4v3c-4.42,0 -8,3.58 -8,8 0,1.57 0.46,3.03 1.24,4.26L6.7,14.8c-0.45,-0.83 -0.7,-1.79 -0.7,-2.8 0,-3.31 2.69,-6 6,-6zM18.76,7.74L17.3,9.2c0.44,0.84 0.7,1.79 0.7,2.8 0,3.31 -2.69,6 -6,6v-3l-4,4 4,4v-3c4.42,0 8,-3.58 8,-8 0,-1.57 -0.46,-3.03 -1.24,-4.26z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M9,3L5,6.99h3L8,14h2L10,6.99h3L9,3zM16,17.01L16,10h-2v7.01h-3L15,21l4,-3.99h-3z"/>
|
||||
</vector>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user