forked from upstream-mirrors/OpenTracks
feat: prioritize Bluetooth sensor data over GPS
- 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.
This commit is contained in:
@@ -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,20 @@ 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 +702,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 +904,15 @@ 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),
|
||||
new DataField(resources.getString(R.string.stats_custom_layout_media_key), false, false, true)
|
||||
);
|
||||
|
||||
RecordingLayout defaultLayout = new RecordingLayout(resources.getString(R.string.stats_custom_layout_default_layout), 2);
|
||||
@@ -855,7 +928,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 +974,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() {
|
||||
|
||||
Reference in New Issue
Block a user