1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-09-01 03:55:47 +02:00

Add daily steps progress chart

This commit is contained in:
José Rebelo 2024-08-27 19:38:10 +01:00
parent f487bc7876
commit 8e3f010629
4 changed files with 174 additions and 47 deletions

View File

@ -2,6 +2,7 @@ package nodomain.freeyourgadget.gadgetbridge.activities.charts;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint; import android.graphics.Paint;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
@ -16,21 +17,36 @@ import androidx.annotation.ColorInt;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import com.github.mikephil.charting.charts.Chart; 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 com.github.mikephil.charting.formatter.ValueFormatter;
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
public class StepsDailyFragment extends StepsFragment { public class StepsDailyFragment extends StepsFragment<StepsDailyFragment.StepsData> {
protected static final Logger LOG = LoggerFactory.getLogger(BodyEnergyFragment.class); protected static final Logger LOG = LoggerFactory.getLogger(BodyEnergyFragment.class);
private TextView mDateView; private TextView mDateView;
@ -38,10 +54,9 @@ public class StepsDailyFragment extends StepsFragment {
private TextView steps; private TextView steps;
private TextView distance; private TextView distance;
ImageView stepsStreaksButton; ImageView stepsStreaksButton;
private LineChart stepsChart;
protected int STEPS_GOAL; protected int STEPS_GOAL;
protected int TOTAL_DAYS = 1;
@Override @Override
public void onResume() { public void onResume() {
@ -62,6 +77,9 @@ public class StepsDailyFragment extends StepsFragment {
stepsGauge = rootView.findViewById(R.id.steps_gauge); stepsGauge = rootView.findViewById(R.id.steps_gauge);
steps = rootView.findViewById(R.id.steps_count); steps = rootView.findViewById(R.id.steps_count);
distance = rootView.findViewById(R.id.steps_distance); distance = rootView.findViewById(R.id.steps_distance);
stepsChart = rootView.findViewById(R.id.steps_daily_chart);
setupStepsChart();
STEPS_GOAL = GBApplication.getPrefs().getInt(ActivityUser.PREF_USER_STEPS_GOAL, ActivityUser.defaultUserStepsGoal); STEPS_GOAL = GBApplication.getPrefs().getInt(ActivityUser.PREF_USER_STEPS_GOAL, ActivityUser.defaultUserStepsGoal);
refresh(); refresh();
@ -89,8 +107,16 @@ public class StepsDailyFragment extends StepsFragment {
day.setTime(chartsHost.getEndDate()); day.setTime(chartsHost.getEndDate());
String formattedDate = new SimpleDateFormat("E, MMM dd").format(chartsHost.getEndDate()); String formattedDate = new SimpleDateFormat("E, MMM dd").format(chartsHost.getEndDate());
mDateView.setText(formattedDate); mDateView.setText(formattedDate);
List<StepsDay> stepsDaysData = getMyStepsDaysData(db, day, device); List<StepsDay> stepsDayList = getMyStepsDaysData(db, day, device);
return new StepsDailyFragment.StepsData(stepsDaysData); final StepsDay stepsDay;
if (stepsDayList.isEmpty()) {
LOG.error("Failed to get StepsDay for {}", day);
stepsDay = new StepsDay(day, 0, 0);
} else {
stepsDay = stepsDayList.get(0);
}
List<? extends ActivitySample> samplesOfDay = getSamplesOfDay(db, day, 0, device);
return new StepsDailyFragment.StepsData(stepsDay, samplesOfDay);
} }
@Override @Override
@ -111,13 +137,101 @@ public class StepsDailyFragment extends StepsFragment {
steps.setText(String.format(String.valueOf(stepsData.todayStepsDay.steps))); steps.setText(String.format(String.valueOf(stepsData.todayStepsDay.steps)));
distance.setText(getString(R.string.steps_distance_unit, stepsData.todayStepsDay.distance)); distance.setText(getString(R.string.steps_distance_unit, stepsData.todayStepsDay.distance));
// Chart
final List<LegendEntry> legendEntries = new ArrayList<>(1);
final LegendEntry stepsEntry = new LegendEntry();
stepsEntry.label = getString(R.string.steps);
stepsEntry.formColor = getResources().getColor(R.color.steps_color);
legendEntries.add(stepsEntry);
final LegendEntry goalEntry = new LegendEntry();
goalEntry.label = getString(R.string.miband_prefs_fitness_goal);
goalEntry.formColor = Color.GRAY;
legendEntries.add(goalEntry);
stepsChart.getLegend().setTextColor(TEXT_COLOR);
stepsChart.getLegend().setCustom(legendEntries);
final List<Entry> lineEntries = new ArrayList<>();
final TimestampTranslation tsTranslation = new TimestampTranslation();
int sum = 0;
for (final ActivitySample sample : stepsData.samples) {
if (sample.getSteps() > 0) {
sum += sample.getSteps();
}
lineEntries.add(new Entry(tsTranslation.shorten(sample.getTimestamp()), sum));
}
stepsChart.getXAxis().setValueFormatter(new SampleXLabelFormatter(tsTranslation));
if (sum < STEPS_GOAL) {
stepsChart.getAxisLeft().setAxisMaximum(STEPS_GOAL);
} else {
stepsChart.getAxisLeft().resetAxisMaximum();
}
final LineDataSet lineDataSet = new LineDataSet(lineEntries, getString(R.string.steps));
lineDataSet.setColor(getResources().getColor(R.color.steps_color));
lineDataSet.setDrawCircles(false);
lineDataSet.setLineWidth(2f);
lineDataSet.setFillAlpha(255);
lineDataSet.setDrawCircles(false);
lineDataSet.setCircleColor(getResources().getColor(R.color.steps_color));
lineDataSet.setAxisDependency(YAxis.AxisDependency.LEFT);
lineDataSet.setDrawValues(false);
lineDataSet.setMode(LineDataSet.Mode.CUBIC_BEZIER);
lineDataSet.setDrawFilled(true);
lineDataSet.setFillAlpha(60);
lineDataSet.setFillColor(getResources().getColor(R.color.steps_color ));
final LimitLine goalLine = new LimitLine(STEPS_GOAL);
goalLine.setLineColor(Color.GRAY);
goalLine.setLineWidth(1.5f);
goalLine.enableDashedLine(10f, 10f, 0f);
stepsChart.getAxisLeft().removeAllLimitLines();
stepsChart.getAxisLeft().addLimitLine(goalLine);
final List<ILineDataSet> lineDataSets = new ArrayList<>();
lineDataSets.add(lineDataSet);
final LineData lineData = new LineData(lineDataSets);
stepsChart.setData(lineData);
} }
@Override @Override
protected void renderCharts() {} protected void renderCharts() {
stepsChart.invalidate();
}
protected void setupLegend(Chart<?> chart) {} protected void setupLegend(Chart<?> chart) {}
private void setupStepsChart() {
stepsChart.getDescription().setEnabled(false);
stepsChart.setDoubleTapToZoomEnabled(false);
final XAxis xAxisBottom = stepsChart.getXAxis();
xAxisBottom.setPosition(XAxis.XAxisPosition.BOTTOM);
xAxisBottom.setDrawLabels(true);
xAxisBottom.setDrawGridLines(false);
xAxisBottom.setEnabled(true);
xAxisBottom.setDrawLimitLinesBehindData(true);
xAxisBottom.setTextColor(CHART_TEXT_COLOR);
xAxisBottom.setAxisMinimum(0f);
xAxisBottom.setAxisMaximum(86400f);
//xAxisBottom.setLabelCount(7, true);
final YAxis yAxisLeft = stepsChart.getAxisLeft();
yAxisLeft.setDrawGridLines(true);
yAxisLeft.setAxisMinimum(0);
yAxisLeft.setDrawTopYLabelEntry(true);
yAxisLeft.setEnabled(true);
yAxisLeft.setTextColor(CHART_TEXT_COLOR);
final YAxis yAxisRight = stepsChart.getAxisRight();
yAxisRight.setEnabled(true);
yAxisRight.setDrawLabels(false);
yAxisRight.setDrawGridLines(false);
yAxisRight.setDrawAxisLine(true);
}
Bitmap drawGauge(int width, int barWidth, @ColorInt int filledColor, int value, int maxValue) { Bitmap drawGauge(int width, int barWidth, @ColorInt int filledColor, int value, int maxValue) {
int height = width; int height = width;
int barMargin = (int) Math.ceil(barWidth / 2f); int barMargin = (int) Math.ceil(barWidth / 2f);
@ -170,4 +284,14 @@ public class StepsDailyFragment extends StepsFragment {
return bitmap; return bitmap;
} }
protected static class StepsData extends ChartsData {
StepsDay todayStepsDay;
List<? extends ActivitySample> samples;
public StepsData(final StepsDay todayStepsDay, final List<? extends ActivitySample> samplesOfDay) {
this.todayStepsDay = todayStepsDay;
this.samples = samplesOfDay;
}
}
} }

View File

@ -1,7 +1,6 @@
package nodomain.freeyourgadget.gadgetbridge.activities.charts; package nodomain.freeyourgadget.gadgetbridge.activities.charts;
import android.app.Activity; import android.app.Activity;
import android.os.Bundle;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -21,7 +20,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue; import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue;
abstract class StepsFragment extends AbstractChartFragment<StepsDailyFragment.StepsData> { abstract class StepsFragment<T extends ChartsData> extends AbstractChartFragment<T> {
protected static final Logger LOG = LoggerFactory.getLogger(StepsDailyFragment.class); protected static final Logger LOG = LoggerFactory.getLogger(StepsDailyFragment.class);
protected int CHART_TEXT_COLOR; protected int CHART_TEXT_COLOR;
@ -93,7 +92,7 @@ abstract class StepsFragment extends AbstractChartFragment<StepsDailyFragment.St
return amounts; return amounts;
} }
private List<? extends ActivitySample> getSamplesOfDay(DBHandler db, Calendar day, int offsetHours, GBDevice device) { protected List<? extends ActivitySample> getSamplesOfDay(DBHandler db, Calendar day, int offsetHours, GBDevice device) {
int startTs; int startTs;
int endTs; int endTs;
@ -126,28 +125,5 @@ abstract class StepsFragment extends AbstractChartFragment<StepsDailyFragment.St
} }
} }
protected static class StepsData extends ChartsData {
List<StepsDay> days;
long stepsDailyAvg = 0;
double distanceDailyAvg = 0;
long totalSteps = 0;
double totalDistance = 0;
StepsDay todayStepsDay;
protected StepsData(List<StepsDay> days) {
this.days = days;
int daysCounter = 0;
for(StepsDay day : days) {
this.totalSteps += day.steps;
this.totalDistance += day.distance;
if (day.steps > 0) {
daysCounter++;
}
}
if (daysCounter > 0) {
this.stepsDailyAvg = this.totalSteps / daysCounter;
this.distanceDailyAvg = this.totalDistance / daysCounter;
}
this.todayStepsDay = days.get(days.size() - 1);
}
}
} }

View File

@ -34,9 +34,8 @@ import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser; import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
import nodomain.freeyourgadget.gadgetbridge.service.devices.sony.wena3.protocol.packets.settings.DayStartHourSetting;
public class StepsPeriodFragment extends StepsFragment { public class StepsPeriodFragment extends StepsFragment<StepsPeriodFragment.StepsData> {
protected static final Logger LOG = LoggerFactory.getLogger(BodyEnergyFragment.class); protected static final Logger LOG = LoggerFactory.getLogger(BodyEnergyFragment.class);
private TextView mDateView; private TextView mDateView;
@ -113,7 +112,9 @@ public class StepsPeriodFragment extends StepsFragment {
yAxisLeft.setEnabled(true); yAxisLeft.setEnabled(true);
yAxisLeft.setTextColor(CHART_TEXT_COLOR); yAxisLeft.setTextColor(CHART_TEXT_COLOR);
yAxisLeft.setAxisMinimum(0f); yAxisLeft.setAxisMinimum(0f);
LimitLine target = new LimitLine(STEPS_GOAL); final LimitLine target = new LimitLine(STEPS_GOAL);
target.setLineColor(Color.GRAY);
target.enableDashedLine(10f, 10f, 0f);
yAxisLeft.addLimitLine(target); yAxisLeft.addLimitLine(target);
final YAxis yAxisRight = stepsChart.getAxisRight(); final YAxis yAxisRight = stepsChart.getAxisRight();
@ -138,7 +139,7 @@ public class StepsPeriodFragment extends StepsFragment {
} }
@Override @Override
protected StepsFragment.StepsData refreshInBackground(ChartsHost chartsHost, DBHandler db, GBDevice device) { protected StepsData refreshInBackground(ChartsHost chartsHost, DBHandler db, GBDevice device) {
Calendar day = Calendar.getInstance(); Calendar day = Calendar.getInstance();
Date to = new Date((long) this.getTSEnd() * 1000); Date to = new Date((long) this.getTSEnd() * 1000);
Date from = DateUtils.addDays(to,-(TOTAL_DAYS - 1)); Date from = DateUtils.addDays(to,-(TOTAL_DAYS - 1));
@ -147,11 +148,11 @@ public class StepsPeriodFragment extends StepsFragment {
mDateView.setText(fromFormattedDate + " - " + toFormattedDate); mDateView.setText(fromFormattedDate + " - " + toFormattedDate);
day.setTime(to); day.setTime(to);
List<StepsDay> stepsDaysData = getMyStepsDaysData(db, day, device); List<StepsDay> stepsDaysData = getMyStepsDaysData(db, day, device);
return new StepsFragment.StepsData(stepsDaysData); return new StepsData(stepsDaysData);
} }
@Override @Override
protected void updateChartsnUIThread(StepsPeriodFragment.StepsData stepsData) { protected void updateChartsnUIThread(StepsData stepsData) {
stepsChart.setData(null); stepsChart.setData(null);
List<BarEntry> entries = new ArrayList<>(); List<BarEntry> entries = new ArrayList<>();
@ -197,4 +198,29 @@ public class StepsPeriodFragment extends StepsFragment {
} }
protected void setupLegend(Chart<?> chart) {} protected void setupLegend(Chart<?> chart) {}
protected static class StepsData extends ChartsData {
List<StepsDay> days;
long stepsDailyAvg = 0;
double distanceDailyAvg = 0;
long totalSteps = 0;
double totalDistance = 0;
StepsDay todayStepsDay;
protected StepsData(List<StepsDay> days) {
this.days = days;
int daysCounter = 0;
for(StepsDay day : days) {
this.totalSteps += day.steps;
this.totalDistance += day.distance;
if (day.steps > 0) {
daysCounter++;
}
}
if (daysCounter > 0) {
this.stepsDailyAvg = this.totalSteps / daysCounter;
this.distanceDailyAvg = this.totalDistance / daysCounter;
}
this.todayStepsDay = days.get(days.size() - 1);
}
}
} }

View File

@ -123,14 +123,15 @@
</TableRow> </TableRow>
</TableLayout> </TableLayout>
<TextView <LinearLayout
android:id="@+id/steps_chart_title" android:layout_width="match_parent"
android:layout_width="wrap_content" android:layout_height="250sp">
android:layout_height="wrap_content" <com.github.mikephil.charting.charts.LineChart
android:layout_gravity="left" android:id="@+id/steps_daily_chart"
android:text="" android:layout_width="0dp"
android:paddingLeft="20dip" android:layout_height="fill_parent"
android:textSize="20sp" /> android:layout_weight="2" />
</LinearLayout>
</LinearLayout> </LinearLayout>
</ScrollView> </ScrollView>