Include workouts in activity list

This commit is contained in:
José Rebelo
2026-06-21 22:05:50 +01:00
parent 79ad5a6079
commit b055ab5075
10 changed files with 203 additions and 83 deletions
@@ -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();
@@ -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();
}
@@ -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) {
@@ -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) {
@@ -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>