forked from upstream-mirrors/OpenTracks
committed by
Dennis Guse
parent
70a2040142
commit
6cfc05af82
+15
@@ -4,6 +4,7 @@ import static android.text.Spanned.SPAN_INCLUSIVE_EXCLUSIVE;
|
||||
import static de.dennisguse.opentracks.settings.PreferencesUtils.shouldVoiceAnnounceAverageHeartRate;
|
||||
import static de.dennisguse.opentracks.settings.PreferencesUtils.shouldVoiceAnnounceAverageSpeedPace;
|
||||
import static de.dennisguse.opentracks.settings.PreferencesUtils.shouldVoiceAnnounceLapHeartRate;
|
||||
import static de.dennisguse.opentracks.settings.PreferencesUtils.shouldVoiceAnnounceLapPower;
|
||||
import static de.dennisguse.opentracks.settings.PreferencesUtils.shouldVoiceAnnounceLapSpeedPace;
|
||||
import static de.dennisguse.opentracks.settings.PreferencesUtils.shouldVoiceAnnounceMovingTime;
|
||||
import static de.dennisguse.opentracks.settings.PreferencesUtils.shouldVoiceAnnounceTotalDistance;
|
||||
@@ -23,6 +24,7 @@ import java.util.Map;
|
||||
|
||||
import de.dennisguse.opentracks.R;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.Power;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.settings.UnitSystem;
|
||||
import de.dennisguse.opentracks.stats.SensorStatistics;
|
||||
@@ -159,6 +161,19 @@ class VoiceAnnouncementUtils {
|
||||
builder.append(".");
|
||||
}
|
||||
|
||||
if (shouldVoiceAnnounceLapPower() && currentInterval != null && currentInterval.hasAveragePower()) {
|
||||
int currentPower = Math.round(currentInterval.getAveragePower().getW());
|
||||
if(shouldVoiceAnnounceUnit()) {
|
||||
String template = context.getResources().getString(R.string.power_x_watt);
|
||||
builder.append(" ")
|
||||
.append(MessageFormat.format(template, Map.of("x", currentPower)));
|
||||
} else {
|
||||
builder.append(" ").append(context.getString(R.string.power)).append(" ");
|
||||
builder.append(String.valueOf(currentPower));
|
||||
}
|
||||
builder.append(".");
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
||||
@@ -434,6 +434,10 @@ public class PreferencesUtils {
|
||||
return getBoolean(R.string.voice_announce_lap_heart_rate_key, false);
|
||||
}
|
||||
|
||||
public static boolean shouldVoiceAnnounceLapPower() {
|
||||
return getBoolean(R.string.voice_announce_lap_power_key, false);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static void setVoiceAnnounceLapHeartRate(boolean value) {
|
||||
setBoolean(R.string.voice_announce_lap_heart_rate_key, value);
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.time.Instant;
|
||||
import de.dennisguse.opentracks.data.models.Altitude;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Power;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.Track;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
@@ -60,6 +61,7 @@ public class TrackStatistics {
|
||||
private Float totalAltitudeLoss_m = null;
|
||||
// The average heart rate seen on this track
|
||||
private HeartRate avgHeartRate = null;
|
||||
private Power avgPower = null;
|
||||
|
||||
private boolean isIdle;
|
||||
|
||||
@@ -83,6 +85,7 @@ public class TrackStatistics {
|
||||
totalAltitudeGain_m = other.totalAltitudeGain_m;
|
||||
totalAltitudeLoss_m = other.totalAltitudeLoss_m;
|
||||
avgHeartRate = other.avgHeartRate;
|
||||
avgPower = other.avgPower;
|
||||
isIdle = other.isIdle;
|
||||
}
|
||||
|
||||
@@ -129,6 +132,19 @@ public class TrackStatistics {
|
||||
}
|
||||
}
|
||||
|
||||
if (avgPower == null) {
|
||||
avgPower = other.avgPower;
|
||||
} else {
|
||||
if (other.avgPower != null) {
|
||||
// Using total time as weights for the averaging.
|
||||
// Important to do this before total time is updated
|
||||
avgPower = Power.of(
|
||||
(totalTime.getSeconds() * avgPower.getW() + other.totalTime.getSeconds() * other.avgPower.getW())
|
||||
/ (totalTime.getSeconds() + other.totalTime.getSeconds())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
totalDistance = totalDistance.plus(other.totalDistance);
|
||||
totalTime = totalTime.plus(other.totalTime);
|
||||
movingTime = movingTime.plus(other.movingTime);
|
||||
@@ -270,6 +286,15 @@ public class TrackStatistics {
|
||||
return avgHeartRate;
|
||||
}
|
||||
|
||||
public boolean hasPower() {
|
||||
return avgPower != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Power getAveragePower() {
|
||||
return avgPower;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the average speed.
|
||||
* This calculation only takes into account the displacement until the last point that was accounted for in statistics.
|
||||
@@ -333,6 +358,12 @@ public class TrackStatistics {
|
||||
}
|
||||
}
|
||||
|
||||
public void setAveragePower(Power power) {
|
||||
if (power != null) {
|
||||
avgPower = power;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasTotalAltitudeGain() {
|
||||
return totalAltitudeGain_m != null;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Power;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
|
||||
@@ -43,6 +44,8 @@ public class TrackStatisticsUpdater {
|
||||
|
||||
private float averageHeartRateBPM;
|
||||
private Duration totalHeartRateDuration = Duration.ZERO;
|
||||
private float averagePowerW;
|
||||
private Duration totalPowerDuration = Duration.ZERO;
|
||||
|
||||
// The current segment's statistics
|
||||
private final TrackStatistics currentSegment;
|
||||
@@ -125,6 +128,17 @@ public class TrackStatisticsUpdater {
|
||||
currentSegment.setAverageHeartRate(HeartRate.of(averageHeartRateBPM));
|
||||
}
|
||||
|
||||
// Update power
|
||||
if (trackPoint.hasPower() && lastTrackPoint != null) {
|
||||
Duration trackPointDuration = Duration.between(lastTrackPoint.getTime(), trackPoint.getTime());
|
||||
Duration newTotalDuration = totalPowerDuration.plus(trackPointDuration);
|
||||
|
||||
averagePowerW = (totalPowerDuration.toMillis() * averagePowerW + trackPointDuration.toMillis() * trackPoint.getPower().getW()) / newTotalDuration.toMillis();
|
||||
totalPowerDuration = newTotalDuration;
|
||||
|
||||
currentSegment.setAveragePower(Power.of(averagePowerW));
|
||||
}
|
||||
|
||||
{
|
||||
// Update total distance
|
||||
Distance movingDistance = null;
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.util.List;
|
||||
import de.dennisguse.opentracks.data.TrackPointIterator;
|
||||
import de.dennisguse.opentracks.data.models.Distance;
|
||||
import de.dennisguse.opentracks.data.models.HeartRate;
|
||||
import de.dennisguse.opentracks.data.models.Power;
|
||||
import de.dennisguse.opentracks.data.models.Speed;
|
||||
import de.dennisguse.opentracks.data.models.TrackPoint;
|
||||
import de.dennisguse.opentracks.stats.TrackStatistics;
|
||||
@@ -103,6 +104,7 @@ public class IntervalStatistics {
|
||||
private Float gain_m;
|
||||
private Float loss_m;
|
||||
private HeartRate avgHeartRate;
|
||||
private Power avgPower;
|
||||
|
||||
public Interval() {
|
||||
}
|
||||
@@ -118,6 +120,7 @@ public class IntervalStatistics {
|
||||
gain_m = i.gain_m;
|
||||
loss_m = i.loss_m;
|
||||
avgHeartRate = i.avgHeartRate;
|
||||
avgPower = i.avgPower;
|
||||
}
|
||||
|
||||
public Interval(Interval i) {
|
||||
@@ -126,6 +129,7 @@ public class IntervalStatistics {
|
||||
gain_m = i.gain_m;
|
||||
loss_m = i.loss_m;
|
||||
avgHeartRate = i.avgHeartRate;
|
||||
avgPower = i.avgPower;
|
||||
}
|
||||
|
||||
public Distance getDistance() {
|
||||
@@ -160,12 +164,21 @@ public class IntervalStatistics {
|
||||
return avgHeartRate;
|
||||
}
|
||||
|
||||
public boolean hasAveragePower() {
|
||||
return avgPower != null;
|
||||
}
|
||||
|
||||
public Power getAveragePower() {
|
||||
return avgPower;
|
||||
}
|
||||
|
||||
private void add(TrackStatistics trackStatistics, @Nullable TrackPoint lastTrackPoint) {
|
||||
distance = distance.plus(trackStatistics.getTotalDistance());
|
||||
time = time.plus(trackStatistics.getTotalTime());
|
||||
gain_m = trackStatistics.hasTotalAltitudeGain() ? trackStatistics.getTotalAltitudeGain() : gain_m;
|
||||
loss_m = trackStatistics.hasTotalAltitudeLoss() ? trackStatistics.getTotalAltitudeLoss() : loss_m;
|
||||
avgHeartRate = trackStatistics.getAverageHeartRate();
|
||||
avgPower = trackStatistics.getAveragePower();
|
||||
if (lastTrackPoint == null) {
|
||||
return;
|
||||
}
|
||||
@@ -183,6 +196,7 @@ public class IntervalStatistics {
|
||||
gain_m = trackStatistics.hasTotalAltitudeGain() ? trackStatistics.getTotalAltitudeGain() : gain_m;
|
||||
loss_m = trackStatistics.hasTotalAltitudeLoss() ? trackStatistics.getTotalAltitudeLoss() : loss_m;
|
||||
avgHeartRate = trackStatistics.getAverageHeartRate();
|
||||
avgPower = trackStatistics.getAveragePower();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -728,4 +728,8 @@ limitations under the License.
|
||||
<string name="introduction_osm_dashboard">OpenTracks itself does not provide a map. Please install OSMDashboard to view your recordings on a map.</string>
|
||||
<string name="settings_recording_min_sampling_interval_title">Sampling time interval</string>
|
||||
<string name="settings_recording_location_distance_summary">%1$s between recorded locations</string>
|
||||
<string name="voice_announce_lap_power_key">voiceAnnounceLapPower</string>
|
||||
<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>
|
||||
</resources>
|
||||
|
||||
@@ -25,6 +25,13 @@
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/settings_announcements_statistics_title">
|
||||
<SwitchPreferenceCompat
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:defaultValue="@bool/voice_announce_unit_default"
|
||||
android:key="@string/voice_announce_unit_key"
|
||||
android:title="@string/settings_announcements_unit" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="@string/voice_announcement_frequency_default"
|
||||
android:entryValues="@array/voice_announcement_frequency_values"
|
||||
@@ -32,13 +39,6 @@
|
||||
android:title="@string/settings_announcements_totaltime_frequency"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="@string/voice_announcement_distance_default"
|
||||
android:entryValues="@array/voice_announcement_distance_values"
|
||||
android:key="@string/voice_announcement_distance_key"
|
||||
android:title="@string/settings_announcements_distance_frequency"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="@bool/voice_announce_total_distance_default"
|
||||
android:key="@string/voice_announce_total_distance_key"
|
||||
@@ -54,26 +54,29 @@
|
||||
android:key="@string/voice_announce_average_speed_pace_key"
|
||||
android:title="@string/settings_announcements_average_speed_pace" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="@bool/voice_announce_lap_speed_pace_default"
|
||||
android:key="@string/voice_announce_lap_speed_pace_key"
|
||||
android:title="@string/settings_announcements_lap_speed_pace" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:defaultValue="@bool/voice_announce_unit_default"
|
||||
android:key="@string/voice_announce_unit_key"
|
||||
android:title="@string/settings_announcements_unit" />
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="@bool/voice_announce_average_heart_rate_default"
|
||||
android:key="@string/voice_announce_average_heart_rate_key"
|
||||
android:title="@string/settings_announcements_average_heart_rate" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="@string/voice_announcement_distance_default"
|
||||
android:entryValues="@array/voice_announcement_distance_values"
|
||||
android:key="@string/voice_announcement_distance_key"
|
||||
android:title="@string/settings_announcements_distance_frequency"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="@bool/voice_announce_lap_speed_pace_default"
|
||||
android:key="@string/voice_announce_lap_speed_pace_key"
|
||||
android:title="@string/settings_announcements_lap_speed_pace" />
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="@bool/voice_announce_lap_heart_rate_default"
|
||||
android:key="@string/voice_announce_lap_heart_rate_key"
|
||||
android:title="@string/settings_announcements_lap_heart_rate" />
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="@string/voice_announce_lap_power_key"
|
||||
android:title="@string/settings_announcements_lap_power" />
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
|
||||
Reference in New Issue
Block a user