1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-06-19 11:30:44 +02:00
Gadgetbridge/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/TemperatureChartFragment.java
2024-03-18 09:12:33 +00:00

240 lines
9.0 KiB
Java

/* Copyright (C) 2023 Alicia Hormann
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.activities.charts;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.github.mikephil.charting.animation.Easing;
import com.github.mikephil.charting.charts.Chart;
import com.github.mikephil.charting.charts.LineChart;
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 com.github.mikephil.charting.formatter.ValueFormatter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
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;
public class TemperatureChartFragment extends AbstractChartFragment<TemperatureChartFragment.TemperatureChartsData> {
protected static final Logger LOG = LoggerFactory.getLogger(TemperatureChartFragment.class);
private LineChart mTemperatureChart;
private int BACKGROUND_COLOR;
private int DESCRIPTION_COLOR;
private int CHART_TEXT_COLOR;
protected final int TOTAL_DAYS = getRangeDays();
@Override
protected void init() {
BACKGROUND_COLOR = GBApplication.getBackgroundColor(requireContext());
DESCRIPTION_COLOR = GBApplication.getTextColor(requireContext());
CHART_TEXT_COLOR = GBApplication.getSecondaryTextColor(requireContext());
}
private int getRangeDays() {
if (GBApplication.getPrefs().getBoolean("charts_range", true)) {
return 30;
} else {
return 7;
}
}
@Override
protected TemperatureChartsData refreshInBackground(final ChartsHost chartsHost, final DBHandler db, final GBDevice device) {
final List<? extends TemperatureSample> samples = getSamples(db, device);
LOG.info("Got {} temperature samples", samples.size());
return new TemperatureChartsDataBuilder(samples).build();
}
@Override
protected void updateChartsnUIThread(final TemperatureChartsData temperatureData) {
mTemperatureChart.setData(null); // workaround for https://github.com/PhilJay/MPAndroidChart/issues/2317
mTemperatureChart.getXAxis().setValueFormatter(temperatureData.getXValueFormatter());
mTemperatureChart.getXAxis().setAvoidFirstLastClipping(true);
// Using approximately the range of survivable body-temperatures (in celsius), rounded to multiples of 5
mTemperatureChart.getAxisLeft().setAxisMinimum(30f);
mTemperatureChart.getAxisLeft().setAxisMaximum(45f);
mTemperatureChart.setData(temperatureData.getData());
}
@Override
public String getTitle() {
return getString(R.string.menuitem_temperature);
}
@Override
public View onCreateView(final LayoutInflater inflater,
final ViewGroup container,
final Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.fragment_temperaturechart, container, false);
mTemperatureChart = rootView.findViewById(R.id.temperature_line_chart);
setupLineChart();
// refresh immediately instead of use refreshIfVisible(), for perceived performance
refresh();
return rootView;
}
private void setupLineChart() {
mTemperatureChart.setBackgroundColor(BACKGROUND_COLOR);
mTemperatureChart.getDescription().setTextColor(DESCRIPTION_COLOR);
configureBarLineChartDefaults(mTemperatureChart);
final XAxis x = mTemperatureChart.getXAxis();
x.setDrawLabels(true);
x.setDrawGridLines(false);
x.setEnabled(true);
x.setTextColor(CHART_TEXT_COLOR);
x.setDrawLimitLinesBehindData(true);
final YAxis yAxisLeft = mTemperatureChart.getAxisLeft();
yAxisLeft.setDrawGridLines(true);
yAxisLeft.setDrawTopYLabelEntry(false);
yAxisLeft.setTextColor(CHART_TEXT_COLOR);
yAxisLeft.setEnabled(true);
final YAxis yAxisRight = mTemperatureChart.getAxisRight();
yAxisRight.setDrawGridLines(false);
yAxisRight.setDrawLabels(false);
}
@Override
protected void setupLegend(final Chart<?> chart) {
}
@Override
protected void renderCharts() {
mTemperatureChart.animateX(ANIM_TIME, Easing.EaseInOutQuart);
}
@Override
protected int getTSStart() {
return getTSEnd() - TOTAL_DAYS*24*60*60;
}
private List<? extends TemperatureSample> getSamples(final DBHandler db, final GBDevice device) {
final int tsStart = getTSStart();
final int tsEnd = getTSEnd();
final DeviceCoordinator coordinator = device.getDeviceCoordinator();
final TimeSampleProvider<? extends TemperatureSample> sampleProvider = coordinator.getTemperatureSampleProvider(device, db.getDaoSession());
return sampleProvider.getAllSamples(tsStart * 1000L, tsEnd * 1000L);
}
protected class TemperatureChartsDataBuilder {
private final List<? extends TemperatureSample> samples;
public TemperatureChartsDataBuilder(final List<? extends TemperatureSample> samples) {
this.samples = samples;
}
public TemperatureChartsData build() {
TimestampTranslation tsTranslation = new TimestampTranslation();
List<Entry> entries = new ArrayList<Entry>();
long firstTs = 0;
for (TemperatureSample sample : samples) {
int timestamp_in_seconds = (int) (sample.getTimestamp() / 1000L);
entries.add(new Entry(tsTranslation.shorten(timestamp_in_seconds), sample.getTemperature()));
if (firstTs == 0) {
firstTs = sample.getTimestamp();
}
}
LineDataSet dataSet = new LineDataSet(entries, getString(R.string.menuitem_temperature));
dataSet.setLineWidth(2.2f);
dataSet.setMode(LineDataSet.Mode.HORIZONTAL_BEZIER);
dataSet.setCubicIntensity(0.1f);
dataSet.setDrawCircles(true);
dataSet.setCircleRadius(5f);
dataSet.setDrawCircleHole(false);
dataSet.setDrawValues(true);
dataSet.setValueTextSize(10f);
dataSet.setValueTextColor(CHART_TEXT_COLOR);
dataSet.setHighlightEnabled(true);
dataSet.setValueFormatter(new MyValueFormatter());
LineData lineData = new LineData(dataSet);
return new TemperatureChartsData(lineData, tsTranslation);
}
}
protected static class TemperatureChartsData extends DefaultChartsData<LineData> {
public TemperatureChartsData(LineData lineData, TimestampTranslation tsTranslation) {
super(lineData, new dateFormatter(tsTranslation));
}
}
protected static class dateFormatter extends ValueFormatter {
private final TimestampTranslation tsTranslation;
SimpleDateFormat annotationDateFormat = new SimpleDateFormat("dd.MM.");
Calendar cal = GregorianCalendar.getInstance();
public dateFormatter(TimestampTranslation tsTranslation) {
this.tsTranslation = tsTranslation;
}
@Override
public String getFormattedValue(float value) {
cal.clear();
int ts = (int) value;
cal.setTimeInMillis(tsTranslation.toOriginalValue(ts) * 1000L);
Date date = cal.getTime();
return annotationDateFormat.format(date);
}
}
protected static class MyValueFormatter extends ValueFormatter {
private final DecimalFormat formatter = new DecimalFormat("0.00");
@Override
public String getPointLabel(Entry entry) {
return formatter.format(entry.getY());
}
}
}