Heartrate zones
This commit is contained in:
Dennis Guse
2022-11-25 19:03:56 +01:00
committed by GitHub
9 changed files with 80 additions and 81 deletions
@@ -1,76 +0,0 @@
package de.dennisguse.opentracks.ui.customRecordingLayout;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.longClick;
import static androidx.test.espresso.matcher.ViewMatchers.assertThat;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static org.hamcrest.CoreMatchers.is;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
import androidx.test.espresso.NoMatchingViewException;
import androidx.test.espresso.ViewAssertion;
import androidx.test.espresso.ViewInteraction;
import androidx.test.espresso.matcher.ViewMatchers;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import androidx.test.rule.GrantPermissionRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.TrackListActivity;
import de.dennisguse.opentracks.settings.PreferencesUtils;
@LargeTest
@RunWith(AndroidJUnit4.class)
public class EspressoCustomRecordingLayoutTest {
@Rule
public ActivityScenarioRule<TrackListActivity> mActivityTestRule = new ActivityScenarioRule<>(TrackListActivity.class);
@Rule
public GrantPermissionRule mGrantPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
@Test
public void customLayoutTest() {
// TrackListActivity: start recording
ViewInteraction trackControllerRecordButton = onView(ViewMatchers.withId(R.id.track_list_fab_action));
trackControllerRecordButton.perform(click());
// Get custom layout preferences and check all data fields are showed.
RecordingLayout recordingLayout = PreferencesUtils.getCustomLayout();
onView(withId(R.id.recycler_view)).check(new RecyclerViewItemCountAssertion((int) recordingLayout.getFields().stream().filter(DataField::isVisible).count()));
// stop recording
ViewInteraction trackControllerStopButton = onView(withId(R.id.track_recording_fab_action));
trackControllerStopButton.perform(longClick());
}
private static class RecyclerViewItemCountAssertion implements ViewAssertion {
private final int expectedCount;
public RecyclerViewItemCountAssertion(int expectedCount) {
this.expectedCount = expectedCount;
}
@Override
public void check(View view, NoMatchingViewException noViewFoundException) {
if (noViewFoundException != null) {
throw noViewFoundException;
}
RecyclerView recyclerView = (RecyclerView) view;
RecyclerView.Adapter<?> adapter = recyclerView.getAdapter();
assertThat(adapter.getItemCount(), is(expectedCount));
}
}
}
@@ -0,0 +1,33 @@
package de.dennisguse.opentracks.data.models;
import androidx.annotation.NonNull;
import de.dennisguse.opentracks.R;
public class HeartRateZones {
private final HeartRate max;
public HeartRateZones(@NonNull HeartRate max) {
this.max = max;
}
public int getColorForZone(HeartRate current) {
if (current != null) {
if (current.getBPM() >= max.getBPM() * 0.9) {
return R.color.heart_rate_zone_textcolor_5;
}
if (current.getBPM() >= max.getBPM() * 0.8) {
return R.color.heart_rate_zone_textcolor_4;
}
if (current.getBPM() >= max.getBPM() * 0.7) {
return R.color.heart_rate_zone_textcolor_3;
}
if (current.getBPM() >= max.getBPM() * 0.6) {
return R.color.heart_rate_zone_textcolor_2;
}
}
return R.color.heart_rate_zone_textcolor_1;
}
}
@@ -385,10 +385,6 @@ public class TrackPoint {
return this;
}
public boolean hasSensorData() {
return hasHeartRate() || hasCadence() || hasPower();
}
public boolean hasHeartRate() {
return heartRate != null;
}
@@ -42,6 +42,8 @@ import java.util.stream.Stream;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.data.models.Distance;
import de.dennisguse.opentracks.data.models.DistanceFormatter;
import de.dennisguse.opentracks.data.models.HeartRate;
import de.dennisguse.opentracks.data.models.HeartRateZones;
import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
import de.dennisguse.opentracks.io.file.TrackFilenameGenerator;
@@ -279,6 +281,12 @@ public class PreferencesUtils {
return getBoolean(R.string.settings_sensor_bluetooth_service_filter_enabled_key, DEFAULT);
}
public static HeartRateZones getHeartRateZones() {
final int DEFAULT = Integer.parseInt(resources.getString(R.string.settings_sensor_heart_rate_max_default));
int value = getInt(R.string.settings_sensor_heart_rate_max_key, DEFAULT);
return new HeartRateZones(HeartRate.of(value));
}
public static boolean shouldShowStatsOnLockscreen() {
final boolean STATS_SHOW_ON_LOCKSCREEN_DEFAULT = resources.getBoolean(R.bool.stats_show_on_lockscreen_while_recording_default);
return getBoolean(R.string.stats_show_on_lockscreen_while_recording_key, STATS_SHOW_ON_LOCKSCREEN_DEFAULT);
@@ -2,15 +2,20 @@ package de.dennisguse.opentracks.viewmodels;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
import androidx.core.content.ContextCompat;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.data.models.DistanceFormatter;
import de.dennisguse.opentracks.data.models.HeartRateZones;
import de.dennisguse.opentracks.data.models.Speed;
import de.dennisguse.opentracks.data.models.SpeedFormatter;
import de.dennisguse.opentracks.data.models.TrackPoint;
import de.dennisguse.opentracks.databinding.StatsGenericItemBinding;
import de.dennisguse.opentracks.sensors.sensorData.SensorDataSet;
import de.dennisguse.opentracks.services.RecordingData;
import de.dennisguse.opentracks.settings.PreferencesUtils;
import de.dennisguse.opentracks.settings.UnitSystem;
import de.dennisguse.opentracks.ui.customRecordingLayout.DataField;
import de.dennisguse.opentracks.util.StringUtils;
@@ -269,10 +274,23 @@ public abstract class GenericStatisticsViewHolder extends StatisticViewHolder<St
valueAndUnit = StringUtils.getHeartRateParts(getContext(), null);
}
//TODO Loads preference every time
HeartRateZones zones = PreferencesUtils.getHeartRateZones();
int textColor;
if (sensorDataSet != null) {
textColor = zones.getColorForZone(sensorDataSet.getHeartRate().first);
} else {
textColor = zones.getColorForZone(null);
}
getBinding().statsValue.setText(valueAndUnit.first);
getBinding().statsUnit.setText(valueAndUnit.second);
getBinding().statsDescriptionMain.setText(R.string.stats_sensors_heart_rate);
getBinding().statsDescriptionSecondary.setVisibility(View.VISIBLE);
getBinding().statsDescriptionSecondary.setText(sensorName);
getBinding().statsValue.setTextColor(ContextCompat.getColor(getContext(), textColor));
}
}
@@ -294,6 +312,8 @@ public abstract class GenericStatisticsViewHolder extends StatisticViewHolder<St
getBinding().statsValue.setText(valueAndUnit.first);
getBinding().statsUnit.setText(valueAndUnit.second);
getBinding().statsDescriptionMain.setText(R.string.stats_sensors_cadence);
getBinding().statsDescriptionSecondary.setVisibility(View.VISIBLE);
getBinding().statsDescriptionSecondary.setText(sensorName);
}
}
@@ -316,6 +336,8 @@ public abstract class GenericStatisticsViewHolder extends StatisticViewHolder<St
getBinding().statsValue.setText(valueAndUnit.first);
getBinding().statsUnit.setText(valueAndUnit.second);
getBinding().statsDescriptionMain.setText(R.string.stats_sensors_power);
getBinding().statsDescriptionSecondary.setVisibility(View.VISIBLE);
getBinding().statsDescriptionSecondary.setText(sensorName);
}
}
+5
View File
@@ -57,4 +57,9 @@ limitations under the License.
<color name="splashscreen_background">#272727</color>
<color name="heart_rate_zone_textcolor_5">#FF0000</color>
<color name="heart_rate_zone_textcolor_4">#FFBD04</color>
<color name="heart_rate_zone_textcolor_3">#6ACC2B</color>
<color name="heart_rate_zone_textcolor_2">#64D1FD</color>
<color name="heart_rate_zone_textcolor_1">@color/opentracks_on_primary_color</color>
</resources>
+3
View File
@@ -44,6 +44,9 @@
true
</bool>
<string name="settings_sensor_heart_rate_max_key" translatable="false">maxHeartRate</string>
<string name="settings_sensor_heart_rate_max_default" translatable="false">220</string>
<string name="show_on_map_format_key" translatable="false">showOnMapFormatKey</string>
<string name="total_rows_deleted_key" translatable="false">totalRowsDeletedKey</string>
+3 -1
View File
@@ -434,6 +434,8 @@ limitations under the License.
<string name="settings_sensor_wheel_circumference">Wheel Circumference (mm)</string>
<string name="settings_sensor_bluetooth_service_filter_enabled">Only show Bluetooth devices that announce required services</string>
<string name="settings_sensor_heart_rate_max">Zones: maximum Heart rate (bpm)</string>
<!-- Settings Stats -->
<string name="settings_stats_rate_title">Preferred rate</string>
<string name="settings_stats_units_imperial">Imperial (mi, ft)</string>
@@ -479,7 +481,7 @@ limitations under the License.
<string name="stats_distance">Distance</string>
<string name="stats_total_time">Total time</string>
<string name="stats_split_interval">Split interval</string>
<string name="stats_sensors_heart_rate">Heart Rate</string>
<string name="stats_sensors_heart_rate">Heart rate</string>
<string name="stats_sensors_cadence">Cadence</string>
<string name="stats_sensors_power">Power</string>
<string name="stats_clock">Clock</string>
+6
View File
@@ -9,6 +9,12 @@
android:defaultValue="@string/sensor_type_value_none"
android:key="@string/settings_sensor_bluetooth_heart_rate_key"
android:title="@string/sensor_state_heart_rate" />
<EditTextPreference
android:defaultValue="@string/settings_sensor_heart_rate_max_default"
android:inputType="number"
android:key="@string/settings_sensor_heart_rate_max_key"
android:title="@string/settings_sensor_heart_rate_max"
app:useSimpleSummaryProvider="true" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/settings_cycling_sensor">