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

Continuous temperature chart.

This commit is contained in:
Me7c7 2024-12-01 19:10:36 +02:00
parent 92f1715b09
commit 922c4a19a9
7 changed files with 362 additions and 1 deletions

View File

@ -160,6 +160,7 @@ public class ActivityChartsActivity extends AbstractChartsActivity {
@NonNull
@Override
public Fragment getItem(int position) {
final DeviceCoordinator coordinator = getDevice().getDeviceCoordinator();
// getItem is called to instantiate the fragment for the given page.
switch (enabledTabsList.get(position)) {
case "activity":
@ -189,7 +190,7 @@ public class ActivityChartsActivity extends AbstractChartsActivity {
case "spo2":
return new Spo2ChartFragment();
case "temperature":
return new TemperatureChartFragment();
return coordinator.supportsContinuousTemperature()? new TemperatureDailyFragment(): new TemperatureChartFragment();
case "cycling":
return new CyclingChartFragment();
case "weight":

View File

@ -0,0 +1,234 @@
package nodomain.freeyourgadget.gadgetbridge.activities.charts;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.github.mikephil.charting.charts.Chart;
import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.components.LegendEntry;
import com.github.mikephil.charting.components.LimitLine;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.data.LineDataSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.TimeSampleProvider;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.TemperatureSample;
import nodomain.freeyourgadget.gadgetbridge.util.Accumulator;
public class TemperatureDailyFragment extends AbstractChartFragment<TemperatureDailyFragment.TemperatureChartData> {
protected static final Logger LOG = LoggerFactory.getLogger(TemperatureDailyFragment.class);
protected int TEMPERATURE_COLOR;
protected int CHART_TEXT_COLOR;
protected int BACKGROUND_COLOR;
protected int DESCRIPTION_COLOR;
protected int LEGEND_TEXT_COLOR;
private TextView dateView;
private TextView tempAverage;
private TextView tempMinimum;
private TextView tempMaximum;
private LineChart tempLineChart;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_temperature, container, false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
rootView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
getChartsHost().enableSwipeRefresh(scrollY == 0);
});
}
dateView = rootView.findViewById(R.id.temp_date_view);
tempLineChart = rootView.findViewById(R.id.temp_line_chart);
tempAverage = rootView.findViewById(R.id.temp_average);
tempMinimum = rootView.findViewById(R.id.temp_minimum);
tempMaximum = rootView.findViewById(R.id.temp_maximum);
setupChart();
refresh();
setupLegend(tempLineChart);
return rootView;
}
@Override
public String getTitle() {
return getString(R.string.menuitem_temperature);
}
@Override
protected void init() {
CHART_TEXT_COLOR = GBApplication.getSecondaryTextColor(requireContext());
DESCRIPTION_COLOR = LEGEND_TEXT_COLOR = GBApplication.getTextColor(requireContext());
TEMPERATURE_COLOR = GBApplication.getSecondaryTextColor(requireContext());
}
@Override
protected TemperatureChartData refreshInBackground(ChartsHost chartsHost, DBHandler db, GBDevice device) {
int startTs = getTSStart();
int endTs = getTSEnd();
final DeviceCoordinator coordinator = device.getDeviceCoordinator();
final TimeSampleProvider<? extends TemperatureSample> sampleProvider = coordinator.getTemperatureSampleProvider(device, db.getDaoSession());
final List<? extends TemperatureSample> samples = sampleProvider.getAllSamples(startTs * 1000L, endTs * 1000L);
LOG.info("Got {} temperature samples", samples.size());
return new TemperatureChartData(samples);
}
@Override
protected void renderCharts() {
tempLineChart.invalidate();
}
private void setupChart() {
tempLineChart.setBackgroundColor(BACKGROUND_COLOR);
tempLineChart.getDescription().setTextColor(DESCRIPTION_COLOR);
tempLineChart.getDescription().setEnabled(false);
XAxis x = tempLineChart.getXAxis();
x.setDrawLabels(true);
x.setDrawGridLines(false);
x.setEnabled(true);
x.setTextColor(CHART_TEXT_COLOR);
x.setDrawLimitLinesBehindData(true);
x.setPosition(XAxis.XAxisPosition.BOTTOM);
x.setAxisMinimum(0f);
x.setAxisMaximum(86400f);
YAxis y = tempLineChart.getAxisLeft();
y.setDrawGridLines(false);
y.setDrawTopYLabelEntry(true);
y.setTextColor(CHART_TEXT_COLOR);
y.setEnabled(true);
y.setAxisMaximum(HeartRateUtils.getInstance().getMaxHeartRate());
y.setAxisMinimum(HeartRateUtils.getInstance().getMinHeartRate());
YAxis yAxisRight = tempLineChart.getAxisRight();
yAxisRight.setDrawGridLines(false);
yAxisRight.setDrawLabels(true);
yAxisRight.setDrawTopYLabelEntry(true);
yAxisRight.setTextColor(CHART_TEXT_COLOR);
yAxisRight.setAxisMaximum(HeartRateUtils.getInstance().getMaxHeartRate());
yAxisRight.setAxisMinimum(HeartRateUtils.getInstance().getMinHeartRate());
refresh();
}
@Override
protected void setupLegend(Chart<?> chart) {
List<LegendEntry> legendEntries = new ArrayList<>(1);
LegendEntry dataEntry = new LegendEntry();
dataEntry.label = getTitle();
dataEntry.formColor = TEMPERATURE_COLOR;
legendEntries.add(dataEntry);
if (GBApplication.getPrefs().getBoolean("charts_show_average", true)) {
LegendEntry dataAverageEntry = new LegendEntry();
dataAverageEntry.label = getString(R.string.hr_average);
dataAverageEntry.formColor = Color.CYAN;
legendEntries.add(dataAverageEntry);
}
chart.getLegend().setCustom(legendEntries);
chart.getLegend().setTextColor(LEGEND_TEXT_COLOR);
chart.getLegend().setWordWrapEnabled(true);
}
@Override
protected void updateChartsnUIThread(TemperatureDailyFragment.TemperatureChartData data) {
Date date = new Date(getTSEnd() * 1000L);
String formattedDate = new SimpleDateFormat("E, MMM dd").format(date);
dateView.setText(formattedDate);
final TimestampTranslation tsTranslation = new TimestampTranslation();
final List<Entry> lineEntries = new ArrayList<>();
List<? extends TemperatureSample> samples = data.samples;
final Accumulator accumulator = new Accumulator();
for (int i =0; i < samples.size(); i++) {
TemperatureSample sample = samples.get(i);
int timestamp_in_seconds = (int) (sample.getTimestamp() / 1000L);
lineEntries.add(new Entry(tsTranslation.shorten(timestamp_in_seconds), sample.getTemperature()));
accumulator.add(sample.getTemperature());
}
LineDataSet dataSet = new LineDataSet(lineEntries, "Heart Rate");
dataSet.setLineWidth(1.5f);
dataSet.setMode(LineDataSet.Mode.HORIZONTAL_BEZIER);
dataSet.setCubicIntensity(0.1f);
dataSet.setDrawCircles(false);
dataSet.setDrawValues(true);
dataSet.setAxisDependency(YAxis.AxisDependency.RIGHT);
dataSet.setColor(TEMPERATURE_COLOR);
dataSet.setValueTextColor(CHART_TEXT_COLOR);
final double average = accumulator.getCount() > 0 ? accumulator.getAverage() : -1;
final double minimum = accumulator.getCount() > 0 ? accumulator.getMin() : -1;
final double maximum = accumulator.getCount() > 0 ? accumulator.getMax() : -1;
tempAverage.setText(average > 0 ? String.format(Locale.ROOT, "%.1f", average) : "-");
tempMinimum.setText(minimum > 0 ? String.format(Locale.ROOT, "%.1f", minimum) : "-");
tempMaximum.setText(maximum > 0 ? String.format(Locale.ROOT, "%.1f", maximum) : "-");
if (minimum > 0) {
long axisMin = Math.max(Math.round(minimum) - 3, 0);
tempLineChart.getAxisLeft().setAxisMinimum(axisMin);
tempLineChart.getAxisRight().setAxisMinimum(axisMin);
}
if (maximum > 0) {
tempLineChart.getAxisLeft().setAxisMaximum(Math.round(maximum) + 3);
tempLineChart.getAxisRight().setAxisMaximum(Math.round(maximum) + 3);
}
tempLineChart.getXAxis().setValueFormatter(new SampleXLabelFormatter(tsTranslation, "HH:mm"));
tempLineChart.setData(new LineData(dataSet));
tempLineChart.getAxisLeft().removeAllLimitLines();
if (average > 0 && GBApplication.getPrefs().getBoolean("charts_show_average", true)) {
final LimitLine averageLine = new LimitLine((float) average);
averageLine.setLineWidth(1.5f);
averageLine.enableDashedLine(15f, 10f, 0f);
averageLine.setLineColor(Color.CYAN);
tempLineChart.getAxisLeft().addLimitLine(averageLine);
}
}
protected static class TemperatureChartData extends ChartsData {
public List<? extends TemperatureSample> samples;
protected TemperatureChartData(List<? extends TemperatureSample> samples) {
this.samples = samples;
}
}
}

View File

@ -529,6 +529,11 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator {
return false;
}
@Override
public boolean supportsContinuousTemperature() {
return false;
}
@Override
public boolean supportsSpo2(final GBDevice device) {
return false;

View File

@ -236,6 +236,12 @@ public interface DeviceCoordinator {
*/
boolean supportsTemperatureMeasurement();
/**
* Returns true if continuous temperature measurement used in device
* (with this coordinator).
*/
boolean supportsContinuousTemperature();
/**
* Returns true if SpO2 measurement and fetching is supported by the device
* (with this coordinator).

View File

@ -193,6 +193,11 @@ public abstract class HuaweiBRCoordinator extends AbstractBLClassicDeviceCoordin
return huaweiCoordinator.supportsTemperature();
}
@Override
public boolean supportsContinuousTemperature() {
return huaweiCoordinator.supportsTemperature();
}
@Override
public InstallHandler findInstallHandler(Uri uri, Context context) {
return huaweiCoordinator.getInstallHandler(uri, context);

View File

@ -202,6 +202,11 @@ public abstract class HuaweiLECoordinator extends AbstractBLEDeviceCoordinator i
return huaweiCoordinator.supportsTemperature();
}
@Override
public boolean supportsContinuousTemperature() {
return huaweiCoordinator.supportsTemperature();
}
@Override
public InstallHandler findInstallHandler(Uri uri, Context context) {
return huaweiCoordinator.getInstallHandler(uri, context);

View File

@ -0,0 +1,105 @@
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/temp_date_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:layout_marginBottom="20dp"
android:gravity="center"
android:textSize="20sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="300sp">
<com.github.mikephil.charting.charts.LineChart
android:id="@+id/temp_line_chart"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="2" />
</LinearLayout>
<GridLayout
android:background="@color/gauge_line_color"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:columnCount="2"
android:layout_marginTop="15dp"
>
<LinearLayout
style="@style/GridTile"
android:layout_marginEnd="1dp"
android:layout_marginTop="2dp"
>
<TextView
android:id="@+id/temp_minimum"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/stats_empty_value"
android:textSize="20sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hr_minimum"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
style="@style/GridTile"
android:layout_marginStart="1dp"
android:layout_marginTop="2dp"
>
<TextView
android:id="@+id/temp_maximum"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/stats_empty_value"
android:textSize="20sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hr_maximum"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
style="@style/GridTile"
android:layout_marginEnd="1dp"
>
<TextView
android:id="@+id/temp_average"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/stats_empty_value"
android:textSize="20sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hr_average"
android:textSize="12sp" />
</LinearLayout>
</GridLayout>
<TextView
android:id="@+id/steps_chart_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:paddingLeft="20dip"
android:text=""
android:textSize="20sp" />
</LinearLayout>
</ScrollView>