2024-08-28 10:36:25 +02:00
|
|
|
/* Copyright (C) 2023-2024 Arjan Schrijver, José Rebelo
|
2024-04-04 21:28:04 +02:00
|
|
|
|
|
|
|
This file is part of Gadgetbridge.
|
|
|
|
|
|
|
|
Gadgetbridge is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU Affero General Public License as published
|
|
|
|
by the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
Gadgetbridge is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU Affero General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
package nodomain.freeyourgadget.gadgetbridge.activities;
|
|
|
|
|
2024-08-28 10:36:25 +02:00
|
|
|
import android.app.Activity;
|
2024-04-04 21:28:04 +02:00
|
|
|
import android.content.BroadcastReceiver;
|
|
|
|
import android.content.Context;
|
|
|
|
import android.content.Intent;
|
|
|
|
import android.content.IntentFilter;
|
|
|
|
import android.os.Bundle;
|
|
|
|
import android.util.DisplayMetrics;
|
|
|
|
import android.view.LayoutInflater;
|
|
|
|
import android.view.Menu;
|
|
|
|
import android.view.MenuInflater;
|
|
|
|
import android.view.MenuItem;
|
|
|
|
import android.view.View;
|
|
|
|
import android.view.ViewGroup;
|
|
|
|
import android.widget.TextView;
|
|
|
|
|
2024-08-28 10:36:25 +02:00
|
|
|
import androidx.activity.result.ActivityResult;
|
|
|
|
import androidx.activity.result.ActivityResultCallback;
|
|
|
|
import androidx.activity.result.ActivityResultLauncher;
|
|
|
|
import androidx.activity.result.contract.ActivityResultContracts;
|
2024-04-04 21:28:04 +02:00
|
|
|
import androidx.annotation.NonNull;
|
2024-08-28 10:36:25 +02:00
|
|
|
import androidx.core.view.MenuProvider;
|
2024-04-04 21:28:04 +02:00
|
|
|
import androidx.fragment.app.Fragment;
|
|
|
|
import androidx.fragment.app.FragmentContainerView;
|
|
|
|
import androidx.gridlayout.widget.GridLayout;
|
|
|
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
|
|
|
|
|
|
|
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.Calendar;
|
|
|
|
import java.util.Collections;
|
|
|
|
import java.util.GregorianCalendar;
|
2024-08-28 10:36:25 +02:00
|
|
|
import java.util.HashMap;
|
2024-04-04 21:28:04 +02:00
|
|
|
import java.util.HashSet;
|
|
|
|
import java.util.List;
|
2024-08-28 10:36:25 +02:00
|
|
|
import java.util.Map;
|
2024-04-04 21:28:04 +02:00
|
|
|
import java.util.Set;
|
2024-08-28 10:36:25 +02:00
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
import java.util.function.Supplier;
|
2024-04-04 21:28:04 +02:00
|
|
|
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.AbstractDashboardWidget;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardActiveTimeWidget;
|
2024-08-28 10:36:25 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardBodyEnergyWidget;
|
2024-04-04 21:28:04 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardCalendarActivity;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardDistanceWidget;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardGoalsWidget;
|
2024-08-28 10:36:25 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardHrvWidget;
|
2024-04-04 21:28:04 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardSleepWidget;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardStepsWidget;
|
2024-08-28 10:36:25 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardStressBreakdownWidget;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardStressSegmentedWidget;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardStressSimpleWidget;
|
2024-04-04 21:28:04 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardTodayWidget;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
2024-08-12 20:41:50 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
|
2024-04-04 21:28:04 +02:00
|
|
|
import nodomain.freeyourgadget.gadgetbridge.util.DashboardUtils;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
|
|
|
|
2024-08-28 10:36:25 +02:00
|
|
|
public class DashboardFragment extends Fragment implements MenuProvider {
|
2024-04-04 21:28:04 +02:00
|
|
|
private static final Logger LOG = LoggerFactory.getLogger(DashboardFragment.class);
|
|
|
|
|
2024-08-28 10:36:25 +02:00
|
|
|
private final Calendar day = GregorianCalendar.getInstance();
|
2024-04-04 21:28:04 +02:00
|
|
|
private TextView textViewDate;
|
|
|
|
private TextView arrowRight;
|
|
|
|
private GridLayout gridLayout;
|
2024-08-28 10:36:25 +02:00
|
|
|
private final Map<String, AbstractDashboardWidget> widgetMap = new HashMap<>();
|
2024-04-04 21:28:04 +02:00
|
|
|
private DashboardData dashboardData = new DashboardData();
|
|
|
|
private boolean isConfigChanged = false;
|
|
|
|
|
2024-08-28 10:36:25 +02:00
|
|
|
private ActivityResultLauncher<Intent> calendarLauncher;
|
|
|
|
private final ActivityResultCallback<ActivityResult> calendarCallback = result -> {
|
|
|
|
if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) {
|
|
|
|
long timeMillis = result.getData().getLongExtra(DashboardCalendarActivity.EXTRA_TIMESTAMP, 0);
|
|
|
|
if (timeMillis != 0) {
|
|
|
|
day.setTimeInMillis(timeMillis);
|
|
|
|
fullRefresh();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-04-04 21:28:04 +02:00
|
|
|
public static final String ACTION_CONFIG_CHANGE = "nodomain.freeyourgadget.gadgetbridge.activities.dashboardfragment.action.config_change";
|
|
|
|
|
|
|
|
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
|
|
|
@Override
|
|
|
|
public void onReceive(Context context, Intent intent) {
|
|
|
|
String action = intent.getAction();
|
|
|
|
if (action == null) return;
|
|
|
|
switch (action) {
|
2024-08-24 15:43:53 +02:00
|
|
|
case GBApplication.ACTION_NEW_DATA:
|
|
|
|
final GBDevice dev = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE);
|
2024-08-28 10:36:25 +02:00
|
|
|
if (dev != null) {
|
2024-08-24 15:43:53 +02:00
|
|
|
if (dashboardData.showAllDevices || dashboardData.showDeviceList.contains(dev.getAddress())) {
|
|
|
|
refresh();
|
|
|
|
}
|
2024-04-04 21:28:04 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ACTION_CONFIG_CHANGE:
|
|
|
|
isConfigChanged = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
@Override
|
2024-08-28 10:36:25 +02:00
|
|
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
2024-04-04 21:28:04 +02:00
|
|
|
super.onCreateView(inflater, container, savedInstanceState);
|
|
|
|
View dashboardView = inflater.inflate(R.layout.fragment_dashboard, container, false);
|
|
|
|
textViewDate = dashboardView.findViewById(R.id.dashboard_date);
|
|
|
|
gridLayout = dashboardView.findViewById(R.id.dashboard_gridlayout);
|
|
|
|
|
2024-08-28 10:36:25 +02:00
|
|
|
calendarLauncher = registerForActivityResult(
|
|
|
|
new ActivityResultContracts.StartActivityForResult(),
|
|
|
|
calendarCallback
|
|
|
|
);
|
|
|
|
|
2024-04-04 21:28:04 +02:00
|
|
|
// Increase column count on landscape, tablets and open foldables
|
|
|
|
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
|
|
|
|
if (displayMetrics.widthPixels / displayMetrics.density >= 600) {
|
|
|
|
gridLayout.setColumnCount(4);
|
|
|
|
}
|
|
|
|
|
2024-08-28 10:36:25 +02:00
|
|
|
final TextView arrowLeft = dashboardView.findViewById(R.id.arrow_left);
|
2024-04-04 21:28:04 +02:00
|
|
|
arrowLeft.setOnClickListener(v -> {
|
|
|
|
day.add(Calendar.DAY_OF_MONTH, -1);
|
|
|
|
refresh();
|
|
|
|
});
|
|
|
|
arrowRight = dashboardView.findViewById(R.id.arrow_right);
|
|
|
|
arrowRight.setOnClickListener(v -> {
|
|
|
|
Calendar today = GregorianCalendar.getInstance();
|
|
|
|
if (!DateTimeUtils.isSameDay(today, day)) {
|
|
|
|
day.add(Calendar.DAY_OF_MONTH, 1);
|
|
|
|
refresh();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (savedInstanceState != null && savedInstanceState.containsKey("dashboard_data") && dashboardData.isEmpty()) {
|
|
|
|
dashboardData = (DashboardData) savedInstanceState.getSerializable("dashboard_data");
|
|
|
|
}
|
|
|
|
|
|
|
|
IntentFilter filterLocal = new IntentFilter();
|
|
|
|
filterLocal.addAction(GBDevice.ACTION_DEVICE_CHANGED);
|
2024-08-24 15:43:53 +02:00
|
|
|
filterLocal.addAction(GBApplication.ACTION_NEW_DATA);
|
2024-04-04 21:28:04 +02:00
|
|
|
filterLocal.addAction(ACTION_CONFIG_CHANGE);
|
|
|
|
LocalBroadcastManager.getInstance(requireContext()).registerReceiver(mReceiver, filterLocal);
|
|
|
|
|
|
|
|
return dashboardView;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onResume() {
|
|
|
|
super.onResume();
|
|
|
|
if (isConfigChanged) {
|
|
|
|
isConfigChanged = false;
|
|
|
|
fullRefresh();
|
2024-08-28 10:36:25 +02:00
|
|
|
} else if (dashboardData.isEmpty() || !widgetMap.containsKey("today")) {
|
2024-04-04 21:28:04 +02:00
|
|
|
refresh();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onDestroy() {
|
|
|
|
LocalBroadcastManager.getInstance(requireContext()).unregisterReceiver(mReceiver);
|
|
|
|
super.onDestroy();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onSaveInstanceState(@NonNull Bundle outState) {
|
|
|
|
super.onSaveInstanceState(outState);
|
|
|
|
outState.putSerializable("dashboard_data", dashboardData);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2024-08-28 10:36:25 +02:00
|
|
|
public void onCreateMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) {
|
2024-04-04 21:28:04 +02:00
|
|
|
inflater.inflate(R.menu.dashboard_menu, menu);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2024-08-28 10:36:25 +02:00
|
|
|
public boolean onMenuItemSelected(@NonNull final MenuItem item) {
|
2024-06-30 21:27:12 +02:00
|
|
|
final int itemId = item.getItemId();
|
|
|
|
if (itemId == R.id.dashboard_show_calendar) {
|
|
|
|
final Intent intent = new Intent(requireActivity(), DashboardCalendarActivity.class);
|
|
|
|
intent.putExtra(DashboardCalendarActivity.EXTRA_TIMESTAMP, day.getTimeInMillis());
|
2024-08-28 10:36:25 +02:00
|
|
|
calendarLauncher.launch(intent);
|
|
|
|
return true;
|
|
|
|
} else if (itemId == R.id.dashboard_settings) {
|
|
|
|
final Intent intent = new Intent(requireActivity(), DashboardPreferencesActivity.class);
|
|
|
|
startActivity(intent);
|
|
|
|
return true;
|
2024-04-04 21:28:04 +02:00
|
|
|
}
|
2024-08-28 10:36:25 +02:00
|
|
|
return false;
|
2024-04-04 21:28:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private void fullRefresh() {
|
|
|
|
gridLayout.removeAllViews();
|
2024-08-28 10:36:25 +02:00
|
|
|
widgetMap.clear();
|
2024-04-04 21:28:04 +02:00
|
|
|
refresh();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void refresh() {
|
|
|
|
day.set(Calendar.HOUR_OF_DAY, 23);
|
|
|
|
day.set(Calendar.MINUTE, 59);
|
|
|
|
day.set(Calendar.SECOND, 59);
|
|
|
|
dashboardData.clear();
|
|
|
|
Prefs prefs = GBApplication.getPrefs();
|
|
|
|
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();
|
2024-08-28 10:36:25 +02:00
|
|
|
String defaultWidgetsOrder = String.join(",", getResources().getStringArray(R.array.pref_dashboard_widgets_order_default));
|
2024-04-04 21:28:04 +02:00
|
|
|
String widgetsOrderPref = prefs.getString("pref_dashboard_widgets_order", defaultWidgetsOrder);
|
2024-08-28 10:36:25 +02:00
|
|
|
String[] widgetsOrder = widgetsOrderPref.split(",");
|
2024-04-04 21:28:04 +02:00
|
|
|
|
|
|
|
Calendar today = GregorianCalendar.getInstance();
|
|
|
|
if (DateTimeUtils.isSameDay(today, day)) {
|
2024-08-28 10:36:25 +02:00
|
|
|
textViewDate.setText(requireContext().getString(R.string.activity_summary_today));
|
2024-04-04 21:28:04 +02:00
|
|
|
arrowRight.setAlpha(0.5f);
|
|
|
|
} else {
|
|
|
|
textViewDate.setText(DateTimeUtils.formatDate(day.getTime()));
|
|
|
|
arrowRight.setAlpha(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
boolean cardsEnabled = prefs.getBoolean("dashboard_cards_enabled", true);
|
|
|
|
|
|
|
|
for (String widgetName : widgetsOrder) {
|
2024-08-28 10:36:25 +02:00
|
|
|
AbstractDashboardWidget widget = widgetMap.get(widgetName);
|
|
|
|
if (widget == null) {
|
|
|
|
int columnSpan = 1;
|
|
|
|
switch (widgetName) {
|
|
|
|
case "today":
|
|
|
|
widget = DashboardTodayWidget.newInstance(dashboardData);
|
|
|
|
columnSpan = prefs.getBoolean("dashboard_widget_today_2columns", true) ? 2 : 1;
|
|
|
|
break;
|
|
|
|
case "goals":
|
|
|
|
widget = DashboardGoalsWidget.newInstance(dashboardData);
|
|
|
|
columnSpan = prefs.getBoolean("dashboard_widget_goals_2columns", true) ? 2 : 1;
|
|
|
|
break;
|
|
|
|
case "steps":
|
|
|
|
widget = DashboardStepsWidget.newInstance(dashboardData);
|
|
|
|
break;
|
|
|
|
case "distance":
|
|
|
|
widget = DashboardDistanceWidget.newInstance(dashboardData);
|
|
|
|
break;
|
|
|
|
case "activetime":
|
|
|
|
widget = DashboardActiveTimeWidget.newInstance(dashboardData);
|
|
|
|
break;
|
|
|
|
case "sleep":
|
|
|
|
widget = DashboardSleepWidget.newInstance(dashboardData);
|
|
|
|
break;
|
|
|
|
case "stress_simple":
|
|
|
|
widget = DashboardStressSimpleWidget.newInstance(dashboardData);
|
|
|
|
break;
|
|
|
|
case "stress_segmented":
|
|
|
|
widget = DashboardStressSegmentedWidget.newInstance(dashboardData);
|
|
|
|
break;
|
|
|
|
case "stress_breakdown":
|
|
|
|
widget = DashboardStressBreakdownWidget.newInstance(dashboardData);
|
|
|
|
break;
|
|
|
|
case "bodyenergy":
|
|
|
|
widget = DashboardBodyEnergyWidget.newInstance(dashboardData);
|
|
|
|
break;
|
|
|
|
case "hrv":
|
|
|
|
widget = DashboardHrvWidget.newInstance(dashboardData);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
LOG.error("Unknown dashboard widget {}", widgetName);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
createWidget(widget, cardsEnabled, columnSpan);
|
|
|
|
|
|
|
|
widgetMap.put(widgetName, widget);
|
|
|
|
} else {
|
|
|
|
widget.update();
|
2024-04-04 21:28:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void createWidget(AbstractDashboardWidget widgetObj, boolean cardsEnabled, int columnSpan) {
|
|
|
|
final float scale = requireContext().getResources().getDisplayMetrics().density;
|
|
|
|
FragmentContainerView fragment = new FragmentContainerView(requireActivity());
|
|
|
|
int fragmentId = View.generateViewId();
|
|
|
|
fragment.setId(fragmentId);
|
2024-04-08 22:23:34 +02:00
|
|
|
getChildFragmentManager()
|
2024-04-04 21:28:04 +02:00
|
|
|
.beginTransaction()
|
|
|
|
.replace(fragmentId, widgetObj)
|
2024-08-28 10:50:33 +02:00
|
|
|
.commitAllowingStateLoss(); // FIXME: #4007
|
2024-04-04 21:28:04 +02:00
|
|
|
|
|
|
|
GridLayout.LayoutParams layoutParams = new GridLayout.LayoutParams(
|
2024-08-28 10:36:25 +02:00
|
|
|
GridLayout.spec(GridLayout.UNDEFINED, GridLayout.FILL, 1f),
|
|
|
|
GridLayout.spec(GridLayout.UNDEFINED, columnSpan, GridLayout.FILL, 1f)
|
2024-04-04 21:28:04 +02:00
|
|
|
);
|
|
|
|
layoutParams.width = 0;
|
|
|
|
int pixels_8dp = (int) (8 * scale + 0.5f);
|
|
|
|
layoutParams.setMargins(pixels_8dp, pixels_8dp, pixels_8dp, pixels_8dp);
|
|
|
|
|
|
|
|
if (cardsEnabled) {
|
|
|
|
MaterialCardView card = new MaterialCardView(requireActivity());
|
|
|
|
int pixels_4dp = (int) (4 * scale + 0.5f);
|
|
|
|
card.setRadius(pixels_4dp);
|
|
|
|
card.setCardElevation(pixels_4dp);
|
|
|
|
card.setContentPadding(pixels_4dp, pixels_4dp, pixels_4dp, pixels_4dp);
|
|
|
|
card.setLayoutParams(layoutParams);
|
|
|
|
card.addView(fragment);
|
|
|
|
gridLayout.addView(card);
|
|
|
|
} else {
|
|
|
|
fragment.setLayoutParams(layoutParams);
|
|
|
|
gridLayout.addView(fragment);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This class serves as a data collection object for all data points used by the various
|
|
|
|
* dashboard widgets. Since retrieving this data can be costly, this class makes sure it will
|
|
|
|
* only be done once. It will be passed to every widget, making sure they have the necessary
|
|
|
|
* data available.
|
|
|
|
*/
|
|
|
|
public static class DashboardData implements Serializable {
|
|
|
|
public boolean showAllDevices;
|
|
|
|
public Set<String> showDeviceList;
|
|
|
|
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;
|
|
|
|
private float sleepGoalFactor;
|
|
|
|
private float distanceTotalMeters;
|
|
|
|
private float distanceGoalFactor;
|
|
|
|
private long activeMinutesTotal;
|
|
|
|
private float activeMinutesGoalFactor;
|
2024-08-28 10:36:25 +02:00
|
|
|
private final Map<String, Serializable> genericData = new ConcurrentHashMap<>();
|
2024-04-04 21:28:04 +02:00
|
|
|
|
|
|
|
public void clear() {
|
|
|
|
stepsTotal = 0;
|
|
|
|
stepsGoalFactor = 0;
|
|
|
|
sleepTotalMinutes = 0;
|
|
|
|
sleepGoalFactor = 0;
|
|
|
|
distanceTotalMeters = 0;
|
|
|
|
distanceGoalFactor = 0;
|
|
|
|
activeMinutesTotal = 0;
|
|
|
|
activeMinutesGoalFactor = 0;
|
|
|
|
generalizedActivities.clear();
|
2024-08-28 10:36:25 +02:00
|
|
|
genericData.clear();
|
2024-04-04 21:28:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isEmpty() {
|
|
|
|
return (stepsTotal == 0 &&
|
|
|
|
stepsGoalFactor == 0 &&
|
|
|
|
sleepTotalMinutes == 0 &&
|
|
|
|
sleepGoalFactor == 0 &&
|
|
|
|
distanceTotalMeters == 0 &&
|
|
|
|
distanceGoalFactor == 0 &&
|
|
|
|
activeMinutesTotal == 0 &&
|
|
|
|
activeMinutesGoalFactor == 0 &&
|
2024-08-28 10:36:25 +02:00
|
|
|
genericData.isEmpty() &&
|
2024-04-04 21:28:04 +02:00
|
|
|
generalizedActivities.isEmpty());
|
|
|
|
}
|
|
|
|
|
|
|
|
public synchronized int getStepsTotal() {
|
|
|
|
if (stepsTotal == 0)
|
|
|
|
stepsTotal = DashboardUtils.getStepsTotal(this);
|
|
|
|
return stepsTotal;
|
|
|
|
}
|
|
|
|
|
|
|
|
public synchronized float getStepsGoalFactor() {
|
|
|
|
if (stepsGoalFactor == 0)
|
|
|
|
stepsGoalFactor = DashboardUtils.getStepsGoalFactor(this);
|
|
|
|
return stepsGoalFactor;
|
|
|
|
}
|
|
|
|
|
|
|
|
public synchronized float getDistanceTotal() {
|
|
|
|
if (distanceTotalMeters == 0)
|
|
|
|
distanceTotalMeters = DashboardUtils.getDistanceTotal(this);
|
|
|
|
return distanceTotalMeters;
|
|
|
|
}
|
|
|
|
|
|
|
|
public synchronized float getDistanceGoalFactor() {
|
|
|
|
if (distanceGoalFactor == 0)
|
|
|
|
distanceGoalFactor = DashboardUtils.getDistanceGoalFactor(this);
|
|
|
|
return distanceGoalFactor;
|
|
|
|
}
|
|
|
|
|
|
|
|
public synchronized long getActiveMinutesTotal() {
|
|
|
|
if (activeMinutesTotal == 0)
|
|
|
|
activeMinutesTotal = DashboardUtils.getActiveMinutesTotal(this);
|
|
|
|
return activeMinutesTotal;
|
|
|
|
}
|
|
|
|
|
|
|
|
public synchronized float getActiveMinutesGoalFactor() {
|
|
|
|
if (activeMinutesGoalFactor == 0)
|
|
|
|
activeMinutesGoalFactor = DashboardUtils.getActiveMinutesGoalFactor(this);
|
|
|
|
return activeMinutesGoalFactor;
|
|
|
|
}
|
|
|
|
|
|
|
|
public synchronized long getSleepMinutesTotal() {
|
|
|
|
if (sleepTotalMinutes == 0)
|
|
|
|
sleepTotalMinutes = DashboardUtils.getSleepMinutesTotal(this);
|
|
|
|
return sleepTotalMinutes;
|
|
|
|
}
|
|
|
|
|
|
|
|
public synchronized float getSleepMinutesGoalFactor() {
|
|
|
|
if (sleepGoalFactor == 0)
|
|
|
|
sleepGoalFactor = DashboardUtils.getSleepMinutesGoalFactor(this);
|
|
|
|
return sleepGoalFactor;
|
|
|
|
}
|
|
|
|
|
2024-08-28 10:36:25 +02:00
|
|
|
public void put(final String key, final Serializable value) {
|
|
|
|
genericData.put(key, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
public Serializable get(final String key) {
|
|
|
|
return genericData.get(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @noinspection UnusedReturnValue
|
|
|
|
*/
|
|
|
|
public Serializable computeIfAbsent(final String key, final Supplier<Serializable> supplier) {
|
|
|
|
return genericData.computeIfAbsent(key, absent -> supplier.get());
|
|
|
|
}
|
|
|
|
|
2024-04-04 21:28:04 +02:00
|
|
|
public static class GeneralizedActivity implements Serializable {
|
2024-08-12 20:41:50 +02:00
|
|
|
public ActivityKind activityKind;
|
2024-04-04 21:28:04 +02:00
|
|
|
public long timeFrom;
|
|
|
|
public long timeTo;
|
|
|
|
|
2024-08-12 20:41:50 +02:00
|
|
|
public GeneralizedActivity(ActivityKind activityKind, long timeFrom, long timeTo) {
|
2024-04-04 21:28:04 +02:00
|
|
|
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";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|