mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-29 23:04:27 +02:00
Include workouts in activity list
This commit is contained in:
+31
-4
@@ -33,20 +33,28 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import de.greenrobot.dao.query.QueryBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.charts.sleep.SleepDetailsView;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummaryDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
|
||||
public abstract class AbstractActivityChartFragment<D extends ChartsData> extends AbstractChartFragment<D> {
|
||||
@@ -166,10 +174,6 @@ public abstract class AbstractActivityChartFragment<D extends ChartsData> extend
|
||||
/**
|
||||
* Returns all kinds of samples for the given device.
|
||||
* To be called from a background thread.
|
||||
*
|
||||
* @param device
|
||||
* @param tsFrom
|
||||
* @param tsTo
|
||||
*/
|
||||
protected List<? extends ActivitySample> getAllSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
|
||||
SampleProvider<? extends ActivitySample> provider = getProvider(db, device);
|
||||
@@ -414,6 +418,29 @@ public abstract class AbstractActivityChartFragment<D extends ChartsData> extend
|
||||
return samples;
|
||||
}
|
||||
|
||||
protected List<BaseActivitySummary> getAllWorkouts(DBHandler db, GBDevice device) {
|
||||
Calendar day = Calendar.getInstance();
|
||||
day.setTimeInMillis(getTSEnd() * 1000L); //we need today initially, which is the end of the time range
|
||||
day.set(Calendar.HOUR_OF_DAY, 0); //and we set time for the start and end of the same day
|
||||
day.set(Calendar.MINUTE, 0);
|
||||
day.set(Calendar.SECOND, 0);
|
||||
final int tsFrom = (int) (day.getTimeInMillis() / 1000);
|
||||
final int tsTo = tsFrom + 24 * 60 * 60 - 1;
|
||||
BaseActivitySummaryDao summaryDao = db.getDaoSession().getBaseActivitySummaryDao();
|
||||
Device dbDevice = DBHelper.findDevice(device, db.getDaoSession());
|
||||
QueryBuilder<BaseActivitySummary> qb = summaryDao.queryBuilder();
|
||||
qb.where(BaseActivitySummaryDao.Properties.DeviceId.eq(Objects.requireNonNull(dbDevice).getId()));
|
||||
qb.where(BaseActivitySummaryDao.Properties.StartTime.gt(new Date(tsFrom * 1000L)));
|
||||
qb.where(BaseActivitySummaryDao.Properties.EndTime.lt(new Date(tsTo * 1000L)));
|
||||
qb.orderAsc(BaseActivitySummaryDao.Properties.StartTime);
|
||||
final List<BaseActivitySummary> summaries = qb.build().list();
|
||||
final ActivitySummaryParser summaryParser = device.getDeviceCoordinator().getActivitySummaryParser(device, requireContext());
|
||||
for (BaseActivitySummary summary : summaries) {
|
||||
summaryParser.parseBinaryData(summary, false);
|
||||
}
|
||||
return summaries;
|
||||
}
|
||||
|
||||
protected List<? extends ActivitySample> getSamplesHighRes(DBHandler db, GBDevice device) {
|
||||
int tsStart = getTSStart();
|
||||
int tsEnd = getTSEnd();
|
||||
|
||||
+17
-17
@@ -44,7 +44,9 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.workouts.WorkoutDetailsActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySession;
|
||||
@@ -71,6 +73,15 @@ public class ActivityListingChartFragment extends AbstractActivityChartFragment<
|
||||
|
||||
stepListAdapter.setOnItemClickListener(position -> {
|
||||
ActivitySession item = stepListAdapter.getItem(position);
|
||||
if (item.getSessionType() == ActivitySession.SESSION_WORKOUT) {
|
||||
final Intent intent = WorkoutDetailsActivity.Companion.createSingleWorkoutIntent(
|
||||
requireContext(),
|
||||
item.getWorkoutSummaryId(),
|
||||
getChartsHost().getDevice()
|
||||
);
|
||||
startActivity(intent);
|
||||
return;
|
||||
}
|
||||
if (item.getSessionType() != ActivitySession.SESSION_SUMMARY) {
|
||||
int tsFrom = (int) (item.getStartTime().getTime() / 1000);
|
||||
int tsTo = (int) (item.getEndTime().getTime() / 1000);
|
||||
@@ -82,12 +93,7 @@ public class ActivityListingChartFragment extends AbstractActivityChartFragment<
|
||||
FloatingActionButton fab;
|
||||
fab = rootView.findViewById(R.id.fab);
|
||||
fab.setVisibility(View.VISIBLE);
|
||||
fab.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
showDashboard(tsDateTo, getChartsHost().getDevice());
|
||||
}
|
||||
});
|
||||
fab.setOnClickListener(v -> showDashboard(tsDateTo, getChartsHost().getDevice()));
|
||||
|
||||
refresh();
|
||||
return rootView;
|
||||
@@ -125,8 +131,9 @@ public class ActivityListingChartFragment extends AbstractActivityChartFragment<
|
||||
boolean isEmptySummary = false;
|
||||
|
||||
if (activitySamples != null) {
|
||||
stepSessions = stepAnalysis.calculateStepSessions(activitySamples);
|
||||
if (stepSessions.size() == 0) {
|
||||
final List<BaseActivitySummary> workouts = getAllWorkouts(db, device);
|
||||
stepSessions = stepAnalysis.calculateStepSessions(activitySamples, workouts);
|
||||
if (stepSessions.isEmpty()) {
|
||||
isEmptySummary = true;
|
||||
}
|
||||
ActivitySession stepSessionsSummary = stepAnalysis.calculateSummary(stepSessions, isEmptySummary);
|
||||
@@ -149,7 +156,7 @@ public class ActivityListingChartFragment extends AbstractActivityChartFragment<
|
||||
}
|
||||
|
||||
//noinspection RedundantIfStatement
|
||||
if (mcd.getStepSessions().size() == 0) {
|
||||
if (mcd.getStepSessions().isEmpty()) {
|
||||
getChartsHost().enableSwipeRefresh(true); //enable pull to refresh, might be needed
|
||||
} else {
|
||||
getChartsHost().enableSwipeRefresh(false); //disable pull to refresh as it collides with swipeable view
|
||||
@@ -197,7 +204,6 @@ public class ActivityListingChartFragment extends AbstractActivityChartFragment<
|
||||
String durationLabel = DateTimeUtils.formatDurationHoursMinutes(ongoingSession.getEndTime().getTime() - ongoingSession.getStartTime().getTime(), TimeUnit.MILLISECONDS);
|
||||
String hrLabel = String.valueOf(ongoingSession.getHeartRateAverage());
|
||||
String activityName = ongoingSession.getActivityKind().getLabel(requireContext());
|
||||
int icon = ongoingSession.getActivityKind().getIcon();
|
||||
|
||||
String text = String.format("%s:\u00A0%s, %s:\u00A0%s, %s:\u00A0%s, %s:\u00A0%s", activityName, durationLabel, getString(R.string.heart_rate), hrLabel, getString(R.string.steps), stepLabel, getString(R.string.distance), distanceLabel);
|
||||
final Snackbar snackbar = Snackbar.make(getView(), text, 1000 * 8);
|
||||
@@ -205,13 +211,7 @@ public class ActivityListingChartFragment extends AbstractActivityChartFragment<
|
||||
View snackbarView = snackbar.getView();
|
||||
snackbarView.setBackgroundColor(requireContext().getResources().getColor(R.color.accent));
|
||||
snackbar.setActionTextColor(Color.WHITE);
|
||||
snackbar.setAction(getString(R.string.dialog_hide).toUpperCase(), new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
snackbar.dismiss();
|
||||
}
|
||||
}
|
||||
);
|
||||
snackbar.setAction(getString(R.string.dialog_hide).toUpperCase(), view -> snackbar.dismiss());
|
||||
snackbar.show();
|
||||
}
|
||||
|
||||
|
||||
+43
-28
@@ -23,7 +23,6 @@ import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.DatePicker;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.SeekBar;
|
||||
@@ -41,16 +40,22 @@ import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import de.greenrobot.dao.query.QueryBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBAccess;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummaryDao;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySummaryParser;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.FormatUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.dialogs.MaterialDialogFragment;
|
||||
@@ -117,7 +122,7 @@ public class ActivityListingDashboard extends MaterialDialogFragment {
|
||||
if (!activity_list_debug_extra_time_range_value) {
|
||||
battery_status_time_span_seekbar.setMax(3);
|
||||
}
|
||||
final TextView battery_status_time_span_text = (TextView) getView().findViewById(R.id.battery_status_time_span_text);
|
||||
final TextView battery_status_time_span_text = getView().findViewById(R.id.battery_status_time_span_text);
|
||||
|
||||
battery_status_time_span_seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
|
||||
@@ -175,32 +180,25 @@ public class ActivityListingDashboard extends MaterialDialogFragment {
|
||||
|
||||
}
|
||||
});
|
||||
battery_status_date_to_layout.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
final Calendar currentDate = Calendar.getInstance();
|
||||
currentDate.setTimeInMillis(timeTo * 1000L);
|
||||
battery_status_date_to_layout.setOnClickListener(v -> {
|
||||
final Calendar currentDate = Calendar.getInstance();
|
||||
currentDate.setTimeInMillis(timeTo * 1000L);
|
||||
|
||||
new DatePickerDialog(getContext(), new DatePickerDialog.OnDateSetListener() {
|
||||
@Override
|
||||
public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
|
||||
new DatePickerDialog(requireContext(), (view1, year, monthOfYear, dayOfMonth) -> {
|
||||
Calendar date = Calendar.getInstance();
|
||||
date.set(year, monthOfYear, dayOfMonth);
|
||||
int time1 = (int) (date.getTimeInMillis() / 1000);
|
||||
Calendar day1 = Calendar.getInstance();
|
||||
day1.setTimeInMillis(time1 * 1000L);
|
||||
day1.set(Calendar.HOUR_OF_DAY, 23);
|
||||
day1.set(Calendar.MINUTE, 59);
|
||||
day1.set(Calendar.SECOND, 59);
|
||||
timeTo = (int) (day1.getTimeInMillis() / 1000);
|
||||
|
||||
Calendar date = Calendar.getInstance();
|
||||
date.set(year, monthOfYear, dayOfMonth);
|
||||
int time = (int) (date.getTimeInMillis() / 1000);
|
||||
Calendar day = Calendar.getInstance();
|
||||
day.setTimeInMillis(time * 1000L);
|
||||
day.set(Calendar.HOUR_OF_DAY, 23);
|
||||
day.set(Calendar.MINUTE, 59);
|
||||
day.set(Calendar.SECOND, 59);
|
||||
timeTo = (int) (day.getTimeInMillis() / 1000);
|
||||
|
||||
battery_status_date_to_text.setText(DateTimeUtils.formatDate(new Date(timeTo * 1000L)));
|
||||
battery_status_time_span_seekbar.setProgress(0);
|
||||
battery_status_time_span_seekbar.setProgress(1);
|
||||
}
|
||||
}, currentDate.get(Calendar.YEAR), currentDate.get(Calendar.MONTH), currentDate.get(Calendar.DATE)).show();
|
||||
}
|
||||
battery_status_date_to_text.setText(DateTimeUtils.formatDate(new Date(timeTo * 1000L)));
|
||||
battery_status_time_span_seekbar.setProgress(0);
|
||||
battery_status_time_span_seekbar.setProgress(1);
|
||||
}, currentDate.get(Calendar.YEAR), currentDate.get(Calendar.MONTH), currentDate.get(Calendar.DATE)).show();
|
||||
});
|
||||
battery_status_time_span_seekbar.setProgress(2);
|
||||
}
|
||||
@@ -213,12 +211,13 @@ public class ActivityListingDashboard extends MaterialDialogFragment {
|
||||
|
||||
List<ActivitySession> stepSessions;
|
||||
List<? extends ActivitySample> activitySamples = getAllSamples(db, gbDevice, timeFrom, timeTo);
|
||||
final List<BaseActivitySummary> workouts = getAllWorkouts(db, gbDevice, timeFrom, timeTo);
|
||||
StepAnalysis stepAnalysis = new StepAnalysis();
|
||||
|
||||
boolean isEmptySummary = false;
|
||||
if (activitySamples != null) {
|
||||
stepSessions = stepAnalysis.calculateStepSessions(activitySamples);
|
||||
if (stepSessions.size() == 0) {
|
||||
stepSessions = stepAnalysis.calculateStepSessions(activitySamples, workouts);
|
||||
if (stepSessions.isEmpty()) {
|
||||
isEmptySummary = true;
|
||||
}
|
||||
stepSessionsSummary = stepAnalysis.calculateSummary(stepSessions, isEmptySummary);
|
||||
@@ -236,6 +235,22 @@ public class ActivityListingDashboard extends MaterialDialogFragment {
|
||||
return provider.getAllActivitySamples(tsFrom, tsTo);
|
||||
}
|
||||
|
||||
protected List<BaseActivitySummary> getAllWorkouts(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
|
||||
BaseActivitySummaryDao summaryDao = db.getDaoSession().getBaseActivitySummaryDao();
|
||||
Device dbDevice = DBHelper.findDevice(device, db.getDaoSession());
|
||||
QueryBuilder<BaseActivitySummary> qb = summaryDao.queryBuilder();
|
||||
qb.where(BaseActivitySummaryDao.Properties.DeviceId.eq(dbDevice.getId()));
|
||||
qb.where(BaseActivitySummaryDao.Properties.StartTime.gt(new Date(tsFrom * 1000L)));
|
||||
qb.where(BaseActivitySummaryDao.Properties.EndTime.lt(new Date(tsTo * 1000L)));
|
||||
qb.orderAsc(BaseActivitySummaryDao.Properties.StartTime);
|
||||
final List<BaseActivitySummary> summaries = qb.build().list();
|
||||
final ActivitySummaryParser summaryParser = device.getDeviceCoordinator().getActivitySummaryParser(device, getContext());
|
||||
for (BaseActivitySummary summary : summaries) {
|
||||
summaryParser.parseBinaryData(summary, false);
|
||||
}
|
||||
return summaries;
|
||||
}
|
||||
|
||||
void indicate_progress(boolean inProgress) {
|
||||
View view = getView();
|
||||
if (view == null) {
|
||||
|
||||
+48
-5
@@ -21,22 +21,58 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.RangeMap;
|
||||
|
||||
public class StepAnalysis {
|
||||
protected static final Logger LOG = LoggerFactory.getLogger(StepAnalysis.class);
|
||||
private int totalDailySteps = 0;
|
||||
|
||||
public List<ActivitySession> calculateStepSessions(List<? extends ActivitySample> samples) {
|
||||
LOG.debug("get all samples activity sessions: {}", samples.size());
|
||||
public List<ActivitySession> calculateStepSessions(List<? extends ActivitySample> samples, final List<BaseActivitySummary> workouts) {
|
||||
LOG.debug("get all samples activity sessions: {}, workouts: {}", samples.size(), workouts.size());
|
||||
|
||||
totalDailySteps = 0;
|
||||
|
||||
// Exclude all explicit samples that match workouts, but keep track of them, so that we can still count the steps
|
||||
// some devices do not include steps in non-step workouts (eg. strength training), which will
|
||||
// otherwise mess up the step count
|
||||
final Map<Long, List<ActivitySample>> samplesDuringWorkouts = new HashMap<>();
|
||||
final RangeMap<Long, Long> workoutsMap = new RangeMap<>(RangeMap.Mode.LOWER_BOUND);
|
||||
workoutsMap.put(0L, 0L);
|
||||
for (BaseActivitySummary workout : workouts) {
|
||||
workoutsMap.put(workout.getStartTime().getTime(), workout.getStartTime().getTime());
|
||||
workoutsMap.put(workout.getEndTime().getTime(), 0L);
|
||||
}
|
||||
samples = samples.stream()
|
||||
.filter(s -> {
|
||||
final Long workoutStart = workoutsMap.get(s.getTimestamp() * 1000L);
|
||||
final boolean isWorkout = workoutStart != null && workoutStart != 0L;
|
||||
if (isWorkout) {
|
||||
if (s.getSteps() > 0) {
|
||||
// FIXME: This is hacky, but we need to add the steps to the daily total here, since they
|
||||
// will not be processed downstream
|
||||
totalDailySteps += s.getSteps();
|
||||
}
|
||||
samplesDuringWorkouts.computeIfAbsent(workoutStart, absent -> new LinkedList<>())
|
||||
.add(s);
|
||||
}
|
||||
return !isWorkout;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
List<ActivitySession> result = new ArrayList<>();
|
||||
ActivityUser activityUser = new ActivityUser();
|
||||
final int MIN_SESSION_LENGTH = 60 * GBApplication.getPrefs().getInt("chart_list_min_session_length", 5);
|
||||
@@ -45,7 +81,6 @@ public class StepAnalysis {
|
||||
int stepLengthCm = activityUser.getStepLengthCm();
|
||||
final double STEP_LENGTH_M = stepLengthCm * 0.01;
|
||||
final double MIN_SESSION_INTENSITY = Math.max(0, Math.min(1, MIN_STEPS_PER_MINUTE * 0.01));
|
||||
totalDailySteps = 0;
|
||||
|
||||
ActivitySample previousSample = null;
|
||||
Date sessionStart = null;
|
||||
@@ -169,7 +204,7 @@ public class StepAnalysis {
|
||||
int session_length = current - starting - durationSinceLastActiveStep;
|
||||
|
||||
if (session_length >= MIN_SESSION_LENGTH) {
|
||||
int heartRateAverage = heartRateSum.size() > 0 ? calculateSumOfInts(heartRateSum) / heartRateSum.size() : 0;
|
||||
int heartRateAverage = !heartRateSum.isEmpty() ? calculateSumOfInts(heartRateSum) / heartRateSum.size() : 0;
|
||||
float distance = activeDistanceCm * 0.01f;
|
||||
sessionEnd = getDateFromSample(previousSample);
|
||||
activityKind = detect_activity_kind(session_length, activeSteps, heartRateAverage, activeIntensity);
|
||||
@@ -178,7 +213,15 @@ public class StepAnalysis {
|
||||
result.add(ongoingActivity);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
for (BaseActivitySummary workout : workouts) {
|
||||
final List<ActivitySample> samplesDuringThisWorkout = samplesDuringWorkouts.get(workout.getStartTime().getTime());
|
||||
result.add(new ActivitySession(workout, samplesDuringThisWorkout));
|
||||
}
|
||||
|
||||
return result.stream()
|
||||
.sorted(Comparator.comparing(ActivitySession::getStartTime))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public ActivitySession calculateSummary(Collection<ActivitySession> sessions, boolean empty) {
|
||||
|
||||
+1
-1
@@ -558,7 +558,7 @@ public class DashboardTodayWidget extends AbstractDashboardWidget {
|
||||
List<? extends ActivitySample> activitySamples = DashboardUtils.getAllSamples(dbHandler, dev, dashboardData);
|
||||
allActivitySamples.addAll(activitySamples);
|
||||
StepAnalysis stepAnalysis = new StepAnalysis();
|
||||
stepSessions.addAll(stepAnalysis.calculateStepSessions(activitySamples));
|
||||
stepSessions.addAll(stepAnalysis.calculateStepSessions(activitySamples, Collections.emptyList()));
|
||||
deviceIds.add(DBHelper.getDevice(dev, dbHandler.getDaoSession()).getId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ public class ActivityListItem {
|
||||
hrLayout.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (intensity >= 0) {
|
||||
if (intensity >= 1f) {
|
||||
final DecimalFormat df = new DecimalFormat("###");
|
||||
intensityLabel.setText(df.format(intensity));
|
||||
intensityLayout.setVisibility(View.VISIBLE);
|
||||
|
||||
@@ -18,6 +18,10 @@ package nodomain.freeyourgadget.gadgetbridge.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Accumulator;
|
||||
|
||||
// ActivitySession holds activities detected by the steps/hr/intensity
|
||||
// and is used in the Activity List
|
||||
@@ -27,6 +31,7 @@ public class ActivitySession implements Serializable {
|
||||
public static int SESSION_SUMMARY = 2;
|
||||
public static int SESSION_ONGOING = 3;
|
||||
public static int SESSION_EMPTY = 4;
|
||||
public static int SESSION_WORKOUT = 5;
|
||||
|
||||
private final Date startTime;
|
||||
private final Date endTime;
|
||||
@@ -39,6 +44,7 @@ public class ActivitySession implements Serializable {
|
||||
// it is identified by SESSION_SUMMARY
|
||||
private int sessionCount = 0;
|
||||
private int sessionType = SESSION_NORMAL;
|
||||
private long workoutSummaryId = -1;
|
||||
private boolean isEmptySummary = false; // in case there is no activity on that day
|
||||
private int totalDaySteps;
|
||||
|
||||
@@ -55,6 +61,45 @@ public class ActivitySession implements Serializable {
|
||||
this.activityKind = activityKind;
|
||||
}
|
||||
|
||||
public ActivitySession(final BaseActivitySummary summary, final List<ActivitySample> samples) {
|
||||
this.startTime = summary.getStartTime();
|
||||
this.endTime = summary.getEndTime();
|
||||
final String summaryDataJson = summary.getSummaryData();
|
||||
|
||||
final Accumulator accSteps = new Accumulator();
|
||||
final Accumulator accDistance = new Accumulator();
|
||||
final Accumulator accHeartRate = new Accumulator();
|
||||
if (samples != null) {
|
||||
for (ActivitySample s : samples) {
|
||||
if (s.getSteps() > 0) {
|
||||
accSteps.add(s.getSteps());
|
||||
}
|
||||
if (s.getDistanceCm() > 0) {
|
||||
accDistance.add(s.getDistanceCm());
|
||||
}
|
||||
if (s.getHeartRate() > 0) {
|
||||
accHeartRate.add(s.getHeartRate());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (summaryDataJson != null) {
|
||||
final ActivitySummaryData summaryData = ActivitySummaryData.fromJson(summaryDataJson);
|
||||
this.activeSteps = summaryData.getNumber(ActivitySummaryEntries.STEPS, accSteps.getSum()).intValue();
|
||||
this.heartRateAverage = summaryData.getNumber(ActivitySummaryEntries.HR_AVG, accHeartRate.getAverage()).intValue();
|
||||
this.distance = summaryData.getNumber(ActivitySummaryEntries.DISTANCE_METERS, accDistance.getSum() * 0.01f).floatValue();
|
||||
} else {
|
||||
this.activeSteps = (int) Math.round(accSteps.getSum());
|
||||
this.heartRateAverage = (int) Math.round(accHeartRate.getAverage());
|
||||
this.distance = (int) Math.round(accDistance.getSum() * 0.01f);
|
||||
}
|
||||
this.intensity = 0;
|
||||
this.sessionType = SESSION_WORKOUT;
|
||||
this.workoutSummaryId = summary.getId();
|
||||
|
||||
this.activityKind = ActivityKind.fromCode(summary.getActivityKind());
|
||||
}
|
||||
|
||||
public ActivitySession(){
|
||||
this.startTime = null;
|
||||
this.endTime = null;
|
||||
@@ -63,7 +108,7 @@ public class ActivitySession implements Serializable {
|
||||
this.intensity = 0;
|
||||
this.distance = 0;
|
||||
this.activityKind = ActivityKind.UNKNOWN;
|
||||
};
|
||||
}
|
||||
|
||||
public Date getStartTime() {
|
||||
return startTime;
|
||||
@@ -124,4 +169,8 @@ public class ActivitySession implements Serializable {
|
||||
public void setTotalDaySteps(int totalDaySteps) {
|
||||
this.totalDaySteps = totalDaySteps;
|
||||
}
|
||||
|
||||
public long getWorkoutSummaryId() {
|
||||
return workoutSummaryId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,19 +207,15 @@ public class DashboardUtils {
|
||||
}
|
||||
|
||||
public static long getActiveMinutes(GBDevice gbDevice, DBHandler db, DashboardFragment.DashboardData dashboardData) {
|
||||
ActivitySession stepSessionsSummary = new ActivitySession();
|
||||
List<ActivitySession> stepSessions;
|
||||
List<? extends ActivitySample> activitySamples = getAllSamples(db, gbDevice, dashboardData);
|
||||
StepAnalysis stepAnalysis = new StepAnalysis();
|
||||
|
||||
boolean isEmptySummary = false;
|
||||
if (activitySamples != null) {
|
||||
stepSessions = stepAnalysis.calculateStepSessions(activitySamples);
|
||||
if (stepSessions.isEmpty()) {
|
||||
isEmptySummary = true;
|
||||
}
|
||||
stepSessionsSummary = stepAnalysis.calculateSummary(stepSessions, isEmptySummary);
|
||||
final List<ActivitySession> stepSessions = stepAnalysis.calculateStepSessions(activitySamples, Collections.emptyList());
|
||||
if (stepSessions.isEmpty()) {
|
||||
isEmptySummary = true;
|
||||
}
|
||||
final ActivitySession stepSessionsSummary = stepAnalysis.calculateSummary(stepSessions, isEmptySummary);
|
||||
long duration = stepSessionsSummary.getEndTime().getTime() - stepSessionsSummary.getStartTime().getTime();
|
||||
return duration / 1000 / 60;
|
||||
}
|
||||
|
||||
@@ -38,16 +38,10 @@ public class RangeMap<K extends Comparable<K>, V> {
|
||||
}
|
||||
|
||||
public RangeMap(final Mode mode) {
|
||||
switch (mode) {
|
||||
case LOWER_BOUND:
|
||||
comparator = (k1, k2) -> k1.compareTo(k2);
|
||||
break;
|
||||
case UPPER_BOUND:
|
||||
comparator = (k1, k2) -> k2.compareTo(k1);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown mode " + mode);
|
||||
}
|
||||
comparator = switch (mode) {
|
||||
case LOWER_BOUND -> Comparable::compareTo;
|
||||
case UPPER_BOUND -> Comparator.reverseOrder();
|
||||
};
|
||||
}
|
||||
|
||||
public void put(final K key, final V value) {
|
||||
@@ -82,6 +76,5 @@ public class RangeMap<K extends Comparable<K>, V> {
|
||||
public enum Mode {
|
||||
LOWER_BOUND,
|
||||
UPPER_BOUND,
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,11 +10,10 @@
|
||||
android:id="@+id/stepsDateView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="center"
|
||||
android:textAllCaps="false"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
android:textStyle="bold" />
|
||||
|
||||
android:gravity="center"
|
||||
android:textSize="20sp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:layout_marginTop="15dp" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/itemListView"
|
||||
@@ -39,6 +38,4 @@
|
||||
android:layout_marginBottom="9dp"
|
||||
app:srcCompat="@drawable/ic_insights" />
|
||||
|
||||
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
Reference in New Issue
Block a user