1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-09-01 03:55:47 +02:00

Garmin: Awake time

This commit is contained in:
a0z 2024-08-25 23:12:00 +02:00 committed by José Rebelo
parent b6e2ce1d9c
commit 21de228204
11 changed files with 272 additions and 116 deletions

View File

@ -62,6 +62,11 @@ public abstract class AbstractActivityChartFragment<D extends ChartsData> extend
return coordinator != null && coordinator.supportsRemSleep();
}
public boolean supportsAwakeSleep(GBDevice device) {
DeviceCoordinator coordinator = device.getDeviceCoordinator();
return coordinator != null && coordinator.supportsAwakeSleep();
}
protected static final class ActivityConfig {
public final ActivityKind type;
public final String label;
@ -196,6 +201,7 @@ public abstract class AbstractActivityChartFragment<D extends ChartsData> extend
List<Entry> deepSleepEntries = new ArrayList<>(numEntries);
List<Entry> lightSleepEntries = new ArrayList<>(numEntries);
List<Entry> remSleepEntries = new ArrayList<>(numEntries);
List<Entry> awakeSleepEntries = new ArrayList<>(numEntries);
List<Entry> notWornEntries = new ArrayList<>(numEntries);
boolean hr = supportsHeartrate(gbDevice);
List<Entry> heartrateEntries = hr ? new ArrayList<Entry>(numEntries) : null;
@ -233,6 +239,7 @@ public abstract class AbstractActivityChartFragment<D extends ChartsData> extend
remSleepEntries.add(createLineEntry(0, ts));
notWornEntries.add(createLineEntry(0, ts));
activityEntries.add(createLineEntry(0, ts));
awakeSleepEntries.add(createLineEntry(0, ts));
}
deepSleepEntries.add(createLineEntry(value + Y_VALUE_DEEP_SLEEP, ts));
break;
@ -244,6 +251,7 @@ public abstract class AbstractActivityChartFragment<D extends ChartsData> extend
remSleepEntries.add(createLineEntry(0, ts));
notWornEntries.add(createLineEntry(0, ts));
activityEntries.add(createLineEntry(0, ts));
awakeSleepEntries.add(createLineEntry(0, ts));
}
lightSleepEntries.add(createLineEntry(value, ts));
break;
@ -255,9 +263,22 @@ public abstract class AbstractActivityChartFragment<D extends ChartsData> extend
deepSleepEntries.add(createLineEntry(0, ts));
notWornEntries.add(createLineEntry(0, ts));
activityEntries.add(createLineEntry(0, ts));
awakeSleepEntries.add(createLineEntry(0, ts));
}
remSleepEntries.add(createLineEntry(value, ts));
break;
case AWAKE_SLEEP:
if (last_type != type) {
awakeSleepEntries.add(createLineEntry(0, ts - 1));
lightSleepEntries.add(createLineEntry(0, ts));
deepSleepEntries.add(createLineEntry(0, ts));
notWornEntries.add(createLineEntry(0, ts));
activityEntries.add(createLineEntry(0, ts));
remSleepEntries.add(createLineEntry(0, ts));
}
awakeSleepEntries.add(createLineEntry(value, ts));
break;
case NOT_WORN:
if (last_type != type) {
notWornEntries.add(createLineEntry(0, ts - 1));
@ -266,6 +287,7 @@ public abstract class AbstractActivityChartFragment<D extends ChartsData> extend
deepSleepEntries.add(createLineEntry(0, ts));
remSleepEntries.add(createLineEntry(0, ts));
activityEntries.add(createLineEntry(0, ts));
awakeSleepEntries.add(createLineEntry(0, ts));
}
notWornEntries.add(createLineEntry(Y_VALUE_DEEP_SLEEP, ts)); //a small value, just to show something on the graphs
break;
@ -335,6 +357,10 @@ public abstract class AbstractActivityChartFragment<D extends ChartsData> extend
LineDataSet remSleepSet = createDataSet(remSleepEntries, akRemSleep.color, "REM Sleep");
lineDataSets.add(remSleepSet);
}
if (supportsAwakeSleep(gbDevice)) {
LineDataSet awakeSleepSet = createDataSet(awakeSleepEntries, akAwakeSleep.color, "Awake Sleep");
lineDataSets.add(awakeSleepSet);
}
LineDataSet notWornSet = createDataSet(notWornEntries, akNotWorn.color, "Not worn");
lineDataSets.add(notWornSet);

View File

@ -19,6 +19,7 @@ package nodomain.freeyourgadget.gadgetbridge.activities.charts;
import android.app.Activity;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@ -222,6 +223,12 @@ public abstract class AbstractWeekChartFragment extends AbstractActivityChartFra
View rootView = inflater.inflate(R.layout.fragment_weeksteps_chart, container, false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
rootView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
getChartsHost().enableSwipeRefresh(scrollY == 0);
});
}
final int goal = getGoal();
if (goal >= 0) {
mTargetValue = goal;

View File

@ -0,0 +1,53 @@
package nodomain.freeyourgadget.gadgetbridge.activities.charts;
import com.github.mikephil.charting.data.BarEntry;
import com.github.mikephil.charting.formatter.StackedValueFormatter;
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
public class BarChartStackedTimeValueFormatter extends StackedValueFormatter {
private float[] processedValues;
private BarEntry lastEntry;
private int lastNonZeroIndex;
private int index = 0;
public BarChartStackedTimeValueFormatter(boolean drawWholeStack, String suffix, int decimals) {
super(drawWholeStack, suffix, decimals);
}
private int getLastNonZeroIndex(float[] array) {
int last = 0;
int i = 0;
for(float v: array) {
last = v == 0 ? last : i;
i++;
}
return last;
}
@Override
public String getBarStackedLabel(float value, BarEntry entry) {
if (lastEntry != entry) {
processedValues = entry.getYVals();
lastEntry = entry;
lastNonZeroIndex = getLastNonZeroIndex(processedValues);
index = 0;
}
if (index == lastNonZeroIndex) {
return getFormattedValue(processedValues);
}
index++;
return "";
}
String getFormattedValue(float[] values) {
float sum = 0;
for (int i = 0; i < values.length - 1; i++) {
sum += values[i];
}
return DateTimeUtils.minutesToHHMM((int) sum);
}
}

View File

@ -135,7 +135,7 @@ public class SleepChartFragment extends AbstractActivityChartFragment<SleepChart
final long deepSleepDuration = calculateDeepSleepDuration(sleepSessions);
final long remSleepDuration = calculateRemSleepDuration(sleepSessions);
final long awakeSleepDuration = calculateAwakeSleepDuration(sleepSessions);
final long totalSeconds = lightSleepDuration + deepSleepDuration + remSleepDuration + awakeSleepDuration;
final long totalSeconds = lightSleepDuration + deepSleepDuration + remSleepDuration;
final List<PieEntry> entries = new ArrayList<>();
final List<Integer> colors = new ArrayList<>();
@ -151,9 +151,10 @@ public class SleepChartFragment extends AbstractActivityChartFragment<SleepChart
colors.add(getColorFor(ActivityKind.REM_SLEEP));
}
entries.add(new PieEntry(awakeSleepDuration, getActivity().getString(R.string.abstract_chart_fragment_kind_awake_sleep)));
colors.add(getColorFor(ActivityKind.AWAKE_SLEEP));
if (supportsAwakeSleep(mGBDevice)) {
entries.add(new PieEntry(awakeSleepDuration, getActivity().getString(R.string.abstract_chart_fragment_kind_awake_sleep)));
colors.add(getColorFor(ActivityKind.AWAKE_SLEEP));
}
} else {
entries.add(new PieEntry(1));
colors.add(getResources().getColor(R.color.gauge_line_color));
@ -235,6 +236,9 @@ public class SleepChartFragment extends AbstractActivityChartFragment<SleepChart
if (!supportsRemSleep(getChartsHost().getDevice())) {
remSleepTimeTextWrapper.setVisibility(View.GONE);
}
if (!supportsAwakeSleep(getChartsHost().getDevice())) {
awakeSleepTimeTextWrapper.setVisibility(View.GONE);
}
mSleepAmountChart.setCenterTextSize(18f);
mSleepAmountChart.setHoleColor(getContext().getResources().getColor(R.color.transparent));
mSleepAmountChart.setData(pieData.getPieData());
@ -455,24 +459,31 @@ public class SleepChartFragment extends AbstractActivityChartFragment<SleepChart
@Override
protected void setupLegend(Chart<?> chart) {
List<LegendEntry> legendEntries = new ArrayList<>(3);
List<LegendEntry> legendEntries = new ArrayList<>(4);
LegendEntry lightSleepEntry = new LegendEntry();
lightSleepEntry.label = akLightSleep.label;
lightSleepEntry.label = getActivity().getString(R.string.sleep_colored_stats_light);
lightSleepEntry.formColor = akLightSleep.color;
legendEntries.add(lightSleepEntry);
LegendEntry deepSleepEntry = new LegendEntry();
deepSleepEntry.label = akDeepSleep.label;
deepSleepEntry.label = getActivity().getString(R.string.sleep_colored_stats_deep);
deepSleepEntry.formColor = akDeepSleep.color;
legendEntries.add(deepSleepEntry);
if (supportsRemSleep(getChartsHost().getDevice())) {
LegendEntry remSleepEntry = new LegendEntry();
remSleepEntry.label = akRemSleep.label;
remSleepEntry.label = getActivity().getString(R.string.sleep_colored_stats_rem);
remSleepEntry.formColor = akRemSleep.color;
legendEntries.add(remSleepEntry);
}
if (supportsAwakeSleep(getChartsHost().getDevice())) {
LegendEntry awakeSleepEntry = new LegendEntry();
awakeSleepEntry.label = getActivity().getString(R.string.abstract_chart_fragment_kind_awake_sleep);
awakeSleepEntry.formColor = akAwakeSleep.color;
legendEntries.add(awakeSleepEntry);
}
if (supportsHeartrate(getChartsHost().getDevice())) {
LegendEntry hrEntry = new LegendEntry();
hrEntry.label = HEARTRATE_LABEL;

View File

@ -94,7 +94,11 @@ public class WeekSleepChartFragment extends AbstractWeekChartFragment {
deepWeeklyTotal += (long) totalAmounts[0];
lightWeeklyTotal += (long) totalAmounts[1];
remWeeklyTotal += (long) totalAmounts[2];
awakeWeeklyTotal += (long) totalAmounts[3];
if (supportsAwakeSleep(getChartsHost().getDevice())) {
awakeWeeklyTotal += (long) totalAmounts[3];
}
day.add(Calendar.DATE, 1);
}
@ -128,21 +132,25 @@ public class WeekSleepChartFragment extends AbstractWeekChartFragment {
return rootView;
}
@Override
protected void updateChartsnUIThread(MyChartsData mcd) {
setupLegend(mWeekChart);
protected void setupWeekChart() {
super.setupWeekChart();
if (TOTAL_DAYS > 7) {
mWeekChart.setRenderer(new AngledLabelsChartRenderer(mWeekChart, mWeekChart.getAnimator(), mWeekChart.getViewPortHandler()));
} else {
mWeekChart.setScaleEnabled(false);
mWeekChart.setTouchEnabled(false);
}
}
@Override
protected void updateChartsnUIThread(MyChartsData mcd) {
setupLegend(mWeekChart);
mWeekChart.setData(null); // workaround for https://github.com/PhilJay/MPAndroidChart/issues/2317
mWeekChart.setData(mcd.getWeekBeforeData().getData());
mWeekChart.getXAxis().setValueFormatter(mcd.getWeekBeforeData().getXValueFormatter());
mWeekChart.getBarData().setValueTextSize(10f);
mWeekChart.getBarData().setValueFormatter(new BarChartStackedTimeValueFormatter(false, "", 0));
if (TOTAL_DAYS_FOR_AVERAGE > 0) {
float avgDeep = Math.abs(this.mySleepWeeklyData.getTotalDeep() / TOTAL_DAYS_FOR_AVERAGE);
@ -164,6 +172,10 @@ public class WeekSleepChartFragment extends AbstractWeekChartFragment {
remSleepTimeTextWrapper.setVisibility(View.GONE);
}
if (!supportsAwakeSleep(getChartsHost().getDevice())) {
awakeSleepTimeTextWrapper.setVisibility(View.GONE);
}
Date to = new Date((long) this.getTSEnd() * 1000);
Date from = DateUtils.addDays(to,-(TOTAL_DAYS - 1));
String toFormattedDate = new SimpleDateFormat("E, MMM dd").format(to);
@ -260,7 +272,13 @@ public class WeekSleepChartFragment extends AbstractWeekChartFragment {
int totalMinutesLightSleep = (int) (totalSecondsLightSleep / 60);
int totalMinutesRemSleep = (int) (totalSecondsRemSleep / 60);
int totalMinutesAwakeSleep = (int) (totalSecondsAwakeSleep / 60);
return new float[]{totalMinutesDeepSleep, totalMinutesLightSleep, totalMinutesRemSleep, totalMinutesAwakeSleep};
float[] activityAmountsTotals = {totalMinutesDeepSleep, totalMinutesLightSleep, totalMinutesRemSleep};
if (supportsAwakeSleep(getChartsHost().getDevice())) {
activityAmountsTotals = ArrayUtils.add(activityAmountsTotals, totalMinutesAwakeSleep);
}
return activityAmountsTotals;
}
@Override
@ -277,6 +295,9 @@ public class WeekSleepChartFragment extends AbstractWeekChartFragment {
if (supportsRemSleep(getChartsHost().getDevice())) {
labels = ArrayUtils.add(labels, getString(R.string.abstract_chart_fragment_kind_rem_sleep));
}
if (supportsAwakeSleep(getChartsHost().getDevice())) {
labels = ArrayUtils.add(labels, getString(R.string.abstract_chart_fragment_kind_awake_sleep));
}
return labels;
}
@ -316,6 +337,9 @@ public class WeekSleepChartFragment extends AbstractWeekChartFragment {
if (supportsRemSleep(getChartsHost().getDevice())) {
colors = ArrayUtils.add(colors, akRemSleep.color);
}
if (supportsAwakeSleep(getChartsHost().getDevice())) {
colors = ArrayUtils.add(colors, akAwakeSleep.color);
}
return colors;
}
@ -324,22 +348,29 @@ public class WeekSleepChartFragment extends AbstractWeekChartFragment {
List<LegendEntry> legendEntries = new ArrayList<>(2);
LegendEntry lightSleepEntry = new LegendEntry();
lightSleepEntry.label = akLightSleep.label;
lightSleepEntry.label = getActivity().getString(R.string.sleep_colored_stats_light);
lightSleepEntry.formColor = akLightSleep.color;
legendEntries.add(lightSleepEntry);
LegendEntry deepSleepEntry = new LegendEntry();
deepSleepEntry.label = akDeepSleep.label;
deepSleepEntry.label = getActivity().getString(R.string.sleep_colored_stats_deep);
deepSleepEntry.formColor = akDeepSleep.color;
legendEntries.add(deepSleepEntry);
if (supportsRemSleep(getChartsHost().getDevice())) {
LegendEntry remSleepEntry = new LegendEntry();
remSleepEntry.label = akRemSleep.label;
remSleepEntry.label = getActivity().getString(R.string.sleep_colored_stats_rem);
remSleepEntry.formColor = akRemSleep.color;
legendEntries.add(remSleepEntry);
}
if (supportsAwakeSleep(getChartsHost().getDevice())) {
LegendEntry awakeSleepEntry = new LegendEntry();
awakeSleepEntry.label = getActivity().getString(R.string.abstract_chart_fragment_kind_awake_sleep);
awakeSleepEntry.formColor = akAwakeSleep.color;
legendEntries.add(awakeSleepEntry);
}
chart.getLegend().setCustom(legendEntries);
chart.getLegend().setTextColor(LEGEND_TEXT_COLOR);
chart.getLegend().setWordWrapEnabled(true);

View File

@ -115,7 +115,7 @@ public class DashboardTodayWidget extends AbstractDashboardWidget {
l_deep_sleep.setSpan(new ForegroundColorSpan(color_deep_sleep), 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
SpannableString l_light_sleep = new SpannableString("" + getString(R.string.activity_type_light_sleep));
l_light_sleep.setSpan(new ForegroundColorSpan(color_light_sleep), 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
SpannableString l_rem_sleep = new SpannableString("" + getString(R.string.abstract_chart_fragment_kind_rem_sleep));
SpannableString l_rem_sleep = new SpannableString("" + getString(R.string.activity_type_rem_sleep));
l_rem_sleep.setSpan(new ForegroundColorSpan(color_rem_sleep), 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
SpannableStringBuilder legendBuilder = new SpannableStringBuilder();
legend.setText(legendBuilder.append(l_not_worn).append(" ").append(l_worn).append("\n").append(l_activity).append(" ").append(l_exercise).append("\n").append(l_light_sleep).append(" ").append(l_deep_sleep).append(" ").append(l_rem_sleep));

View File

@ -628,6 +628,11 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator {
return false;
}
@Override
public boolean supportsAwakeSleep() {
return false;
}
@Override
public boolean supportsWeather() {
return false;

View File

@ -550,6 +550,11 @@ public interface DeviceCoordinator {
*/
boolean supportsRemSleep();
/**
* Indicates whether the device supports Awake sleep tracking.
*/
boolean supportsAwakeSleep();
/**
* Indicates whether the device supports current weather and/or weather
* forecast display.

View File

@ -230,6 +230,11 @@ public abstract class GarminCoordinator extends AbstractBLEDeviceCoordinator {
return true;
}
@Override
public boolean supportsAwakeSleep() {
return true;
}
@Override
public boolean supportsFindDevice() {
return true;

View File

@ -193,104 +193,6 @@
</TableRow>
</TableLayout>
<View
android:layout_width="fill_parent"
android:layout_height="1dp"
android:background="@color/secondarytext" />
<GridLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_columnWeight="1"
android:gravity="center"
android:orientation="vertical"
android:paddingHorizontal="5dip">
<TextView
android:id="@+id/sleep_hr_lowest"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="20dip"
android:gravity="center"
android:text="@string/stats_empty_value"
android:textSize="20sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:text="@string/stats_lowest_hr"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_columnWeight="1"
android:gravity="center"
android:orientation="vertical"
android:paddingHorizontal="5dip">
<TextView
android:id="@+id/sleep_hr_highest"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="20dip"
android:gravity="center"
android:text="@string/stats_empty_value"
android:textSize="20sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:text="@string/stats_highest_hr"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_columnWeight="1"
android:gravity="center"
android:orientation="vertical"
android:paddingHorizontal="5dip">
<TextView
android:id="@+id/sleep_movement_intensity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="20dip"
android:gravity="center"
android:text="@string/stats_empty_value"
android:textSize="20sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:text="@string/movement_intensity"
android:textSize="12sp" />
</LinearLayout>
</GridLayout>
<View
android:layout_width="fill_parent"
android:layout_height="1dp"
android:layout_marginTop="25dp"
android:layout_marginBottom="25dp"
android:background="@color/secondarytext" />
<com.github.mikephil.charting.charts.LineChart
android:id="@+id/sleepchart"
android:layout_width="fill_parent"
@ -298,5 +200,115 @@
android:layout_marginBottom="25dp"
android:layout_weight="4" />
<TableLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="30dp"
android:layout_weight="3"
android:shrinkColumns="*"
android:stretchColumns="*">
<TableRow
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:weightSum="2">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:paddingLeft="20dip"
android:paddingTop="20dip"
android:paddingRight="20dip">
<View
android:layout_width="fill_parent"
android:layout_height="5px"
android:background="@color/value_line_color" />
<TextView
android:id="@+id/sleep_hr_lowest"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:layout_marginTop="20dip"
android:text="0"
android:textSize="20sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:text="@string/stats_lowest_hr"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:paddingLeft="20dip"
android:paddingTop="20dip"
android:paddingRight="20dip">
<View
android:layout_width="fill_parent"
android:layout_height="5px"
android:background="@color/value_line_color" />
<TextView
android:id="@+id/sleep_hr_highest"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:layout_marginTop="20dip"
android:text="0"
android:textSize="20sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:text="@string/stats_highest_hr"
android:textSize="12sp" />
</LinearLayout>
</TableRow>
<TableRow
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:weightSum="2">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:paddingLeft="20dip"
android:paddingTop="20dip"
android:paddingRight="20dip">
<View
android:layout_width="fill_parent"
android:layout_height="5px"
android:background="@color/value_line_color" />
<TextView
android:id="@+id/sleep_movement_intensity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:layout_marginTop="20dip"
android:text="0"
android:textSize="20sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:text="@string/movement_intensity"
android:textSize="12sp" />
</LinearLayout>
</TableRow>
</TableLayout>
</LinearLayout>
</ScrollView>

View File

@ -1310,6 +1310,7 @@
<string name="activity_type_not_measured">Not measured</string>
<string name="activity_type_activity">Activity</string>
<string name="activity_type_light_sleep">Light sleep</string>
<string name="activity_type_rem_sleep">REM sleep</string>
<string name="activity_type_deep_sleep">Deep sleep</string>
<string name="activity_type_not_worn">Device not worn</string>
<string name="activity_type_running">Running</string>