diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 795931d76..609082ebd 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -514,7 +514,7 @@
android:name=".devices.lenovo.LenovoWatchCalibrationActivity"
android:label="@string/title_activity_LenovoWatch_calibration" />
{
private static final Logger LOG = LoggerFactory.getLogger(ActivitySummariesChartFragment.class);
private LineChart mChart;
@@ -139,7 +139,7 @@ public class ActivitySummariesChartFragment extends AbstractChartFragment {
}
@Override
- protected void setupLegend(Chart chart) {
+ protected void setupLegend(Chart> chart) {
List legendEntries = new ArrayList<>(5);
LegendEntry activityEntry = new LegendEntry();
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractActivityChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractActivityChartFragment.java
new file mode 100644
index 000000000..c0cf26e8b
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractActivityChartFragment.java
@@ -0,0 +1,475 @@
+/* Copyright (C) 2015-2020 0nse, Andreas Shimokawa, Carsten Pfeiffer,
+ Daniele Gobbetti, Dikay900, Pavel Elagin, vanous, walkjivefly
+
+ 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.charts;
+
+import android.util.TypedValue;
+
+import androidx.core.content.ContextCompat;
+
+import com.github.mikephil.charting.components.YAxis;
+import com.github.mikephil.charting.data.Entry;
+import com.github.mikephil.charting.data.LineData;
+import com.github.mikephil.charting.data.LineDataSet;
+import com.github.mikephil.charting.formatter.ValueFormatter;
+import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.List;
+
+import nodomain.freeyourgadget.gadgetbridge.GBApplication;
+import nodomain.freeyourgadget.gadgetbridge.R;
+import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils;
+import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
+import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
+import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
+import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample;
+import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
+import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
+import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
+import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
+import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
+
+public abstract class AbstractActivityChartFragment extends AbstractChartFragment {
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractActivityChartFragment.class);
+
+ public boolean supportsHeartrate(GBDevice device) {
+ DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device);
+ return coordinator != null && coordinator.supportsHeartRateMeasurement(device);
+ }
+
+ public boolean supportsRemSleep(GBDevice device) {
+ DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device);
+ return coordinator != null && coordinator.supportsRemSleep();
+ }
+
+ protected static final class ActivityConfig {
+ public final int type;
+ public final String label;
+ public final Integer color;
+
+ public ActivityConfig(int kind, String label, Integer color) {
+ this.type = kind;
+ this.label = label;
+ this.color = color;
+ }
+ }
+
+ protected ActivityConfig akActivity;
+ protected ActivityConfig akLightSleep;
+ protected ActivityConfig akDeepSleep;
+ protected ActivityConfig akRemSleep;
+ protected ActivityConfig akNotWorn;
+
+ protected int BACKGROUND_COLOR;
+ protected int DESCRIPTION_COLOR;
+ protected int CHART_TEXT_COLOR;
+ protected int LEGEND_TEXT_COLOR;
+ protected int HEARTRATE_COLOR;
+ protected int HEARTRATE_FILL_COLOR;
+ protected int AK_ACTIVITY_COLOR;
+ protected int AK_DEEP_SLEEP_COLOR;
+ protected int AK_REM_SLEEP_COLOR;
+ protected int AK_LIGHT_SLEEP_COLOR;
+ protected int AK_NOT_WORN_COLOR;
+
+ protected String HEARTRATE_LABEL;
+ protected String HEARTRATE_AVERAGE_LABEL;
+
+ protected void init() {
+ Prefs prefs = GBApplication.getPrefs();
+ TypedValue runningColor = new TypedValue();
+ BACKGROUND_COLOR = GBApplication.getBackgroundColor(getContext());
+ LEGEND_TEXT_COLOR = DESCRIPTION_COLOR = GBApplication.getTextColor(getContext());
+ CHART_TEXT_COLOR = ContextCompat.getColor(getContext(), R.color.secondarytext);
+ if (prefs.getBoolean("chart_heartrate_color", false)) {
+ HEARTRATE_COLOR = ContextCompat.getColor(getContext(), R.color.chart_heartrate_alternative);
+ }else{
+ HEARTRATE_COLOR = ContextCompat.getColor(getContext(), R.color.chart_heartrate);
+ }
+ HEARTRATE_FILL_COLOR = ContextCompat.getColor(getContext(), R.color.chart_heartrate_fill);
+
+ getContext().getTheme().resolveAttribute(R.attr.chart_activity, runningColor, true);
+ AK_ACTIVITY_COLOR = runningColor.data;
+ getContext().getTheme().resolveAttribute(R.attr.chart_deep_sleep, runningColor, true);
+ AK_DEEP_SLEEP_COLOR = runningColor.data;
+ getContext().getTheme().resolveAttribute(R.attr.chart_light_sleep, runningColor, true);
+ AK_LIGHT_SLEEP_COLOR = runningColor.data;
+ getContext().getTheme().resolveAttribute(R.attr.chart_rem_sleep, runningColor, true);
+ AK_REM_SLEEP_COLOR = runningColor.data;
+ getContext().getTheme().resolveAttribute(R.attr.chart_not_worn, runningColor, true);
+ AK_NOT_WORN_COLOR = runningColor.data;
+
+ HEARTRATE_LABEL = getContext().getString(R.string.charts_legend_heartrate);
+ HEARTRATE_AVERAGE_LABEL = getContext().getString(R.string.charts_legend_heartrate_average);
+
+ akActivity = new ActivityConfig(ActivityKind.TYPE_ACTIVITY, getString(R.string.abstract_chart_fragment_kind_activity), AK_ACTIVITY_COLOR);
+ akLightSleep = new ActivityConfig(ActivityKind.TYPE_LIGHT_SLEEP, getString(R.string.abstract_chart_fragment_kind_light_sleep), AK_LIGHT_SLEEP_COLOR);
+ akDeepSleep = new ActivityConfig(ActivityKind.TYPE_DEEP_SLEEP, getString(R.string.abstract_chart_fragment_kind_deep_sleep), AK_DEEP_SLEEP_COLOR);
+ akRemSleep = new ActivityConfig(ActivityKind.TYPE_REM_SLEEP, getString(R.string.abstract_chart_fragment_kind_rem_sleep), AK_REM_SLEEP_COLOR);
+ akNotWorn = new ActivityConfig(ActivityKind.TYPE_NOT_WORN, getString(R.string.abstract_chart_fragment_kind_not_worn), AK_NOT_WORN_COLOR);
+ }
+
+ protected Integer getColorFor(int activityKind) {
+ switch (activityKind) {
+ case ActivityKind.TYPE_DEEP_SLEEP:
+ return akDeepSleep.color;
+ case ActivityKind.TYPE_LIGHT_SLEEP:
+ return akLightSleep.color;
+ case ActivityKind.TYPE_REM_SLEEP:
+ return akRemSleep.color;
+ case ActivityKind.TYPE_ACTIVITY:
+ return akActivity.color;
+ }
+ return akActivity.color;
+ }
+
+ protected SampleProvider extends AbstractActivitySample> getProvider(DBHandler db, GBDevice device) {
+ DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device);
+ return coordinator.getSampleProvider(device, db.getDaoSession());
+ }
+
+ /**
+ * Returns all kinds of samples for the given device.
+ * To be called from a background thread.
+ *
+ * @param device
+ * @param tsFrom
+ * @param tsTo
+ */
+ protected List extends ActivitySample> getAllSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
+ SampleProvider extends ActivitySample> provider = getProvider(db, device);
+ return provider.getAllActivitySamples(tsFrom, tsTo);
+ }
+
+ protected List extends AbstractActivitySample> getActivitySamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
+ SampleProvider extends AbstractActivitySample> provider = getProvider(db, device);
+ return provider.getActivitySamples(tsFrom, tsTo);
+ }
+
+
+ protected List extends ActivitySample> getSleepSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
+ SampleProvider extends ActivitySample> provider = getProvider(db, device);
+ return provider.getSleepSamples(tsFrom, tsTo);
+ }
+
+ public DefaultChartsData refresh(GBDevice gbDevice, List extends ActivitySample> samples) {
+// Calendar cal = GregorianCalendar.getInstance();
+// cal.clear();
+ TimestampTranslation tsTranslation = new TimestampTranslation();
+// Date date;
+// String dateStringFrom = "";
+// String dateStringTo = "";
+// ArrayList xLabels = null;
+
+ LOG.info("" + getTitle() + ": number of samples:" + samples.size());
+ LineData lineData;
+ if (samples.size() > 1) {
+ boolean annotate = true;
+ boolean use_steps_as_movement;
+
+ int last_type = ActivityKind.TYPE_UNKNOWN;
+
+ int numEntries = samples.size();
+ List activityEntries = new ArrayList<>(numEntries);
+ List deepSleepEntries = new ArrayList<>(numEntries);
+ List lightSleepEntries = new ArrayList<>(numEntries);
+ List remSleepEntries = new ArrayList<>(numEntries);
+ List notWornEntries = new ArrayList<>(numEntries);
+ boolean hr = supportsHeartrate(gbDevice);
+ List heartrateEntries = hr ? new ArrayList(numEntries) : null;
+ List colors = new ArrayList<>(numEntries); // this is kinda inefficient...
+ int lastHrSampleIndex = -1;
+ HeartRateUtils heartRateUtilsInstance = HeartRateUtils.getInstance();
+
+ for (int i = 0; i < numEntries; i++) {
+ ActivitySample sample = samples.get(i);
+ int type = sample.getKind();
+ int ts = tsTranslation.shorten(sample.getTimestamp());
+
+// System.out.println(ts);
+// ts = i;
+ // determine start and end dates
+// if (i == 0) {
+// cal.setTimeInMillis(ts * 1000L); // make sure it's converted to long
+// date = cal.getTime();
+// dateStringFrom = dateFormat.format(date);
+// } else if (i == samples.size() - 1) {
+// cal.setTimeInMillis(ts * 1000L); // same here
+// date = cal.getTime();
+// dateStringTo = dateFormat.format(date);
+// }
+
+ float movement = sample.getIntensity();
+
+ float value = movement;
+ switch (type) {
+ case ActivityKind.TYPE_DEEP_SLEEP:
+ if (last_type != type) { //FIXME: this is ugly but it works (repeated in each case)
+ deepSleepEntries.add(createLineEntry(0, ts - 1));
+
+ lightSleepEntries.add(createLineEntry(0, ts));
+ remSleepEntries.add(createLineEntry(0, ts));
+ notWornEntries.add(createLineEntry(0, ts));
+ activityEntries.add(createLineEntry(0, ts));
+ }
+ deepSleepEntries.add(createLineEntry(value + SleepUtils.Y_VALUE_DEEP_SLEEP, ts));
+ break;
+ case ActivityKind.TYPE_LIGHT_SLEEP:
+ if (last_type != type) {
+ lightSleepEntries.add(createLineEntry(0, ts - 1));
+
+ deepSleepEntries.add(createLineEntry(0, ts));
+ remSleepEntries.add(createLineEntry(0, ts));
+ notWornEntries.add(createLineEntry(0, ts));
+ activityEntries.add(createLineEntry(0, ts));
+ }
+ lightSleepEntries.add(createLineEntry(value, ts));
+ break;
+ case ActivityKind.TYPE_REM_SLEEP:
+ if (last_type != type) {
+ remSleepEntries.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(value, ts));
+ break;
+ case ActivityKind.TYPE_NOT_WORN:
+ if (last_type != type) {
+ notWornEntries.add(createLineEntry(0, ts - 1));
+
+ lightSleepEntries.add(createLineEntry(0, ts));
+ deepSleepEntries.add(createLineEntry(0, ts));
+ remSleepEntries.add(createLineEntry(0, ts));
+ activityEntries.add(createLineEntry(0, ts));
+ }
+ notWornEntries.add(createLineEntry(SleepUtils.Y_VALUE_DEEP_SLEEP, ts)); //a small value, just to show something on the graphs
+ break;
+ default:
+// short steps = sample.getSteps();
+// if (use_steps_as_movement && steps != 0) {
+// // I'm not sure using steps for this is actually a good idea
+// movement = steps;
+// }
+// value = ((float) movement) / movement_divisor;
+ if (last_type != type) {
+ activityEntries.add(createLineEntry(0, ts - 1));
+
+ lightSleepEntries.add(createLineEntry(0, ts));
+ notWornEntries.add(createLineEntry(0, ts));
+ deepSleepEntries.add(createLineEntry(0, ts));
+ remSleepEntries.add(createLineEntry(0, ts));
+ }
+ activityEntries.add(createLineEntry(value, ts));
+ }
+ if (hr && sample.getKind() != ActivityKind.TYPE_NOT_WORN && heartRateUtilsInstance.isValidHeartRateValue(sample.getHeartRate())) {
+ if (lastHrSampleIndex > -1 && ts - lastHrSampleIndex > 1800*HeartRateUtils.MAX_HR_MEASUREMENTS_GAP_MINUTES) {
+ heartrateEntries.add(createLineEntry(0, lastHrSampleIndex + 1));
+ heartrateEntries.add(createLineEntry(0, ts - 1));
+ }
+
+ heartrateEntries.add(createLineEntry(sample.getHeartRate(), ts));
+ lastHrSampleIndex = ts;
+ }
+
+ String xLabel = "";
+ if (annotate) {
+// cal.setTimeInMillis((ts + tsOffset) * 1000L);
+// date = cal.getTime();
+// String dateString = annotationDateFormat.format(date);
+// xLabel = dateString;
+// if (last_type != type) {
+// if (isSleep(last_type) && !isSleep(type)) {
+// // woken up
+// LimitLine line = new LimitLine(i, dateString);
+// line.enableDashedLine(8, 8, 0);
+// line.setTextColor(Color.WHITE);
+// line.setTextSize(15);
+// chart.getXAxis().addLimitLine(line);
+// } else if (!isSleep(last_type) && isSleep(type)) {
+// // fallen asleep
+// LimitLine line = new LimitLine(i, dateString);
+// line.enableDashedLine(8, 8, 0);
+// line.setTextSize(15);
+// line.setTextColor(Color.WHITE);
+// chart.getXAxis().addLimitLine(line);
+// }
+// }
+ }
+ last_type = type;
+ }
+
+
+ List lineDataSets = new ArrayList<>();
+ LineDataSet activitySet = createDataSet(activityEntries, akActivity.color, "Activity");
+ lineDataSets.add(activitySet);
+ LineDataSet deepSleepSet = createDataSet(deepSleepEntries, akDeepSleep.color, "Deep Sleep");
+ lineDataSets.add(deepSleepSet);
+ LineDataSet lightSleepSet = createDataSet(lightSleepEntries, akLightSleep.color, "Light Sleep");
+ lineDataSets.add(lightSleepSet);
+ if (supportsRemSleep(gbDevice)) {
+ LineDataSet remSleepSet = createDataSet(remSleepEntries, akRemSleep.color, "REM Sleep");
+ lineDataSets.add(remSleepSet);
+ }
+ LineDataSet notWornSet = createDataSet(notWornEntries, akNotWorn.color, "Not worn");
+ lineDataSets.add(notWornSet);
+
+ if (hr && heartrateEntries.size() > 0) {
+ LineDataSet heartrateSet = createHeartrateSet(heartrateEntries, "Heart Rate");
+
+ lineDataSets.add(heartrateSet);
+ }
+ lineData = new LineData(lineDataSets);
+
+// chart.setDescription(getString(R.string.sleep_activity_date_range, dateStringFrom, dateStringTo));
+// chart.setDescriptionPosition(?, ?);
+ } else {
+ lineData = new LineData();
+ }
+
+ ValueFormatter xValueFormatter = new SampleXLabelFormatter(tsTranslation);
+ return new DefaultChartsData(lineData, xValueFormatter);
+ }
+
+ protected Entry createLineEntry(float value, int xValue) {
+ return new Entry(xValue, value);
+ }
+
+ protected LineDataSet createDataSet(List values, Integer color, String label) {
+ LineDataSet set1 = new LineDataSet(values, label);
+ set1.setColor(color);
+// set1.setDrawCubic(true);
+// set1.setCubicIntensity(0.2f);
+ set1.setDrawFilled(true);
+ set1.setDrawCircles(false);
+// set1.setLineWidth(2f);
+// set1.setCircleSize(5f);
+ set1.setFillColor(color);
+ set1.setFillAlpha(255);
+ set1.setDrawValues(false);
+// set1.setHighLightColor(Color.rgb(128, 0, 255));
+// set1.setColor(Color.rgb(89, 178, 44));
+ set1.setValueTextColor(CHART_TEXT_COLOR);
+ set1.setAxisDependency(YAxis.AxisDependency.LEFT);
+ return set1;
+ }
+
+ protected LineDataSet createHeartrateSet(List values, String label) {
+ LineDataSet set1 = new LineDataSet(values, label);
+ set1.setLineWidth(2.2f);
+ set1.setColor(HEARTRATE_COLOR);
+// set1.setDrawCubic(true);
+ set1.setMode(LineDataSet.Mode.HORIZONTAL_BEZIER);
+ set1.setCubicIntensity(0.1f);
+ set1.setDrawCircles(false);
+// set1.setCircleRadius(2f);
+// set1.setDrawFilled(true);
+// set1.setColor(getResources().getColor(android.R.color.background_light));
+// set1.setCircleColor(HEARTRATE_COLOR);
+// set1.setFillColor(ColorTemplate.getHoloBlue());
+// set1.setHighLightColor(Color.rgb(128, 0, 255));
+// set1.setColor(Color.rgb(89, 178, 44));
+ set1.setDrawValues(true);
+ set1.setValueTextColor(CHART_TEXT_COLOR);
+ set1.setAxisDependency(YAxis.AxisDependency.RIGHT);
+ return set1;
+ }
+
+ /**
+ * Implement this to supply the samples to be displayed.
+ *
+ * @param db
+ * @param device
+ * @param tsFrom
+ * @param tsTo
+ * @return
+ */
+ protected abstract List extends ActivitySample> getSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo);
+
+ protected List extends ActivitySample> getSamples(DBHandler db, GBDevice device) {
+ int tsStart = getTSStart();
+ int tsEnd = getTSEnd();
+ List samples = (List) getSamples(db, device, tsStart, tsEnd);
+ ensureStartAndEndSamples(samples, tsStart, tsEnd);
+// List samples2 = new ArrayList<>();
+// int min = Math.min(samples.size(), 10);
+// int min = Math.min(samples.size(), 10);
+// for (int i = 0; i < min; i++) {
+// samples2.add(samples.get(i));
+// }
+// return samples2;
+ return samples;
+ }
+
+ protected List extends ActivitySample> getSamplesofSleep(DBHandler db, GBDevice device) {
+ int SLEEP_HOUR_LIMIT = 12;
+
+ int tsStart = getTSStart();
+ Calendar day = GregorianCalendar.getInstance();
+ day.setTimeInMillis(tsStart * 1000L);
+ day.set(Calendar.HOUR_OF_DAY, SLEEP_HOUR_LIMIT);
+ day.set(Calendar.MINUTE, 0);
+ day.set(Calendar.SECOND, 0);
+ tsStart = toTimestamp(day.getTime());
+
+ int tsEnd = getTSEnd();
+ day.setTimeInMillis(tsEnd* 1000L);
+ day.set(Calendar.HOUR_OF_DAY, SLEEP_HOUR_LIMIT);
+ day.set(Calendar.MINUTE, 0);
+ day.set(Calendar.SECOND, 0);
+ tsEnd = toTimestamp(day.getTime());
+
+ List samples = (List) getSamples(db, device, tsStart, tsEnd);
+ ensureStartAndEndSamples(samples, tsStart, tsEnd);
+ return samples;
+ }
+
+ protected void ensureStartAndEndSamples(List samples, int tsStart, int tsEnd) {
+ if (samples == null || samples.isEmpty()) {
+ return;
+ }
+ ActivitySample lastSample = samples.get(samples.size() - 1);
+ if (lastSample.getTimestamp() < tsEnd) {
+ samples.add(createTrailingActivitySample(lastSample, tsEnd));
+ }
+
+ ActivitySample firstSample = samples.get(0);
+ if (firstSample.getTimestamp() > tsStart) {
+ samples.add(createTrailingActivitySample(firstSample, tsStart));
+ }
+ }
+
+ private ActivitySample createTrailingActivitySample(ActivitySample referenceSample, int timestamp) {
+ TrailingActivitySample sample = new TrailingActivitySample();
+ if (referenceSample instanceof AbstractActivitySample) {
+ AbstractActivitySample reference = (AbstractActivitySample) referenceSample;
+ sample.setUserId(reference.getUserId());
+ sample.setDeviceId(reference.getDeviceId());
+ sample.setProvider(reference.getProvider());
+ }
+ sample.setTimestamp(timestamp);
+ return sample;
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java
index ed211e41e..3d3398f3e 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartFragment.java
@@ -17,57 +17,37 @@
along with this program. If not, see . */
package nodomain.freeyourgadget.gadgetbridge.activities.charts;
+import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.AsyncTask;
import android.os.Bundle;
-import android.util.TypedValue;
import android.view.View;
-import androidx.annotation.Nullable;
-import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentActivity;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.github.mikephil.charting.charts.BarChart;
import com.github.mikephil.charting.charts.BarLineChartBase;
import com.github.mikephil.charting.charts.Chart;
-import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.data.Entry;
-import com.github.mikephil.charting.data.LineData;
-import com.github.mikephil.charting.data.LineDataSet;
-import com.github.mikephil.charting.formatter.ValueFormatter;
-import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Calendar;
import java.util.Date;
-import java.util.GregorianCalendar;
import java.util.HashSet;
-import java.util.List;
import java.util.Set;
-import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBFragment;
-import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateUtils;
import nodomain.freeyourgadget.gadgetbridge.database.DBAccess;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
-import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
-import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
-import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
-import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
-import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
-import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
-import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
/**
* A base class fragment to be used with ChartsActivity. The fragment can supply
@@ -87,7 +67,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
* The default implementations #handleDatePrev(Date,Date) and #handleDateNext(Date,Date)
* shift the date by one day.
*/
-public abstract class AbstractChartFragment extends AbstractGBFragment {
+public abstract class AbstractChartFragment extends AbstractGBFragment {
protected final int ANIM_TIME = 250;
private static final Logger LOG = LoggerFactory.getLogger(AbstractChartFragment.class);
@@ -99,60 +79,10 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
AbstractChartFragment.this.onReceive(context, intent);
}
};
+
private boolean mChartDirty = true;
private AsyncTask refreshTask;
- public boolean isChartDirty() {
- return mChartDirty;
- }
-
- @Override
- public abstract String getTitle();
-
- public boolean supportsHeartrate(GBDevice device) {
- DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device);
- return coordinator != null && coordinator.supportsHeartRateMeasurement(device);
- }
-
- public boolean supportsRemSleep(GBDevice device) {
- DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device);
- return coordinator != null && coordinator.supportsRemSleep();
- }
-
- protected static final class ActivityConfig {
- public final int type;
- public final String label;
- public final Integer color;
-
- public ActivityConfig(int kind, String label, Integer color) {
- this.type = kind;
- this.label = label;
- this.color = color;
- }
- }
-
- protected ActivityConfig akActivity;
- protected ActivityConfig akLightSleep;
- protected ActivityConfig akDeepSleep;
- protected ActivityConfig akRemSleep;
- protected ActivityConfig akNotWorn;
-
-
- protected int BACKGROUND_COLOR;
- protected int DESCRIPTION_COLOR;
- protected int CHART_TEXT_COLOR;
- protected int LEGEND_TEXT_COLOR;
- protected int HEARTRATE_COLOR;
- protected int HEARTRATE_FILL_COLOR;
- protected int AK_ACTIVITY_COLOR;
- protected int AK_DEEP_SLEEP_COLOR;
- protected int AK_REM_SLEEP_COLOR;
- protected int AK_LIGHT_SLEEP_COLOR;
- protected int AK_NOT_WORN_COLOR;
-
- protected String HEARTRATE_LABEL;
- protected String HEARTRATE_AVERAGE_LABEL;
-
protected AbstractChartFragment(String... intentFilterActions) {
mIntentFilterActions = new HashSet<>();
if (intentFilterActions != null) {
@@ -167,62 +97,73 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
mIntentFilterActions.add(ChartsHost.REFRESH);
}
+ @Override
+ public abstract String getTitle();
+
+ /**
+ * Called in the fragment's onCreate, initializes this fragment.
+ */
+ protected abstract void init();
+
+ /**
+ * This method reads the data from the database, analyzes and prepares it for
+ * the charts. This will be called from a background task, so there must not be
+ * any UI access. #updateChartsInUIThread and #renderCharts will be automatically called after this method.
+ */
+ protected abstract D refreshInBackground(ChartsHost chartsHost, DBHandler db, GBDevice device);
+
+ /**
+ * Triggers the actual (re-) rendering of the chart.
+ * Always called from the UI thread.
+ */
+ protected abstract void renderCharts();
+
+ protected abstract void setupLegend(Chart> chart);
+
+ protected abstract void updateChartsnUIThread(D chartsData);
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
init();
- IntentFilter filter = new IntentFilter();
+ final IntentFilter filter = new IntentFilter();
for (String action : mIntentFilterActions) {
filter.addAction(action);
}
- LocalBroadcastManager.getInstance(getActivity()).registerReceiver(mReceiver, filter);
+ LocalBroadcastManager.getInstance(requireActivity()).registerReceiver(mReceiver, filter);
}
- protected void init() {
- Prefs prefs = GBApplication.getPrefs();
- TypedValue runningColor = new TypedValue();
- BACKGROUND_COLOR = GBApplication.getBackgroundColor(getContext());
- LEGEND_TEXT_COLOR = DESCRIPTION_COLOR = GBApplication.getTextColor(getContext());
- CHART_TEXT_COLOR = ContextCompat.getColor(getContext(), R.color.secondarytext);
- if (prefs.getBoolean("chart_heartrate_color", false)) {
- HEARTRATE_COLOR = ContextCompat.getColor(getContext(), R.color.chart_heartrate_alternative);
- }else{
- HEARTRATE_COLOR = ContextCompat.getColor(getContext(), R.color.chart_heartrate);
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ LocalBroadcastManager.getInstance(requireActivity()).unregisterReceiver(mReceiver);
+ }
+
+ /**
+ * Called when this fragment has been fully scrolled into the activity.
+ *
+ * @see #isVisibleInActivity()
+ * @see #onMadeInvisibleInActivity()
+ */
+ @Override
+ protected void onMadeVisibleInActivity() {
+ super.onMadeVisibleInActivity();
+ showDateBar(true);
+ if (mChartDirty) {
+ refresh();
}
- HEARTRATE_FILL_COLOR = ContextCompat.getColor(getContext(), R.color.chart_heartrate_fill);
+ }
- getContext().getTheme().resolveAttribute(R.attr.chart_activity, runningColor, true);
- AK_ACTIVITY_COLOR = runningColor.data;
- getContext().getTheme().resolveAttribute(R.attr.chart_deep_sleep, runningColor, true);
- AK_DEEP_SLEEP_COLOR = runningColor.data;
- getContext().getTheme().resolveAttribute(R.attr.chart_light_sleep, runningColor, true);
- AK_LIGHT_SLEEP_COLOR = runningColor.data;
- getContext().getTheme().resolveAttribute(R.attr.chart_rem_sleep, runningColor, true);
- AK_REM_SLEEP_COLOR = runningColor.data;
- getContext().getTheme().resolveAttribute(R.attr.chart_not_worn, runningColor, true);
- AK_NOT_WORN_COLOR = runningColor.data;
-
- HEARTRATE_LABEL = getContext().getString(R.string.charts_legend_heartrate);
- HEARTRATE_AVERAGE_LABEL = getContext().getString(R.string.charts_legend_heartrate_average);
-
- akActivity = new ActivityConfig(ActivityKind.TYPE_ACTIVITY, getString(R.string.abstract_chart_fragment_kind_activity), AK_ACTIVITY_COLOR);
- akLightSleep = new ActivityConfig(ActivityKind.TYPE_LIGHT_SLEEP, getString(R.string.abstract_chart_fragment_kind_light_sleep), AK_LIGHT_SLEEP_COLOR);
- akDeepSleep = new ActivityConfig(ActivityKind.TYPE_DEEP_SLEEP, getString(R.string.abstract_chart_fragment_kind_deep_sleep), AK_DEEP_SLEEP_COLOR);
- akRemSleep = new ActivityConfig(ActivityKind.TYPE_REM_SLEEP, getString(R.string.abstract_chart_fragment_kind_rem_sleep), AK_REM_SLEEP_COLOR);
- akNotWorn = new ActivityConfig(ActivityKind.TYPE_NOT_WORN, getString(R.string.abstract_chart_fragment_kind_not_worn), AK_NOT_WORN_COLOR);
+ protected ChartsHost getChartsHost() {
+ return (ChartsHost) requireActivity();
}
private void setStartDate(Date date) {
getChartsHost().setStartDate(date);
}
- @Nullable
- protected ChartsHost getChartsHost() {
- return (ChartsHost) getActivity();
- }
-
private void setEndDate(Date date) {
getChartsHost().setEndDate(date);
}
@@ -235,56 +176,47 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
return getChartsHost().getEndDate();
}
- /**
- * Called when this fragment has been fully scrolled into the activity.
- *
- * @see #isVisibleInActivity()
- * @see #onMadeInvisibleInActivity()
- */
- @Override
- protected void onMadeVisibleInActivity() {
- super.onMadeVisibleInActivity();
- showDateBar(true);
- if (isChartDirty()) {
- refresh();
- }
+ protected int getTSEnd() {
+ return toTimestamp(getEndDate());
+ }
+
+ protected int getTSStart() {
+ return toTimestamp(getStartDate());
+ }
+
+ protected int toTimestamp(Date date) {
+ return (int) ((date.getTime() / 1000));
}
protected void showDateBar(boolean show) {
getChartsHost().getDateBar().setVisibility(show ? View.VISIBLE : View.GONE);
}
- @Override
- public void onDestroy() {
- super.onDestroy();
- LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(mReceiver);
- }
-
protected void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ChartsHost.REFRESH.equals(action)) {
refresh();
} else if (ChartsHost.DATE_NEXT_DAY.equals(action)) {
- handleDate(getStartDate(), getEndDate(),+1);
+ handleDate(getStartDate(), getEndDate(), +1);
} else if (ChartsHost.DATE_PREV_DAY.equals(action)) {
- handleDate(getStartDate(), getEndDate(),-1);
+ handleDate(getStartDate(), getEndDate(), -1);
} else if (ChartsHost.DATE_NEXT_WEEK.equals(action)) {
- handleDate(getStartDate(), getEndDate(),+7);
+ handleDate(getStartDate(), getEndDate(), +7);
} else if (ChartsHost.DATE_PREV_WEEK.equals(action)) {
- handleDate(getStartDate(), getEndDate(),-7);
+ handleDate(getStartDate(), getEndDate(), -7);
} else if (ChartsHost.DATE_NEXT_MONTH.equals(action)) {
//calculate dates to jump by month but keep subsequent logic working
- int time1 = DateTimeUtils.shiftMonths((int )(getStartDate().getTime()/1000), 1);
- int time2 = DateTimeUtils.shiftMonths((int )(getEndDate().getTime()/1000), 1);
+ int time1 = DateTimeUtils.shiftMonths((int) (getStartDate().getTime() / 1000), 1);
+ int time2 = DateTimeUtils.shiftMonths((int) (getEndDate().getTime() / 1000), 1);
Date date1 = DateTimeUtils.shiftByDays(new Date(time1 * 1000L), 30);
Date date2 = DateTimeUtils.shiftByDays(new Date(time2 * 1000L), 30);
- handleDate(date1, date2,-30);
+ handleDate(date1, date2, -30);
} else if (ChartsHost.DATE_PREV_MONTH.equals(action)) {
- int time1 = DateTimeUtils.shiftMonths((int )(getStartDate().getTime()/1000), -1);
- int time2 = DateTimeUtils.shiftMonths((int )(getEndDate().getTime()/1000), -1);
+ int time1 = DateTimeUtils.shiftMonths((int) (getStartDate().getTime() / 1000), -1);
+ int time2 = DateTimeUtils.shiftMonths((int) (getEndDate().getTime() / 1000), -1);
Date date1 = DateTimeUtils.shiftByDays(new Date(time1 * 1000L), -30);
Date date2 = DateTimeUtils.shiftByDays(new Date(time2 * 1000L), -30);
- handleDate(date1, date2,30);
+ handleDate(date1, date2, 30);
}
}
@@ -292,21 +224,20 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
* Default implementation shifts the dates by one day, if visible
* and calls #refreshIfVisible().
*
- * @param startDate
- * @param endDate
- * @param Offset
+ * @param startDate the start date
+ * @param endDate the end date
+ * @param offset the offset, in days
*/
- protected void handleDate(Date startDate, Date endDate, Integer Offset) {
+ private void handleDate(Date startDate, Date endDate, Integer offset) {
if (isVisibleInActivity()) {
- if (!shiftDates(startDate, endDate, Offset)) {
+ if (!shiftDates(startDate, endDate, offset)) {
return;
}
}
refreshIfVisible();
}
-
- protected void refreshIfVisible() {
+ private void refreshIfVisible() {
if (isVisibleInActivity()) {
refresh();
} else {
@@ -317,65 +248,22 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
/**
* Shifts the given dates by offset days. offset may be positive or negative.
*
- * @param startDate
- * @param endDate
+ * @param startDate the start date
+ * @param endDate the end date
* @param offset a positive or negative number of days to shift the dates
* @return true if the shift was successful and false otherwise
*/
- protected boolean shiftDates(Date startDate, Date endDate, int offset) {
+ private boolean shiftDates(Date startDate, Date endDate, int offset) {
Date newStart = DateTimeUtils.shiftByDays(startDate, offset);
Date newEnd = DateTimeUtils.shiftByDays(endDate, offset);
Date now = new Date();
if (newEnd.after(now)) { //allow to jump to the end (now) if week/month reach after now
- newEnd=now;
- newStart=DateTimeUtils.shiftByDays(now,-1);
+ newEnd = now;
+ newStart = DateTimeUtils.shiftByDays(now, -1);
}
return setDateRange(newStart, newEnd);
}
- protected Integer getColorFor(int activityKind) {
- switch (activityKind) {
- case ActivityKind.TYPE_DEEP_SLEEP:
- return akDeepSleep.color;
- case ActivityKind.TYPE_LIGHT_SLEEP:
- return akLightSleep.color;
- case ActivityKind.TYPE_REM_SLEEP:
- return akRemSleep.color;
- case ActivityKind.TYPE_ACTIVITY:
- return akActivity.color;
- }
- return akActivity.color;
- }
-
- protected SampleProvider extends AbstractActivitySample> getProvider(DBHandler db, GBDevice device) {
- DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device);
- return coordinator.getSampleProvider(device, db.getDaoSession());
- }
-
- /**
- * Returns all kinds of samples for the given device.
- * To be called from a background thread.
- *
- * @param device
- * @param tsFrom
- * @param tsTo
- */
- protected List extends ActivitySample> getAllSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
- SampleProvider extends ActivitySample> provider = getProvider(db, device);
- return provider.getAllActivitySamples(tsFrom, tsTo);
- }
-
- protected List extends AbstractActivitySample> getActivitySamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
- SampleProvider extends AbstractActivitySample> provider = getProvider(db, device);
- return provider.getActivitySamples(tsFrom, tsTo);
- }
-
-
- protected List extends ActivitySample> getSleepSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo) {
- SampleProvider extends ActivitySample> provider = getProvider(db, device);
- return provider.getSleepSamples(tsFrom, tsTo);
- }
-
protected void configureChartDefaults(Chart> chart) {
chart.getXAxis().setValueFormatter(new TimestampValueFormatter());
chart.getDescription().setText("");
@@ -432,271 +320,21 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
}
}
- /**
- * This method reads the data from the database, analyzes and prepares it for
- * the charts. This will be called from a background task, so there must not be
- * any UI access. #updateChartsInUIThread and #renderCharts will be automatically called after this method.
- */
- protected abstract ChartsData refreshInBackground(ChartsHost chartsHost, DBHandler db, GBDevice device);
-
- /**
- * Triggers the actual (re-) rendering of the chart.
- * Always called from the UI thread.
- */
- protected abstract void renderCharts();
-
- public DefaultChartsData refresh(GBDevice gbDevice, List extends ActivitySample> samples) {
-// Calendar cal = GregorianCalendar.getInstance();
-// cal.clear();
- TimestampTranslation tsTranslation = new TimestampTranslation();
-// Date date;
-// String dateStringFrom = "";
-// String dateStringTo = "";
-// ArrayList xLabels = null;
-
- LOG.info("" + getTitle() + ": number of samples:" + samples.size());
- LineData lineData;
- if (samples.size() > 1) {
- boolean annotate = true;
- boolean use_steps_as_movement;
-
- int last_type = ActivityKind.TYPE_UNKNOWN;
-
- int numEntries = samples.size();
- List activityEntries = new ArrayList<>(numEntries);
- List deepSleepEntries = new ArrayList<>(numEntries);
- List lightSleepEntries = new ArrayList<>(numEntries);
- List remSleepEntries = new ArrayList<>(numEntries);
- List notWornEntries = new ArrayList<>(numEntries);
- boolean hr = supportsHeartrate(gbDevice);
- List heartrateEntries = hr ? new ArrayList(numEntries) : null;
- List colors = new ArrayList<>(numEntries); // this is kinda inefficient...
- int lastHrSampleIndex = -1;
- HeartRateUtils heartRateUtilsInstance = HeartRateUtils.getInstance();
-
- for (int i = 0; i < numEntries; i++) {
- ActivitySample sample = samples.get(i);
- int type = sample.getKind();
- int ts = tsTranslation.shorten(sample.getTimestamp());
-
-// System.out.println(ts);
-// ts = i;
- // determine start and end dates
-// if (i == 0) {
-// cal.setTimeInMillis(ts * 1000L); // make sure it's converted to long
-// date = cal.getTime();
-// dateStringFrom = dateFormat.format(date);
-// } else if (i == samples.size() - 1) {
-// cal.setTimeInMillis(ts * 1000L); // same here
-// date = cal.getTime();
-// dateStringTo = dateFormat.format(date);
-// }
-
- float movement = sample.getIntensity();
-
- float value = movement;
- switch (type) {
- case ActivityKind.TYPE_DEEP_SLEEP:
- if (last_type != type) { //FIXME: this is ugly but it works (repeated in each case)
- deepSleepEntries.add(createLineEntry(0, ts - 1));
-
- lightSleepEntries.add(createLineEntry(0, ts));
- remSleepEntries.add(createLineEntry(0, ts));
- notWornEntries.add(createLineEntry(0, ts));
- activityEntries.add(createLineEntry(0, ts));
- }
- deepSleepEntries.add(createLineEntry(value + SleepUtils.Y_VALUE_DEEP_SLEEP, ts));
- break;
- case ActivityKind.TYPE_LIGHT_SLEEP:
- if (last_type != type) {
- lightSleepEntries.add(createLineEntry(0, ts - 1));
-
- deepSleepEntries.add(createLineEntry(0, ts));
- remSleepEntries.add(createLineEntry(0, ts));
- notWornEntries.add(createLineEntry(0, ts));
- activityEntries.add(createLineEntry(0, ts));
- }
- lightSleepEntries.add(createLineEntry(value, ts));
- break;
- case ActivityKind.TYPE_REM_SLEEP:
- if (last_type != type) {
- remSleepEntries.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(value, ts));
- break;
- case ActivityKind.TYPE_NOT_WORN:
- if (last_type != type) {
- notWornEntries.add(createLineEntry(0, ts - 1));
-
- lightSleepEntries.add(createLineEntry(0, ts));
- deepSleepEntries.add(createLineEntry(0, ts));
- remSleepEntries.add(createLineEntry(0, ts));
- activityEntries.add(createLineEntry(0, ts));
- }
- notWornEntries.add(createLineEntry(SleepUtils.Y_VALUE_DEEP_SLEEP, ts)); //a small value, just to show something on the graphs
- break;
- default:
-// short steps = sample.getSteps();
-// if (use_steps_as_movement && steps != 0) {
-// // I'm not sure using steps for this is actually a good idea
-// movement = steps;
-// }
-// value = ((float) movement) / movement_divisor;
- if (last_type != type) {
- activityEntries.add(createLineEntry(0, ts - 1));
-
- lightSleepEntries.add(createLineEntry(0, ts));
- notWornEntries.add(createLineEntry(0, ts));
- deepSleepEntries.add(createLineEntry(0, ts));
- remSleepEntries.add(createLineEntry(0, ts));
- }
- activityEntries.add(createLineEntry(value, ts));
- }
- if (hr && sample.getKind() != ActivityKind.TYPE_NOT_WORN && heartRateUtilsInstance.isValidHeartRateValue(sample.getHeartRate())) {
- if (lastHrSampleIndex > -1 && ts - lastHrSampleIndex > 1800*HeartRateUtils.MAX_HR_MEASUREMENTS_GAP_MINUTES) {
- heartrateEntries.add(createLineEntry(0, lastHrSampleIndex + 1));
- heartrateEntries.add(createLineEntry(0, ts - 1));
- }
-
- heartrateEntries.add(createLineEntry(sample.getHeartRate(), ts));
- lastHrSampleIndex = ts;
- }
-
- String xLabel = "";
- if (annotate) {
-// cal.setTimeInMillis((ts + tsOffset) * 1000L);
-// date = cal.getTime();
-// String dateString = annotationDateFormat.format(date);
-// xLabel = dateString;
-// if (last_type != type) {
-// if (isSleep(last_type) && !isSleep(type)) {
-// // woken up
-// LimitLine line = new LimitLine(i, dateString);
-// line.enableDashedLine(8, 8, 0);
-// line.setTextColor(Color.WHITE);
-// line.setTextSize(15);
-// chart.getXAxis().addLimitLine(line);
-// } else if (!isSleep(last_type) && isSleep(type)) {
-// // fallen asleep
-// LimitLine line = new LimitLine(i, dateString);
-// line.enableDashedLine(8, 8, 0);
-// line.setTextSize(15);
-// line.setTextColor(Color.WHITE);
-// chart.getXAxis().addLimitLine(line);
-// }
-// }
- }
- last_type = type;
- }
-
-
- List lineDataSets = new ArrayList<>();
- LineDataSet activitySet = createDataSet(activityEntries, akActivity.color, "Activity");
- lineDataSets.add(activitySet);
- LineDataSet deepSleepSet = createDataSet(deepSleepEntries, akDeepSleep.color, "Deep Sleep");
- lineDataSets.add(deepSleepSet);
- LineDataSet lightSleepSet = createDataSet(lightSleepEntries, akLightSleep.color, "Light Sleep");
- lineDataSets.add(lightSleepSet);
- if (supportsRemSleep(gbDevice)) {
- LineDataSet remSleepSet = createDataSet(remSleepEntries, akRemSleep.color, "REM Sleep");
- lineDataSets.add(remSleepSet);
- }
- LineDataSet notWornSet = createDataSet(notWornEntries, akNotWorn.color, "Not worn");
- lineDataSets.add(notWornSet);
-
- if (hr && heartrateEntries.size() > 0) {
- LineDataSet heartrateSet = createHeartrateSet(heartrateEntries, "Heart Rate");
-
- lineDataSets.add(heartrateSet);
- }
- lineData = new LineData(lineDataSets);
-
-// chart.setDescription(getString(R.string.sleep_activity_date_range, dateStringFrom, dateStringTo));
-// chart.setDescriptionPosition(?, ?);
- } else {
- lineData = new LineData();
- }
-
- ValueFormatter xValueFormatter = new SampleXLabelFormatter(tsTranslation);
- return new DefaultChartsData(lineData, xValueFormatter);
- }
-
- /**
- * Implement this to supply the samples to be displayed.
- *
- * @param db
- * @param device
- * @param tsFrom
- * @param tsTo
- * @return
- */
- protected abstract List extends ActivitySample> getSamples(DBHandler db, GBDevice device, int tsFrom, int tsTo);
-
- protected abstract void setupLegend(Chart chart);
-
- protected Entry createLineEntry(float value, int xValue) {
- return new Entry(xValue, value);
- }
-
- protected LineDataSet createDataSet(List values, Integer color, String label) {
- LineDataSet set1 = new LineDataSet(values, label);
- set1.setColor(color);
-// set1.setDrawCubic(true);
-// set1.setCubicIntensity(0.2f);
- set1.setDrawFilled(true);
- set1.setDrawCircles(false);
-// set1.setLineWidth(2f);
-// set1.setCircleSize(5f);
- set1.setFillColor(color);
- set1.setFillAlpha(255);
- set1.setDrawValues(false);
-// set1.setHighLightColor(Color.rgb(128, 0, 255));
-// set1.setColor(Color.rgb(89, 178, 44));
- set1.setValueTextColor(CHART_TEXT_COLOR);
- set1.setAxisDependency(YAxis.AxisDependency.LEFT);
- return set1;
- }
-
- protected LineDataSet createHeartrateSet(List values, String label) {
- LineDataSet set1 = new LineDataSet(values, label);
- set1.setLineWidth(2.2f);
- set1.setColor(HEARTRATE_COLOR);
-// set1.setDrawCubic(true);
- set1.setMode(LineDataSet.Mode.HORIZONTAL_BEZIER);
- set1.setCubicIntensity(0.1f);
- set1.setDrawCircles(false);
-// set1.setCircleRadius(2f);
-// set1.setDrawFilled(true);
-// set1.setColor(getResources().getColor(android.R.color.background_light));
-// set1.setCircleColor(HEARTRATE_COLOR);
-// set1.setFillColor(ColorTemplate.getHoloBlue());
-// set1.setHighLightColor(Color.rgb(128, 0, 255));
-// set1.setColor(Color.rgb(89, 178, 44));
- set1.setDrawValues(true);
- set1.setValueTextColor(CHART_TEXT_COLOR);
- set1.setAxisDependency(YAxis.AxisDependency.RIGHT);
- return set1;
- }
-
- protected RefreshTask createRefreshTask(String task, Context context) {
+ private RefreshTask createRefreshTask(final String task, final Context context) {
return new RefreshTask(task, context);
}
- public class RefreshTask extends DBAccess {
- private ChartsData chartsData;
+ @SuppressLint("StaticFieldLeak")
+ private final class RefreshTask extends DBAccess {
+ private D chartsData;
- public RefreshTask(String task, Context context) {
+ public RefreshTask(final String task, final Context context) {
super(task, context);
}
@Override
- protected void doInBackground(DBHandler db) {
- ChartsHost chartsHost = getChartsHost();
+ protected void doInBackground(final DBHandler db) {
+ final ChartsHost chartsHost = getChartsHost();
if (chartsHost != null) {
chartsData = refreshInBackground(chartsHost, db, chartsHost.getDevice());
} else {
@@ -705,9 +343,9 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
}
@Override
- protected void onPostExecute(Object o) {
+ protected void onPostExecute(final Object o) {
super.onPostExecute(o);
- FragmentActivity activity = getActivity();
+ final FragmentActivity activity = getActivity();
if (activity != null && !activity.isFinishing() && !activity.isDestroyed()) {
updateChartsnUIThread(chartsData);
renderCharts();
@@ -717,22 +355,20 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
}
}
- protected abstract void updateChartsnUIThread(ChartsData chartsData);
-
/**
* Returns true if the date was successfully shifted, and false if the shift
* was ignored, e.g. when the to-value is in the future.
*
- * @param from
- * @param to
+ * @param from the start date
+ * @param to the end date
*/
- public boolean setDateRange(Date from, Date to) {
+ private boolean setDateRange(final Date from, final Date to) {
if (from.compareTo(to) > 0) {
throw new IllegalArgumentException("Bad date range: " + from + ".." + to);
}
- Date now = new Date();
+ final Date now = new Date();
if (to.after(now) || //do not refresh chart if we reached now
- to.getTime()/10000 == (getEndDate().getTime()/10000)) {
+ to.getTime() / 10000 == (getEndDate().getTime() / 10000)) {
return false;
}
setStartDate(from);
@@ -740,88 +376,11 @@ public abstract class AbstractChartFragment extends AbstractGBFragment {
return true;
}
- protected void updateDateInfo(Date from, Date to) {
+ private void updateDateInfo(final Date from, final Date to) {
if (from.equals(to)) {
getChartsHost().setDateInfo(DateTimeUtils.formatDate(from));
} else {
getChartsHost().setDateInfo(DateTimeUtils.formatDateRange(from, to));
}
}
-
- protected List extends ActivitySample> getSamples(DBHandler db, GBDevice device) {
- int tsStart = getTSStart();
- int tsEnd = getTSEnd();
- List samples = (List) getSamples(db, device, tsStart, tsEnd);
- ensureStartAndEndSamples(samples, tsStart, tsEnd);
-// List samples2 = new ArrayList<>();
-// int min = Math.min(samples.size(), 10);
-// int min = Math.min(samples.size(), 10);
-// for (int i = 0; i < min; i++) {
-// samples2.add(samples.get(i));
-// }
-// return samples2;
- return samples;
- }
-
- protected List extends ActivitySample> getSamplesofSleep(DBHandler db, GBDevice device) {
- int SLEEP_HOUR_LIMIT = 12;
-
- int tsStart = getTSStart();
- Calendar day = GregorianCalendar.getInstance();
- day.setTimeInMillis(tsStart * 1000L);
- day.set(Calendar.HOUR_OF_DAY, SLEEP_HOUR_LIMIT);
- day.set(Calendar.MINUTE, 0);
- day.set(Calendar.SECOND, 0);
- tsStart = toTimestamp(day.getTime());
-
- int tsEnd = getTSEnd();
- day.setTimeInMillis(tsEnd* 1000L);
- day.set(Calendar.HOUR_OF_DAY, SLEEP_HOUR_LIMIT);
- day.set(Calendar.MINUTE, 0);
- day.set(Calendar.SECOND, 0);
- tsEnd = toTimestamp(day.getTime());
-
- List samples = (List) getSamples(db, device, tsStart, tsEnd);
- ensureStartAndEndSamples(samples, tsStart, tsEnd);
- return samples;
- }
-
- protected void ensureStartAndEndSamples(List samples, int tsStart, int tsEnd) {
- if (samples == null || samples.isEmpty()) {
- return;
- }
- ActivitySample lastSample = samples.get(samples.size() - 1);
- if (lastSample.getTimestamp() < tsEnd) {
- samples.add(createTrailingActivitySample(lastSample, tsEnd));
- }
-
- ActivitySample firstSample = samples.get(0);
- if (firstSample.getTimestamp() > tsStart) {
- samples.add(createTrailingActivitySample(firstSample, tsStart));
- }
- }
-
- private ActivitySample createTrailingActivitySample(ActivitySample referenceSample, int timestamp) {
- TrailingActivitySample sample = new TrailingActivitySample();
- if (referenceSample instanceof AbstractActivitySample) {
- AbstractActivitySample reference = (AbstractActivitySample) referenceSample;
- sample.setUserId(reference.getUserId());
- sample.setDeviceId(reference.getDeviceId());
- sample.setProvider(reference.getProvider());
- }
- sample.setTimestamp(timestamp);
- return sample;
- }
-
- private int getTSEnd() {
- return toTimestamp(getEndDate());
- }
-
- private int getTSStart() {
- return toTimestamp(getStartDate());
- }
-
- private int toTimestamp(Date date) {
- return (int) ((date.getTime() / 1000));
- }
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartsActivity.java
new file mode 100644
index 000000000..83de45923
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractChartsActivity.java
@@ -0,0 +1,285 @@
+/* Copyright (C) 2015-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
+ Gobbetti, vanous, Vebryn
+
+ 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.charts;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+import androidx.viewpager.widget.ViewPager;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+
+import nodomain.freeyourgadget.gadgetbridge.GBApplication;
+import nodomain.freeyourgadget.gadgetbridge.R;
+import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBFragmentActivity;
+import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
+import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
+import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
+import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
+import nodomain.freeyourgadget.gadgetbridge.util.GB;
+import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue;
+
+public abstract class AbstractChartsActivity extends AbstractGBFragmentActivity implements ChartsHost {
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractChartsActivity.class);
+
+ public static final String EXTRA_FRAGMENT_ID = "fragment";
+ public static final int REQUEST_CODE_PREFERENCES = 1;
+
+ private TextView mDateControl;
+
+ private Date mStartDate;
+ private Date mEndDate;
+ private SwipeRefreshLayout swipeLayout;
+
+ List enabledTabsList;
+
+ private GBDevice mGBDevice;
+ private ViewGroup dateBar;
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ switch (Objects.requireNonNull(action)) {
+ case GBDevice.ACTION_DEVICE_CHANGED:
+ GBDevice dev = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE);
+ if (dev != null) {
+ refreshBusyState(dev);
+ }
+ break;
+ }
+ }
+ };
+
+ private void refreshBusyState(GBDevice dev) {
+ if (dev.isBusy()) {
+ swipeLayout.setRefreshing(true);
+ } else {
+ boolean wasBusy = swipeLayout.isRefreshing();
+ swipeLayout.setRefreshing(false);
+ if (wasBusy) {
+ LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(REFRESH));
+ }
+ }
+ enableSwipeRefresh(true);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_charts);
+ int tabFragmentToOpen = -1;
+
+ initDates();
+
+ final IntentFilter filterLocal = new IntentFilter();
+ filterLocal.addAction(GBDevice.ACTION_DEVICE_CHANGED);
+ LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filterLocal);
+
+ final Bundle extras = getIntent().getExtras();
+ if (extras != null) {
+ mGBDevice = extras.getParcelable(GBDevice.EXTRA_DEVICE);
+ tabFragmentToOpen = extras.getInt(EXTRA_FRAGMENT_ID);
+ } else {
+ throw new IllegalArgumentException("Must provide a device when invoking this activity");
+ }
+ enabledTabsList = fillChartsTabsList();
+
+ swipeLayout = findViewById(R.id.activity_swipe_layout);
+ swipeLayout.setOnRefreshListener(this::fetchRecordedData);
+ enableSwipeRefresh(true);
+
+ // Set up the ViewPager with the sections adapter.
+ final NonSwipeableViewPager viewPager = findViewById(R.id.charts_pager);
+ viewPager.setAdapter(getPagerAdapter());
+ if (tabFragmentToOpen > -1) {
+ viewPager.setCurrentItem(tabFragmentToOpen); // open the tab as specified in the intent
+ }
+
+ viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ enableSwipeRefresh(state == ViewPager.SCROLL_STATE_IDLE);
+ }
+ });
+
+ dateBar = findViewById(R.id.charts_date_bar);
+ mDateControl = findViewById(R.id.charts_text_date);
+ mDateControl.setOnClickListener(v -> {
+ String detailedDuration = formatDetailedDuration();
+ new ShowDurationDialog(detailedDuration, AbstractChartsActivity.this).show();
+ });
+
+ Button mPrevButton = findViewById(R.id.charts_previous_day);
+ mPrevButton.setOnClickListener(v -> handleButtonClicked(DATE_PREV_DAY));
+ Button mNextButton = findViewById(R.id.charts_next_day);
+ mNextButton.setOnClickListener(v -> handleButtonClicked(DATE_NEXT_DAY));
+
+ Button mPrevWeekButton = findViewById(R.id.charts_previous_week);
+ mPrevWeekButton.setOnClickListener(v -> handleButtonClicked(DATE_PREV_WEEK));
+ Button mNextWeekButton = findViewById(R.id.charts_next_week);
+ mNextWeekButton.setOnClickListener(v -> handleButtonClicked(DATE_NEXT_WEEK));
+
+ Button mPrevMonthButton = findViewById(R.id.charts_previous_month);
+ mPrevMonthButton.setOnClickListener(v -> handleButtonClicked(DATE_PREV_MONTH));
+ Button mNextMonthButton = findViewById(R.id.charts_next_month);
+ mNextMonthButton.setOnClickListener(v -> handleButtonClicked(DATE_NEXT_MONTH));
+ }
+
+ protected abstract List fillChartsTabsList();
+
+ private String formatDetailedDuration() {
+ final SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm");
+ final String dateStringFrom = dateFormat.format(getStartDate());
+ final String dateStringTo = dateFormat.format(getEndDate());
+
+ return getString(R.string.sleep_activity_date_range, dateStringFrom, dateStringTo);
+ }
+
+ protected void initDates() {
+ setEndDate(new Date());
+ setStartDate(DateTimeUtils.shiftByDays(getEndDate(), -1));
+ }
+
+ @Override
+ public GBDevice getDevice() {
+ return mGBDevice;
+ }
+
+ @Override
+ public void setStartDate(Date startDate) {
+ mStartDate = startDate;
+ }
+
+ @Override
+ public void setEndDate(Date endDate) {
+ mEndDate = endDate;
+ }
+
+ @Override
+ public Date getStartDate() {
+ return mStartDate;
+ }
+
+ @Override
+ public Date getEndDate() {
+ return mEndDate;
+ }
+
+ @Override
+ public void setDateInfo(final String dateInfo) {
+ mDateControl.setText(dateInfo);
+ }
+
+ @Override
+ public ViewGroup getDateBar() {
+ return dateBar;
+ }
+
+ private void handleButtonClicked(final String action) {
+ LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(action));
+ }
+
+ @Override
+ protected void onDestroy() {
+ LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
+ super.onDestroy();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ getMenuInflater().inflate(R.menu.menu_charts, menu);
+
+ if (!mGBDevice.isConnected() || !supportsRefresh()) {
+ menu.removeItem(R.id.charts_fetch_activity_data);
+ }
+ return true;
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == REQUEST_CODE_PREFERENCES) {
+ this.recreate();
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.charts_fetch_activity_data:
+ fetchRecordedData();
+ return true;
+ case R.id.prefs_charts_menu:
+ Intent settingsIntent = new Intent(this, ChartsPreferencesActivity.class);
+ startActivityForResult(settingsIntent, REQUEST_CODE_PREFERENCES);
+ return true;
+ default:
+ break;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void enableSwipeRefresh(boolean enable) {
+ swipeLayout.setEnabled(enable && allowRefresh());
+ }
+
+ protected abstract boolean supportsRefresh();
+
+ protected abstract boolean allowRefresh();
+
+ protected abstract int getRecordedDataType();
+
+ private void fetchRecordedData() {
+ if (getDevice().isInitialized()) {
+ GBApplication.deviceService(getDevice()).onFetchRecordedData(getRecordedDataType());
+ } else {
+ swipeLayout.setRefreshing(false);
+ GB.toast(this, getString(R.string.device_not_connected), Toast.LENGTH_SHORT, GB.ERROR);
+ }
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractWeekChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractWeekChartFragment.java
index c14b34a3d..d8c22f75c 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractWeekChartFragment.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/AbstractWeekChartFragment.java
@@ -60,7 +60,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue;
-public abstract class AbstractWeekChartFragment extends AbstractChartFragment {
+public abstract class AbstractWeekChartFragment extends AbstractActivityChartFragment {
protected static final Logger LOG = LoggerFactory.getLogger(AbstractWeekChartFragment.class);
protected final int TOTAL_DAYS = getRangeDays();
protected int TOTAL_DAYS_FOR_AVERAGE = 0;
@@ -76,20 +76,18 @@ public abstract class AbstractWeekChartFragment extends AbstractChartFragment {
ImageView stepsStreaksButton;
@Override
- protected ChartsData refreshInBackground(ChartsHost chartsHost, DBHandler db, GBDevice device) {
+ protected MyChartsData refreshInBackground(ChartsHost chartsHost, DBHandler db, GBDevice device) {
Calendar day = Calendar.getInstance();
day.setTime(chartsHost.getEndDate());
//NB: we could have omitted the day, but this way we can move things to the past easily
DayData dayData = refreshDayPie(db, day, device);
- WeekChartsData weekBeforeData = refreshWeekBeforeData(db, mWeekChart, day, device);
+ WeekChartsData weekBeforeData = refreshWeekBeforeData(db, mWeekChart, day, device);
return new MyChartsData(dayData, weekBeforeData);
}
@Override
- protected void updateChartsnUIThread(ChartsData chartsData) {
- MyChartsData mcd = (MyChartsData) chartsData;
-
+ protected void updateChartsnUIThread(MyChartsData mcd) {
setupLegend(mWeekChart);
mTodayPieChart.setCenterText(mcd.getDayData().centerText);
mTodayPieChart.setData(mcd.getDayData().data);
@@ -353,7 +351,7 @@ public abstract class AbstractWeekChartFragment extends AbstractChartFragment {
}
}
- private static class MyChartsData extends ChartsData {
+ protected static class MyChartsData extends ChartsData {
private final WeekChartsData weekBeforeData;
private final DayData dayData;
@@ -379,7 +377,7 @@ public abstract class AbstractWeekChartFragment extends AbstractChartFragment {
Activity activity = getActivity();
int key = (int) (day.getTimeInMillis() / 1000) + (mOffsetHours * 3600);
if (activity != null) {
- activityAmountCache = ((ChartsActivity) activity).mActivityAmountCache;
+ activityAmountCache = ((ActivityChartsActivity) activity).mActivityAmountCache;
amounts = (ActivityAmounts) (activityAmountCache.lookup(key));
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityChartsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityChartsActivity.java
new file mode 100644
index 000000000..3e3be81e1
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityChartsActivity.java
@@ -0,0 +1,167 @@
+/* Copyright (C) 2015-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
+ Gobbetti, vanous, Vebryn
+
+ 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.charts;
+
+import android.content.Context;
+
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentStatePagerAdapter;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import nodomain.freeyourgadget.gadgetbridge.GBApplication;
+import nodomain.freeyourgadget.gadgetbridge.R;
+import nodomain.freeyourgadget.gadgetbridge.activities.AbstractFragmentPagerAdapter;
+import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
+import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
+import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
+import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes;
+import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
+import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue;
+import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
+
+public class ActivityChartsActivity extends AbstractChartsActivity {
+ LimitedQueue mActivityAmountCache = new LimitedQueue(60);
+
+ @Override
+ protected AbstractFragmentPagerAdapter createFragmentPagerAdapter(final FragmentManager fragmentManager) {
+ return new SectionsPagerAdapter(fragmentManager);
+ }
+
+ @Override
+ protected int getRecordedDataType() {
+ return RecordedDataTypes.TYPE_ACTIVITY;
+ }
+
+ @Override
+ protected boolean supportsRefresh() {
+ final DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(getDevice());
+ return coordinator.supportsActivityDataFetching();
+ }
+
+ @Override
+ protected boolean allowRefresh() {
+ final DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(getDevice());
+ return coordinator.allowFetchActivityData(getDevice()) && supportsRefresh();
+ }
+
+ @Override
+ protected List fillChartsTabsList() {
+ return fillChartsTabsList(getDevice(), this);
+ }
+
+ private static List fillChartsTabsList(final GBDevice device, final Context context) {
+ final List tabList;
+ final Prefs prefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(device.getAddress()));
+ final String myTabs = prefs.getString(DeviceSettingsPreferenceConst.PREFS_DEVICE_CHARTS_TABS, null);
+
+ if (myTabs == null) {
+ //make list mutable to be able to remove items later
+ tabList = new ArrayList<>(Arrays.asList(context.getResources().getStringArray(R.array.pref_charts_tabs_items_default)));
+ } else {
+ tabList = new ArrayList<>(Arrays.asList(myTabs.split(",")));
+ }
+ final DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device);
+ if (!coordinator.supportsRealtimeData()) {
+ tabList.remove("livestats");
+ }
+ return tabList;
+ }
+
+ public static int getChartsTabIndex(final String tab, final GBDevice device, final Context context) {
+ final List enabledTabsList = fillChartsTabsList(device, context);
+ return enabledTabsList.indexOf(tab);
+ }
+
+ /**
+ * A {@link FragmentStatePagerAdapter} that returns a fragment corresponding to
+ * one of the sections/tabs/pages.
+ */
+ private class SectionsPagerAdapter extends AbstractFragmentPagerAdapter {
+ SectionsPagerAdapter(FragmentManager fm) {
+ super(fm);
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ // getItem is called to instantiate the fragment for the given page.
+ switch (enabledTabsList.get(position)) {
+ case "activity":
+ return new ActivitySleepChartFragment();
+ case "activitylist":
+ return new ActivityListingChartFragment();
+ case "sleep":
+ return new SleepChartFragment();
+ case "sleepweek":
+ return new WeekSleepChartFragment();
+ case "stepsweek":
+ return new WeekStepsChartFragment();
+ case "speedzones":
+ return new SpeedZonesFragment();
+ case "livestats":
+ return new LiveActivityFragment();
+ }
+ return null;
+ }
+
+ @Override
+ public int getCount() {
+ return enabledTabsList.toArray().length;
+ }
+
+ private String getSleepTitle() {
+ if (GBApplication.getPrefs().getBoolean("charts_range", true)) {
+ return getString(R.string.weeksleepchart_sleep_a_month);
+ } else {
+ return getString(R.string.weeksleepchart_sleep_a_week);
+ }
+ }
+
+ public String getStepsTitle() {
+ if (GBApplication.getPrefs().getBoolean("charts_range", true)) {
+ return getString(R.string.weekstepschart_steps_a_month);
+ } else {
+ return getString(R.string.weekstepschart_steps_a_week);
+ }
+ }
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ switch (enabledTabsList.get(position)) {
+ case "activity":
+ return getString(R.string.activity_sleepchart_activity_and_sleep);
+ case "activitylist":
+ return getString(R.string.charts_activity_list);
+ case "sleep":
+ return getString(R.string.sleepchart_your_sleep);
+ case "sleepweek":
+ return getSleepTitle();
+ case "stepsweek":
+ return getStepsTitle();
+ case "speedzones":
+ return getString(R.string.stats_title);
+ case "livestats":
+ return getString(R.string.liveactivity_live_activity);
+ }
+ return super.getPageTitle(position);
+ }
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityListingChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityListingChartFragment.java
index 386c9a0d5..7fc773038 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityListingChartFragment.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivityListingChartFragment.java
@@ -40,7 +40,6 @@ import org.slf4j.LoggerFactory;
import java.util.Calendar;
import java.util.Date;
-import java.util.GregorianCalendar;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
@@ -51,7 +50,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySession;
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
-public class ActivityListingChartFragment extends AbstractChartFragment {
+public class ActivityListingChartFragment extends AbstractActivityChartFragment {
protected static final Logger LOG = LoggerFactory.getLogger(ActivityListingChartFragment.class);
int tsDateTo;
@@ -114,7 +113,7 @@ public class ActivityListingChartFragment extends AbstractChartFragment {
}
@Override
- protected ChartsData refreshInBackground(ChartsHost chartsHost, DBHandler db, GBDevice device) {
+ protected MyChartsData refreshInBackground(ChartsHost chartsHost, DBHandler db, GBDevice device) {
List extends ActivitySample> activitySamples;
activitySamples = getSamples(db, device);
List stepSessions = null;
@@ -138,16 +137,14 @@ public class ActivityListingChartFragment extends AbstractChartFragment {
}
@Override
- protected void updateChartsnUIThread(ChartsData chartsData) {
- MyChartsData mcd = (MyChartsData) chartsData;
-
+ protected void updateChartsnUIThread(MyChartsData mcd) {
if (mcd == null) {
return;
}
if (mcd.getStepSessions() == null) {
return;
}
-
+
if (mcd.getStepSessions().toArray().length == 0) {
getChartsHost().enableSwipeRefresh(true); //enable pull to refresh, might be needed
} else {
@@ -170,7 +167,7 @@ public class ActivityListingChartFragment extends AbstractChartFragment {
}
@Override
- protected void setupLegend(Chart chart) {
+ protected void setupLegend(Chart> chart) {
}
@Override
@@ -200,7 +197,7 @@ public class ActivityListingChartFragment extends AbstractChartFragment {
final Snackbar snackbar = Snackbar.make(rootView, text, 1000 * 8);
View snackbarView = snackbar.getView();
- snackbarView.setBackgroundColor(getContext().getResources().getColor(R.color.accent));
+ snackbarView.setBackgroundColor(requireContext().getResources().getColor(R.color.accent));
snackbar.setActionTextColor(Color.WHITE);
snackbar.setAction(getString(R.string.dialog_hide).toUpperCase(), new View.OnClickListener() {
@Override
@@ -213,18 +210,18 @@ public class ActivityListingChartFragment extends AbstractChartFragment {
}
private void showDashboard(int date, GBDevice device) {
- FragmentManager fm = getActivity().getSupportFragmentManager();
+ FragmentManager fm = requireActivity().getSupportFragmentManager();
ActivityListingDashboard listingDashboardFragment = ActivityListingDashboard.newInstance(date, device);
listingDashboardFragment.show(fm, "activity_list_total_dashboard");
}
private void showDetail(int tsFrom, int tsTo, ActivitySession item, GBDevice device) {
- FragmentManager fm = getActivity().getSupportFragmentManager();
+ FragmentManager fm = requireActivity().getSupportFragmentManager();
ActivityListingDetail listingDetailFragment = ActivityListingDetail.newInstance(tsFrom, tsTo, item, device);
listingDetailFragment.show(fm, "activity_list_detail");
}
- private static class MyChartsData extends ChartsData {
+ protected static final class MyChartsData extends ChartsData {
private final List stepSessions;
private final ActivitySession ongoingSession;
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivitySleepChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivitySleepChartFragment.java
index 1504e8640..8b42186b9 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivitySleepChartFragment.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ActivitySleepChartFragment.java
@@ -46,7 +46,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
-public class ActivitySleepChartFragment extends AbstractChartFragment {
+public class ActivitySleepChartFragment extends AbstractActivityChartFragment> {
protected static final Logger LOG = LoggerFactory.getLogger(ActivitySleepChartFragment.class);
private LineChart mChart;
@@ -127,14 +127,13 @@ public class ActivitySleepChartFragment extends AbstractChartFragment {
}
@Override
- protected ChartsData refreshInBackground(ChartsHost chartsHost, DBHandler db, GBDevice device) {
+ protected DefaultChartsData refreshInBackground(ChartsHost chartsHost, DBHandler db, GBDevice device) {
List extends ActivitySample> samples = getSamples(db, device);
return refresh(device, samples);
}
@Override
- protected void updateChartsnUIThread(ChartsData chartsData) {
- DefaultChartsData dcd = (DefaultChartsData) chartsData;
+ protected void updateChartsnUIThread(DefaultChartsData dcd) {
mChart.getLegend().setTextColor(LEGEND_TEXT_COLOR);
mChart.setData(null); // workaround for https://github.com/PhilJay/MPAndroidChart/issues/2317
mChart.getXAxis().setValueFormatter(dcd.getXValueFormatter());
@@ -148,7 +147,7 @@ public class ActivitySleepChartFragment extends AbstractChartFragment {
}
@Override
- protected void setupLegend(Chart chart) {
+ protected void setupLegend(Chart> chart) {
List legendEntries = new ArrayList<>(5);
LegendEntry activityEntry = new LegendEntry();
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsActivity.java
deleted file mode 100644
index 2ec238169..000000000
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsActivity.java
+++ /dev/null
@@ -1,435 +0,0 @@
-/* Copyright (C) 2015-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
- Gobbetti, vanous, Vebryn
-
- 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.charts;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentManager;
-import androidx.fragment.app.FragmentStatePagerAdapter;
-import androidx.localbroadcastmanager.content.LocalBroadcastManager;
-import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
-import androidx.viewpager.widget.ViewPager;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.Objects;
-
-import nodomain.freeyourgadget.gadgetbridge.GBApplication;
-import nodomain.freeyourgadget.gadgetbridge.R;
-import nodomain.freeyourgadget.gadgetbridge.activities.AbstractFragmentPagerAdapter;
-import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBFragmentActivity;
-import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
-import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
-import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
-import nodomain.freeyourgadget.gadgetbridge.model.RecordedDataTypes;
-import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
-import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper;
-import nodomain.freeyourgadget.gadgetbridge.util.GB;
-import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue;
-import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
-
-public class ChartsActivity extends AbstractGBFragmentActivity implements ChartsHost {
- private static final Logger LOG = LoggerFactory.getLogger(ChartsActivity.class);
- public static final String EXTRA_FRAGMENT_ID = "fragment";
-
- private TextView mDateControl;
-
- private Date mStartDate;
- private Date mEndDate;
- private SwipeRefreshLayout swipeLayout;
-
- LimitedQueue mActivityAmountCache = new LimitedQueue(60);
- ArrayList enabledTabsList;
-
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- switch (Objects.requireNonNull(action)) {
- case GBDevice.ACTION_DEVICE_CHANGED:
- GBDevice dev = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE);
- refreshBusyState(dev);
- break;
- }
- }
- };
- private GBDevice mGBDevice;
- private ViewGroup dateBar;
-
- private void refreshBusyState(GBDevice dev) {
- if (dev.isBusy()) {
- swipeLayout.setRefreshing(true);
- } else {
- boolean wasBusy = swipeLayout.isRefreshing();
- swipeLayout.setRefreshing(false);
- if (wasBusy) {
- LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(REFRESH));
- }
- }
- enableSwipeRefresh(true);
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_charts);
- int tabFragmentToOpen = -1;
-
- initDates();
-
- IntentFilter filterLocal = new IntentFilter();
- filterLocal.addAction(GBDevice.ACTION_DEVICE_CHANGED);
- LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filterLocal);
-
- Bundle extras = getIntent().getExtras();
- if (extras != null) {
- mGBDevice = extras.getParcelable(GBDevice.EXTRA_DEVICE);
- tabFragmentToOpen = extras.getInt(EXTRA_FRAGMENT_ID);
-
- } else {
- throw new IllegalArgumentException("Must provide a device when invoking this activity");
- }
- enabledTabsList = fillChartsTabsList(getDevice(), this);
-
- swipeLayout = findViewById(R.id.activity_swipe_layout);
- swipeLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
- @Override
- public void onRefresh() {
- fetchActivityData();
- }
- });
- enableSwipeRefresh(true);
-
- // Set up the ViewPager with the sections adapter.
- NonSwipeableViewPager viewPager = findViewById(R.id.charts_pager);
- viewPager.setAdapter(getPagerAdapter());
- if (tabFragmentToOpen > -1) {
- viewPager.setCurrentItem(tabFragmentToOpen); //open the tab as specified in the intent
- }
-
- viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
- @Override
- public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
- }
-
- @Override
- public void onPageSelected(int position) {
- }
-
- @Override
- public void onPageScrollStateChanged(int state) {
- enableSwipeRefresh(state == ViewPager.SCROLL_STATE_IDLE);
- }
- });
-
- dateBar = findViewById(R.id.charts_date_bar);
- mDateControl = findViewById(R.id.charts_text_date);
- mDateControl.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- String detailedDuration = formatDetailedDuration();
- new ShowDurationDialog(detailedDuration, ChartsActivity.this).show();
- }
- });
-
- Button mPrevButton = findViewById(R.id.charts_previous_day);
- mPrevButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- handleButtonClicked(DATE_PREV_DAY);
- }
- });
- Button mNextButton = findViewById(R.id.charts_next_day);
- mNextButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- handleButtonClicked(DATE_NEXT_DAY);
- }
- });
-
- Button mPrevWeekButton = findViewById(R.id.charts_previous_week);
- mPrevWeekButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- handleButtonClicked(DATE_PREV_WEEK);
- }
- });
- Button mNextWeekButton = findViewById(R.id.charts_next_week);
- mNextWeekButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- handleButtonClicked(DATE_NEXT_WEEK);
- }
- });
-
- Button mPrevMonthButton = findViewById(R.id.charts_previous_month);
- mPrevMonthButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- handleButtonClicked(DATE_PREV_MONTH);
- }
- });
- Button mNextMonthButton = findViewById(R.id.charts_next_month);
- mNextMonthButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- handleButtonClicked(DATE_NEXT_MONTH);
- }
- });
-
-
- }
-
- private static ArrayList fillChartsTabsList(GBDevice device, Context context) {
- ArrayList arrayList = new ArrayList();
- Prefs prefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(device.getAddress()));
- String myTabs = prefs.getString(DeviceSettingsPreferenceConst.PREFS_DEVICE_CHARTS_TABS, null);
-
- if (myTabs == null) {
- //make list mutable to be able to remove items later
- arrayList = new ArrayList(Arrays.asList(context.getResources().getStringArray(R.array.pref_charts_tabs_items_default)));
- } else {
- arrayList = new ArrayList(Arrays.asList(myTabs.split(",")));
- }
- DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(device);
- if (!coordinator.supportsRealtimeData()) {
- arrayList.remove("livestats");
- }
- return arrayList;
- }
-
- public static int getChartsTabIndex(String tab, GBDevice device, Context context) {
- ArrayList enabledTabsList = new ArrayList();
- enabledTabsList = fillChartsTabsList(device, context);
- return enabledTabsList.indexOf(tab);
- }
-
- private String formatDetailedDuration() {
- SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm");
- String dateStringFrom = dateFormat.format(getStartDate());
- String dateStringTo = dateFormat.format(getEndDate());
-
- return getString(R.string.sleep_activity_date_range, dateStringFrom, dateStringTo);
- }
-
- protected void initDates() {
- setEndDate(new Date());
- setStartDate(DateTimeUtils.shiftByDays(getEndDate(), -1));
- }
-
- @Override
- public GBDevice getDevice() {
- return mGBDevice;
- }
-
- @Override
- public void setStartDate(Date startDate) {
- mStartDate = startDate;
- }
-
- @Override
- public void setEndDate(Date endDate) {
- mEndDate = endDate;
- }
-
- @Override
- public Date getStartDate() {
- return mStartDate;
- }
-
- @Override
- public Date getEndDate() {
- return mEndDate;
- }
-
- private void handleButtonClicked(String Action) {
- LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(Action));
- }
-
- @Override
- protected void onDestroy() {
- LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
- super.onDestroy();
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
- getMenuInflater().inflate(R.menu.menu_charts, menu);
-
- DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(mGBDevice);
- if (!mGBDevice.isConnected() || !coordinator.supportsActivityDataFetching()) {
- menu.removeItem(R.id.charts_fetch_activity_data);
- }
- return true;
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (requestCode == 1) {
- this.recreate();
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.charts_fetch_activity_data:
- fetchActivityData();
- return true;
- case R.id.prefs_charts_menu:
- Intent settingsIntent = new Intent(this, ChartsPreferencesActivity.class);
- startActivityForResult(settingsIntent,1);
- return true;
- default:
- break;
- }
-
- return super.onOptionsItemSelected(item);
- }
-
- @Override
- public void enableSwipeRefresh(boolean enable) {
- DeviceCoordinator coordinator = DeviceHelper.getInstance().getCoordinator(mGBDevice);
- swipeLayout.setEnabled(enable && coordinator.allowFetchActivityData(mGBDevice));
- }
-
- private void fetchActivityData() {
- if (getDevice().isInitialized()) {
- GBApplication.deviceService(getDevice()).onFetchRecordedData(RecordedDataTypes.TYPE_ACTIVITY);
- } else {
- swipeLayout.setRefreshing(false);
- GB.toast(this, getString(R.string.device_not_connected), Toast.LENGTH_SHORT, GB.ERROR);
- }
- }
-
- @Override
- public void setDateInfo(String dateInfo) {
- mDateControl.setText(dateInfo);
- }
-
- @Override
- protected AbstractFragmentPagerAdapter createFragmentPagerAdapter(FragmentManager fragmentManager) {
- return new SectionsPagerAdapter(fragmentManager);
- }
-
- @Override
- public ViewGroup getDateBar() {
- return dateBar;
- }
-
-
-
-
- /**
- * A {@link FragmentStatePagerAdapter} that returns a fragment corresponding to
- * one of the sections/tabs/pages.
- */
- public class SectionsPagerAdapter extends AbstractFragmentPagerAdapter {
- SectionsPagerAdapter(FragmentManager fm) {
- super(fm);
- }
-
-
- @Override
- public Fragment getItem(int position) {
- // getItem is called to instantiate the fragment for the given page.
- switch (enabledTabsList.get(position)) {
- case "activity":
- return new ActivitySleepChartFragment();
- case "activitylist":
- return new ActivityListingChartFragment();
- case "sleep":
- return new SleepChartFragment();
- case "sleepweek":
- return new WeekSleepChartFragment();
- case "stepsweek":
- return new WeekStepsChartFragment();
- case "speedzones":
- return new SpeedZonesFragment();
- case "livestats":
- return new LiveActivityFragment();
- }
- return null;
- }
-
- @Override
- public int getCount() {
- return enabledTabsList.toArray().length;
- }
-
- private String getSleepTitle() {
- if (GBApplication.getPrefs().getBoolean("charts_range", true)) {
- return getString(R.string.weeksleepchart_sleep_a_month);
- }
- else{
- return getString(R.string.weeksleepchart_sleep_a_week);
- }
- }
-
- public String getStepsTitle() {
- if (GBApplication.getPrefs().getBoolean("charts_range", true)) {
- return getString(R.string.weekstepschart_steps_a_month);
- }
- else{
- return getString(R.string.weekstepschart_steps_a_week);
- }
- }
-
- @Override
- public CharSequence getPageTitle(int position) {
-
- switch (enabledTabsList.get(position)) {
- case "activity":
- return getString(R.string.activity_sleepchart_activity_and_sleep);
- case "activitylist":
- return getString(R.string.charts_activity_list);
- case "sleep":
- return getString(R.string.sleepchart_your_sleep);
- case "sleepweek":
- return getSleepTitle();
- case "stepsweek":
- return getStepsTitle();
- case "speedzones":
- return getString(R.string.stats_title);
- case "livestats":
- return getString(R.string.liveactivity_live_activity);
- }
- return super.getPageTitle(position);
- }
- }
-}
-
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsHost.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsHost.java
index 8f93350d6..f1f1d257f 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsHost.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/ChartsHost.java
@@ -23,15 +23,14 @@ import java.util.Date;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
public interface ChartsHost {
- String DATE_PREV_DAY = ChartsActivity.class.getName().concat(".date_prev_day");
- String DATE_NEXT_DAY = ChartsActivity.class.getName().concat(".date_next_day");
- String DATE_PREV_WEEK = ChartsActivity.class.getName().concat(".date_prev_week");
- String DATE_NEXT_WEEK = ChartsActivity.class.getName().concat(".date_next_week");
- String DATE_PREV_MONTH = ChartsActivity.class.getName().concat(".date_prev_month");
- String DATE_NEXT_MONTH = ChartsActivity.class.getName().concat(".date_next_month");
+ String DATE_PREV_DAY = ChartsHost.class.getName().concat(".date_prev_day");
+ String DATE_NEXT_DAY = ChartsHost.class.getName().concat(".date_next_day");
+ String DATE_PREV_WEEK = ChartsHost.class.getName().concat(".date_prev_week");
+ String DATE_NEXT_WEEK = ChartsHost.class.getName().concat(".date_next_week");
+ String DATE_PREV_MONTH = ChartsHost.class.getName().concat(".date_prev_month");
+ String DATE_NEXT_MONTH = ChartsHost.class.getName().concat(".date_next_month");
-
- String REFRESH = ChartsActivity.class.getName().concat(".refresh");
+ String REFRESH = ChartsHost.class.getName().concat(".refresh");
GBDevice getDevice();
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java
index 2f7489c15..8e3cec074 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/LiveActivityFragment.java
@@ -66,7 +66,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
-public class LiveActivityFragment extends AbstractChartFragment {
+public class LiveActivityFragment extends AbstractActivityChartFragment {
private static final Logger LOG = LoggerFactory.getLogger(LiveActivityFragment.class);
private static final int MAX_STEPS_PER_MINUTE = 300;
private static final int MIN_STEPS_PER_MINUTE = 60;
@@ -533,7 +533,7 @@ public class LiveActivityFragment extends AbstractChartFragment {
}
@Override
- protected void setupLegend(Chart chart) {
+ protected void setupLegend(Chart> chart) {
// no legend
}
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepChartFragment.java
index 2d571137b..345030990 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepChartFragment.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SleepChartFragment.java
@@ -67,7 +67,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
-public class SleepChartFragment extends AbstractChartFragment {
+public class SleepChartFragment extends AbstractActivityChartFragment {
protected static final Logger LOG = LoggerFactory.getLogger(ActivitySleepChartFragment.class);
private LineChart mActivityChart;
@@ -94,7 +94,7 @@ public class SleepChartFragment extends AbstractChartFragment {
@Override
- protected ChartsData refreshInBackground(ChartsHost chartsHost, DBHandler db, GBDevice device) {
+ protected MyChartsData refreshInBackground(ChartsHost chartsHost, DBHandler db, GBDevice device) {
List extends ActivitySample> samples;
if (CHARTS_SLEEP_RANGE_24H) {
samples = getSamples(db, device);
@@ -117,7 +117,7 @@ public class SleepChartFragment extends AbstractChartFragment {
}
}
}
- DefaultChartsData chartsData = refresh(device, samples);
+ DefaultChartsData chartsData = refresh(device, samples);
Triple hrData = calculateHrData(samples);
Triple intensityData = calculateIntensityData(samples);
return new MyChartsData(mySleepChartsData, chartsData, hrData.getLeft(), hrData.getMiddle(), hrData.getRight(), intensityData.getLeft(), intensityData.getMiddle(), intensityData.getRight());
@@ -197,8 +197,7 @@ public class SleepChartFragment extends AbstractChartFragment {
}
@Override
- protected void updateChartsnUIThread(ChartsData chartsData) {
- MyChartsData mcd = (MyChartsData) chartsData;
+ protected void updateChartsnUIThread(MyChartsData mcd) {
MySleepChartsData pieData = mcd.getPieData();
mSleepAmountChart.setCenterText(pieData.getTotalSleep());
mSleepAmountChart.setData(pieData.getPieData());
@@ -420,7 +419,7 @@ public class SleepChartFragment extends AbstractChartFragment {
}
@Override
- protected void setupLegend(Chart chart) {
+ protected void setupLegend(Chart> chart) {
List legendEntries = new ArrayList<>(3);
LegendEntry lightSleepEntry = new LegendEntry();
lightSleepEntry.label = akLightSleep.label;
@@ -497,7 +496,7 @@ public class SleepChartFragment extends AbstractChartFragment {
}
}
- private static class MyChartsData extends ChartsData {
+ protected static class MyChartsData extends ChartsData {
private final DefaultChartsData chartsData;
private final MySleepChartsData pieData;
private final float heartRateAverage;
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SpeedZonesFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SpeedZonesFragment.java
index e0f24db43..eaea07b73 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SpeedZonesFragment.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/SpeedZonesFragment.java
@@ -45,7 +45,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
-public class SpeedZonesFragment extends AbstractChartFragment {
+public class SpeedZonesFragment extends AbstractActivityChartFragment {
protected static final Logger LOG = LoggerFactory.getLogger(SpeedZonesFragment.class);
private HorizontalBarChart mStatsChart;
@@ -139,7 +139,7 @@ public class SpeedZonesFragment extends AbstractChartFragment {
}
@Override
- protected void setupLegend(Chart chart) {
+ protected void setupLegend(Chart> chart) {
// no legend here, it is all about the steps here
chart.getLegend().setEnabled(false);
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekSleepChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekSleepChartFragment.java
index d893e1727..3c79cf0a6 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekSleepChartFragment.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekSleepChartFragment.java
@@ -170,7 +170,7 @@ public class WeekSleepChartFragment extends AbstractWeekChartFragment {
}
@Override
- protected void setupLegend(Chart chart) {
+ protected void setupLegend(Chart> chart) {
List legendEntries = new ArrayList<>(2);
LegendEntry lightSleepEntry = new LegendEntry();
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekStepsChartFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekStepsChartFragment.java
index b31e18832..35547b401 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekStepsChartFragment.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/charts/WeekStepsChartFragment.java
@@ -101,7 +101,7 @@ public class WeekStepsChartFragment extends AbstractWeekChartFragment {
}
@Override
- protected void setupLegend(Chart chart) {
+ protected void setupLegend(Chart> chart) {
// no legend here, it is all about the steps here
chart.getLegend().setEnabled(false);
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapterv2.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapterv2.java
index 2e71223b9..c37971f2f 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapterv2.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapterv2.java
@@ -96,7 +96,7 @@ import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2;
import nodomain.freeyourgadget.gadgetbridge.activities.HeartRateDialog;
import nodomain.freeyourgadget.gadgetbridge.activities.OpenFwAppInstallerActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.VibrationActivity;
-import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsActivity;
+import nodomain.freeyourgadget.gadgetbridge.activities.charts.ActivityChartsActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
@@ -496,7 +496,7 @@ public class GBDeviceAdapterv2 extends ListAdapter> activitiesStatusMiniCharts = new Hashtable<>();
- activitiesStatusMiniCharts.put(holder.TotalStepsChart, new Pair<>(showActivitySteps && steps > 0, ChartsActivity.getChartsTabIndex("stepsweek", device, context)));
- activitiesStatusMiniCharts.put(holder.SleepTimeChart, new Pair<>(showActivitySleep && sleep > 0, ChartsActivity.getChartsTabIndex("sleep", device, context)));
- activitiesStatusMiniCharts.put(holder.TotalDistanceChart, new Pair<>(showActivityDistance && steps > 0, ChartsActivity.getChartsTabIndex("activity", device, context)));
+ activitiesStatusMiniCharts.put(holder.TotalStepsChart, new Pair<>(showActivitySteps && steps > 0, ActivityChartsActivity.getChartsTabIndex("stepsweek", device, context)));
+ activitiesStatusMiniCharts.put(holder.SleepTimeChart, new Pair<>(showActivitySleep && sleep > 0, ActivityChartsActivity.getChartsTabIndex("sleep", device, context)));
+ activitiesStatusMiniCharts.put(holder.TotalDistanceChart, new Pair<>(showActivityDistance && steps > 0, ActivityChartsActivity.getChartsTabIndex("activity", device, context)));
for (Map.Entry> miniCharts : activitiesStatusMiniCharts.entrySet()) {
PieChart miniChart = miniCharts.getKey();
@@ -1327,9 +1327,9 @@ public class GBDeviceAdapterv2 extends ListAdapter
+ tools:context="nodomain.freeyourgadget.gadgetbridge.activities.charts.ActivityChartsActivity$PlaceholderFragment">
+ tools:context="nodomain.freeyourgadget.gadgetbridge.activities.charts.ActivityChartsActivity$PlaceholderFragment">
+ tools:context="nodomain.freeyourgadget.gadgetbridge.activities.charts.ActivityChartsActivity">
+ tools:context="nodomain.freeyourgadget.gadgetbridge.activities.charts.ActivityChartsActivity">
+ tools:context="nodomain.freeyourgadget.gadgetbridge.activities.charts.ActivityChartsActivity$PlaceholderFragment">
+ tools:context="nodomain.freeyourgadget.gadgetbridge.activities.charts.ActivityChartsActivity$PlaceholderFragment">
+ tools:context="nodomain.freeyourgadget.gadgetbridge.activities.charts.ActivityChartsActivity$PlaceholderFragment">
+ tools:context="nodomain.freeyourgadget.gadgetbridge.activities.charts.ActivityChartsActivity$PlaceholderFragment">
+ tools:context="nodomain.freeyourgadget.gadgetbridge.activities.charts.ActivityChartsActivity$PlaceholderFragment">
+ tools:context="nodomain.freeyourgadget.gadgetbridge.activities.charts.ActivityChartsActivity$PlaceholderFragment">
+ tools:context="nodomain.freeyourgadget.gadgetbridge.activities.charts.ActivityChartsActivity">