From 1aeb9ca3326cd36d59d37cc6c40bba0a8a9c575f Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Mon, 19 Sep 2022 22:14:08 +0200 Subject: [PATCH 1/4] Heartrate zones: adding preference for max heartrate. Part of #1118. --- .../de/dennisguse/opentracks/data/models/TrackPoint.java | 4 ---- .../de/dennisguse/opentracks/settings/PreferencesUtils.java | 5 +++++ src/main/res/values/settings.xml | 3 +++ src/main/res/values/strings.xml | 4 +++- src/main/res/xml/settings_sensors.xml | 6 ++++++ 5 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/main/java/de/dennisguse/opentracks/data/models/TrackPoint.java b/src/main/java/de/dennisguse/opentracks/data/models/TrackPoint.java index 883818e7e..f7f42d54a 100644 --- a/src/main/java/de/dennisguse/opentracks/data/models/TrackPoint.java +++ b/src/main/java/de/dennisguse/opentracks/data/models/TrackPoint.java @@ -385,10 +385,6 @@ public class TrackPoint { return this; } - public boolean hasSensorData() { - return hasHeartRate() || hasCadence() || hasPower(); - } - public boolean hasHeartRate() { return heartRate != null; } diff --git a/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java b/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java index 2b8a6e426..31666e758 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java +++ b/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java @@ -279,6 +279,11 @@ public class PreferencesUtils { return getBoolean(R.string.settings_sensor_bluetooth_service_filter_enabled_key, DEFAULT); } + public static int getMaxHeartrate() { + final int DEFAULT = Integer.parseInt(resources.getString(R.string.settings_sensor_heart_rate_max_default)); + return getInt(R.string.settings_sensor_heart_rate_max_key, DEFAULT); + } + 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); diff --git a/src/main/res/values/settings.xml b/src/main/res/values/settings.xml index 5b12fb8d1..b7e6abb86 100644 --- a/src/main/res/values/settings.xml +++ b/src/main/res/values/settings.xml @@ -44,6 +44,9 @@ true + maxHeartRate + 220 + showOnMapFormatKey totalRowsDeletedKey diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 99549cc76..dec125e1d 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -434,6 +434,8 @@ limitations under the License. Wheel Circumference (mm) Only show Bluetooth devices that announce required services + Zones: maximum Heart rate (bpm) + Preferred rate Imperial (mi, ft) @@ -479,7 +481,7 @@ limitations under the License. Distance Total time Split interval - Heart Rate + Heart rate Cadence Power Clock diff --git a/src/main/res/xml/settings_sensors.xml b/src/main/res/xml/settings_sensors.xml index 23a4be516..1a8130066 100644 --- a/src/main/res/xml/settings_sensors.xml +++ b/src/main/res/xml/settings_sensors.xml @@ -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" /> + From 19fe5275b9858b01528d55829a2a1b49a418e792 Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Fri, 25 Nov 2022 07:41:04 +0100 Subject: [PATCH 2/4] Heartrate zones: adding preference for max heartrate. Fixes #1118. --- .../data/models/HeartRateZones.java | 33 +++++++++++++++++++ .../opentracks/settings/PreferencesUtils.java | 7 ++-- .../GenericStatisticsViewHolder.java | 15 +++++++++ src/main/res/values/colors.xml | 5 +++ 4 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 src/main/java/de/dennisguse/opentracks/data/models/HeartRateZones.java diff --git a/src/main/java/de/dennisguse/opentracks/data/models/HeartRateZones.java b/src/main/java/de/dennisguse/opentracks/data/models/HeartRateZones.java new file mode 100644 index 000000000..3fe04de03 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/data/models/HeartRateZones.java @@ -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; + } +} diff --git a/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java b/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java index 31666e758..d6335d252 100644 --- a/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java +++ b/src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java @@ -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,9 +281,10 @@ public class PreferencesUtils { return getBoolean(R.string.settings_sensor_bluetooth_service_filter_enabled_key, DEFAULT); } - public static int getMaxHeartrate() { + public static HeartRateZones getHeartRateZones() { final int DEFAULT = Integer.parseInt(resources.getString(R.string.settings_sensor_heart_rate_max_default)); - return getInt(R.string.settings_sensor_heart_rate_max_key, DEFAULT); + int value = getInt(R.string.settings_sensor_heart_rate_max_key, DEFAULT); + return new HeartRateZones(HeartRate.of(value)); } public static boolean shouldShowStatsOnLockscreen() { diff --git a/src/main/java/de/dennisguse/opentracks/viewmodels/GenericStatisticsViewHolder.java b/src/main/java/de/dennisguse/opentracks/viewmodels/GenericStatisticsViewHolder.java index 61e0354b9..cbe656234 100644 --- a/src/main/java/de/dennisguse/opentracks/viewmodels/GenericStatisticsViewHolder.java +++ b/src/main/java/de/dennisguse/opentracks/viewmodels/GenericStatisticsViewHolder.java @@ -3,14 +3,18 @@ package de.dennisguse.opentracks.viewmodels; import android.util.Pair; import android.view.LayoutInflater; +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 +273,21 @@ public abstract class GenericStatisticsViewHolder extends StatisticViewHolder#272727 + #FF0000 + #FFBD04 + #6ACC2B + #64D1FD + @color/opentracks_on_primary_color \ No newline at end of file From dd91e05fb6e4f0bf866393e24c5cd5ac8a0ffc5e Mon Sep 17 00:00:00 2001 From: Dennis Guse Date: Fri, 25 Nov 2022 07:42:18 +0100 Subject: [PATCH 3/4] Bugfix: sensor name/address must be visible. Introduced in d70e7b8788f1ae250899edfd49bae8a5a8ef2e74 --- .../opentracks/viewmodels/GenericStatisticsViewHolder.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/de/dennisguse/opentracks/viewmodels/GenericStatisticsViewHolder.java b/src/main/java/de/dennisguse/opentracks/viewmodels/GenericStatisticsViewHolder.java index cbe656234..050d802fa 100644 --- a/src/main/java/de/dennisguse/opentracks/viewmodels/GenericStatisticsViewHolder.java +++ b/src/main/java/de/dennisguse/opentracks/viewmodels/GenericStatisticsViewHolder.java @@ -2,6 +2,7 @@ package de.dennisguse.opentracks.viewmodels; import android.util.Pair; import android.view.LayoutInflater; +import android.view.View; import androidx.core.content.ContextCompat; @@ -285,6 +286,8 @@ public abstract class GenericStatisticsViewHolder extends StatisticViewHolder Date: Fri, 25 Nov 2022 07:50:25 +0100 Subject: [PATCH 4/4] Removed obsolete test. Introduced in d70e7b8788f1ae250899edfd49bae8a5a8ef2e74 --- .../EspressoCustomRecordingLayoutTest.java | 76 ------------------- 1 file changed, 76 deletions(-) delete mode 100644 src/androidTest/java/de/dennisguse/opentracks/ui/customRecordingLayout/EspressoCustomRecordingLayoutTest.java diff --git a/src/androidTest/java/de/dennisguse/opentracks/ui/customRecordingLayout/EspressoCustomRecordingLayoutTest.java b/src/androidTest/java/de/dennisguse/opentracks/ui/customRecordingLayout/EspressoCustomRecordingLayoutTest.java deleted file mode 100644 index 1a0317334..000000000 --- a/src/androidTest/java/de/dennisguse/opentracks/ui/customRecordingLayout/EspressoCustomRecordingLayoutTest.java +++ /dev/null @@ -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 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)); - } - } - -}