diff --git a/.gitignore b/.gitignore index 04d47c56d6b887c18d9c38c1dd029ecbb1e9c277..007d64268841d3fb3282a82e2376522174a3d543 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ local.properties .idea bugfender.properties /app/app.iml +gradle.properties \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index bc39a1084d08f077d7b3212257ff6b73dbe50000..027d93daafacb3fecbcc0d36f3fc6073a5eeb215 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -27,7 +27,7 @@ apply plugin: 'realm-android' def gitVersion() { // current dir is <your proj>/app, so it's likely that all your git repo files are in the dir // above. - ext.repo = Grgit.open(project.file('..')) + ext.repo = Grgit.open() // should result in the same value as running // git tag -l | wc -l or git tag -l | grep -c ".*" - @@ -36,14 +36,15 @@ def gitVersion() { } def gitCommitId() { - //def process = ['sh', '-c', 'git tag -l | grep -c ".*" -'].execute().text.trim() - //return process.toInteger() + 1 - //return 42 - // current dir is <your proj>/app, so it's likely that all your git repo files are in the dir - // above. - ext.repo = Grgit.open(project.file('..')) + ext.repo = Grgit.open() + + return ext.repo.log().first().id.substring(0, 7) //+ " " + ext.repo.branch.current.name +} + - return ext.repo.log().first().id.substring(0, 7) +def gitBranch() { + ext.repo = Grgit.open() + return ext.repo.branch.current.name } def getBugfenderApiKey() { @@ -66,7 +67,7 @@ android { applicationId "info.nightscout.android" minSdkVersion 14 targetSdkVersion 23 - versionName project.properties['version'] + "/" + gitCommitId() + versionName project.properties['version'] + "/" + gitCommitId() // + " (" + gitBranch()+")" versionCode gitVersion() buildConfigField "String", "BUGFENDER_API_KEY", getBugfenderApiKey() } @@ -154,8 +155,7 @@ dependencies { compile 'com.mikepenz:google-material-typeface:2.2.0.1.original@aar' compile 'uk.co.chrisjenx:calligraphy:2.2.0' compile 'com.bugfender.sdk:android:0.6.2' - compile 'com.github.PhilJay:MPAndroidChart-Realm:v2.0.2@aar' - compile 'com.github.PhilJay:MPAndroidChart:v3.0.1' + compile 'com.jjoe64:graphview:4.2.1' compile 'com.android.support:support-v4:23.4.0' compile 'com.google.code.gson:gson:2.7' compile 'com.squareup.retrofit2:retrofit:2.1.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 17456ec980eb0686f523c15b44c12a3d4973e906..4f075c37ba2f7387d322e3ed95c22c04eb2b6368 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -80,11 +80,7 @@ <receiver android:name=".medtronic.service.MedtronicCnlAlarmReceiver" /> <receiver android:name=".upload.nightscout.NightscoutUploadReceiver" /> - <receiver android:name=".xdrip_plus.XDripPlusUploadReceiver" /><!-- ATTENTION: This was auto-generated to add Google Play services to your project for - App Indexing. See https://g.co/AppIndexing/AndroidStudio for more information. --> - <meta-data - android:name="com.google.android.gms.version" - android:value="@integer/google_play_services_version" /> + <receiver android:name=".xdrip_plus.XDripPlusUploadReceiver" /> </application> diff --git a/app/src/main/java/info/nightscout/android/medtronic/MainActivity.java b/app/src/main/java/info/nightscout/android/medtronic/MainActivity.java index a16375d180e4bc9f250490bb4425cce173ee0ed2..681f50ee06f600d809b4b987fb57f55366ffcebc 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/MainActivity.java +++ b/app/src/main/java/info/nightscout/android/medtronic/MainActivity.java @@ -12,6 +12,7 @@ import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Paint; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbManager; import android.net.Uri; @@ -34,28 +35,19 @@ import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; -import android.view.MotionEvent; import android.view.View; import android.widget.TextView; import android.widget.TextView.BufferType; - -import com.github.mikephil.charting.charts.ScatterChart; -import com.github.mikephil.charting.components.AxisBase; -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.ScatterData; -import com.github.mikephil.charting.data.ScatterDataSet; -import com.github.mikephil.charting.data.realm.implementation.RealmScatterDataSet; -import com.github.mikephil.charting.formatter.IAxisValueFormatter; -import com.github.mikephil.charting.formatter.IValueFormatter; -import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; -import com.github.mikephil.charting.listener.ChartTouchListener; -import com.github.mikephil.charting.listener.OnChartGestureListener; -import com.github.mikephil.charting.renderer.ScatterChartRenderer; -import com.github.mikephil.charting.renderer.scatter.IShapeRenderer; -import com.github.mikephil.charting.utils.Transformer; -import com.github.mikephil.charting.utils.ViewPortHandler; +import android.widget.Toast; + +import com.jjoe64.graphview.DefaultLabelFormatter; +import com.jjoe64.graphview.GraphView; +import com.jjoe64.graphview.Viewport; +import com.jjoe64.graphview.series.DataPoint; +import com.jjoe64.graphview.series.DataPointInterface; +import com.jjoe64.graphview.series.OnDataPointTapListener; +import com.jjoe64.graphview.series.PointsGraphSeries; +import com.jjoe64.graphview.series.Series; import com.mikepenz.google_material_typeface_library.GoogleMaterial; import com.mikepenz.materialdrawer.AccountHeaderBuilder; import com.mikepenz.materialdrawer.Drawer; @@ -65,9 +57,8 @@ import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; import java.text.DateFormat; import java.text.DecimalFormat; -import java.util.ArrayList; +import java.text.NumberFormat; import java.util.Date; -import java.util.List; import java.util.Locale; import java.util.Queue; import java.util.concurrent.ArrayBlockingQueue; @@ -83,7 +74,6 @@ import info.nightscout.android.model.medtronicNg.PumpInfo; import info.nightscout.android.model.medtronicNg.PumpStatusEvent; import info.nightscout.android.settings.SettingsActivity; import info.nightscout.android.upload.nightscout.NightscoutUploadIntentService; -import io.realm.DynamicRealmObject; import io.realm.Realm; import io.realm.RealmChangeListener; import io.realm.RealmResults; @@ -92,6 +82,7 @@ import uk.co.chrisjenx.calligraphy.CalligraphyContextWrapper; public class MainActivity extends AppCompatActivity implements OnSharedPreferenceChangeListener, OnEulaAgreedTo { private static final String TAG = MainActivity.class.getSimpleName(); + public static final float MMOLXLFACTOR = 18.016f; public static int batLevel = 0; public static boolean reducePollOnPumpAway = false; @@ -99,12 +90,18 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc public static long lowBatteryPollInterval = MedtronicCnlIntentService.LOW_BATTERY_POLL_PERIOD_MS; private static long activePumpMac; + private int chartZoom = 3; + private boolean hasZoomedChart = false; + + private NumberFormat sgvFormatter; + private boolean mmolxl; + private boolean mmolxlDecimals; boolean mEnableCgmService = true; SharedPreferences prefs = null; private PumpInfo mActivePump; private TextView mTextViewLog; // This will eventually move to a status page. - private ScatterChart mChart; + private GraphView mChart; private Intent mNightscoutUploadService; private Handler mUiRefreshHandler = new Handler(); private Runnable mUiRefreshRunnable = new RefreshDisplayRunnable(); @@ -112,17 +109,31 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc private StatusMessageReceiver statusMessageReceiver = new StatusMessageReceiver(); private MedtronicCnlAlarmReceiver medtronicCnlAlarmReceiver = new MedtronicCnlAlarmReceiver(); + /** + * calculate the next poll timestamp based on last svg event + * + * @param pumpStatusData + * @return timestamp + */ public static long getNextPoll(PumpStatusEvent pumpStatusData) { - long nextPoll = pumpStatusData.getEventDate().getTime() + pumpStatusData.getPumpTimeOffset() - + MedtronicCnlIntentService.POLL_GRACE_PERIOD_MS; + long nextPoll = pumpStatusData.getEventDate().getTime() + pumpStatusData.getPumpTimeOffset(), + now = System.currentTimeMillis(); - if (pumpStatusData.getBatteryPercentage() > 25) { - // poll every 5 min - nextPoll += MainActivity.pollInterval; + // align to next poll slot + if (nextPoll + 2 * 60 * 60 * 1000 < now) { // last event more than 2h old -> could be a calibration + nextPoll = System.currentTimeMillis() + 1000; } else { - // if pump battery seems to be empty reduce polling to save battery (every 15 min) - //TODO add message & document it - nextPoll += MainActivity.lowBatteryPollInterval; + // align to poll interval + nextPoll += (((now - nextPoll) / MainActivity.pollInterval)) * MainActivity.pollInterval + + MedtronicCnlIntentService.POLL_GRACE_PERIOD_MS; + if (pumpStatusData.getBatteryPercentage() > 25) { + // poll every 5 min + nextPoll += MainActivity.pollInterval; + } else { + // if pump battery seems to be empty reduce polling to save battery (every 15 min) + //TODO add message & document it + nextPoll += MainActivity.lowBatteryPollInterval; + } } return nextPoll; @@ -149,6 +160,18 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc MainActivity.pollInterval = Long.parseLong(prefs.getString("pollInterval", Long.toString(MedtronicCnlIntentService.POLL_PERIOD_MS))); MainActivity.lowBatteryPollInterval = Long.parseLong(prefs.getString("lowBatPollInterval", Long.toString(MedtronicCnlIntentService.LOW_BATTERY_POLL_PERIOD_MS))); MainActivity.reducePollOnPumpAway = prefs.getBoolean("doublePollOnPumpAway", false); + chartZoom = Integer.parseInt(prefs.getString("chartZoom", "3")); + mmolxl = prefs.getBoolean("mmolxl", false); + mmolxlDecimals = prefs.getBoolean("mmolDecimals", false); + + if (mmolxl) { + if (mmolxlDecimals) + sgvFormatter = new DecimalFormat("0.00"); + else + sgvFormatter = new DecimalFormat("0.0"); + } else { + sgvFormatter = new DecimalFormat("0"); + } // Disable battery optimization to avoid missing values on 6.0+ // taken from https://github.com/NightscoutFoundation/xDrip/blob/master/app/src/main/java/com/eveningoutpost/dexdrip/Home.java#L277L298 @@ -260,7 +283,8 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc stopCgmService(); finish(); } else if (drawerItem.equals(itemGetNow)) { - startCgmService(); + // It was triggered by user so start reading of data now and not based on last poll. + startCgmService(0); } else if (drawerItem.equals(itemClearLog)) { clearLogText(); } @@ -271,118 +295,57 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc .build(); mTextViewLog = (TextView) findViewById(R.id.textview_log); - mChart = (ScatterChart) findViewById(R.id.chart); - - mChart.setDescription(null); // Hide the description - mChart.setTouchEnabled(true); - mChart.setPinchZoom(true); - mChart.setHighlightPerDragEnabled(false); - mChart.setHighlightPerTapEnabled(false); - mChart.setOnChartGestureListener(new OnChartGestureListener() { + mChart = (GraphView) findViewById(R.id.chart); - @Override - public void onChartGestureStart(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) {} + // disable scrolling at the moment + mChart.getViewport().setScalable(false); + mChart.getViewport().setScrollable(false); + mChart.getViewport().setXAxisBoundsManual(true); + final long now = System.currentTimeMillis(), + left = now - chartZoom * 60 * 60 * 1000; - @Override - public void onChartGestureEnd(MotionEvent me, ChartTouchListener.ChartGesture lastPerformedGesture) {} + mChart.getViewport().setMaxX(now); + mChart.getViewport().setMinX(left); + mChart.getViewport().setOnXAxisBoundsChangedListener(new Viewport.OnXAxisBoundsChangedListener() { @Override - public void onChartLongPressed(MotionEvent me) { - mChart.fitScreen(); + public void onXAxisBoundsChanged(double minX, double maxX, Reason reason) { + double rightX = mChart.getSeries().get(0).getHighestValueX(); + hasZoomedChart = (rightX != maxX || rightX - chartZoom * 60 * 60 * 1000 != minX); } - - @Override - public void onChartDoubleTapped(MotionEvent me) {} - - @Override - public void onChartSingleTapped(MotionEvent me) {} - - @Override - public void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY) {} - - @Override - public void onChartScale(MotionEvent me, float scaleX, float scaleY) {} - - @Override - public void onChartTranslate(MotionEvent me, float dX, float dY) {} }); - XAxis xAxis = mChart.getXAxis(); - xAxis.setPosition(XAxis.XAxisPosition.BOTTOM); - xAxis.setTextSize(10f); - xAxis.setTextColor(Color.WHITE); - xAxis.setDrawAxisLine(true); - xAxis.setDrawGridLines(true); - xAxis.setDrawLabels(true); - xAxis.setValueFormatter(new IAxisValueFormatter() { - private DateFormat mFormat = DateFormat.getTimeInstance(DateFormat.SHORT); - + mChart.setOnLongClickListener(new View.OnLongClickListener() { @Override - public String getFormattedValue(float value, AxisBase axis) { - return mFormat.format(new Date((long) value)); + public boolean onLongClick(View v) { + if (!mChart.getSeries().isEmpty() && !mChart.getSeries().get(0).isEmpty()) { + double rightX = mChart.getSeries().get(0).getHighestValueX(); + mChart.getViewport().setMaxX(rightX); + mChart.getViewport().setMinX(rightX - chartZoom * 60 * 60 * 1000); + } + hasZoomedChart = false; + return true; } }); + mChart.getGridLabelRenderer().setNumHorizontalLabels(6); + mChart.getGridLabelRenderer().setHumanRounding(false); - // left axis - mChart.getAxisLeft().setDrawLabels(false); - - // right axis - YAxis yAxis = mChart.getAxisRight(); - yAxis.setTextSize(10f); - yAxis.setTextColor(Color.WHITE); - - mChart.getLegend().setEnabled(false); // Hide the legend - - // TODO: remove if if "coloring bug" in MPAndroidChart is fixed - // see: https://github.com/PhilJay/MPAndroidChart/issues/2682 - mChart.setRenderer(new ScatterChartRenderer(mChart, mChart.getAnimator(), mChart.getViewPortHandler()) { - - float[] mPixelBuffer = new float[2]; - + mChart.getGridLabelRenderer().setLabelFormatter(new DefaultLabelFormatter() { + DateFormat mFormat = DateFormat.getTimeInstance(DateFormat.SHORT); @Override - protected void drawDataSet(Canvas c, IScatterDataSet dataSet) { - - ViewPortHandler viewPortHandler = mViewPortHandler; - - Transformer trans = mChart.getTransformer(dataSet.getAxisDependency()); - - float phaseY = mAnimator.getPhaseY(); - - IShapeRenderer renderer = dataSet.getShapeRenderer(); - if (renderer == null) { - Log.i("MISSING", "There's no IShapeRenderer specified for ScatterDataSet"); - return; - } - - int max = (int)(Math.min( - Math.ceil((float)dataSet.getEntryCount() * mAnimator.getPhaseX()), - (float)dataSet.getEntryCount())); - - for (int i = 0; i < max; i++) { - - Entry e = dataSet.getEntryForIndex(i); - - mPixelBuffer[0] = e.getX(); - mPixelBuffer[1] = e.getY() * phaseY; - - trans.pointValuesToPixel(mPixelBuffer); - - if (!viewPortHandler.isInBoundsRight(mPixelBuffer[0])) - break; - - if (!viewPortHandler.isInBoundsLeft(mPixelBuffer[0]) - || !viewPortHandler.isInBoundsY(mPixelBuffer[1])) - continue; - - mRenderPaint.setColor(dataSet.getColor(i)); - renderer.renderShape( - c, dataSet, mViewPortHandler, - mPixelBuffer[0], mPixelBuffer[1], - mRenderPaint); + public String formatLabel(double value, boolean isValueX) { + if (isValueX) { + return mFormat.format(new Date((long) value)); + } else { + if (mmolxl) { + return sgvFormatter.format(value / MMOLXLFACTOR); + } else { + return sgvFormatter.format(value); + } } - } - }); + }} + ); } @Override @@ -462,7 +425,18 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc } private void startCgmService() { - startCgmService(System.currentTimeMillis() + 1000); + startCgmServiceDelayed(0); + } + + private void startCgmServiceDelayed(long delay) { + RealmResults<PumpStatusEvent> results = mRealm.where(PumpStatusEvent.class) + .findAllSorted("eventDate", Sort.DESCENDING); + + if (results.size() > 0) { + startCgmService(getNextPoll(results.first()) + delay); + } else { + startCgmService(System.currentTimeMillis() + (delay==0?1000:delay)); + } } private void startCgmService(long initialPoll) { @@ -543,7 +517,17 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc mEnableCgmService = true; startCgmService(); } - } else if (key.equals("mmolxl")) { + } else if (key.equals("mmolxl") || key.equals("mmolDecimals")) { + mmolxl = sharedPreferences.getBoolean("mmolxl", false); + mmolxlDecimals = sharedPreferences.getBoolean("mmolDecimals", false); + if (mmolxl) { + if (mmolxlDecimals) + sgvFormatter = new DecimalFormat("0.00"); + else + sgvFormatter = new DecimalFormat("0.0"); + } else { + sgvFormatter = new DecimalFormat("0"); + } refreshDisplay(); } else if (key.equals("pollInterval")) { MainActivity.pollInterval = Long.parseLong(sharedPreferences.getString("pollInterval", @@ -553,6 +537,10 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc Long.toString(MedtronicCnlIntentService.LOW_BATTERY_POLL_PERIOD_MS))); } else if (key.equals("doublePollOnPumpAway")) { MainActivity.reducePollOnPumpAway = sharedPreferences.getBoolean("doublePollOnPumpAway", false); + } else if (key.equals("chartZoom")) { + chartZoom = Integer.parseInt(sharedPreferences.getString("chartZoom", "3")); + hasZoomedChart = false; + refreshDisplay(); } } @@ -737,7 +725,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc TextView textViewBg = (TextView) findViewById(R.id.textview_bg); TextView textViewBgTime = (TextView) findViewById(R.id.textview_bg_time); TextView textViewUnits = (TextView) findViewById(R.id.textview_units); - if (prefs.getBoolean("mmolxl", false)) { + if (mmolxl) { textViewUnits.setText(R.string.text_unit_mmolxl); } else { textViewUnits.setText(R.string.text_unit_mgxdl); @@ -763,27 +751,17 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc if (pumpStatusData != null) { - String sgvString, units; - if (prefs.getBoolean("mmolxl", false)) { - DecimalFormat df; - if (prefs.getBoolean("mmolDecimals", false)) - df = new DecimalFormat("0.00"); - else - df = new DecimalFormat("0.0"); - + String sgvString; + if (mmolxl) { float fBgValue = (float) pumpStatusData.getSgv(); - sgvString = df.format(fBgValue / 18.016f); - units = "mmol/L"; + sgvString = sgvFormatter.format(fBgValue / MMOLXLFACTOR); Log.d(TAG, sgvString + " mmol/L"); - } else { sgvString = String.valueOf(pumpStatusData.getSgv()); - units = "mg/dL"; Log.d(TAG, sgvString + " mg/dL"); } textViewBg.setText(sgvString); - textViewUnits.setText(units); textViewBgTime.setText(DateUtils.getRelativeTimeSpanString(pumpStatusData.getEventDate().getTime())); textViewTrend.setText(Html.fromHtml(renderTrendHtml(pumpStatusData.getCgmTrend()))); textViewIOB.setText(String.format(Locale.getDefault(), "%.2f", pumpStatusData.getActiveInsulin())); @@ -825,77 +803,102 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc private void updateChart(RealmResults<PumpStatusEvent> results) { int size = results.size(); - if (size == 0) return; + if (size == 0) { + final long now = System.currentTimeMillis(), + left = now - chartZoom * 60 * 60 * 1000; + + mChart.getViewport().setXAxisBoundsManual(true); + mChart.getViewport().setMaxX(now); + mChart.getViewport().setMinX(left); + + mChart.getViewport().setYAxisBoundsManual(true); + if (mmolxl) { + mChart.getViewport().setMinY(80 / MMOLXLFACTOR); + mChart.getViewport().setMaxY(120 / MMOLXLFACTOR); + } else { + mChart.getViewport().setMinY(80); + mChart.getViewport().setMaxY(120); + } + mChart.postInvalidate(); + return; + } - List<Entry> entries = new ArrayList<Entry>(size); - int[] colors = new int[size]; // getColor is called with (i/2) + DataPoint[] entries = new DataPoint[size]; + final long left = System.currentTimeMillis() - chartZoom * 60 * 60 * 1000; + int pos = 0; for (PumpStatusEvent pumpStatus: results) { // turn your data into Entry objects - int sgv = pumpStatus.getSgv(), - pos = entries.size(); - - entries.add(new Entry(pumpStatus.getEventDate().getTime(), pumpStatus.getSgv())); - //TODO: need to be configurable - if (sgv < 80) - colors[pos] = Color.RED; - else if (sgv <= 180) - colors[pos] = Color.GREEN; - else if (sgv <= 260) - colors[pos] = Color.YELLOW; - else - colors[pos] = Color.RED; + int sgv = pumpStatus.getSgv(); + + if (mmolxl) { + entries[pos++] = new DataPoint(pumpStatus.getEventDate(), pumpStatus.getSgv() / MMOLXLFACTOR); + } else { + entries[pos++] = new DataPoint(pumpStatus.getEventDate(), pumpStatus.getSgv()); + } } - if (mChart.getData() == null) { - mChart.setMinimumHeight(200); - ScatterDataSet dataSet = new ScatterDataSet(entries, null); + if (mChart.getSeries().size() == 0) { +// long now = System.currentTimeMillis(); +// entries = new DataPoint[1000]; +// int j = 0; +// for(long i = now - 24*60*60*1000; i < now - 30*60*1000; i+= 5*60*1000) { +// entries[j++] = new DataPoint(i, (float) (Math.random()*200 + 89)); +// } +// entries = Arrays.copyOfRange(entries, 0, j); - dataSet.setColors(colors); // disabled tue to a bug(??) in MPAndroid Chart - //dataSet.setColor(Color.LTGRAY); - dataSet.setValueTextColor(Color.WHITE); - dataSet.setScatterShape(ScatterChart.ScatterShape.CIRCLE); - dataSet.setScatterShapeSize(7.2f); - dataSet.setValueFormatter(new IValueFormatter() { - @Override - public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) { - DecimalFormat df; + PointsGraphSeries sgvSerie = new PointsGraphSeries(entries); +// sgvSerie.setSize(3.6f); +// sgvSerie.setColor(Color.LTGRAY); + + + sgvSerie.setOnDataPointTapListener(new OnDataPointTapListener() { + DateFormat mFormat = DateFormat.getTimeInstance(DateFormat.MEDIUM); - if (prefs.getBoolean("mmolxl", false)) { - if (prefs.getBoolean("mmolDecimals", false)) - df = new DecimalFormat("0.00"); - else - df = new DecimalFormat("0.0"); + @Override + public void onTap(Series series, DataPointInterface dataPoint) { + double sgv = dataPoint.getY(); - return df.format(value / 18.016f); + StringBuilder sb = new StringBuilder(mFormat.format(new Date((long) dataPoint.getX())) + ": "); + if (mmolxl) { + sb.append(sgvFormatter.format(sgv / MMOLXLFACTOR)); } else { - return new DecimalFormat("0").format(value); + sb.append(sgvFormatter.format(sgv)); } + Toast.makeText(getBaseContext(), sb.toString(), Toast.LENGTH_SHORT).show(); } }); - ArrayList<IScatterDataSet> dataSets = new ArrayList<IScatterDataSet>(); - dataSets.add(dataSet); + sgvSerie.setCustomShape(new PointsGraphSeries.CustomShape() { + @Override + public void draw(Canvas canvas, Paint paint, float x, float y, DataPointInterface dataPoint) { + double sgv = dataPoint.getY(); + if (sgv < 80) + paint.setColor(Color.RED); + else if (sgv <= 180) + paint.setColor(Color.GREEN); + else if (sgv <= 260) + paint.setColor(Color.YELLOW); + else + paint.setColor(Color.RED); + canvas.drawCircle(x, y, 3.6f, paint); + } + }); - ScatterData lineData = new ScatterData(dataSets); - mChart.setData(lineData); + mChart.getViewport().setYAxisBoundsManual(false); + mChart.addSeries(sgvSerie); } else { - ((ScatterDataSet)mChart.getScatterData().getDataSets().get(0)).setValues(entries); - ((ScatterDataSet)mChart.getScatterData().getDataSets().get(0)).setColors(colors); // disabled tue to a bug(??) in MPAndroid Chart + if (entries.length > 0) { + ((PointsGraphSeries) mChart.getSeries().get(0)).resetData(entries); + } } - //TODO: make the display timespan configurable - //long now = System.currentTimeMillis(); - - //Log.d(TAG, "Graph limits: " + (new Date(now - 24 * 60 * 60 * 1000) + " - " + (new Date(now)))); - //mChart.setVisibleXRangeMaximum(12); - //mChart.setVisibleXRangeMinimum(0); - //mChart.getXAxis().setAxisMaximum((now + 0*60*1000)); - //mChart.getXAxis().setAxisMinimum(now - 35 * 60 * 1000); - - //mChart.moveViewToX(now - 35*60*1000); // - 24 * 60 * 60 * 1000); - //mChart.invalidate(); - mChart.postInvalidate(); + // set vieport to latest SGV + long lastSGVTimestamp = (long) mChart.getSeries().get(0).getHighestValueX(); + if (!hasZoomedChart) { + mChart.getViewport().setMaxX(lastSGVTimestamp); + mChart.getViewport().setMinX(lastSGVTimestamp - chartZoom * 60 * 60 * 1000); + } } } @@ -934,7 +937,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc if (hasUsbPermission()) { // Give the USB a little time to warm up first - startCgmService(System.currentTimeMillis() + MedtronicCnlIntentService.USB_WARMUP_TIME_MS); + startCgmServiceDelayed(MedtronicCnlIntentService.USB_WARMUP_TIME_MS); } else { Log.d(TAG, "No permission for USB. Waiting."); waitForUsbPermission(); @@ -964,31 +967,4 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc } } - private class PumsStatusDataSet extends RealmScatterDataSet<PumpStatusEvent> { - - public PumsStatusDataSet(RealmResults<PumpStatusEvent> result, String yValuesField) { - super(result, yValuesField); - } - - public PumsStatusDataSet(RealmResults<PumpStatusEvent> result, String xValuesField, String yValuesField) { - super(result, xValuesField, yValuesField); - } - - public Entry buildEntryFromResultObject(PumpStatusEvent realmObject, float x) { - DynamicRealmObject dynamicObject = new DynamicRealmObject(realmObject); - float xFloat, yFloat; - - if (mXValuesField == null) { - xFloat = x; - } else { - xFloat = dynamicObject.getDate(mXValuesField).getTime(); - } - yFloat = dynamicObject.getInt(mYValuesField); - - return new Entry(mXValuesField == null ? x : xFloat, yFloat); - } - - } - - } diff --git a/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlIntentService.java b/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlIntentService.java index bc4212726eac58819188576a30749516d2ab1ad6..23fdd5dcf4a4fba01066e5e9b1ac38c6792e48e1 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlIntentService.java +++ b/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlIntentService.java @@ -21,15 +21,14 @@ import java.util.Date; import java.util.Locale; import java.util.concurrent.TimeoutException; -import info.nightscout.android.BuildConfig; import info.nightscout.android.R; import info.nightscout.android.USB.UsbHidDriver; import info.nightscout.android.medtronic.MainActivity; import info.nightscout.android.medtronic.MedtronicCnlReader; import info.nightscout.android.medtronic.exception.ChecksumException; import info.nightscout.android.medtronic.exception.EncryptionException; -import info.nightscout.android.medtronic.message.MessageUtils; import info.nightscout.android.medtronic.exception.UnexpectedMessageException; +import info.nightscout.android.medtronic.message.MessageUtils; import info.nightscout.android.model.medtronicNg.ContourNextLinkInfo; import info.nightscout.android.model.medtronicNg.PumpInfo; import info.nightscout.android.model.medtronicNg.PumpStatusEvent; @@ -38,8 +37,6 @@ import info.nightscout.android.xdrip_plus.XDripPlusUploadReceiver; import io.realm.Realm; import io.realm.RealmResults; -import static info.nightscout.android.medtronic.MainActivity.setActivePumpMac; - public class MedtronicCnlIntentService extends IntentService { public final static int USB_VID = 0x1a79; public final static int USB_PID = 0x6210; @@ -155,12 +152,7 @@ public class MedtronicCnlIntentService extends IntentService { .findFirst(); if (info == null) { - // TODO - use realm.createObject()? info = realm.createObject(ContourNextLinkInfo.class, cnlReader.getStickSerial()); - //info = new ContourNextLinkInfo(); - ///info.setSerialNumber(cnlReader.getStickSerial()); - - //info = realm.copyToRealm(info); } cnlReader.getPumpSession().setStickSerial(info.getSerialNumber()); @@ -204,7 +196,7 @@ public class MedtronicCnlIntentService extends IntentService { // reduce polling interval to half until pump is available MedtronicCnlAlarmManager.setAlarm(activePump.getLastQueryTS() + - (MainActivity.pollInterval + MedtronicCnlIntentService.POLL_GRACE_PERIOD_MS) / (MainActivity.reducePollOnPumpAway?2L:1L) + (MainActivity.pollInterval / (MainActivity.reducePollOnPumpAway?2L:1L)) ); } else { setActivePumpMac(pumpMAC); diff --git a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpStatusEvent.java b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpStatusEvent.java index 60ad396bfba9043268d5e65486dee173f711d6ee..cfe52c2cf6dd3ea601171455a3b5ca1adf67b851 100644 --- a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpStatusEvent.java +++ b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpStatusEvent.java @@ -1,7 +1,5 @@ package info.nightscout.android.model.medtronicNg; -import com.github.mikephil.charting.interfaces.datasets.IScatterDataSet; - import java.util.Date; import io.realm.RealmObject; diff --git a/app/src/main/java/info/nightscout/android/settings/SettingsFragment.java b/app/src/main/java/info/nightscout/android/settings/SettingsFragment.java index d14c7ed19af7df4025bd0731227abfecfb380208..cbabb6a4ec7e9a497f487642f0fd8f1f06ccf3da 100644 --- a/app/src/main/java/info/nightscout/android/settings/SettingsFragment.java +++ b/app/src/main/java/info/nightscout/android/settings/SettingsFragment.java @@ -51,8 +51,7 @@ public class SettingsFragment extends PreferenceFragment implements OnSharedPref */ private void setMinBatPollIntervall(ListPreference pollIntervalPref, ListPreference lowBatPollIntervalPref) { final String currentValue = lowBatPollIntervalPref.getValue(); - final int pollIntervalPos = pollIntervalPref.findIndexOfValue(pollIntervalPref.getValue()), - lowBatPollIntervalPos = lowBatPollIntervalPref.findIndexOfValue(currentValue), + final int pollIntervalPos = (pollIntervalPref.findIndexOfValue(pollIntervalPref.getValue()) >= 0?pollIntervalPref.findIndexOfValue(pollIntervalPref.getValue()):0), length = pollIntervalPref.getEntries().length; CharSequence[] entries = new String[length - pollIntervalPos], diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 1360a2b54f3cdb032af6023402f818de2b39e7f6..95148622918e6cc996d1ce7e5c3075d7ff3e399a 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -119,11 +119,10 @@ </LinearLayout> - <com.github.mikephil.charting.charts.ScatterChart - android:id="@+id/chart" + <com.jjoe64.graphview.GraphView android:layout_width="match_parent" - android:layout_height="wrap_content" - android:visibility="visible" /> + android:layout_height="100dip" + android:id="@+id/chart" /> <ScrollView android:id="@+id/scrollView" diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index d84a02f6bb920f7d4afe27e28ed60ab9cba5f38b..cfb3458e897140a9308cac32f15041128630caad 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -21,4 +21,20 @@ <item>3600000</item> <!--item>0</item--> </string-array> + + <string-array name="chart_zoom"> + <item>1 hour</item> + <item>3 hours</item> + <item>6 hours</item> + <item>12 hours</item> + <item>24 hours</item> + </string-array> + + <string-array name="chart_zoom_hours"> + <item>1</item> + <item>3</item> + <item>6</item> + <item>12</item> + <item>24</item> + </string-array> </resources> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 48268a1e642c4b34d2d4e52b74dbdd9963f34731..580e85d04a9bbe67a3b112aa026ec9c260224707 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -57,4 +57,5 @@ <string name="no_registered_contour_next_link_devices">No registered Contour Next Link devices</string> <string name="to_register_a_contour_next_link_you_must_first_plug_it_in_and_get_a_reading_from_the_pump">To register a Contour Next Link you must first plug it in, and get a reading from the pump.</string> <string name="serial_number">Serial number</string> + <string name="preferences_chart_interval">Chart Zoom</string> </resources> diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 0183e9581d19c4460d5007c0d754336c9a092668..de93838a97ceafac7a7701ecfbb6ee914ed9c5c3 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -41,6 +41,15 @@ android:entries="@array/poll_interval" android:entryValues="@array/poll_interval_millis"/> </PreferenceCategory> + <PreferenceCategory android:title="Display"> + <ListPreference + android:key="chartZoom" + android:defaultValue="3" + android:title="@string/preferences_chart_interval" + android:summary="%s" + android:entries="@array/chart_zoom" + android:entryValues="@array/chart_zoom_hours"/> + </PreferenceCategory> <PreferenceCategory android:title="Sharing"> <CheckBoxPreference android:disableDependentsState="false"