From d82a696bfb1a9714cd7de21a06946d661a71884e Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Sun, 17 Jul 2016 15:04:33 +0200 Subject: [PATCH] show treatments in overview graph --- .../nightscout/androidaps/db/BgReading.java | 1 + .../nightscout/androidaps/db/Treatment.java | 76 ++-- .../interfaces/TreatmentsInterface.java | 4 + .../DataPointWithLabelInterface.java | 52 +++ .../PointsWithLabelGraphSeries.java | 333 ++++++++++++++++++ .../plugins/Overview/OverviewFragment.java | 23 +- .../Treatments/TreatmentsFragment.java | 6 + 7 files changed, 456 insertions(+), 39 deletions(-) create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/Overview/GraphSeriesExtension/DataPointWithLabelInterface.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/Overview/GraphSeriesExtension/PointsWithLabelGraphSeries.java diff --git a/app/src/main/java/info/nightscout/androidaps/db/BgReading.java b/app/src/main/java/info/nightscout/androidaps/db/BgReading.java index 4c78bd9c58..9e4d2eda1f 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/BgReading.java +++ b/app/src/main/java/info/nightscout/androidaps/db/BgReading.java @@ -83,4 +83,5 @@ public class BgReading implements DataPointInterface { public double getY() { return valueToUnits(units); } + } diff --git a/app/src/main/java/info/nightscout/androidaps/db/Treatment.java b/app/src/main/java/info/nightscout/androidaps/db/Treatment.java index d3084bc4c6..b67e18ee95 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/Treatment.java +++ b/app/src/main/java/info/nightscout/androidaps/db/Treatment.java @@ -1,10 +1,5 @@ package info.nightscout.androidaps.db; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ResolveInfo; -import android.os.Bundle; - import com.j256.ormlite.field.DatabaseField; import com.j256.ormlite.table.DatabaseTable; @@ -16,17 +11,16 @@ import org.slf4j.LoggerFactory; import java.util.Date; import java.util.List; -import info.nightscout.androidaps.Config; -import info.nightscout.androidaps.data.Iob; -import info.nightscout.androidaps.Services.Intents; import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.data.Iob; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderFragment; -import info.nightscout.client.data.DbLogger; +import info.nightscout.androidaps.plugins.Overview.GraphSeriesExtension.DataPointWithLabelInterface; import info.nightscout.client.data.NSProfile; import info.nightscout.utils.DateUtil; +import info.nightscout.utils.DecimalFormatter; @DatabaseTable(tableName = "Treatments") -public class Treatment { +public class Treatment implements DataPointWithLabelInterface { private static Logger log = LoggerFactory.getLogger(Treatment.class); public long getTimeIndex() { @@ -99,6 +93,39 @@ public class Treatment { "}"; } + // DataPointInterface + @Override + public double getX() { + return timeIndex; + } + + // default when no sgv around available + private double yValue = 0; + + @Override + public double getY() { + return yValue; + } + + @Override + public String getLabel() { + String label = ""; + if (insulin > 0) label += DecimalFormatter.to2Decimal(insulin) + "U"; + if (carbs > 0) label += (label.equals("") ? "" : " ") + DecimalFormatter.to0Decimal(carbs) + "g"; + return label; + } + + public void setYValue(List bgReadingsArray) { + NSProfile profile = MainApp.getConfigBuilder().getActiveProfile().getProfile(); + if (profile == null) return; + for (int r = bgReadingsArray.size() - 1; r >= 0; r--) { + BgReading reading = bgReadingsArray.get(r); + if (reading.timeIndex > timeIndex) continue; + yValue = NSProfile.fromMgdlToUnits(reading.value, profile.getUnits()); + break; + } + } + public void sendToNSClient() { JSONObject data = new JSONObject(); try { @@ -113,31 +140,4 @@ public class Treatment { ConfigBuilderFragment.uploadCareportalEntryToNS(data); } - public void updateToNSClient() { - Context context = MainApp.instance().getApplicationContext(); - Bundle bundle = new Bundle(); - bundle.putString("action", "dbUpdate"); - bundle.putString("collection", "treatments"); - JSONObject data = new JSONObject(); - try { - data.put("eventType", "Meal Bolus"); - data.put("insulin", insulin); - data.put("carbs", carbs.intValue()); - data.put("created_at", DateUtil.toISOString(created_at)); - data.put("timeIndex", timeIndex); - } catch (JSONException e) { - e.printStackTrace(); - } - bundle.putString("data", data.toString()); - bundle.putString("_id", _id); - Intent intent = new Intent(Intents.ACTION_DATABASE); - intent.putExtras(bundle); - intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); - context.sendBroadcast(intent); - List q = context.getPackageManager().queryBroadcastReceivers(intent, 0); - if (q.size() < 1) { - log.error("DBUPDATE No receivers"); - } else if (Config.logNSUpload) - log.debug("DBUPDATE dbUpdate " + q.size() + " receivers " + _id + " " + data.toString()); - } -} + } diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java b/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java index 215f33eedd..fc80c8efe5 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java @@ -1,5 +1,8 @@ package info.nightscout.androidaps.interfaces; +import java.util.List; + +import info.nightscout.androidaps.db.Treatment; import info.nightscout.androidaps.plugins.OpenAPSMA.IobTotal; import info.nightscout.androidaps.plugins.Treatments.TreatmentsFragment; @@ -11,4 +14,5 @@ public interface TreatmentsInterface { void updateTotalIOB(); IobTotal getLastCalculation(); TreatmentsFragment.MealData getMealData(); + List getTreatments(); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/GraphSeriesExtension/DataPointWithLabelInterface.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/GraphSeriesExtension/DataPointWithLabelInterface.java new file mode 100644 index 0000000000..af6c61272c --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/GraphSeriesExtension/DataPointWithLabelInterface.java @@ -0,0 +1,52 @@ +package info.nightscout.androidaps.plugins.Overview.GraphSeriesExtension; +/** + * GraphView + * Copyright (C) 2014 Jonas Gehring + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, + * with the "Linking Exception", which can be found at the license.txt + * file in this program. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * with the "Linking Exception" along with this program; if not, + * write to the author Jonas Gehring . + */ + +/** + * Added by mike + */ + +import com.jjoe64.graphview.series.DataPointInterface; + +/** + * interface of data points. Implement this in order + * to use your class in {@link com.jjoe64.graphview.series.Series}. + * + * You can also use the default implementation {@link com.jjoe64.graphview.series.DataPoint} so + * you do not have to implement it for yourself. + * + * @author jjoe64 + */ +public interface DataPointWithLabelInterface extends DataPointInterface{ + /** + * @return the x value + */ + public double getX(); + + /** + * @return the y value + */ + public double getY(); + + /** + * @return the label value + */ + public String getLabel(); +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/GraphSeriesExtension/PointsWithLabelGraphSeries.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/GraphSeriesExtension/PointsWithLabelGraphSeries.java new file mode 100644 index 0000000000..44c07ef6c8 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/GraphSeriesExtension/PointsWithLabelGraphSeries.java @@ -0,0 +1,333 @@ +package info.nightscout.androidaps.plugins.Overview.GraphSeriesExtension; + +/** + * GraphView + * Copyright (C) 2014 Jonas Gehring + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, + * with the "Linking Exception", which can be found at the license.txt + * file in this program. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * with the "Linking Exception" along with this program; if not, + * write to the author Jonas Gehring . + */ + +/** + * Added by mike + */ + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Point; + +import com.jjoe64.graphview.GraphView; +import com.jjoe64.graphview.series.BaseSeries; + +import java.util.Iterator; + +/** + * Series that plots the data as points. + * The points can be different shapes or a + * complete custom drawing. + * + * @author jjoe64 + */ +public class PointsWithLabelGraphSeries extends BaseSeries { + /** + * interface to implement a custom + * drawing for the data points. + */ + public static interface CustomShape { + /** + * called when drawing a single data point. + * use the x and y coordinates to render your + * drawing at this point. + * + * @param canvas canvas to draw on + * @param paint internal paint object. this has the correct color. + * But you can use your own paint. + * @param x x-coordinate the point has to be drawn to + * @param y y-coordinate the point has to be drawn to + * @param dataPoint the related data point + */ + void draw(Canvas canvas, Paint paint, float x, float y, DataPointWithLabelInterface dataPoint); + } + + /** + * choose a predefined shape to render for + * each data point. + * You can also render a custom drawing via {@link com.jjoe64.graphview.series.PointsGraphSeries.CustomShape} + */ + public enum Shape { + /** + * draws a point / circle + */ + POINT, + + /** + * draws a triangle + */ + TRIANGLE, + + /** + * draws a rectangle + */ + RECTANGLE + } + + /** + * wrapped styles for this series + */ + private final class Styles { + /** + * this is used for the size of the shape that + * will be drawn. + * This is useless if you are using a custom shape. + */ + float size; + + /** + * the shape that will be drawn for each point. + */ + Shape shape; + } + + /** + * wrapped styles + */ + private Styles mStyles; + + /** + * internal paint object + */ + private Paint mPaint; + + /** + * handler to use a custom drawing + */ + private CustomShape mCustomShape; + + /** + * creates the series without data + */ + public PointsWithLabelGraphSeries() { + init(); + } + + /** + * creates the series with data + * + * @param data datapoints + */ + public PointsWithLabelGraphSeries(E[] data) { + super(data); + init(); + } + + /** + * inits the internal objects + * set the defaults + */ + protected void init() { + mStyles = new Styles(); + mStyles.size = 20f; + mPaint = new Paint(); + mPaint.setStrokeCap(Paint.Cap.ROUND); + setShape(Shape.POINT); + } + + /** + * plot the data to the viewport + * + * @param graphView graphview + * @param canvas canvas to draw on + * @param isSecondScale whether it is the second scale + */ + @Override + public void draw(GraphView graphView, Canvas canvas, boolean isSecondScale) { + resetDataPoints(); + + // get data + double maxX = graphView.getViewport().getMaxX(false); + double minX = graphView.getViewport().getMinX(false); + + double maxY; + double minY; + if (isSecondScale) { + maxY = graphView.getSecondScale().getMaxY(); + minY = graphView.getSecondScale().getMinY(); + } else { + maxY = graphView.getViewport().getMaxY(false); + minY = graphView.getViewport().getMinY(false); + } + + Iterator values = getValues(minX, maxX); + + // draw background + double lastEndY = 0; + double lastEndX = 0; + + // draw data + mPaint.setColor(getColor()); + + double diffY = maxY - minY; + double diffX = maxX - minX; + + float graphHeight = graphView.getGraphContentHeight(); + float graphWidth = graphView.getGraphContentWidth(); + float graphLeft = graphView.getGraphContentLeft(); + float graphTop = graphView.getGraphContentTop(); + + lastEndY = 0; + lastEndX = 0; + float firstX = 0; + int i=0; + while (values.hasNext()) { + E value = values.next(); + + double valY = value.getY() - minY; + double ratY = valY / diffY; + double y = graphHeight * ratY; + + double valX = value.getX() - minX; + double ratX = valX / diffX; + double x = graphWidth * ratX; + + double orgX = x; + double orgY = y; + + // overdraw + boolean overdraw = false; + if (x > graphWidth) { // end right + overdraw = true; + } + if (y < 0) { // end bottom + overdraw = true; + } + if (y > graphHeight) { // end top + overdraw = true; + } + /* Fix a bug that continue to show the DOT after Y axis */ + if(x < 0) { + overdraw = true; + } + + float endX = (float) x + (graphLeft + 1); + float endY = (float) (graphTop - y) + graphHeight; + registerDataPoint(endX, endY, value); + + // draw data point + if (!overdraw) { + if (mCustomShape != null) { + mCustomShape.draw(canvas, mPaint, endX, endY, value); + } else if (mStyles.shape == Shape.POINT) { + canvas.drawCircle(endX, endY, mStyles.size, mPaint); + } else if (mStyles.shape == Shape.RECTANGLE) { + canvas.drawRect(endX-mStyles.size, endY-mStyles.size, endX+mStyles.size, endY+mStyles.size, mPaint); + } else if (mStyles.shape == Shape.TRIANGLE) { + Point[] points = new Point[3]; + points[0] = new Point((int)endX, (int)(endY-getSize())); + points[1] = new Point((int)(endX+getSize()), (int)(endY+getSize()*0.67)); + points[2] = new Point((int)(endX-getSize()), (int)(endY+getSize()*0.67)); + drawArrows(points, canvas, mPaint); + } + // set values above point + if (value.getLabel() != null) { + float px = endX; + float py = endY - (int) (getSize()); + canvas.save(); + canvas.rotate(-45, px, py); + mPaint.setTextSize((int) (getSize() * 2.5)); + mPaint.setFakeBoldText(true); + canvas.drawText(value.getLabel(), px + getSize(), py, mPaint); + canvas.restore(); + } + } + + i++; + } + + } + + /** + * helper to render triangle + * + * @param point array with 3 coordinates + * @param canvas canvas to draw on + * @param paint paint object + */ + private void drawArrows(Point[] point, Canvas canvas, Paint paint) { + float [] points = new float[8]; + points[0] = point[0].x; + points[1] = point[0].y; + points[2] = point[1].x; + points[3] = point[1].y; + points[4] = point[2].x; + points[5] = point[2].y; + points[6] = point[0].x; + points[7] = point[0].y; + + canvas.drawVertices(Canvas.VertexMode.TRIANGLES, 8, points, 0, null, 0, null, 0, null, 0, 0, paint); + Path path = new Path(); + path.moveTo(point[0].x , point[0].y); + path.lineTo(point[1].x,point[1].y); + path.lineTo(point[2].x,point[2].y); + canvas.drawPath(path,paint); + } + + /** + * This is used for the size of the shape that + * will be drawn. + * This is useless if you are using a custom shape. + * + * @return the size of the shape + */ + public float getSize() { + return mStyles.size; + } + + /** + * This is used for the size of the shape that + * will be drawn. + * This is useless if you are using a custom shape. + * + * @param radius the size of the shape + */ + public void setSize(float radius) { + mStyles.size = radius; + } + + /** + * @return the shape that will be drawn for each point + */ + public Shape getShape() { + return mStyles.shape; + } + + /** + * @param s the shape that will be drawn for each point + */ + public void setShape(Shape s) { + mStyles.shape = s; + } + + /** + * Use a custom handler to render your own + * drawing for each data point. + * + * @param shape handler to use a custom drawing + */ + public void setCustomShape(CustomShape shape) { + mCustomShape = shape; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java index c1f3f4e2a8..7b94253f90 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java @@ -44,6 +44,7 @@ import info.nightscout.androidaps.data.PumpEnactResult; import info.nightscout.androidaps.db.BgReading; import info.nightscout.androidaps.db.DatabaseHelper; import info.nightscout.androidaps.db.TempBasal; +import info.nightscout.androidaps.db.Treatment; import info.nightscout.androidaps.events.EventNewBG; import info.nightscout.androidaps.events.EventPreferenceChange; import info.nightscout.androidaps.events.EventRefreshGui; @@ -59,6 +60,7 @@ import info.nightscout.androidaps.plugins.Overview.Dialogs.NewExtendedBolusDialo import info.nightscout.androidaps.plugins.Overview.Dialogs.NewTempBasalDialog; import info.nightscout.androidaps.plugins.Overview.Dialogs.NewTreatmentDialog; import info.nightscout.androidaps.plugins.Overview.Dialogs.WizardDialog; +import info.nightscout.androidaps.plugins.Overview.GraphSeriesExtension.PointsWithLabelGraphSeries; import info.nightscout.client.data.NSProfile; import info.nightscout.utils.DecimalFormatter; import info.nightscout.utils.Round; @@ -520,6 +522,7 @@ public class OverviewFragment extends Fragment implements PluginBase { LineGraphSeries seriesNow = null; PointsGraphSeries seriesInRage = null; PointsGraphSeries seriesOutOfRange = null; + PointsWithLabelGraphSeries seriesTreatments = null; // remove old data from graph bgGraph.removeAllSeries(); @@ -606,7 +609,6 @@ public class OverviewFragment extends Fragment implements PluginBase { seriesOutOfRange.setColor(Color.RED); } - // **** HIGH and LOW targets graph **** DataPoint[] lowDataPoints = new DataPoint[]{ new DataPoint(fromTime, lowLine), @@ -639,6 +641,25 @@ public class OverviewFragment extends Fragment implements PluginBase { seriesNow.setCustomPaint(paint); + // Treatments + List treatments = MainApp.getConfigBuilder().getActiveTreatments().getTreatments(); + List filteredTreatments = new ArrayList(); + + for (int tx = 0; tx < treatments.size(); tx ++) { + Treatment t = treatments.get(tx); + if (t.getTimeIndex() < fromTime || t.getTimeIndex() > now) continue; + t.setYValue(bgReadingsArray); + filteredTreatments.add(t); + } + Treatment[] treatmentsArray = new Treatment[filteredTreatments.size()]; + treatmentsArray = filteredTreatments.toArray(treatmentsArray); + if (treatmentsArray.length > 0) { + bgGraph.addSeries(seriesTreatments = new PointsWithLabelGraphSeries(treatmentsArray)); + seriesTreatments.setShape(PointsWithLabelGraphSeries.Shape.TRIANGLE); + seriesTreatments.setSize(10); + seriesTreatments.setColor(Color.CYAN); + } + // set manual x bounds to have nice steps bgGraph.getViewport().setMaxX(toTime); bgGraph.getViewport().setMinX(fromTime); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsFragment.java index 7c71937b40..3b01b4c8b8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsFragment.java @@ -180,6 +180,12 @@ public class TreatmentsFragment extends Fragment implements View.OnClickListener return result; } + @Override + public List getTreatments() { + return treatments; + } + + public static class RecyclerViewAdapter extends RecyclerView.Adapter { List treatments;