1
0
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:
José Rebelo 2024-09-21 19:14:04 +01:00
parent 6258ccd4db
commit c700d49bf0
14 changed files with 224 additions and 122 deletions

View File

@ -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) {

View File

@ -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);
}
}

View File

@ -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()) {

View File

@ -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);

View File

@ -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++;

View File

@ -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;

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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() +

View File

@ -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;
}

View File

@ -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

View File

@ -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();
}

View File

@ -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) {