Skip to content

Commit 39c993f

Browse files
committed
Add settings toggles for chart series
Adds toggles to the User Interface section of the app Settings that allow the user to display or hide the three series available in the chart: Elevation, Speed/Pace, and Heart Rate. As a user of a heart rate monitor primarily during strength workouts and combat sports, the distance and elevation graphs are not helpful to me - in addition, their scale means that I am not able to easily see my exact heart rate. This should allow users in a similar situation to better determine exactly what is most helpful for them to be seen on the graph. Also updates heart rate series possible intervals - the smallest interval was 25, which was too low for most zone 1 cardio charts - if your heart rate goes from 70 to 125, with a 25 interval, most of the chart will be whitespace.
1 parent ae237bd commit 39c993f

File tree

6 files changed

+150
-27
lines changed

6 files changed

+150
-27
lines changed

src/main/java/de/dennisguse/opentracks/chart/ChartFragment.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, Strin
8686
});
8787
}
8888
}
89+
8990
if (PreferencesUtils.isKey(R.string.stats_rate_key, key)) {
9091
boolean reportSpeed = PreferencesUtils.isReportSpeed(activityTypeLocalized);
9192
if (reportSpeed != viewBinding.chartView.getReportSpeed()) {
@@ -99,6 +100,21 @@ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, Strin
99100
});
100101
}
101102
}
103+
104+
if (PreferencesUtils.isKey(R.string.chart_display_elevation_key, key)) {
105+
viewBinding.chartView.setShowElevation(PreferencesUtils.shouldShowElevation());
106+
refreshChart();
107+
}
108+
109+
if (PreferencesUtils.isKey(R.string.chart_display_pace_or_speed_key, key)) {
110+
viewBinding.chartView.setShowPaceOrSpeed(PreferencesUtils.shouldShowPaceOrSpeed());
111+
refreshChart();
112+
}
113+
114+
if (PreferencesUtils.isKey(R.string.chart_display_heart_rate_key, key)) {
115+
viewBinding.chartView.setShowHeartRate(PreferencesUtils.shouldShowHeartRate());
116+
refreshChart();
117+
}
102118
}
103119
};
104120

@@ -273,4 +289,13 @@ private void runOnUiThread(Runnable runnable) {
273289
fragmentActivity.runOnUiThread(runnable);
274290
}
275291
}
292+
293+
private void refreshChart() {
294+
runOnUiThread(() -> {
295+
if (isResumed()) {
296+
viewBinding.chartView.invalidate();
297+
viewBinding.chartView.requestLayout();
298+
}
299+
});
300+
}
276301
}

src/main/java/de/dennisguse/opentracks/chart/ChartView.java

Lines changed: 63 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,10 @@ public class ChartView extends View {
8686
}
8787

8888
private final List<ChartValueSeries> seriesList = new LinkedList<>();
89+
private final ChartValueSeries elevationSeries;
8990
private final ChartValueSeries speedSeries;
9091
private final ChartValueSeries paceSeries;
92+
private final ChartValueSeries heartRateSeries;
9193

9294
private final LinkedList<ChartPoint> chartPoints = new LinkedList<>();
9395
private final List<Marker> markers = new LinkedList<>();
@@ -123,6 +125,7 @@ public class ChartView extends View {
123125
private boolean chartByDistance = false;
124126
private UnitSystem unitSystem = UnitSystem.defaultUnitSystem();
125127
private boolean reportSpeed = true;
128+
private boolean showPaceOrSpeed = true;
126129
private boolean showPointer = false;
127130

128131
private final GestureDetectorCompat detectorScrollFlingTab = new GestureDetectorCompat(getContext(), new GestureDetector.SimpleOnGestureListener() {
@@ -176,17 +179,17 @@ public ChartView(Context context, AttributeSet attributeSet) {
176179
int fontSizeSmall = ThemeUtils.getFontSizeSmallInPx(context);
177180
int fontSizeMedium = ThemeUtils.getFontSizeMediumInPx(context);
178181

179-
seriesList.add(new ChartValueSeries(context,
180-
Integer.MIN_VALUE,
181-
Integer.MAX_VALUE,
182-
new int[]{5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000},
183-
R.string.description_altitude_metric,
184-
R.string.description_altitude_imperial,
185-
R.string.description_altitude_imperial,
186-
R.color.chart_altitude_fill,
187-
R.color.chart_altitude_border,
188-
fontSizeSmall,
189-
fontSizeMedium) {
182+
elevationSeries = new ChartValueSeries(context,
183+
Integer.MIN_VALUE,
184+
Integer.MAX_VALUE,
185+
new int[]{5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000},
186+
R.string.description_altitude_metric,
187+
R.string.description_altitude_imperial,
188+
R.string.description_altitude_imperial,
189+
R.color.chart_altitude_fill,
190+
R.color.chart_altitude_border,
191+
fontSizeSmall,
192+
fontSizeMedium) {
190193
@Override
191194
protected Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
192195
return chartPoint.altitude();
@@ -196,7 +199,8 @@ protected Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
196199
protected boolean drawIfChartPointHasNoData() {
197200
return false;
198201
}
199-
});
202+
};
203+
seriesList.add(elevationSeries);
200204

201205
speedSeries = new ChartValueSeries(context,
202206
0,
@@ -244,10 +248,15 @@ protected boolean drawIfChartPointHasNoData() {
244248
};
245249
seriesList.add(paceSeries);
246250

247-
seriesList.add(new ChartValueSeries(context,
251+
heartRateSeries = new ChartValueSeries(context,
248252
0,
249253
Integer.MAX_VALUE,
250-
new int[]{25, 50},
254+
// For Zone 5 cardio, the 25 value should result in nice visuals, as the values
255+
// will range from ~70 - ~180 (around 4.5 intervals).
256+
// For Zone 1 cardio, the 15 value should result in nice visuals, as the values
257+
// will range from ~70 - ~120 (around 3.5 intervals)
258+
// The fallback of 50 should give appropriate visuals for values outside this range.
259+
new int[]{15, 25, 50},
251260
R.string.description_sensor_heart_rate,
252261
R.string.description_sensor_heart_rate,
253262
R.string.description_sensor_heart_rate,
@@ -264,7 +273,8 @@ protected Double extractDataFromChartPoint(@NonNull ChartPoint chartPoint) {
264273
protected boolean drawIfChartPointHasNoData() {
265274
return false;
266275
}
267-
});
276+
};
277+
seriesList.add(heartRateSeries);
268278

269279
seriesList.add(new ChartValueSeries(context,
270280
0,
@@ -343,9 +353,15 @@ protected boolean drawIfChartPointHasNoData() {
343353
setClickable(true);
344354
updateDimensions();
345355

346-
// either speedSeries or paceSeries should be enabled.
347-
speedSeries.setEnabled(reportSpeed);
348-
paceSeries.setEnabled(!reportSpeed);
356+
// Either speedSeries or paceSeries should be enabled, if one is shown.
357+
if (showPaceOrSpeed) {
358+
speedSeries.setEnabled(reportSpeed);
359+
paceSeries.setEnabled(!reportSpeed);
360+
}
361+
362+
// Defaults for our chart series.
363+
heartRateSeries.setEnabled(true);
364+
elevationSeries.setEnabled(true);
349365
}
350366

351367
@Override
@@ -379,6 +395,12 @@ public void setReportSpeed(boolean value) {
379395
}
380396

381397
public boolean applyReportSpeed() {
398+
if (!showPaceOrSpeed) {
399+
paceSeries.setEnabled(false);
400+
speedSeries.setEnabled(false);
401+
return true;
402+
}
403+
382404
if (reportSpeed) {
383405
if (!speedSeries.isEnabled()) {
384406
speedSeries.setEnabled(true);
@@ -396,6 +418,20 @@ public boolean applyReportSpeed() {
396418
return false;
397419
}
398420

421+
void setShowElevation(boolean value) {
422+
elevationSeries.setEnabled(value);
423+
}
424+
void setShowPaceOrSpeed(boolean value) {
425+
showPaceOrSpeed = value;
426+
427+
// we want to make sure we show whatever version the user has
428+
// selected when we turn this back on.
429+
applyReportSpeed();
430+
}
431+
void setShowHeartRate(boolean value) {
432+
heartRateSeries.setEnabled(value);
433+
}
434+
399435
public void setShowPointer(boolean value) {
400436
showPointer = value;
401437
}
@@ -644,10 +680,18 @@ private record TitleDimensions(
644680
*/
645681
private void drawSeriesTitles(Canvas canvas) {
646682
Iterator<TitlePosition> tpI = titleDimensions.titlePositions.iterator();
683+
647684
for (ChartValueSeries chartValueSeries : seriesList) {
648685
if (chartValueSeries.isEnabled() && chartValueSeries.hasData() || allowIfEmpty(chartValueSeries)) {
649686
String title = getContext().getString(chartValueSeries.getTitleId(unitSystem));
650687
Paint paint = chartValueSeries.getTitlePaint();
688+
689+
// It is possible for the titlePositions to become empty temporarily, while switching between
690+
// chart screens quickly.
691+
if (!tpI.hasNext()) {
692+
return;
693+
}
694+
651695
TitlePosition tp = tpI.next();
652696
int y = topBorder - spacer - (titleDimensions.lineCount - tp.line) * (titleDimensions.lineHeight + spacer);
653697
canvas.drawText(title, tp.xPos + getScrollX(), y, paint);
@@ -774,7 +818,7 @@ private void drawYAxis(Canvas canvas) {
774818

775819
//TODO
776820
int markerXPosition = x - spacer;
777-
int index = titleDimensions.titlePositions.size() - 1; // index only onver the visible chart series
821+
int index = titleDimensions.titlePositions.size() - 1; // index only over the visible chart series
778822
final int lastDrawn2ndLineMarkerIndex = getYmarkerCountOn1stLine();
779823
for (int i = seriesList.size()-1; i>=0 ;--i) { // draw markers from the last series to achieve right alignment
780824
ChartValueSeries chartValueSeries = seriesList.get(i);

src/main/java/de/dennisguse/opentracks/settings/PreferencesUtils.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,21 @@ public static boolean shouldUseFullscreen() {
318318
return getBoolean(R.string.stats_fullscreen_while_recording_key, DEFAULT);
319319
}
320320

321+
public static boolean shouldShowElevation() {
322+
final boolean DEFAULT = resources.getBoolean(R.bool.chart_display_elevation_default);
323+
return getBoolean(R.string.chart_display_elevation_key, DEFAULT);
324+
}
325+
326+
public static boolean shouldShowPaceOrSpeed() {
327+
final boolean DEFAULT = resources.getBoolean(R.bool.chart_display_pace_or_speed_default);
328+
return getBoolean(R.string.chart_display_pace_or_speed_key, DEFAULT);
329+
}
330+
331+
public static boolean shouldShowHeartRate() {
332+
final boolean DEFAULT = resources.getBoolean(R.bool.chart_display_heart_rate_default);
333+
return getBoolean(R.string.chart_display_heart_rate_key, DEFAULT);
334+
}
335+
321336
public static boolean shouldUseDynamicColors() {
322337
final boolean DEFAULT = resources.getBoolean(R.bool.settings_ui_dynamic_colors_default);
323338
return getBoolean(R.string.settings_ui_dynamic_colors_key, DEFAULT);

src/main/res/values/settings.xml

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<resources>
33
<!-- Do not translate anything in this file directly. -->
4-
<string name="stats_show_on_lockscreen_while_recording_key" translatable="false">trackdetail_show_on_lockscreen_while_recording</string>
5-
<bool name="stats_show_on_lockscreen_while_recording_default" translatable="false">false</bool>
6-
7-
<string name="stats_keep_screen_on_while_recording_key" translatable="false">trackdetail_keep_screen_on_while_recording</string>
8-
<bool name="stats_keep_screen_on_while_recording_default" translatable="false">false</bool>
9-
10-
<string name="stats_fullscreen_while_recording_key" translatable="false">trackdetail_fullscreen_while_recording</string>
11-
<bool name="stats_fullscreen_while_recording_default" translatable="false">false</bool>
124

135
<!-- Main settings -->
146
<string name="settings_defaults_key" translatable="false">settingsDefaults</string>
@@ -279,9 +271,32 @@
279271
<string name="show_introduction_screen_key" translatable="false">showIntroduction</string>
280272
<bool name="show_introduction_screen_default">true</bool>
281273

274+
<!-- User Interface Settings -->
275+
282276
<string name="settings_ui_dynamic_colors_key" translatable="false">uiDynamicColors</string>
283277
<bool name="settings_ui_dynamic_colors_default" translatable="false">false</bool>
284278

279+
<!-- Recording Settings -->
280+
<string name="stats_show_on_lockscreen_while_recording_key" translatable="false">trackdetail_show_on_lockscreen_while_recording</string>
281+
<bool name="stats_show_on_lockscreen_while_recording_default" translatable="false">false</bool>
282+
283+
<string name="stats_keep_screen_on_while_recording_key" translatable="false">trackdetail_keep_screen_on_while_recording</string>
284+
<bool name="stats_keep_screen_on_while_recording_default" translatable="false">false</bool>
285+
286+
<string name="stats_fullscreen_while_recording_key" translatable="false">trackdetail_fullscreen_while_recording</string>
287+
<bool name="stats_fullscreen_while_recording_default" translatable="false">false</bool>
288+
289+
<!-- Chart Settings -->
290+
<string name="chart_display_elevation_key" translatable="false">chart_display_elevation</string>
291+
<bool name="chart_display_elevation_default" translatable="false">true</bool>
292+
293+
<string name="chart_display_pace_or_speed_key" translatable="false">chart_display_pace_or_speed</string>
294+
<bool name="chart_display_pace_or_speed_default" translatable="false">true</bool>
295+
296+
<string name="chart_display_heart_rate_key" translatable="false">chart_display_heart_rate</string>
297+
<bool name="chart_display_heart_rate_default" translatable="false">true</bool>
298+
299+
285300
<!-- androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode() -->
286301
<string name="locale_key" translatable="false">localeKey</string>
287302

src/main/res/values/strings.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,11 @@ limitations under the License.
344344
<!-- Settings Chart -->
345345
<string name="settings_chart_by_distance">By distance</string>
346346
<string name="settings_chart_by_time">By time</string>
347+
<string name="settings_charts_title">Charts</string>
348+
<string name="settings_chart_display_elevation_title">Display elevation</string>
349+
<string name="settings_chart_display_pace_or_speed_title">Display pace or speed</string>
350+
<string name="settings_chart_display_heart_rate_title">Display heart rate</string>
351+
347352
<!-- Settings Recording -->
348353
<string name="settings_recording_title">Recording</string>
349354
<string name="settings_theme_switch_restart">Theme changes may require a manual restart.</string>

src/main/res/xml/settings_user_interface.xml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,23 @@
5858
android:title="@string/settings_recording_fullscreen_on_while_recording_title" />
5959
</PreferenceCategory>
6060

61+
<PreferenceCategory app:title="@string/settings_charts_title">
62+
<SwitchPreferenceCompat
63+
android:icon="@drawable/ic_activity_climbing_24dp"
64+
android:defaultValue="@bool/chart_display_elevation_default"
65+
android:key="@string/chart_display_elevation_key"
66+
android:title="@string/settings_chart_display_elevation_title" />
67+
68+
<SwitchPreferenceCompat
69+
android:icon="@drawable/ic_activity_snowboarding_24dp"
70+
android:defaultValue="@bool/chart_display_pace_or_speed_default"
71+
android:key="@string/chart_display_pace_or_speed_key"
72+
android:title="@string/settings_chart_display_pace_or_speed_title" />
73+
74+
<SwitchPreferenceCompat
75+
android:icon="@drawable/ic_activity_workout_24dp"
76+
android:defaultValue="@bool/chart_display_heart_rate_default"
77+
android:key="@string/chart_display_heart_rate_key"
78+
android:title="@string/settings_chart_display_heart_rate_title" />
79+
</PreferenceCategory>
6180
</PreferenceScreen>

0 commit comments

Comments
 (0)