package info.nightscout.androidaps; import android.content.Context; import android.graphics.DashPathEffect; import android.preference.PreferenceManager; import android.support.v4.content.ContextCompat; import android.text.format.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.List; import java.util.TimeZone; import lecho.lib.hellocharts.model.Axis; import lecho.lib.hellocharts.model.AxisValue; import lecho.lib.hellocharts.model.Line; import lecho.lib.hellocharts.model.LineChartData; import lecho.lib.hellocharts.model.PointValue; import lecho.lib.hellocharts.model.Viewport; /** * Created by emmablack on 11/15/14. */ public class BgGraphBuilder { private ArrayList basalWatchDataList; public List tempWatchDataList; private int timespan; public double end_time; public double start_time; public double fuzzyTimeDenom = (1000 * 60 * 1); public Context context; public double highMark; public double lowMark; public List bgDataList = new ArrayList(); public int pointSize; public int highColor; public int lowColor; public int midColor; public int gridColour; public int basalCenterColor; public int basalBackgroundColor; public boolean singleLine = false; private double endHour; private List inRangeValues = new ArrayList(); private List highValues = new ArrayList(); private List lowValues = new ArrayList(); public Viewport viewport; public BgGraphBuilder(Context context, List aBgList, List tempWatchDataList, ArrayList basalWatchDataList, int aPointSize, int aMidColor, int gridColour, int basalBackgroundColor, int basalCenterColor, int timespan) { end_time = new Date().getTime() + (1000 * 60 * 6 * timespan); //Now plus 30 minutes padding (for 5 hours. Less if less.) start_time = new Date().getTime() - (1000 * 60 * 60 * timespan); //timespan hours ago this.bgDataList = aBgList; this.context = context; this.highMark = aBgList.get(aBgList.size() - 1).high; this.lowMark = aBgList.get(aBgList.size() - 1).low; this.pointSize = aPointSize; this.singleLine = true; this.midColor = aMidColor; this.lowColor = aMidColor; this.highColor = aMidColor; this.timespan = timespan; this.tempWatchDataList = tempWatchDataList; this.basalWatchDataList = basalWatchDataList; this.gridColour = gridColour; this.basalCenterColor = basalCenterColor; this.basalBackgroundColor = basalBackgroundColor; } // TODO: use for ambient mode! public BgGraphBuilder(Context context, List aBgList, List tempWatchDataList, ArrayList basalWatchDataList, int aPointSize, int aHighColor, int aLowColor, int aMidColor, int gridColour, int basalBackgroundColor, int basalCenterColor, int timespan) { end_time = new Date().getTime() + (1000 * 60 * 6 * timespan); //Now plus 30 minutes padding (for 5 hours. Less if less.) start_time = new Date().getTime() - (1000 * 60 * 60 * timespan); //timespan hours ago this.bgDataList = aBgList; this.context = context; this.highMark = aBgList.get(aBgList.size() - 1).high; this.lowMark = aBgList.get(aBgList.size() - 1).low; this.pointSize = aPointSize; this.highColor = aHighColor; this.lowColor = aLowColor; this.midColor = aMidColor; this.timespan = timespan; this.tempWatchDataList = tempWatchDataList; this.basalWatchDataList = basalWatchDataList; this.gridColour = gridColour; this.basalCenterColor = basalCenterColor; this.basalBackgroundColor = basalBackgroundColor; } public LineChartData lineData() { LineChartData lineData = new LineChartData(defaultLines()); lineData.setAxisYLeft(yAxis()); lineData.setAxisXBottom(xAxis()); return lineData; } public List defaultLines() { addBgReadingValues(); List lines = new ArrayList(); lines.add(highLine()); lines.add(lowLine()); lines.add(inRangeValuesLine()); lines.add(lowValuesLine()); lines.add(highValuesLine()); double minChart = lowMark; double maxChart = highMark; for ( BgWatchData bgd:bgDataList) { if(bgd.sgv > maxChart){ maxChart = bgd.sgv; } if(bgd.sgv < minChart){ minChart = bgd.sgv; } } double maxBasal = 0.1; for (BasalWatchData bwd: basalWatchDataList) { if(bwd.amount > maxBasal){ maxBasal = bwd.amount; } } double maxTemp = maxBasal; for (TempWatchData twd: tempWatchDataList) { if(twd.amount > maxTemp){ maxTemp = twd.amount; } } double factor = (maxChart-minChart)/maxTemp; // in case basal is the highest, don't paint it totally at the top. factor = Math.min(factor, ((maxChart-minChart)/maxBasal)*(2/3d)); boolean highlight = PreferenceManager .getDefaultSharedPreferences(context) .getBoolean("highlight_basals", false); for (TempWatchData twd: tempWatchDataList) { if(twd.endTime > start_time) { lines.add(tempValuesLine(twd, (float) minChart, factor, false, highlight?3:2)); if(highlight){ lines.add(tempValuesLine(twd, (float) minChart, factor, true, 1)); } } } lines.add(basalLine((float) minChart, factor, highlight)); return lines; } private Line basalLine(float offset, double factor, boolean highlight) { List pointValues = new ArrayList(); for (BasalWatchData bwd: basalWatchDataList) { if(bwd.endTime > start_time) { long begin = (long) Math.max(start_time, bwd.startTime); pointValues.add(new PointValue(fuzz(begin), offset + (float) (factor * bwd.amount))); pointValues.add(new PointValue(fuzz(bwd.endTime), offset + (float) (factor * bwd.amount))); } } Line basalLine = new Line(pointValues); basalLine.setHasPoints(false); basalLine.setColor(basalCenterColor); basalLine.setPathEffect(new DashPathEffect(new float[]{4f, 3f}, 4f)); basalLine.setStrokeWidth(highlight?2:1); return basalLine; } public Line highValuesLine() { Line highValuesLine = new Line(highValues); highValuesLine.setColor(highColor); highValuesLine.setHasLines(false); highValuesLine.setPointRadius(pointSize); highValuesLine.setHasPoints(true); return highValuesLine; } public Line lowValuesLine() { Line lowValuesLine = new Line(lowValues); lowValuesLine.setColor(lowColor); lowValuesLine.setHasLines(false); lowValuesLine.setPointRadius(pointSize); lowValuesLine.setHasPoints(true); return lowValuesLine; } public Line inRangeValuesLine() { Line inRangeValuesLine = new Line(inRangeValues); inRangeValuesLine.setColor(midColor); if(singleLine) { inRangeValuesLine.setHasLines(true); inRangeValuesLine.setHasPoints(false); inRangeValuesLine.setStrokeWidth(pointSize); } else { inRangeValuesLine.setPointRadius(pointSize); inRangeValuesLine.setHasPoints(true); inRangeValuesLine.setHasLines(false); } return inRangeValuesLine; } public Line tempValuesLine(TempWatchData twd, float offset, double factor, boolean isHighlightLine, int strokeWidth) { List lineValues = new ArrayList(); long begin = (long) Math.max(start_time, twd.startTime); lineValues.add(new PointValue(fuzz(begin), offset + (float) (factor * twd.startBasal))); lineValues.add(new PointValue(fuzz(begin), offset + (float) (factor * twd.amount))); lineValues.add(new PointValue(fuzz(twd.endTime), offset + (float) (factor * twd.amount))); lineValues.add(new PointValue(fuzz(twd.endTime), offset + (float) (factor * twd.endBasal))); Line valueLine = new Line(lineValues); valueLine.setHasPoints(false); if (isHighlightLine){ valueLine.setColor(basalCenterColor); valueLine.setStrokeWidth(1); }else { valueLine.setColor(basalBackgroundColor); valueLine.setStrokeWidth(strokeWidth); } return valueLine; } private void addBgReadingValues() { if(singleLine) { for (BgWatchData bgReading : bgDataList) { if(bgReading.timestamp > start_time) { if (bgReading.sgv >= 400) { inRangeValues.add(new PointValue(fuzz(bgReading.timestamp), (float) 400)); } else if (bgReading.sgv >= highMark) { inRangeValues.add(new PointValue(fuzz(bgReading.timestamp), (float) bgReading.sgv)); } else if (bgReading.sgv >= lowMark) { inRangeValues.add(new PointValue(fuzz(bgReading.timestamp), (float) bgReading.sgv)); } else if (bgReading.sgv >= 40) { inRangeValues.add(new PointValue(fuzz(bgReading.timestamp), (float) bgReading.sgv)); } else if (bgReading.sgv >= 11) { inRangeValues.add(new PointValue(fuzz(bgReading.timestamp), (float) 40)); } } } } else { for (BgWatchData bgReading : bgDataList) { if (bgReading.timestamp > start_time) { if (bgReading.sgv >= 400) { highValues.add(new PointValue(fuzz(bgReading.timestamp), (float) 400)); } else if (bgReading.sgv >= highMark) { highValues.add(new PointValue(fuzz(bgReading.timestamp), (float) bgReading.sgv)); } else if (bgReading.sgv >= lowMark) { inRangeValues.add(new PointValue(fuzz(bgReading.timestamp), (float) bgReading.sgv)); } else if (bgReading.sgv >= 40) { lowValues.add(new PointValue(fuzz(bgReading.timestamp), (float) bgReading.sgv)); } else if (bgReading.sgv >= 11) { lowValues.add(new PointValue(fuzz(bgReading.timestamp), (float) 40)); } } } } } public Line highLine() { List highLineValues = new ArrayList(); highLineValues.add(new PointValue(fuzz(start_time), (float) highMark)); highLineValues.add(new PointValue(fuzz(end_time), (float) highMark)); Line highLine = new Line(highLineValues); highLine.setHasPoints(false); highLine.setStrokeWidth(1); highLine.setColor(highColor); return highLine; } public Line lowLine() { List lowLineValues = new ArrayList(); lowLineValues.add(new PointValue(fuzz(start_time), (float) lowMark)); lowLineValues.add(new PointValue(fuzz(end_time), (float) lowMark)); Line lowLine = new Line(lowLineValues); lowLine.setHasPoints(false); lowLine.setColor(lowColor); lowLine.setStrokeWidth(1); return lowLine; } /////////AXIS RELATED////////////// public Axis yAxis() { Axis yAxis = new Axis(); yAxis.setAutoGenerated(true); List axisValues = new ArrayList(); yAxis.setValues(axisValues); yAxis.setHasLines(false); yAxis.setLineColor(gridColour); return yAxis; } public Axis xAxis() { final boolean is24 = DateFormat.is24HourFormat(context); Axis xAxis = new Axis(); xAxis.setAutoGenerated(false); List xAxisValues = new ArrayList(); GregorianCalendar now = new GregorianCalendar(); GregorianCalendar today = new GregorianCalendar(now.get(Calendar.YEAR), now.get(Calendar.MONTH), now.get(Calendar.DAY_OF_MONTH)); SimpleDateFormat timeFormat = new SimpleDateFormat(is24? "HH" : "h a"); timeFormat.setTimeZone(TimeZone.getDefault()); double start_hour = today.getTime().getTime(); double timeNow = new Date().getTime(); for (int l = 0; l <= 24; l++) { if ((start_hour + (60000 * 60 * (l))) < timeNow) { if ((start_hour + (60000 * 60 * (l + 1))) >= timeNow) { endHour = start_hour + (60000 * 60 * (l)); l = 25; } } } //Display current time on the graph SimpleDateFormat longTimeFormat = new SimpleDateFormat(is24? "HH:mm" : "h:mm a"); xAxisValues.add(new AxisValue(fuzz(timeNow), (longTimeFormat.format(timeNow)).toCharArray())); //Add whole hours endTime the axis (as long as they are more than 15 mins away from the current time) for (int l = 0; l <= 24; l++) { double timestamp = endHour - (60000 * 60 * l); if((timestamp - timeNow < 0) && (timestamp > start_time)) { if(Math.abs(timestamp - timeNow) > (1000 * 60 * 8 * timespan)){ xAxisValues.add(new AxisValue(fuzz(timestamp), (timeFormat.format(timestamp)).toCharArray())); }else { xAxisValues.add(new AxisValue(fuzz(timestamp), "".toCharArray())); } } } xAxis.setValues(xAxisValues); xAxis.setTextSize(10); xAxis.setHasLines(true); xAxis.setLineColor(gridColour); xAxis.setTextColor(gridColour); return xAxis; } public float fuzz(double value) { return (float) Math.round(value / fuzzyTimeDenom); } }