package com.nightscout;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Typeface;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.RemoteViews;
import android.widget.TextView;
import com.androidplot.util.PixelUtils;
import com.androidplot.xy.BoundaryMode;
import com.androidplot.xy.LineAndPointFormatter;
import com.androidplot.xy.PointLabelFormatter;
import com.androidplot.xy.PointLabeler;
import com.androidplot.xy.XYGraphWidget;
import com.androidplot.xy.XYPlot;
import com.androidplot.xy.XYSeries;
import com.androidplot.xy.XYStepMode;
import com.nightscout.data.dao.TimeMatechedRecordDao;
import com.nightscout.events.BTStatusEvent;
import com.nightscout.events.RefreshData;
import com.nightscout.events.RefreshError;
import com.nightscout.events.RefreshingData;
import com.nightscout.events.WidgetData;
import com.squareup.otto.Subscribe;
import java.text.DecimalFormat;
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.TimerTask;
import com.nightscout.data.TimeMatechedRecord;
public class MainActivity extends Activity implements View.OnTouchListener {
private long rangeStart;
@Override
protected void onResume() {
super.onResume();
Log.i(TAG, "onResume");
isDisplayed = true;
updateUI();
mHandler.sendMessageDelayed(Message.obtain(mHandler, TICK_WHAT), mFrequency);
}
@Override
protected void onStart() {
super.onStart();
Log.i(TAG, "onStart");
mHandlerWidget.sendMessageDelayed(Message.obtain(mHandlerWidget, TICK_WHAT), mFrequencyWidget);
}
@Override
protected void onStop() {
super.onStop();
Log.i(TAG, "onStop");
isDisplayed = false;
}
static final String TAG = MainActivity.class.getSimpleName();
static final SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm");
int time = 20;
//Timer timer;
TimerTask task;
int range = 300*40;
boolean isDisplayed = false;
boolean isBTconnected = false;
XYPlot plot;
XYSeriesData series1 ;
XYSeriesConstMin seriesMin ;
XYSeriesConstMax seriesMax ;
TextView tv1 ;
TextView tv2 ;
TextView tv3 ;
TextView status ;
TextView tvInRange ;
ProgressBar progressBar;
Handler handler;
private Button button;
private Button button6;
private Button button12;
private Button button24;
private Button buttonRefresh;
private long m_lastPacketTime;
private final long mFrequency = 100; // milliseconds
private final int TICK_WHAT = 2;
private Handler mHandler = new Handler() {
public void handleMessage(Message m) {
updateElapsedTime();
if(isDisplayed) {
sendMessageDelayed(Message.obtain(this, TICK_WHAT), mFrequency);
}
}
};
private final long mFrequencyWidget = 60000; // milliseconds
private Handler mHandlerWidget = new Handler() {
public void handleMessage(Message m) {
updateWidgets();
}
};
private void updateElapsedTime() {
if(isDisplayed) {
long countDown = 300 - (System.currentTimeMillis() - m_lastPacketTime) / 1000;
while(countDown<0) {
countDown += 300;
}
tvInRange.setText("" + countDown);
progressBar.setProgress((int) countDown);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate");
setContentView(R.layout.activity_main);
try {
MainApp.bus().unregister(this);
} catch (RuntimeException x) {
// Ignore
}
MainApp.bus().register(this);
{
String alarm = Context.ALARM_SERVICE;
AlarmManager am = ( AlarmManager ) getSystemService( alarm );
Intent intent = new Intent( "REFRESH_THIS" );
PendingIntent pi = PendingIntent.getBroadcast( this, 0, intent, 0 );
int type = AlarmManager.ELAPSED_REALTIME_WAKEUP;
long interval = 300000L;
long triggerTime = SystemClock.elapsedRealtime() + interval;
am.cancel(pi);
am.setRepeating( type, SystemClock.elapsedRealtime(), interval, pi );
}
handler = new Handler();
tv1 = (TextView) findViewById(R.id.text);
tv2 = (TextView) findViewById(R.id.text2);
tv3 = (TextView) findViewById(R.id.text3);
status = (TextView) findViewById(R.id.textViewStatus);
tvInRange = (TextView) findViewById(R.id.textInRange);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
progressBar.setMax(300);
initButtonsAndSetup();
// initialize our XYPlot reference:
plot = (XYPlot) findViewById(R.id.mySimpleXYPlot);
plot.setOnTouchListener(this);
// Turn the above arrays into XYSeries':
series1 = new XYSeriesData();
seriesMin = new XYSeriesConstMin();
seriesMax = new XYSeriesConstMax();
// Create a formatter to use for drawing a series using LineAndPointRenderer
// and configure it from xml:
Paint backgroundPaint = plot.getBackgroundPaint();
backgroundPaint.setColor(Color.BLACK);
plot.setBackgroundPaint(backgroundPaint);
plot.setBorderPaint(null);
// LineAndPointFormatter series1Format = createLineAndPointFormatter();
// add a new series' to the xyplot:
plot.addSeries(seriesMin, createLineAndPointFormatter(Color.RED, 2,true));
plot.addSeries(seriesMax, createLineAndPointFormatter(Color.YELLOW, 2,true));
plot.addSeries(series1, createLineAndPointFormatter(Color.WHITE, 6,false));
// reduce the number of range labels
plot.setRangeStep(XYStepMode.INCREMENT_BY_VAL, 2);
plot.setDomainStep(XYStepMode.INCREMENT_BY_VAL, 1800);
// plot.getGraphWidget().setDomainLabelOrientation(-90);
XYGraphWidget graphWidget = plot.getGraphWidget();
// graphWidget.position(-10, XLayoutStyle.ABSOLUTE_FROM_LEFT, 0, YLayoutStyle.ABSOLUTE_FROM_TOP);
Paint gridBackgroundPaint = graphWidget.getGridBackgroundPaint();
gridBackgroundPaint.setColor(Color.GRAY);
gridBackgroundPaint.setAlpha(250);
gridBackgroundPaint.setStyle(Paint.Style.STROKE);
graphWidget.setGridBackgroundPaint(gridBackgroundPaint);
graphWidget.setRangeAxisLeft(false);
// Paint borderPaint = new Paint();
// borderPaint.setColor(Color.GRAY);
// borderPaint.setStyle(Paint.Style.STROKE);
// graphWidget.setBorderPaint(borderPaint);
// graphWidget.setRangeLabelHorizontalOffset(PixelUtils.dpToPix(13));
graphWidget.setRangeLabelVerticalOffset(PixelUtils.dpToPix(-4));
graphWidget.setRangeTick(true);
graphWidget.setDomainTick(true);
Paint rangeLabelPaint = graphWidget.getRangeLabelPaint();
rangeLabelPaint.setTextAlign(Paint.Align.LEFT);
// rangeLabelPaint.setTypeface(Typeface.DEFAULT_BOLD);
rangeLabelPaint.setTypeface(Typeface.create("sans-serif-light",Typeface.BOLD));
rangeLabelPaint.setColor(Color.WHITE);
graphWidget.setRangeLabelPaint(rangeLabelPaint);
Paint domainLabelPaint = graphWidget.getDomainLabelPaint();
domainLabelPaint.setTypeface(Typeface.create("sans-serif-light",Typeface.BOLD));
domainLabelPaint.setColor(Color.WHITE);
graphWidget.setDomainLabelPaint(domainLabelPaint);
// For vertical lines:
Paint domainGridLine = graphWidget.getDomainGridLinePaint();
domainGridLine.setColor(Color.GRAY);
domainGridLine.setAlpha(230);
graphWidget.setDomainGridLinePaint(domainGridLine);//ridLinePaint().setColor(Color.TRANSPARENT);
graphWidget.setDomainSubGridLinePaint(null);
graphWidget.setDomainLabelTickExtension(10);
//For Horizontal lines:
Paint gridLine = graphWidget.getRangeGridLinePaint();
gridLine.setColor(Color.GRAY);
gridLine.setAlpha(230);
graphWidget.setRangeGridLinePaint(gridLine);//idLinePaint().setColor(Color.TRANSPARENT);
graphWidget.setRangeSubGridLinePaint(null);
graphWidget.setRangeLabelTickExtension(5);
graphWidget.setBackgroundPaint(null);
// SizeMetrics sm = new SizeMetrics(0, SizeLayoutType.FILL, 0,SizeLayoutType.FILL);
// graphWidget.setSize(sm);
graphWidget.setDrawMarkersEnabled(false);
plot.getLayoutManager().remove(plot.getLegendWidget());
plot.getLayoutManager().remove(plot.getDomainLabelWidget());
plot.getLayoutManager().remove(plot.getRangeLabelWidget());
plot.getLayoutManager().remove(plot.getTitleWidget());
plot.getLayoutManager().refreshLayout();
// plot.getLegendWidget().setVisible(false);
// plot.getDomainLabelWidget().setVisible(false);
// plot.getRangeLabelWidget().setVisible(false);
// plot.getTitleWidget().setVisible(false);
// plot.getGraphWidget().position(-0.5f, XLayoutStyle.RELATIVE_TO_RIGHT,
// -0.5f, YLayoutStyle.RELATIVE_TO_BOTTOM,
// AnchorPosition.CENTER);
// graphWidget.setRangeLabelWidth(30);
// graphWidget.setDomainLabelWidth(10);
plot.setRangeValueFormat(new DecimalFormat("#"));
Format domainValueFormat = new Format() {
@Override
public Object parseObject(String string, ParsePosition position) {
return null;
}
@Override
public StringBuffer format(Object object, StringBuffer buffer,FieldPosition field) {
long object1 = ((Double) object).longValue();
Date tmp = new Date(object1*1000);
String formated = timeFormat.format(tmp);
// Log.d(TAG,"formatting "+tmp + " to "+formated);
return buffer.append(formated);
}
};
plot.setDomainValueFormat(domainValueFormat);//mat(new DecimalFormat("#"));
plot.setRangeBoundaries(2, BoundaryMode.FIXED, 22,BoundaryMode.FIXED);
// plot.setDomainBoundaries(6, 12, BoundaryMode.FIXED);
//startTimer();
updateUI();
}
private void initButtonsAndSetup() {
button = (Button) findViewById(R.id.button3);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
range = 300*40;
plot.setDomainStep(XYStepMode.INCREMENT_BY_VAL, 1800);
plot.setDomainBoundaries( series1.glucoseReadRecords[series1.glucoseReadRecords.length-1].LocalSecondsSinceDexcomEpoch-range, series1.glucoseReadRecords[series1.glucoseReadRecords.length-1].LocalSecondsSinceDexcomEpoch, BoundaryMode.FIXED);
// plot.redraw();
updateUI();
}
});
button6 = (Button) findViewById(R.id.button6);
button6.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
range = 300*80;
plot.setDomainStep(XYStepMode.INCREMENT_BY_VAL, 3600);
plot.setDomainBoundaries( series1.glucoseReadRecords[series1.glucoseReadRecords.length-1].LocalSecondsSinceDexcomEpoch-range, series1.glucoseReadRecords[series1.glucoseReadRecords.length-1].LocalSecondsSinceDexcomEpoch, BoundaryMode.FIXED);
// plot.redraw();
updateUI();
}
});
button12 = (Button) findViewById(R.id.button12);
button12.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
range = 300*160;
plot.setDomainStep(XYStepMode.INCREMENT_BY_VAL, 7200);
plot.setDomainBoundaries( series1.glucoseReadRecords[series1.glucoseReadRecords.length-1].LocalSecondsSinceDexcomEpoch-range, series1.glucoseReadRecords[series1.glucoseReadRecords.length-1].LocalSecondsSinceDexcomEpoch, BoundaryMode.FIXED);
// plot.redraw();
updateUI();
}
});
button24 = (Button) findViewById(R.id.button24);
button24.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
range = 300*320;
plot.setDomainStep(XYStepMode.INCREMENT_BY_VAL, 14400);
plot.setDomainBoundaries( series1.glucoseReadRecords[series1.glucoseReadRecords.length-1].LocalSecondsSinceDexcomEpoch-range, series1.glucoseReadRecords[series1.glucoseReadRecords.length-1].LocalSecondsSinceDexcomEpoch, BoundaryMode.FIXED);
// plot.redraw();
updateUI();
// button24.setPressed(false);
}
});
buttonRefresh = (Button) findViewById(R.id.buttonRefresh);
buttonRefresh.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
MainApp.bus().post(new RefreshData());
}
});
}
private LineAndPointFormatter createLineAndPointFormatter(int color,float width, boolean line) {
LineAndPointFormatter series1Format = new LineAndPointFormatter();
PointLabelFormatter pointLabelFormatter = new PointLabelFormatter();
series1Format.setPointLabeler(new PointLabeler() {
@Override
public String getLabel(XYSeries series, int index) {
return "";
}
});
series1Format.setPointLabelFormatter(pointLabelFormatter);
series1Format.configure(getApplicationContext(),
R.xml.line_point_formatter_with_plf1);
Paint lp = series1Format.getLinePaint();
lp.setColor(color);
lp.setAntiAlias(false);
lp.setStrokeWidth(width);
if(line) {
series1Format.setVertexPaint(null);
series1Format.setLinePaint(lp);
} else {
Paint linep = new Paint(lp);
linep.setStrokeWidth(1);
linep.setColor(color);
series1Format.setLinePaint(null);
series1Format.setVertexPaint(lp);
}
series1Format.setPointLabelFormatter(null);
return series1Format;
}
@Override
public void onDestroy()
{
super.onDestroy();
Log.i(TAG, "Activity destroyed");
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Subscribe
public void onRefreshErrorEvent(final RefreshError event) {
Log.d(TAG, "onRefreshErrorEvent");
runOnUiThread(new Runnable() {
@Override
public void run() {
try {
status.setText(event.mMessage);
} catch (RuntimeException e) {
Log.e(TAG, "updateUI error " + e.getMessage(), e);
}
}
});
}
@Subscribe
public void onRefreshingDataEvent(RefreshingData event) {
Log.d(TAG, "onRefreshingDataEvent");
if(isDisplayed) {
}
}
@Subscribe
public void onRefreshDataEvent(WidgetData event) {
updateWidgets();
}
@Subscribe
public void onRefreshDataEvent(RefreshData event) {
Log.d(TAG, "onRefreshDataEvent");
//if(isDisplayed) {
updateUI();
//}
updateWidgets();
}
@Subscribe
public void onBTStatusEvent(final BTStatusEvent event) {
Log.d(TAG, "onBTStatusEvent");
isBTconnected = "BT".equals(event.mStatus);
runOnUiThread(new Runnable() {
@Override
public void run() {
status.setText(""+ getBTstatusString());
}});
}
TimeMatechedRecord current,beforeCurrent;
public void updateUI() {
Log.d(TAG, "updateUI");
runOnUiThread(new Runnable() {
@Override
public void run() {
try {
DecimalFormat df = new DecimalFormat("#.00");
tv1.setText("..");
status.setText(""+ getBTstatusString());
TimeMatechedRecordDao dao = new TimeMatechedRecordDao(getApplicationContext());
List<TimeMatechedRecord> all = dao.listAll();
// Log.d(TAG, "Data range in local DB "
// + timeFormat.format( all.get(0).LocalSecondsSinceDexcomEpoch )
// + timeFormat.format( all.get(all.size()-1).LocalSecondsSinceDexcomEpoch )
// );
if(all.size()>300) {
all = all.subList(all.size() - 300, all.size());
}
TimeMatechedRecord[] glucoseReadRecords = all.toArray(new TimeMatechedRecord[all.size()]);
// Log.d(TAG, "Data updated "+all.size());
TimeMatechedRecord glucoseReadRecord = glucoseReadRecords[glucoseReadRecords.length - 1];
current = glucoseReadRecord;
beforeCurrent = glucoseReadRecords[glucoseReadRecords.length - 2];
series1.glucoseReadRecords = glucoseReadRecords;
seriesMin.glucoseReadRecords = glucoseReadRecords;
seriesMax.glucoseReadRecords = glucoseReadRecords;
rangeStart = glucoseReadRecord.LocalSecondsSinceDexcomEpoch;
plot.setDomainBoundaries(
rangeStart - range,
rangeStart,
BoundaryMode.FIXED);
lastScrolling = 0;
plot.redraw();
int glucoseValueWithFlags = (int) glucoseReadRecord.GlucoseValue;
if (glucoseValueWithFlags == 10) {
tv1.setText("???");
} else if (glucoseValueWithFlags == 5) {
tv1.setText("?");
} else if (glucoseValueWithFlags==0 ) {
tv1.setText("");
}else{
tv1.setText("" + df.format(glucoseValueWithFlags / 18d));
}
tv3.setText(timeFormat.format( glucoseReadRecord.LocalSecondsSinceDexcomEpoch*1000));
m_lastPacketTime =glucoseReadRecord.LocalSecondsSinceDexcomEpoch*1000;
Log.d(TAG,"Last record in DB " + glucoseReadRecord.timeIndex);
} catch (RuntimeException e) {
Log.e(TAG, "updateUI error "+e.getMessage(),e);
}
}
});
}
private String getBTstatusString() {
return (isBTconnected ? "BT" : "");
// return (isBTconnected ? "" : "");
}
public void updateWidgets() {
handler.post(new Runnable() {
@Override
public void run() {
Log.d(TAG,"updating widgets");
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getApplicationContext());
ComponentName thisWidget = new ComponentName(getApplicationContext(),DexAppWidgetProvider.class);
int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
DecimalFormat df = new DecimalFormat("0.00");
for (int widgetId : allWidgetIds) {
AppWidgetProviderInfo info = appWidgetManager.getAppWidgetInfo(widgetId);
RemoteViews remoteViews = new RemoteViews(getApplicationContext().getPackageName(),R.layout.widget_layout);
String widgetText = "No data yet";
TimeMatechedRecord glucoseReadRecord = current;
TimeMatechedRecord glucoseReadRecordOld = beforeCurrent;
if(glucoseReadRecord.GlucoseValue!=0) {
widgetText =
" " +
df.format(glucoseReadRecord.GlucoseValue / 18d) + " (" +
df.format(glucoseReadRecord.GlucoseValue / 18d - glucoseReadRecordOld.GlucoseValue / 18d) +
") " ;
}
remoteViews.setTextViewText(R.id.textViewStatus,
""+
((System.currentTimeMillis() - m_lastPacketTime)/60000)
+ " mins ago "
);
remoteViews.setTextViewText(R.id.update, widgetText);
appWidgetManager.updateAppWidget(widgetId, remoteViews);
}
if(allWidgetIds.length>0) {
mHandlerWidget.sendMessageDelayed(Message.obtain(mHandlerWidget, TICK_WHAT), mFrequencyWidget);
}
}
});
}
PointF firstFinger;
float lastScrolling;
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: // Start gesture
firstFinger = new PointF(motionEvent.getX(), motionEvent.getY());
//Log.d(TAG,"onTouch "+view + " "+motionEvent);
break;
case MotionEvent.ACTION_UP:
lastScrolling+= (motionEvent.getX() -firstFinger.x)*2;
//Log.d(TAG,"onTouch ACTION_UP "+lastScrolling);
plot.setDomainBoundaries(
rangeStart - range-lastScrolling,
rangeStart-lastScrolling,
BoundaryMode.FIXED);
plot.redraw();
break;
case MotionEvent.ACTION_MOVE:
float deltaX = (motionEvent.getX() - firstFinger.x) * 20;
lastScrolling+= deltaX;
//Log.d(TAG,"onTouch ACTION_MOVE "+lastScrolling + " "+deltaX) ;
if(deltaX>60 || deltaX<-60) {
plot.setDomainBoundaries(
rangeStart - range - lastScrolling,
rangeStart - lastScrolling,
BoundaryMode.FIXED);
plot.redraw();
firstFinger = new PointF(motionEvent.getX(), motionEvent.getY());
}
break;
}
return true;
}
private void scroll(float lastScrolling) {
//Log.d(TAG,"scroll "+lastScrolling);
}
}