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 4c48e51f9..453b0934d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractSampleProvider.java @@ -20,11 +20,16 @@ package nodomain.freeyourgadget.gadgetbridge.devices; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.time.Instant; import java.time.LocalDate; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.List; +import java.util.ListIterator; import de.greenrobot.dao.AbstractDao; import de.greenrobot.dao.Property; @@ -36,6 +41,7 @@ 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.model.ActivitySample; /** * Base class for all sample providers. A Sample provider is device specific and provides @@ -43,6 +49,8 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; * @param the sample type */ public abstract class AbstractSampleProvider implements SampleProvider { + private static final Logger LOG = LoggerFactory.getLogger(AbstractSampleProvider.class); + private static final WhereCondition[] NO_CONDITIONS = new WhereCondition[0]; private final DaoSession mSession; private final GBDevice mDevice; @@ -60,11 +68,13 @@ public abstract class AbstractSampleProvider i return mSession; } + @NonNull @Override public List getAllActivitySamples(int timestamp_from, int timestamp_to) { return getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_ALL); } + @NonNull @Override public List getActivitySamples(int timestamp_from, int timestamp_to) { if (getRawKindSampleProperty() != null) { @@ -74,6 +84,7 @@ public abstract class AbstractSampleProvider i } } + @NonNull @Override public List getSleepSamples(int timestamp_from, int timestamp_to) { final DeviceCoordinator coordinator = getDevice().getDeviceCoordinator(); @@ -167,14 +178,14 @@ public abstract class AbstractSampleProvider i /** * Detaches all samples of this type from the session. Changes to them may not be * written back to the database. - * + *

* Subclasses should call this method after performing custom queries. */ protected void detachFromSession() { getSampleDao().detachAll(); } - private WhereCondition[] getClauseForActivityType(QueryBuilder qb, int activityTypes) { + private WhereCondition[] getClauseForActivityType(QueryBuilder qb, int activityTypes) { if (activityTypes == ActivityKind.TYPE_ALL) { return NO_CONDITIONS; } @@ -184,7 +195,7 @@ public abstract class AbstractSampleProvider i return new WhereCondition[] { activityTypeCondition }; } - private WhereCondition getActivityTypeConditions(QueryBuilder qb, int[] dbActivityTypes) { + private WhereCondition getActivityTypeConditions(QueryBuilder qb, int[] dbActivityTypes) { // What a crappy QueryBuilder API ;-( QueryBuilder.or(WhereCondition[]) with a runtime array length // check would have worked just fine. if (dbActivityTypes.length == 0) { @@ -295,4 +306,73 @@ public abstract class AbstractSampleProvider i return d1.equals(d2); } + + protected List fillGaps(final List samples, final int timestamp_from, final int timestamp_to) { + if (samples.isEmpty()) { + return samples; + } + + final long nanoStart = System.nanoTime(); + + final List ret = new ArrayList<>(samples); + + //ret.sort(Comparator.comparingLong(T::getTimestamp)); + + final int firstTimestamp = ret.get(0).getTimestamp(); + if (firstTimestamp - timestamp_from > 60) { + // Gap at the start + for (int ts = timestamp_from; ts <= firstTimestamp + 60; ts += 60) { + final T dummySample = createActivitySample(); + dummySample.setTimestamp(ts); + dummySample.setRawKind(ActivityKind.TYPE_UNKNOWN); + dummySample.setRawIntensity(ActivitySample.NOT_MEASURED); + dummySample.setSteps(ActivitySample.NOT_MEASURED); + dummySample.setProvider(this); + ret.add(0, dummySample); + } + } + + final int lastTimestamp = ret.get(ret.size() - 1).getTimestamp(); + if (timestamp_to - lastTimestamp > 60) { + // Gap at the end + for (int ts = lastTimestamp + 60; ts <= timestamp_to; ts += 60) { + final T dummySample = createActivitySample(); + dummySample.setTimestamp(ts); + dummySample.setRawKind(ActivityKind.TYPE_UNKNOWN); + dummySample.setRawIntensity(ActivitySample.NOT_MEASURED); + dummySample.setSteps(ActivitySample.NOT_MEASURED); + dummySample.setProvider(this); + ret.add(dummySample); + } + } + + final ListIterator it = ret.listIterator(); + T previousSample = it.next(); + + while (it.hasNext()) { + final T sample = it.next(); + if (sample.getTimestamp() - previousSample.getTimestamp() > 60) { + LOG.trace("Filling gap between {} and {}", Instant.ofEpochSecond(previousSample.getTimestamp() + 60), Instant.ofEpochSecond(sample.getTimestamp())); + for (int ts = previousSample.getTimestamp() + 60; ts < sample.getTimestamp(); ts += 60) { + final T dummySample = createActivitySample(); + dummySample.setTimestamp(ts); + dummySample.setRawKind(ActivityKind.TYPE_UNKNOWN); + dummySample.setRawIntensity(ActivitySample.NOT_MEASURED); + dummySample.setSteps(ActivitySample.NOT_MEASURED); + dummySample.setProvider(this); + it.add(dummySample); + } + } + previousSample = sample; + } + + final long nanoEnd = System.nanoTime(); + + final long executionTime = (nanoEnd - nanoStart) / 1000000; + + final int dummyCount = ret.size() - samples.size(); + LOG.trace("Filled gaps with {} samples in {}ms", dummyCount, executionTime); + + return ret; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/casio/gbx100/CasioGBX100SampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/casio/gbx100/CasioGBX100SampleProvider.java index 890dad8e1..fe6cdf8c1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/casio/gbx100/CasioGBX100SampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/casio/gbx100/CasioGBX100SampleProvider.java @@ -85,11 +85,13 @@ public class CasioGBX100SampleProvider extends AbstractSampleProvider getActivitySamples(int timestamp_from, int timestamp_to) { return super.getActivitySamples(timestamp_from, timestamp_to); } + @NonNull @Override public List getAllActivitySamples(int timestamp_from, int timestamp_to) { return super.getActivitySamples(timestamp_from, timestamp_to); 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 33074c748..b92d51249 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 @@ -98,7 +98,11 @@ public class GarminActivitySampleProvider extends AbstractSampleProvider samples = super.getGBActivitySamples(timestamp_from, timestamp_to, activityType); + final List samples = fillGaps( + super.getGBActivitySamples(timestamp_from, timestamp_to, activityType), + timestamp_from, + timestamp_to + ); if (!samples.isEmpty()) { convertCumulativeSteps(samples, GarminActivitySampleDao.Properties.Steps); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java index 3dd0a86af..6b187af1d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/hplus/HPlusHealthSampleProvider.java @@ -118,6 +118,7 @@ public class HPlusHealthSampleProvider extends AbstractSampleProvider getSleepSamples(int timestamp_from, int timestamp_to) { return getAllActivitySamples(timestamp_from, timestamp_to); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/lenovo/watchxplus/WatchXPlusSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/lenovo/watchxplus/WatchXPlusSampleProvider.java index 469b82c4e..d99cec585 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/lenovo/watchxplus/WatchXPlusSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/lenovo/watchxplus/WatchXPlusSampleProvider.java @@ -132,6 +132,7 @@ public class WatchXPlusSampleProvider extends AbstractSampleProvider getAllActivitySamples(int timestamp_from, int timestamp_to) { boolean showRawData = GBApplication.getDeviceSpecificSharedPrefs(mDevice.getAddress()).getBoolean(WatchXPlusConstants.PREF_SHOW_RAW_GRAPH, false); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java index aa8acda3a..fee00ddeb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/pebble/PebbleHealthSampleProvider.java @@ -17,6 +17,8 @@ along with this program. If not, see . */ package nodomain.freeyourgadget.gadgetbridge.devices.pebble; +import androidx.annotation.NonNull; + import java.util.Collections; import java.util.List; @@ -50,6 +52,7 @@ public class PebbleHealthSampleProvider extends AbstractSampleProvider getAllActivitySamples(int timestamp_from, int timestamp_to) { List samples = super.getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_ALL); @@ -84,6 +87,7 @@ public class PebbleHealthSampleProvider extends AbstractSampleProvider. */ package nodomain.freeyourgadget.gadgetbridge.devices.pebble; +import androidx.annotation.NonNull; + import de.greenrobot.dao.AbstractDao; import de.greenrobot.dao.Property; import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider; @@ -62,10 +64,12 @@ public class PebbleMisfitSampleProvider extends AbstractSampleProvider. */ package nodomain.freeyourgadget.gadgetbridge.devices.pebble; +import androidx.annotation.NonNull; + import de.greenrobot.dao.AbstractDao; import de.greenrobot.dao.Property; import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider; @@ -37,6 +39,7 @@ public class PebbleMorpheuzSampleProvider extends AbstractSampleProvider getActivitySamples(int timestamp_from, int timestamp_to) { return super.getActivitySamples(timestamp_from, timestamp_to); } + @NonNull @Override public List getAllActivitySamples(int timestamp_from, int timestamp_to) { return super.getAllActivitySamples(timestamp_from, timestamp_to); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/withingssteelhr/WithingsSteelHRSampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/withingssteelhr/WithingsSteelHRSampleProvider.java index 0427be36c..4fa134172 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/withingssteelhr/WithingsSteelHRSampleProvider.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/withingssteelhr/WithingsSteelHRSampleProvider.java @@ -70,6 +70,7 @@ public class WithingsSteelHRSampleProvider extends AbstractSampleProvider getActivitySamples(int timestamp_from, int timestamp_to) { return super.getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_ALL);