private void displayCurrentInfoFromReading(BgReading lastBgReading, boolean predictive) { double estimate = 0; if ((new Date().getTime()) - (60000 * 11) - lastBgReading.timestamp > 0) { notificationText.setText("Signal Missed"); if (!predictive) { estimate = lastBgReading.calculated_value; } else { estimate = BgReading.estimated_bg(lastBgReading.timestamp + (6000 * 7)); } currentBgValueText.setText(bgGraphBuilder.unitized_string(estimate)); currentBgValueText.setPaintFlags( currentBgValueText.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); dexbridgeBattery.setPaintFlags( dexbridgeBattery.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); } else { if (notificationText.getText().length() == 0) { notificationText.setTextColor(Color.WHITE); } if (!predictive) { estimate = lastBgReading.calculated_value; String stringEstimate = bgGraphBuilder.unitized_string(estimate); String slope_arrow = lastBgReading.slopeArrow(); if (lastBgReading.hide_slope) { slope_arrow = ""; } currentBgValueText.setText(stringEstimate + " " + slope_arrow); } else { estimate = BgReading.activePrediction(); String stringEstimate = bgGraphBuilder.unitized_string(estimate); currentBgValueText.setText(stringEstimate + " " + BgReading.activeSlopeArrow()); } } int minutes = (int) (System.currentTimeMillis() - lastBgReading.timestamp) / (60 * 1000); notificationText.append("\n" + minutes + ((minutes == 1) ? " Minute ago" : " Minutes ago")); List<BgReading> bgReadingList = BgReading.latest(2); if (bgReadingList != null && bgReadingList.size() == 2) { // same logic as in xDripWidget (refactor that to BGReadings to avoid redundancy / later // inconsistencies)? if (BgGraphBuilder.isXLargeTablet(getApplicationContext())) { notificationText.append(" "); } else { notificationText.append("\n"); } notificationText.append(bgGraphBuilder.unitizedDeltaString(true, true)); } if (bgGraphBuilder.unitized(estimate) <= bgGraphBuilder.lowMark) { currentBgValueText.setTextColor(Color.parseColor("#C30909")); } else if (bgGraphBuilder.unitized(estimate) >= bgGraphBuilder.highMark) { currentBgValueText.setTextColor(Color.parseColor("#FFBB33")); } else { currentBgValueText.setTextColor(Color.WHITE); } }
public String unitizedDeltaString(boolean showUnit, boolean highGranularity) { List<BgReading> last2 = BgReading.latest(2); if (last2.size() < 2 || last2.get(0).timestamp - last2.get(1).timestamp > 20 * 60 * 1000) { // don't show delta if there are not enough values or the values are more than 20 mintes apart return "???"; } double value = BgReading.currentSlope() * 5 * 60 * 1000; if (Math.abs(value) > 100) { // a delta > 100 will not happen with real BG values -> problematic sensor data return "ERR"; } // TODO: allow localization from os settings once pebble doesn't require english locale DecimalFormat df = new DecimalFormat("#", new DecimalFormatSymbols(Locale.ENGLISH)); String delta_sign = ""; if (value > 0) { delta_sign = "+"; } if (doMgdl) { if (highGranularity) { df.setMaximumFractionDigits(1); } else { df.setMaximumFractionDigits(0); } return delta_sign + df.format(unitized(value)) + (showUnit ? " mg/dL" : ""); } else { if (highGranularity) { df.setMaximumFractionDigits(2); } else { df.setMaximumFractionDigits(1); } df.setMinimumFractionDigits(1); df.setMinimumIntegerDigits(1); return delta_sign + df.format(unitized(value)) + (showUnit ? " mmol/L" : ""); } }
private void updateCurrentBgInfoCommon(TextView notificationText) { if (alreadyDisplayedBgInfoCommon) return; // with bluetooth and wifi, skip second time alreadyDisplayedBgInfoCommon = true; final boolean isSensorActive = Sensor.isActive(); if (!isSensorActive) { notificationText.setText("Now start your sensor"); return; } final long now = System.currentTimeMillis(); if (Sensor.currentSensor().started_at + 60000 * 60 * 2 >= now) { double waitTime = (Sensor.currentSensor().started_at + 60000 * 60 * 2 - now) / 60000.0; notificationText.setText( "Please wait while sensor warms up! (" + String.format("%.2f", waitTime) + " minutes)"); return; } if (BgReading.latest(2).size() > 1) { List<Calibration> calibrations = Calibration.latest(2); if (calibrations.size() > 1) { if (calibrations.get(0).possible_bad != null && calibrations.get(0).possible_bad == true && calibrations.get(1).possible_bad != null && calibrations.get(1).possible_bad != true) { notificationText.setText( "Possible bad calibration slope, please have a glass of water, wash hands, then recalibrate in a few!"); } displayCurrentInfo(); } else { notificationText.setText("Please enter two calibrations to get started!"); } } else { if (BgReading.latestUnCalculated(2).size() < 2) { notificationText.setText("Please wait, need 2 readings from transmitter first."); } else { List<Calibration> calibrations = Calibration.latest(2); if (calibrations.size() < 2) { notificationText.setText("Please enter two calibrations to get started!"); } } } }
public BgGraphBuilder(Context context, long start, long end, int numValues) { end_time = end; start_time = start; bgReadings = BgReading.latestForGraph(numValues, start, end); calibrations = Calibration.latestForGraph(numValues, start, end); this.context = context; this.prefs = PreferenceManager.getDefaultSharedPreferences(context); this.highMark = Double.parseDouble(prefs.getString("highValue", "170")); this.lowMark = Double.parseDouble(prefs.getString("lowValue", "70")); this.doMgdl = (prefs.getString("units", "mgdl").equals("mgdl")); defaultMinY = unitized(40); defaultMaxY = unitized(250); pointSize = isXLargeTablet(context) ? 5 : 3; axisTextSize = isXLargeTablet(context) ? 20 : Axis.DEFAULT_TEXT_SIZE_SP; previewAxisTextSize = isXLargeTablet(context) ? 12 : 5; hoursPreviewStep = isXLargeTablet(context) ? 2 : 1; }
public void displayCurrentInfo() { DecimalFormat df = new DecimalFormat("#"); df.setMaximumFractionDigits(0); boolean isDexbridge = CollectionServiceStarter.isDexbridgeWixel(getApplicationContext()); int bridgeBattery = prefs.getInt("bridge_battery", 0); if (isDexbridge) { if (bridgeBattery == 0) { dexbridgeBattery.setText("Waiting for packet"); } else { dexbridgeBattery.setText("Bridge Battery: " + bridgeBattery + "%"); } if (bridgeBattery < 50) dexbridgeBattery.setTextColor(Color.YELLOW); if (bridgeBattery < 25) dexbridgeBattery.setTextColor(Color.RED); else dexbridgeBattery.setTextColor(Color.GREEN); dexbridgeBattery.setVisibility(View.VISIBLE); } else { dexbridgeBattery.setVisibility(View.INVISIBLE); } if ((currentBgValueText.getPaintFlags() & Paint.STRIKE_THRU_TEXT_FLAG) > 0) { currentBgValueText.setPaintFlags( currentBgValueText.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG)); dexbridgeBattery.setPaintFlags( dexbridgeBattery.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG)); } BgReading lastBgReading = BgReading.lastNoSenssor(); boolean predictive = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()) .getBoolean("predictive_bg", false); if (isBTShare) { predictive = false; } if (lastBgReading != null) { displayCurrentInfoFromReading(lastBgReading, predictive); } setupCharts(); }
/** Created by stephenblack on 11/15/14. */ public class BgGraphBuilder { public static final int FUZZER = (1000 * 30 * 5); public double end_time = (new Date().getTime() + (60000 * 10)) / FUZZER; public double start_time = end_time - ((60000 * 60 * 24)) / FUZZER; public Context context; public SharedPreferences prefs; public double highMark; public double lowMark; public double defaultMinY; public double defaultMaxY; public boolean doMgdl; final int pointSize; final int axisTextSize; final int previewAxisTextSize; final int hoursPreviewStep; private double endHour; private final int numValues = (60 / 5) * 24; private final List<BgReading> bgReadings = BgReading.latestForGraph(numValues, (start_time * FUZZER)); private List<PointValue> inRangeValues = new ArrayList<PointValue>(); private List<PointValue> highValues = new ArrayList<PointValue>(); private List<PointValue> lowValues = new ArrayList<PointValue>(); private List<PointValue> rawInterpretedValues = new ArrayList<PointValue>(); public Viewport viewport; public BgGraphBuilder(Context context) { this.context = context; this.prefs = PreferenceManager.getDefaultSharedPreferences(context); this.highMark = Double.parseDouble(prefs.getString("highValue", "170")); this.lowMark = Double.parseDouble(prefs.getString("lowValue", "70")); this.doMgdl = (prefs.getString("units", "mgdl").compareTo("mgdl") == 0); defaultMinY = unitized(40); defaultMaxY = unitized(250); pointSize = isXLargeTablet(context) ? 5 : 3; axisTextSize = isXLargeTablet(context) ? 20 : Axis.DEFAULT_TEXT_SIZE_SP; previewAxisTextSize = isXLargeTablet(context) ? 12 : 5; hoursPreviewStep = isXLargeTablet(context) ? 2 : 1; } public LineChartData lineData() { LineChartData lineData = new LineChartData(defaultLines()); lineData.setAxisYLeft(yAxis()); lineData.setAxisXBottom(xAxis()); return lineData; } public LineChartData previewLineData() { LineChartData previewLineData = new LineChartData(lineData()); previewLineData.setAxisYLeft(yAxis()); previewLineData.setAxisXBottom(previewXAxis()); previewLineData.getLines().get(4).setPointRadius(2); previewLineData.getLines().get(5).setPointRadius(2); previewLineData.getLines().get(6).setPointRadius(2); return previewLineData; } public List<Line> defaultLines() { addBgReadingValues(); List<Line> lines = new ArrayList<Line>(); lines.add(minShowLine()); lines.add(maxShowLine()); lines.add(highLine()); lines.add(lowLine()); lines.add(inRangeValuesLine()); lines.add(lowValuesLine()); lines.add(highValuesLine()); lines.add(rawInterpretedLine()); return lines; } public Line highValuesLine() { Line highValuesLine = new Line(highValues); highValuesLine.setColor(Utils.COLOR_ORANGE); highValuesLine.setHasLines(false); highValuesLine.setPointRadius(pointSize); highValuesLine.setHasPoints(true); return highValuesLine; } public Line lowValuesLine() { Line lowValuesLine = new Line(lowValues); lowValuesLine.setColor(Color.parseColor("#C30909")); lowValuesLine.setHasLines(false); lowValuesLine.setPointRadius(pointSize); lowValuesLine.setHasPoints(true); return lowValuesLine; } public Line inRangeValuesLine() { Line inRangeValuesLine = new Line(inRangeValues); inRangeValuesLine.setColor(Utils.COLOR_BLUE); inRangeValuesLine.setHasLines(false); inRangeValuesLine.setPointRadius(pointSize); inRangeValuesLine.setHasPoints(true); return inRangeValuesLine; } public Line rawInterpretedLine() { Line line = new Line(rawInterpretedValues); line.setHasLines(false); line.setPointRadius(1); line.setHasPoints(true); return line; } private void addBgReadingValues() { for (BgReading bgReading : bgReadings) { if (bgReading.raw_calculated != 0 && prefs.getBoolean("interpret_raw", false)) { rawInterpretedValues.add( new PointValue( (float) (bgReading.timestamp / FUZZER), (float) unitized(bgReading.raw_calculated))); } else if (bgReading.calculated_value >= 400) { highValues.add( new PointValue((float) (bgReading.timestamp / FUZZER), (float) unitized(400))); } else if (unitized(bgReading.calculated_value) >= highMark) { highValues.add( new PointValue( (float) (bgReading.timestamp / FUZZER), (float) unitized(bgReading.calculated_value))); } else if (unitized(bgReading.calculated_value) >= lowMark) { inRangeValues.add( new PointValue( (float) (bgReading.timestamp / FUZZER), (float) unitized(bgReading.calculated_value))); } else if (bgReading.calculated_value >= 40) { lowValues.add( new PointValue( (float) (bgReading.timestamp / FUZZER), (float) unitized(bgReading.calculated_value))); } else if (bgReading.calculated_value > 13) { lowValues.add(new PointValue((float) (bgReading.timestamp / FUZZER), (float) unitized(40))); } } } public Line highLine() { List<PointValue> highLineValues = new ArrayList<PointValue>(); highLineValues.add(new PointValue((float) start_time, (float) highMark)); highLineValues.add(new PointValue((float) end_time, (float) highMark)); Line highLine = new Line(highLineValues); highLine.setHasPoints(false); highLine.setStrokeWidth(1); highLine.setColor(Utils.COLOR_ORANGE); return highLine; } public Line lowLine() { List<PointValue> lowLineValues = new ArrayList<PointValue>(); lowLineValues.add(new PointValue((float) start_time, (float) lowMark)); lowLineValues.add(new PointValue((float) end_time, (float) lowMark)); Line lowLine = new Line(lowLineValues); lowLine.setHasPoints(false); lowLine.setAreaTransparency(50); lowLine.setColor(Color.parseColor("#C30909")); lowLine.setStrokeWidth(1); lowLine.setFilled(true); return lowLine; } public Line maxShowLine() { List<PointValue> maxShowValues = new ArrayList<PointValue>(); maxShowValues.add(new PointValue((float) start_time, (float) defaultMaxY)); maxShowValues.add(new PointValue((float) end_time, (float) defaultMaxY)); Line maxShowLine = new Line(maxShowValues); maxShowLine.setHasLines(false); maxShowLine.setHasPoints(false); return maxShowLine; } public Line minShowLine() { List<PointValue> minShowValues = new ArrayList<PointValue>(); minShowValues.add(new PointValue((float) start_time, (float) defaultMinY)); minShowValues.add(new PointValue((float) end_time, (float) defaultMinY)); Line minShowLine = new Line(minShowValues); minShowLine.setHasPoints(false); minShowLine.setHasLines(false); return minShowLine; } ///////// AXIS RELATED////////////// public Axis yAxis() { Axis yAxis = new Axis(); yAxis.setAutoGenerated(false); List<AxisValue> axisValues = new ArrayList<AxisValue>(); for (int j = 1; j <= 12; j += 1) { if (doMgdl) { axisValues.add(new AxisValue(j * 50)); } else { axisValues.add(new AxisValue(j * 2)); } } yAxis.setValues(axisValues); yAxis.setHasLines(true); yAxis.setMaxLabelChars(5); yAxis.setInside(true); yAxis.setTextSize(axisTextSize); return yAxis; } public Axis xAxis() { Axis xAxis = new Axis(); xAxis.setAutoGenerated(false); List<AxisValue> xAxisValues = new ArrayList<AxisValue>(); GregorianCalendar now = new GregorianCalendar(); GregorianCalendar today = new GregorianCalendar( now.get(Calendar.YEAR), now.get(Calendar.MONTH), now.get(Calendar.DAY_OF_MONTH)); final java.text.DateFormat timeFormat = hourFormat(); timeFormat.setTimeZone(TimeZone.getDefault()); double start_hour_block = today.getTime().getTime(); double timeNow = new Date().getTime(); for (int l = 0; l <= 24; l++) { if ((start_hour_block + (60000 * 60 * (l))) < timeNow) { if ((start_hour_block + (60000 * 60 * (l + 1))) >= timeNow) { endHour = start_hour_block + (60000 * 60 * (l)); l = 25; } } } for (int l = 0; l <= 24; l++) { double timestamp = (endHour - (60000 * 60 * l)); xAxisValues.add( new AxisValue((long) (timestamp / FUZZER), (timeFormat.format(timestamp)).toCharArray())); } xAxis.setValues(xAxisValues); xAxis.setHasLines(true); xAxis.setTextSize(axisTextSize); return xAxis; } private SimpleDateFormat hourFormat() { return new SimpleDateFormat(DateFormat.is24HourFormat(context) ? "HH" : "h a"); } public static boolean isXLargeTablet(Context context) { return (context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_XLARGE; } public Axis previewXAxis() { List<AxisValue> previewXaxisValues = new ArrayList<AxisValue>(); final java.text.DateFormat timeFormat = hourFormat(); timeFormat.setTimeZone(TimeZone.getDefault()); for (int l = 0; l <= 24; l += hoursPreviewStep) { double timestamp = (endHour - (60000 * 60 * l)); previewXaxisValues.add( new AxisValue((long) (timestamp / FUZZER), (timeFormat.format(timestamp)).toCharArray())); } Axis previewXaxis = new Axis(); previewXaxis.setValues(previewXaxisValues); previewXaxis.setHasLines(true); previewXaxis.setTextSize(previewAxisTextSize); return previewXaxis; } ///////// VIEWPORT RELATED////////////// public Viewport advanceViewport(Chart chart, Chart previewChart) { viewport = new Viewport(previewChart.getMaximumViewport()); viewport.inset((float) ((86400000 / 2.5) / FUZZER), 0); double distance_to_move = ((new Date().getTime()) / FUZZER) - viewport.left - (((viewport.right - viewport.left) / 2)); viewport.offset((float) distance_to_move, 0); return viewport; } public double unitized(double value) { if (doMgdl) { return value; } else { return mmolConvert(value); } } public String unitized_string(double value) { DecimalFormat df = new DecimalFormat("#"); if (value >= 400) { return "HIGH"; } else if (value >= 40) { if (doMgdl) { df.setMaximumFractionDigits(0); return df.format(value); } else { df.setMaximumFractionDigits(1); // next line ensures mmol/l value is XX.x always. Required by PebbleSync, and probably not // a bad idea. df.setMinimumFractionDigits(1); return df.format(mmolConvert(value)); } } else if (value > 12) { return "LOW"; } else { switch ((int) value) { case 0: return "??0"; case 1: return "?SN"; case 2: return "??2"; case 3: return "?NA"; case 5: return "?NC"; case 6: return "?CD"; case 9: return "?AD"; case 12: return "?RF"; default: return "???"; } } } public String unitizedDeltaString(double value) { DecimalFormat df = new DecimalFormat("#"); df.setMaximumFractionDigits(1); String delta_sign = ""; if (value > 0.1) { delta_sign = "+"; } if (doMgdl) { return delta_sign + df.format(unitized(value)) + " mg/dl"; } else { df.setMinimumFractionDigits(1); df.setMinimumIntegerDigits(1); return delta_sign + df.format(unitized(value)) + " mmol"; } } public double mmolConvert(double mgdl) { return mgdl * Constants.MGDL_TO_MMOLL; } public String unit() { if (doMgdl) { return "mg/dl"; } else { return "mmol"; } } }