mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-12-28 11:35:48 +01:00
parent
bbadb2b1ef
commit
ce7b0db5b1
@ -137,6 +137,12 @@ public abstract class AbstractSampleProvider<T extends AbstractActivitySample> i
|
||||
return sample;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the activity samples between two timestamps. Exactly one every minute.
|
||||
* @param timestamp_from Start timestamp
|
||||
* @param timestamp_to End timestamp
|
||||
* @return Exactly one sample for every minute
|
||||
*/
|
||||
protected List<T> getGBActivitySamples(int timestamp_from, int timestamp_to) {
|
||||
QueryBuilder<T> qb = getSampleDao().queryBuilder();
|
||||
Property timestampProperty = getTimestampSampleProperty();
|
||||
|
@ -16,21 +16,16 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.huawei;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import de.greenrobot.dao.AbstractDao;
|
||||
import de.greenrobot.dao.Property;
|
||||
import de.greenrobot.dao.query.QueryBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
@ -44,11 +39,12 @@ import nodomain.freeyourgadget.gadgetbridge.entities.HuaweiWorkoutSummarySampleD
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets.FitnessData;
|
||||
|
||||
public class HuaweiSampleProvider extends AbstractSampleProvider<HuaweiActivitySample> {
|
||||
/*
|
||||
* We save all data by saving a marker at the begin and end.
|
||||
* We save all data by saving a marker at the begin and end. We do not actively use these for
|
||||
* showing the data at the moment, but the samples are still saved as such, to keep the table
|
||||
* entries consistent.
|
||||
* Meaning of fields that are not self-explanatory:
|
||||
* - `otherTimestamp`
|
||||
* The timestamp of the other marker, if it's larger this is the begin, otherwise the end
|
||||
@ -289,296 +285,189 @@ public class HuaweiSampleProvider extends AbstractSampleProvider<HuaweiActivityS
|
||||
return samples;
|
||||
}
|
||||
|
||||
private static class SampleLoopState {
|
||||
public long deviceId = 0;
|
||||
public long userId = 0;
|
||||
|
||||
public int sleepModifier = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that this does a lot more than the normal implementation, as it takes care of everything
|
||||
* that is necessary for proper displaying of data.
|
||||
*
|
||||
* This essentially boils down to four things:
|
||||
* - It adds in the workout heart rate data without activity data in between
|
||||
* - It adds a sample with intensity zero before start markers (start of block)
|
||||
* - It adds a sample with intensity zero after end markers (end of block)
|
||||
* - It modifies some blocks so the sleep data gets handled correctly
|
||||
* The second and fourth are necessary for proper stats calculation, the third is mostly for
|
||||
* nicer graphs.
|
||||
*
|
||||
* Note that the data in the database isn't changed, as the samples are detached.
|
||||
* This takes the following three steps:
|
||||
* - Generate a sample every minute
|
||||
* - Add the activity sample data to the generated samples
|
||||
* - Add the workout data to the generated samples
|
||||
*/
|
||||
@Override
|
||||
protected List<HuaweiActivitySample> getGBActivitySamples(int timestamp_from, int timestamp_to) {
|
||||
// Note that the result of this function has to be sorted by timestamp!
|
||||
List<HuaweiActivitySample> rawSamples = getRawOrderedActivitySamples(timestamp_from, timestamp_to);
|
||||
List<HuaweiWorkoutDataSample> workoutSamples = getRawOrderedWorkoutSamplesWithHeartRate(timestamp_from, timestamp_to);
|
||||
|
||||
List<int[]> workoutSpans = getWorkoutSpans(rawSamples, workoutSamples, 5);
|
||||
List<HuaweiActivitySample> processedSamples = new ArrayList<>();
|
||||
|
||||
Iterator<HuaweiActivitySample> itRawSamples = rawSamples.iterator();
|
||||
Iterator<HuaweiWorkoutDataSample> itWorkoutSamples = workoutSamples.iterator();
|
||||
|
||||
HuaweiActivitySample nextRawSample = null;
|
||||
if (itRawSamples.hasNext())
|
||||
nextRawSample = itRawSamples.next();
|
||||
HuaweiWorkoutDataSample nextWorkoutSample = null;
|
||||
if (itWorkoutSamples.hasNext())
|
||||
nextWorkoutSample = itWorkoutSamples.next();
|
||||
|
||||
SampleLoopState state = new SampleLoopState();
|
||||
if (nextRawSample != null) {
|
||||
state.deviceId = nextRawSample.getDeviceId();
|
||||
state.userId = nextRawSample.getUserId();
|
||||
for (int timestamp = timestamp_from; timestamp <= timestamp_to; timestamp += 60) {
|
||||
processedSamples.add(createDummySample(timestamp));
|
||||
}
|
||||
|
||||
while (nextRawSample != null || nextWorkoutSample != null) {
|
||||
if (nextRawSample == null || (nextWorkoutSample != null && nextWorkoutSample.getTimestamp() < nextRawSample.getTimestamp())) {
|
||||
processWorkoutSample(processedSamples, state, nextWorkoutSample);
|
||||
nextWorkoutSample = itWorkoutSamples.hasNext() ? itWorkoutSamples.next() : null;
|
||||
} else {
|
||||
boolean sampleInWorkout = isInWorkout(workoutSpans, nextRawSample.getTimestamp());
|
||||
if (sampleInWorkout) {
|
||||
nextRawSample.setHeartRate(ActivitySample.NOT_MEASURED);
|
||||
nextRawSample.setRawIntensity(0);
|
||||
}
|
||||
processRawSample(processedSamples, state, nextRawSample);
|
||||
nextRawSample = itRawSamples.hasNext() ? itRawSamples.next() : null;
|
||||
}
|
||||
}
|
||||
processedSamples = interpolate(processedSamples);
|
||||
overlayActivitySamples(processedSamples, timestamp_from, timestamp_to);
|
||||
overlayWorkoutSamples(processedSamples, timestamp_from, timestamp_to);
|
||||
|
||||
return processedSamples;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculates the timespans: [start, end] of workouts
|
||||
* Normal activities should not be processed when in middle of workout
|
||||
**/
|
||||
private List<int[]> getWorkoutSpans(List<HuaweiActivitySample> activity, List<HuaweiWorkoutDataSample> workout, int threshold) {
|
||||
List<int[]> validActivitySpans = new ArrayList<>();
|
||||
|
||||
Iterator<HuaweiActivitySample> activityIterator = activity.iterator();
|
||||
Iterator<HuaweiWorkoutDataSample> workoutIterator = workout.iterator();
|
||||
|
||||
HuaweiActivitySample currentActivity = activityIterator.hasNext() ? activityIterator.next() : null;
|
||||
HuaweiWorkoutDataSample currentWorkout = workoutIterator.hasNext() ? workoutIterator.next() : null;
|
||||
|
||||
int consecutiveActivityCount = 0;
|
||||
Integer spanStart = null;
|
||||
|
||||
int workoutEnd = 0;
|
||||
while (currentActivity != null || currentWorkout != null) {
|
||||
if (currentWorkout == null || (currentActivity != null && currentActivity.getTimestamp() < currentWorkout.getTimestamp())) {
|
||||
// handle activity
|
||||
if (spanStart != null) {
|
||||
// We're in workout, check for activity interruption
|
||||
consecutiveActivityCount++;
|
||||
if (consecutiveActivityCount > threshold) {
|
||||
// Enough activity samples to interrupt the workout
|
||||
validActivitySpans.add(new int[]{spanStart, workoutEnd});
|
||||
spanStart = null;
|
||||
consecutiveActivityCount = 0;
|
||||
}
|
||||
}
|
||||
currentActivity = activityIterator.hasNext() ? activityIterator.next() : null;
|
||||
} else {
|
||||
// handle workout
|
||||
if (spanStart == null) {
|
||||
spanStart = currentWorkout.getTimestamp();
|
||||
}
|
||||
workoutEnd = currentWorkout.getTimestamp();
|
||||
consecutiveActivityCount = 0;
|
||||
currentWorkout = workoutIterator.hasNext() ? workoutIterator.next() : null;
|
||||
}
|
||||
}
|
||||
|
||||
// If there's an open valid span at the end, close it
|
||||
if (spanStart != null) {
|
||||
validActivitySpans.add(new int[]{spanStart, workoutEnd});
|
||||
}
|
||||
|
||||
return validActivitySpans;
|
||||
}
|
||||
|
||||
private boolean isInWorkout(List<int[]> validSpans, int timestamp) {
|
||||
for (int[] span : validSpans) {
|
||||
if (timestamp > span[0] && timestamp < span[1]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private List<HuaweiActivitySample> interpolate(List<HuaweiActivitySample> processedSamples) {
|
||||
List<HuaweiActivitySample> retv = new ArrayList<>();
|
||||
|
||||
if (processedSamples.isEmpty())
|
||||
return retv;
|
||||
|
||||
HuaweiActivitySample lastSample = processedSamples.get(0);
|
||||
retv.add(lastSample);
|
||||
for (int i = 1; i < processedSamples.size() - 1; i++) {
|
||||
HuaweiActivitySample sample = processedSamples.get(i);
|
||||
|
||||
int timediff = sample.getTimestamp() - lastSample.getTimestamp();
|
||||
if (timediff > 60) {
|
||||
if (lastSample.getRawKind() != -1 && sample.getRawKind() != lastSample.getRawKind()) {
|
||||
HuaweiActivitySample postSample = new HuaweiActivitySample(
|
||||
lastSample.getTimestamp() + 1,
|
||||
lastSample.getDeviceId(),
|
||||
lastSample.getUserId(),
|
||||
0,
|
||||
(byte) 0x00,
|
||||
ActivitySample.NOT_MEASURED,
|
||||
0,
|
||||
ActivitySample.NOT_MEASURED,
|
||||
ActivitySample.NOT_MEASURED,
|
||||
ActivitySample.NOT_MEASURED,
|
||||
ActivitySample.NOT_MEASURED,
|
||||
ActivitySample.NOT_MEASURED
|
||||
);
|
||||
postSample.setProvider(this);
|
||||
retv.add(postSample);
|
||||
}
|
||||
|
||||
if (sample.getRawKind() != -1 && sample.getRawKind() != lastSample.getRawKind()) {
|
||||
HuaweiActivitySample preSample = new HuaweiActivitySample(
|
||||
sample.getTimestamp() - 1,
|
||||
sample.getDeviceId(),
|
||||
sample.getUserId(),
|
||||
0,
|
||||
(byte) 0x00,
|
||||
ActivitySample.NOT_MEASURED,
|
||||
0,
|
||||
ActivitySample.NOT_MEASURED,
|
||||
ActivitySample.NOT_MEASURED,
|
||||
ActivitySample.NOT_MEASURED,
|
||||
ActivitySample.NOT_MEASURED,
|
||||
ActivitySample.NOT_MEASURED
|
||||
);
|
||||
preSample.setProvider(this);
|
||||
retv.add(preSample);
|
||||
}
|
||||
}
|
||||
|
||||
retv.add(sample);
|
||||
lastSample = sample;
|
||||
}
|
||||
|
||||
if (lastSample.getRawKind() != -1) {
|
||||
HuaweiActivitySample postSample = new HuaweiActivitySample(
|
||||
lastSample.getTimestamp() + 1,
|
||||
lastSample.getDeviceId(),
|
||||
lastSample.getUserId(),
|
||||
0,
|
||||
(byte) 0x00,
|
||||
ActivitySample.NOT_MEASURED,
|
||||
0,
|
||||
ActivitySample.NOT_MEASURED,
|
||||
ActivitySample.NOT_MEASURED,
|
||||
ActivitySample.NOT_MEASURED,
|
||||
ActivitySample.NOT_MEASURED,
|
||||
ActivitySample.NOT_MEASURED
|
||||
);
|
||||
postSample.setProvider(this);
|
||||
retv.add(postSample);
|
||||
}
|
||||
|
||||
return retv;
|
||||
}
|
||||
|
||||
private void processRawSample(List<HuaweiActivitySample> processedSamples, SampleLoopState state, HuaweiActivitySample sample) {
|
||||
// Filter on Source 0x0d, Type 0x01, until we know what it is and how we should handle them.
|
||||
// Just showing them currently has some issues.
|
||||
if (sample.getSource() == FitnessData.MessageData.sleepId && sample.getRawKind() == RawTypes.UNKNOWN)
|
||||
return;
|
||||
|
||||
HuaweiActivitySample lastSample = null;
|
||||
|
||||
boolean isStartMarker = sample.getTimestamp() < sample.getOtherTimestamp();
|
||||
|
||||
// Handle preferences for wakeup status ignore - can fix some quirks on some devices
|
||||
if (sample.getRawKind() == 0x08) {
|
||||
SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress());
|
||||
if (isStartMarker && prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_IGNORE_WAKEUP_STATUS_START, false))
|
||||
return;
|
||||
if (!isStartMarker && prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_IGNORE_WAKEUP_STATUS_END, false))
|
||||
return;
|
||||
}
|
||||
|
||||
// Backdate the end marker by one - otherwise the interpolation fails
|
||||
if (sample.getTimestamp() > sample.getOtherTimestamp())
|
||||
sample.setTimestamp(sample.getTimestamp() - 1);
|
||||
|
||||
if (!processedSamples.isEmpty())
|
||||
lastSample = processedSamples.get(processedSamples.size() - 1);
|
||||
if (lastSample != null && lastSample.getTimestamp() == sample.getTimestamp()) {
|
||||
// Merge the samples - only if there isn't any data yet, except the kind
|
||||
|
||||
if (lastSample.getRawKind() == -1)
|
||||
lastSample.setRawKind(sample.getRawKind());
|
||||
// Do overwrite the kind if the new sample is a starting sample
|
||||
if (isStartMarker && sample.getRawKind() != -1) {
|
||||
lastSample.setRawKind(sample.getRawKind());
|
||||
lastSample.setOtherTimestamp(sample.getOtherTimestamp()); // Necessary for interpolation
|
||||
}
|
||||
|
||||
if (lastSample.getRawIntensity() == -1)
|
||||
lastSample.setRawIntensity(sample.getRawIntensity());
|
||||
if (lastSample.getSteps() == -1)
|
||||
lastSample.setSteps(sample.getSteps());
|
||||
if (lastSample.getCalories() == -1)
|
||||
lastSample.setCalories(sample.getCalories());
|
||||
if (lastSample.getDistance() == -1)
|
||||
lastSample.setDistance(sample.getDistance());
|
||||
if (lastSample.getSpo() == -1)
|
||||
lastSample.setSpo(sample.getSpo());
|
||||
if (lastSample.getHeartRate() == -1)
|
||||
lastSample.setHeartRate(sample.getHeartRate());
|
||||
if (lastSample.getSource() != sample.getSource())
|
||||
lastSample.setSource((byte) 0x00);
|
||||
} else {
|
||||
if (state.sleepModifier != 0)
|
||||
sample.setRawKind(state.sleepModifier);
|
||||
processedSamples.add(sample);
|
||||
}
|
||||
|
||||
if (
|
||||
(sample.getSource() == FitnessData.MessageData.sleepId || sample.getSource() == 0x0a) // Sleep sources
|
||||
&& (sample.getRawKind() == RawTypes.LIGHT_SLEEP || sample.getRawKind() == RawTypes.DEEP_SLEEP) // Sleep types
|
||||
) {
|
||||
if (isStartMarker)
|
||||
state.sleepModifier = sample.getRawKind();
|
||||
else
|
||||
state.sleepModifier = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void processWorkoutSample(List<HuaweiActivitySample> processedSamples, SampleLoopState state, HuaweiWorkoutDataSample workoutSample) {
|
||||
processRawSample(processedSamples, state, convertWorkoutSampleToActivitySample(workoutSample, state));
|
||||
}
|
||||
|
||||
private HuaweiActivitySample convertWorkoutSampleToActivitySample(HuaweiWorkoutDataSample workoutSample, SampleLoopState state) {
|
||||
int hr = workoutSample.getHeartRate() & 0xFF;
|
||||
HuaweiActivitySample newSample = new HuaweiActivitySample(
|
||||
workoutSample.getTimestamp(),
|
||||
state.deviceId,
|
||||
state.userId,
|
||||
private HuaweiActivitySample createDummySample(int timestamp) {
|
||||
HuaweiActivitySample activitySample = new HuaweiActivitySample(
|
||||
timestamp,
|
||||
-1,
|
||||
-1,
|
||||
0,
|
||||
(byte) 0x00,
|
||||
ActivitySample.NOT_MEASURED,
|
||||
0,
|
||||
ActivitySample.NOT_MEASURED,
|
||||
ActivitySample.NOT_MEASURED,
|
||||
ActivitySample.NOT_MEASURED,
|
||||
ActivitySample.NOT_MEASURED,
|
||||
ActivitySample.NOT_MEASURED,
|
||||
hr
|
||||
);
|
||||
newSample.setProvider(this);
|
||||
return newSample;
|
||||
ActivitySample.NOT_MEASURED);
|
||||
activitySample.setProvider(this);
|
||||
return activitySample;
|
||||
}
|
||||
|
||||
/*
|
||||
* For every activity sample, it adds the data into the following processed sample.
|
||||
* If there are multiple activity samples, the steps, calories, and distance is added together.
|
||||
* For the SpO and HR only the last value is used.
|
||||
*/
|
||||
private void overlayActivitySamples(List<HuaweiActivitySample> processedSamples, int timestamp_from, int timestamp_to) {
|
||||
List<HuaweiActivitySample> activitySamples = getRawOrderedActivitySamples(timestamp_from, timestamp_to);
|
||||
|
||||
int currentIndex = 0;
|
||||
|
||||
boolean hasData = false;
|
||||
|
||||
int stepCount = ActivitySample.NOT_MEASURED;
|
||||
int calorieCount = ActivitySample.NOT_MEASURED;
|
||||
int distanceCount = ActivitySample.NOT_MEASURED;
|
||||
|
||||
int lastSpo = ActivitySample.NOT_MEASURED;
|
||||
int lastHr = ActivitySample.NOT_MEASURED;
|
||||
|
||||
int stateModifier = ActivitySample.NOT_MEASURED;
|
||||
|
||||
for (HuaweiActivitySample activitySample : activitySamples) {
|
||||
// Skip the processed samples that are before this activity sample
|
||||
while (activitySample.getTimestamp() > processedSamples.get(currentIndex).getTimestamp()) {
|
||||
// Add data to current index sample
|
||||
if (hasData || stateModifier != ActivitySample.NOT_MEASURED)
|
||||
processedSamples.get(currentIndex).setRawIntensity(1);
|
||||
processedSamples.get(currentIndex).setSteps(stepCount);
|
||||
processedSamples.get(currentIndex).setCalories(calorieCount);
|
||||
processedSamples.get(currentIndex).setDistance(distanceCount);
|
||||
processedSamples.get(currentIndex).setSpo(lastSpo);
|
||||
processedSamples.get(currentIndex).setHeartRate(lastHr);
|
||||
processedSamples.get(currentIndex).setRawKind(stateModifier);
|
||||
|
||||
// Reset counters
|
||||
hasData = false;
|
||||
stepCount = ActivitySample.NOT_MEASURED;
|
||||
calorieCount = ActivitySample.NOT_MEASURED;
|
||||
distanceCount = ActivitySample.NOT_MEASURED;
|
||||
lastSpo = ActivitySample.NOT_MEASURED;
|
||||
lastHr = ActivitySample.NOT_MEASURED;
|
||||
|
||||
currentIndex += 1;
|
||||
if (currentIndex > processedSamples.size())
|
||||
return;
|
||||
}
|
||||
|
||||
// Update data
|
||||
if (activitySample.getSteps() != ActivitySample.NOT_MEASURED) {
|
||||
if (stepCount == ActivitySample.NOT_MEASURED)
|
||||
stepCount = 0;
|
||||
stepCount += activitySample.getSteps();
|
||||
hasData = true;
|
||||
}
|
||||
if (activitySample.getCalories() != ActivitySample.NOT_MEASURED) {
|
||||
if (calorieCount == ActivitySample.NOT_MEASURED)
|
||||
calorieCount = 0;
|
||||
calorieCount += activitySample.getCalories();
|
||||
hasData = true;
|
||||
}
|
||||
if (activitySample.getDistance() != ActivitySample.NOT_MEASURED) {
|
||||
if (distanceCount == ActivitySample.NOT_MEASURED)
|
||||
distanceCount = 0;
|
||||
distanceCount += activitySample.getDistance();
|
||||
hasData = true;
|
||||
}
|
||||
if (activitySample.getSpo() != ActivitySample.NOT_MEASURED) {
|
||||
lastSpo = activitySample.getSpo();
|
||||
hasData = true;
|
||||
}
|
||||
if (activitySample.getHeartRate() != ActivitySample.NOT_MEASURED) {
|
||||
lastHr = activitySample.getHeartRate();
|
||||
hasData = true;
|
||||
}
|
||||
if (activitySample.getRawKind() != ActivitySample.NOT_MEASURED) {
|
||||
if (activitySample.getTimestamp() < activitySample.getOtherTimestamp()) {
|
||||
// Starting of modifier
|
||||
stateModifier = activitySample.getRawKind();
|
||||
} else {
|
||||
// End of modifier, remove it if it was for the same state
|
||||
if (activitySample.getRawKind() == stateModifier)
|
||||
stateModifier = ActivitySample.NOT_MEASURED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there is still data, it has to be part of the next index of processed samples
|
||||
currentIndex += 1;
|
||||
if (currentIndex >= processedSamples.size())
|
||||
return;
|
||||
if (hasData || stateModifier != ActivitySample.NOT_MEASURED)
|
||||
processedSamples.get(currentIndex).setRawIntensity(10);
|
||||
processedSamples.get(currentIndex).setSteps(stepCount);
|
||||
processedSamples.get(currentIndex).setCalories(calorieCount);
|
||||
processedSamples.get(currentIndex).setDistance(distanceCount);
|
||||
processedSamples.get(currentIndex).setSpo(lastSpo);
|
||||
processedSamples.get(currentIndex).setHeartRate(lastHr);
|
||||
processedSamples.get(currentIndex).setRawKind(stateModifier);
|
||||
}
|
||||
|
||||
/*
|
||||
* For every workout sample, it adds the data into the following processed sample.
|
||||
* It also detects if it is still in the same workout, and resets the HR and intensity for the
|
||||
* samples in between, see #4126 for the reasoning.
|
||||
* NOTE: Huawei devices tend to generate a lot more data - mine up to every 5 seconds. Most of
|
||||
* this is lost in the conversion to data by the minute. It only shows the most recent value.
|
||||
*/
|
||||
private void overlayWorkoutSamples(List<HuaweiActivitySample> processedSamples, int timestamp_from, int timestamp_to) {
|
||||
int currentIndex = 0;
|
||||
|
||||
int lastHr = ActivitySample.NOT_MEASURED;
|
||||
|
||||
List<HuaweiWorkoutDataSample> workoutSamples = getRawOrderedWorkoutSamplesWithHeartRate(timestamp_from, timestamp_to);
|
||||
|
||||
for (int i = 0; i < workoutSamples.size(); i++) {
|
||||
// Look ahead to see if this is still the same workout
|
||||
boolean inWorkout = i != 0 && workoutSamples.get(i).getWorkoutId() == workoutSamples.get(i - 1).getWorkoutId();
|
||||
|
||||
// Skip the processed sample that are before this workout sample
|
||||
while (workoutSamples.get(i).getTimestamp() > processedSamples.get(currentIndex).getTimestamp()) {
|
||||
if (inWorkout) {
|
||||
processedSamples.get(currentIndex).setHeartRate(lastHr);
|
||||
processedSamples.get(currentIndex).setRawIntensity(0);
|
||||
}
|
||||
|
||||
// Reset
|
||||
lastHr = ActivitySample.NOT_MEASURED;
|
||||
|
||||
currentIndex += 1;
|
||||
if (currentIndex > processedSamples.size())
|
||||
return;
|
||||
}
|
||||
|
||||
if (workoutSamples.get(i).getHeartRate() != ActivitySample.NOT_MEASURED)
|
||||
lastHr = workoutSamples.get(i).getHeartRate() & 0xFF;
|
||||
}
|
||||
|
||||
// If there is still data, it has to be part of the next index of processed samples
|
||||
// Data being present implies it's still in a workout
|
||||
currentIndex += 1;
|
||||
if (currentIndex >= processedSamples.size())
|
||||
return;
|
||||
if (lastHr != ActivitySample.NOT_MEASURED) {
|
||||
processedSamples.get(currentIndex).setHeartRate(lastHr);
|
||||
processedSamples.get(currentIndex).setRawIntensity(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user