From 9a0e9fd05a29eead451087a8ea9a490b2916ed7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Rebelo?= Date: Fri, 3 Oct 2025 20:22:02 +0100 Subject: [PATCH] CMF Watch Pro: Fix some activity samples being discarded --- .../devices/AbstractSampleProvider.java | 4 +- .../samples/CmfActivitySampleProvider.java | 48 ++++++++++++------- 2 files changed, 34 insertions(+), 18 deletions(-) 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 4b21a18dcd..84d5c7e87c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java @@ -187,7 +187,8 @@ public abstract class AbstractSampleProvider i } Property deviceProperty = getDeviceIdentifierSampleProperty(); qb.where(deviceProperty.eq(dbDevice.getId()), timestampProperty.ge(timestamp_from)) - .where(timestampProperty.le(timestamp_to)); + .where(timestampProperty.le(timestamp_to)) + .orderAsc(timestampProperty); List samples = qb.build().list(); for (T sample : samples) { sample.setProvider(this); @@ -264,6 +265,7 @@ public abstract class AbstractSampleProvider i int prevSteps = samples.get(0).getSteps(); int prevDistance = samples.get(0).getDistanceCm(); int prevActiveCalories = samples.get(0).getActiveCalories(); + // Round timestamp to the nearest minute samples.get(0).setTimestamp((samples.get(0).getTimestamp() / 60) * 60); int bak; 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 d1c42e511e..674daf0060 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 @@ -22,13 +22,13 @@ import androidx.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.time.LocalDate; import java.util.ArrayList; -import java.util.Calendar; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import de.greenrobot.dao.AbstractDao; import de.greenrobot.dao.Property; @@ -108,16 +108,13 @@ public class CmfActivitySampleProvider extends AbstractSampleProvider sampleByTs = new HashMap<>(); - for (final CmfActivitySample sample : samples) { - sampleByTs.put(sample.getTimestamp(), sample); - } + final Map sampleByTs = getActivitySampleMapByTimestamp(samples); overlayHeartRate(sampleByTs, timestamp_from, timestamp_to); overlaySleep(sampleByTs, timestamp_from, timestamp_to); final List finalSamples = new ArrayList<>(sampleByTs.values()); - Collections.sort(finalSamples, (a, b) -> Integer.compare(a.getTimestamp(), b.getTimestamp())); + Collections.sort(finalSamples, Comparator.comparingInt(CmfActivitySample::getTimestamp)); final long nanoEnd = System.nanoTime(); @@ -128,6 +125,27 @@ public class CmfActivitySampleProvider extends AbstractSampleProvider getActivitySampleMapByTimestamp(final List samples) { + final Map sampleByTs = new HashMap<>(samples.size()); + for (final CmfActivitySample sample : samples) { + sampleByTs.compute(sample.getTimestamp(), (k, existingSample) -> { + // Combine potential duplicates introduced by convertCumulativeSteps + if (existingSample == null) { + return sample; + } + existingSample.setRawIntensity(Math.max(sample.getRawIntensity(), existingSample.getRawIntensity())); + existingSample.setSteps(Math.max(sample.getSteps(), existingSample.getSteps())); + existingSample.setHeartRate(Math.max(sample.getHeartRate(), existingSample.getHeartRate())); + existingSample.setDistance(Math.max(Objects.requireNonNullElse(sample.getDistance(), -1), Objects.requireNonNullElse(existingSample.getDistance(), -1))); + existingSample.setCalories(Math.max(Objects.requireNonNullElse(sample.getCalories(), -1), Objects.requireNonNullElse(existingSample.getCalories(), -1))); + existingSample.setCalories(Math.max(sample.getHeartRate(), existingSample.getHeartRate())); + return existingSample; + }); + } + return sampleByTs; + } + 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); @@ -173,15 +191,11 @@ public class CmfActivitySampleProvider extends AbstractSampleProvider ActivityKind.DEEP_SLEEP; + case 2 -> ActivityKind.LIGHT_SLEEP; + case 3 -> ActivityKind.REM_SLEEP; + default -> ActivityKind.UNKNOWN; + }; } }