From 371ac276a5ffa2935faada9a223a751f2f323098 Mon Sep 17 00:00:00 2001 From: Q-er <9142398+Q-er@users.noreply.github.com> Date: Tue, 17 Sep 2019 00:47:10 +0200 Subject: [PATCH] Allow multiple sleep sessions per day --- .../activities/charts/SleepAnalysis.java | 102 ++++++++++++++ .../activities/charts/SleepChartFragment.java | 124 ++++++++++-------- 2 files changed, 171 insertions(+), 55 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepAnalysis.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepAnalysis.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepAnalysis.java new file mode 100644 index 000000000..eec9d664a --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepAnalysis.java @@ -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 calculateSleepSessions(List samples) { + List 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; + } + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepChartFragment.java index e28011701..3d388c846 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepChartFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepChartFragment.java @@ -25,8 +25,6 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; -import androidx.annotation.Nullable; - import com.github.mikephil.charting.animation.Easing; import com.github.mikephil.charting.charts.Chart; import com.github.mikephil.charting.charts.LineChart; @@ -44,16 +42,17 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; -import java.util.Date; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils; +import nodomain.freeyourgadget.gadgetbridge.activities.charts.SleepAnalysis.SleepSession; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityAmount; -import nodomain.freeyourgadget.gadgetbridge.model.ActivityAmounts; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample; import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils; @@ -82,39 +81,35 @@ public class SleepChartFragment extends AbstractChartFragment { } private MySleepChartsData refreshSleepAmounts(GBDevice mGBDevice, List samples) { - ActivityAnalysis analysis = new ActivityAnalysis(); - ActivityAmounts amounts = analysis.calculateActivityAmounts(samples); + SleepAnalysis sleepAnalysis = new SleepAnalysis(); + List sleepSessions = sleepAnalysis.calculateSleepSessions(samples); + PieData data = new PieData(); - List entries = new ArrayList<>(); - List colors = new ArrayList<>(); -// int index = 0; - long totalSeconds = 0; - Date startSleep = null; - Date endSleep = null; - for (ActivityAmount amount : amounts.getAmounts()) { - if ((amount.getActivityKind() & ActivityKind.TYPE_SLEEP) != 0) { - long value = amount.getTotalSeconds(); - if(startSleep == null){ - startSleep = amount.getStartDate(); - } else { - if(startSleep.after(amount.getStartDate())) - startSleep = amount.getStartDate(); - } - if(endSleep == null){ - endSleep = amount.getEndDate(); - } else { - if(endSleep.before(amount.getEndDate())) - endSleep = amount.getEndDate(); - } - totalSeconds += value; -// entries.add(new PieEntry(value, index++)); - entries.add(new PieEntry(value, amount.getName(getActivity()))); - colors.add(getColorFor(amount.getActivityKind())); -// data.addXValue(amount.getName(getActivity())); - } + final long lightSleepDuration = calculateLightSleepDuration(sleepSessions); + final long deepSleepDuration = calculateDeepSleepDuration(sleepSessions); + + final long totalSeconds = lightSleepDuration + deepSleepDuration; + + final List entries; + final List colors; + + if (sleepSessions.isEmpty()) { + entries = Collections.emptyList(); + colors = Collections.emptyList(); + } else { + entries = Arrays.asList( + 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)) + ); + colors = Arrays.asList( + getColorFor(ActivityKind.TYPE_LIGHT_SLEEP), + getColorFor(ActivityKind.TYPE_DEEP_SLEEP) + ); } + + String totalSleep = DateTimeUtils.formatDurationHoursMinutes(totalSeconds, TimeUnit.SECONDS); PieDataSet set = new PieDataSet(entries, ""); set.setValueFormatter(new ValueFormatter() { @@ -131,27 +126,54 @@ public class SleepChartFragment extends AbstractChartFragment { data.setDataSet(set); //setupLegend(pieChart); - return new MySleepChartsData(totalSleep, data, startSleep, endSleep); + return new MySleepChartsData(totalSleep, data, sleepSessions); + } + + private long calculateLightSleepDuration(List sleepSessions) { + long result = 0; + for (SleepSession sleepSession : sleepSessions) { + result += sleepSession.getLightSleepDuration(); + } + return result; + } + + private long calculateDeepSleepDuration(List sleepSessions) { + long result = 0; + for (SleepSession sleepSession : sleepSessions) { + result += sleepSession.getDeepSleepDuration(); + } + return result; } @Override protected void updateChartsnUIThread(ChartsData chartsData) { MyChartsData mcd = (MyChartsData) chartsData; - mSleepAmountChart.setCenterText(mcd.getPieData().getTotalSleep()); - mSleepAmountChart.setData(mcd.getPieData().getPieData()); + MySleepChartsData pieData = mcd.getPieData(); + mSleepAmountChart.setCenterText(pieData.getTotalSleep()); + mSleepAmountChart.setData(pieData.getPieData()); mActivityChart.setData(null); // workaround for https://github.com/PhilJay/MPAndroidChart/issues/2317 mActivityChart.getXAxis().setValueFormatter(mcd.getChartsData().getXValueFormatter()); mActivityChart.setData(mcd.getChartsData().getData()); - if (mcd.getPieData().getStartSleep() != null && mcd.getPieData().getEndSleep() != null) { - mSleepchartInfo.setText(getContext().getString( - R.string.you_slept, - DateTimeUtils.timeToString(mcd.getPieData().getStartSleep()), - DateTimeUtils.timeToString(mcd.getPieData().getEndSleep()))); + + mSleepchartInfo.setText(buildYouSleptText(pieData)); + } + + 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 { - 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 @@ -275,14 +297,12 @@ public class SleepChartFragment extends AbstractChartFragment { private static class MySleepChartsData extends ChartsData { private String totalSleep; private final PieData pieData; - private @Nullable Date startSleep; - private @Nullable Date endSleep; + private final List sleepSessions; - public MySleepChartsData(String totalSleep, PieData pieData, @Nullable Date startSleep, @Nullable Date endSleep) { + public MySleepChartsData(String totalSleep, PieData pieData, List sleepSessions) { this.totalSleep = totalSleep; this.pieData = pieData; - this.startSleep = startSleep; - this.endSleep = endSleep; + this.sleepSessions = sleepSessions; } public PieData getPieData() { @@ -293,14 +313,8 @@ public class SleepChartFragment extends AbstractChartFragment { return totalSleep; } - @Nullable - public Date getStartSleep() { - return startSleep; - } - - @Nullable - public Date getEndSleep() { - return endSleep; + public List getSleepSessions() { + return sleepSessions; } }