mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-27 04:16:49 +01:00
Calories: add fragment
This commit is contained in:
parent
c119b04478
commit
68ff0dfa0b
@ -127,7 +127,7 @@ public class GBApplication extends Application {
|
|||||||
private static SharedPreferences sharedPrefs;
|
private static SharedPreferences sharedPrefs;
|
||||||
private static final String PREFS_VERSION = "shared_preferences_version";
|
private static final String PREFS_VERSION = "shared_preferences_version";
|
||||||
//if preferences have to be migrated, increment the following and add the migration logic in migratePrefs below; see http://stackoverflow.com/questions/16397848/how-can-i-migrate-android-preferences-with-a-new-version
|
//if preferences have to be migrated, increment the following and add the migration logic in migratePrefs below; see http://stackoverflow.com/questions/16397848/how-can-i-migrate-android-preferences-with-a-new-version
|
||||||
private static final int CURRENT_PREFS_VERSION = 42;
|
private static final int CURRENT_PREFS_VERSION = 44;
|
||||||
|
|
||||||
private static final LimitedQueue<Integer, String> mIDSenderLookup = new LimitedQueue<>(16);
|
private static final LimitedQueue<Integer, String> mIDSenderLookup = new LimitedQueue<>(16);
|
||||||
private static GBPrefs prefs;
|
private static GBPrefs prefs;
|
||||||
@ -1838,6 +1838,44 @@ public class GBApplication extends Application {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oldVersion < 43) {
|
||||||
|
// Add the new calories tab to all devices.
|
||||||
|
try (DBHandler db = acquireDB()) {
|
||||||
|
final DaoSession daoSession = db.getDaoSession();
|
||||||
|
final List<Device> activeDevices = DBHelper.getActiveDevices(daoSession);
|
||||||
|
|
||||||
|
for (final Device dbDevice : activeDevices) {
|
||||||
|
final SharedPreferences deviceSharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(dbDevice.getIdentifier());
|
||||||
|
|
||||||
|
final String chartsTabsValue = deviceSharedPrefs.getString("charts_tabs", null);
|
||||||
|
if (chartsTabsValue == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String newPrefValue;
|
||||||
|
if (!StringUtils.isBlank(chartsTabsValue)) {
|
||||||
|
newPrefValue = chartsTabsValue + ",calories";
|
||||||
|
} else {
|
||||||
|
newPrefValue = "calories";
|
||||||
|
}
|
||||||
|
|
||||||
|
final SharedPreferences.Editor deviceSharedPrefsEdit = deviceSharedPrefs.edit();
|
||||||
|
deviceSharedPrefsEdit.putString("charts_tabs", newPrefValue);
|
||||||
|
deviceSharedPrefsEdit.apply();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Failed to migrate prefs to version 43", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldVersion < 44) {
|
||||||
|
// Add new dashboard calories widgets.
|
||||||
|
final String dashboardWidgetsOrder = sharedPrefs.getString("pref_dashboard_widgets_order", null);
|
||||||
|
if (!StringUtils.isBlank(dashboardWidgetsOrder) && !dashboardWidgetsOrder.contains("calories")) {
|
||||||
|
editor.putString("pref_dashboard_widgets_order", dashboardWidgetsOrder + ",calories,calories_active,calories_segmented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
editor.putString(PREFS_VERSION, Integer.toString(CURRENT_PREFS_VERSION));
|
editor.putString(PREFS_VERSION, Integer.toString(CURRENT_PREFS_VERSION));
|
||||||
editor.apply();
|
editor.apply();
|
||||||
}
|
}
|
||||||
|
@ -63,9 +63,12 @@ import java.util.function.Supplier;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.AbstractDashboardWidget;
|
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.AbstractDashboardWidget;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardCaloriesActiveGoalWidget;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardActiveTimeWidget;
|
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardActiveTimeWidget;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardBodyEnergyWidget;
|
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardBodyEnergyWidget;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardCalendarActivity;
|
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardCalendarActivity;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardCaloriesTotalSegmentedWidget;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardCaloriesGoalWidget;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardDistanceWidget;
|
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardDistanceWidget;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardGoalsWidget;
|
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardGoalsWidget;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardHrvWidget;
|
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.DashboardHrvWidget;
|
||||||
@ -310,6 +313,15 @@ public class DashboardFragment extends Fragment implements MenuProvider {
|
|||||||
case "vo2max":
|
case "vo2max":
|
||||||
widget = DashboardVO2MaxAnyWidget.newInstance(dashboardData);
|
widget = DashboardVO2MaxAnyWidget.newInstance(dashboardData);
|
||||||
break;
|
break;
|
||||||
|
case "calories":
|
||||||
|
widget = DashboardCaloriesGoalWidget.newInstance(dashboardData);
|
||||||
|
break;
|
||||||
|
case "calories_active":
|
||||||
|
widget = DashboardCaloriesActiveGoalWidget.newInstance(dashboardData);
|
||||||
|
break;
|
||||||
|
case "calories_segmented":
|
||||||
|
widget = DashboardCaloriesTotalSegmentedWidget.newInstance(dashboardData);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
LOG.error("Unknown dashboard widget {}", widgetName);
|
LOG.error("Unknown dashboard widget {}", widgetName);
|
||||||
continue;
|
continue;
|
||||||
@ -372,6 +384,11 @@ public class DashboardFragment extends Fragment implements MenuProvider {
|
|||||||
public final List<GeneralizedActivity> generalizedActivities = Collections.synchronizedList(new ArrayList<>());
|
public final List<GeneralizedActivity> generalizedActivities = Collections.synchronizedList(new ArrayList<>());
|
||||||
private int stepsTotal;
|
private int stepsTotal;
|
||||||
private float stepsGoalFactor;
|
private float stepsGoalFactor;
|
||||||
|
private int restingCaloriesTotal;
|
||||||
|
private int activeCaloriesTotal;
|
||||||
|
private float activeCaloriesGoalFactor;
|
||||||
|
private int caloriesTotal;
|
||||||
|
private float caloriesGoalFactor;
|
||||||
private long sleepTotalMinutes;
|
private long sleepTotalMinutes;
|
||||||
private float sleepGoalFactor;
|
private float sleepGoalFactor;
|
||||||
private float distanceTotalMeters;
|
private float distanceTotalMeters;
|
||||||
@ -381,6 +398,11 @@ public class DashboardFragment extends Fragment implements MenuProvider {
|
|||||||
private final Map<String, Serializable> genericData = new ConcurrentHashMap<>();
|
private final Map<String, Serializable> genericData = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public void clear() {
|
public void clear() {
|
||||||
|
restingCaloriesTotal = 0;
|
||||||
|
activeCaloriesTotal = 0;
|
||||||
|
activeCaloriesGoalFactor = 0;
|
||||||
|
caloriesTotal = 0;
|
||||||
|
caloriesGoalFactor = 0;
|
||||||
stepsTotal = 0;
|
stepsTotal = 0;
|
||||||
stepsGoalFactor = 0;
|
stepsGoalFactor = 0;
|
||||||
sleepTotalMinutes = 0;
|
sleepTotalMinutes = 0;
|
||||||
@ -396,6 +418,11 @@ public class DashboardFragment extends Fragment implements MenuProvider {
|
|||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return (stepsTotal == 0 &&
|
return (stepsTotal == 0 &&
|
||||||
stepsGoalFactor == 0 &&
|
stepsGoalFactor == 0 &&
|
||||||
|
restingCaloriesTotal == 0 &&
|
||||||
|
activeCaloriesTotal == 0 &&
|
||||||
|
activeCaloriesGoalFactor == 0 &&
|
||||||
|
caloriesTotal == 0 &&
|
||||||
|
caloriesGoalFactor == 0 &&
|
||||||
sleepTotalMinutes == 0 &&
|
sleepTotalMinutes == 0 &&
|
||||||
sleepGoalFactor == 0 &&
|
sleepGoalFactor == 0 &&
|
||||||
distanceTotalMeters == 0 &&
|
distanceTotalMeters == 0 &&
|
||||||
@ -454,6 +481,36 @@ public class DashboardFragment extends Fragment implements MenuProvider {
|
|||||||
return sleepGoalFactor;
|
return sleepGoalFactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized int getActiveCaloriesTotal() {
|
||||||
|
if (activeCaloriesTotal == 0)
|
||||||
|
activeCaloriesTotal = DashboardUtils.getActiveCaloriesTotal(this);
|
||||||
|
return activeCaloriesTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized int getRestingCaloriesTotal() {
|
||||||
|
if (restingCaloriesTotal == 0)
|
||||||
|
restingCaloriesTotal = DashboardUtils.getRestingCaloriesTotal(this);
|
||||||
|
return restingCaloriesTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized float getActiveCaloriesGoalFactor() {
|
||||||
|
if (activeCaloriesGoalFactor == 0)
|
||||||
|
activeCaloriesGoalFactor = DashboardUtils.getActiveCaloriesGoalFactor(this);
|
||||||
|
return activeCaloriesGoalFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized int getCaloriesTotal() {
|
||||||
|
if (caloriesTotal == 0)
|
||||||
|
caloriesTotal = getRestingCaloriesTotal() + getActiveCaloriesTotal();
|
||||||
|
return caloriesTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized float getCaloriesGoalFactor() {
|
||||||
|
if (caloriesGoalFactor == 0)
|
||||||
|
caloriesGoalFactor = DashboardUtils.getCaloriesGoalFactor(this);
|
||||||
|
return caloriesGoalFactor;
|
||||||
|
}
|
||||||
|
|
||||||
public void put(final String key, final Serializable value) {
|
public void put(final String key, final Serializable value) {
|
||||||
genericData.put(key, value);
|
genericData.put(key, value);
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,7 @@ public abstract class AbstractChartsActivity extends AbstractGBFragmentActivity
|
|||||||
public static final String EXTRA_SINGLE_FRAGMENT_NAME = "singleFragmentName";
|
public static final String EXTRA_SINGLE_FRAGMENT_NAME = "singleFragmentName";
|
||||||
public static final String EXTRA_ACTIONBAR_TITLE = "actionbarTitle";
|
public static final String EXTRA_ACTIONBAR_TITLE = "actionbarTitle";
|
||||||
public static final String EXTRA_TIMESTAMP = "timestamp";
|
public static final String EXTRA_TIMESTAMP = "timestamp";
|
||||||
|
public static final String EXTRA_MODE = "mode";
|
||||||
|
|
||||||
private TextView mDateControl;
|
private TextView mDateControl;
|
||||||
|
|
||||||
|
@ -80,6 +80,11 @@ public class ActivityAnalysis {
|
|||||||
amount.addDistance(distance);
|
amount.addDistance(distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final int activeCalories = sample.getActiveCalories();
|
||||||
|
if (activeCalories > 0) {
|
||||||
|
amount.addActiveCalories(activeCalories);
|
||||||
|
}
|
||||||
|
|
||||||
if (previousSample != null) {
|
if (previousSample != null) {
|
||||||
long timeDifference = sample.getTimestamp() - previousSample.getTimestamp();
|
long timeDifference = sample.getTimestamp() - previousSample.getTimestamp();
|
||||||
if (previousSample.getRawKind() == sample.getRawKind()) {
|
if (previousSample.getRawKind() == sample.getRawKind()) {
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package nodomain.freeyourgadget.gadgetbridge.activities.charts;
|
package nodomain.freeyourgadget.gadgetbridge.activities.charts;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@ -133,6 +134,9 @@ public class ActivityChartsActivity extends AbstractChartsActivity {
|
|||||||
if (!coordinator.supportsVO2Max()) {
|
if (!coordinator.supportsVO2Max()) {
|
||||||
tabList.remove("vo2max");
|
tabList.remove("vo2max");
|
||||||
}
|
}
|
||||||
|
if (!coordinator.supportsActiveCalories() && !coordinator.supportsRestingCalories()) {
|
||||||
|
tabList.remove("calories");
|
||||||
|
}
|
||||||
return tabList;
|
return tabList;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,6 +191,10 @@ public class ActivityChartsActivity extends AbstractChartsActivity {
|
|||||||
return new CyclingChartFragment();
|
return new CyclingChartFragment();
|
||||||
case "weight":
|
case "weight":
|
||||||
return new WeightChartFragment();
|
return new WeightChartFragment();
|
||||||
|
case "calories":
|
||||||
|
Intent intent = getIntent();
|
||||||
|
String mode = intent.getStringExtra(ActivityChartsActivity.EXTRA_MODE);
|
||||||
|
return CaloriesDailyFragment.newInstance(mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new UnknownFragment();
|
return new UnknownFragment();
|
||||||
@ -232,6 +240,8 @@ public class ActivityChartsActivity extends AbstractChartsActivity {
|
|||||||
return getString(R.string.title_cycling);
|
return getString(R.string.title_cycling);
|
||||||
case "weight":
|
case "weight":
|
||||||
return getString(R.string.menuitem_weight);
|
return getString(R.string.menuitem_weight);
|
||||||
|
case "calories":
|
||||||
|
return getString(R.string.calories);
|
||||||
}
|
}
|
||||||
|
|
||||||
return String.format(Locale.getDefault(), "Unknown %d", position);
|
return String.format(Locale.getDefault(), "Unknown %d", position);
|
||||||
|
@ -0,0 +1,251 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.activities.charts;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
|
import com.github.mikephil.charting.charts.Chart;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.EnumUtils;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.GaugeDrawer;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.TimeSampleProvider;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.entities.GarminRestingMetabolicRateSample;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.RestingMetabolicRateSample;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.model.TimeSample;
|
||||||
|
|
||||||
|
public class CaloriesDailyFragment extends AbstractChartFragment<CaloriesDailyFragment.CaloriesData> {
|
||||||
|
|
||||||
|
private ImageView caloriesGauge;
|
||||||
|
private TextView dateView;
|
||||||
|
private TextView caloriesResting;
|
||||||
|
private LinearLayout caloriesRestingWrapper;
|
||||||
|
private TextView caloriesActive;
|
||||||
|
private TextView caloriesActiveGoal;
|
||||||
|
private TextView caloriesTotalGoal;
|
||||||
|
private LinearLayout caloriesTotalGoalWrapper;
|
||||||
|
protected int CALORIES_GOAL;
|
||||||
|
public enum GaugeViewMode {
|
||||||
|
ACTIVE_CALORIES_GOAL,
|
||||||
|
TOTAL_CALORIES_GOAL,
|
||||||
|
TOTAL_CALORIES_SEGMENT
|
||||||
|
}
|
||||||
|
private GaugeViewMode gaugeViewMode;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(final Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
if (getArguments() != null) {
|
||||||
|
String mode = getArguments().getString(ActivityChartsActivity.EXTRA_MODE, "");
|
||||||
|
if (EnumUtils.isValidEnum(GaugeViewMode.class, mode)) {
|
||||||
|
gaugeViewMode = GaugeViewMode.valueOf(mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CaloriesDailyFragment newInstance(final String mode) {
|
||||||
|
final CaloriesDailyFragment fragment = new CaloriesDailyFragment();
|
||||||
|
final Bundle args = new Bundle();
|
||||||
|
args.putString(ActivityChartsActivity.EXTRA_MODE, mode);
|
||||||
|
fragment.setArguments(args);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
View rootView = inflater.inflate(R.layout.fragment_calories, container, false);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
rootView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
|
||||||
|
getChartsHost().enableSwipeRefresh(scrollY == 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
caloriesGauge = rootView.findViewById(R.id.calories_gauge);
|
||||||
|
dateView = rootView.findViewById(R.id.date_view);
|
||||||
|
caloriesResting = rootView.findViewById(R.id.calories_resting);
|
||||||
|
caloriesRestingWrapper = rootView.findViewById(R.id.calories_resting_wrapper);
|
||||||
|
caloriesActive = rootView.findViewById(R.id.calories_active);
|
||||||
|
caloriesActiveGoal = rootView.findViewById(R.id.calories_active_goal);
|
||||||
|
caloriesTotalGoal = rootView.findViewById(R.id.calories_total_goal);
|
||||||
|
caloriesTotalGoalWrapper = rootView.findViewById(R.id.calories_total_goal_wrapper);
|
||||||
|
ActivityUser activityUser = new ActivityUser();
|
||||||
|
int TOTAL_CALORIES_GOAL = activityUser.getCaloriesBurntGoal();
|
||||||
|
caloriesTotalGoal.setText(String.valueOf(TOTAL_CALORIES_GOAL));
|
||||||
|
int ACTIVE_CALORIES_GOAL = activityUser.getActiveCaloriesBurntGoal();
|
||||||
|
caloriesActiveGoal.setText(String.valueOf(ACTIVE_CALORIES_GOAL));
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
if (!supportsActiveCalories()) {
|
||||||
|
caloriesActive.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gaugeViewMode == null) {
|
||||||
|
gaugeViewMode = GaugeViewMode.TOTAL_CALORIES_SEGMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gaugeViewMode.equals(GaugeViewMode.ACTIVE_CALORIES_GOAL)) {
|
||||||
|
CALORIES_GOAL = ACTIVE_CALORIES_GOAL;
|
||||||
|
} else if (gaugeViewMode.equals(GaugeViewMode.TOTAL_CALORIES_GOAL)) {
|
||||||
|
CALORIES_GOAL = TOTAL_CALORIES_GOAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rootView;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean supportsActiveCalories() {
|
||||||
|
final GBDevice device = getChartsHost().getDevice();
|
||||||
|
return device.getDeviceCoordinator().supportsActiveCalories();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected TimeSample getRestingMetabolicRate(DBHandler db, GBDevice device) {
|
||||||
|
TimeSampleProvider<? extends RestingMetabolicRateSample> provider = device.getDeviceCoordinator().getRestingMetabolicRateProvider(device, db.getDaoSession());
|
||||||
|
return provider.getLatestSample();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<? extends AbstractActivitySample> getActivitySamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
|
||||||
|
SampleProvider<? extends ActivitySample> provider = device.getDeviceCoordinator().getSampleProvider(device, db.getDaoSession());
|
||||||
|
return provider.getAllActivitySamples(tsFrom, tsTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTitle() {
|
||||||
|
return getString(R.string.calories);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CaloriesDailyFragment.CaloriesData refreshInBackground(ChartsHost chartsHost, DBHandler db, GBDevice device) {
|
||||||
|
Calendar calendar = Calendar.getInstance();
|
||||||
|
Calendar day = Calendar.getInstance();
|
||||||
|
day.setTime(chartsHost.getEndDate());
|
||||||
|
day.add(Calendar.DATE, 0);
|
||||||
|
day.set(Calendar.HOUR_OF_DAY, 0);
|
||||||
|
day.set(Calendar.MINUTE, 0);
|
||||||
|
day.set(Calendar.SECOND, 0);
|
||||||
|
day.add(Calendar.HOUR, 0);
|
||||||
|
int startTs = (int) (day.getTimeInMillis() / 1000);
|
||||||
|
int endTs = startTs + 24 * 60 * 60 - 1;
|
||||||
|
Date date = new Date((long) endTs * 1000);
|
||||||
|
String formattedDate = new SimpleDateFormat("E, MMM dd").format(date);
|
||||||
|
dateView.setText(formattedDate);
|
||||||
|
List<? extends ActivitySample> samples = getActivitySamples(db, device, startTs, endTs);
|
||||||
|
TimeSample metabolicRate = getRestingMetabolicRate(db, device);
|
||||||
|
int totalBurnt;
|
||||||
|
int activeBurnt = 0;
|
||||||
|
boolean sameDay = calendar.get(Calendar.DAY_OF_YEAR) == day.get(Calendar.DAY_OF_YEAR) &&
|
||||||
|
calendar.get(Calendar.YEAR) == day.get(Calendar.YEAR);
|
||||||
|
double passedDayProportion = 1;
|
||||||
|
if (sameDay) {
|
||||||
|
passedDayProportion = (double) (calendar.getTimeInMillis() - day.getTimeInMillis()) / (24L * 60 * 60 * 1000);
|
||||||
|
}
|
||||||
|
int restingBurnt = (int) ((double) ((GarminRestingMetabolicRateSample) metabolicRate).getRestingMetabolicRate() * passedDayProportion);
|
||||||
|
|
||||||
|
for (int i = 0; i <= samples.size() - 1; i++) {
|
||||||
|
ActivitySample sample = samples.get(i);
|
||||||
|
if (sample.getActiveCalories() > 0) {
|
||||||
|
activeBurnt += sample.getActiveCalories();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
totalBurnt = restingBurnt + activeBurnt;
|
||||||
|
|
||||||
|
return new CaloriesData(totalBurnt, activeBurnt, restingBurnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updateChartsnUIThread(CaloriesDailyFragment.CaloriesData data) {
|
||||||
|
int restingCalories = data.restingBurnt;
|
||||||
|
int activeCalories = data.activeBurnt;
|
||||||
|
int totalCalories = activeCalories + restingCalories;
|
||||||
|
caloriesActive.setText(String.valueOf(activeCalories));
|
||||||
|
caloriesResting.setText(String.valueOf(restingCalories));
|
||||||
|
|
||||||
|
if (gaugeViewMode.equals(GaugeViewMode.TOTAL_CALORIES_SEGMENT)) {
|
||||||
|
int[] colors = new int[] {
|
||||||
|
ContextCompat.getColor(GBApplication.getContext(), R.color.calories_resting_color),
|
||||||
|
ContextCompat.getColor(GBApplication.getContext(), R.color.calories_color)
|
||||||
|
};
|
||||||
|
float[] segments = new float[] {
|
||||||
|
restingCalories > 0 ? (float) restingCalories / totalCalories : 0,
|
||||||
|
activeCalories > 0 ? (float) activeCalories / totalCalories : 0
|
||||||
|
};
|
||||||
|
final int width = (int) TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_DIP,
|
||||||
|
300,
|
||||||
|
GBApplication.getContext().getResources().getDisplayMetrics()
|
||||||
|
);
|
||||||
|
caloriesGauge.setImageBitmap(GaugeDrawer.drawCircleGaugeSegmented(
|
||||||
|
width,
|
||||||
|
width / 15,
|
||||||
|
colors,
|
||||||
|
segments,
|
||||||
|
true,
|
||||||
|
String.valueOf(totalCalories),
|
||||||
|
getContext().getString(R.string.total_burnt),
|
||||||
|
getContext()
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
int value = 0;
|
||||||
|
if (gaugeViewMode.equals(GaugeViewMode.ACTIVE_CALORIES_GOAL)) {
|
||||||
|
value = activeCalories;
|
||||||
|
} else if (gaugeViewMode.equals(GaugeViewMode.TOTAL_CALORIES_GOAL)) {
|
||||||
|
value = totalCalories;
|
||||||
|
}
|
||||||
|
final int width = (int) TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_DIP,
|
||||||
|
300,
|
||||||
|
GBApplication.getContext().getResources().getDisplayMetrics()
|
||||||
|
);
|
||||||
|
caloriesGauge.setImageBitmap(GaugeDrawer.drawCircleGauge(
|
||||||
|
width,
|
||||||
|
width / 15,
|
||||||
|
getResources().getColor(R.color.calories_color),
|
||||||
|
value,
|
||||||
|
CALORIES_GOAL,
|
||||||
|
getContext()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderCharts() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setupLegend(Chart<?> chart) {}
|
||||||
|
|
||||||
|
protected static class CaloriesData extends ChartsData {
|
||||||
|
public int activeBurnt;
|
||||||
|
public int restingBurnt;
|
||||||
|
public int totalBurnt;
|
||||||
|
|
||||||
|
protected CaloriesData(int totalBurnt, int activeBurnt, int restingBurnt) {
|
||||||
|
this.totalBurnt = totalBurnt;
|
||||||
|
this.activeBurnt = activeBurnt;
|
||||||
|
this.restingBurnt = restingBurnt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -37,6 +37,7 @@ import java.util.List;
|
|||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.GaugeDrawer;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||||
@ -123,12 +124,13 @@ public class StepsDailyFragment extends StepsFragment<StepsDailyFragment.StepsDa
|
|||||||
GBApplication.getContext().getResources().getDisplayMetrics()
|
GBApplication.getContext().getResources().getDisplayMetrics()
|
||||||
);
|
);
|
||||||
|
|
||||||
stepsGauge.setImageBitmap(drawGauge(
|
stepsGauge.setImageBitmap(GaugeDrawer.drawCircleGauge(
|
||||||
width,
|
width,
|
||||||
width / 15,
|
width / 15,
|
||||||
getResources().getColor(R.color.steps_color),
|
getResources().getColor(R.color.steps_color),
|
||||||
(int) stepsData.todayStepsDay.steps,
|
(int) stepsData.todayStepsDay.steps,
|
||||||
STEPS_GOAL
|
STEPS_GOAL,
|
||||||
|
getContext()
|
||||||
));
|
));
|
||||||
|
|
||||||
steps.setText(String.format(String.valueOf(stepsData.todayStepsDay.steps)));
|
steps.setText(String.format(String.valueOf(stepsData.todayStepsDay.steps)));
|
||||||
@ -225,59 +227,6 @@ public class StepsDailyFragment extends StepsFragment<StepsDailyFragment.StepsDa
|
|||||||
yAxisRight.setDrawAxisLine(true);
|
yAxisRight.setDrawAxisLine(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Bitmap drawGauge(int width, int barWidth, @ColorInt int filledColor, int value, int maxValue) {
|
|
||||||
int height = width;
|
|
||||||
int barMargin = (int) Math.ceil(barWidth / 2f);
|
|
||||||
float filledFactor = (float) value / maxValue;
|
|
||||||
|
|
||||||
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
|
||||||
Canvas canvas = new Canvas(bitmap);
|
|
||||||
Paint paint = new Paint();
|
|
||||||
paint.setAntiAlias(true);
|
|
||||||
paint.setStyle(Paint.Style.STROKE);
|
|
||||||
paint.setStrokeCap(Paint.Cap.ROUND);
|
|
||||||
paint.setStrokeWidth(barWidth);
|
|
||||||
paint.setColor(getResources().getColor(R.color.gauge_line_color));
|
|
||||||
canvas.drawArc(
|
|
||||||
barMargin,
|
|
||||||
barMargin,
|
|
||||||
width - barMargin,
|
|
||||||
width - barMargin,
|
|
||||||
90,
|
|
||||||
360,
|
|
||||||
false,
|
|
||||||
paint);
|
|
||||||
paint.setStrokeWidth(barWidth);
|
|
||||||
paint.setColor(filledColor);
|
|
||||||
canvas.drawArc(
|
|
||||||
barMargin,
|
|
||||||
barMargin,
|
|
||||||
width - barMargin,
|
|
||||||
height - barMargin,
|
|
||||||
270,
|
|
||||||
360 * filledFactor,
|
|
||||||
false,
|
|
||||||
paint
|
|
||||||
);
|
|
||||||
|
|
||||||
Paint textPaint = new Paint();
|
|
||||||
textPaint.setColor(TEXT_COLOR);
|
|
||||||
float textPixels = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, width * 0.06f, requireContext().getResources().getDisplayMetrics());
|
|
||||||
textPaint.setTextSize(textPixels);
|
|
||||||
textPaint.setTextAlign(Paint.Align.CENTER);
|
|
||||||
int yPos = (int) ((float) height / 2 - ((textPaint.descent() + textPaint.ascent()) / 2)) ;
|
|
||||||
canvas.drawText(String.valueOf(value), width / 2f, yPos, textPaint);
|
|
||||||
Paint textLowerPaint = new Paint();
|
|
||||||
textLowerPaint.setColor(TEXT_COLOR);
|
|
||||||
textLowerPaint.setTextAlign(Paint.Align.CENTER);
|
|
||||||
float textLowerPixels = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, width * 0.025f, requireContext().getResources().getDisplayMetrics());
|
|
||||||
textLowerPaint.setTextSize(textLowerPixels);
|
|
||||||
int yPosLowerText = (int) ((float) height / 2 - textPaint.ascent()) ;
|
|
||||||
canvas.drawText(String.valueOf(maxValue), width / 2f, yPosLowerText, textLowerPaint);
|
|
||||||
|
|
||||||
return bitmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static class StepsData extends ChartsData {
|
protected static class StepsData extends ChartsData {
|
||||||
StepsDay todayStepsDay;
|
StepsDay todayStepsDay;
|
||||||
List<? extends ActivitySample> samples;
|
List<? extends ActivitySample> samples;
|
||||||
|
@ -87,7 +87,7 @@ public abstract class AbstractDashboardWidget extends Fragment {
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onClickOpenChart(final View view, final String chart, final int label) {
|
protected void onClickOpenChart(final View view, final String chart, final int label, final String mode) {
|
||||||
view.setOnClickListener(v -> {
|
view.setOnClickListener(v -> {
|
||||||
chooseDevice(dashboardData, device -> {
|
chooseDevice(dashboardData, device -> {
|
||||||
final Intent startIntent;
|
final Intent startIntent;
|
||||||
@ -96,6 +96,7 @@ public abstract class AbstractDashboardWidget extends Fragment {
|
|||||||
startIntent.putExtra(ActivityChartsActivity.EXTRA_SINGLE_FRAGMENT_NAME, chart);
|
startIntent.putExtra(ActivityChartsActivity.EXTRA_SINGLE_FRAGMENT_NAME, chart);
|
||||||
startIntent.putExtra(ActivityChartsActivity.EXTRA_ACTIONBAR_TITLE, label);
|
startIntent.putExtra(ActivityChartsActivity.EXTRA_ACTIONBAR_TITLE, label);
|
||||||
startIntent.putExtra(ActivityChartsActivity.EXTRA_TIMESTAMP, dashboardData.timeTo);
|
startIntent.putExtra(ActivityChartsActivity.EXTRA_TIMESTAMP, dashboardData.timeTo);
|
||||||
|
startIntent.putExtra(ActivityChartsActivity.EXTRA_MODE, mode);
|
||||||
requireContext().startActivity(startIntent);
|
requireContext().startActivity(startIntent);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -42,18 +42,24 @@ public abstract class AbstractGaugeWidget extends AbstractDashboardWidget {
|
|||||||
|
|
||||||
private final int label;
|
private final int label;
|
||||||
private final String targetActivityTab;
|
private final String targetActivityTab;
|
||||||
|
private String mode = "";
|
||||||
|
|
||||||
public AbstractGaugeWidget(@StringRes final int label, @Nullable final String targetActivityTab) {
|
public AbstractGaugeWidget(@StringRes final int label, @Nullable final String targetActivityTab) {
|
||||||
this.label = label;
|
this.label = label;
|
||||||
this.targetActivityTab = targetActivityTab;
|
this.targetActivityTab = targetActivityTab;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AbstractGaugeWidget(@StringRes final int label, @Nullable final String targetActivityTab, final String mode) {
|
||||||
|
this(label, targetActivityTab);
|
||||||
|
this.mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
|
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
|
||||||
final View fragmentView = inflater.inflate(R.layout.dashboard_widget_generic_gauge, container, false);
|
final View fragmentView = inflater.inflate(R.layout.dashboard_widget_generic_gauge, container, false);
|
||||||
|
|
||||||
if (targetActivityTab != null) {
|
if (targetActivityTab != null) {
|
||||||
onClickOpenChart(fragmentView, targetActivityTab, label);
|
onClickOpenChart(fragmentView, targetActivityTab, label, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
gaugeValue = fragmentView.findViewById(R.id.gauge_value);
|
gaugeValue = fragmentView.findViewById(R.id.gauge_value);
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.activities.dashboard;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.DashboardFragment;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.charts.CaloriesDailyFragment;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple {@link AbstractDashboardWidget} subclass.
|
||||||
|
* Use the {@link DashboardCaloriesActiveGoalWidget#newInstance} factory method to
|
||||||
|
* create an instance of this fragment.
|
||||||
|
*/
|
||||||
|
public class DashboardCaloriesActiveGoalWidget extends AbstractGaugeWidget {
|
||||||
|
public DashboardCaloriesActiveGoalWidget() {
|
||||||
|
super(R.string.active_calories, "calories", CaloriesDailyFragment.GaugeViewMode.ACTIVE_CALORIES_GOAL.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this factory method to create a new instance of
|
||||||
|
* this fragment using the provided parameters.
|
||||||
|
*
|
||||||
|
* @param dashboardData An instance of DashboardFragment.DashboardData.
|
||||||
|
* @return A new instance of fragment DashboardStepsWidget.
|
||||||
|
*/
|
||||||
|
public static DashboardCaloriesActiveGoalWidget newInstance(final DashboardFragment.DashboardData dashboardData) {
|
||||||
|
final DashboardCaloriesActiveGoalWidget fragment = new DashboardCaloriesActiveGoalWidget();
|
||||||
|
final Bundle args = new Bundle();
|
||||||
|
args.putSerializable(ARG_DASHBOARD_DATA, dashboardData);
|
||||||
|
fragment.setArguments(args);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isSupportedBy(final GBDevice device) {
|
||||||
|
return device.getDeviceCoordinator().supportsActiveCalories();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void populateData(final DashboardFragment.DashboardData dashboardData) {
|
||||||
|
dashboardData.getActiveCaloriesTotal();
|
||||||
|
dashboardData.getActiveCaloriesGoalFactor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void draw(final DashboardFragment.DashboardData dashboardData) {
|
||||||
|
setText(String.valueOf(dashboardData.getActiveCaloriesTotal()));
|
||||||
|
final int colorCalories = ContextCompat.getColor(GBApplication.getContext(), R.color.calories_color);
|
||||||
|
drawSimpleGauge(
|
||||||
|
colorCalories,
|
||||||
|
dashboardData.getActiveCaloriesGoalFactor()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.activities.dashboard;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.DashboardFragment;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.charts.CaloriesDailyFragment;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple {@link AbstractDashboardWidget} subclass.
|
||||||
|
* Use the {@link DashboardCaloriesGoalWidget#newInstance} factory method to
|
||||||
|
* create an instance of this fragment.
|
||||||
|
*/
|
||||||
|
public class DashboardCaloriesGoalWidget extends AbstractGaugeWidget {
|
||||||
|
public DashboardCaloriesGoalWidget() {
|
||||||
|
super(R.string.calories, "calories", CaloriesDailyFragment.GaugeViewMode.TOTAL_CALORIES_GOAL.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this factory method to create a new instance of
|
||||||
|
* this fragment using the provided parameters.
|
||||||
|
*
|
||||||
|
* @param dashboardData An instance of DashboardFragment.DashboardData.
|
||||||
|
* @return A new instance of fragment DashboardStepsWidget.
|
||||||
|
*/
|
||||||
|
public static DashboardCaloriesGoalWidget newInstance(final DashboardFragment.DashboardData dashboardData) {
|
||||||
|
final DashboardCaloriesGoalWidget fragment = new DashboardCaloriesGoalWidget();
|
||||||
|
final Bundle args = new Bundle();
|
||||||
|
args.putSerializable(ARG_DASHBOARD_DATA, dashboardData);
|
||||||
|
fragment.setArguments(args);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isSupportedBy(final GBDevice device) {
|
||||||
|
return device.getDeviceCoordinator().supportsActiveCalories();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void populateData(final DashboardFragment.DashboardData dashboardData) {
|
||||||
|
dashboardData.getCaloriesTotal();
|
||||||
|
dashboardData.getCaloriesGoalFactor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void draw(final DashboardFragment.DashboardData dashboardData) {
|
||||||
|
setText(String.valueOf(dashboardData.getCaloriesTotal()));
|
||||||
|
final int colorCalories = ContextCompat.getColor(GBApplication.getContext(), R.color.calories_color);
|
||||||
|
drawSimpleGauge(
|
||||||
|
colorCalories,
|
||||||
|
dashboardData.getCaloriesGoalFactor()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
package nodomain.freeyourgadget.gadgetbridge.activities.dashboard;
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.DashboardFragment;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.activities.charts.CaloriesDailyFragment;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple {@link AbstractDashboardWidget} subclass.
|
||||||
|
* Use the {@link DashboardCaloriesTotalSegmentedWidget#newInstance} factory method to
|
||||||
|
* create an instance of this fragment.
|
||||||
|
*/
|
||||||
|
public class DashboardCaloriesTotalSegmentedWidget extends AbstractGaugeWidget {
|
||||||
|
public DashboardCaloriesTotalSegmentedWidget() {
|
||||||
|
super(R.string.calories, "calories", CaloriesDailyFragment.GaugeViewMode.TOTAL_CALORIES_SEGMENT.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this factory method to create a new instance of
|
||||||
|
* this fragment using the provided parameters.
|
||||||
|
*
|
||||||
|
* @param dashboardData An instance of DashboardFragment.DashboardData.
|
||||||
|
* @return A new instance of fragment DashboardStepsWidget.
|
||||||
|
*/
|
||||||
|
public static DashboardCaloriesTotalSegmentedWidget newInstance(final DashboardFragment.DashboardData dashboardData) {
|
||||||
|
final DashboardCaloriesTotalSegmentedWidget fragment = new DashboardCaloriesTotalSegmentedWidget();
|
||||||
|
final Bundle args = new Bundle();
|
||||||
|
args.putSerializable(ARG_DASHBOARD_DATA, dashboardData);
|
||||||
|
fragment.setArguments(args);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void populateData(final DashboardFragment.DashboardData dashboardData) {
|
||||||
|
dashboardData.getActiveCaloriesTotal();
|
||||||
|
dashboardData.getRestingCaloriesTotal();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void draw(final DashboardFragment.DashboardData dashboardData) {
|
||||||
|
int activeCalories = dashboardData.getActiveCaloriesTotal();
|
||||||
|
int restingCalories = dashboardData.getRestingCaloriesTotal();
|
||||||
|
int totalCalories = activeCalories + restingCalories;
|
||||||
|
setText(String.valueOf(totalCalories));
|
||||||
|
final int[] colors;
|
||||||
|
final float[] segments;
|
||||||
|
if (totalCalories != 0) {
|
||||||
|
colors = new int[] {
|
||||||
|
ContextCompat.getColor(GBApplication.getContext(), R.color.calories_resting_color),
|
||||||
|
ContextCompat.getColor(GBApplication.getContext(), R.color.calories_color)
|
||||||
|
};
|
||||||
|
segments = new float[] {
|
||||||
|
restingCalories > 0 ? (float) restingCalories / totalCalories : 0,
|
||||||
|
activeCalories > 0 ? (float) activeCalories / totalCalories : 0
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
colors = new int[]{
|
||||||
|
Color.argb(25, 128, 128, 128)
|
||||||
|
};
|
||||||
|
segments = new float[] {
|
||||||
|
1f
|
||||||
|
};
|
||||||
|
}
|
||||||
|
drawSegmentedGauge(
|
||||||
|
colors,
|
||||||
|
segments,
|
||||||
|
-1,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package nodomain.freeyourgadget.gadgetbridge.activities.dashboard;
|
package nodomain.freeyourgadget.gadgetbridge.activities.dashboard;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
@ -15,6 +16,7 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||||
|
|
||||||
public class GaugeDrawer {
|
public class GaugeDrawer {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(GaugeDrawer.class);
|
private static final Logger LOG = LoggerFactory.getLogger(GaugeDrawer.class);
|
||||||
@ -194,6 +196,131 @@ public class GaugeDrawer {
|
|||||||
gaugeBar.setImageBitmap(bitmap);
|
gaugeBar.setImageBitmap(bitmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Bitmap drawCircleGaugeSegmented(int width, int barWidth, final int[] colors, final float[] segments, final boolean gapBetweenSegments, String text, String lowerText, Context context) {
|
||||||
|
int TEXT_COLOR = GBApplication.getTextColor(context);
|
||||||
|
int height = width;
|
||||||
|
int barMargin = (int) Math.ceil(barWidth / 2f);
|
||||||
|
|
||||||
|
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||||
|
Canvas canvas = new Canvas(bitmap);
|
||||||
|
Paint paint = new Paint();
|
||||||
|
paint.setAntiAlias(true);
|
||||||
|
paint.setStyle(Paint.Style.STROKE);
|
||||||
|
paint.setStrokeCap(Paint.Cap.BUTT);
|
||||||
|
paint.setStrokeWidth(barWidth);
|
||||||
|
paint.setColor(context.getResources().getColor(R.color.gauge_line_color));
|
||||||
|
canvas.drawArc(
|
||||||
|
barMargin,
|
||||||
|
barMargin,
|
||||||
|
width - barMargin,
|
||||||
|
width - barMargin,
|
||||||
|
90,
|
||||||
|
360,
|
||||||
|
false,
|
||||||
|
paint);
|
||||||
|
paint.setStrokeWidth(barWidth);
|
||||||
|
|
||||||
|
float angleSum = 0;
|
||||||
|
for (int i = 0; i < segments.length; i++) {
|
||||||
|
if (segments[i] == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
paint.setColor(colors[i]);
|
||||||
|
paint.setStrokeWidth(barWidth);
|
||||||
|
|
||||||
|
float startAngleDegrees = 270 + angleSum * 360;
|
||||||
|
float sweepAngleDegrees = segments[i] * 360;
|
||||||
|
|
||||||
|
if (gapBetweenSegments) {
|
||||||
|
sweepAngleDegrees -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.drawArc(
|
||||||
|
barMargin,
|
||||||
|
barMargin,
|
||||||
|
width - barMargin,
|
||||||
|
height - barMargin,
|
||||||
|
startAngleDegrees,
|
||||||
|
sweepAngleDegrees,
|
||||||
|
false,
|
||||||
|
paint
|
||||||
|
);
|
||||||
|
angleSum += segments[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
Paint textPaint = new Paint();
|
||||||
|
textPaint.setColor(TEXT_COLOR);
|
||||||
|
float textPixels = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, width * 0.06f, context.getResources().getDisplayMetrics());
|
||||||
|
textPaint.setTextSize(textPixels);
|
||||||
|
textPaint.setTextAlign(Paint.Align.CENTER);
|
||||||
|
int yPos = (int) ((float) height / 2 - ((textPaint.descent() + textPaint.ascent()) / 2)) ;
|
||||||
|
canvas.drawText(String.valueOf(text), width / 2f, yPos, textPaint);
|
||||||
|
Paint textLowerPaint = new Paint();
|
||||||
|
textLowerPaint.setColor(TEXT_COLOR);
|
||||||
|
textLowerPaint.setTextAlign(Paint.Align.CENTER);
|
||||||
|
float textLowerPixels = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, width * 0.025f, context.getResources().getDisplayMetrics());
|
||||||
|
textLowerPaint.setTextSize(textLowerPixels);
|
||||||
|
int yPosLowerText = (int) ((float) height / 2 - textPaint.ascent()) ;
|
||||||
|
canvas.drawText(String.valueOf(lowerText), width / 2f, yPosLowerText, textLowerPaint);
|
||||||
|
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Bitmap drawCircleGauge(int width, int barWidth, @ColorInt int filledColor, int value, int maxValue, Context context) {
|
||||||
|
int TEXT_COLOR = GBApplication.getTextColor(context);
|
||||||
|
int height = width;
|
||||||
|
int barMargin = (int) Math.ceil(barWidth / 2f);
|
||||||
|
float filledFactor = (float) value / maxValue;
|
||||||
|
|
||||||
|
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||||
|
Canvas canvas = new Canvas(bitmap);
|
||||||
|
Paint paint = new Paint();
|
||||||
|
paint.setAntiAlias(true);
|
||||||
|
paint.setStyle(Paint.Style.STROKE);
|
||||||
|
paint.setStrokeCap(Paint.Cap.ROUND);
|
||||||
|
paint.setStrokeWidth(barWidth);
|
||||||
|
paint.setColor(context.getResources().getColor(R.color.gauge_line_color));
|
||||||
|
canvas.drawArc(
|
||||||
|
barMargin,
|
||||||
|
barMargin,
|
||||||
|
width - barMargin,
|
||||||
|
width - barMargin,
|
||||||
|
90,
|
||||||
|
360,
|
||||||
|
false,
|
||||||
|
paint);
|
||||||
|
paint.setStrokeWidth(barWidth);
|
||||||
|
paint.setColor(filledColor);
|
||||||
|
canvas.drawArc(
|
||||||
|
barMargin,
|
||||||
|
barMargin,
|
||||||
|
width - barMargin,
|
||||||
|
height - barMargin,
|
||||||
|
270,
|
||||||
|
360 * filledFactor,
|
||||||
|
false,
|
||||||
|
paint
|
||||||
|
);
|
||||||
|
|
||||||
|
Paint textPaint = new Paint();
|
||||||
|
textPaint.setColor(TEXT_COLOR);
|
||||||
|
float textPixels = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, width * 0.06f, context.getResources().getDisplayMetrics());
|
||||||
|
textPaint.setTextSize(textPixels);
|
||||||
|
textPaint.setTextAlign(Paint.Align.CENTER);
|
||||||
|
int yPos = (int) ((float) height / 2 - ((textPaint.descent() + textPaint.ascent()) / 2)) ;
|
||||||
|
canvas.drawText(String.valueOf(value), width / 2f, yPos, textPaint);
|
||||||
|
Paint textLowerPaint = new Paint();
|
||||||
|
textLowerPaint.setColor(TEXT_COLOR);
|
||||||
|
textLowerPaint.setTextAlign(Paint.Align.CENTER);
|
||||||
|
float textLowerPixels = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, width * 0.025f, context.getResources().getDisplayMetrics());
|
||||||
|
textLowerPaint.setTextSize(textLowerPixels);
|
||||||
|
int yPosLowerText = (int) ((float) height / 2 - textPaint.ascent()) ;
|
||||||
|
canvas.drawText(String.valueOf(maxValue), width / 2f, yPosLowerText, textLowerPaint);
|
||||||
|
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
public static double normalize(final double value, final double min, final double max) {
|
public static double normalize(final double value, final double min, final double max) {
|
||||||
return normalize(value, min, max, 0, 1);
|
return normalize(value, min, max, 0, 1);
|
||||||
}
|
}
|
||||||
|
@ -496,6 +496,16 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsActiveCalories() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRestingCalories() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsActivityTabs() {
|
public boolean supportsActivityTabs() {
|
||||||
return supportsActivityTracking();
|
return supportsActivityTracking();
|
||||||
|
@ -227,6 +227,8 @@ public interface DeviceCoordinator {
|
|||||||
boolean supportsStepCounter();
|
boolean supportsStepCounter();
|
||||||
boolean supportsSpeedzones();
|
boolean supportsSpeedzones();
|
||||||
boolean supportsActivityTabs();
|
boolean supportsActivityTabs();
|
||||||
|
boolean supportsRestingCalories();
|
||||||
|
boolean supportsActiveCalories();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if measurement and fetching of body temperature is supported by the device
|
* Returns true if measurement and fetching of body temperature is supported by the device
|
||||||
|
@ -20,7 +20,6 @@ import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpec
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer;
|
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsScreen;
|
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsScreen;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractBLEDeviceCoordinator;
|
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractBLEDeviceCoordinator;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.DefaultRestingMetabolicRateProvider;
|
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.WorkoutVo2MaxSampleProvider;
|
import nodomain.freeyourgadget.gadgetbridge.devices.WorkoutVo2MaxSampleProvider;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
|
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
|
||||||
@ -251,6 +250,16 @@ public abstract class GarminCoordinator extends AbstractBLEDeviceCoordinator {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsActiveCalories() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRestingCalories() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int[] getStressRanges() {
|
public int[] getStressRanges() {
|
||||||
// 1-25 = relaxed
|
// 1-25 = relaxed
|
||||||
|
@ -24,6 +24,7 @@ public class ActivityAmount {
|
|||||||
private long totalSeconds;
|
private long totalSeconds;
|
||||||
private long totalSteps;
|
private long totalSteps;
|
||||||
private long totalDistance;
|
private long totalDistance;
|
||||||
|
private long totalActiveCalories;
|
||||||
private Date startDate = null;
|
private Date startDate = null;
|
||||||
private Date endDate = null;
|
private Date endDate = null;
|
||||||
|
|
||||||
@ -43,6 +44,10 @@ public class ActivityAmount {
|
|||||||
totalDistance += distance;
|
totalDistance += distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addActiveCalories(long activeCalories) {
|
||||||
|
totalActiveCalories += activeCalories;
|
||||||
|
}
|
||||||
|
|
||||||
public long getTotalSeconds() {
|
public long getTotalSeconds() {
|
||||||
return totalSeconds;
|
return totalSeconds;
|
||||||
}
|
}
|
||||||
@ -55,6 +60,10 @@ public class ActivityAmount {
|
|||||||
return totalDistance;
|
return totalDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getTotalActiveCalories() {
|
||||||
|
return totalActiveCalories;
|
||||||
|
}
|
||||||
|
|
||||||
public ActivityKind getActivityKind() {
|
public ActivityKind getActivityKind() {
|
||||||
return activityKind;
|
return activityKind;
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,7 @@ public class ActivityUser {
|
|||||||
private int activityUserSleepDurationGoal;
|
private int activityUserSleepDurationGoal;
|
||||||
private int activityUserStepsGoal;
|
private int activityUserStepsGoal;
|
||||||
private int activityUserCaloriesBurntGoal;
|
private int activityUserCaloriesBurntGoal;
|
||||||
|
private int activityUserActiveCaloriesBurntGoal;
|
||||||
private int activityUserDistanceGoalMeters;
|
private int activityUserDistanceGoalMeters;
|
||||||
private int activityUserActiveTimeGoalMinutes;
|
private int activityUserActiveTimeGoalMinutes;
|
||||||
private int activityUserStandingTimeGoalHours;
|
private int activityUserStandingTimeGoalHours;
|
||||||
@ -58,6 +59,7 @@ public class ActivityUser {
|
|||||||
public static final int defaultUserSleepDurationGoal = 7;
|
public static final int defaultUserSleepDurationGoal = 7;
|
||||||
public static final int defaultUserStepsGoal = 8000;
|
public static final int defaultUserStepsGoal = 8000;
|
||||||
public static final int defaultUserCaloriesBurntGoal = 2000;
|
public static final int defaultUserCaloriesBurntGoal = 2000;
|
||||||
|
public static final int defaultUserActiveCaloriesBurntGoal = 350;
|
||||||
public static final int defaultUserDistanceGoalMeters = 5000;
|
public static final int defaultUserDistanceGoalMeters = 5000;
|
||||||
public static final int defaultUserActiveTimeGoalMinutes = 60;
|
public static final int defaultUserActiveTimeGoalMinutes = 60;
|
||||||
public static final int defaultUserStepLengthCm = 0;
|
public static final int defaultUserStepLengthCm = 0;
|
||||||
@ -73,6 +75,7 @@ public class ActivityUser {
|
|||||||
public static final String PREF_USER_SLEEP_DURATION = "activity_user_sleep_duration";
|
public static final String PREF_USER_SLEEP_DURATION = "activity_user_sleep_duration";
|
||||||
public static final String PREF_USER_STEPS_GOAL = "fitness_goal"; // FIXME: for compatibility
|
public static final String PREF_USER_STEPS_GOAL = "fitness_goal"; // FIXME: for compatibility
|
||||||
public static final String PREF_USER_CALORIES_BURNT = "activity_user_calories_burnt";
|
public static final String PREF_USER_CALORIES_BURNT = "activity_user_calories_burnt";
|
||||||
|
public static final String PREF_USER_ACTIVE_CALORIES_BURNT = "activity_user_active_calories_burnt";
|
||||||
public static final String PREF_USER_DISTANCE_METERS = "activity_user_distance_meters";
|
public static final String PREF_USER_DISTANCE_METERS = "activity_user_distance_meters";
|
||||||
public static final String PREF_USER_ACTIVETIME_MINUTES = "activity_user_activetime_minutes";
|
public static final String PREF_USER_ACTIVETIME_MINUTES = "activity_user_activetime_minutes";
|
||||||
public static final String PREF_USER_STEP_LENGTH_CM = "activity_user_step_length_cm";
|
public static final String PREF_USER_STEP_LENGTH_CM = "activity_user_step_length_cm";
|
||||||
@ -160,6 +163,7 @@ public class ActivityUser {
|
|||||||
activityUserSleepDurationGoal = prefs.getInt(PREF_USER_SLEEP_DURATION, defaultUserSleepDurationGoal);
|
activityUserSleepDurationGoal = prefs.getInt(PREF_USER_SLEEP_DURATION, defaultUserSleepDurationGoal);
|
||||||
activityUserStepsGoal = prefs.getInt(PREF_USER_STEPS_GOAL, defaultUserStepsGoal);
|
activityUserStepsGoal = prefs.getInt(PREF_USER_STEPS_GOAL, defaultUserStepsGoal);
|
||||||
activityUserCaloriesBurntGoal = prefs.getInt(PREF_USER_CALORIES_BURNT, defaultUserCaloriesBurntGoal);
|
activityUserCaloriesBurntGoal = prefs.getInt(PREF_USER_CALORIES_BURNT, defaultUserCaloriesBurntGoal);
|
||||||
|
activityUserActiveCaloriesBurntGoal = prefs.getInt(PREF_USER_ACTIVE_CALORIES_BURNT, defaultUserActiveCaloriesBurntGoal);
|
||||||
activityUserDistanceGoalMeters = prefs.getInt(PREF_USER_DISTANCE_METERS, defaultUserDistanceGoalMeters);
|
activityUserDistanceGoalMeters = prefs.getInt(PREF_USER_DISTANCE_METERS, defaultUserDistanceGoalMeters);
|
||||||
activityUserActiveTimeGoalMinutes = prefs.getInt(PREF_USER_ACTIVETIME_MINUTES, defaultUserActiveTimeGoalMinutes);
|
activityUserActiveTimeGoalMinutes = prefs.getInt(PREF_USER_ACTIVETIME_MINUTES, defaultUserActiveTimeGoalMinutes);
|
||||||
activityUserStandingTimeGoalHours = prefs.getInt(PREF_USER_GOAL_STANDING_TIME_HOURS, defaultUserGoalStandingTimeHours);
|
activityUserStandingTimeGoalHours = prefs.getInt(PREF_USER_GOAL_STANDING_TIME_HOURS, defaultUserGoalStandingTimeHours);
|
||||||
@ -187,6 +191,14 @@ public class ActivityUser {
|
|||||||
return activityUserCaloriesBurntGoal;
|
return activityUserCaloriesBurntGoal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getActiveCaloriesBurntGoal()
|
||||||
|
{
|
||||||
|
if (activityUserActiveCaloriesBurntGoal < 1) {
|
||||||
|
activityUserActiveCaloriesBurntGoal = defaultUserActiveCaloriesBurntGoal;
|
||||||
|
}
|
||||||
|
return activityUserActiveCaloriesBurntGoal;
|
||||||
|
}
|
||||||
|
|
||||||
public int getDistanceGoalMeters()
|
public int getDistanceGoalMeters()
|
||||||
{
|
{
|
||||||
if (activityUserDistanceGoalMeters < 1) {
|
if (activityUserDistanceGoalMeters < 1) {
|
||||||
|
@ -33,7 +33,9 @@ import nodomain.freeyourgadget.gadgetbridge.activities.charts.ActivityAnalysis;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
|
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.devices.TimeSampleProvider;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample;
|
import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.entities.GarminRestingMetabolicRateSample;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||||
|
|
||||||
|
|
||||||
@ -42,22 +44,34 @@ public class DailyTotals implements Serializable {
|
|||||||
|
|
||||||
private final long steps;
|
private final long steps;
|
||||||
private final long distance;
|
private final long distance;
|
||||||
|
private final long activeCalories;
|
||||||
|
private final long restingCalories;
|
||||||
private final long[] sleep; // light deep rem awake
|
private final long[] sleep; // light deep rem awake
|
||||||
|
|
||||||
public DailyTotals() {
|
public DailyTotals() {
|
||||||
this(0, 0, new long[]{0, 0, 0 ,0});
|
this(0, 0, new long[]{0, 0, 0 ,0}, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DailyTotals(final long steps, final long distance, final long[] sleep) {
|
public DailyTotals(final long steps, final long distance, final long[] sleep, final long activeCalories, final long restingCalories) {
|
||||||
this.steps = steps;
|
this.steps = steps;
|
||||||
this.distance = distance;
|
this.distance = distance;
|
||||||
this.sleep = sleep;
|
this.sleep = sleep;
|
||||||
|
this.activeCalories = activeCalories;
|
||||||
|
this.restingCalories = restingCalories;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getSteps() {
|
public long getSteps() {
|
||||||
return steps;
|
return steps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getActiveCalories() {
|
||||||
|
return activeCalories;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getRestingCalories() {
|
||||||
|
return restingCalories;
|
||||||
|
}
|
||||||
|
|
||||||
public long getDistance() {
|
public long getDistance() {
|
||||||
return distance;
|
return distance;
|
||||||
}
|
}
|
||||||
@ -79,17 +93,27 @@ public class DailyTotals implements Serializable {
|
|||||||
|
|
||||||
public static DailyTotals getDailyTotalsForDevice(GBDevice device, Calendar day, DBHandler handler) {
|
public static DailyTotals getDailyTotalsForDevice(GBDevice device, Calendar day, DBHandler handler) {
|
||||||
ActivityAnalysis analysis = new ActivityAnalysis();
|
ActivityAnalysis analysis = new ActivityAnalysis();
|
||||||
ActivityAmounts amountsSteps;
|
ActivityAmounts totalAmounts;
|
||||||
ActivityAmounts amountsSleep;
|
ActivityAmounts amountsSleep;
|
||||||
|
|
||||||
amountsSteps = analysis.calculateActivityAmounts(getSamplesOfDay(handler, day, 0, device));
|
totalAmounts = analysis.calculateActivityAmounts(getSamplesOfDay(handler, day, 0, device));
|
||||||
amountsSleep = analysis.calculateActivityAmounts(getSamplesOfDay(handler, day, -12, device));
|
amountsSleep = analysis.calculateActivityAmounts(getSamplesOfDay(handler, day, -12, device));
|
||||||
|
|
||||||
long[] sleep = getTotalsSleepForActivityAmounts(amountsSleep);
|
long[] sleep = getTotalsSleepForActivityAmounts(amountsSleep);
|
||||||
Pair<Long, Long> stepsDistance = getTotalsStepsForActivityAmounts(amountsSteps);
|
|
||||||
|
long totalSteps = 0;
|
||||||
|
long totalDistance = 0;
|
||||||
|
long totalActiveCalories = 0;
|
||||||
|
long totalRestingCalories = 0;
|
||||||
|
for (ActivityAmount amount : totalAmounts.getAmounts()) {
|
||||||
|
totalSteps += amount.getTotalSteps();
|
||||||
|
totalDistance += amount.getTotalDistance();
|
||||||
|
totalActiveCalories += amount.getTotalActiveCalories();
|
||||||
|
}
|
||||||
|
totalRestingCalories = getRestingCaloriesOfDay(handler, day, device);
|
||||||
|
|
||||||
// Purposely not including awake sleep
|
// Purposely not including awake sleep
|
||||||
return new DailyTotals(stepsDistance.getLeft(), stepsDistance.getRight(), sleep);
|
return new DailyTotals(totalSteps, totalDistance, sleep, totalActiveCalories, totalRestingCalories);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long[] getTotalsSleepForActivityAmounts(ActivityAmounts activityAmounts) {
|
private static long[] getTotalsSleepForActivityAmounts(ActivityAmounts activityAmounts) {
|
||||||
@ -115,17 +139,6 @@ public class DailyTotals implements Serializable {
|
|||||||
return new long[]{totalMinutesLightSleep, totalMinutesDeepSleep, totalMinutesRemSleep, totalMinutesAwakeSleep};
|
return new long[]{totalMinutesLightSleep, totalMinutesDeepSleep, totalMinutesRemSleep, totalMinutesAwakeSleep};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Pair<Long, Long> getTotalsStepsForActivityAmounts(ActivityAmounts activityAmounts) {
|
|
||||||
long totalSteps = 0;
|
|
||||||
long totalDistance = 0;
|
|
||||||
|
|
||||||
for (ActivityAmount amount : activityAmounts.getAmounts()) {
|
|
||||||
totalSteps += amount.getTotalSteps();
|
|
||||||
totalDistance += amount.getTotalDistance();
|
|
||||||
}
|
|
||||||
return Pair.of(totalSteps, totalDistance);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<? extends ActivitySample> getSamplesOfDay(DBHandler db, Calendar day, int offsetHours, GBDevice device) {
|
private static List<? extends ActivitySample> getSamplesOfDay(DBHandler db, Calendar day, int offsetHours, GBDevice device) {
|
||||||
int startTs;
|
int startTs;
|
||||||
int endTs;
|
int endTs;
|
||||||
@ -142,10 +155,32 @@ public class DailyTotals implements Serializable {
|
|||||||
return getSamples(db, device, startTs, endTs);
|
return getSamples(db, device, startTs, endTs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int getRestingCaloriesOfDay(DBHandler db, Calendar day, GBDevice device) {
|
||||||
|
Calendar calendar = Calendar.getInstance();
|
||||||
|
day.add(Calendar.DATE, 0);
|
||||||
|
day.set(Calendar.HOUR_OF_DAY, 0);
|
||||||
|
day.set(Calendar.MINUTE, 0);
|
||||||
|
day.set(Calendar.SECOND, 0);
|
||||||
|
day.add(Calendar.HOUR, 0);
|
||||||
|
TimeSample metabolicRate = getRestingMetabolicRate(db, device);
|
||||||
|
double passedDayProportion = 1;
|
||||||
|
boolean sameDay = calendar.get(Calendar.DAY_OF_YEAR) == day.get(Calendar.DAY_OF_YEAR) &&
|
||||||
|
calendar.get(Calendar.YEAR) == day.get(Calendar.YEAR);
|
||||||
|
if (sameDay) {
|
||||||
|
passedDayProportion = (double) (calendar.getTimeInMillis() - day.getTimeInMillis()) / (24L * 60 * 60 * 1000);
|
||||||
|
}
|
||||||
|
return (int) ((double) ((GarminRestingMetabolicRateSample) metabolicRate).getRestingMetabolicRate() * passedDayProportion);
|
||||||
|
}
|
||||||
|
|
||||||
public static List<? extends ActivitySample> getSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
|
public static List<? extends ActivitySample> getSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
|
||||||
return getAllSamples(db, device, tsFrom, tsTo);
|
return getAllSamples(db, device, tsFrom, tsTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static TimeSample getRestingMetabolicRate(DBHandler db, GBDevice device) {
|
||||||
|
TimeSampleProvider<? extends RestingMetabolicRateSample> provider = device.getDeviceCoordinator().getRestingMetabolicRateProvider(device, db.getDaoSession());
|
||||||
|
return provider.getLatestSample();
|
||||||
|
}
|
||||||
|
|
||||||
protected static SampleProvider<? extends AbstractActivitySample> getProvider(DBHandler db, GBDevice device) {
|
protected static SampleProvider<? extends AbstractActivitySample> getProvider(DBHandler db, GBDevice device) {
|
||||||
DeviceCoordinator coordinator = device.getDeviceCoordinator();
|
DeviceCoordinator coordinator = device.getDeviceCoordinator();
|
||||||
return coordinator.getSampleProvider(device, db.getDaoSession());
|
return coordinator.getSampleProvider(device, db.getDaoSession());
|
||||||
|
@ -63,6 +63,36 @@ public class DashboardUtils {
|
|||||||
return totalSteps;
|
return totalSteps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int getActiveCaloriesTotal(DashboardFragment.DashboardData dashboardData) {
|
||||||
|
List<GBDevice> devices = GBApplication.app().getDeviceManager().getDevices();
|
||||||
|
int totalActiveCalories = 0;
|
||||||
|
try (DBHandler dbHandler = GBApplication.acquireDB()) {
|
||||||
|
for (GBDevice dev : devices) {
|
||||||
|
if ((dashboardData.showAllDevices || dashboardData.showDeviceList.contains(dev.getAddress())) && dev.getDeviceCoordinator().supportsActivityTracking()) {
|
||||||
|
totalActiveCalories += (int) getDailyTotals(dev, dbHandler, dashboardData.timeTo).getActiveCalories();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.warn("Could not calculate total amount of active calories: ", e);
|
||||||
|
}
|
||||||
|
return totalActiveCalories;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getRestingCaloriesTotal(DashboardFragment.DashboardData dashboardData) {
|
||||||
|
List<GBDevice> devices = GBApplication.app().getDeviceManager().getDevices();
|
||||||
|
int totalRestingCalories = 0;
|
||||||
|
try (DBHandler dbHandler = GBApplication.acquireDB()) {
|
||||||
|
for (GBDevice dev : devices) {
|
||||||
|
if ((dashboardData.showAllDevices || dashboardData.showDeviceList.contains(dev.getAddress())) && dev.getDeviceCoordinator().supportsActivityTracking()) {
|
||||||
|
totalRestingCalories += (int) getDailyTotals(dev, dbHandler, dashboardData.timeTo).getRestingCalories();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.warn("Could not calculate total amount of resting calories: ", e);
|
||||||
|
}
|
||||||
|
return totalRestingCalories;
|
||||||
|
}
|
||||||
|
|
||||||
public static float getStepsGoalFactor(DashboardFragment.DashboardData dashboardData) {
|
public static float getStepsGoalFactor(DashboardFragment.DashboardData dashboardData) {
|
||||||
ActivityUser activityUser = new ActivityUser();
|
ActivityUser activityUser = new ActivityUser();
|
||||||
float stepsGoal = activityUser.getStepsGoal();
|
float stepsGoal = activityUser.getStepsGoal();
|
||||||
@ -134,6 +164,24 @@ public class DashboardUtils {
|
|||||||
return goalFactor;
|
return goalFactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static float getActiveCaloriesGoalFactor(DashboardFragment.DashboardData dashboardData) {
|
||||||
|
ActivityUser activityUser = new ActivityUser();
|
||||||
|
int caloriesGoal = activityUser.getActiveCaloriesBurntGoal();
|
||||||
|
float goalFactor = (float) getActiveCaloriesTotal(dashboardData) / caloriesGoal;
|
||||||
|
if (goalFactor > 1) goalFactor = 1;
|
||||||
|
|
||||||
|
return goalFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float getCaloriesGoalFactor(DashboardFragment.DashboardData dashboardData) {
|
||||||
|
ActivityUser activityUser = new ActivityUser();
|
||||||
|
int caloriesGoal = activityUser.getCaloriesBurntGoal();
|
||||||
|
float goalFactor = (float) (getRestingCaloriesTotal(dashboardData) + getActiveCaloriesTotal(dashboardData)) / caloriesGoal;
|
||||||
|
if (goalFactor > 1) goalFactor = 1;
|
||||||
|
|
||||||
|
return goalFactor;
|
||||||
|
}
|
||||||
|
|
||||||
public static long getActiveMinutesTotal(DashboardFragment.DashboardData dashboardData) {
|
public static long getActiveMinutesTotal(DashboardFragment.DashboardData dashboardData) {
|
||||||
List<GBDevice> devices = GBApplication.app().getDeviceManager().getDevices();
|
List<GBDevice> devices = GBApplication.app().getDeviceManager().getDevices();
|
||||||
long totalActiveMinutes = 0;
|
long totalActiveMinutes = 0;
|
||||||
|
125
app/src/main/res/layout/fragment_calories.xml
Normal file
125
app/src/main/res/layout/fragment_calories.xml
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/date_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="15dp"
|
||||||
|
android:layout_marginBottom="20dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textSize="20sp" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="180dp"
|
||||||
|
android:layout_height="180dp"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:scaleType="fitStart"
|
||||||
|
android:id="@+id/calories_gauge" />
|
||||||
|
|
||||||
|
<GridLayout
|
||||||
|
android:id="@+id/calories_types_wrapper"
|
||||||
|
android:background="@color/gauge_line_color"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:columnCount="2"
|
||||||
|
android:layout_marginTop="15dp"
|
||||||
|
>
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/calories_active_wrapper"
|
||||||
|
style="@style/GridTile"
|
||||||
|
android:layout_marginEnd="1dp"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/calories_active"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/stats_empty_value"
|
||||||
|
android:textSize="20sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/active"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/calories_active_goal_wrapper"
|
||||||
|
style="@style/GridTile"
|
||||||
|
android:layout_marginStart="1dp"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/calories_active_goal"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/stats_empty_value"
|
||||||
|
android:textSize="20sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/active_goal"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/calories_resting_wrapper"
|
||||||
|
style="@style/GridTile"
|
||||||
|
android:layout_marginEnd="1dp"
|
||||||
|
>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/calories_resting"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/stats_empty_value"
|
||||||
|
android:textSize="20sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/hr_resting"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/calories_total_goal_wrapper"
|
||||||
|
style="@style/GridTile"
|
||||||
|
android:layout_marginStart="1dp"
|
||||||
|
>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/calories_total_goal"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/stats_empty_value"
|
||||||
|
android:textSize="20sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/total_goal"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</GridLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/steps_chart_title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="left"
|
||||||
|
android:paddingLeft="20dip"
|
||||||
|
android:text=""
|
||||||
|
android:textSize="20sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
@ -3113,6 +3113,7 @@
|
|||||||
<item>@string/pref_header_spo2</item>
|
<item>@string/pref_header_spo2</item>
|
||||||
<item>@string/menuitem_temperature</item>
|
<item>@string/menuitem_temperature</item>
|
||||||
<item>@string/menuitem_weight</item>
|
<item>@string/menuitem_weight</item>
|
||||||
|
<item>@string/menuitem_calories</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="pref_charts_tabs_values">
|
<string-array name="pref_charts_tabs_values">
|
||||||
@ -3131,6 +3132,7 @@
|
|||||||
<item>@string/p_spo2</item>
|
<item>@string/p_spo2</item>
|
||||||
<item>@string/p_temperature</item>
|
<item>@string/p_temperature</item>
|
||||||
<item>@string/p_weight</item>
|
<item>@string/p_weight</item>
|
||||||
|
<item>@string/p_calories</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="pref_charts_tabs_items_default">
|
<string-array name="pref_charts_tabs_items_default">
|
||||||
@ -3150,6 +3152,7 @@
|
|||||||
<item>@string/p_spo2</item>
|
<item>@string/p_spo2</item>
|
||||||
<item>@string/p_temperature</item>
|
<item>@string/p_temperature</item>
|
||||||
<item>@string/p_weight</item>
|
<item>@string/p_weight</item>
|
||||||
|
<item>@string/p_calories</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
|
||||||
@ -4250,6 +4253,9 @@
|
|||||||
<item>@string/menuitem_vo2_max</item>
|
<item>@string/menuitem_vo2_max</item>
|
||||||
<item>@string/vo2max_running</item>
|
<item>@string/vo2max_running</item>
|
||||||
<item>@string/vo2max_cycling</item>
|
<item>@string/vo2max_cycling</item>
|
||||||
|
<item>@string/menuitem_calories_goal</item>
|
||||||
|
<item>@string/menuitem_calories_active_goal</item>
|
||||||
|
<item>@string/menuitem_calories_segmented</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="pref_dashboard_widgets_order_values">
|
<string-array name="pref_dashboard_widgets_order_values">
|
||||||
@ -4267,6 +4273,9 @@
|
|||||||
<item>vo2max</item>
|
<item>vo2max</item>
|
||||||
<item>vo2max_running</item>
|
<item>vo2max_running</item>
|
||||||
<item>vo2max_cycling</item>
|
<item>vo2max_cycling</item>
|
||||||
|
<item>calories</item>
|
||||||
|
<item>calories_active</item>
|
||||||
|
<item>calories_segmented</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="pref_dashboard_widgets_order_default">
|
<string-array name="pref_dashboard_widgets_order_default">
|
||||||
@ -4280,5 +4289,8 @@
|
|||||||
<item>stress_segmented</item>
|
<item>stress_segmented</item>
|
||||||
<item>hrv</item>
|
<item>hrv</item>
|
||||||
<item>vo2max</item>
|
<item>vo2max</item>
|
||||||
|
<item>calories</item>
|
||||||
|
<item>calories_active</item>
|
||||||
|
<item>calories_segmented</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -61,6 +61,8 @@
|
|||||||
<color name="body_energy_level_color" type="color">#5ac234</color>
|
<color name="body_energy_level_color" type="color">#5ac234</color>
|
||||||
<color name="body_energy_lost_color" type="color">#ff6c43</color>
|
<color name="body_energy_lost_color" type="color">#ff6c43</color>
|
||||||
<color name="steps_color" type="color">#00c9bf</color>
|
<color name="steps_color" type="color">#00c9bf</color>
|
||||||
|
<color name="calories_color" type="color">#fa4502</color>
|
||||||
|
<color name="calories_resting_color" type="color">#1f49f2</color>
|
||||||
|
|
||||||
<color name="value_line_color" type="color">#858585</color>
|
<color name="value_line_color" type="color">#858585</color>
|
||||||
<color name="row_separator_light" type="color">#ffe2e2e5</color>
|
<color name="row_separator_light" type="color">#ffe2e2e5</color>
|
||||||
|
@ -926,6 +926,7 @@
|
|||||||
<string name="updatefirmwareoperation_failed_low_mtu">Current MTU of %1$d is too low, please enable high MTU in the device settings and disconnect/re-connect the device.</string>
|
<string name="updatefirmwareoperation_failed_low_mtu">Current MTU of %1$d is too low, please enable high MTU in the device settings and disconnect/re-connect the device.</string>
|
||||||
<string name="chart_steps">Steps</string>
|
<string name="chart_steps">Steps</string>
|
||||||
<string name="calories">Calories</string>
|
<string name="calories">Calories</string>
|
||||||
|
<string name="active_calories">Active calories</string>
|
||||||
<string name="distance">Distance</string>
|
<string name="distance">Distance</string>
|
||||||
<string name="clock">Clock</string>
|
<string name="clock">Clock</string>
|
||||||
<string name="heart_rate">Heart rate</string>
|
<string name="heart_rate">Heart rate</string>
|
||||||
@ -933,6 +934,11 @@
|
|||||||
<string name="hr_maximum">Maximum</string>
|
<string name="hr_maximum">Maximum</string>
|
||||||
<string name="hr_minimum">Minimum</string>
|
<string name="hr_minimum">Minimum</string>
|
||||||
<string name="hr_average">Average</string>
|
<string name="hr_average">Average</string>
|
||||||
|
<string name="active">Active</string>
|
||||||
|
<string name="active_goal">Active goal</string>
|
||||||
|
<string name="total_goal">Total goal</string>
|
||||||
|
<string name="total_burnt">Total burnt</string>
|
||||||
|
<string name="goal">Goal</string>
|
||||||
<string name="blood_pressure">Blood pressure</string>
|
<string name="blood_pressure">Blood pressure</string>
|
||||||
<string name="getting_heart_rate">Measuring</string>
|
<string name="getting_heart_rate">Measuring</string>
|
||||||
<string name="heart_rate_result">Measurement results</string>
|
<string name="heart_rate_result">Measurement results</string>
|
||||||
@ -1228,6 +1234,7 @@
|
|||||||
<string name="activity_prefs_activetime_minutes">Daily target: active time in minutes</string>
|
<string name="activity_prefs_activetime_minutes">Daily target: active time in minutes</string>
|
||||||
<string name="activity_prefs_goal_standing_time_minutes">Daily target: standing time in minutes</string>
|
<string name="activity_prefs_goal_standing_time_minutes">Daily target: standing time in minutes</string>
|
||||||
<string name="activity_prefs_goal_fat_burn_time_minutes">Daily target: fat burn time in minutes</string>
|
<string name="activity_prefs_goal_fat_burn_time_minutes">Daily target: fat burn time in minutes</string>
|
||||||
|
<string name="activity_prefs_goal_active_calories_burnt">Daily target: active calories burnt</string>
|
||||||
<string name="active_time">Active time</string>
|
<string name="active_time">Active time</string>
|
||||||
<string name="standing_time">Standing time</string>
|
<string name="standing_time">Standing time</string>
|
||||||
<string name="pref_title_pebble_health_store_raw">Store raw record in the database</string>
|
<string name="pref_title_pebble_health_store_raw">Store raw record in the database</string>
|
||||||
@ -1931,6 +1938,9 @@
|
|||||||
<string name="menuitem_stress_simple">Stress (simple)</string>
|
<string name="menuitem_stress_simple">Stress (simple)</string>
|
||||||
<string name="menuitem_stress_segmented">Stress (segmented)</string>
|
<string name="menuitem_stress_segmented">Stress (segmented)</string>
|
||||||
<string name="menuitem_stress_breakdown">Stress (breakdown)</string>
|
<string name="menuitem_stress_breakdown">Stress (breakdown)</string>
|
||||||
|
<string name="menuitem_calories_segmented">Calories(segmented)</string>
|
||||||
|
<string name="menuitem_calories_active_goal">Calories goal(active)</string>
|
||||||
|
<string name="menuitem_calories_goal">Calories goal(total)</string>
|
||||||
<string name="menuitem_pai">PAI</string>
|
<string name="menuitem_pai">PAI</string>
|
||||||
<string name="menuitem_hr">Heart Rate</string>
|
<string name="menuitem_hr">Heart Rate</string>
|
||||||
<string name="menuitem_spo2">SpO2</string>
|
<string name="menuitem_spo2">SpO2</string>
|
||||||
@ -1951,6 +1961,7 @@
|
|||||||
<string name="menuitem_widgets">Widgets</string>
|
<string name="menuitem_widgets">Widgets</string>
|
||||||
<string name="menuitem_temperature">Temperature</string>
|
<string name="menuitem_temperature">Temperature</string>
|
||||||
<string name="menuitem_weight">Weight</string>
|
<string name="menuitem_weight">Weight</string>
|
||||||
|
<string name="menuitem_calories">Calories</string>
|
||||||
<string name="menuitem_barometer">Barometer</string>
|
<string name="menuitem_barometer">Barometer</string>
|
||||||
<string name="menuitem_flashlight">Flashlight</string>
|
<string name="menuitem_flashlight">Flashlight</string>
|
||||||
<string name='menuitem_email'>E-mail</string>
|
<string name='menuitem_email'>E-mail</string>
|
||||||
|
@ -93,6 +93,15 @@
|
|||||||
android:title="@string/activity_prefs_calories_burnt"
|
android:title="@string/activity_prefs_calories_burnt"
|
||||||
app:useSimpleSummaryProvider="true" />
|
app:useSimpleSummaryProvider="true" />
|
||||||
|
|
||||||
|
<EditTextPreference
|
||||||
|
app:iconSpaceReserved="false"
|
||||||
|
android:defaultValue="350"
|
||||||
|
android:inputType="number"
|
||||||
|
android:key="activity_user_goal_active_calories_burnt"
|
||||||
|
android:maxLength="3"
|
||||||
|
android:title="@string/activity_prefs_goal_active_calories_burnt"
|
||||||
|
app:useSimpleSummaryProvider="true" />
|
||||||
|
|
||||||
<EditTextPreference
|
<EditTextPreference
|
||||||
app:iconSpaceReserved="false"
|
app:iconSpaceReserved="false"
|
||||||
android:defaultValue="5000"
|
android:defaultValue="5000"
|
||||||
|
Loading…
Reference in New Issue
Block a user