forked from upstream-mirrors/OpenTracks
7c6141f98c
- Restructured sensor logic to give absolute priority to Bluetooth sensors, largely ignoring GPS data for distance and speed calculations. - Implemented downstream UI expansions, custom recording layouts, and new settings to reflect and control the new sensor-first flow. - Added MediaNotificationListenerService and integrated media playback tracking within statistics. - Modified VoiceAnnouncementUtils to harmonize announcements with the updated architecture. - Added 'About This Fork' section to the README. Note: These modifications were co-created with Antigravity AI.
1467 lines
75 KiB
Plaintext
1467 lines
75 KiB
Plaintext
diff --git a/src/androidTest/java/de/dennisguse/opentracks/util/EspressoUtils.java b/src/androidTest/java/de/dennisguse/opentracks/util/EspressoUtils.java
|
|
index 459c55148..c10651480 100644
|
|
--- a/src/androidTest/java/de/dennisguse/opentracks/util/EspressoUtils.java
|
|
+++ b/src/androidTest/java/de/dennisguse/opentracks/util/EspressoUtils.java
|
|
@@ -1,8 +1,8 @@
|
|
package de.dennisguse.opentracks.util;
|
|
|
|
-import static org.hamcrest.Matchers.allOf;
|
|
-import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
|
import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom;
|
|
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
|
|
+import static org.hamcrest.Matchers.allOf;
|
|
|
|
import android.app.Activity;
|
|
import android.os.SystemClock;
|
|
@@ -14,11 +14,11 @@ import androidx.recyclerview.widget.RecyclerView;
|
|
import androidx.test.espresso.PerformException;
|
|
import androidx.test.espresso.UiController;
|
|
import androidx.test.espresso.ViewAction;
|
|
+import androidx.test.espresso.util.HumanReadables;
|
|
+import androidx.test.espresso.util.TreeIterables;
|
|
import androidx.test.platform.app.InstrumentationRegistry;
|
|
import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
|
|
import androidx.test.runner.lifecycle.Stage;
|
|
-import androidx.test.espresso.util.HumanReadables;
|
|
-import androidx.test.espresso.util.TreeIterables;
|
|
import androidx.viewpager2.widget.ViewPager2;
|
|
|
|
import com.google.android.material.tabs.TabLayout;
|
|
@@ -33,8 +33,6 @@ import java.util.function.Predicate;
|
|
|
|
public class EspressoUtils {
|
|
|
|
- private static final long VIEW_POLLING_INTERVAL_MS = 50;
|
|
-
|
|
public static Matcher<View> withRecyclerViewItemCount(final int count) {
|
|
return new TypeSafeMatcher<>() {
|
|
@Override
|
|
diff --git a/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java b/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java
|
|
index d0e56ff4d..7bf360409 100644
|
|
--- a/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java
|
|
+++ b/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java
|
|
@@ -67,6 +67,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|
private static final String CURRENT_TAB_TAG_KEY = "current_tab_tag_key";
|
|
|
|
private Snackbar snackbar;
|
|
+ private Snackbar sensorWarningSnackbar;
|
|
|
|
// The following are setFrequency in onCreate
|
|
private ContentProviderUtils contentProviderUtils;
|
|
@@ -86,6 +87,9 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|
service.getGpsStatusObservable()
|
|
.observe(TrackRecordingActivity.this, this::onGpsStatusChanged);
|
|
|
|
+ service.getSensorDisconnectedWarningObservable()
|
|
+ .observe(TrackRecordingActivity.this, this::onSensorDisconnectedWarningChanged);
|
|
+
|
|
if (!service.isRecording()) {
|
|
finish();
|
|
return;
|
|
@@ -408,4 +412,27 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|
});
|
|
snackbar.show();
|
|
}
|
|
+
|
|
+ private void onSensorDisconnectedWarningChanged(boolean show) {
|
|
+ if (show) {
|
|
+ if (sensorWarningSnackbar == null) {
|
|
+ sensorWarningSnackbar = Snackbar
|
|
+ .make(viewBinding.trackRecordingCoordinatorLayout,
|
|
+ getString(R.string.sensor_disconnected_warning),
|
|
+ Snackbar.LENGTH_INDEFINITE)
|
|
+ .setAction(getString(R.string.sensor_disconnected_switch_action), v -> {
|
|
+ if (trackRecordingServiceConnection != null && trackRecordingServiceConnection.getTrackRecordingService() != null) {
|
|
+ trackRecordingServiceConnection.getTrackRecordingService().setOverrideSensorWithGPS(true);
|
|
+ }
|
|
+ });
|
|
+ }
|
|
+ if (!sensorWarningSnackbar.isShown()) {
|
|
+ sensorWarningSnackbar.show();
|
|
+ }
|
|
+ } else {
|
|
+ if (sensorWarningSnackbar != null && sensorWarningSnackbar.isShown()) {
|
|
+ sensorWarningSnackbar.dismiss();
|
|
+ }
|
|
+ }
|
|
+ }
|
|
}
|
|
\ No newline at end of file
|
|
diff --git a/src/main/java/de/dennisguse/opentracks/data/statistics/ExtremityMonitor.java b/src/main/java/de/dennisguse/opentracks/data/statistics/ExtremityMonitor.java
|
|
index ac7147034..ade7a2785 100644
|
|
--- a/src/main/java/de/dennisguse/opentracks/data/statistics/ExtremityMonitor.java
|
|
+++ b/src/main/java/de/dennisguse/opentracks/data/statistics/ExtremityMonitor.java
|
|
@@ -50,15 +50,6 @@ public class ExtremityMonitor {
|
|
return min;
|
|
}
|
|
|
|
- /**
|
|
- * Sets the minimum value.
|
|
- *
|
|
- * @param min the minimum value
|
|
- */
|
|
- public void setMin(double min) {
|
|
- this.min = min;
|
|
- }
|
|
-
|
|
/**
|
|
* Gets the maximum value seen.
|
|
*/
|
|
@@ -66,15 +57,6 @@ public class ExtremityMonitor {
|
|
return max;
|
|
}
|
|
|
|
- /**
|
|
- * Sets the maximum value.
|
|
- *
|
|
- * @param max the maximum value
|
|
- */
|
|
- public void setMax(double max) {
|
|
- this.max = max;
|
|
- }
|
|
-
|
|
/**
|
|
* Updates the min and the max with a new value.
|
|
*
|
|
diff --git a/src/main/java/de/dennisguse/opentracks/data/statistics/TrackStatisticsUpdater.java b/src/main/java/de/dennisguse/opentracks/data/statistics/TrackStatisticsUpdater.java
|
|
index 46ad43431..855b104fa 100644
|
|
--- a/src/main/java/de/dennisguse/opentracks/data/statistics/TrackStatisticsUpdater.java
|
|
+++ b/src/main/java/de/dennisguse/opentracks/data/statistics/TrackStatisticsUpdater.java
|
|
@@ -54,6 +54,7 @@ public class TrackStatisticsUpdater {
|
|
private TrackPoint lastTrackPoint;
|
|
|
|
private boolean isIdle;
|
|
+ private boolean forceZeroDistance;
|
|
|
|
public TrackStatisticsUpdater(@NonNull TrackPoint trackPoint) {
|
|
addTrackPoint(trackPoint);
|
|
@@ -78,6 +79,7 @@ public class TrackStatisticsUpdater {
|
|
|
|
this.lastTrackPoint = toCopy.lastTrackPoint;
|
|
this.isIdle = toCopy.isIdle;
|
|
+ this.forceZeroDistance = toCopy.forceZeroDistance;
|
|
resetAverageHeartRate();
|
|
|
|
addTrackPoint(tmp);
|
|
@@ -94,6 +96,10 @@ public class TrackStatisticsUpdater {
|
|
return isIdle;
|
|
}
|
|
|
|
+ public void setForceZeroDistance(boolean forceZeroDistance) {
|
|
+ this.forceZeroDistance = forceZeroDistance;
|
|
+ }
|
|
+
|
|
public Statistics getCurrentSegment() {
|
|
return currentSegment.getStatistics();
|
|
}
|
|
@@ -147,6 +153,8 @@ public class TrackStatisticsUpdater {
|
|
Distance movingDistance = null;
|
|
if (trackPoint.sensorDistance() != null) {
|
|
movingDistance = trackPoint.sensorDistance();
|
|
+ } else if (forceZeroDistance) {
|
|
+ movingDistance = Distance.ZERO;
|
|
} else if (lastTrackPoint != null
|
|
&& lastTrackPoint.position().hasLocation()
|
|
&& trackPoint.position().hasLocation()) {
|
|
diff --git a/src/main/java/de/dennisguse/opentracks/fragments/ChooseActivityTypeDialogFragment.java b/src/main/java/de/dennisguse/opentracks/fragments/ChooseActivityTypeDialogFragment.java
|
|
index e6967d633..84858768a 100644
|
|
--- a/src/main/java/de/dennisguse/opentracks/fragments/ChooseActivityTypeDialogFragment.java
|
|
+++ b/src/main/java/de/dennisguse/opentracks/fragments/ChooseActivityTypeDialogFragment.java
|
|
@@ -56,7 +56,7 @@ public class ChooseActivityTypeDialogFragment extends DialogFragment implements
|
|
ActivityType.WORKOUT
|
|
);
|
|
|
|
- private static int getPosition(Context context, ActivityType activityType) {
|
|
+ private static int getPosition(ActivityType activityType) {
|
|
if (activityType == null) {
|
|
return -1;
|
|
}
|
|
@@ -92,7 +92,7 @@ public class ChooseActivityTypeDialogFragment extends DialogFragment implements
|
|
}
|
|
|
|
final ChooseActivityTypeImageAdapter imageAdapter = new ChooseActivityTypeImageAdapter(iconDrawableIds);
|
|
- int position = getPosition(getContext(), preselectedActivityType);
|
|
+ int position = getPosition(preselectedActivityType);
|
|
if (position != -1) {
|
|
imageAdapter.setSelected(position);
|
|
}
|
|
diff --git a/src/main/java/de/dennisguse/opentracks/io/healthconnect/exporter/HealthConnectUtils.java b/src/main/java/de/dennisguse/opentracks/io/healthconnect/exporter/HealthConnectUtils.java
|
|
index 591a0070c..cc3998bf5 100644
|
|
--- a/src/main/java/de/dennisguse/opentracks/io/healthconnect/exporter/HealthConnectUtils.java
|
|
+++ b/src/main/java/de/dennisguse/opentracks/io/healthconnect/exporter/HealthConnectUtils.java
|
|
@@ -27,8 +27,6 @@ import kotlinx.coroutines.BuildersKt;
|
|
*/
|
|
public class HealthConnectUtils {
|
|
|
|
- private static final String TAG = HealthConnectUtils.class.getSimpleName();
|
|
-
|
|
static final String WRITE_EXERCISE_PERMISSION = HealthPermission.getWritePermission(JvmClassMappingKt.getKotlinClass(ExerciseSessionRecord.class));
|
|
static final String WRITE_EXERCISE_ROUTE_PERMISSION = HealthPermission.PERMISSION_WRITE_EXERCISE_ROUTE;
|
|
|
|
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/GpsStatusManager.java b/src/main/java/de/dennisguse/opentracks/sensors/GpsStatusManager.java
|
|
index 5a5154d95..d82dbac81 100644
|
|
--- a/src/main/java/de/dennisguse/opentracks/sensors/GpsStatusManager.java
|
|
+++ b/src/main/java/de/dennisguse/opentracks/sensors/GpsStatusManager.java
|
|
@@ -20,8 +20,6 @@ import de.dennisguse.opentracks.settings.PreferencesUtils;
|
|
*/
|
|
class GpsStatusManager {
|
|
|
|
- private static final String TAG = GpsStatusManager.class.getSimpleName();
|
|
-
|
|
// The duration that GpsStatus waits from minimal interval to consider GPS lost.
|
|
@VisibleForTesting
|
|
public static final Duration SIGNAL_LOST_THRESHOLD = Duration.ofSeconds(30);
|
|
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/SensorHandler.java b/src/main/java/de/dennisguse/opentracks/sensors/SensorHandler.java
|
|
index 31433932d..0a7c6de8c 100644
|
|
--- a/src/main/java/de/dennisguse/opentracks/sensors/SensorHandler.java
|
|
+++ b/src/main/java/de/dennisguse/opentracks/sensors/SensorHandler.java
|
|
@@ -115,7 +115,8 @@ public abstract class SensorHandler<AggregatorInput, AggregatorOutput> implement
|
|
return;
|
|
}
|
|
|
|
- SensorHandler.this.aggregator.resetAggregated();
|
|
+ SensorHandler.this.aggregator = null;
|
|
+ sensorManager.onChange();
|
|
}
|
|
|
|
@Override
|
|
@@ -130,5 +131,6 @@ public abstract class SensorHandler<AggregatorInput, AggregatorOutput> implement
|
|
@Override
|
|
public void onDisconnected() {
|
|
SensorHandler.this.aggregator = null;
|
|
+ sensorManager.onChange();
|
|
}
|
|
}
|
|
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java b/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java
|
|
index b2a9ba1bc..7429b048a 100644
|
|
--- a/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java
|
|
+++ b/src/main/java/de/dennisguse/opentracks/sensors/SensorManager.java
|
|
@@ -239,6 +239,9 @@ public class SensorManager implements SharedPreferences.OnSharedPreferenceChange
|
|
}
|
|
|
|
public SensorDataSet getSensorDataSet(Instant now) {
|
|
+ if (!isStarted()) {
|
|
+ return new SensorDataSet(new SensorData<>(Position.of(now), ""), null, null, null, null, null, null, null);
|
|
+ }
|
|
//We always need a Position with now
|
|
SensorData<Position> gpsSensorData = gpsHandler.getSensorData(now);
|
|
if (gpsSensorData != null && gpsSensorData.data() != null) {
|
|
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorCyclingCadence.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorCyclingCadence.java
|
|
index f10fcace9..42ead636b 100644
|
|
--- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorCyclingCadence.java
|
|
+++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorCyclingCadence.java
|
|
@@ -5,6 +5,7 @@ import android.util.Log;
|
|
import androidx.annotation.NonNull;
|
|
|
|
import java.time.Duration;
|
|
+import java.time.Instant;
|
|
|
|
import de.dennisguse.opentracks.data.models.Cadence;
|
|
import de.dennisguse.opentracks.sensors.UintUtils;
|
|
@@ -18,17 +19,20 @@ public class AggregatorCyclingCadence extends Aggregator<CyclingCadenceBluetooth
|
|
super(sensorAddress, sensorName);
|
|
}
|
|
|
|
+ private Instant lastValidUpdate;
|
|
+
|
|
@Override
|
|
protected void computeValue(@NonNull Raw<CyclingCadenceBluetooth.CyclingCadenceMeasurement> current) {
|
|
if (current.value() instanceof CyclingCadenceBluetooth.DirectCadenceData directCadenceData) {
|
|
output = directCadenceData.cadence();
|
|
+ lastValidUpdate = current.time();
|
|
return;
|
|
}
|
|
|
|
- computeRawCadence((CyclingCadenceBluetooth.CrankData) current.value());
|
|
+ computeRawCadence((CyclingCadenceBluetooth.CrankData) current.value(), current.time());
|
|
}
|
|
|
|
- private void computeRawCadence(@NonNull CyclingCadenceBluetooth.CrankData currentRaw) {
|
|
+ private void computeRawCadence(@NonNull CyclingCadenceBluetooth.CrankData currentRaw, Instant now) {
|
|
CyclingCadenceBluetooth.CrankData previousRaw = getPreviousRaw();
|
|
if (previousRaw == null) return;
|
|
|
|
@@ -36,6 +40,9 @@ public class AggregatorCyclingCadence extends Aggregator<CyclingCadenceBluetooth
|
|
Duration timeDiff = Duration.ofMillis((long) timeDiff_ms);
|
|
|
|
if (timeDiff.isZero()) {
|
|
+ if (lastValidUpdate != null && Duration.between(lastValidUpdate, now).compareTo(getMaxSensorAge()) > 0) {
|
|
+ resetOutdated();
|
|
+ }
|
|
return;
|
|
}
|
|
if (timeDiff.isNegative()) {
|
|
@@ -52,9 +59,15 @@ public class AggregatorCyclingCadence extends Aggregator<CyclingCadenceBluetooth
|
|
}
|
|
|
|
long crankDiff = UintUtils.diff(currentRaw.crankRevolutionsCount(), previousRaw.crankRevolutionsCount(), UintUtils.UINT32_MAX);
|
|
+ lastValidUpdate = now;
|
|
output = Cadence.of(crankDiff, timeDiff);
|
|
}
|
|
|
|
+ @Override
|
|
+ protected void resetOutdated() {
|
|
+ output = Cadence.of(0f);
|
|
+ }
|
|
+
|
|
private CyclingCadenceBluetooth.CrankData getPreviousRaw() {
|
|
if (previous == null) {
|
|
return null;
|
|
diff --git a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorCyclingDistanceSpeed.java b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorCyclingDistanceSpeed.java
|
|
index db7b65d7c..d80381f46 100644
|
|
--- a/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorCyclingDistanceSpeed.java
|
|
+++ b/src/main/java/de/dennisguse/opentracks/sensors/sensorData/AggregatorCyclingDistanceSpeed.java
|
|
@@ -5,6 +5,7 @@ import android.util.Log;
|
|
import androidx.annotation.NonNull;
|
|
|
|
import java.time.Duration;
|
|
+import java.time.Instant;
|
|
|
|
import de.dennisguse.opentracks.data.models.Distance;
|
|
import de.dennisguse.opentracks.data.models.Speed;
|
|
@@ -21,9 +22,12 @@ public class AggregatorCyclingDistanceSpeed extends Aggregator<CyclingDistanceSp
|
|
super(sensorAddress, sensorName);
|
|
}
|
|
|
|
+ private Instant lastValidUpdate;
|
|
+
|
|
@Override
|
|
protected void computeValue(@NonNull Raw<CyclingDistanceSpeedBluetooth.WheelData> current) {
|
|
if (previous == null) {
|
|
+ lastValidUpdate = current.time();
|
|
return;
|
|
}
|
|
|
|
@@ -31,6 +35,9 @@ public class AggregatorCyclingDistanceSpeed extends Aggregator<CyclingDistanceSp
|
|
Duration timeDiff = Duration.ofMillis((long) timeDiff_ms);
|
|
|
|
if (timeDiff.isZero()) {
|
|
+ if (lastValidUpdate != null && Duration.between(lastValidUpdate, current.time()).compareTo(getMaxSensorAge()) > 0) {
|
|
+ resetOutdated();
|
|
+ }
|
|
return;
|
|
}
|
|
if (timeDiff.isNegative()) {
|
|
@@ -52,6 +59,7 @@ public class AggregatorCyclingDistanceSpeed extends Aggregator<CyclingDistanceSp
|
|
distanceOverall = distance.plus(output.distanceOverall);
|
|
}
|
|
Speed speed_mps = Speed.of(distance, timeDiff);
|
|
+ lastValidUpdate = current.time();
|
|
output = new Data(distance, distanceOverall, speed_mps);
|
|
}
|
|
|
|
diff --git a/src/main/java/de/dennisguse/opentracks/services/RecordingData.java b/src/main/java/de/dennisguse/opentracks/services/RecordingData.java
|
|
index b85449e9f..34b409688 100644
|
|
--- a/src/main/java/de/dennisguse/opentracks/services/RecordingData.java
|
|
+++ b/src/main/java/de/dennisguse/opentracks/services/RecordingData.java
|
|
@@ -16,9 +16,13 @@ public record RecordingData(
|
|
*/
|
|
TrackPoint latestTrackPoint,
|
|
SensorDataSet sensorDataSet,
|
|
- Statistics currentSegment) {
|
|
+ Statistics currentSegment,
|
|
+ String autoPauseStatus,
|
|
+ String idleTimer,
|
|
+ String walkingTimer,
|
|
+ String markerTimer) {
|
|
|
|
- public static final RecordingData NOT_RECORDING = new RecordingData(null, null, null, null);
|
|
+ public static final RecordingData NOT_RECORDING = new RecordingData(null, null, null, null, "", "", "", "");
|
|
|
|
public Statistics trackStatistics() {
|
|
return track.statistics();
|
|
diff --git a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingManager.java b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingManager.java
|
|
index 3a4197aee..256924137 100644
|
|
--- a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingManager.java
|
|
+++ b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingManager.java
|
|
@@ -40,6 +40,7 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
|
private final ContentProviderUtils contentProviderUtils;
|
|
private final Context context;
|
|
private final IdleObserver idleObserver;
|
|
+ private final SensorDisconnectObserver sensorDisconnectObserver;
|
|
|
|
private final Handler handler;
|
|
|
|
@@ -59,9 +60,24 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
|
private TrackPoint lastStoredTrackPoint;
|
|
private TrackPoint lastStoredTrackPointWithLocation;
|
|
|
|
- TrackRecordingManager(Context context, TrackPointCreator trackPointCreator, IdleObserver idleObserver, Handler handler) {
|
|
+ private boolean hasSensorBeenConnected = false;
|
|
+ private boolean isSensorDisconnectedWarningActive = false;
|
|
+
|
|
+ private boolean autoPauseOnCadence0;
|
|
+ private de.dennisguse.opentracks.data.models.Speed autoPauseCadence0Speed;
|
|
+ private boolean hasCadenceSensorBeenConnected = false;
|
|
+ private String autoPauseStatus = "N/A";
|
|
+
|
|
+ private java.time.Instant idleTimeoutScheduledAt;
|
|
+ private java.time.Instant walkingStartedAt;
|
|
+ private java.time.Instant idleStartedAt;
|
|
+
|
|
+ private boolean gpsOverriden = false;
|
|
+
|
|
+ TrackRecordingManager(Context context, TrackPointCreator trackPointCreator, IdleObserver idleObserver, SensorDisconnectObserver sensorDisconnectObserver, Handler handler) {
|
|
this.context = context;
|
|
this.idleObserver = idleObserver;
|
|
+ this.sensorDisconnectObserver = sensorDisconnectObserver;
|
|
this.trackPointCreator = trackPointCreator;
|
|
this.handler = handler;
|
|
contentProviderUtils = new ContentProviderUtils(context);
|
|
@@ -97,6 +113,23 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
|
|
|
onNewTrackPoint(segmentStartTrackPoint);
|
|
|
|
+ handler.post(() -> {
|
|
+ if (trackId != null) {
|
|
+ onIdle();
|
|
+ }
|
|
+ });
|
|
+ String address = de.dennisguse.opentracks.settings.PreferencesUtils.getString(de.dennisguse.opentracks.R.string.settings_sensor_bluetooth_cycling_speed_key, de.dennisguse.opentracks.sensors.driver.SensorType.NONE.getPreferenceValue());
|
|
+ if (!de.dennisguse.opentracks.sensors.driver.SensorType.NONE.getPreferenceValue().equals(address)) {
|
|
+ handler.postDelayed(() -> {
|
|
+ if (trackId != null && !hasSensorBeenConnected && !isSensorDisconnectedWarningActive) {
|
|
+ isSensorDisconnectedWarningActive = true;
|
|
+ if (sensorDisconnectObserver != null) {
|
|
+ sensorDisconnectObserver.onSensorDisconnectedWarning(true);
|
|
+ }
|
|
+ }
|
|
+ }, 10000);
|
|
+ }
|
|
+
|
|
return trackId;
|
|
}
|
|
|
|
@@ -116,6 +149,23 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
|
|
|
reset();
|
|
|
|
+ handler.post(() -> {
|
|
+ if (this.trackId != null) {
|
|
+ onIdle();
|
|
+ }
|
|
+ });
|
|
+ String address = de.dennisguse.opentracks.settings.PreferencesUtils.getString(de.dennisguse.opentracks.R.string.settings_sensor_bluetooth_cycling_speed_key, de.dennisguse.opentracks.sensors.driver.SensorType.NONE.getPreferenceValue());
|
|
+ if (!de.dennisguse.opentracks.sensors.driver.SensorType.NONE.getPreferenceValue().equals(address)) {
|
|
+ handler.postDelayed(() -> {
|
|
+ if (trackId != null && !hasSensorBeenConnected && !isSensorDisconnectedWarningActive) {
|
|
+ isSensorDisconnectedWarningActive = true;
|
|
+ if (sensorDisconnectObserver != null) {
|
|
+ sensorDisconnectObserver.onSensorDisconnectedWarning(true);
|
|
+ }
|
|
+ }
|
|
+ }, 10000);
|
|
+ }
|
|
+
|
|
return true;
|
|
}
|
|
|
|
@@ -125,6 +175,10 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
|
|
|
trackId = null;
|
|
trackStatisticsUpdater = null;
|
|
+
|
|
+ idleTimeoutScheduledAt = null;
|
|
+ walkingStartedAt = null;
|
|
+ idleStartedAt = null;
|
|
|
|
reset();
|
|
}
|
|
@@ -144,14 +198,48 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
|
|
|
TrackBuilder trackBuilder = new TrackBuilder(track);
|
|
trackBuilder.setStatistics(tmpTrackStatisticsUpdater.getTrackStatistics());
|
|
-
|
|
- return new RecordingData(trackBuilder.getTrack(), TrackPointWithEGM2008, current.second, tmpTrackStatisticsUpdater.getCurrentSegment());
|
|
+
|
|
+ java.time.Instant now = java.time.Instant.now();
|
|
+ String idleTimer = "";
|
|
+ if (idleTimeoutScheduledAt != null && !"IDLE".equals(autoPauseStatus) && !"WALKING".equals(autoPauseStatus)) {
|
|
+ java.time.Duration d = java.time.Duration.between(now, idleTimeoutScheduledAt);
|
|
+ if (!d.isNegative()) idleTimer = de.dennisguse.opentracks.util.StringUtils.formatElapsedTime(d);
|
|
+ }
|
|
+
|
|
+ String walkingTimer = "";
|
|
+ if (walkingStartedAt != null && "WALKING".equals(autoPauseStatus)) {
|
|
+ java.time.Duration d = java.time.Duration.between(walkingStartedAt, now);
|
|
+ if (!d.isNegative()) walkingTimer = de.dennisguse.opentracks.util.StringUtils.formatElapsedTime(d);
|
|
+ }
|
|
+
|
|
+ String markerTimer = "";
|
|
+ if (idleStartedAt != null && "IDLE".equals(autoPauseStatus)) {
|
|
+ java.time.Duration d = java.time.Duration.between(idleStartedAt, now);
|
|
+ if (!d.isNegative()) markerTimer = de.dennisguse.opentracks.util.StringUtils.formatElapsedTime(d);
|
|
+ }
|
|
+
|
|
+ return new RecordingData(trackBuilder.getTrack(), TrackPointWithEGM2008, current.second, tmpTrackStatisticsUpdater.getCurrentSegment(), autoPauseStatus, idleTimer, walkingTimer, markerTimer);
|
|
}
|
|
|
|
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
|
|
public synchronized void onIdle() {
|
|
Log.d(TAG, "Becoming idle");
|
|
- onNewTrackPoint(trackPointCreator.createIdle());
|
|
+ TrackPoint idlePoint = trackPointCreator.createIdle();
|
|
+
|
|
+ boolean preventIdle = de.dennisguse.opentracks.settings.PreferencesUtils.shouldPreventIdleIfCadence();
|
|
+ if (preventIdle && hasCadenceSensorBeenConnected) {
|
|
+ if (idlePoint.cadence() != null && idlePoint.cadence().value_rpm() > 0) {
|
|
+ scheduleNewIdleTimeout();
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!"IDLE".equals(autoPauseStatus)) {
|
|
+ idleStartedAt = java.time.Instant.now();
|
|
+ }
|
|
+ autoPauseStatus = "IDLE";
|
|
+
|
|
+ onNewTrackPoint(idlePoint);
|
|
|
|
idleObserver.onIdle();
|
|
}
|
|
@@ -167,6 +255,27 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
|
lastTrackPointUIWithAltitude = trackPoint;
|
|
}
|
|
|
|
+ boolean sensorHasDistance = trackPoint.sensorDistance() != null;
|
|
+ if (sensorHasDistance) {
|
|
+ hasSensorBeenConnected = true;
|
|
+ if (isSensorDisconnectedWarningActive) {
|
|
+ isSensorDisconnectedWarningActive = false;
|
|
+ sensorDisconnectObserver.onSensorDisconnectedWarning(false);
|
|
+ sensorDisconnectObserver.onSensorConnected();
|
|
+ }
|
|
+ } else if (hasSensorBeenConnected) {
|
|
+ if (!isSensorDisconnectedWarningActive) {
|
|
+ isSensorDisconnectedWarningActive = true;
|
|
+ sensorDisconnectObserver.onSensorDisconnectedWarning(true);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (trackStatisticsUpdater != null) {
|
|
+ String address = de.dennisguse.opentracks.settings.PreferencesUtils.getString(de.dennisguse.opentracks.R.string.settings_sensor_bluetooth_cycling_speed_key, de.dennisguse.opentracks.sensors.driver.SensorType.NONE.getPreferenceValue());
|
|
+ boolean isSensorExpected = !de.dennisguse.opentracks.sensors.driver.SensorType.NONE.getPreferenceValue().equals(address);
|
|
+ trackStatisticsUpdater.setForceZeroDistance(hasSensorBeenConnected || (isSensorExpected && !gpsOverriden));
|
|
+ }
|
|
+
|
|
if (trackPoint.type() == TrackPoint.Type.IDLE) {
|
|
insertTrackPoint(trackPoint, true);
|
|
handler.removeCallbacks(ON_IDLE);
|
|
@@ -200,18 +309,57 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
|
return true;
|
|
}
|
|
|
|
+ if (trackPoint.cadence() != null) {
|
|
+ hasCadenceSensorBeenConnected = true;
|
|
+ }
|
|
+
|
|
+ boolean isWalkingWithBike = false;
|
|
+ if (autoPauseOnCadence0 && hasCadenceSensorBeenConnected && autoPauseCadence0Speed != null) {
|
|
+ if (trackPoint.cadence() == null || trackPoint.cadence().getRPM() == 0) {
|
|
+ if (trackPoint.position().hasSpeed() && trackPoint.position().speed().lessThan(autoPauseCadence0Speed)) {
|
|
+ isWalkingWithBike = true;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
Distance distanceToLastStoredTrackPoint;
|
|
- if (trackPoint.position().hasLocation() && !lastStoredTrackPoint.position().hasLocation()) {
|
|
- distanceToLastStoredTrackPoint = trackPoint.distanceToPreviousFromLocation(lastStoredTrackPointWithLocation);
|
|
+ if (isWalkingWithBike) {
|
|
+ distanceToLastStoredTrackPoint = Distance.ZERO;
|
|
+ autoPauseStatus = "WALKING";
|
|
} else {
|
|
- distanceToLastStoredTrackPoint = trackPoint.distanceToPrevious(lastStoredTrackPoint);
|
|
+ if (trackPoint.position().hasLocation() && !lastStoredTrackPoint.position().hasLocation()) {
|
|
+ distanceToLastStoredTrackPoint = trackPoint.distanceToPreviousFromLocation(lastStoredTrackPointWithLocation);
|
|
+ } else {
|
|
+ distanceToLastStoredTrackPoint = trackPoint.distanceToPrevious(lastStoredTrackPoint);
|
|
+ }
|
|
}
|
|
|
|
if (distanceToLastStoredTrackPoint.greaterThan(maxRecordingDistance)) {
|
|
- trackPoint = trackPoint.with(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
|
+ // Check idle duration before creating SEGMENT_START_AUTOMATIC
|
|
+ boolean shouldCreateSegment = true;
|
|
+ if (idleStartedAt != null) {
|
|
+ java.time.Duration idleDuration = java.time.Duration.between(idleStartedAt, java.time.Instant.now());
|
|
+ java.time.Duration markerDuration = de.dennisguse.opentracks.settings.PreferencesUtils.getMarkerIdleDuration();
|
|
+ if (idleDuration.compareTo(markerDuration) < 0) {
|
|
+ shouldCreateSegment = false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (shouldCreateSegment) {
|
|
+ trackPoint = trackPoint.with(TrackPoint.Type.SEGMENT_START_AUTOMATIC);
|
|
+ idleObserver.onLongBreak();
|
|
+ }
|
|
insertTrackPoint(trackPoint, true);
|
|
|
|
scheduleNewIdleTimeout();
|
|
+ if (!isWalkingWithBike) {
|
|
+ autoPauseStatus = "REC";
|
|
+ } else {
|
|
+ if (!"WALKING".equals(autoPauseStatus)) {
|
|
+ walkingStartedAt = java.time.Instant.now();
|
|
+ }
|
|
+ autoPauseStatus = "WALKING";
|
|
+ }
|
|
return true;
|
|
}
|
|
|
|
@@ -219,6 +367,14 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
|
insertTrackPoint(trackPoint, false);
|
|
|
|
scheduleNewIdleTimeout();
|
|
+ if (!isWalkingWithBike) {
|
|
+ autoPauseStatus = "REC";
|
|
+ } else {
|
|
+ if (!"WALKING".equals(autoPauseStatus)) {
|
|
+ walkingStartedAt = java.time.Instant.now();
|
|
+ }
|
|
+ autoPauseStatus = "WALKING";
|
|
+ }
|
|
return true;
|
|
}
|
|
|
|
@@ -235,6 +391,7 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
|
}
|
|
handler.removeCallbacks(ON_IDLE);
|
|
handler.postDelayed(ON_IDLE, idleDuration.toMillis());
|
|
+ idleTimeoutScheduledAt = java.time.Instant.now().plus(idleDuration);
|
|
}
|
|
|
|
Statistics getTrackStatistics() {
|
|
@@ -284,6 +441,13 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
|
|
|
lastStoredTrackPoint = null;
|
|
lastStoredTrackPointWithLocation = null;
|
|
+
|
|
+ hasSensorBeenConnected = false;
|
|
+ isSensorDisconnectedWarningActive = false;
|
|
+ hasCadenceSensorBeenConnected = false;
|
|
+ if (sensorDisconnectObserver != null) {
|
|
+ sensorDisconnectObserver.onSensorDisconnectedWarning(false);
|
|
+ }
|
|
}
|
|
|
|
@Override
|
|
@@ -297,13 +461,39 @@ public class TrackRecordingManager implements SharedPreferences.OnSharedPreferen
|
|
if (PreferencesUtils.isKey(R.string.idle_duration_key, key)) {
|
|
idleDuration = PreferencesUtils.getIdleDurationTimeout();
|
|
}
|
|
+ if (PreferencesUtils.isKey(R.string.settings_recording_auto_pause_on_cadence_0_key, key)) {
|
|
+ autoPauseOnCadence0 = PreferencesUtils.shouldAutoPauseOnCadence0();
|
|
+ }
|
|
+ if (PreferencesUtils.isKey(R.string.settings_recording_auto_pause_on_cadence_0_speed_key, key)) {
|
|
+ autoPauseCadence0Speed = PreferencesUtils.getAutoPauseCadence0Speed();
|
|
+ }
|
|
}
|
|
|
|
public TrackPoint getLastStoredTrackPointWithLocation() {
|
|
return lastStoredTrackPointWithLocation;
|
|
}
|
|
|
|
+ public void setOverrideSensorWithGPS(boolean override) {
|
|
+ if (override) {
|
|
+ hasSensorBeenConnected = false;
|
|
+ gpsOverriden = true;
|
|
+ if (isSensorDisconnectedWarningActive) {
|
|
+ isSensorDisconnectedWarningActive = false;
|
|
+ sensorDisconnectObserver.onSensorDisconnectedWarning(false);
|
|
+ }
|
|
+ if (trackStatisticsUpdater != null) {
|
|
+ trackStatisticsUpdater.setForceZeroDistance(false);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
public interface IdleObserver {
|
|
void onIdle();
|
|
+ void onLongBreak();
|
|
+ }
|
|
+
|
|
+ public interface SensorDisconnectObserver {
|
|
+ void onSensorDisconnectedWarning(boolean show);
|
|
+ void onSensorConnected();
|
|
}
|
|
}
|
|
diff --git a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java
|
|
index 8ca4c5a4a..c28f3b224 100644
|
|
--- a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java
|
|
+++ b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java
|
|
@@ -44,7 +44,7 @@ import de.dennisguse.opentracks.services.announcement.VoiceAnnouncementManager;
|
|
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
|
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
|
|
|
-public class TrackRecordingService extends Service implements TrackPointCreator.Callback, SharedPreferences.OnSharedPreferenceChangeListener, TrackRecordingManager.IdleObserver {
|
|
+public class TrackRecordingService extends Service implements TrackPointCreator.Callback, SharedPreferences.OnSharedPreferenceChangeListener, TrackRecordingManager.IdleObserver, TrackRecordingManager.SensorDisconnectObserver {
|
|
|
|
private static final String TAG = TrackRecordingService.class.getSimpleName();
|
|
|
|
@@ -84,6 +84,7 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
|
private RecordingStatus recordingStatus;
|
|
private MutableLiveData<RecordingStatus> recordingStatusObservable;
|
|
private MutableLiveData<GpsStatusValue> gpsStatusObservable;
|
|
+ private MutableLiveData<Boolean> sensorDisconnectedWarningObservable;
|
|
private MutableLiveData<RecordingData> recordingDataObservable;
|
|
|
|
private Handler handler;
|
|
@@ -104,10 +105,11 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
|
recordingStatusObservable = new MutableLiveData<>();
|
|
updateRecordingStatus(STATUS_DEFAULT);
|
|
gpsStatusObservable = new MutableLiveData<>(STATUS_GPS_DEFAULT);
|
|
+ sensorDisconnectedWarningObservable = new MutableLiveData<>(false);
|
|
recordingDataObservable = new MutableLiveData<>(RecordingData.NOT_RECORDING);
|
|
|
|
trackPointCreator = new TrackPointCreator(this);
|
|
- trackRecordingManager = new TrackRecordingManager(this, trackPointCreator, this, handler);
|
|
+ trackRecordingManager = new TrackRecordingManager(this, trackPointCreator, this, this, handler);
|
|
|
|
voiceAnnouncementManager = new VoiceAnnouncementManager(this);
|
|
notificationManager = new TrackRecordingServiceNotificationManager(this);
|
|
@@ -138,6 +140,7 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
|
|
|
recordingStatusObservable = null;
|
|
gpsStatusObservable = null;
|
|
+ sensorDisconnectedWarningObservable = null;
|
|
recordingDataObservable = null;
|
|
|
|
Log.d(TAG, "Destroyed");
|
|
@@ -315,6 +318,10 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
|
return gpsStatusObservable;
|
|
}
|
|
|
|
+ public LiveData<Boolean> getSensorDisconnectedWarningObservable() {
|
|
+ return sensorDisconnectedWarningObservable;
|
|
+ }
|
|
+
|
|
public LiveData<RecordingData> getRecordingDataObservable() {
|
|
return recordingDataObservable;
|
|
}
|
|
@@ -337,6 +344,32 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
|
voiceAnnouncementManager.announceIdle();
|
|
}
|
|
|
|
+ @Override
|
|
+ public void onLongBreak() {
|
|
+ voiceAnnouncementManager.announceLongBreak();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onSensorDisconnectedWarning(boolean show) {
|
|
+ if (show) {
|
|
+ voiceAnnouncementManager.announceSensorDisconnected();
|
|
+ }
|
|
+ if (sensorDisconnectedWarningObservable != null) {
|
|
+ sensorDisconnectedWarningObservable.postValue(show);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void onSensorConnected() {
|
|
+ voiceAnnouncementManager.announceSensorConnected();
|
|
+ }
|
|
+
|
|
+ public void setOverrideSensorWithGPS(boolean override) {
|
|
+ if (trackRecordingManager != null) {
|
|
+ trackRecordingManager.setOverrideSensorWithGPS(override);
|
|
+ }
|
|
+ }
|
|
+
|
|
@VisibleForTesting
|
|
public void stopUpdateRecordingData() {
|
|
handler.removeCallbacks(updateRecordingData);
|
|
diff --git a/src/main/java/de/dennisguse/opentracks/services/announcement/VoiceAnnouncementManager.java b/src/main/java/de/dennisguse/opentracks/services/announcement/VoiceAnnouncementManager.java
|
|
index af2e5b651..1c3988884 100644
|
|
--- a/src/main/java/de/dennisguse/opentracks/services/announcement/VoiceAnnouncementManager.java
|
|
+++ b/src/main/java/de/dennisguse/opentracks/services/announcement/VoiceAnnouncementManager.java
|
|
@@ -119,6 +119,30 @@ public class VoiceAnnouncementManager implements SharedPreferences.OnSharedPrefe
|
|
voiceAnnouncement.speak(VoiceAnnouncementUtils.createIdle(context));
|
|
}
|
|
|
|
+ public void announceLongBreak() {
|
|
+ if (shouldNotAnnounce() || !PreferencesUtils.shouldVoiceAnnouncementLongBreak()) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ voiceAnnouncement.speak(VoiceAnnouncementUtils.createLongBreak(context));
|
|
+ }
|
|
+
|
|
+ public void announceSensorDisconnected() {
|
|
+ if (shouldNotAnnounce() || !PreferencesUtils.shouldVoiceAnnouncementSensorDisconnected()) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ voiceAnnouncement.speak(VoiceAnnouncementUtils.createSensorDisconnected(context));
|
|
+ }
|
|
+
|
|
+ public void announceSensorConnected() {
|
|
+ if (shouldNotAnnounce() || !PreferencesUtils.shouldVoiceAnnouncementSensorConnected()) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ voiceAnnouncement.speak(VoiceAnnouncementUtils.createSensorConnected(context));
|
|
+ }
|
|
+
|
|
public void announceStatisticsIfNeeded(@NonNull RecordingData data) {
|
|
if (shouldNotAnnounce()) {
|
|
return;
|
|
diff --git a/src/main/java/de/dennisguse/opentracks/services/announcement/VoiceAnnouncementUtils.java b/src/main/java/de/dennisguse/opentracks/services/announcement/VoiceAnnouncementUtils.java
|
|
index bbce637e9..3fe16ce73 100644
|
|
--- a/src/main/java/de/dennisguse/opentracks/services/announcement/VoiceAnnouncementUtils.java
|
|
+++ b/src/main/java/de/dennisguse/opentracks/services/announcement/VoiceAnnouncementUtils.java
|
|
@@ -50,6 +50,21 @@ class VoiceAnnouncementUtils {
|
|
.append(context.getString(R.string.voiceIdle));
|
|
}
|
|
|
|
+ static Spannable createLongBreak(Context context) {
|
|
+ return new SpannableStringBuilder()
|
|
+ .append(context.getString(R.string.voiceLongBreak));
|
|
+ }
|
|
+
|
|
+ static Spannable createSensorDisconnected(Context context) {
|
|
+ return new SpannableStringBuilder()
|
|
+ .append(context.getString(R.string.voiceSensorDisconnected));
|
|
+ }
|
|
+
|
|
+ static Spannable createSensorConnected(Context context) {
|
|
+ return new SpannableStringBuilder()
|
|
+ .append(context.getString(R.string.voiceSensorConnected));
|
|
+ }
|
|
+
|
|
static Spannable createStatistics(Context context, Track track, SensorDataSet sensorDataSet, UnitSystem unitSystem, boolean isReportSpeed, @Nullable Statistics currentInterval, @Nullable SensorStatistics sensorStatistics) {
|
|
Statistics trackStatistics = track.statistics();
|
|
|
|
diff --git a/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java b/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java
|
|
index 696379cf0..8e449c65b 100644
|
|
--- a/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java
|
|
+++ b/src/main/java/de/dennisguse/opentracks/services/handlers/TrackPointCreator.java
|
|
@@ -101,8 +101,11 @@ public class TrackPointCreator implements SharedPreferences.OnSharedPreferenceCh
|
|
position = position.withCoordinates(lastStoredTrackPointWithLocation.position());
|
|
}
|
|
|
|
- if (lastTrackPointUISpeed != null)
|
|
+ if (sensorDataSet.speed() != null) {
|
|
+ position = position.with(sensorDataSet.speed().data());
|
|
+ } else if (lastTrackPointUISpeed != null) {
|
|
position = position.with(lastTrackPointUISpeed.position().speed());
|
|
+ }
|
|
|
|
if (lastTrackPointUIAltitude != null)
|
|
position = position.with(lastTrackPointUIAltitude.position().altitude());
|
|
diff --git a/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java b/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java
|
|
index 2ef449f90..2d346cd9f 100644
|
|
--- a/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java
|
|
+++ b/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java
|
|
@@ -38,11 +38,13 @@ import java.util.Arrays;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.Optional;
|
|
+import java.util.stream.Collectors;
|
|
|
|
import de.dennisguse.opentracks.R;
|
|
import de.dennisguse.opentracks.data.models.ActivityType;
|
|
import de.dennisguse.opentracks.data.models.Distance;
|
|
import de.dennisguse.opentracks.data.models.DistanceFormatter;
|
|
+import de.dennisguse.opentracks.data.models.Speed;
|
|
import de.dennisguse.opentracks.data.models.HeartRate;
|
|
import de.dennisguse.opentracks.data.models.HeartRateZones;
|
|
import de.dennisguse.opentracks.data.models.Track;
|
|
@@ -249,6 +251,10 @@ public class PreferencesUtils {
|
|
setString(R.string.stats_units_key, unitSystem.getPreferenceId());
|
|
}
|
|
|
|
+ public static Duration getMarkerIdleDuration() {
|
|
+ return Duration.ofMinutes(getInt(R.string.marker_idle_duration_key, 3));
|
|
+ }
|
|
+
|
|
//TODO Check if actually needed or can be superseeded by a flexible default in getUnit()
|
|
public static void applyDefaultUnit() {
|
|
if (getString(R.string.stats_units_key, "").isEmpty()) {
|
|
@@ -342,6 +348,18 @@ public class PreferencesUtils {
|
|
return getBoolean(R.string.voice_announce_idle_key, true);
|
|
}
|
|
|
|
+ public static boolean shouldVoiceAnnouncementLongBreak() {
|
|
+ return getBoolean(R.string.voice_announce_long_break_key, true);
|
|
+ }
|
|
+
|
|
+ public static boolean shouldVoiceAnnouncementSensorDisconnected() {
|
|
+ return getBoolean(R.string.voice_announce_sensor_disconnected_key, true);
|
|
+ }
|
|
+
|
|
+ public static boolean shouldVoiceAnnouncementSensorConnected() {
|
|
+ return getBoolean(R.string.voice_announce_sensor_connected_key, true);
|
|
+ }
|
|
+
|
|
public static boolean shouldVoiceAnnounceTime() {
|
|
return getBoolean(R.string.voice_announce_time_key, true);
|
|
}
|
|
@@ -682,6 +700,51 @@ public class PreferencesUtils {
|
|
return entries;
|
|
}
|
|
|
|
+ static String[] getMarkerIdleDurationEntries() {
|
|
+ String[] entryValues = resources.getStringArray(R.array.marker_idle_duration_values);
|
|
+ String[] entries = new String[entryValues.length];
|
|
+
|
|
+ final int defaultDuration = Integer.parseInt(resources.getString(R.string.marker_idle_duration_default));
|
|
+
|
|
+ for (int i = 0; i < entryValues.length; i++) {
|
|
+ int value = Integer.parseInt(entryValues[i]);
|
|
+
|
|
+ if (value == defaultDuration) {
|
|
+ entries[i] = resources.getString(R.string.value_integer_minute_recommended, value);
|
|
+ } else {
|
|
+ entries[i] = resources.getString(R.string.value_integer_minute, value);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return entries;
|
|
+ }
|
|
+
|
|
+ public static boolean shouldAutoPauseOnCadence0() {
|
|
+ return getBoolean(R.string.settings_recording_auto_pause_on_cadence_0_key, false);
|
|
+ }
|
|
+
|
|
+ public static boolean shouldPreventIdleIfCadence() {
|
|
+ return getBoolean(R.string.settings_recording_prevent_idle_if_cadence_key, true);
|
|
+ }
|
|
+
|
|
+ public static Speed getAutoPauseCadence0Speed() {
|
|
+ final int DEFAULT = Integer.parseInt(resources.getString(R.string.auto_pause_cadence_0_speed_default));
|
|
+ int value = getInt(R.string.settings_recording_auto_pause_on_cadence_0_speed_key, DEFAULT);
|
|
+ return Speed.ofKMH(value);
|
|
+ }
|
|
+
|
|
+ static String[] getAutoPauseCadence0SpeedEntries() {
|
|
+ String[] entryValues = resources.getStringArray(R.array.auto_pause_cadence_0_speed_values);
|
|
+ String[] entries = new String[entryValues.length];
|
|
+
|
|
+ for (int i = 0; i < entryValues.length; i++) {
|
|
+ int value = Integer.parseInt(entryValues[i]);
|
|
+ entries[i] = value + " km/h";
|
|
+ }
|
|
+
|
|
+ return entries;
|
|
+ }
|
|
+
|
|
public static boolean shouldInstantExportToFile() {
|
|
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();
|
|
@@ -839,7 +902,14 @@ public class PreferencesUtils {
|
|
new DataField(resources.getString(R.string.stats_custom_layout_cadence_key), false, false, false),
|
|
new DataField(resources.getString(R.string.stats_custom_layout_power_key), false, false, false),
|
|
new DataField(resources.getString(R.string.stats_custom_layout_clock_key), false, false, false),
|
|
- new DataField(resources.getString(R.string.stats_custom_layout_temperature_key), false, false, false)
|
|
+ new DataField(resources.getString(R.string.stats_custom_layout_temperature_key), false, false, false),
|
|
+ new DataField(resources.getString(R.string.stats_custom_layout_auto_pause_status_key), false, false, false),
|
|
+ new DataField(resources.getString(R.string.stats_custom_layout_sensor_status_hr_key), false, false, false),
|
|
+ new DataField(resources.getString(R.string.stats_custom_layout_sensor_status_cadence_key), false, false, false),
|
|
+ new DataField(resources.getString(R.string.stats_custom_layout_sensor_status_speed_key), false, false, false),
|
|
+ new DataField(resources.getString(R.string.stats_custom_layout_debug_idle_timer_key), false, false, false),
|
|
+ new DataField(resources.getString(R.string.stats_custom_layout_debug_walking_timer_key), false, false, false),
|
|
+ new DataField(resources.getString(R.string.stats_custom_layout_debug_marker_timer_key), false, false, false)
|
|
);
|
|
|
|
RecordingLayout defaultLayout = new RecordingLayout(resources.getString(R.string.stats_custom_layout_default_layout), 2);
|
|
@@ -855,7 +925,7 @@ public class PreferencesUtils {
|
|
String selectedLayout = getSelectedLayoutName();
|
|
String csvCustomLayouts = getString(R.string.stats_custom_layouts_key, buildDefaultLayout());
|
|
|
|
- List<RecordingLayout> recordingLayouts = RecordingLayoutIO.fromCsv(csvCustomLayouts, resources);
|
|
+ List<RecordingLayout> recordingLayouts = getAllCustomLayouts();
|
|
|
|
RecordingLayout fallback = !recordingLayouts.isEmpty() ? recordingLayouts.get(0) : getDefaultLayout();
|
|
|
|
@@ -901,7 +971,18 @@ public class PreferencesUtils {
|
|
public static List<RecordingLayout> getAllCustomLayouts() {
|
|
String csvCustomLayouts = getString(R.string.stats_custom_layouts_key, buildDefaultLayout());
|
|
|
|
- return RecordingLayoutIO.fromCsv(csvCustomLayouts, resources);
|
|
+ List<RecordingLayout> layouts = RecordingLayoutIO.fromCsv(csvCustomLayouts, resources);
|
|
+ List<DataField> defaultFields = getDefaultLayout().getFields();
|
|
+
|
|
+ for (RecordingLayout layout : layouts) {
|
|
+ for (DataField df : defaultFields) {
|
|
+ boolean hasField = layout.getFields().stream().anyMatch(f -> f.getKey().equals(df.getKey()));
|
|
+ if (!hasField) {
|
|
+ layout.addField(new DataField(df.getKey(), false, false, df.isWide()));
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return layouts;
|
|
}
|
|
|
|
public static void resetCustomLayoutPreferences() {
|
|
diff --git a/src/main/java/de/dennisguse/opentracks/settings/RecordingSettingsFragment.java b/src/main/java/de/dennisguse/opentracks/settings/RecordingSettingsFragment.java
|
|
index 7408de78f..71ac834de 100644
|
|
--- a/src/main/java/de/dennisguse/opentracks/settings/RecordingSettingsFragment.java
|
|
+++ b/src/main/java/de/dennisguse/opentracks/settings/RecordingSettingsFragment.java
|
|
@@ -57,5 +57,15 @@ public class RecordingSettingsFragment extends PreferenceFragmentCompat {
|
|
|
|
ListPreference idleDuration = findPreference(getString(R.string.idle_duration_key));
|
|
idleDuration.setEntries(PreferencesUtils.getIdleDurationEntries());
|
|
+
|
|
+ ListPreference autoPauseCadence0Speed = findPreference(getString(R.string.settings_recording_auto_pause_on_cadence_0_speed_key));
|
|
+ if (autoPauseCadence0Speed != null) {
|
|
+ autoPauseCadence0Speed.setEntries(PreferencesUtils.getAutoPauseCadence0SpeedEntries());
|
|
+ }
|
|
+
|
|
+ ListPreference markerIdleDuration = findPreference(getString(R.string.marker_idle_duration_key));
|
|
+ if (markerIdleDuration != null) {
|
|
+ markerIdleDuration.setEntries(PreferencesUtils.getMarkerIdleDurationEntries());
|
|
+ }
|
|
}
|
|
}
|
|
diff --git a/src/main/java/de/dennisguse/opentracks/ui/aggregatedStatistics/FilterDialogFragment.java b/src/main/java/de/dennisguse/opentracks/ui/aggregatedStatistics/FilterDialogFragment.java
|
|
index 5241e7c97..19d78072a 100644
|
|
--- a/src/main/java/de/dennisguse/opentracks/ui/aggregatedStatistics/FilterDialogFragment.java
|
|
+++ b/src/main/java/de/dennisguse/opentracks/ui/aggregatedStatistics/FilterDialogFragment.java
|
|
@@ -136,12 +136,6 @@ public class FilterDialogFragment extends DialogFragment {
|
|
public final String value;
|
|
public boolean isChecked;
|
|
|
|
- public FilterItem(String id, String value) {
|
|
- this.id = id;
|
|
- this.value = value;
|
|
- this.isChecked = true;
|
|
- }
|
|
-
|
|
public FilterItem(String id, String value, boolean isChecked) {
|
|
this.id = id;
|
|
this.value = value;
|
|
diff --git a/src/main/java/de/dennisguse/opentracks/ui/customRecordingLayout/RecordingLayoutIO.java b/src/main/java/de/dennisguse/opentracks/ui/customRecordingLayout/RecordingLayoutIO.java
|
|
index 0ebb5f0f8..7b8c31958 100644
|
|
--- a/src/main/java/de/dennisguse/opentracks/ui/customRecordingLayout/RecordingLayoutIO.java
|
|
+++ b/src/main/java/de/dennisguse/opentracks/ui/customRecordingLayout/RecordingLayoutIO.java
|
|
@@ -40,6 +40,13 @@ public class RecordingLayoutIO {
|
|
Log.e(TAG, "Invalid CSV layout. It shouldn't happen: " + csvLine);
|
|
return recordingLayout;
|
|
}
|
|
+
|
|
+ String key = fieldParts[0];
|
|
+ if (key.equals(resources.getString(R.string.stats_custom_layout_sensor_status_key))) {
|
|
+ // Ignore deprecated sensor_status key to safely remove it from user's custom layout
|
|
+ continue;
|
|
+ }
|
|
+
|
|
recordingLayout.addField(fromCSV(fieldParts, resources));
|
|
}
|
|
return recordingLayout;
|
|
diff --git a/src/main/java/de/dennisguse/opentracks/ui/customRecordingLayout/SettingsCustomLayoutEditAdapter.java b/src/main/java/de/dennisguse/opentracks/ui/customRecordingLayout/SettingsCustomLayoutEditAdapter.java
|
|
index 3d62ca203..841324b84 100644
|
|
--- a/src/main/java/de/dennisguse/opentracks/ui/customRecordingLayout/SettingsCustomLayoutEditAdapter.java
|
|
+++ b/src/main/java/de/dennisguse/opentracks/ui/customRecordingLayout/SettingsCustomLayoutEditAdapter.java
|
|
@@ -77,7 +77,7 @@ public class SettingsCustomLayoutEditAdapter extends RecyclerView.Adapter<Recycl
|
|
Speed.ZERO
|
|
);
|
|
|
|
- demoData = new RecordingData(track, new TrackPoint(TrackPoint.Type.TRACKPOINT, position), null, statistics);
|
|
+ demoData = new RecordingData(track, new TrackPoint(TrackPoint.Type.TRACKPOINT, position), null, statistics, "", "", "", "");
|
|
}
|
|
|
|
private RecordingLayout recordingLayout;
|
|
diff --git a/src/main/java/de/dennisguse/opentracks/util/FileUtils.java b/src/main/java/de/dennisguse/opentracks/util/FileUtils.java
|
|
index 40f9e966d..b18272129 100644
|
|
--- a/src/main/java/de/dennisguse/opentracks/util/FileUtils.java
|
|
+++ b/src/main/java/de/dennisguse/opentracks/util/FileUtils.java
|
|
@@ -104,10 +104,6 @@ public class FileUtils {
|
|
return fileName.substring(index + 1);
|
|
}
|
|
|
|
- public static String getExtension(DocumentFile file) {
|
|
- return getExtension(file.getName());
|
|
- }
|
|
-
|
|
public static String getExtension(Uri uri) {
|
|
return getExtension(uri.toString());
|
|
}
|
|
diff --git a/src/main/java/de/dennisguse/opentracks/util/IntentUtils.java b/src/main/java/de/dennisguse/opentracks/util/IntentUtils.java
|
|
index f0468c62d..0e6cf9d0f 100644
|
|
--- a/src/main/java/de/dennisguse/opentracks/util/IntentUtils.java
|
|
+++ b/src/main/java/de/dennisguse/opentracks/util/IntentUtils.java
|
|
@@ -36,8 +36,6 @@ public class IntentUtils {
|
|
|
|
private static final String TAG = IntentUtils.class.getSimpleName();
|
|
|
|
- private static final String JPEG_EXTENSION = "jpeg";
|
|
-
|
|
private IntentUtils() {
|
|
}
|
|
|
|
diff --git a/src/main/java/de/dennisguse/opentracks/viewmodels/GenericStatisticsViewHolder.java b/src/main/java/de/dennisguse/opentracks/viewmodels/GenericStatisticsViewHolder.java
|
|
index 8193dba41..ee08ed501 100644
|
|
--- a/src/main/java/de/dennisguse/opentracks/viewmodels/GenericStatisticsViewHolder.java
|
|
+++ b/src/main/java/de/dennisguse/opentracks/viewmodels/GenericStatisticsViewHolder.java
|
|
@@ -288,4 +288,84 @@ public abstract class GenericStatisticsViewHolder extends StatisticViewHolder<St
|
|
getBinding().statsDescriptionMain.setText(R.string.stats_coordinates);
|
|
}
|
|
}
|
|
+
|
|
+ public static class AutoPauseStatus extends GenericStatisticsViewHolder {
|
|
+
|
|
+ @Override
|
|
+ public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
|
+ String value = data.autoPauseStatus();
|
|
+ if (value == null || value.isEmpty()) {
|
|
+ value = "N/A";
|
|
+ }
|
|
+ getBinding().statsValue.setText(value);
|
|
+ getBinding().statsUnit.setText("");
|
|
+ getBinding().statsDescriptionMain.setText(getContext().getString(R.string.stats_auto_pause_status));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static class SensorStatusHR extends GenericStatisticsViewHolder {
|
|
+ @Override
|
|
+ public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
|
+ String status = "Brak";
|
|
+ if (data.sensorDataSet() != null && data.sensorDataSet().heartRate() != null) {
|
|
+ status = "OK";
|
|
+ }
|
|
+ getBinding().statsValue.setText(status);
|
|
+ getBinding().statsUnit.setText("");
|
|
+ getBinding().statsDescriptionMain.setText(getContext().getString(R.string.stats_sensor_status_hr));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static class SensorStatusCadence extends GenericStatisticsViewHolder {
|
|
+ @Override
|
|
+ public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
|
+ String status = "Brak";
|
|
+ if (data.sensorDataSet() != null && data.sensorDataSet().cadence() != null) {
|
|
+ status = "OK";
|
|
+ }
|
|
+ getBinding().statsValue.setText(status);
|
|
+ getBinding().statsUnit.setText("");
|
|
+ getBinding().statsDescriptionMain.setText(getContext().getString(R.string.stats_sensor_status_cadence));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static class SensorStatusSpeed extends GenericStatisticsViewHolder {
|
|
+ @Override
|
|
+ public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
|
+ String status = "Brak";
|
|
+ if (data.sensorDataSet() != null && data.sensorDataSet().speed() != null) {
|
|
+ status = "OK";
|
|
+ }
|
|
+ getBinding().statsValue.setText(status);
|
|
+ getBinding().statsUnit.setText("");
|
|
+ getBinding().statsDescriptionMain.setText(getContext().getString(R.string.stats_sensor_status_speed));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static class DebugIdleTimer extends GenericStatisticsViewHolder {
|
|
+ @Override
|
|
+ public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
|
+ getBinding().statsValue.setText(data.idleTimer().isEmpty() ? "---" : data.idleTimer());
|
|
+ getBinding().statsUnit.setText("");
|
|
+ getBinding().statsDescriptionMain.setText(getContext().getString(R.string.stats_debug_idle_timer));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static class DebugWalkingTimer extends GenericStatisticsViewHolder {
|
|
+ @Override
|
|
+ public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
|
+ getBinding().statsValue.setText(data.walkingTimer().isEmpty() ? "---" : data.walkingTimer());
|
|
+ getBinding().statsUnit.setText("");
|
|
+ getBinding().statsDescriptionMain.setText(getContext().getString(R.string.stats_debug_walking_timer));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ public static class DebugMarkerTimer extends GenericStatisticsViewHolder {
|
|
+ @Override
|
|
+ public void onChanged(UnitSystem unitSystem, RecordingData data) {
|
|
+ getBinding().statsValue.setText(data.markerTimer().isEmpty() ? "---" : data.markerTimer());
|
|
+ getBinding().statsUnit.setText("");
|
|
+ getBinding().statsDescriptionMain.setText(getContext().getString(R.string.stats_debug_marker_timer));
|
|
+ }
|
|
+ }
|
|
}
|
|
diff --git a/src/main/java/de/dennisguse/opentracks/viewmodels/Mapping.java b/src/main/java/de/dennisguse/opentracks/viewmodels/Mapping.java
|
|
index 822a745e0..f3f4e5c8f 100644
|
|
--- a/src/main/java/de/dennisguse/opentracks/viewmodels/Mapping.java
|
|
+++ b/src/main/java/de/dennisguse/opentracks/viewmodels/Mapping.java
|
|
@@ -38,6 +38,13 @@ public class Mapping {
|
|
m.put(context.getString(R.string.stats_custom_layout_cadence_key), SensorStatisticsViewHolder.SensorCadence::new);
|
|
m.put(context.getString(R.string.stats_custom_layout_power_key), SensorStatisticsViewHolder.SensorPower::new);
|
|
m.put(context.getString(R.string.stats_custom_layout_clock_key), ClockViewHolder::new);
|
|
+ m.put(context.getString(R.string.stats_custom_layout_auto_pause_status_key), GenericStatisticsViewHolder.AutoPauseStatus::new);
|
|
+ m.put(context.getString(R.string.stats_custom_layout_sensor_status_hr_key), GenericStatisticsViewHolder.SensorStatusHR::new);
|
|
+ m.put(context.getString(R.string.stats_custom_layout_sensor_status_cadence_key), GenericStatisticsViewHolder.SensorStatusCadence::new);
|
|
+ m.put(context.getString(R.string.stats_custom_layout_sensor_status_speed_key), GenericStatisticsViewHolder.SensorStatusSpeed::new);
|
|
+ m.put(context.getString(R.string.stats_custom_layout_debug_idle_timer_key), GenericStatisticsViewHolder.DebugIdleTimer::new);
|
|
+ m.put(context.getString(R.string.stats_custom_layout_debug_walking_timer_key), GenericStatisticsViewHolder.DebugWalkingTimer::new);
|
|
+ m.put(context.getString(R.string.stats_custom_layout_debug_marker_timer_key), GenericStatisticsViewHolder.DebugMarkerTimer::new);
|
|
|
|
return m;
|
|
}
|
|
diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml
|
|
index 4c5a554b4..5765d6a12 100644
|
|
--- a/src/main/res/values-pl/strings.xml
|
|
+++ b/src/main/res/values-pl/strings.xml
|
|
@@ -197,6 +197,7 @@ limitations under the License.
|
|
<string name="settings_recording_max_recording_distance_summary">Nowy segment jest tworzony, jeśli odległość między dwiema lokalizacjami jest większa niż %1$s</string>
|
|
<string name="settings_recording_max_recording_distance_title">Maksymalna odległość</string>
|
|
<string name="settings_recording_min_recording_distance_title">Rejestrowanie odcinka</string>
|
|
+ <string name="settings_recording_marker_idle_duration_title">Minimalny czas długiej przerwy</string>
|
|
<string name="settings_recording_min_recording_interval_title">Rejestrowanie interwału</string>
|
|
<string name="settings_recording_min_required_accuracy_title">Dokładność GPS-u</string>
|
|
<string name="settings_recording_track_name_date_iso_8601_option">Data (ISO 8601)</string>
|
|
@@ -281,6 +282,7 @@ limitations under the License.
|
|
<string name="value_integer_meter_recommended">%1$d m (zalecane)</string>
|
|
<string name="value_integer_mile_hour">%1$d mil/h</string>
|
|
<string name="value_integer_minute">%1$d min</string>
|
|
+ <string name="value_integer_minute_recommended">%1$d min (zalecane)</string>
|
|
<string name="value_integer_second">%1$d s</string>
|
|
<string name="value_none">Brak</string>
|
|
<string name="value_off">Wyłącz</string>
|
|
@@ -443,6 +445,8 @@ limitations under the License.
|
|
<string name="settings_announcements_totaltime_frequency">Przedział czasu</string>
|
|
<string name="stats_sensors_heart_rate">Tętno</string>
|
|
<string name="stats_clock">Zegar</string>
|
|
+ <string name="stats_auto_pause_status">Status auto-pauzy</string>
|
|
+ <string name="stats_sensor_status">Status czujników</string>
|
|
<string name="settings_announcements_distance_frequency">Długość okrążenia</string>
|
|
<string name="settings_layout_reset_confirm_title">Zresetować twoje układy\?</string>
|
|
<string name="settings_cycling_sensor">Kolarstwo</string>
|
|
@@ -523,6 +527,10 @@ limitations under the License.
|
|
<string name="settings_announcements_lap_speed_pace">Tempo/prędkość okrążenia</string>
|
|
<string name="track_delete_number_of_tracks">%1$d ścieżki</string>
|
|
<string name="settings_announcements_idle_title">Przerwa</string>
|
|
+ <string name="settings_announcements_long_break_title">Długa przerwa (filiżanka)</string>
|
|
+ <string name="settings_announcements_sensor_category_title">Czujniki</string>
|
|
+ <string name="settings_announcements_sensor_disconnected_title">Odłączono czujnik</string>
|
|
+ <string name="settings_announcements_sensor_connected_title">Podłączono czujnik</string>
|
|
<string name="settings_recording_title">Nagrywanie</string>
|
|
<string name="settings_theme_switch_restart">Zmiana motywu może wymagać ręcznego restartu.</string>
|
|
<string name="settings_ui_dynamic_colors_summary">Użyj ręcznie zdefiniowanych Dynamicznych Kolorów (tylko Android 12+; wymaga ręcznego restartu)</string>
|
|
@@ -585,4 +593,15 @@ limitations under the License.
|
|
<string name="show_on_dashboard">API panelu OpenTracks</string>
|
|
<string name="marker_detail_title">%1$d z %2$d: %3$s</string>
|
|
<string name="voice_on_device_speaker_title">Użyj wbudowanego głośnika</string>
|
|
+ <string name="sensor_disconnected_warning">Czujnik rozłączony. Kontynuować z GPS?</string>
|
|
+ <string name="sensor_disconnected_switch_action">Przełącz</string>
|
|
+ <string name="settings_recording_auto_pause_on_cadence_0_title">Zatrzymaj przy prowadzeniu roweru</string>
|
|
+ <string name="settings_recording_auto_pause_on_cadence_0_summary">Uruchom autopauzę jeżeli kadencja wynosi 0 a prędkość wskazuje na chód</string>
|
|
+ <string name="settings_recording_auto_pause_on_cadence_0_speed_title">Maksymalna prędkość prowadzenia</string>
|
|
+ <string name="settings_recording_auto_pause_on_cadence_0_speed_summary">Prędkość poniżej której wykrywane jest prowadzenie roweru (gdy kadencja to 0)</string>
|
|
+ <string name="settings_recording_prevent_idle_if_cadence_title">Zapobiegaj pauzie podczas pedałowania</string>
|
|
+ <string name="settings_recording_prevent_idle_if_cadence_summary">Nie włączaj trybu postoju jeżeli pedałujesz, nawet przy braku ruchu</string>
|
|
+ <string name="voiceLongBreak">Rozpoczęto długą przerwę.</string>
|
|
+ <string name="voiceSensorDisconnected">Odłączono czujnik.</string>
|
|
+ <string name="voiceSensorConnected">Podłączono czujnik.</string>
|
|
</resources>
|
|
diff --git a/src/main/res/values/settings.xml b/src/main/res/values/settings.xml
|
|
index 5d9c2038e..3c1323a95 100644
|
|
--- a/src/main/res/values/settings.xml
|
|
+++ b/src/main/res/values/settings.xml
|
|
@@ -128,6 +128,14 @@
|
|
<string name="idle_duration_key" translatable="false">idleSpeedDuration</string>
|
|
<string name="idle_duration_default" translatable="false">10</string>
|
|
<string name="idle_duration_off" translatable="false">0</string>
|
|
+ <string-array name="auto_pause_cadence_0_speed_values">
|
|
+ <item>4</item>
|
|
+ <item>6</item>
|
|
+ <item>8</item>
|
|
+ <item>10</item>
|
|
+ <item>12</item>
|
|
+ </string-array>
|
|
+
|
|
<string-array name="idle_duration_values">
|
|
<item>@string/idle_duration_off</item>
|
|
<item>5</item>
|
|
@@ -139,6 +147,17 @@
|
|
<item>120</item>
|
|
</string-array>
|
|
|
|
+ <string name="marker_idle_duration_default" translatable="false">3</string>
|
|
+ <string-array name="marker_idle_duration_values">
|
|
+ <item>1</item>
|
|
+ <item>2</item>
|
|
+ <item>@string/marker_idle_duration_default</item>
|
|
+ <item>5</item>
|
|
+ <item>10</item>
|
|
+ <item>15</item>
|
|
+ <item>30</item>
|
|
+ </string-array>
|
|
+
|
|
<string name="stats_rate_key" translatable="false">statsRate</string>
|
|
<string name="stats_rate_default" translatable="false">@string/stats_rate_speed_or_pace_default</string>
|
|
<string-array name="stats_rate_metric_options">
|
|
@@ -243,6 +262,12 @@
|
|
<bool name="voice_on_device_speaker_default" translatable="false">true</bool>
|
|
<string name="voice_announce_idle_key" translatable="false">voice_announce_idle_key</string>
|
|
<bool name="voice_announce_idle_default" translatable="false">false</bool>
|
|
+ <string name="voice_announce_long_break_key" translatable="false">voice_announce_long_break_key</string>
|
|
+ <bool name="voice_announce_long_break_default" translatable="false">true</bool>
|
|
+ <string name="voice_announce_sensor_disconnected_key" translatable="false">voice_announce_sensor_disconnected_key</string>
|
|
+ <bool name="voice_announce_sensor_disconnected_default" translatable="false">true</bool>
|
|
+ <string name="voice_announce_sensor_connected_key" translatable="false">voice_announce_sensor_connected_key</string>
|
|
+ <bool name="voice_announce_sensor_connected_default" translatable="false">true</bool>
|
|
|
|
<string name="voice_announce_total_distance_key" translatable="false">voiceAnnounceTotalDistance</string>
|
|
<bool name="voice_announce_total_distance_default" translatable="false">true</bool>
|
|
@@ -379,6 +404,14 @@
|
|
<string name="stats_custom_layout_average_cadence_key" translatable="false">average_cadence</string>
|
|
<string name="stats_custom_layout_power_key" translatable="false">power</string>
|
|
<string name="stats_custom_layout_clock_key" translatable="false">time</string>
|
|
+ <string name="stats_custom_layout_auto_pause_status_key" translatable="false">auto_pause_status</string>
|
|
+ <string name="stats_custom_layout_sensor_status_key" translatable="false">sensor_status</string>
|
|
+ <string name="stats_custom_layout_sensor_status_hr_key" translatable="false">sensor_status_hr</string>
|
|
+ <string name="stats_custom_layout_sensor_status_cadence_key" translatable="false">sensor_status_cadence</string>
|
|
+ <string name="stats_custom_layout_sensor_status_speed_key" translatable="false">sensor_status_speed</string>
|
|
+ <string name="stats_custom_layout_debug_idle_timer_key" translatable="false">debug_idle_timer</string>
|
|
+ <string name="stats_custom_layout_debug_walking_timer_key" translatable="false">debug_walking_timer</string>
|
|
+ <string name="stats_custom_layout_debug_marker_timer_key" translatable="false">debug_marker_timer</string>
|
|
|
|
<!-- Stats empty values -->
|
|
<string name="stats_empty_value_integer" translatable="false">0</string>
|
|
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
|
|
index c36234e09..dc51c72ef 100644
|
|
--- a/src/main/res/values/strings.xml
|
|
+++ b/src/main/res/values/strings.xml
|
|
@@ -331,6 +331,10 @@ limitations under the License.
|
|
<string name="settings_sensors_summary">Cycling Sensors, Wheel Circumference, Running Sensors</string>
|
|
<string name="settings_announcements_idle_category_title">Idle Announcements</string>
|
|
<string name="settings_announcements_idle_title">Idle</string>
|
|
+ <string name="settings_announcements_long_break_title">Long break</string>
|
|
+ <string name="settings_announcements_sensor_category_title">Sensors</string>
|
|
+ <string name="settings_announcements_sensor_disconnected_title">Sensor disconnected</string>
|
|
+ <string name="settings_announcements_sensor_connected_title">Sensor connected</string>
|
|
<string name="settings_announcements_statistics_title">Statistics Announcements</string>
|
|
<string name="settings_announcements_summary">Time, Distance, Voice Speed</string>
|
|
<string name="settings_import_export_title">Import and Export</string>
|
|
@@ -369,7 +373,9 @@ limitations under the License.
|
|
<string name="settings_recording_max_recording_distance_summary">A new segment is created if the distance between two locations is greater than %1$s</string>
|
|
<string name="settings_recording_max_recording_distance_title">Max distance</string>
|
|
<string name="settings_recording_min_recording_distance_title">Recording distance interval</string>
|
|
- <string name="settings_recording_min_recording_interval_title">Recording time interval</string>
|
|
+ <string name="marker_idle_duration_key" translatable="false">marker_idle_duration</string>
|
|
+ <string name="settings_recording_marker_idle_duration_title">Marker idle duration</string>
|
|
+ <string name="settings_recording_min_recording_interval_key" translatable="false">recording_min_recording_interval_key</string>
|
|
<string name="settings_recording_min_required_accuracy_summary">Location is ignored if its accuracy is greater than %1$s</string>
|
|
<string name="settings_recording_min_required_accuracy_title">GPS accuracy</string>
|
|
<string name="settings_recording_track_name_date_iso_8601_option">Date (ISO 8601)</string>
|
|
@@ -478,6 +484,14 @@ limitations under the License.
|
|
<string name="stats_sensors_cadence">Cadence</string>
|
|
<string name="stats_sensors_power">Power</string>
|
|
<string name="stats_clock">Clock</string>
|
|
+ <string name="stats_auto_pause_status">Auto-Pause Status</string>
|
|
+ <string name="stats_sensor_status_hr">Sensor HR</string>
|
|
+ <string name="stats_sensor_status_cadence">Sensor Cadence</string>
|
|
+ <string name="stats_sensor_status_speed">Sensor Speed</string>
|
|
+ <string name="stats_debug_idle_timer">Idle Timer</string>
|
|
+ <string name="stats_debug_walking_timer">Walking Timer</string>
|
|
+ <string name="stats_debug_marker_timer">Marker Timer</string>
|
|
+
|
|
<!-- Track -->
|
|
<string name="track_name_format">Track %1$d</string>
|
|
<string name="track_recording_notification_accuracy">Location accuracy: %1$s</string>
|
|
@@ -536,6 +550,7 @@ limitations under the License.
|
|
<string name="value_float_mile_hour_recommended">%1$.1f mi/h (recommended)</string>
|
|
<string name="value_float_knots_recommended">%1$.1f knots (recommended)</string>
|
|
<string name="value_integer_minute">%1$d min</string>
|
|
+ <string name="value_integer_minute_recommended">%1$d min (recommended)</string>
|
|
<string name="value_integer_second">%1$d s</string>
|
|
<string name="value_none">None</string>
|
|
<string name="value_off">Off</string>
|
|
@@ -543,6 +558,9 @@ limitations under the License.
|
|
<string name="value_smallest_recommended">Smallest (recommended)</string>
|
|
<string name="value_int_seconds">%1$d s (recommended)</string>
|
|
<string name="voiceIdle">Becoming idle.</string>
|
|
+ <string name="voiceLongBreak">Long break recorded.</string>
|
|
+ <string name="voiceSensorDisconnected">Sensor disconnected.</string>
|
|
+ <string name="voiceSensorConnected">Sensor connected.</string>
|
|
<string name="voiceHoursPlural">
|
|
{n, plural,
|
|
=1 {1 hour}
|
|
@@ -705,4 +723,16 @@ limitations under the License.
|
|
<string name="settings_announcements_lap_power">Lap power</string>
|
|
<string name="power_x_watt">Power {x, plural, =1 {1 watt} other {# watts} }</string>
|
|
<string name="power">Power</string>
|
|
+ <string name="settings_recording_prevent_idle_if_cadence_key" translatable="false">preventIdleIfCadence</string>
|
|
+ <string name="settings_recording_prevent_idle_if_cadence_title">Prevent auto-pause if pedaling</string>
|
|
+ <string name="settings_recording_prevent_idle_if_cadence_summary">Do not enter idle mode while pedaling even if not moving</string>
|
|
+ <string name="sensor_disconnected_warning">Sensor disconnected. Continue with GPS?</string>
|
|
+ <string name="sensor_disconnected_switch_action">Switch</string>
|
|
+ <string name="settings_recording_auto_pause_on_cadence_0_key" translatable="false">autoPauseCadence0</string>
|
|
+ <string name="settings_recording_auto_pause_on_cadence_0_title">Auto-pause when carrying bike</string>
|
|
+ <string name="settings_recording_auto_pause_on_cadence_0_summary">Pause recording if cadence is 0 and speed is low</string>
|
|
+ <string name="settings_recording_auto_pause_on_cadence_0_speed_key" translatable="false">autoPauseCadence0Speed</string>
|
|
+ <string name="settings_recording_auto_pause_on_cadence_0_speed_title">Auto-pause max speed</string>
|
|
+ <string name="settings_recording_auto_pause_on_cadence_0_speed_summary">Speed below which walking with bike is detected (if cadence is 0)</string>
|
|
+ <string name="auto_pause_cadence_0_speed_default" translatable="false">8</string>
|
|
</resources>
|
|
diff --git a/src/main/res/xml/settings_announcements.xml b/src/main/res/xml/settings_announcements.xml
|
|
index 3adf5fa29..919270f62 100644
|
|
--- a/src/main/res/xml/settings_announcements.xml
|
|
+++ b/src/main/res/xml/settings_announcements.xml
|
|
@@ -22,6 +22,21 @@
|
|
android:defaultValue="@bool/voice_announce_idle_default"
|
|
android:key="@string/voice_announce_idle_key"
|
|
android:title="@string/settings_announcements_idle_title" />
|
|
+ <SwitchPreferenceCompat
|
|
+ android:defaultValue="@bool/voice_announce_long_break_default"
|
|
+ android:key="@string/voice_announce_long_break_key"
|
|
+ android:title="@string/settings_announcements_long_break_title" />
|
|
+ </PreferenceCategory>
|
|
+
|
|
+ <PreferenceCategory android:title="@string/settings_announcements_sensor_category_title">
|
|
+ <SwitchPreferenceCompat
|
|
+ android:defaultValue="@bool/voice_announce_sensor_disconnected_default"
|
|
+ android:key="@string/voice_announce_sensor_disconnected_key"
|
|
+ android:title="@string/settings_announcements_sensor_disconnected_title" />
|
|
+ <SwitchPreferenceCompat
|
|
+ android:defaultValue="@bool/voice_announce_sensor_connected_default"
|
|
+ android:key="@string/voice_announce_sensor_connected_key"
|
|
+ android:title="@string/settings_announcements_sensor_connected_title" />
|
|
</PreferenceCategory>
|
|
|
|
<PreferenceCategory android:title="@string/settings_announcements_statistics_title">
|
|
diff --git a/src/main/res/xml/settings_recording.xml b/src/main/res/xml/settings_recording.xml
|
|
index 2a16d3041..d4dc66d0d 100644
|
|
--- a/src/main/res/xml/settings_recording.xml
|
|
+++ b/src/main/res/xml/settings_recording.xml
|
|
@@ -4,6 +4,25 @@
|
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
android:title="@string/settings_recording_title">
|
|
|
|
+ <SwitchPreferenceCompat
|
|
+ android:defaultValue="false"
|
|
+ android:key="@string/settings_recording_auto_pause_on_cadence_0_key"
|
|
+ android:title="@string/settings_recording_auto_pause_on_cadence_0_title"
|
|
+ android:summary="@string/settings_recording_auto_pause_on_cadence_0_summary" />
|
|
+
|
|
+ <SwitchPreferenceCompat
|
|
+ android:defaultValue="true"
|
|
+ android:key="@string/settings_recording_prevent_idle_if_cadence_key"
|
|
+ android:title="@string/settings_recording_prevent_idle_if_cadence_title"
|
|
+ android:summary="@string/settings_recording_prevent_idle_if_cadence_summary" />
|
|
+
|
|
+ <ListPreference
|
|
+ android:defaultValue="@string/auto_pause_cadence_0_speed_default"
|
|
+ android:entryValues="@array/auto_pause_cadence_0_speed_values"
|
|
+ android:key="@string/settings_recording_auto_pause_on_cadence_0_speed_key"
|
|
+ android:title="@string/settings_recording_auto_pause_on_cadence_0_speed_title"
|
|
+ app:useSimpleSummaryProvider="true" />
|
|
+
|
|
<ListPreference
|
|
android:defaultValue="@string/recording_distance_interval_default"
|
|
android:entryValues="@array/recording_distance_interval_values"
|
|
@@ -23,4 +42,11 @@
|
|
android:title="@string/settings_recording_idle_timeout_title"
|
|
app:useSimpleSummaryProvider="true" />
|
|
|
|
+ <ListPreference
|
|
+ android:defaultValue="@string/marker_idle_duration_default"
|
|
+ android:entryValues="@array/marker_idle_duration_values"
|
|
+ android:key="@string/marker_idle_duration_key"
|
|
+ android:title="@string/settings_recording_marker_idle_duration_title"
|
|
+ app:useSimpleSummaryProvider="true" />
|
|
+
|
|
</PreferenceScreen>
|
|
\ No newline at end of file
|