mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-06-29 16:26:18 +02:00
Reduce dashboard full refreshes and fix crashes
This commit is contained in:
parent
98d54f011b
commit
ad721980b2
|
@ -40,9 +40,14 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
|||
|
||||
import com.google.android.material.card.MaterialCardView;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
@ -67,7 +72,8 @@ import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
|
|||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
|
||||
public class DashboardFragment extends Fragment {
|
||||
private boolean isVisible = false;
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DashboardFragment.class);
|
||||
|
||||
private Calendar day = GregorianCalendar.getInstance();
|
||||
private TextView textViewDate;
|
||||
private TextView arrowLeft;
|
||||
|
@ -142,6 +148,11 @@ public class DashboardFragment extends Fragment {
|
|||
}
|
||||
});
|
||||
|
||||
if (savedInstanceState != null && savedInstanceState.containsKey("dashboard_data") && dashboardData.isEmpty()) {
|
||||
dashboardData = (DashboardData) savedInstanceState.getSerializable("dashboard_data");
|
||||
}
|
||||
|
||||
// Make sure the widget fragments are (re)instantiated when drawing the dashboard
|
||||
todayWidget = null;
|
||||
goalsWidget = null;
|
||||
stepsWidget = null;
|
||||
|
@ -149,10 +160,6 @@ public class DashboardFragment extends Fragment {
|
|||
activeTimeWidget = null;
|
||||
sleepWidget = null;
|
||||
|
||||
// Only load widgets here if the Dashboard is visible.
|
||||
// This prevents a hard crash when replacing the fragment in createWidget() via a FragmentManager.
|
||||
if (isVisible) refresh();
|
||||
|
||||
IntentFilter filterLocal = new IntentFilter();
|
||||
filterLocal.addAction(GBDevice.ACTION_DEVICE_CHANGED);
|
||||
LocalBroadcastManager.getInstance(requireContext()).registerReceiver(mReceiver, filterLocal);
|
||||
|
@ -163,7 +170,8 @@ public class DashboardFragment extends Fragment {
|
|||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
fullRefresh();
|
||||
draw();
|
||||
if (dashboardData.isEmpty() || todayWidget == null) refresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -173,13 +181,9 @@ public class DashboardFragment extends Fragment {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setUserVisibleHint(boolean isVisibleToUser) {
|
||||
super.setUserVisibleHint(isVisibleToUser);
|
||||
if (isVisibleToUser) {
|
||||
isVisible = true;
|
||||
} else {
|
||||
isVisible = false;
|
||||
}
|
||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putSerializable("dashboard_data", dashboardData);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -207,10 +211,10 @@ public class DashboardFragment extends Fragment {
|
|||
long timeMillis = data.getLongExtra(DashboardCalendarActivity.EXTRA_TIMESTAMP, 0);
|
||||
if (timeMillis != 0) {
|
||||
day.setTimeInMillis(timeMillis);
|
||||
}
|
||||
}
|
||||
fullRefresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void fullRefresh() {
|
||||
gridLayout.removeAllViews();
|
||||
|
@ -230,14 +234,19 @@ public class DashboardFragment extends Fragment {
|
|||
day.set(Calendar.SECOND, 59);
|
||||
dashboardData.clear();
|
||||
Prefs prefs = GBApplication.getPrefs();
|
||||
String defaultWidgetsOrder = String.join(",", getResources().getStringArray(R.array.pref_dashboard_widgets_order_values));
|
||||
String widgetsOrderPref = prefs.getString("pref_dashboard_widgets_order", defaultWidgetsOrder);
|
||||
List<String> widgetsOrder = Arrays.asList(widgetsOrderPref.split(","));
|
||||
dashboardData.showAllDevices = prefs.getBoolean("dashboard_devices_all", true);
|
||||
dashboardData.showDeviceList = prefs.getStringSet("dashboard_devices_multiselect", new HashSet<>());
|
||||
dashboardData.hrIntervalSecs = prefs.getInt("dashboard_widget_today_hr_interval", 1) * 60;
|
||||
dashboardData.timeTo = (int) (day.getTimeInMillis() / 1000);
|
||||
dashboardData.timeFrom = DateTimeUtils.shiftDays(dashboardData.timeTo, -1);
|
||||
draw();
|
||||
}
|
||||
|
||||
private void draw() {
|
||||
Prefs prefs = GBApplication.getPrefs();
|
||||
String defaultWidgetsOrder = String.join(",", getResources().getStringArray(R.array.pref_dashboard_widgets_order_values));
|
||||
String widgetsOrderPref = prefs.getString("pref_dashboard_widgets_order", defaultWidgetsOrder);
|
||||
List<String> widgetsOrder = Arrays.asList(widgetsOrderPref.split(","));
|
||||
|
||||
Calendar today = GregorianCalendar.getInstance();
|
||||
if (DateTimeUtils.isSameDay(today, day)) {
|
||||
|
@ -349,6 +358,7 @@ public class DashboardFragment extends Fragment {
|
|||
public int hrIntervalSecs;
|
||||
public int timeFrom;
|
||||
public int timeTo;
|
||||
public final List<GeneralizedActivity> generalizedActivities = Collections.synchronizedList(new ArrayList<>());
|
||||
private int stepsTotal;
|
||||
private float stepsGoalFactor;
|
||||
private long sleepTotalMinutes;
|
||||
|
@ -367,6 +377,19 @@ public class DashboardFragment extends Fragment {
|
|||
distanceGoalFactor = 0;
|
||||
activeMinutesTotal = 0;
|
||||
activeMinutesGoalFactor = 0;
|
||||
generalizedActivities.clear();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return (stepsTotal == 0 &&
|
||||
stepsGoalFactor == 0 &&
|
||||
sleepTotalMinutes == 0 &&
|
||||
sleepGoalFactor == 0 &&
|
||||
distanceTotalMeters == 0 &&
|
||||
distanceGoalFactor == 0 &&
|
||||
activeMinutesTotal == 0 &&
|
||||
activeMinutesGoalFactor == 0 &&
|
||||
generalizedActivities.isEmpty());
|
||||
}
|
||||
|
||||
public synchronized int getStepsTotal() {
|
||||
|
@ -416,5 +439,23 @@ public class DashboardFragment extends Fragment {
|
|||
sleepGoalFactor = DashboardUtils.getSleepMinutesGoalFactor(this);
|
||||
return sleepGoalFactor;
|
||||
}
|
||||
|
||||
public static class GeneralizedActivity implements Serializable {
|
||||
public int activityKind;
|
||||
public long timeFrom;
|
||||
public long timeTo;
|
||||
|
||||
public GeneralizedActivity(int activityKind, long timeFrom, long timeTo) {
|
||||
this.activityKind = activityKind;
|
||||
this.timeFrom = timeFrom;
|
||||
this.timeTo = timeTo;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Generalized activity: timeFrom=" + timeFrom + ", timeTo=" + timeTo + ", activityKind=" + activityKind + ", calculated duration: " + (timeTo - timeFrom) + " seconds";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -115,7 +115,9 @@ public class DevicesFragment extends Fragment {
|
|||
deviceListView.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
createRefreshTask("get activity data", getActivity().getApplication()).execute();
|
||||
if (getContext() != null) {
|
||||
createRefreshTask("get activity data", getContext()).execute();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -201,7 +203,7 @@ public class DevicesFragment extends Fragment {
|
|||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
unregisterForContextMenu(deviceListView);
|
||||
if (deviceListView != null) unregisterForContextMenu(deviceListView);
|
||||
LocalBroadcastManager.getInstance(requireContext()).unregisterReceiver(mReceiver);
|
||||
super.onDestroy();
|
||||
}
|
||||
|
|
|
@ -34,8 +34,6 @@ import android.view.ViewGroup;
|
|||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -123,7 +121,11 @@ public class DashboardTodayWidget extends AbstractDashboardWidget {
|
|||
|
||||
legend.setVisibility(prefs.getBoolean("dashboard_widget_today_legend", true) ? View.VISIBLE : View.GONE);
|
||||
|
||||
if (dashboardData.generalizedActivities.isEmpty()) {
|
||||
fillData();
|
||||
} else {
|
||||
draw();
|
||||
}
|
||||
|
||||
return todayView;
|
||||
}
|
||||
|
@ -134,158 +136,9 @@ public class DashboardTodayWidget extends AbstractDashboardWidget {
|
|||
if (todayChart != null) fillData();
|
||||
}
|
||||
|
||||
protected void fillData() {
|
||||
if (todayView == null) return;
|
||||
todayView.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
FillDataAsyncTask myAsyncTask = new FillDataAsyncTask();
|
||||
myAsyncTask.execute();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class FillDataAsyncTask extends AsyncTask<Void, Void, Void> {
|
||||
private final TreeMap<Long, Integer> activityTimestamps = new TreeMap<>();
|
||||
private final List<GeneralizedActivity> generalizedActivities = new ArrayList<>();
|
||||
Bitmap todayBitmap;
|
||||
|
||||
private void addActivity(long timeFrom, long timeTo, int activityKind) {
|
||||
for (long i = timeFrom; i<=timeTo; i++) {
|
||||
// If the current timestamp isn't saved yet, do so immediately
|
||||
if (activityTimestamps.get(i) == null) {
|
||||
activityTimestamps.put(i, activityKind);
|
||||
continue;
|
||||
}
|
||||
// If the current timestamp is already saved, compare the activity kinds and
|
||||
// keep the most 'important' one
|
||||
switch (activityTimestamps.get(i)) {
|
||||
case ActivityKind.TYPE_EXERCISE:
|
||||
break;
|
||||
case ActivityKind.TYPE_ACTIVITY:
|
||||
if (activityKind == ActivityKind.TYPE_EXERCISE)
|
||||
activityTimestamps.put(i, activityKind);
|
||||
break;
|
||||
case ActivityKind.TYPE_DEEP_SLEEP:
|
||||
if (activityKind == ActivityKind.TYPE_EXERCISE ||
|
||||
activityKind == ActivityKind.TYPE_ACTIVITY)
|
||||
activityTimestamps.put(i, activityKind);
|
||||
break;
|
||||
case ActivityKind.TYPE_LIGHT_SLEEP:
|
||||
if (activityKind == ActivityKind.TYPE_EXERCISE ||
|
||||
activityKind == ActivityKind.TYPE_ACTIVITY ||
|
||||
activityKind == ActivityKind.TYPE_DEEP_SLEEP)
|
||||
activityTimestamps.put(i, activityKind);
|
||||
break;
|
||||
case ActivityKind.TYPE_REM_SLEEP:
|
||||
if (activityKind == ActivityKind.TYPE_EXERCISE ||
|
||||
activityKind == ActivityKind.TYPE_ACTIVITY ||
|
||||
activityKind == ActivityKind.TYPE_DEEP_SLEEP ||
|
||||
activityKind == ActivityKind.TYPE_LIGHT_SLEEP)
|
||||
activityTimestamps.put(i, activityKind);
|
||||
break;
|
||||
case ActivityKind.TYPE_SLEEP:
|
||||
if (activityKind == ActivityKind.TYPE_EXERCISE ||
|
||||
activityKind == ActivityKind.TYPE_ACTIVITY ||
|
||||
activityKind == ActivityKind.TYPE_DEEP_SLEEP ||
|
||||
activityKind == ActivityKind.TYPE_LIGHT_SLEEP ||
|
||||
activityKind == ActivityKind.TYPE_REM_SLEEP)
|
||||
activityTimestamps.put(i, activityKind);
|
||||
break;
|
||||
default:
|
||||
activityTimestamps.put(i, activityKind);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void calculateWornSessions(List<ActivitySample> samples) {
|
||||
int firstTimestamp = 0;
|
||||
int lastTimestamp = 0;
|
||||
for (ActivitySample sample : samples) {
|
||||
if (sample.getHeartRate() < 10 && firstTimestamp == 0) continue;
|
||||
if (firstTimestamp == 0) firstTimestamp = sample.getTimestamp();
|
||||
if (lastTimestamp == 0) lastTimestamp = sample.getTimestamp();
|
||||
if ((sample.getHeartRate() < 10 || sample.getTimestamp() > lastTimestamp + dashboardData.hrIntervalSecs) && firstTimestamp != lastTimestamp) {
|
||||
LOG.info("Registered worn session from " + firstTimestamp + " to " + lastTimestamp);
|
||||
addActivity(firstTimestamp, lastTimestamp, ActivityKind.TYPE_NOT_MEASURED);
|
||||
if (sample.getHeartRate() < 10) {
|
||||
firstTimestamp = 0;
|
||||
lastTimestamp = 0;
|
||||
} else {
|
||||
firstTimestamp = sample.getTimestamp();
|
||||
lastTimestamp = sample.getTimestamp();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
lastTimestamp = sample.getTimestamp();
|
||||
}
|
||||
if (firstTimestamp != lastTimestamp) {
|
||||
LOG.info("Registered worn session from " + firstTimestamp + " to " + lastTimestamp);
|
||||
addActivity(firstTimestamp, lastTimestamp, ActivityKind.TYPE_NOT_MEASURED);
|
||||
}
|
||||
}
|
||||
|
||||
private void createGeneralizedActivities() {
|
||||
GeneralizedActivity previous = null;
|
||||
long midDaySecond = dashboardData.timeFrom + (12 * 60 * 60);
|
||||
for (Map.Entry<Long, Integer> activity : activityTimestamps.entrySet()) {
|
||||
long timestamp = activity.getKey();
|
||||
int activityKind = activity.getValue();
|
||||
if (previous == null || previous.activityKind != activityKind || (!mode_24h && timestamp == midDaySecond) || previous.timeTo < timestamp - 60) {
|
||||
previous = new GeneralizedActivity(activityKind, timestamp, timestamp);
|
||||
generalizedActivities.add(previous);
|
||||
} else {
|
||||
previous.timeTo = timestamp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
// Retrieve activity data
|
||||
List<GBDevice> devices = GBApplication.app().getDeviceManager().getDevices();
|
||||
List<ActivitySample> allActivitySamples = new ArrayList<>();
|
||||
List<ActivitySession> stepSessions = new ArrayList<>();
|
||||
List<BaseActivitySummary> activitySummaries = null;
|
||||
try (DBHandler dbHandler = GBApplication.acquireDB()) {
|
||||
for (GBDevice dev : devices) {
|
||||
if ((dashboardData.showAllDevices || dashboardData.showDeviceList.contains(dev.getAddress())) && dev.getDeviceCoordinator().supportsActivityTracking()) {
|
||||
List<? extends ActivitySample> activitySamples = DashboardUtils.getAllSamples(dbHandler, dev, dashboardData);
|
||||
allActivitySamples.addAll(activitySamples);
|
||||
StepAnalysis stepAnalysis = new StepAnalysis();
|
||||
stepSessions.addAll(stepAnalysis.calculateStepSessions(activitySamples));
|
||||
}
|
||||
}
|
||||
activitySummaries = DashboardUtils.getWorkoutSamples(dbHandler, dashboardData);
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Could not retrieve activity amounts: ", e);
|
||||
}
|
||||
Collections.sort(allActivitySamples, (lhs, rhs) -> Integer.valueOf(lhs.getTimestamp()).compareTo(rhs.getTimestamp()));
|
||||
|
||||
// Determine worn sessions from heart rate samples
|
||||
calculateWornSessions(allActivitySamples);
|
||||
|
||||
// Integrate various data from multiple devices
|
||||
long midDaySecond = dashboardData.timeFrom + (12 * 60 * 60);
|
||||
for (ActivitySample sample : allActivitySamples) {
|
||||
// Handle only TYPE_NOT_WORN and TYPE_SLEEP (including variants) here
|
||||
if (sample.getKind() != ActivityKind.TYPE_NOT_WORN && (sample.getKind() == ActivityKind.TYPE_NOT_MEASURED || (sample.getKind() & ActivityKind.TYPE_SLEEP) == 0))
|
||||
continue;
|
||||
// Add to day results
|
||||
addActivity(sample.getTimestamp(), sample.getTimestamp() + 60, sample.getKind());
|
||||
}
|
||||
if (activitySummaries != null) {
|
||||
for (BaseActivitySummary baseActivitySummary : activitySummaries) {
|
||||
addActivity(baseActivitySummary.getStartTime().getTime() / 1000, baseActivitySummary.getEndTime().getTime() / 1000, ActivityKind.TYPE_EXERCISE);
|
||||
}
|
||||
}
|
||||
for (ActivitySession session : stepSessions) {
|
||||
addActivity(session.getStartTime().getTime() / 1000, session.getEndTime().getTime() / 1000, ActivityKind.TYPE_ACTIVITY);
|
||||
}
|
||||
createGeneralizedActivities();
|
||||
|
||||
private void draw() {
|
||||
// Prepare circular chart
|
||||
long midDaySecond = dashboardData.timeFrom + (12 * 60 * 60);
|
||||
int width = 500;
|
||||
int height = 500;
|
||||
int barWidth = 40;
|
||||
|
@ -294,7 +147,7 @@ public class DashboardTodayWidget extends AbstractDashboardWidget {
|
|||
float outerCircleMargin = mode_24h ? barWidth / 2f : barWidth / 2f + hourTextPixels * 1.3f;
|
||||
float innerCircleMargin = outerCircleMargin + barWidth * 1.3f;
|
||||
float degreeFactor = mode_24h ? 240 : 120;
|
||||
todayBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
Bitmap todayBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(todayBitmap);
|
||||
Paint paint = new Paint();
|
||||
paint.setAntiAlias(true);
|
||||
|
@ -361,7 +214,7 @@ public class DashboardTodayWidget extends AbstractDashboardWidget {
|
|||
// Draw generalized activities on circular chart
|
||||
long secondIndex = dashboardData.timeFrom;
|
||||
long currentTime = Calendar.getInstance().getTimeInMillis() / 1000;
|
||||
for (GeneralizedActivity activity : generalizedActivities) {
|
||||
for (DashboardFragment.DashboardData.GeneralizedActivity activity : dashboardData.generalizedActivities) {
|
||||
// Determine margin depending on 24h/12h mode
|
||||
float margin = (mode_24h || activity.timeFrom >= midDaySecond) ? outerCircleMargin : innerCircleMargin;
|
||||
// Draw inactive slices
|
||||
|
@ -450,31 +303,169 @@ public class DashboardTodayWidget extends AbstractDashboardWidget {
|
|||
canvas.drawArc(outerCircleMargin, outerCircleMargin, width - outerCircleMargin, height - outerCircleMargin, 270 + (secondIndex - dashboardData.timeFrom) / degreeFactor, (dashboardData.timeTo - secondIndex) / degreeFactor, false, paint);
|
||||
}
|
||||
|
||||
todayChart.setImageBitmap(todayBitmap);
|
||||
}
|
||||
|
||||
protected void fillData() {
|
||||
if (todayView == null) return;
|
||||
todayView.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
FillDataAsyncTask myAsyncTask = new FillDataAsyncTask();
|
||||
myAsyncTask.execute();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class FillDataAsyncTask extends AsyncTask<Void, Void, Void> {
|
||||
private final TreeMap<Long, Integer> activityTimestamps = new TreeMap<>();
|
||||
|
||||
private void addActivity(long timeFrom, long timeTo, int activityKind) {
|
||||
for (long i = timeFrom; i<=timeTo; i++) {
|
||||
// If the current timestamp isn't saved yet, do so immediately
|
||||
if (activityTimestamps.get(i) == null) {
|
||||
activityTimestamps.put(i, activityKind);
|
||||
continue;
|
||||
}
|
||||
// If the current timestamp is already saved, compare the activity kinds and
|
||||
// keep the most 'important' one
|
||||
switch (activityTimestamps.get(i)) {
|
||||
case ActivityKind.TYPE_EXERCISE:
|
||||
break;
|
||||
case ActivityKind.TYPE_ACTIVITY:
|
||||
if (activityKind == ActivityKind.TYPE_EXERCISE)
|
||||
activityTimestamps.put(i, activityKind);
|
||||
break;
|
||||
case ActivityKind.TYPE_DEEP_SLEEP:
|
||||
if (activityKind == ActivityKind.TYPE_EXERCISE ||
|
||||
activityKind == ActivityKind.TYPE_ACTIVITY)
|
||||
activityTimestamps.put(i, activityKind);
|
||||
break;
|
||||
case ActivityKind.TYPE_LIGHT_SLEEP:
|
||||
if (activityKind == ActivityKind.TYPE_EXERCISE ||
|
||||
activityKind == ActivityKind.TYPE_ACTIVITY ||
|
||||
activityKind == ActivityKind.TYPE_DEEP_SLEEP)
|
||||
activityTimestamps.put(i, activityKind);
|
||||
break;
|
||||
case ActivityKind.TYPE_REM_SLEEP:
|
||||
if (activityKind == ActivityKind.TYPE_EXERCISE ||
|
||||
activityKind == ActivityKind.TYPE_ACTIVITY ||
|
||||
activityKind == ActivityKind.TYPE_DEEP_SLEEP ||
|
||||
activityKind == ActivityKind.TYPE_LIGHT_SLEEP)
|
||||
activityTimestamps.put(i, activityKind);
|
||||
break;
|
||||
case ActivityKind.TYPE_SLEEP:
|
||||
if (activityKind == ActivityKind.TYPE_EXERCISE ||
|
||||
activityKind == ActivityKind.TYPE_ACTIVITY ||
|
||||
activityKind == ActivityKind.TYPE_DEEP_SLEEP ||
|
||||
activityKind == ActivityKind.TYPE_LIGHT_SLEEP ||
|
||||
activityKind == ActivityKind.TYPE_REM_SLEEP)
|
||||
activityTimestamps.put(i, activityKind);
|
||||
break;
|
||||
default:
|
||||
activityTimestamps.put(i, activityKind);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void calculateWornSessions(List<ActivitySample> samples) {
|
||||
int firstTimestamp = 0;
|
||||
int lastTimestamp = 0;
|
||||
|
||||
for (ActivitySample sample : samples) {
|
||||
if (sample.getHeartRate() < 10 && firstTimestamp == 0) continue;
|
||||
if (firstTimestamp == 0) firstTimestamp = sample.getTimestamp();
|
||||
if (lastTimestamp == 0) lastTimestamp = sample.getTimestamp();
|
||||
if ((sample.getHeartRate() < 10 || sample.getTimestamp() > lastTimestamp + dashboardData.hrIntervalSecs) && firstTimestamp != lastTimestamp) {
|
||||
LOG.info("Registered worn session from " + firstTimestamp + " to " + lastTimestamp);
|
||||
addActivity(firstTimestamp, lastTimestamp, ActivityKind.TYPE_NOT_MEASURED);
|
||||
if (sample.getHeartRate() < 10) {
|
||||
firstTimestamp = 0;
|
||||
lastTimestamp = 0;
|
||||
} else {
|
||||
firstTimestamp = sample.getTimestamp();
|
||||
lastTimestamp = sample.getTimestamp();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
lastTimestamp = sample.getTimestamp();
|
||||
}
|
||||
if (firstTimestamp != lastTimestamp) {
|
||||
LOG.info("Registered worn session from " + firstTimestamp + " to " + lastTimestamp);
|
||||
addActivity(firstTimestamp, lastTimestamp, ActivityKind.TYPE_NOT_MEASURED);
|
||||
}
|
||||
}
|
||||
|
||||
private void createGeneralizedActivities() {
|
||||
DashboardFragment.DashboardData.GeneralizedActivity previous = null;
|
||||
long midDaySecond = dashboardData.timeFrom + (12 * 60 * 60);
|
||||
for (Map.Entry<Long, Integer> activity : activityTimestamps.entrySet()) {
|
||||
long timestamp = activity.getKey();
|
||||
int activityKind = activity.getValue();
|
||||
if (previous == null || previous.activityKind != activityKind || (!mode_24h && timestamp == midDaySecond) || previous.timeTo < timestamp - 60) {
|
||||
previous = new DashboardFragment.DashboardData.GeneralizedActivity(activityKind, timestamp, timestamp);
|
||||
dashboardData.generalizedActivities.add(previous);
|
||||
} else {
|
||||
previous.timeTo = timestamp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
// Retrieve activity data
|
||||
dashboardData.generalizedActivities.clear();
|
||||
List<GBDevice> devices = GBApplication.app().getDeviceManager().getDevices();
|
||||
List<ActivitySample> allActivitySamples = new ArrayList<>();
|
||||
List<ActivitySession> stepSessions = new ArrayList<>();
|
||||
List<BaseActivitySummary> activitySummaries = null;
|
||||
try (DBHandler dbHandler = GBApplication.acquireDB()) {
|
||||
for (GBDevice dev : devices) {
|
||||
if ((dashboardData.showAllDevices || dashboardData.showDeviceList.contains(dev.getAddress())) && dev.getDeviceCoordinator().supportsActivityTracking()) {
|
||||
List<? extends ActivitySample> activitySamples = DashboardUtils.getAllSamples(dbHandler, dev, dashboardData);
|
||||
allActivitySamples.addAll(activitySamples);
|
||||
StepAnalysis stepAnalysis = new StepAnalysis();
|
||||
stepSessions.addAll(stepAnalysis.calculateStepSessions(activitySamples));
|
||||
}
|
||||
}
|
||||
activitySummaries = DashboardUtils.getWorkoutSamples(dbHandler, dashboardData);
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Could not retrieve activity amounts: ", e);
|
||||
}
|
||||
Collections.sort(allActivitySamples, (lhs, rhs) -> Integer.valueOf(lhs.getTimestamp()).compareTo(rhs.getTimestamp()));
|
||||
|
||||
// Determine worn sessions from heart rate samples
|
||||
calculateWornSessions(allActivitySamples);
|
||||
|
||||
// Integrate various data from multiple devices
|
||||
for (ActivitySample sample : allActivitySamples) {
|
||||
// Handle only TYPE_NOT_WORN and TYPE_SLEEP (including variants) here
|
||||
if (sample.getKind() != ActivityKind.TYPE_NOT_WORN && (sample.getKind() == ActivityKind.TYPE_NOT_MEASURED || (sample.getKind() & ActivityKind.TYPE_SLEEP) == 0))
|
||||
continue;
|
||||
// Add to day results
|
||||
addActivity(sample.getTimestamp(), sample.getTimestamp() + 60, sample.getKind());
|
||||
}
|
||||
if (activitySummaries != null) {
|
||||
for (BaseActivitySummary baseActivitySummary : activitySummaries) {
|
||||
addActivity(baseActivitySummary.getStartTime().getTime() / 1000, baseActivitySummary.getEndTime().getTime() / 1000, ActivityKind.TYPE_EXERCISE);
|
||||
}
|
||||
}
|
||||
for (ActivitySession session : stepSessions) {
|
||||
addActivity(session.getStartTime().getTime() / 1000, session.getEndTime().getTime() / 1000, ActivityKind.TYPE_ACTIVITY);
|
||||
}
|
||||
createGeneralizedActivities();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void unused) {
|
||||
super.onPostExecute(unused);
|
||||
todayChart.setImageBitmap(todayBitmap);
|
||||
try {
|
||||
draw();
|
||||
} catch (IllegalStateException e) {
|
||||
LOG.warn("calling draw() failed: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private class GeneralizedActivity {
|
||||
public int activityKind;
|
||||
public long timeFrom;
|
||||
public long timeTo;
|
||||
|
||||
private GeneralizedActivity(int activityKind, long timeFrom, long timeTo) {
|
||||
this.activityKind = activityKind;
|
||||
this.timeFrom = timeFrom;
|
||||
this.timeTo = timeTo;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Generalized activity: timeFrom=" + timeFrom + ", timeTo=" + timeTo + ", activityKind=" + activityKind + ", calculated duration: " + (timeTo - timeFrom) + " seconds";
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user