diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java index d020116e2..f7886e44b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java @@ -18,11 +18,15 @@ package nodomain.freeyourgadget.gadgetbridge.devices; import java.util.ArrayList; +import java.util.Calendar; import java.util.Collections; import java.util.List; import androidx.annotation.NonNull; import androidx.annotation.Nullable; + +import org.threeten.bp.LocalDate; + import de.greenrobot.dao.AbstractDao; import de.greenrobot.dao.Property; import de.greenrobot.dao.query.QueryBuilder; @@ -33,7 +37,6 @@ import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; -import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; /** * Base class for all sample providers. A Sample provider is device specific and provides @@ -233,4 +236,64 @@ public abstract class AbstractSampleProvider i @NonNull protected abstract Property getDeviceIdentifierSampleProperty(); + + public void convertCumulativeSteps(final List samples, final Property stepsSampleProperty) { + final T lastSample = getLastSampleWithStepsBefore(samples.get(0).getTimestamp(), stepsSampleProperty); + if (lastSample != null && sameDay(lastSample, samples.get(0)) && samples.get(0).getSteps() > 0) { + samples.get(0).setSteps(samples.get(0).getSteps() - lastSample.getSteps()); + } + + // Steps on the Garmin Watch are reported cumulatively per day - convert them to + // This slightly breaks activity recognition, because we don't have per-minute granularity... + int prevSteps = samples.get(0).getSteps(); + samples.get(0).setTimestamp((samples.get(0).getTimestamp() / 60) * 60); + + for (int i = 1; i < samples.size(); i++) { + final T s1 = samples.get(i - 1); + final T s2 = samples.get(i); + s2.setTimestamp((s2.getTimestamp() / 60) * 60); + + if (!sameDay(s1, s2)) { + // went past midnight - reset steps + prevSteps = s2.getSteps() > 0 ? s2.getSteps() : 0; + } else if (s2.getSteps() > 0) { + // New steps sample for the current day - subtract the previous seen sample + int bak = s2.getSteps(); + s2.setSteps(s2.getSteps() - prevSteps); + prevSteps = bak; + } + } + } + + @Nullable + public T getLastSampleWithStepsBefore(final int timestampTo, final Property stepsSampleProperty) { + final Device dbDevice = DBHelper.findDevice(getDevice(), getSession()); + if (dbDevice == null) { + // no device, no sample + return null; + } + + final List samples = getSampleDao().queryBuilder() + .where( + getDeviceIdentifierSampleProperty().eq(dbDevice.getId()), + getTimestampSampleProperty().le(timestampTo), + stepsSampleProperty.gt(-1) + ).orderDesc(getTimestampSampleProperty()) + .limit(1) + .list(); + + return !samples.isEmpty() ? samples.get(0) : null; + } + + public boolean sameDay(final T s1, final T s2) { + final Calendar cal = Calendar.getInstance(); + + cal.setTimeInMillis(s1.getTimestamp() * 1000L - 1000L); + final LocalDate d1 = LocalDate.of(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH)); + + cal.setTimeInMillis(s2.getTimestamp() * 1000L - 1000L); + final LocalDate d2 = LocalDate.of(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH)); + + return d1.equals(d2); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/cmfwatchpro/samples/CmfActivitySampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/cmfwatchpro/samples/CmfActivitySampleProvider.java index 1dec31d0b..5f63b97e4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/cmfwatchpro/samples/CmfActivitySampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/cmfwatchpro/samples/CmfActivitySampleProvider.java @@ -105,7 +105,7 @@ public class CmfActivitySampleProvider extends AbstractSampleProvider samples = super.getGBActivitySamples(timestamp_from, timestamp_to, activityType); if (!samples.isEmpty()) { - convertCumulativeSteps(samples); + convertCumulativeSteps(samples, CmfActivitySampleDao.Properties.Steps); } final Map sampleByTs = new HashMap<>(); @@ -128,32 +128,6 @@ public class CmfActivitySampleProvider extends AbstractSampleProvider samples) { - final Calendar cal = Calendar.getInstance(); - - // Steps on the Cmf Watch are reported cumulatively per day - convert them to - // This slightly breaks activity recognition, because we don't have per-minute granularity... - int prevSteps = samples.get(0).getSteps(); - samples.get(0).setTimestamp((int) (samples.get(0).getTimestamp() / 60) * 60); - - for (int i = 1; i < samples.size(); i++) { - final CmfActivitySample s1 = samples.get(i - 1); - final CmfActivitySample s2 = samples.get(i); - s2.setTimestamp((int) (s2.getTimestamp() / 60) * 60); - - cal.setTimeInMillis(s1.getTimestamp() * 1000L - 1000L); - final LocalDate d1 = LocalDate.of(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH)); - cal.setTimeInMillis(s2.getTimestamp() * 1000L - 1000L); - final LocalDate d2 = LocalDate.of(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH)); - - if (d1.equals(d2)) { - int bak = s2.getSteps(); - s2.setSteps(s2.getSteps() - prevSteps); - prevSteps = bak; - } - } - } - private void overlayHeartRate(final Map sampleByTs, final int timestamp_from, final int timestamp_to) { final CmfHeartRateSampleProvider heartRateSampleProvider = new CmfHeartRateSampleProvider(getDevice(), getSession()); final List hrSamples = heartRateSampleProvider.getAllSamples(timestamp_from * 1000L, timestamp_to * 1000L); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/GarminActivitySampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/GarminActivitySampleProvider.java index 084d9c034..8a6412b10 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/GarminActivitySampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/GarminActivitySampleProvider.java @@ -21,21 +21,17 @@ import androidx.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.threeten.bp.LocalDate; -import java.util.Calendar; import java.util.List; import de.greenrobot.dao.AbstractDao; import de.greenrobot.dao.Property; import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider; -import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.XiaomiSleepTimeSampleProvider; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.GarminActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.GarminActivitySampleDao; import nodomain.freeyourgadget.gadgetbridge.entities.GarminEventSample; import nodomain.freeyourgadget.gadgetbridge.entities.GarminSleepStageSample; -import nodomain.freeyourgadget.gadgetbridge.entities.XiaomiSleepTimeSample; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionSleepStage; @@ -105,7 +101,7 @@ public class GarminActivitySampleProvider extends AbstractSampleProvider samples = super.getGBActivitySamples(timestamp_from, timestamp_to, activityType); if (!samples.isEmpty()) { - convertCumulativeSteps(samples); + convertCumulativeSteps(samples, GarminActivitySampleDao.Properties.Steps); } overlaySleep(samples, timestamp_from, timestamp_to); @@ -119,36 +115,6 @@ public class GarminActivitySampleProvider extends AbstractSampleProvider samples) { - final Calendar cal = Calendar.getInstance(); - - // Steps on the Garmin Watch are reported cumulatively per day - convert them to - // This slightly breaks activity recognition, because we don't have per-minute granularity... - int prevSteps = samples.get(0).getSteps(); - samples.get(0).setTimestamp((samples.get(0).getTimestamp() / 60) * 60); - - for (int i = 1; i < samples.size(); i++) { - final GarminActivitySample s1 = samples.get(i - 1); - final GarminActivitySample s2 = samples.get(i); - s2.setTimestamp((s2.getTimestamp() / 60) * 60); - - cal.setTimeInMillis(s1.getTimestamp() * 1000L - 1000L); - final LocalDate d1 = LocalDate.of(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH)); - cal.setTimeInMillis(s2.getTimestamp() * 1000L - 1000L); - final LocalDate d2 = LocalDate.of(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH)); - - if (!d1.equals(d2)) { - // went past midnight - reset steps - prevSteps = s2.getSteps() > 0 ? s2.getSteps() : 0; - } else if (s2.getSteps() > 0) { - // New steps sample for the current day - subtract the previous seen sample - int bak = s2.getSteps(); - s2.setSteps(s2.getSteps() - prevSteps); - prevSteps = bak; - } - } - } - public void overlaySleep(final List samples, final int timestamp_from, final int timestamp_to) { // The samples provided by Garmin are upper-bound timestamps of the sleep stage final RangeMap stagesMap = new RangeMap<>(RangeMap.Mode.UPPER_BOUND);