1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-07-08 22:51:37 +02:00

Allow multiple sleep sessions per day

This commit is contained in:
Q-er 2019-09-17 00:47:10 +02:00 committed by Andreas Shimokawa
parent 744848fae7
commit 371ac276a5
2 changed files with 171 additions and 55 deletions

View File

@ -0,0 +1,102 @@
package nodomain.freeyourgadget.gadgetbridge.activities.charts;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
public class SleepAnalysis {
public static final long MIN_SESSION_LENGTH = 5 * 60;
public static final long MAX_WAKE_PHASE_LENGTH = 2 * 60 * 60;
public List<SleepSession> calculateSleepSessions(List<? extends ActivitySample> samples) {
List<SleepSession> result = new ArrayList<>();
ActivitySample previousSample = null;
Date sleepStart = null;
Date sleepEnd = null;
long lightSleepDuration = 0;
long deepSleepDuration = 0;
long durationSinceLastSleep = 0;
for (ActivitySample sample : samples) {
if (isSleep(sample)) {
if (sleepStart == null)
sleepStart = getDateFromSample(sample);
sleepEnd = getDateFromSample(sample);
durationSinceLastSleep = 0;
}
if (previousSample != null) {
long durationSinceLastSample = sample.getTimestamp() - previousSample.getTimestamp();
if (sample.getKind() == ActivityKind.TYPE_LIGHT_SLEEP) {
lightSleepDuration += durationSinceLastSample;
} else if (sample.getKind() == ActivityKind.TYPE_DEEP_SLEEP) {
deepSleepDuration += durationSinceLastSample;
} else {
durationSinceLastSleep += durationSinceLastSample;
if (sleepStart != null && durationSinceLastSleep > MAX_WAKE_PHASE_LENGTH) {
if (lightSleepDuration + deepSleepDuration > MIN_SESSION_LENGTH)
result.add(new SleepSession(sleepStart, sleepEnd, lightSleepDuration, deepSleepDuration));
sleepStart = null;
sleepEnd = null;
lightSleepDuration = 0;
deepSleepDuration = 0;
}
}
}
previousSample = sample;
}
if (lightSleepDuration + deepSleepDuration > MIN_SESSION_LENGTH) {
result.add(new SleepSession(sleepStart, sleepEnd, lightSleepDuration, deepSleepDuration));
}
return result;
}
private boolean isSleep(ActivitySample sample) {
return sample.getKind() == ActivityKind.TYPE_DEEP_SLEEP || sample.getKind() == ActivityKind.TYPE_LIGHT_SLEEP;
}
private Date getDateFromSample(ActivitySample sample) {
return new Date(sample.getTimestamp() * 1000L);
}
public static class SleepSession {
private final Date sleepStart;
private final Date sleepEnd;
private final long lightSleepDuration;
private final long deepSleepDuration;
private SleepSession(Date sleepStart,
Date sleepEnd,
long lightSleepDuration,
long deepSleepDuration) {
this.sleepStart = sleepStart;
this.sleepEnd = sleepEnd;
this.lightSleepDuration = lightSleepDuration;
this.deepSleepDuration = deepSleepDuration;
}
public Date getSleepStart() {
return sleepStart;
}
public Date getSleepEnd() {
return sleepEnd;
}
public long getLightSleepDuration() {
return lightSleepDuration;
}
public long getDeepSleepDuration() {
return deepSleepDuration;
}
}
}

View File

@ -25,8 +25,6 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.Nullable;
import com.github.mikephil.charting.animation.Easing; import com.github.mikephil.charting.animation.Easing;
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.charts.LineChart;
@ -44,16 +42,17 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils; import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils;
import nodomain.freeyourgadget.gadgetbridge.activities.charts.SleepAnalysis.SleepSession;
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.ActivityAmount; import nodomain.freeyourgadget.gadgetbridge.model.ActivityAmount;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityAmounts;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
@ -82,39 +81,35 @@ public class SleepChartFragment extends AbstractChartFragment {
} }
private MySleepChartsData refreshSleepAmounts(GBDevice mGBDevice, List<? extends ActivitySample> samples) { private MySleepChartsData refreshSleepAmounts(GBDevice mGBDevice, List<? extends ActivitySample> samples) {
ActivityAnalysis analysis = new ActivityAnalysis(); SleepAnalysis sleepAnalysis = new SleepAnalysis();
ActivityAmounts amounts = analysis.calculateActivityAmounts(samples); List<SleepSession> sleepSessions = sleepAnalysis.calculateSleepSessions(samples);
PieData data = new PieData(); PieData data = new PieData();
List<PieEntry> entries = new ArrayList<>();
List<Integer> colors = new ArrayList<>();
// int index = 0;
long totalSeconds = 0;
Date startSleep = null;
Date endSleep = null;
for (ActivityAmount amount : amounts.getAmounts()) { final long lightSleepDuration = calculateLightSleepDuration(sleepSessions);
if ((amount.getActivityKind() & ActivityKind.TYPE_SLEEP) != 0) { final long deepSleepDuration = calculateDeepSleepDuration(sleepSessions);
long value = amount.getTotalSeconds();
if(startSleep == null){ final long totalSeconds = lightSleepDuration + deepSleepDuration;
startSleep = amount.getStartDate();
} else { final List<PieEntry> entries;
if(startSleep.after(amount.getStartDate())) final List<Integer> colors;
startSleep = amount.getStartDate();
} if (sleepSessions.isEmpty()) {
if(endSleep == null){ entries = Collections.emptyList();
endSleep = amount.getEndDate(); colors = Collections.emptyList();
} else { } else {
if(endSleep.before(amount.getEndDate())) entries = Arrays.asList(
endSleep = amount.getEndDate(); new PieEntry(lightSleepDuration, getActivity().getString(R.string.abstract_chart_fragment_kind_light_sleep)),
} new PieEntry(deepSleepDuration, getActivity().getString(R.string.abstract_chart_fragment_kind_deep_sleep))
totalSeconds += value; );
// entries.add(new PieEntry(value, index++)); colors = Arrays.asList(
entries.add(new PieEntry(value, amount.getName(getActivity()))); getColorFor(ActivityKind.TYPE_LIGHT_SLEEP),
colors.add(getColorFor(amount.getActivityKind())); getColorFor(ActivityKind.TYPE_DEEP_SLEEP)
// data.addXValue(amount.getName(getActivity())); );
}
} }
String totalSleep = DateTimeUtils.formatDurationHoursMinutes(totalSeconds, TimeUnit.SECONDS); String totalSleep = DateTimeUtils.formatDurationHoursMinutes(totalSeconds, TimeUnit.SECONDS);
PieDataSet set = new PieDataSet(entries, ""); PieDataSet set = new PieDataSet(entries, "");
set.setValueFormatter(new ValueFormatter() { set.setValueFormatter(new ValueFormatter() {
@ -131,27 +126,54 @@ public class SleepChartFragment extends AbstractChartFragment {
data.setDataSet(set); data.setDataSet(set);
//setupLegend(pieChart); //setupLegend(pieChart);
return new MySleepChartsData(totalSleep, data, startSleep, endSleep); return new MySleepChartsData(totalSleep, data, sleepSessions);
}
private long calculateLightSleepDuration(List<SleepSession> sleepSessions) {
long result = 0;
for (SleepSession sleepSession : sleepSessions) {
result += sleepSession.getLightSleepDuration();
}
return result;
}
private long calculateDeepSleepDuration(List<SleepSession> sleepSessions) {
long result = 0;
for (SleepSession sleepSession : sleepSessions) {
result += sleepSession.getDeepSleepDuration();
}
return result;
} }
@Override @Override
protected void updateChartsnUIThread(ChartsData chartsData) { protected void updateChartsnUIThread(ChartsData chartsData) {
MyChartsData mcd = (MyChartsData) chartsData; MyChartsData mcd = (MyChartsData) chartsData;
mSleepAmountChart.setCenterText(mcd.getPieData().getTotalSleep()); MySleepChartsData pieData = mcd.getPieData();
mSleepAmountChart.setData(mcd.getPieData().getPieData()); mSleepAmountChart.setCenterText(pieData.getTotalSleep());
mSleepAmountChart.setData(pieData.getPieData());
mActivityChart.setData(null); // workaround for https://github.com/PhilJay/MPAndroidChart/issues/2317 mActivityChart.setData(null); // workaround for https://github.com/PhilJay/MPAndroidChart/issues/2317
mActivityChart.getXAxis().setValueFormatter(mcd.getChartsData().getXValueFormatter()); mActivityChart.getXAxis().setValueFormatter(mcd.getChartsData().getXValueFormatter());
mActivityChart.setData(mcd.getChartsData().getData()); mActivityChart.setData(mcd.getChartsData().getData());
if (mcd.getPieData().getStartSleep() != null && mcd.getPieData().getEndSleep() != null) {
mSleepchartInfo.setText(getContext().getString( mSleepchartInfo.setText(buildYouSleptText(pieData));
R.string.you_slept, }
DateTimeUtils.timeToString(mcd.getPieData().getStartSleep()),
DateTimeUtils.timeToString(mcd.getPieData().getEndSleep()))); private String buildYouSleptText(MySleepChartsData pieData) {
final StringBuilder result = new StringBuilder();
if (pieData.getSleepSessions().isEmpty()) {
result.append(getContext().getString(R.string.you_did_not_sleep));
} else { } else {
mSleepchartInfo.setText(getContext().getString(R.string.you_did_not_sleep)); for (SleepSession sleepSession : pieData.getSleepSessions()) {
result.append(getContext().getString(
R.string.you_slept,
DateTimeUtils.timeToString(sleepSession.getSleepStart()),
DateTimeUtils.timeToString(sleepSession.getSleepEnd())));
result.append('\n');
}
} }
return result.toString();
} }
@Override @Override
@ -275,14 +297,12 @@ public class SleepChartFragment extends AbstractChartFragment {
private static class MySleepChartsData extends ChartsData { private static class MySleepChartsData extends ChartsData {
private String totalSleep; private String totalSleep;
private final PieData pieData; private final PieData pieData;
private @Nullable Date startSleep; private final List<SleepSession> sleepSessions;
private @Nullable Date endSleep;
public MySleepChartsData(String totalSleep, PieData pieData, @Nullable Date startSleep, @Nullable Date endSleep) { public MySleepChartsData(String totalSleep, PieData pieData, List<SleepSession> sleepSessions) {
this.totalSleep = totalSleep; this.totalSleep = totalSleep;
this.pieData = pieData; this.pieData = pieData;
this.startSleep = startSleep; this.sleepSessions = sleepSessions;
this.endSleep = endSleep;
} }
public PieData getPieData() { public PieData getPieData() {
@ -293,14 +313,8 @@ public class SleepChartFragment extends AbstractChartFragment {
return totalSleep; return totalSleep;
} }
@Nullable public List<SleepSession> getSleepSessions() {
public Date getStartSleep() { return sleepSessions;
return startSleep;
}
@Nullable
public Date getEndSleep() {
return endSleep;
} }
} }