Work in progress #45

Comparison between Light Sleep and Deep Sleep.
(Currently wrong, needs more analysis)
This commit is contained in:
cpfeiffer 2015-07-14 00:29:32 +02:00
parent b1e2671bec
commit d498bd976a
11 changed files with 296 additions and 60 deletions

View File

@ -33,6 +33,9 @@ public class GBActivitySample {
this.type = type;
}
/**
* Timestamp of the sample, resolution is seconds!
*/
public int getTimestamp() {
return timestamp;
}

View File

@ -133,14 +133,11 @@ public abstract class AbstractChartFragment extends Fragment {
chart.setDrawGridBackground(false);
}
protected void refresh(GBDevice mGBDevice, BarLineChartBase chart) {
if (mGBDevice == null) {
protected void refresh(GBDevice gbDevice, BarLineChartBase chart, List<GBActivitySample> samples) {
if (gbDevice == null) {
return;
}
// ArrayList<GBActivitySample> samples = getTestSamples(mGBDevice, -1, -1);
List<GBActivitySample> samples = getSamples(mGBDevice, -1, -1);
Calendar cal = GregorianCalendar.getInstance();
cal.clear();
Date date;
@ -152,7 +149,7 @@ public abstract class AbstractChartFragment extends Fragment {
float movement_divisor;
boolean annotate = true;
boolean use_steps_as_movement;
switch (getProvider(mGBDevice)) {
switch (getProvider(gbDevice)) {
case GBActivitySample.PROVIDER_MIBAND:
// maybe this should be configurable 256 seems way off, though.
movement_divisor = 180.0f; //256.0f;
@ -278,7 +275,7 @@ public abstract class AbstractChartFragment extends Fragment {
protected abstract List<GBActivitySample> getSamples(GBDevice device, int tsFrom, int tsTo);
protected abstract void setupLegend(BarLineChartBase chart);
protected abstract void setupLegend(Chart chart);
protected BarEntry createBarEntry(float value, int index) {
return new BarEntry(value, index);

View File

@ -11,6 +11,7 @@ import android.view.View;
import android.view.ViewGroup;
import com.github.mikephil.charting.charts.BarLineChartBase;
import com.github.mikephil.charting.charts.Chart;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.components.YAxis;
@ -47,7 +48,8 @@ public class ActivitySleepChartFragment extends AbstractChartFragment {
mSmartAlarmTo = intent.getIntExtra("smartalarm_to", -1);
mTimestampFrom = intent.getIntExtra("recording_base_timestamp", -1);
mSmartAlarmGoneOff = intent.getIntExtra("alarm_gone_off", -1);
refresh(mGBDevice, mChart);
List<GBActivitySample> samples = getSamples(mGBDevice, -1, -1);
refresh(mGBDevice, mChart, samples);
}
}
};
@ -68,7 +70,7 @@ public class ActivitySleepChartFragment extends AbstractChartFragment {
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(mReceiver, filter);
mChart = (BarLineChartBase) rootView.findViewById(R.id.sleepchart);
mChart = (BarLineChartBase) rootView.findViewById(R.id.activitysleepchart);
setupChart();
@ -106,7 +108,8 @@ public class ActivitySleepChartFragment extends AbstractChartFragment {
yAxisRight.setDrawTopYLabelEntry(false);
yAxisRight.setTextColor(CHART_TEXT_COLOR);
refresh(mGBDevice, mChart);
List<GBActivitySample> samples = getSamples(mGBDevice, -1, -1);
refresh(mGBDevice, mChart, samples);
mChart.getLegend().setTextColor(LEGEND_TEXT_COLOR);
// mChart.getLegend().setEnabled(false);
@ -123,7 +126,7 @@ public class ActivitySleepChartFragment extends AbstractChartFragment {
super.onDestroy();
}
protected void setupLegend(BarLineChartBase chart) {
protected void setupLegend(Chart chart) {
List<Integer> legendColors = new ArrayList<>(3);
List<String> legendLabels = new ArrayList<>(3);
legendColors.add(akActivity.color);

View File

@ -11,8 +11,13 @@ import android.view.View;
import android.view.ViewGroup;
import com.github.mikephil.charting.charts.BarLineChartBase;
import com.github.mikephil.charting.charts.Chart;
import com.github.mikephil.charting.charts.PieChart;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.PieData;
import com.github.mikephil.charting.data.PieDataSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -24,12 +29,16 @@ import nodomain.freeyourgadget.gadgetbridge.ControlCenter;
import nodomain.freeyourgadget.gadgetbridge.GBActivitySample;
import nodomain.freeyourgadget.gadgetbridge.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.charts.ActivityAmount;
import nodomain.freeyourgadget.gadgetbridge.charts.ActivityAmounts;
import nodomain.freeyourgadget.gadgetbridge.charts.ActivityAnalysis;
public class SleepChartFragment extends AbstractChartFragment {
protected static final Logger LOG = LoggerFactory.getLogger(ActivitySleepChartFragment.class);
private BarLineChartBase mChart;
private BarLineChartBase mActivityChart;
private PieChart mSleepAmountChart;
private int mSmartAlarmFrom = -1;
private int mSmartAlarmTo = -1;
@ -47,15 +56,62 @@ public class SleepChartFragment extends AbstractChartFragment {
mSmartAlarmTo = intent.getIntExtra("smartalarm_to", -1);
mTimestampFrom = intent.getIntExtra("recording_base_timestamp", -1);
mSmartAlarmGoneOff = intent.getIntExtra("alarm_gone_off", -1);
refresh(mGBDevice, mChart);
refresh();
}
}
};
private void refresh() {
List<GBActivitySample> samples = getSamples();
refresh(mGBDevice, mActivityChart, getSamples());
refreshSleepAmounts(mGBDevice, mSleepAmountChart, samples);
mActivityChart.invalidate();
mSleepAmountChart.invalidate();
}
private List<GBActivitySample> getSamples() {
return getSamples(mGBDevice, -1, -1);
}
private void refreshSleepAmounts(GBDevice mGBDevice, PieChart pieChart, List<GBActivitySample> samples) {
ActivityAnalysis analysis = new ActivityAnalysis();
ActivityAmounts amounts = analysis.calculateActivityAmounts(samples);
float hoursOfSleep = amounts.getTotalSeconds() / (float) (60 * 60);
pieChart.setCenterText((int)hoursOfSleep + "h"); // FIXME
PieData data = new PieData();
List<Entry> entries = new ArrayList<>();
List<Integer> colors = new ArrayList<>();
int index = 0;
for (ActivityAmount amount : amounts.getAmounts()) {
entries.add(new Entry(amount.getTotalSeconds(), index++));
colors.add(getColorFor(amount.getActivityKind()));
data.addXValue(amount.getName(getActivity()));
}
PieDataSet set = new PieDataSet(entries, "Sleep comparison");
set.setColors(colors);
data.setDataSet(set);
pieChart.setData(data);
setupLegend(pieChart);
pieChart.invalidate();
}
private Integer getColorFor(int activityKind) {
switch (activityKind) {
case nodomain.freeyourgadget.gadgetbridge.charts.ActivityKind.TYPE_DEEP_SLEEP:
return akDeepSleep.color;
case nodomain.freeyourgadget.gadgetbridge.charts.ActivityKind.TYPE_LIGHT_SLEEP:
return akLightSleep.color;
}
return akActivity.color;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_charts, container, false);
View rootView = inflater.inflate(R.layout.fragment_sleepchart, container, false);
Bundle extras = getActivity().getIntent().getExtras();
if (extras != null) {
@ -68,32 +124,42 @@ public class SleepChartFragment extends AbstractChartFragment {
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(mReceiver, filter);
mChart = (BarLineChartBase) rootView.findViewById(R.id.sleepchart);
mActivityChart = (BarLineChartBase) rootView.findViewById(R.id.sleepchart);
mSleepAmountChart = (PieChart) rootView.findViewById(R.id.sleepchart_pie_light_deep);
setupChart();
setupActivityChart();
setupSleepAmountChart();
refresh();
return rootView;
}
private void setupSleepAmountChart() {
mSleepAmountChart.setBackgroundColor(BACKGROUND_COLOR);
mSleepAmountChart.setDescriptionColor(DESCRIPTION_COLOR);
}
@Override
public void onDestroy() {
LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(mReceiver);
super.onDestroy();
}
private void setupChart() {
mChart.setBackgroundColor(BACKGROUND_COLOR);
mChart.setDescriptionColor(DESCRIPTION_COLOR);
configureBarLineChartDefaults(mChart);
XAxis x = mChart.getXAxis();
private void setupActivityChart() {
mActivityChart.setBackgroundColor(BACKGROUND_COLOR);
mActivityChart.setDescriptionColor(DESCRIPTION_COLOR);
configureBarLineChartDefaults(mActivityChart);
XAxis x = mActivityChart.getXAxis();
x.setDrawLabels(true);
x.setDrawGridLines(false);
x.setEnabled(true);
x.setTextColor(CHART_TEXT_COLOR);
x.setDrawLimitLinesBehindData(true);
YAxis y = mChart.getAxisLeft();
YAxis y = mActivityChart.getAxisLeft();
y.setDrawGridLines(false);
// y.setDrawLabels(false);
// TODO: make fixed max value optional
@ -104,33 +170,31 @@ public class SleepChartFragment extends AbstractChartFragment {
// y.setLabelCount(5);
y.setEnabled(true);
YAxis yAxisRight = mChart.getAxisRight();
YAxis yAxisRight = mActivityChart.getAxisRight();
yAxisRight.setDrawGridLines(false);
yAxisRight.setEnabled(false);
yAxisRight.setDrawLabels(false);
yAxisRight.setDrawTopYLabelEntry(false);
yAxisRight.setTextColor(CHART_TEXT_COLOR);
refresh(mGBDevice, mChart);
mChart.getLegend().setTextColor(LEGEND_TEXT_COLOR);
// mChart.getLegend().setEnabled(false);
// mActivityChart.getLegend().setEnabled(false);
//
// mChart.animateXY(2000, 2000);
// mActivityChart.animateXY(2000, 2000);
// don't forget to refresh the drawing
mChart.invalidate();
// mActivityChart.invalidate();
}
protected void setupLegend(BarLineChartBase chart) {
List<Integer> legendColors = new ArrayList<>(3);
List<String> legendLabels = new ArrayList<>(3);
protected void setupLegend(Chart chart) {
List<Integer> legendColors = new ArrayList<>(2);
List<String> legendLabels = new ArrayList<>(2);
legendColors.add(akLightSleep.color);
legendLabels.add(akLightSleep.label);
legendColors.add(akDeepSleep.color);
legendLabels.add(akDeepSleep.label);
chart.getLegend().setColors(legendColors);
chart.getLegend().setLabels(legendLabels);
chart.getLegend().setTextColor(LEGEND_TEXT_COLOR);
}
@Override

View File

@ -0,0 +1,43 @@
package nodomain.freeyourgadget.gadgetbridge.charts;
import android.content.Context;
public class ActivityAmount {
private int activityKind;
private short percent;
private long totalSeconds;
public ActivityAmount(int activityKind) {
this.activityKind = activityKind;
}
public void addSeconds(long seconds) {
totalSeconds += seconds;
}
public long getTotalSeconds() {
return totalSeconds;
}
public int getActivityKind() {
return activityKind;
}
public short getPercent() {
return percent;
}
public void setPercent(short percent) {
this.percent = percent;
}
public String getName(Context context) {
switch (activityKind) {
case ActivityKind.TYPE_DEEP_SLEEP:
return "Deep Sleep";
case ActivityKind.TYPE_LIGHT_SLEEP:
return "Light Sleep";
}
return "Activity";
}
}

View File

@ -0,0 +1,29 @@
package nodomain.freeyourgadget.gadgetbridge.charts;
import java.util.ArrayList;
import java.util.List;
public class ActivityAmounts {
private List<ActivityAmount> amounts = new ArrayList<>(4);
private long totalSeconds;
public void addAmount(ActivityAmount amount) {
amounts.add(amount);
totalSeconds += amount.getTotalSeconds();
}
public List<ActivityAmount> getAmounts() {
return amounts;
}
public long getTotalSeconds() {
return totalSeconds;
}
public void calculatePercentages() {
for (ActivityAmount amount : amounts) {
float fraction = amount.getTotalSeconds() / (float) totalSeconds;
amount.setPercent((short) (fraction * 100));
}
}
}

View File

@ -0,0 +1,62 @@
package nodomain.freeyourgadget.gadgetbridge.charts;
import java.util.ArrayList;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.GBActivitySample;
public class ActivityAnalysis {
public ActivityAmounts calculateActivityAmounts(List<GBActivitySample> samples) {
ActivityAmount deepSleep = new ActivityAmount(ActivityKind.TYPE_DEEP_SLEEP);
ActivityAmount lightSleep = new ActivityAmount(ActivityKind.TYPE_LIGHT_SLEEP);
ActivityAmount activity = new ActivityAmount(ActivityKind.TYPE_ACTIVITY);
ActivityAmount previousAmount = null;
GBActivitySample previousSample = null;
for(GBActivitySample sample : samples) {
ActivityAmount amount = null;
switch (sample.getType()) {
case GBActivitySample.TYPE_DEEP_SLEEP:
amount = deepSleep;
break;
case GBActivitySample.TYPE_LIGHT_SLEEP:
amount = lightSleep;
break;
case GBActivitySample.TYPE_UNKNOWN:
default:
amount = activity;
break;
}
if (previousSample != null) {
long timeDifference = sample.getTimestamp() - previousSample.getTimestamp();
if (previousSample.getType() == sample.getType()) {
amount.addSeconds(timeDifference);
} else {
long sharedTimeDifference = (long) (timeDifference / 2.0f);
previousAmount.addSeconds(sharedTimeDifference);
amount.addSeconds(sharedTimeDifference);
}
} else {
// nothing to do, we can only calculate when we have the next sample
}
previousAmount = amount;
previousSample = sample;
}
ActivityAmounts result = new ActivityAmounts();
if (deepSleep.getTotalSeconds() > 0) {
result.addAmount(deepSleep);
}
if (lightSleep.getTotalSeconds() > 0) {
result.addAmount(lightSleep);
}
if (activity.getTotalSeconds() > 0) {
result.addAmount(activity);
}
result.calculatePercentages();
return result;
}
}

View File

@ -0,0 +1,29 @@
package nodomain.freeyourgadget.gadgetbridge.charts;
import java.util.Arrays;
import nodomain.freeyourgadget.gadgetbridge.GBActivitySample;
public class ActivityKind {
public static final int TYPE_ACTIVITY = 1;
public static final int TYPE_LIGHT_SLEEP = 2;
public static final int TYPE_DEEP_SLEEP = 4;
public static final int TYPE_SLEEP = TYPE_LIGHT_SLEEP | TYPE_DEEP_SLEEP;
public static final int TYPE_ALL = TYPE_ACTIVITY | TYPE_SLEEP;
public static byte[] mapToDBActivityTypes(int types) {
byte[] result = new byte[3];
int i = 0;
if ((types & ActivityKind.TYPE_ACTIVITY) != 0) {
result[i++] = GBActivitySample.TYPE_UNKNOWN;
}
if ((types & ActivityKind.TYPE_DEEP_SLEEP) != 0) {
result[i++] = GBActivitySample.TYPE_DEEP_SLEEP;
}
if ((types & ActivityKind.TYPE_LIGHT_SLEEP) != 0) {
result[i++] = GBActivitySample.TYPE_LIGHT_SLEEP;
}
return Arrays.copyOf(result, i);
}
}

View File

@ -15,6 +15,7 @@ import java.util.Arrays;
import nodomain.freeyourgadget.gadgetbridge.GB;
import nodomain.freeyourgadget.gadgetbridge.GBActivitySample;
import nodomain.freeyourgadget.gadgetbridge.charts.ActivityKind;
import nodomain.freeyourgadget.gadgetbridge.database.schema.ActivityDBCreationScript;
import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.DATABASE_NAME;
@ -27,13 +28,6 @@ import static nodomain.freeyourgadget.gadgetbridge.database.DBConstants.TABLE_GB
public class ActivityDatabaseHandler extends SQLiteOpenHelper {
private static final int TYPE_ACTIVITY = 1;
private static final int TYPE_LIGHT_SLEEP = 2;
private static final int TYPE_DEEP_SLEEP = 4;
private static final int TYPE_SLEEP = TYPE_LIGHT_SLEEP | TYPE_DEEP_SLEEP;
private static final int TYPE_ALL = TYPE_ACTIVITY | TYPE_SLEEP;
private static final Logger LOG = LoggerFactory.getLogger(ActivityDatabaseHandler.class);
private static final int DATABASE_VERSION = 5;
@ -124,15 +118,15 @@ public class ActivityDatabaseHandler extends SQLiteOpenHelper {
}
public ArrayList<GBActivitySample> getSleepSamples(int timestamp_from, int timestamp_to, byte provider) {
return getGBActivitySamples(timestamp_from, timestamp_to, TYPE_SLEEP, provider);
return getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_SLEEP, provider);
}
public ArrayList<GBActivitySample> getActivitySamples(int timestamp_from, int timestamp_to, byte provider) {
return getGBActivitySamples(timestamp_from, timestamp_to, TYPE_ACTIVITY, provider);
return getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_ACTIVITY, provider);
}
public ArrayList<GBActivitySample> getAllActivitySamples(int timestamp_from, int timestamp_to, byte provider) {
return getGBActivitySamples(timestamp_from, timestamp_to, TYPE_ALL, provider);
return getGBActivitySamples(timestamp_from, timestamp_to, ActivityKind.TYPE_ALL, provider);
}
/**
@ -171,12 +165,12 @@ public class ActivityDatabaseHandler extends SQLiteOpenHelper {
}
private String getWhereClauseFor(int activityTypes) {
if (activityTypes == TYPE_ALL) {
if (activityTypes == ActivityKind.TYPE_ALL) {
return ""; // no further restriction
}
StringBuilder builder = new StringBuilder(" and (");
byte[] dbActivityTypes = mapToDBActivityTypes(activityTypes);
byte[] dbActivityTypes = ActivityKind.mapToDBActivityTypes(activityTypes);
for (int i = 0; i < dbActivityTypes.length; i++) {
builder.append(" type=").append(dbActivityTypes[i]);
if (i + 1 < dbActivityTypes.length) {
@ -186,19 +180,4 @@ public class ActivityDatabaseHandler extends SQLiteOpenHelper {
builder.append(')');
return builder.toString();
}
private byte[] mapToDBActivityTypes(int types) {
byte[] result = new byte[3];
int i = 0;
if ((types & TYPE_ACTIVITY) != 0) {
result[i++] = GBActivitySample.TYPE_UNKNOWN;
}
if ((types & TYPE_DEEP_SLEEP) != 0) {
result[i++] = GBActivitySample.TYPE_DEEP_SLEEP;
}
if ((types & TYPE_LIGHT_SLEEP) != 0) {
result[i++] = GBActivitySample.TYPE_LIGHT_SLEEP;
}
return Arrays.copyOf(result, i);
}
}

View File

@ -4,7 +4,7 @@
tools:context="nodomain.freeyourgadget.gadgetbridge.activities.ChartsActivity$PlaceholderFragment">
<nodomain.freeyourgadget.gadgetbridge.charts.CustomBarChart
android:id="@+id/sleepchart"
android:id="@+id/activitysleepchart"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>

View File

@ -0,0 +1,27 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="nodomain.freeyourgadget.gadgetbridge.activities.ChartsActivity$PlaceholderFragment"
android:orientation="vertical">
<nodomain.freeyourgadget.gadgetbridge.charts.CustomBarChart
android:id="@+id/sleepchart"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="20" />
<com.github.mikephil.charting.charts.PieChart
android:id="@+id/sleepchart_pie_light_deep"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="20">
</com.github.mikephil.charting.charts.PieChart>
<TextView
android:text="Test"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="20" />
</LinearLayout>