/* Copyright (C) 2024 José Rebelo 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 . */ package nodomain.freeyourgadget.gadgetbridge.activities.dashboard; import android.os.Bundle; import android.text.Spannable; import android.text.SpannableString; import android.text.TextUtils; import android.text.format.DateUtils; import android.text.style.RelativeSizeSpan; import androidx.core.content.ContextCompat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Serializable; import java.util.List; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.DashboardFragment; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.BodyEnergySample; public class DashboardBodyEnergyWidget extends AbstractGaugeWidget { private static final Logger LOG = LoggerFactory.getLogger(DashboardBodyEnergyWidget.class); public DashboardBodyEnergyWidget() { super(R.string.body_energy, "bodyenergy"); } public static DashboardBodyEnergyWidget newInstance(final DashboardFragment.DashboardData dashboardData) { final DashboardBodyEnergyWidget fragment = new DashboardBodyEnergyWidget(); 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().supportsBodyEnergy(); } @Override protected void populateData(final DashboardFragment.DashboardData dashboardData) { final List devices = getSupportedDevices(dashboardData); final boolean isToday = DateUtils.isToday(dashboardData.timeTo * 1000L); final BodyEnergyData data = new BodyEnergyData(); data.isToday = isToday; if (isToday) { // Latest stress sample for today BodyEnergySample sample = null; try (DBHandler dbHandler = GBApplication.acquireDB()) { for (GBDevice dev : devices) { final BodyEnergySample latestSample = dev.getDeviceCoordinator().getBodyEnergySampleProvider(dev, dbHandler.getDaoSession()) .getLatestSample(); if (latestSample != null && (sample == null || latestSample.getTimestamp() > sample.getTimestamp())) { sample = latestSample; } } if (sample != null) { data.value = sample.getEnergy(); } } catch (final Exception e) { LOG.error("Could not get body energy for today", e); } } else { // Gain / loss for the period try (DBHandler dbHandler = GBApplication.acquireDB()) { for (GBDevice dev : devices) { if ((dashboardData.showAllDevices || dashboardData.showDeviceList.contains(dev.getAddress())) && dev.getDeviceCoordinator().supportsBodyEnergy()) { final List samples = dev.getDeviceCoordinator() .getBodyEnergySampleProvider(dev, dbHandler.getDaoSession()) .getAllSamples(dashboardData.timeFrom * 1000L, dashboardData.timeTo * 1000L); if (samples.size() > 1) { int gained = 0; int lost = 0; for (int i = 1; i < samples.size(); i++) { final BodyEnergySample s1 = samples.get(i - 1); final BodyEnergySample s2 = samples.get(i); if (s2.getEnergy() > s1.getEnergy()) { gained += s2.getEnergy() - s1.getEnergy(); } else { lost += s1.getEnergy() - s2.getEnergy(); } } data.gained = gained; data.lost = lost; } } } } catch (final Exception e) { LOG.error("Could not calculate average stress", e); } } dashboardData.put("bodyenergy", data); } @Override protected void draw(final DashboardFragment.DashboardData dashboardData) { final BodyEnergyData bodyEnergyData = (BodyEnergyData) dashboardData.get("bodyenergy"); if (bodyEnergyData == null) { drawSimpleGauge(0, -1); return; } final int colorEnergy = ContextCompat.getColor(GBApplication.getContext(), R.color.body_energy_level_color); if (bodyEnergyData.isToday) { if (bodyEnergyData.value < 0) { drawSimpleGauge(0, -1); return; } setText(String.valueOf(bodyEnergyData.value)); drawSimpleGauge( colorEnergy, bodyEnergyData.value / 100f ); } else { if (bodyEnergyData.gained < 0 || bodyEnergyData.lost < 0) { drawSimpleGauge(0, -1); return; } final int diff = bodyEnergyData.gained - bodyEnergyData.lost; final SpannableString spanGain = new SpannableString("↑" + bodyEnergyData.gained); final SpannableString spanLost = new SpannableString("↓" + bodyEnergyData.lost); spanGain.setSpan(new RelativeSizeSpan(0.65f), 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); spanLost.setSpan(new RelativeSizeSpan(0.65f), 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); setText(TextUtils.concat(spanGain, " ", spanLost)); drawSimpleGauge( colorEnergy, Math.abs(diff) / 100f ); final int[] colors = { colorEnergy, ContextCompat.getColor(GBApplication.getContext(), R.color.body_energy_lost_color) }; final float[] segments = { bodyEnergyData.gained / (float) (bodyEnergyData.gained + bodyEnergyData.lost), bodyEnergyData.lost / (float) (bodyEnergyData.gained + bodyEnergyData.lost), }; drawSegmentedGauge( colors, segments, -1, false, true ); } } private static class BodyEnergyData implements Serializable { private int value = -1; private int gained = -1; private int lost = -1; private boolean isToday; } }