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 6acd98f5eaa221f38f68dd7b69c1efd336b0d89e..2e2fab454bf50f53735d055af908f3d63be10a53 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/MainActivity.java +++ b/app/src/main/java/info/nightscout/android/medtronic/MainActivity.java @@ -52,8 +52,6 @@ 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.utils.ViewPortHandler; -import com.google.android.gms.appindexing.Action; -import com.google.android.gms.appindexing.Thing; import com.mikepenz.google_material_typeface_library.GoogleMaterial; import com.mikepenz.materialdrawer.AccountHeaderBuilder; import com.mikepenz.materialdrawer.Drawer; @@ -83,6 +81,7 @@ 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; import io.realm.Sort; import uk.co.chrisjenx.calligraphy.CalligraphyContextWrapper; @@ -97,6 +96,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc private static long activePumpMac; + boolean mEnableCgmService = true; SharedPreferences prefs = null; private PumpInfo mActivePump; @@ -109,9 +109,20 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc private StatusMessageReceiver statusMessageReceiver = new StatusMessageReceiver(); private MedtronicCnlAlarmReceiver medtronicCnlAlarmReceiver = new MedtronicCnlAlarmReceiver(); + public static long getNextPoll(PumpStatusEvent pumpStatusData) { + long nextPoll = pumpStatusData.getEventDate().getTime() + pumpStatusData.getPumpTimeOffset() + + 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; + } - public static void setActivePumpMac(long pumpMac) { - activePumpMac = pumpMac; + return nextPoll; } @Override @@ -162,8 +173,8 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc statusMessageReceiver, new IntentFilter(MedtronicCnlIntentService.Constants.ACTION_STATUS_MESSAGE)); LocalBroadcastManager.getInstance(this).registerReceiver( - new RefreshDataReceiver(), - new IntentFilter(MedtronicCnlIntentService.Constants.ACTION_REFRESH_DATA)); + new UpdatePumpReceiver(), + new IntentFilter(MedtronicCnlIntentService.Constants.ACTION_UPDATE_PUMP)); mEnableCgmService = Eula.show(this, prefs); @@ -533,9 +544,17 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc } } + public static void setActivePumpMac(long pumpMac) { + activePumpMac = pumpMac; + } + private PumpInfo getActivePump() { if (activePumpMac != 0L && (mActivePump == null || !mActivePump.isValid() || mActivePump.getPumpMac() != activePumpMac)) { - mActivePump = null; + if (mActivePump != null) { + // remove listener on old pump + mActivePump.removeChangeListeners(); + mActivePump = null; + } PumpInfo pump = mRealm .where(PumpInfo.class) @@ -544,29 +563,52 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc if (pump != null && pump.isValid()) { mActivePump = pump; + mActivePump.addChangeListener(new RealmChangeListener<PumpInfo>() { + long lastQueryTS = 0; + @Override + public void onChange(PumpInfo pump) { + // prevent double updating after deleting old events below + if (pump.getLastQueryTS() == lastQueryTS || !pump.isValid()) { + return; + } + + PumpStatusEvent pumpStatusData = pump.getPumpHistory().last();; + + lastQueryTS = pump.getLastQueryTS(); + + startCgmService(MainActivity.getNextPoll(pumpStatusData)); + + // Delete invalid or old records from Realm + // TODO - show an error message if the valid records haven't been uploaded + final RealmResults<PumpStatusEvent> results = + mRealm.where(PumpStatusEvent.class) + .equalTo("sgv", 0) + .or() + .lessThan("eventDate", new Date(System.currentTimeMillis() - (24 * 60 * 60 * 1000))) + .findAll(); + + if (results.size() > 0) { + mRealm.executeTransaction(new Realm.Transaction() { + @Override + public void execute(Realm realm) { + // Delete all matches + Log.d(TAG, "Deleting " + results.size() + " records from realm"); + results.deleteAllFromRealm(); + } + }); + } + + // TODO - handle isOffline in NightscoutUploadIntentService? + uploadCgmData(); + refreshDisplay(); + } + }); } } return mActivePump; } - /** - * ATTENTION: This was auto-generated to implement the App Indexing API. - * See https://g.co/AppIndexing/AndroidStudio for more information. - */ - public Action getIndexApiAction() { - Thing object = new Thing.Builder() - .setName("Main Page") // TODO: Define a title for the content shown. - // TODO: Make sure this auto-generated URL is correct. - .setUrl(Uri.parse("http://[ENTER-YOUR-URL-HERE]")) - .build(); - return new Action.Builder(Action.TYPE_VIEW) - .setObject(object) - .setActionStatus(Action.STATUS_TYPE_COMPLETED) - .build(); - } - - private class StatusMessageReceiver extends BroadcastReceiver { private class StatusMessage { private long timestamp; @@ -656,6 +698,8 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc PumpInfo pump = getActivePump(); if (pump != null && pump.isValid()) { + Log.d(TAG, "history display refresh size: " + pump.getPumpHistory().size()); + Log.d(TAG, "history display refresh date: " + pump.getPumpHistory().last().getEventDate()); pumpStatusData = pump.getPumpHistory().last(); } @@ -796,7 +840,10 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc } } - private class RefreshDataReceiver extends BroadcastReceiver { + /** + * has to be done in MainActivity thread + */ + private class UpdatePumpReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { @@ -805,54 +852,8 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc if (mRealm.isClosed()) { return; } - - PumpStatusEvent pumpStatusData = null; - - PumpInfo pump = getActivePump(); - - if (pump != null && pump.isValid()) { - pumpStatusData = pump.getPumpHistory().last(); - } else { - return; - } - - long nextPoll = pumpStatusData.getEventDate().getTime() + pumpStatusData.getPumpTimeOffset() - + 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; - } - startCgmService(nextPoll); - - // Delete invalid or old records from Realm - // TODO - show an error message if the valid records haven't been uploaded - final RealmResults<PumpStatusEvent> results = - mRealm.where(PumpStatusEvent.class) - .equalTo("sgv", 0) - .or() - .lessThan("eventDate", new Date(System.currentTimeMillis() - (24 * 60 * 60 * 1000))) - .findAll(); - - if (results.size() > 0) { - mRealm.executeTransaction(new Realm.Transaction() { - @Override - public void execute(Realm realm) { - // Delete all matches - Log.d(TAG, "Deleting " + results.size() + " records from realm"); - results.deleteAllFromRealm(); - } - }); - } - - // TODO - handle isOffline in NightscoutUploadIntentService? - uploadCgmData(); - - refreshDisplay(); + //init local pump listener + getActivePump(); } } diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusResponseMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusResponseMessage.java index 67802a3cba0db138e3e5366f8b948b3714e36071..4ef072df354fe1c0fb4ecdb5d72f3f4f0c1072fd 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusResponseMessage.java +++ b/app/src/main/java/info/nightscout/android/medtronic/message/PumpStatusResponseMessage.java @@ -125,6 +125,7 @@ public class PumpStatusResponseMessage extends MedtronicSendMessageResponseMessa // TODO - this should go in the sgvDate, and eventDate should be the time of this poll. sgvDate = MessageUtils.decodeDateTime(rtc, offset); + Log.d(TAG, "original sgv date: " + sgvDate); // Predictive low suspend // TODO - there is more status info in this byte other than just a boolean yes/no diff --git a/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlAlarmManager.java b/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlAlarmManager.java index 87a79d857b6463973d846c39e4cb06a6b9c7a46b..240a2bfe44d853781c7d44f79dbae445d04555df 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlAlarmManager.java +++ b/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlAlarmManager.java @@ -50,6 +50,8 @@ public class MedtronicCnlAlarmManager { if (alarmManager == null || pendingIntent == null) return; + Log.d(TAG, "request to set Alarm at " + new Date(millis)); + long now = System.currentTimeMillis(); // don't trigger the past if (millis < now) 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 a08639bbdad11736f56a398b8f66ac0d44973ea0..d56b74b45e076b91467e9215d6b5a8bd92c0211f 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 @@ -38,6 +38,8 @@ 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; @@ -154,10 +156,11 @@ public class MedtronicCnlIntentService extends IntentService { if (info == null) { // TODO - use realm.createObject()? - info = new ContourNextLinkInfo(); - info.setSerialNumber(cnlReader.getStickSerial()); + info = realm.createObject(ContourNextLinkInfo.class, cnlReader.getStickSerial()); + //info = new ContourNextLinkInfo(); + ///info.setSerialNumber(cnlReader.getStickSerial()); - info = realm.copyToRealm(info); + //info = realm.copyToRealm(info); } cnlReader.getPumpSession().setStickSerial(info.getSerialNumber()); @@ -182,17 +185,19 @@ public class MedtronicCnlIntentService extends IntentService { long pumpMAC = cnlReader.getPumpSession().getPumpMAC(); Log.i(TAG, "PumpInfo MAC: " + (pumpMAC & 0xffffff)); - MainActivity.setActivePumpMac(pumpMAC); PumpInfo activePump = realm .where(PumpInfo.class) .equalTo("pumpMac", pumpMAC) .findFirst(); if (activePump == null) { - activePump = realm.createObject(PumpInfo.class); - activePump.setPumpMac(pumpMAC); + activePump = realm.createObject(PumpInfo.class, pumpMAC); + //activePump.setPumpMac(pumpMAC); } + activePump.updateLastQueryTS(); + + byte radioChannel = cnlReader.negotiateChannel(activePump.getLastRadioChannel()); if (radioChannel == 0) { sendStatus("Could not communicate with the 640g. Are you near the pump?"); @@ -204,6 +209,7 @@ public class MedtronicCnlIntentService extends IntentService { (MainActivity.pollInterval + MedtronicCnlIntentService.POLL_GRACE_PERIOD_MS) / (MainActivity.reducePollOnPumpAway?2L:1L) ); } else { + setActivePumpMac(pumpMAC); activePump.setLastRadioChannel(radioChannel); sendStatus(String.format(Locale.getDefault(), "Connected to Contour Next Link on channel %d.", (int) radioChannel)); Log.d(TAG, String.format("Connected to Contour Next Link on channel %d.", (int) radioChannel)); @@ -226,21 +232,9 @@ public class MedtronicCnlIntentService extends IntentService { pumpRecord.setPumpTimeOffset(pumpOffset); pumpRecord.setPumpDate(new Date(pumpTime - pumpOffset)); cnlReader.updatePumpStatus(pumpRecord); - activePump.getPumpHistory().add(pumpRecord); - - // start reading other data in debug only - if (BuildConfig.DEBUG) { - // read basal pattern - //cnlReader.getBasalPatterns(); - - // Read history - //cnlReader.getHistory(); - } - cnlReader.endEHSMSession(); - boolean cancelTransaction = true; if (pumpRecord.getSgv() != 0) { // Check that the record doesn't already exist before committing RealmResults<PumpStatusEvent> checkExistingRecords = activePump.getPumpHistory() @@ -250,18 +244,17 @@ public class MedtronicCnlIntentService extends IntentService { .findAll(); // There should be the 1 record we've already added in this transaction. - if (checkExistingRecords.size() <= 1) { - realm.commitTransaction(); - cancelTransaction = false; + if (checkExistingRecords.size() == 0) { + activePump.getPumpHistory().add(pumpRecord); } - // Tell the Main Activity we have new data - sendMessage(Constants.ACTION_REFRESH_DATA); + Log.d(TAG, "history reading size: " + activePump.getPumpHistory().size()); + Log.d(TAG, "history reading date: " + activePump.getPumpHistory().last().getEventDate()); } - if (cancelTransaction) { - realm.cancelTransaction(); - } + realm.commitTransaction(); + // Tell the Main Activity we have new data + sendMessage(Constants.ACTION_UPDATE_PUMP); } } catch (UnexpectedMessageException e) { Log.e(TAG, "Unexpected Message", e); @@ -308,6 +301,10 @@ public class MedtronicCnlIntentService extends IntentService { } } + private void setActivePumpMac(long pumpMAC) { + MainActivity.setActivePumpMac(pumpMAC); + } + // reliable wake alarm manager wake up for all android versions public static void wakeUpIntent(Context context, long wakeTime, PendingIntent pendingIntent) { final AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); @@ -347,6 +344,7 @@ public class MedtronicCnlIntentService extends IntentService { public static final String ACTION_USB_PERMISSION = "info.nightscout.android.medtronic.USB_PERMISSION"; public static final String ACTION_REFRESH_DATA = "info.nightscout.android.medtronic.service.CGM_DATA"; public static final String ACTION_USB_REGISTER = "info.nightscout.android.medtronic.USB_REGISTER"; + public static final String ACTION_UPDATE_PUMP = "info.nightscout.android.medtronic.UPDATE_PUMP"; public static final String EXTENDED_DATA = "info.nightscout.android.medtronic.service.DATA"; } diff --git a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpInfo.java b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpInfo.java index 3b25a51612206e6ffc265a1b6e1e18190b056d71..dac3391fc883cfb4cf6806b1aad6ca4e74fd016f 100644 --- a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpInfo.java +++ b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpInfo.java @@ -2,6 +2,8 @@ package info.nightscout.android.model.medtronicNg; import io.realm.RealmList; import io.realm.RealmObject; +import io.realm.Sort; +import io.realm.annotations.Ignore; import io.realm.annotations.PrimaryKey; /** @@ -12,6 +14,7 @@ public class PumpInfo extends RealmObject { private long pumpMac; private String deviceName; private byte lastRadioChannel; + private long lastQueryTS = 0; private RealmList<ContourNextLinkInfo> associatedCnls; private RealmList<PumpStatusEvent> pumpHistory; @@ -19,7 +22,7 @@ public class PumpInfo extends RealmObject { return pumpMac; } - public void setPumpMac(long pumpMac) { + private void setPumpMac(long pumpMac) { this.pumpMac = pumpMac; } @@ -58,4 +61,12 @@ public class PumpInfo extends RealmObject { public long getPumpSerial() { return pumpMac & 0xffffff; } + + public long getLastQueryTS() { + return lastQueryTS; + } + + public void updateLastQueryTS() { + lastQueryTS = System.currentTimeMillis(); + } }