mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-27 12:26:48 +01:00
Allow gadgets to provide distance and calories
This commit is contained in:
parent
6258ccd4db
commit
c700d49bf0
@ -61,16 +61,14 @@ public class Widget extends AppWidgetProvider {
|
||||
static BroadcastReceiver broadcastReceiver = null;
|
||||
|
||||
|
||||
private long[] getSteps(GBDevice gbDevice) {
|
||||
private DailyTotals getSteps(GBDevice gbDevice) {
|
||||
Context context = GBApplication.getContext();
|
||||
Calendar day = GregorianCalendar.getInstance();
|
||||
|
||||
if (!(context instanceof GBApplication)) {
|
||||
return new long[]{0, 0, 0};
|
||||
return new DailyTotals();
|
||||
}
|
||||
DailyTotals ds = new DailyTotals();
|
||||
return ds.getDailyTotalsForDevice(gbDevice, day);
|
||||
//return ds.getDailyTotalsForAllDevices(day);
|
||||
return DailyTotals.getDailyTotalsForDevice(gbDevice, day);
|
||||
}
|
||||
|
||||
private String getHM(long value) {
|
||||
@ -117,16 +115,17 @@ public class Widget extends AppWidgetProvider {
|
||||
PendingIntent startChartsPIntent = PendingIntentUtils.getActivity(context, appWidgetId, startChartsIntent, PendingIntent.FLAG_CANCEL_CURRENT, false);
|
||||
views.setOnClickPendingIntent(R.id.todaywidget_bottom_layout, startChartsPIntent);
|
||||
|
||||
long[] dailyTotals = getSteps(deviceForWidget);
|
||||
int steps = (int) dailyTotals[0];
|
||||
int sleep = (int) dailyTotals[1];
|
||||
DailyTotals dailyTotals = getSteps(deviceForWidget);
|
||||
int steps = (int) dailyTotals.getSteps();
|
||||
int sleep = (int) dailyTotals.getSleep();
|
||||
int distanceCm = (int) dailyTotals.getDistance();
|
||||
ActivityUser activityUser = new ActivityUser();
|
||||
int stepGoal = activityUser.getStepsGoal();
|
||||
int sleepGoal = activityUser.getSleepDurationGoal();
|
||||
int sleepGoalMinutes = sleepGoal * 60;
|
||||
int distanceGoal = activityUser.getDistanceGoalMeters() * 100;
|
||||
int stepLength = activityUser.getStepLengthCm();
|
||||
double distanceMeters = dailyTotals[0] * stepLength * 0.01;
|
||||
double distanceMeters = (distanceCm > 0 ? distanceCm : steps * stepLength) * 0.01;
|
||||
String distanceFormatted = FormatUtils.getFormattedDistanceLabel(distanceMeters);
|
||||
|
||||
if (sleep < 1) {
|
||||
|
@ -66,7 +66,7 @@ public class DevicesFragment extends Fragment {
|
||||
private RecyclerView deviceListView;
|
||||
private FloatingActionButton fab;
|
||||
List<GBDevice> deviceList;
|
||||
private HashMap<String,long[]> deviceActivityHashMap = new HashMap();
|
||||
private HashMap<String, DailyTotals> deviceActivityHashMap = new HashMap();
|
||||
|
||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
@ -219,11 +219,10 @@ public class DevicesFragment extends Fragment {
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
private long[] getSteps(GBDevice device, DBHandler db) {
|
||||
private DailyTotals getSteps(GBDevice device, DBHandler db) {
|
||||
Calendar day = GregorianCalendar.getInstance();
|
||||
|
||||
DailyTotals ds = new DailyTotals();
|
||||
return ds.getDailyTotalsForDevice(device, day, db);
|
||||
return DailyTotals.getDailyTotalsForDevice(device, day, db);
|
||||
}
|
||||
|
||||
public void refreshPairedDevices() {
|
||||
@ -266,7 +265,7 @@ public class DevicesFragment extends Fragment {
|
||||
final DeviceCoordinator coordinator = gbDevice.getDeviceCoordinator();
|
||||
final boolean showActivityCard = GBApplication.getDevicePrefs(gbDevice).getBoolean(DeviceSettingsPreferenceConst.PREFS_ACTIVITY_IN_DEVICE_CARD, true);
|
||||
if (coordinator.supportsActivityTracking() && showActivityCard) {
|
||||
final long[] stepsAndSleepData = getSteps(gbDevice, db);
|
||||
final DailyTotals stepsAndSleepData = getSteps(gbDevice, db);
|
||||
deviceActivityHashMap.put(gbDevice.getAddress(), stepsAndSleepData);
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +75,11 @@ public class ActivityAnalysis {
|
||||
amount.addSteps(steps);
|
||||
}
|
||||
|
||||
final int distance = sample.getDistanceCm();
|
||||
if (distance >= 0) {
|
||||
amount.addDistance(distance);
|
||||
}
|
||||
|
||||
if (previousSample != null) {
|
||||
long timeDifference = sample.getTimestamp() - previousSample.getTimestamp();
|
||||
if (previousSample.getRawKind() == sample.getRawKind()) {
|
||||
|
@ -35,7 +35,7 @@ public class StepAnalysis {
|
||||
private int totalDailySteps = 0;
|
||||
|
||||
public List<ActivitySession> calculateStepSessions(List<? extends ActivitySample> samples) {
|
||||
LOG.debug("get all samples activitysessions: " + samples.toArray().length);
|
||||
LOG.debug("get all samples activity sessions: {}", samples.size());
|
||||
List<ActivitySession> result = new ArrayList<>();
|
||||
ActivityUser activityUser = new ActivityUser();
|
||||
final int MIN_SESSION_LENGTH = 60 * GBApplication.getPrefs().getInt("chart_list_min_session_length", 5);
|
||||
@ -50,7 +50,9 @@ public class StepAnalysis {
|
||||
Date sessionStart = null;
|
||||
Date sessionEnd;
|
||||
int activeSteps = 0; //steps that we count
|
||||
int activeDistanceCm = 0;
|
||||
int stepsBetweenActivePeriods = 0; //steps during time when we maybe take a rest but then restart
|
||||
int distanceBetweenActivePeriods = 0;
|
||||
int durationSinceLastActiveStep = 0;
|
||||
ActivityKind activityKind;
|
||||
|
||||
@ -77,7 +79,18 @@ public class StepAnalysis {
|
||||
|
||||
if (sessionStart == null) {
|
||||
sessionStart = getDateFromSample(sample);
|
||||
activeSteps = sample.getSteps();
|
||||
if (sample.getSteps() >= 0) {
|
||||
activeSteps = sample.getSteps();
|
||||
} else {
|
||||
activeSteps = 0;
|
||||
}
|
||||
if (sample.getDistanceCm() >= 0) {
|
||||
activeDistanceCm = sample.getDistanceCm();
|
||||
} else if (activeSteps > 0) {
|
||||
activeDistanceCm = activeSteps * stepLengthCm;
|
||||
} else {
|
||||
activeDistanceCm = 0;
|
||||
}
|
||||
activeIntensity = sample.getIntensity();
|
||||
heartRateSum = new ArrayList<>();
|
||||
if (heartRateUtilsInstance.isValidHeartRateValue(sample.getHeartRate())) {
|
||||
@ -85,6 +98,7 @@ public class StepAnalysis {
|
||||
}
|
||||
durationSinceLastActiveStep = 0;
|
||||
stepsBetweenActivePeriods = 0;
|
||||
distanceBetweenActivePeriods = 0;
|
||||
heartRateBetweenActivePeriodsSum = new ArrayList<>();
|
||||
previousSample = null;
|
||||
}
|
||||
@ -94,6 +108,11 @@ public class StepAnalysis {
|
||||
if (sample.getSteps() > MIN_STEPS_PER_MINUTE || //either some steps
|
||||
(sample.getIntensity() > MIN_SESSION_INTENSITY && sample.getSteps() > 0)) { //or some intensity plus at least one step
|
||||
activeSteps += sample.getSteps() + stepsBetweenActivePeriods;
|
||||
if (sample.getDistanceCm() >= 0) {
|
||||
activeDistanceCm += sample.getDistanceCm() + distanceBetweenActivePeriods;
|
||||
} else {
|
||||
activeDistanceCm += sample.getSteps() * stepLengthCm + distanceBetweenActivePeriods;
|
||||
}
|
||||
activeIntensity += sample.getIntensity() + intensityBetweenActivePeriods;
|
||||
if (heartRateUtilsInstance.isValidHeartRateValue(sample.getHeartRate())) {
|
||||
heartRateSum.add(sample.getHeartRate());
|
||||
@ -101,11 +120,19 @@ public class StepAnalysis {
|
||||
heartRateSum.addAll(heartRateBetweenActivePeriodsSum);
|
||||
heartRateBetweenActivePeriodsSum = new ArrayList<>();
|
||||
stepsBetweenActivePeriods = 0;
|
||||
distanceBetweenActivePeriods = 0;
|
||||
intensityBetweenActivePeriods = 0;
|
||||
durationSinceLastActiveStep = 0;
|
||||
|
||||
} else { //short break data to remember, we will add it to the rest later, if break not too long
|
||||
stepsBetweenActivePeriods += sample.getSteps();
|
||||
if (sample.getSteps() >= 0) {
|
||||
stepsBetweenActivePeriods += sample.getSteps();
|
||||
}
|
||||
if (sample.getDistanceCm() >= 0) {
|
||||
distanceBetweenActivePeriods += sample.getDistanceCm();
|
||||
} else if (sample.getSteps() > 0) {
|
||||
distanceBetweenActivePeriods += sample.getSteps() * stepLengthCm;
|
||||
}
|
||||
if (heartRateUtilsInstance.isValidHeartRateValue(sample.getHeartRate())) {
|
||||
heartRateBetweenActivePeriodsSum.add(sample.getHeartRate());
|
||||
}
|
||||
@ -120,7 +147,7 @@ public class StepAnalysis {
|
||||
|
||||
if (session_length >= MIN_SESSION_LENGTH) { //valid activity session
|
||||
int heartRateAverage = heartRateSum.toArray().length > 0 ? calculateSumOfInts(heartRateSum) / heartRateSum.toArray().length : 0;
|
||||
float distance = (float) (activeSteps * STEP_LENGTH_M);
|
||||
float distance = activeDistanceCm * 0.01f;
|
||||
sessionEnd = new Date((sample.getTimestamp() - durationSinceLastActiveStep) * 1000L);
|
||||
activityKind = detect_activity_kind(session_length, activeSteps, heartRateAverage, activeIntensity);
|
||||
ActivitySession activitySession = new ActivitySession(sessionStart, sessionEnd, activeSteps, heartRateAverage, activeIntensity, distance, activityKind);
|
||||
@ -135,14 +162,14 @@ public class StepAnalysis {
|
||||
}
|
||||
//trailing activity: make sure we show the last portion of the data as well in case no further activity is recorded yet
|
||||
|
||||
if (sessionStart != null && previousSample != null) {
|
||||
if (sessionStart != null) {
|
||||
int current = previousSample.getTimestamp();
|
||||
int starting = (int) (sessionStart.getTime() / 1000);
|
||||
int session_length = current - starting - durationSinceLastActiveStep;
|
||||
|
||||
if (session_length >= MIN_SESSION_LENGTH) {
|
||||
int heartRateAverage = heartRateSum.toArray().length > 0 ? calculateSumOfInts(heartRateSum) / heartRateSum.toArray().length : 0;
|
||||
float distance = (float) (activeSteps * STEP_LENGTH_M);
|
||||
float distance = activeDistanceCm * 0.01f;
|
||||
sessionEnd = getDateFromSample(previousSample);
|
||||
activityKind = detect_activity_kind(session_length, activeSteps, heartRateAverage, activeIntensity);
|
||||
ActivitySession ongoingActivity = new ActivitySession(sessionStart, sessionEnd, activeSteps, heartRateAverage, activeIntensity, distance, activityKind);
|
||||
|
@ -291,8 +291,7 @@ public class StepStreaksDashboard extends MaterialDialogFragment {
|
||||
int all_steps = 0;
|
||||
int firstDataTimestamp = 0;
|
||||
|
||||
DailyTotals dailyTotals = new DailyTotals();
|
||||
ActivitySample firstSample = dailyTotals.getFirstSample(db, device);
|
||||
ActivitySample firstSample = DailyTotals.getFirstSample(db, device);
|
||||
if (firstSample == null) { //no data at all
|
||||
return;
|
||||
}
|
||||
@ -306,8 +305,8 @@ public class StepStreaksDashboard extends MaterialDialogFragment {
|
||||
break;
|
||||
}
|
||||
|
||||
long[] daily_data = dailyTotals.getDailyTotalsForDevice(device, day, db);
|
||||
int steps_this_day = (int) daily_data[0];
|
||||
DailyTotals daily_data = DailyTotals.getDailyTotalsForDevice(device, day, db);
|
||||
int steps_this_day = (int) daily_data.getSteps();
|
||||
|
||||
if (steps_this_day > 0) {
|
||||
all_step_days++;
|
||||
|
@ -51,20 +51,25 @@ abstract class StepsFragment<T extends ChartsData> extends AbstractChartFragment
|
||||
List<StepsDay> daysData = new ArrayList<>();;
|
||||
for (int counter = 0; counter < TOTAL_DAYS; counter++) {
|
||||
long totalSteps = 0;
|
||||
long totalDistance = 0;
|
||||
ActivityAmounts amounts = getActivityAmountsForDay(db, day, device);
|
||||
for (ActivityAmount amount : amounts.getAmounts()) {
|
||||
if (amount.getTotalSteps() > 0) {
|
||||
totalSteps += amount.getTotalSteps();
|
||||
}
|
||||
if (amount.getTotalDistance() > 0) {
|
||||
totalDistance += amount.getTotalDistance();
|
||||
}
|
||||
}
|
||||
double distance = 0;
|
||||
if (totalSteps > 0) {
|
||||
double distance = totalDistance;
|
||||
if (totalDistance == 0 && totalSteps > 0) {
|
||||
// For gadgets that do not report distance, compute it from the steps
|
||||
ActivityUser activityUser = new ActivityUser();
|
||||
int stepLength = activityUser.getStepLengthCm();
|
||||
distance = ((stepLength * 1.0 / 100) * totalSteps) / 1000;
|
||||
distance = stepLength * totalSteps;
|
||||
}
|
||||
Calendar d = (Calendar) day.clone();
|
||||
daysData.add(new StepsDay(d, totalSteps, distance));
|
||||
daysData.add(new StepsDay(d, totalSteps, distance / 100_000));
|
||||
day.add(Calendar.DATE, 1);
|
||||
}
|
||||
return daysData;
|
||||
|
@ -122,6 +122,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceFolder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.BatteryState;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DailyTotals;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
|
||||
@ -143,10 +144,10 @@ public class GBDeviceAdapterv2 extends ListAdapter<GBDevice, GBDeviceAdapterv2.V
|
||||
private String expandedDeviceAddress = "";
|
||||
private String expandedFolderName = "";
|
||||
private ViewGroup parent;
|
||||
private HashMap<String, long[]> deviceActivityMap = new HashMap();
|
||||
private HashMap<String, DailyTotals> deviceActivityMap = new HashMap<>();
|
||||
private final StableIdGenerator idGenerator = new StableIdGenerator();
|
||||
|
||||
public GBDeviceAdapterv2(Context context, List<GBDevice> deviceList, HashMap<String,long[]> deviceMap) {
|
||||
public GBDeviceAdapterv2(Context context, List<GBDevice> deviceList, HashMap<String, DailyTotals> deviceMap) {
|
||||
super(new GBDeviceDiffUtil());
|
||||
this.context = context;
|
||||
this.deviceList = deviceList;
|
||||
@ -304,7 +305,7 @@ public class GBDeviceAdapterv2 extends ListAdapter<GBDevice, GBDeviceAdapterv2.V
|
||||
holder.container.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
long[] dailyTotals = new long[]{0, 0};
|
||||
DailyTotals dailyTotals = new DailyTotals();
|
||||
if (deviceActivityMap.containsKey(device.getAddress())) {
|
||||
dailyTotals = deviceActivityMap.get(device.getAddress());
|
||||
}
|
||||
@ -1331,7 +1332,7 @@ public class GBDeviceAdapterv2 extends ListAdapter<GBDevice, GBDeviceAdapterv2.V
|
||||
snackbar.show();
|
||||
}
|
||||
|
||||
private void setActivityCard(ViewHolder holder, final GBDevice device, long[] dailyTotals) {
|
||||
private void setActivityCard(ViewHolder holder, final GBDevice device, DailyTotals dailyTotals) {
|
||||
boolean showActivityCard = GBApplication.getDeviceSpecificSharedPrefs(device.getAddress()).getBoolean(DeviceSettingsPreferenceConst.PREFS_ACTIVITY_IN_DEVICE_CARD, true);
|
||||
holder.cardViewActivityCardLayout.setVisibility(showActivityCard ? View.VISIBLE : View.GONE);
|
||||
|
||||
@ -1339,15 +1340,16 @@ public class GBDeviceAdapterv2 extends ListAdapter<GBDevice, GBDeviceAdapterv2.V
|
||||
return;
|
||||
}
|
||||
|
||||
int steps = (int) dailyTotals[0];
|
||||
int sleep = (int) dailyTotals[1];
|
||||
int steps = (int) dailyTotals.getSteps();
|
||||
int sleep = (int) dailyTotals.getSleep();
|
||||
int distanceCm = (int) dailyTotals.getDistance();
|
||||
ActivityUser activityUser = new ActivityUser();
|
||||
int stepGoal = activityUser.getStepsGoal();
|
||||
int sleepGoal = activityUser.getSleepDurationGoal();
|
||||
int sleepGoalMinutes = sleepGoal * 60;
|
||||
int distanceGoal = activityUser.getDistanceGoalMeters() * 100;
|
||||
int stepLength = activityUser.getStepLengthCm();
|
||||
double distanceMeters = dailyTotals[0] * stepLength * 0.01;
|
||||
double distanceMeters = (distanceCm > 0 ? distanceCm : steps * stepLength) * 0.01;
|
||||
String distanceFormatted = FormatUtils.getFormattedDistanceLabel(distanceMeters);
|
||||
|
||||
setUpChart(holder.TotalStepsChart);
|
||||
|
@ -28,6 +28,7 @@ import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Set;
|
||||
@ -197,7 +198,10 @@ public abstract class AbstractSampleProvider<T extends AbstractActivitySample> i
|
||||
// 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();
|
||||
int prevDistance = samples.get(0).getDistanceCm();
|
||||
int prevActiveCalories = samples.get(0).getActiveCalories();
|
||||
samples.get(0).setTimestamp((samples.get(0).getTimestamp() / 60) * 60);
|
||||
int bak;
|
||||
|
||||
for (int i = 1; i < samples.size(); i++) {
|
||||
final T s1 = samples.get(i - 1);
|
||||
@ -207,11 +211,26 @@ public abstract class AbstractSampleProvider<T extends AbstractActivitySample> i
|
||||
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;
|
||||
prevDistance = s2.getDistanceCm() > 0 ? s2.getDistanceCm() : 0;
|
||||
prevActiveCalories = s2.getActiveCalories() > 0 ? s2.getActiveCalories() : 0;
|
||||
} else {
|
||||
// New value for the current day - subtract the previous seen sample
|
||||
|
||||
if (s2.getSteps() > 0) {
|
||||
bak = s2.getSteps();
|
||||
s2.setSteps(s2.getSteps() - prevSteps);
|
||||
prevSteps = bak;
|
||||
}
|
||||
if (s2.getDistanceCm() > 0) {
|
||||
bak = s2.getDistanceCm();
|
||||
s2.setDistanceCm(s2.getDistanceCm() - prevDistance);
|
||||
prevDistance = bak;
|
||||
}
|
||||
if (s2.getActiveCalories() > 0) {
|
||||
bak = s2.getActiveCalories();
|
||||
s2.setActiveCalories(s2.getActiveCalories() - prevActiveCalories);
|
||||
prevActiveCalories = bak;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -255,7 +274,7 @@ public abstract class AbstractSampleProvider<T extends AbstractActivitySample> i
|
||||
|
||||
final long nanoStart = System.nanoTime();
|
||||
|
||||
final List<T> ret = new ArrayList<>(samples);
|
||||
final List<T> ret = new LinkedList<>(samples);
|
||||
|
||||
//ret.sort(Comparator.comparingLong(T::getTimestamp));
|
||||
|
||||
@ -263,13 +282,7 @@ public abstract class AbstractSampleProvider<T extends AbstractActivitySample> i
|
||||
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.UNKNOWN.getCode());
|
||||
dummySample.setRawIntensity(ActivitySample.NOT_MEASURED);
|
||||
dummySample.setSteps(ActivitySample.NOT_MEASURED);
|
||||
dummySample.setProvider(this);
|
||||
ret.add(0, dummySample);
|
||||
ret.add(0, createDummySample(ts));
|
||||
}
|
||||
}
|
||||
|
||||
@ -279,13 +292,7 @@ public abstract class AbstractSampleProvider<T extends AbstractActivitySample> i
|
||||
if (minTo - lastTimestamp > 60) {
|
||||
// Gap at the end
|
||||
for (int ts = lastTimestamp + 60; ts <= minTo; ts += 60) {
|
||||
final T dummySample = createActivitySample();
|
||||
dummySample.setTimestamp(ts);
|
||||
dummySample.setRawKind(ActivityKind.UNKNOWN.getCode());
|
||||
dummySample.setRawIntensity(ActivitySample.NOT_MEASURED);
|
||||
dummySample.setSteps(ActivitySample.NOT_MEASURED);
|
||||
dummySample.setProvider(this);
|
||||
ret.add(dummySample);
|
||||
ret.add(createDummySample(ts));
|
||||
}
|
||||
}
|
||||
|
||||
@ -297,13 +304,7 @@ public abstract class AbstractSampleProvider<T extends AbstractActivitySample> i
|
||||
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.UNKNOWN.getCode());
|
||||
dummySample.setRawIntensity(ActivitySample.NOT_MEASURED);
|
||||
dummySample.setSteps(ActivitySample.NOT_MEASURED);
|
||||
dummySample.setProvider(this);
|
||||
it.add(dummySample);
|
||||
it.add(createDummySample(ts));
|
||||
}
|
||||
}
|
||||
previousSample = sample;
|
||||
@ -318,4 +319,17 @@ public abstract class AbstractSampleProvider<T extends AbstractActivitySample> i
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private T createDummySample(final int ts) {
|
||||
final T dummySample = createActivitySample();
|
||||
dummySample.setTimestamp(ts);
|
||||
dummySample.setRawKind(ActivityKind.UNKNOWN.getCode());
|
||||
dummySample.setRawIntensity(ActivitySample.NOT_MEASURED);
|
||||
dummySample.setSteps(ActivitySample.NOT_MEASURED);
|
||||
dummySample.setHeartRate(ActivitySample.NOT_MEASURED);
|
||||
dummySample.setDistanceCm(ActivitySample.NOT_MEASURED);
|
||||
dummySample.setActiveCalories(ActivitySample.NOT_MEASURED);
|
||||
dummySample.setProvider(this);
|
||||
return dummySample;
|
||||
}
|
||||
}
|
||||
|
@ -20,8 +20,6 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
@ -39,8 +37,6 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
|
||||
|
||||
public class TestSampleProvider extends AbstractSampleProvider<TestSampleProvider.TestActivitySample> {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TestSampleProvider.class);
|
||||
|
||||
public TestSampleProvider(final GBDevice device, final DaoSession session) {
|
||||
super(device, session);
|
||||
}
|
||||
@ -59,12 +55,14 @@ public class TestSampleProvider extends AbstractSampleProvider<TestSampleProvide
|
||||
@NonNull
|
||||
@Override
|
||||
protected Property getTimestampSampleProperty() {
|
||||
//noinspection DataFlowIssue not database-backed
|
||||
return null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected Property getDeviceIdentifierSampleProperty() {
|
||||
//noinspection DataFlowIssue not database-backed
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -159,7 +157,7 @@ public class TestSampleProvider extends AbstractSampleProvider<TestSampleProvide
|
||||
}
|
||||
}
|
||||
|
||||
steps += TestDeviceRand.randInt(ts, -steps, 100 - steps) * dayActivityFactor;
|
||||
steps += (int) (TestDeviceRand.randInt(ts, -steps, 100 - steps) * dayActivityFactor);
|
||||
intensity += TestDeviceRand.randInt(ts, -1, 1);
|
||||
hr += TestDeviceRand.randInt(ts, -2, 2);
|
||||
}
|
||||
@ -250,5 +248,15 @@ public class TestSampleProvider extends AbstractSampleProvider<TestSampleProvide
|
||||
public int getSteps() {
|
||||
return steps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDistanceCm() {
|
||||
return steps * 67;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getActiveCalories() {
|
||||
return (int) Math.round(steps * 0.04);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,8 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
package nodomain.freeyourgadget.gadgetbridge.entities;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
@ -29,7 +31,7 @@ public abstract class AbstractActivitySample implements ActivitySample {
|
||||
return mProvider;
|
||||
}
|
||||
|
||||
public void setProvider(SampleProvider provider) {
|
||||
public void setProvider(SampleProvider<?> provider) {
|
||||
mProvider = provider;
|
||||
}
|
||||
|
||||
@ -57,6 +59,12 @@ public abstract class AbstractActivitySample implements ActivitySample {
|
||||
public void setSteps(int steps) {
|
||||
}
|
||||
|
||||
public void setDistanceCm(int distance) {
|
||||
}
|
||||
|
||||
public void setActiveCalories(int activeCalories) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Unix timestamp of the sample, i.e. the number of seconds since 1970-01-01 00:00:00 UTC.
|
||||
*/
|
||||
@ -89,6 +97,17 @@ public abstract class AbstractActivitySample implements ActivitySample {
|
||||
return NOT_MEASURED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDistanceCm() {
|
||||
return NOT_MEASURED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getActiveCalories() {
|
||||
return NOT_MEASURED;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
ActivityKind kind = getProvider() != null ? getKind() : ActivityKind.NOT_MEASURED;
|
||||
@ -97,7 +116,9 @@ public abstract class AbstractActivitySample implements ActivitySample {
|
||||
"timestamp=" + DateTimeUtils.formatDateTime(DateTimeUtils.parseTimeStamp(getTimestamp())) +
|
||||
", intensity=" + intensity +
|
||||
", steps=" + getSteps() +
|
||||
", heartrate=" + getHeartRate() +
|
||||
", distanceCm=" + getDistanceCm() +
|
||||
", activeCalories=" + getActiveCalories() +
|
||||
", heartRate=" + getHeartRate() +
|
||||
", type=" + kind +
|
||||
", userId=" + getUserId() +
|
||||
", deviceId=" + getDeviceId() +
|
||||
|
@ -23,6 +23,7 @@ public class ActivityAmount {
|
||||
private short percent;
|
||||
private long totalSeconds;
|
||||
private long totalSteps;
|
||||
private long totalDistance;
|
||||
private Date startDate = null;
|
||||
private Date endDate = null;
|
||||
|
||||
@ -38,6 +39,10 @@ public class ActivityAmount {
|
||||
totalSteps += steps;
|
||||
}
|
||||
|
||||
public void addDistance(long distance) {
|
||||
totalDistance += distance;
|
||||
}
|
||||
|
||||
public long getTotalSeconds() {
|
||||
return totalSeconds;
|
||||
}
|
||||
@ -46,6 +51,10 @@ public class ActivityAmount {
|
||||
return totalSteps;
|
||||
}
|
||||
|
||||
public long getTotalDistance() {
|
||||
return totalDistance;
|
||||
}
|
||||
|
||||
public ActivityKind getActivityKind() {
|
||||
return activityKind;
|
||||
}
|
||||
|
@ -72,6 +72,16 @@ public interface ActivitySample extends TimeStamped {
|
||||
*/
|
||||
int getSteps();
|
||||
|
||||
/**
|
||||
* Returns the distance moved during the period of this sample, in cm. -1 if unknown.
|
||||
*/
|
||||
int getDistanceCm();
|
||||
|
||||
/**
|
||||
* Returns the calories burned during the period of this sample, in kcal. -1 if unknown.
|
||||
*/
|
||||
int getActiveCalories();
|
||||
|
||||
/**
|
||||
* Returns the heart rate measured at the corresponding timestamp.
|
||||
* The value is returned in heart beats per minute, in the range from
|
||||
|
@ -19,9 +19,12 @@ package nodomain.freeyourgadget.gadgetbridge.model;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
|
||||
@ -34,47 +37,46 @@ import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
|
||||
|
||||
public class DailyTotals {
|
||||
public class DailyTotals implements Serializable {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DailyTotals.class);
|
||||
|
||||
private final long steps;
|
||||
private final long distance;
|
||||
private final long[] sleep; // light deep rem awake
|
||||
|
||||
public long[] getDailyTotalsForAllDevices(Calendar day) {
|
||||
Context context = GBApplication.getContext();
|
||||
//get today's steps for all devices in GB
|
||||
long all_steps = 0;
|
||||
long all_sleep = 0;
|
||||
|
||||
if (context instanceof GBApplication) {
|
||||
GBApplication gbApp = (GBApplication) context;
|
||||
List<? extends GBDevice> devices = gbApp.getDeviceManager().getDevices();
|
||||
for (GBDevice device : devices) {
|
||||
DeviceCoordinator coordinator = device.getDeviceCoordinator();
|
||||
if (!coordinator.supportsActivityDataFetching() && !coordinator.supportsActivityTracking()) {
|
||||
continue;
|
||||
}
|
||||
long[] all_daily = getDailyTotalsForDevice(device, day);
|
||||
all_steps += all_daily[0];
|
||||
all_sleep += all_daily[1];
|
||||
}
|
||||
}
|
||||
//LOG.debug("gbwidget daily totals, all steps:" + all_steps);
|
||||
//LOG.debug("gbwidget daily totals, all sleep:" + all_sleep);
|
||||
return new long[]{all_steps, all_sleep};
|
||||
public DailyTotals() {
|
||||
this(0, 0, new long[]{0, 0, 0 ,0});
|
||||
}
|
||||
|
||||
public DailyTotals(final long steps, final long distance, final long[] sleep) {
|
||||
this.steps = steps;
|
||||
this.distance = distance;
|
||||
this.sleep = sleep;
|
||||
}
|
||||
|
||||
public long[] getDailyTotalsForDevice(GBDevice device, Calendar day) {
|
||||
public long getSteps() {
|
||||
return steps;
|
||||
}
|
||||
|
||||
public long getDistance() {
|
||||
return distance;
|
||||
}
|
||||
|
||||
public long getSleep() {
|
||||
return (long) Arrays.stream(sleep).asDoubleStream().sum();
|
||||
}
|
||||
|
||||
public static DailyTotals getDailyTotalsForDevice(GBDevice device, Calendar day) {
|
||||
|
||||
try (DBHandler handler = GBApplication.acquireDB()) {
|
||||
return getDailyTotalsForDevice(device, day, handler);
|
||||
|
||||
} catch (Exception e) {
|
||||
//GB.toast("Error loading sleep/steps widget data for device: " + device, Toast.LENGTH_SHORT, GB.ERROR, e);
|
||||
return new long[]{0, 0};
|
||||
return new DailyTotals();
|
||||
}
|
||||
}
|
||||
|
||||
public long[] getDailyTotalsForDevice(GBDevice device, Calendar day, DBHandler handler) {
|
||||
public static DailyTotals getDailyTotalsForDevice(GBDevice device, Calendar day, DBHandler handler) {
|
||||
ActivityAnalysis analysis = new ActivityAnalysis();
|
||||
ActivityAmounts amountsSteps;
|
||||
ActivityAmounts amountsSleep;
|
||||
@ -83,13 +85,13 @@ public class DailyTotals {
|
||||
amountsSleep = analysis.calculateActivityAmounts(getSamplesOfDay(handler, day, -12, device));
|
||||
|
||||
long[] sleep = getTotalsSleepForActivityAmounts(amountsSleep);
|
||||
long steps = getTotalsStepsForActivityAmounts(amountsSteps);
|
||||
Pair<Long, Long> stepsDistance = getTotalsStepsForActivityAmounts(amountsSteps);
|
||||
|
||||
// Purposely not including awake sleep
|
||||
return new long[]{steps, sleep[0] + sleep[1] + sleep[2]};
|
||||
return new DailyTotals(stepsDistance.getLeft(), stepsDistance.getRight(), sleep);
|
||||
}
|
||||
|
||||
private long[] getTotalsSleepForActivityAmounts(ActivityAmounts activityAmounts) {
|
||||
private static long[] getTotalsSleepForActivityAmounts(ActivityAmounts activityAmounts) {
|
||||
long totalSecondsDeepSleep = 0;
|
||||
long totalSecondsLightSleep = 0;
|
||||
long totalSecondsRemSleep = 0;
|
||||
@ -109,21 +111,21 @@ public class DailyTotals {
|
||||
long totalMinutesLightSleep = (totalSecondsLightSleep / 60);
|
||||
long totalMinutesRemSleep = (totalSecondsRemSleep / 60);
|
||||
long totalMinutesAwakeSleep = (totalSecondsAwakeSleep / 60);
|
||||
return new long[]{totalMinutesDeepSleep, totalMinutesLightSleep, totalMinutesRemSleep, totalMinutesAwakeSleep};
|
||||
return new long[]{totalMinutesLightSleep, totalMinutesDeepSleep, totalMinutesRemSleep, totalMinutesAwakeSleep};
|
||||
}
|
||||
|
||||
|
||||
public long getTotalsStepsForActivityAmounts(ActivityAmounts activityAmounts) {
|
||||
public static Pair<Long, Long> getTotalsStepsForActivityAmounts(ActivityAmounts activityAmounts) {
|
||||
long totalSteps = 0;
|
||||
long totalDistance = 0;
|
||||
|
||||
for (ActivityAmount amount : activityAmounts.getAmounts()) {
|
||||
totalSteps += amount.getTotalSteps();
|
||||
totalDistance += amount.getTotalDistance();
|
||||
}
|
||||
return totalSteps;
|
||||
return Pair.of(totalSteps, totalDistance);
|
||||
}
|
||||
|
||||
|
||||
private List<? extends ActivitySample> getSamplesOfDay(DBHandler db, Calendar day, int offsetHours, GBDevice device) {
|
||||
private static List<? extends ActivitySample> getSamplesOfDay(DBHandler db, Calendar day, int offsetHours, GBDevice device) {
|
||||
int startTs;
|
||||
int endTs;
|
||||
|
||||
@ -139,23 +141,21 @@ public class DailyTotals {
|
||||
return getSamples(db, device, startTs, endTs);
|
||||
}
|
||||
|
||||
|
||||
public List<? extends ActivitySample> getSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
|
||||
public static List<? extends ActivitySample> getSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
|
||||
return getAllSamples(db, device, tsFrom, tsTo);
|
||||
}
|
||||
|
||||
|
||||
protected SampleProvider<? extends AbstractActivitySample> getProvider(DBHandler db, GBDevice device) {
|
||||
protected static SampleProvider<? extends AbstractActivitySample> getProvider(DBHandler db, GBDevice device) {
|
||||
DeviceCoordinator coordinator = device.getDeviceCoordinator();
|
||||
return coordinator.getSampleProvider(device, db.getDaoSession());
|
||||
}
|
||||
|
||||
protected List<? extends ActivitySample> getAllSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
|
||||
protected static List<? extends ActivitySample> getAllSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
|
||||
SampleProvider<? extends ActivitySample> provider = getProvider(db, device);
|
||||
return provider.getAllActivitySamples(tsFrom, tsTo);
|
||||
}
|
||||
|
||||
public ActivitySample getFirstSample(DBHandler db, GBDevice device) {
|
||||
public static ActivitySample getFirstSample(DBHandler db, GBDevice device) {
|
||||
SampleProvider<? extends ActivitySample> provider = getProvider(db, device);
|
||||
return provider.getFirstActivitySample();
|
||||
}
|
||||
|
@ -42,11 +42,10 @@ import nodomain.freeyourgadget.gadgetbridge.model.DailyTotals;
|
||||
public class DashboardUtils {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DashboardUtils.class);
|
||||
|
||||
public static long getSteps(GBDevice device, DBHandler db, int timeTo) {
|
||||
public static DailyTotals getDailyTotals(GBDevice device, DBHandler db, int timeTo) {
|
||||
Calendar day = GregorianCalendar.getInstance();
|
||||
day.setTimeInMillis(timeTo * 1000L);
|
||||
DailyTotals ds = new DailyTotals();
|
||||
return ds.getDailyTotalsForDevice(device, day, db)[0];
|
||||
return DailyTotals.getDailyTotalsForDevice(device, day, db);
|
||||
}
|
||||
|
||||
public static int getStepsTotal(DashboardFragment.DashboardData dashboardData) {
|
||||
@ -55,7 +54,7 @@ public class DashboardUtils {
|
||||
try (DBHandler dbHandler = GBApplication.acquireDB()) {
|
||||
for (GBDevice dev : devices) {
|
||||
if ((dashboardData.showAllDevices || dashboardData.showDeviceList.contains(dev.getAddress())) && dev.getDeviceCoordinator().supportsActivityTracking()) {
|
||||
totalSteps += getSteps(dev, dbHandler, dashboardData.timeTo);
|
||||
totalSteps += (int) getDailyTotals(dev, dbHandler, dashboardData.timeTo).getSteps();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@ -76,8 +75,7 @@ public class DashboardUtils {
|
||||
public static long getSleep(GBDevice device, DBHandler db, int timeTo) {
|
||||
Calendar day = GregorianCalendar.getInstance();
|
||||
day.setTimeInMillis(timeTo * 1000L);
|
||||
DailyTotals ds = new DailyTotals();
|
||||
return ds.getDailyTotalsForDevice(device, day, db)[1];
|
||||
return DailyTotals.getDailyTotalsForDevice(device, day, db).getSleep();
|
||||
}
|
||||
|
||||
public static long getSleepMinutesTotal(DashboardFragment.DashboardData dashboardData) {
|
||||
@ -105,20 +103,26 @@ public class DashboardUtils {
|
||||
}
|
||||
|
||||
public static float getDistanceTotal(DashboardFragment.DashboardData dashboardData) {
|
||||
ActivityUser activityUser = new ActivityUser();
|
||||
int stepLength = activityUser.getStepLengthCm();
|
||||
|
||||
List<GBDevice> devices = GBApplication.app().getDeviceManager().getDevices();
|
||||
long totalSteps = 0;
|
||||
long totalDistanceCm = 0;
|
||||
try (DBHandler dbHandler = GBApplication.acquireDB()) {
|
||||
for (GBDevice dev : devices) {
|
||||
if ((dashboardData.showAllDevices || dashboardData.showDeviceList.contains(dev.getAddress())) && dev.getDeviceCoordinator().supportsActivityTracking()) {
|
||||
totalSteps += getSteps(dev, dbHandler, dashboardData.timeTo);
|
||||
final DailyTotals dailyTotals = getDailyTotals(dev, dbHandler, dashboardData.timeTo);
|
||||
if (dailyTotals.getSteps() > 0 && dailyTotals.getDistance() > 0) {
|
||||
totalDistanceCm += dailyTotals.getDistance();
|
||||
} else {
|
||||
totalDistanceCm += dailyTotals.getSteps() * stepLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Could not calculate total distance: ", e);
|
||||
}
|
||||
ActivityUser activityUser = new ActivityUser();
|
||||
int stepLength = activityUser.getStepLengthCm();
|
||||
return totalSteps * stepLength * 0.01f;
|
||||
return totalDistanceCm * 0.01f;
|
||||
}
|
||||
|
||||
public static float getDistanceGoalFactor(DashboardFragment.DashboardData dashboardData) {
|
||||
|
Loading…
Reference in New Issue
Block a user