1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-12-26 10:35:50 +01:00

VO2Max: replace GENERAL vo2max with ANY, add widgets

This commit is contained in:
a0z 2024-09-17 22:39:01 +02:00 committed by José Rebelo
parent 9f0d426a9f
commit f2227bb083
18 changed files with 289 additions and 115 deletions

View File

@ -127,7 +127,7 @@ public class GBApplication extends Application {
private static SharedPreferences sharedPrefs;
private static final String PREFS_VERSION = "shared_preferences_version";
//if preferences have to be migrated, increment the following and add the migration logic in migratePrefs below; see http://stackoverflow.com/questions/16397848/how-can-i-migrate-android-preferences-with-a-new-version
private static final int CURRENT_PREFS_VERSION = 40;
private static final int CURRENT_PREFS_VERSION = 41;
private static final LimitedQueue<Integer, String> mIDSenderLookup = new LimitedQueue<>(16);
private static GBPrefs prefs;
@ -1823,6 +1823,14 @@ public class GBApplication extends Application {
}
}
if (oldVersion < 41) {
// Add vo2max widget.
final String dashboardWidgetsOrder = sharedPrefs.getString("pref_dashboard_widgets_order", null);
if (!StringUtils.isBlank(dashboardWidgetsOrder) && !dashboardWidgetsOrder.contains("vo2max")) {
editor.putString("pref_dashboard_widgets_order", dashboardWidgetsOrder + ",vo2max");
}
}
editor.putString(PREFS_VERSION, Integer.toString(CURRENT_PREFS_VERSION));
editor.apply();
}

View File

@ -75,6 +75,9 @@ import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardStress
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardStressSegmentedWidget;
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardStressSimpleWidget;
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardTodayWidget;
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardVO2MaxCyclingWidget;
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardVO2MaxAnyWidget;
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardVO2MaxRunningWidget;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
import nodomain.freeyourgadget.gadgetbridge.util.DashboardUtils;
@ -298,6 +301,15 @@ public class DashboardFragment extends Fragment implements MenuProvider {
case "hrv":
widget = DashboardHrvWidget.newInstance(dashboardData);
break;
case "vo2max_running":
widget = DashboardVO2MaxRunningWidget.newInstance(dashboardData);
break;
case "vo2max_cycling":
widget = DashboardVO2MaxCyclingWidget.newInstance(dashboardData);
break;
case "vo2max":
widget = DashboardVO2MaxAnyWidget.newInstance(dashboardData);
break;
default:
LOG.error("Unknown dashboard widget {}", widgetName);
continue;

View File

@ -246,7 +246,7 @@ public class HeartRateDailyFragment extends AbstractChartFragment<HeartRateDaily
hrLineChart.getAxisRight().setAxisMaximum(maximum + 30);
}
hrLineChart.getXAxis().setValueFormatter(new SampleXLabelFormatter(tsTranslation));
hrLineChart.getXAxis().setValueFormatter(new SampleXLabelFormatter(tsTranslation, "HH:mm"));
hrLineChart.setData(new LineData(dataSet));
}

View File

@ -36,6 +36,7 @@ import java.util.Map;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.AbstractDashboardVO2MaxWidget;
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.GaugeDrawer;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
@ -49,17 +50,14 @@ public class VO2MaxFragment extends AbstractChartFragment<VO2MaxFragment.VO2MaxD
protected static final Logger LOG = LoggerFactory.getLogger(VO2MaxFragment.class);
private TextView mDateView;
private TextView vo2MaxGeneralValue;
private TextView vo2MaxRunningValue;
private TextView vo2MaxCyclingValue;
private ImageView vo2MaxGeneralGauge;
private ImageView vo2MaxRunningGauge;
private ImageView vo2MaxCyclingGauge;
protected GaugeDrawer gaugeDrawer = new GaugeDrawer();
private LineChart vo2MaxChart;
private RelativeLayout vo2maxCyclingWrapper;
private RelativeLayout vo2maxRunningWrapper;
private RelativeLayout vo2maxGeneralWrapper;
private GridLayout tilesGridWrapper;
private int tsFrom;
GBDevice device;
@ -73,15 +71,12 @@ public class VO2MaxFragment extends AbstractChartFragment<VO2MaxFragment.VO2MaxD
View rootView = inflater.inflate(R.layout.fragment_vo2max, container, false);
mDateView = rootView.findViewById(R.id.vo2max_date_view);
vo2MaxGeneralValue = rootView.findViewById(R.id.vo2max_general_gauge_value);
vo2MaxRunningValue = rootView.findViewById(R.id.vo2max_running_gauge_value);
vo2MaxCyclingValue = rootView.findViewById(R.id.vo2max_cycling_gauge_value);
vo2MaxGeneralGauge = rootView.findViewById(R.id.vo2max_general_gauge);
vo2MaxRunningGauge = rootView.findViewById(R.id.vo2max_running_gauge);
vo2MaxCyclingGauge = rootView.findViewById(R.id.vo2max_cycling_gauge);
vo2MaxChart = rootView.findViewById(R.id.vo2max_chart);
vo2maxCyclingWrapper = rootView.findViewById(R.id.vo2max_cycling_card_layout);
vo2maxGeneralWrapper = rootView.findViewById(R.id.vo2max_general_card_layout);
vo2maxRunningWrapper = rootView.findViewById(R.id.vo2max_running_card_layout);
tilesGridWrapper = rootView.findViewById(R.id.tiles_grid_wrapper);
device = getChartsHost().getDevice();
@ -91,11 +86,9 @@ public class VO2MaxFragment extends AbstractChartFragment<VO2MaxFragment.VO2MaxD
if (!supportsVO2MaxRunning(device)) {
tilesGridWrapper.removeView(vo2maxRunningWrapper);
}
if (!supportsVO2MaxGeneral(device)) {
tilesGridWrapper.removeView(vo2maxGeneralWrapper);
}
setupVO2MaxChart();
refresh();
setupLegend(vo2MaxChart);
return rootView;
@ -106,11 +99,6 @@ public class VO2MaxFragment extends AbstractChartFragment<VO2MaxFragment.VO2MaxD
return coordinator != null && coordinator.supportsVO2MaxCycling();
}
public boolean supportsVO2MaxGeneral(GBDevice device) {
DeviceCoordinator coordinator = device.getDeviceCoordinator();
return coordinator != null && coordinator.supportsVO2MaxGeneral();
}
public boolean supportsVO2MaxRunning(GBDevice device) {
DeviceCoordinator coordinator = device.getDeviceCoordinator();
return coordinator != null && coordinator.supportsVO2MaxRunning();
@ -160,7 +148,6 @@ public class VO2MaxFragment extends AbstractChartFragment<VO2MaxFragment.VO2MaxD
TimestampTranslation tsTranslation = new TimestampTranslation();
List<Entry> runningEntries = new ArrayList<>();
List<Entry> cyclingEntries = new ArrayList<>();
List<Entry> generalEntries = new ArrayList<>();
vo2MaxData.records.forEach((record) -> {
float nd = (float) (record.timestamp - this.tsFrom) / (60 * 60 * 24);
switch (record.type) {
@ -170,54 +157,25 @@ public class VO2MaxFragment extends AbstractChartFragment<VO2MaxFragment.VO2MaxD
case CYCLING:
cyclingEntries.add(new Entry(nd, record.value));
break;
case GENERAL:
generalEntries.add(new Entry(nd, record.value));
break;
}
});
final int[] colors = {
ContextCompat.getColor(GBApplication.getContext(), R.color.vo2max_value_poor_color),
ContextCompat.getColor(GBApplication.getContext(), R.color.vo2max_value_fair_color),
ContextCompat.getColor(GBApplication.getContext(), R.color.vo2max_value_good_color),
ContextCompat.getColor(GBApplication.getContext(), R.color.vo2max_value_excellent_color),
ContextCompat.getColor(GBApplication.getContext(), R.color.vo2max_value_superior_color),
};
final float[] segments = {
0.20F,
0.20F,
0.20F,
0.20F,
0.20F,
};
float[] vo2MaxRanges = {
55.4F,
51.1F,
45.4F,
41.7F,
0.0F,
};
final int[] colors = AbstractDashboardVO2MaxWidget.getColors();
final float[] segments = AbstractDashboardVO2MaxWidget.getSegments();
float[] vo2MaxRanges = AbstractDashboardVO2MaxWidget.getVO2MaxRanges();
final List<ILineDataSet> lineDataSets = new ArrayList<>();
if (supportsVO2MaxGeneral(device)) {
VO2MaxRecord latestGeneralRecord = vo2MaxData.getLatestValue(Vo2MaxSample.Type.GENERAL);
float generalVO2MaxValue = calculateVO2maxGaugeValue(vo2MaxRanges, latestGeneralRecord != null ? latestGeneralRecord.value : 0);
gaugeDrawer.drawSegmentedGauge(vo2MaxGeneralGauge, colors, segments, generalVO2MaxValue, false, true);
vo2MaxGeneralValue.setText(String.valueOf(latestGeneralRecord != null ? Math.round(latestGeneralRecord.value) : "-"));
lineDataSets.add(createDataSet(generalEntries, getResources().getColor(R.color.vo2max_general_char_line_color), getString(R.string.vo2_max_general)));
}
if (supportsVO2MaxRunning(device)) {
VO2MaxRecord latestRunningRecord = vo2MaxData.getLatestValue(Vo2MaxSample.Type.RUNNING);
float runningVO2MaxValue = calculateVO2maxGaugeValue(vo2MaxRanges, latestRunningRecord != null ? latestRunningRecord.value : 0);
vo2MaxRunningValue.setText(String.valueOf(latestRunningRecord != null ? Math.round(latestRunningRecord.value) : "-"));
gaugeDrawer.drawSegmentedGauge(vo2MaxRunningGauge, colors, segments, runningVO2MaxValue, false, true);
lineDataSets.add(createDataSet(runningEntries, getResources().getColor(R.color.vo2max_running_char_line_color), getString(R.string.vo2_max_running)));
lineDataSets.add(createDataSet(runningEntries, getResources().getColor(R.color.vo2max_running_char_line_color), getString(R.string.vo2max_running)));
}
if (supportsVO2MaxCycling(device)) {
VO2MaxRecord latestCyclingRecord = vo2MaxData.getLatestValue(Vo2MaxSample.Type.CYCLING);
float cyclingVO2MaxValue = calculateVO2maxGaugeValue(vo2MaxRanges, latestCyclingRecord != null ? latestCyclingRecord.value : 0);
gaugeDrawer.drawSegmentedGauge(vo2MaxCyclingGauge, colors, segments, cyclingVO2MaxValue, false, true);
vo2MaxCyclingValue.setText(String.valueOf(latestCyclingRecord != null ? Math.round(latestCyclingRecord.value) : "-"));
lineDataSets.add(createDataSet(cyclingEntries, getResources().getColor(R.color.vo2max_cycling_char_line_color), getString(R.string.vo2_max_cycling)));
lineDataSets.add(createDataSet(cyclingEntries, getResources().getColor(R.color.vo2max_cycling_char_line_color), getString(R.string.vo2max_cycling)));
}
final LineData lineData = new LineData(lineDataSets);
vo2MaxChart.getXAxis().setValueFormatter(getVO2MaxLineChartValueFormatter());
@ -320,7 +278,10 @@ public class VO2MaxFragment extends AbstractChartFragment<VO2MaxFragment.VO2MaxD
yAxisRight.setDrawAxisLine(true);
}
protected void setupLegend(Chart<?> chart) {}
protected void setupLegend(Chart<?> chart) {
chart.getLegend().setTextColor(LEGEND_TEXT_COLOR);
chart.getLegend().setWordWrapEnabled(true);
}
protected static class VO2MaxRecord {
float value;

View File

@ -0,0 +1,124 @@
package nodomain.freeyourgadget.gadgetbridge.activities.dashboard;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Serializable;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.DashboardFragment;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.Vo2MaxSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.Vo2MaxSample;
public abstract class AbstractDashboardVO2MaxWidget extends AbstractGaugeWidget implements DashboardVO2MaxWidgetInterface {
protected static final Logger LOG = LoggerFactory.getLogger(AbstractDashboardVO2MaxWidget.class);
public AbstractDashboardVO2MaxWidget(int label, @Nullable String targetActivityTab) {
super(label, targetActivityTab);
}
@Override
protected void populateData(final DashboardFragment.DashboardData dashboardData) {
final List<GBDevice> devices = getSupportedDevices(dashboardData);
final VO2MaxData data = new VO2MaxData();
// Latest vo2max sample.
Vo2MaxSample sample = null;
try (DBHandler dbHandler = GBApplication.acquireDB()) {
for (GBDevice dev : devices) {
final Vo2MaxSampleProvider sampleProvider = (Vo2MaxSampleProvider) dev.getDeviceCoordinator().getVo2MaxSampleProvider(dev, dbHandler.getDaoSession());
final Vo2MaxSample latestSample = sampleProvider.getLatestSample(getVO2MaxType(), dashboardData.timeTo * 1000L);
if (latestSample != null && (sample == null || latestSample.getTimestamp() > sample.getTimestamp())) {
sample = latestSample;
}
}
if (sample != null) {
data.value = sample.getValue();
}
} catch (final Exception e) {
LOG.error("Could not get vo2max for today", e);
}
dashboardData.put(getWidgetKey(), data);
}
public static int[] getColors() {
return new int[]{
ContextCompat.getColor(GBApplication.getContext(), R.color.vo2max_value_poor_color),
ContextCompat.getColor(GBApplication.getContext(), R.color.vo2max_value_fair_color),
ContextCompat.getColor(GBApplication.getContext(), R.color.vo2max_value_good_color),
ContextCompat.getColor(GBApplication.getContext(), R.color.vo2max_value_excellent_color),
ContextCompat.getColor(GBApplication.getContext(), R.color.vo2max_value_superior_color),
};
}
public static float[] getSegments() {
return new float[] {
0.20F,
0.20F,
0.20F,
0.20F,
0.20F,
};
}
public static float[] getVO2MaxRanges() {
return new float[] {
55.4F,
51.1F,
45.4F,
41.7F,
0.0F,
};
}
@Override
protected void draw(final DashboardFragment.DashboardData dashboardData) {
final VO2MaxData vo2MaxData = (VO2MaxData) dashboardData.get(getWidgetKey());
if (vo2MaxData == null) {
drawSimpleGauge(0, -1);
return;
}
final int[] colors = getColors();
final float[] segments = getSegments();
final float[] vo2MaxRanges = getVO2MaxRanges();
float vo2MaxValue = calculateVO2maxGaugeValue(vo2MaxRanges, vo2MaxData.value != -1 ? vo2MaxData.value : 0);
setText(String.valueOf(vo2MaxData.value != -1 ? Math.round(vo2MaxData.value) : "-"));
drawSegmentedGauge(
colors,
segments,
vo2MaxValue,
false,
true
);
}
private float calculateVO2maxGaugeValue(float[] vo2MaxRanges, float vo2MaxValue) {
float value = -1;
for (int i = 0; i < vo2MaxRanges.length; i++) {
if (vo2MaxValue - vo2MaxRanges[i] > 0) {
float rangeValue = i - 1 >= 0 ? vo2MaxRanges[i-1] : 60F;
float rangeDiff = rangeValue - vo2MaxRanges[i];
float valueDiff = vo2MaxValue - vo2MaxRanges[i];
float multiplayer = valueDiff / rangeDiff;
value = (4 - i) * 0.2F + 0.2F * (multiplayer > 1 ? 1 : multiplayer) ;
break;
}
}
return value;
}
private static class VO2MaxData implements Serializable {
private float value = -1;
}
}

View File

@ -16,22 +16,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.activities.dashboard;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.ColorInt;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;

View File

@ -0,0 +1,36 @@
package nodomain.freeyourgadget.gadgetbridge.activities.dashboard;
import android.os.Bundle;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.DashboardFragment;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.Vo2MaxSample;
public class DashboardVO2MaxAnyWidget extends AbstractDashboardVO2MaxWidget {
public DashboardVO2MaxAnyWidget() {
super(R.string.vo2max, "vo2max");
}
public static DashboardVO2MaxAnyWidget newInstance(final DashboardFragment.DashboardData dashboardData) {
final DashboardVO2MaxAnyWidget fragment = new DashboardVO2MaxAnyWidget();
final Bundle args = new Bundle();
args.putSerializable(ARG_DASHBOARD_DATA, dashboardData);
fragment.setArguments(args);
return fragment;
}
public Vo2MaxSample.Type getVO2MaxType() {
return Vo2MaxSample.Type.ANY;
}
public String getWidgetKey() {
return "vo2max";
}
@Override
protected boolean isSupportedBy(final GBDevice device) {
return device.getDeviceCoordinator().supportsVO2Max();
}
}

View File

@ -0,0 +1,36 @@
package nodomain.freeyourgadget.gadgetbridge.activities.dashboard;
import android.os.Bundle;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.DashboardFragment;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.Vo2MaxSample;
public class DashboardVO2MaxCyclingWidget extends AbstractDashboardVO2MaxWidget {
public DashboardVO2MaxCyclingWidget() {
super(R.string.vo2max_cycling, "vo2max");
}
public static DashboardVO2MaxCyclingWidget newInstance(final DashboardFragment.DashboardData dashboardData) {
final DashboardVO2MaxCyclingWidget fragment = new DashboardVO2MaxCyclingWidget();
final Bundle args = new Bundle();
args.putSerializable(ARG_DASHBOARD_DATA, dashboardData);
fragment.setArguments(args);
return fragment;
}
public Vo2MaxSample.Type getVO2MaxType() {
return Vo2MaxSample.Type.CYCLING;
}
public String getWidgetKey() {
return "vo2max_cycling";
}
@Override
protected boolean isSupportedBy(final GBDevice device) {
return device.getDeviceCoordinator().supportsVO2MaxCycling();
}
}

View File

@ -0,0 +1,36 @@
package nodomain.freeyourgadget.gadgetbridge.activities.dashboard;
import android.os.Bundle;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.DashboardFragment;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.Vo2MaxSample;
public class DashboardVO2MaxRunningWidget extends AbstractDashboardVO2MaxWidget {
public DashboardVO2MaxRunningWidget() {
super(R.string.vo2max_running, "vo2max");
}
public static DashboardVO2MaxRunningWidget newInstance(final DashboardFragment.DashboardData dashboardData) {
final DashboardVO2MaxRunningWidget fragment = new DashboardVO2MaxRunningWidget();
final Bundle args = new Bundle();
args.putSerializable(ARG_DASHBOARD_DATA, dashboardData);
fragment.setArguments(args);
return fragment;
}
public Vo2MaxSample.Type getVO2MaxType() {
return Vo2MaxSample.Type.RUNNING;
}
public String getWidgetKey() {
return "vo2max_running";
}
@Override
protected boolean isSupportedBy(final GBDevice device) {
return device.getDeviceCoordinator().supportsVO2MaxRunning();
}
}

View File

@ -0,0 +1,8 @@
package nodomain.freeyourgadget.gadgetbridge.activities.dashboard;
import nodomain.freeyourgadget.gadgetbridge.model.Vo2MaxSample;
public interface DashboardVO2MaxWidgetInterface {
Vo2MaxSample.Type getVO2MaxType();
String getWidgetKey();
}

View File

@ -490,11 +490,6 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator {
return false;
}
@Override
public boolean supportsVO2MaxGeneral() {
return false;
}
@Override
public boolean supportsActivityTabs() {
return supportsActivityTracking();

View File

@ -221,7 +221,6 @@ public interface DeviceCoordinator {
boolean supportsHrvMeasurement();
boolean supportsVO2Max();
boolean supportsVO2MaxCycling();
boolean supportsVO2MaxGeneral();
boolean supportsVO2MaxRunning();
boolean supportsSleepMeasurement();
boolean supportsStepCounter();

View File

@ -146,7 +146,7 @@ public class GarminVo2MaxSampleProvider implements Vo2MaxSampleProvider<Vo2MaxSa
@Nullable
@Override
public Vo2MaxSample getLatestSample() {
return getLatestSample(Vo2MaxSample.Type.GENERAL, 0);
return getLatestSample(Vo2MaxSample.Type.ANY, 0);
}
@Nullable
@ -231,7 +231,7 @@ public class GarminVo2MaxSampleProvider implements Vo2MaxSampleProvider<Vo2MaxSa
type = Vo2MaxSample.Type.CYCLING;
break;
default:
type = Vo2MaxSample.Type.GENERAL;
type = Vo2MaxSample.Type.ANY;
}
return new GarminVo2maxSample(summary.getStartTime().getTime(), type, (float) value);
} catch (final JSONException e) {

View File

@ -18,7 +18,7 @@ package nodomain.freeyourgadget.gadgetbridge.model;
public interface Vo2MaxSample extends TimeSample {
enum Type {
GENERAL,
ANY,
RUNNING,
CYCLING
}

View File

@ -23,41 +23,6 @@
android:layout_height="wrap_content"
android:columnCount="2"
>
<RelativeLayout
android:id="@+id/vo2max_general_card_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:orientation="vertical"
tools:ignore="UselessParent"
android:layout_gravity="fill"
android:layout_columnWeight="1"
>
<ImageView
android:id="@+id/vo2max_general_gauge"
android:layout_width="150dp"
android:layout_height="75dp"
android:layout_centerHorizontal="true"
android:scaleType="fitStart" />
<TextView
android:id="@+id/vo2max_general_gauge_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="28dp"
android:text="@string/stats_empty_value"
android:textSize="30sp" />
<TextView
android:id="@+id/vo2max_general_gauge_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/vo2max_general_gauge_value"
android:layout_centerHorizontal="true"
android:text="@string/vo2max_general_gauge_label" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/vo2max_running_card_layout"
android:layout_width="wrap_content"
@ -91,7 +56,7 @@
android:layout_height="wrap_content"
android:layout_below="@+id/vo2max_running_gauge_value"
android:layout_centerHorizontal="true"
android:text="@string/vo2max_running_gauge_label" />
android:text="@string/running" />
</RelativeLayout>
<RelativeLayout
@ -127,7 +92,7 @@
android:layout_height="wrap_content"
android:layout_below="@+id/vo2max_cycling_gauge_value"
android:layout_centerHorizontal="true"
android:text="@string/vo2max_cycling_gauge_label" />
android:text="@string/cycling" />
</RelativeLayout>

View File

@ -4177,11 +4177,13 @@
<item>@string/active_time</item>
<item>@string/menuitem_sleep</item>
<item>@string/body_energy</item>
<item>@string/vo2max</item>
<item>@string/menuitem_stress_simple</item>
<item>@string/menuitem_stress_segmented</item>
<item>@string/menuitem_stress_breakdown</item>
<item>@string/hrv</item>
<item>@string/vo2max</item>
<item>@string/vo2max_running</item>
<item>@string/vo2max_cycling</item>
</string-array>
<string-array name="pref_dashboard_widgets_order_values">
@ -4192,11 +4194,13 @@
<item>activetime</item>
<item>sleep</item>
<item>bodyenergy</item>
<item>vo2max</item>
<item>stress_simple</item>
<item>stress_segmented</item>
<item>stress_breakdown</item>
<item>hrv</item>
<item>vo2max</item>
<item>vo2max_running</item>
<item>vo2max_cycling</item>
</string-array>
<string-array name="pref_dashboard_widgets_order_default">
@ -4209,5 +4213,6 @@
<item>bodyenergy</item>
<item>stress_segmented</item>
<item>hrv</item>
<item>vo2max</item>
</string-array>
</resources>

View File

@ -53,7 +53,6 @@
<color name="hrv_status_char_line_color" type="color">#d12a2a</color>
<color name="vo2max_running_char_line_color" type="color">#46acea</color>
<color name="vo2max_cycling_char_line_color" type="color">#59b22c</color>
<color name="vo2max_general_char_line_color" type="color">#824be3</color>
<color name="vo2max_value_poor_color" type="color">#d93832</color>
<color name="vo2max_value_fair_color" type="color">#ffa703</color>
<color name="vo2max_value_good_color" type="color">#04c79c</color>

View File

@ -1623,9 +1623,8 @@
<string name="hrv_status_unit">%1$d ms</string>
<string name="hrv_status_baseline">%1$d-%2$d ms</string>
<string name="hrv_status_baseline_label">Baseline</string>
<string name="vo2_max_running">VO2Max Running</string>
<string name="vo2_max_cycling">VO2Max Cycling</string>
<string name="vo2_max_general">VO2Max General</string>
<string name="running">Running</string>
<string name="cycling">Cycling</string>
<string name="bpm_value_unit">%1$d bpm</string>
<string name="steps_distance_unit">%1$,.2f km</string>
<string name="body_energy_gained">Gained</string>
@ -1993,9 +1992,6 @@
<string name="warning">Warning!</string>
<string name="note">Note</string>
<string name="no_data">No data</string>
<string name="vo2max_general_gauge_label">VO2Max</string>
<string name="vo2max_running_gauge_label">Running VO2Max</string>
<string name="vo2max_cycling_gauge_label">Cycling VO2Max</string>
<!-- LED Color -->
<string name="preferences_led_color">LED Color</string>
<!-- FM transmitters -->
@ -2537,6 +2533,8 @@
<string name="pref_header_hrv_status">HRV Status</string>
<string name="body_energy">Body Energy</string>
<string name="vo2max">VO2 Max</string>
<string name="vo2max_running">VO2 Max (Running)</string>
<string name="vo2max_cycling">VO2 Max (Cycling)</string>
<string name="thirty_days_timeline">30 Days Timeline</string>
<string name="pref_header_sony_ambient_sound_control">Ambient Sound Control</string>
<string name="pref_header_sony_sound_control">Sound Control</string>