diff --git a/app/src/main/java/info/nightscout/android/UploaderApplication.java b/app/src/main/java/info/nightscout/android/UploaderApplication.java index e83930549d22d99fa37e3c30726ee14bb0aba72e..c84575b56456326cf0990abf97d9794567a2858b 100644 --- a/app/src/main/java/info/nightscout/android/UploaderApplication.java +++ b/app/src/main/java/info/nightscout/android/UploaderApplication.java @@ -97,18 +97,6 @@ public class UploaderApplication extends Application { .modules(new HistoryModule()) .deleteRealmIfMigrationNeeded() .build(); - - Realm storeRealm = Realm.getInstance(storeConfiguration); - if (storeRealm.where(DataStore.class).findFirst() == null) { - storeRealm.executeTransaction(new Realm.Transaction() { - @Override - public void execute(Realm realm) { - realm.createObject(DataStore.class); - } - }); - } - storeRealm.close(); - } public static long getStartupRealtime() { 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 d65acc7899ec9b0766c40a73fe3f8091e25da494..4261eb8ef3352d8c899f01250b3ffd854c721882 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/MainActivity.java +++ b/app/src/main/java/info/nightscout/android/medtronic/MainActivity.java @@ -119,6 +119,14 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc historyRealm = Realm.getInstance(UploaderApplication.getHistoryConfiguration()); dataStore = storeRealm.where(DataStore.class).findFirst(); + if (dataStore == null) { + storeRealm.executeTransaction(new Realm.Transaction() { + @Override + public void execute(Realm realm) { + dataStore = realm.createObject(DataStore.class); + } + }); + } // limit date for NS backfill sync period, set to this init date to stop overwrite of older NS data (pref option to override) if (dataStore.getNightscoutLimitDate() == null) { @@ -548,6 +556,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc dataStore.setSysPollErrorRetry(Long.parseLong(sharedPreferences.getString("sysPollErrorRetry", "90000"))); dataStore.setSysPollOldSgvRetry(Long.parseLong(sharedPreferences.getString("sysPollOldSgvRetry", "90000"))); dataStore.setSysEnableWait500ms(sharedPreferences.getBoolean("sysEnableWait500ms", false)); + dataStore.setSysEnableUsbPermissionDialog(sharedPreferences.getBoolean("sysEnableUsbPermissionDialog", false)); // debug dataStore.setDbgEnableExtendedErrors(sharedPreferences.getBoolean("dbgEnableExtendedErrors", false)); @@ -569,6 +578,7 @@ public class MainActivity extends AppCompatActivity implements OnSharedPreferenc dataStore.setNsProfileDefault(Integer.parseInt(sharedPreferences.getString("nsProfileDefault", "1"))); dataStore.setNsActiveInsulinTime(Float.parseFloat(sharedPreferences.getString("nsActiveInsulinTime", "3"))); dataStore.setNsEnablePatternChange(sharedPreferences.getBoolean("nsEnablePatternChange", true)); + dataStore.setNsGramsPerExchange(Integer.parseInt(sharedPreferences.getString("nsGramsPerExchange", "15"))); dataStore.setNsEnableInsertBGasCGM(sharedPreferences.getBoolean("nsEnableInsertBGasCGM", false)); // pattern and preset naming diff --git a/app/src/main/java/info/nightscout/android/medtronic/PumpHistoryHandler.java b/app/src/main/java/info/nightscout/android/medtronic/PumpHistoryHandler.java index e3c181ff0a8c6b8eb5c7692b4c27a656820fe209..6ca17f569eab3d184973ffe26f2b34813377ef96 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/PumpHistoryHandler.java +++ b/app/src/main/java/info/nightscout/android/medtronic/PumpHistoryHandler.java @@ -75,14 +75,14 @@ public class PumpHistoryHandler { historyRealm = Realm.getInstance(UploaderApplication.getHistoryConfiguration()); historyDB = new ArrayList<>(); - historyDB.add(new DBitem("CGM", false, 400, historyRealm.where(PumpHistoryCGM.class).findAll())); - historyDB.add(new DBitem("BOLUS", true, 30, historyRealm.where(PumpHistoryBolus.class).findAll())); - historyDB.add(new DBitem("BASAL", true,30, historyRealm.where(PumpHistoryBasal.class).findAll())); + historyDB.add(new DBitem("CGM", false, 300, historyRealm.where(PumpHistoryCGM.class).findAll())); + historyDB.add(new DBitem("BOLUS", true, 20, historyRealm.where(PumpHistoryBolus.class).findAll())); + historyDB.add(new DBitem("BASAL", true,20, historyRealm.where(PumpHistoryBasal.class).findAll())); historyDB.add(new DBitem("PATTERN", true,10, historyRealm.where(PumpHistoryPattern.class).findAll())); - historyDB.add(new DBitem("BG", true,30, historyRealm.where(PumpHistoryBG.class).findAll())); + historyDB.add(new DBitem("BG", true,20, historyRealm.where(PumpHistoryBG.class).findAll())); historyDB.add(new DBitem("PROFILE", false,10, historyRealm.where(PumpHistoryProfile.class).findAll())); historyDB.add(new DBitem("MISC", true,10, historyRealm.where(PumpHistoryMisc.class).findAll())); - historyDB.add(new DBitem("LOOP", true,300, historyRealm.where(PumpHistoryLoop.class).findAll())); + historyDB.add(new DBitem("LOOP", true,200, historyRealm.where(PumpHistoryLoop.class).findAll())); historyDB.add(new DBitem("DEBUG", true,20, historyRealm.where(PumpHistoryDebug.class).findAll())); } @@ -280,34 +280,74 @@ public class PumpHistoryHandler { if (dataStore.isNameBasalPatternChanged() && (dataStore.isNsEnableProfileSingle() || dataStore.isNsEnableProfileOffset())) { - userLogMessage("Basal Pattern Names changed, updating pattern switch treatments in Nightscout"); - - final RealmResults<PumpHistoryProfile> results = historyRealm - .where(PumpHistoryProfile.class) - .equalTo("profileSwitch", true) + final RealmResults<PumpHistoryPattern> results = historyRealm + .where(PumpHistoryPattern.class) .findAll(); - historyRealm.executeTransaction(new Realm.Transaction() { + + if (results.size() > 0) { + Log.d(TAG, "NameBasalPatternChanged: Found " + results.size() + " pattern switch treatments to update"); + userLogMessage("Basal Pattern Names changed, updating pattern switch treatments in Nightscout"); + + historyRealm.executeTransaction(new Realm.Transaction() { + @Override + public void execute(Realm realm) { + for (PumpHistoryPattern record : results) { + record.setUploadREQ(true); + record.setUploadACK(true); + } + } + }); + + } + + storeRealm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { - for (PumpHistoryProfile record : results) { - record.setUploadREQ(true); - } + dataStore.setNameBasalPatternChanged(false); } }); + } + } + + public void checkGramsPerExchangeChanged() { + + if (dataStore.isNsGramsPerExchangeChanged()) { + + final RealmResults<PumpHistoryBolus> results = historyRealm + .where(PumpHistoryBolus.class) + .equalTo("programmed", true) + .equalTo("estimate", true) + .equalTo("carbUnits", PumpHistoryParser.CARB_UNITS.EXCHANGES.get()) + .findAll(); + + if (results.size() > 0) { + Log.d(TAG, "GramsPerExchangeChanged: Found " + results.size() + " carb/bolus treatments to update"); + userLogMessage("Grams per Exchange changed, updating carb/bolus treatments in Nightscout"); + + historyRealm.executeTransaction(new Realm.Transaction() { + @Override + public void execute(Realm realm) { + for (PumpHistoryBolus record : results) { + record.setUploadREQ(true); + record.setUploadACK(true); + } + } + }); + } + storeRealm.executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { - dataStore.setNameBasalPatternChanged(false); + dataStore.setNsGramsPerExchangeChanged(false); } }); } - } public void cgm(final PumpStatusEvent pumpRecord) { // push the current sgv from status (always have latest sgv available even if there are comms errors after this) - if (pumpRecord.isValidSGV()) { + if (pumpRecord.isCgmActive()) { final Date date = pumpRecord.getCgmDate(); final int rtc = pumpRecord.getCgmRTC(); @@ -317,7 +357,7 @@ public class PumpHistoryHandler { .where(PumpHistoryCGM.class) .findAllSorted("eventDate", Sort.ASCENDING); - // sgv is available do we need the backfill? + // cgm is available do we need the backfill? if (dataStore.isSysEnableCgmHistory() && results.size() == 0 || (results.size() > 0 && date.getTime() - results.last().getEventDate().getTime() > 9 * 60 * 1000L)) { @@ -335,6 +375,7 @@ public class PumpHistoryHandler { public void execute(Realm realm) { PumpHistoryCGM.cgm(historyRealm, date, rtc, offset, pumpRecord.getSgv(), + pumpRecord.getCgmExceptionType(), pumpRecord.getCgmTrendString() ); } diff --git a/app/src/main/java/info/nightscout/android/medtronic/StatusNotification.java b/app/src/main/java/info/nightscout/android/medtronic/StatusNotification.java index 72740e1ad646468a66e0f076fa8b6360cf3e512d..dca732f8f8b9ebf03fae3f8eaccb2437aed49ffa 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/StatusNotification.java +++ b/app/src/main/java/info/nightscout/android/medtronic/StatusNotification.java @@ -122,6 +122,11 @@ public class StatusNotification { private void updateNotification() { Log.d(TAG, "updateNotification called"); + if (realm == null || storeRealm == null || historyRealm == null) { + Log.e(TAG, "unexpected null for Realm"); + return; + } + long now = System.currentTimeMillis(); String poll = ""; @@ -150,7 +155,7 @@ public class StatusNotification { if (sgvAge < 60 && deltaTime < 30) { int diff = results.first().getSgv() - results.get(1).getSgv(); if (dataStore.isMmolxl()) - delta += "Δ " + (diff > 0 ? "+" : "") + new BigDecimal(diff / MMOLXLFACTOR).setScale(2, BigDecimal.ROUND_HALF_UP); + delta += "Δ " + (diff > 0 ? "+" : "") + new BigDecimal(diff / MMOLXLFACTOR).setScale(2, BigDecimal.ROUND_HALF_UP).toPlainString(); else delta += "Δ " + (diff > 0 ? "+" : "") + diff; if (deltaTime > 6) diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkMessage.java index 2a7af0c0752ba2ba2286e9f9bbdae5773af834f7..527aaeef1fd4fb6fa559f8e62846fe6be63314cd 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkMessage.java +++ b/app/src/main/java/info/nightscout/android/medtronic/message/ContourNextLinkMessage.java @@ -359,6 +359,7 @@ public abstract class ContourNextLinkMessage { expectedSegments = multipacketSession.packetsToFetch; fetchMoreData = true; } else if (MedtronicSendMessageRequestMessage.MessageType.MULTIPACKET_SEGMENT_TRANSMISSION.response(cmd)) { + if (multipacketSession == null) throw new UnexpectedMessageException("multipacketSession not initiated before segment received" + tag); multipacketSession.addSegment(decrypted); expectedSegments--; @@ -447,23 +448,27 @@ public abstract class ContourNextLinkMessage { } private void addSegment(byte[] data) throws UnexpectedMessageException { - int packetNumber = read16BEtoUInt(data, 0x0003); - int packetSize = data.length - 7; - segments[packetNumber] = true; - - Log.d(TAG, "*** Got a Multipacket Segment: " + (packetNumber + 1) + " of " + this.packetsToFetch + ", count: " + segmentsFilled() + " [packetSize=" + packetSize + " " + this.packetSize + "/" + this.lastPacketSize + "]"); - - if (packetNumber == lastPacketNumber() && - packetSize != this.lastPacketSize) { - throw new UnexpectedMessageException("Multipacket Transfer last packet size mismatch"); - } else if (packetNumber != lastPacketNumber() && - packetSize != this.packetSize) { - throw new UnexpectedMessageException("Multipacket Transfer packet size mismatch"); - } + try { + int packetNumber = read16BEtoUInt(data, 0x0003); + int packetSize = data.length - 7; + segments[packetNumber] = true; + + Log.d(TAG, "*** Got a Multipacket Segment: " + (packetNumber + 1) + " of " + this.packetsToFetch + ", count: " + segmentsFilled() + " [packetSize=" + packetSize + " " + this.packetSize + "/" + this.lastPacketSize + "]"); + + if (packetNumber == lastPacketNumber() && + packetSize != this.lastPacketSize) { + throw new UnexpectedMessageException("Multipacket Transfer last packet size mismatch"); + } else if (packetNumber != lastPacketNumber() && + packetSize != this.packetSize) { + throw new UnexpectedMessageException("Multipacket Transfer packet size mismatch"); + } - int to = (packetNumber * this.packetSize) + 1; - int from = 5; - while (from < packetSize + 5) this.response[to++] = data[from++]; + int to = (packetNumber * this.packetSize) + 1; + int from = 5; + while (from < packetSize + 5) this.response[to++] = data[from++]; + } catch (Exception e) { + throw new UnexpectedMessageException("Multipacket Transfer bad segment data received"); + } } private byte[] missingSegments() { diff --git a/app/src/main/java/info/nightscout/android/medtronic/message/ReadHistoryRequestMessage.java b/app/src/main/java/info/nightscout/android/medtronic/message/ReadHistoryRequestMessage.java index 16460d3719cf95bf2c7eafdd99aabb8c76221eed..12ae62f8d4d3e1cdcfbc09c8a1105a4b5a1ef2d4 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/message/ReadHistoryRequestMessage.java +++ b/app/src/main/java/info/nightscout/android/medtronic/message/ReadHistoryRequestMessage.java @@ -93,7 +93,7 @@ public class ReadHistoryRequestMessage extends MedtronicSendMessageRequestMessag // Check that we have the correct number of bytes in this message if (payload.length - HEADER_SIZE != historySizeCompressed) { - throw new UnexpectedMessageException("Unexpected update message size"); + throw new UnexpectedMessageException("Unexpected message size"); } byte[] blockPayload; @@ -101,12 +101,16 @@ public class ReadHistoryRequestMessage extends MedtronicSendMessageRequestMessag if (historyCompressed) { blockPayload = new byte[historySizeUncompressed]; - LzoAlgorithm algorithm = LzoAlgorithm.LZO1X; - LzoDecompressor decompressor = LzoLibrary.getInstance().newDecompressor(algorithm, null); - int lzoReturnCode = decompressor.decompress(payload, HEADER_SIZE, historySizeCompressed, blockPayload, 0, new lzo_uintp(historySizeUncompressed)); + try { + LzoAlgorithm algorithm = LzoAlgorithm.LZO1X; + LzoDecompressor decompressor = LzoLibrary.getInstance().newDecompressor(algorithm, null); + int lzoReturnCode = decompressor.decompress(payload, HEADER_SIZE, historySizeCompressed, blockPayload, 0, new lzo_uintp(historySizeUncompressed)); + if (lzoReturnCode != LzoTransformer.LZO_E_OK) + throw new UnexpectedMessageException("Error trying to decompress message (" + decompressor.toErrorString(lzoReturnCode) + ")"); + } catch (Exception e) { + throw new UnexpectedMessageException("Error trying to decompress message"); + } - if (lzoReturnCode != LzoTransformer.LZO_E_OK) - throw new UnexpectedMessageException("Error trying to decompress update message (" + decompressor.toErrorString(lzoReturnCode) + ")"); } else { blockPayload = Arrays.copyOfRange(payload, HEADER_SIZE, payload.length); } diff --git a/app/src/main/java/info/nightscout/android/medtronic/service/MasterService.java b/app/src/main/java/info/nightscout/android/medtronic/service/MasterService.java index ac52965cfac274735d7bd45764f57a169f342bbc..4d9df44416a6130f8dd985788afc36fcbf7aa407 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/service/MasterService.java +++ b/app/src/main/java/info/nightscout/android/medtronic/service/MasterService.java @@ -94,48 +94,53 @@ public class MasterService extends Service { mContext = this.getBaseContext(); - IntentFilter masterServiceIntentFilter = new IntentFilter(); - masterServiceIntentFilter.addAction(Constants.ACTION_CNL_COMMS_ACTIVE); - masterServiceIntentFilter.addAction(Constants.ACTION_CNL_COMMS_FINISHED); - masterServiceIntentFilter.addAction(Constants.ACTION_STOP_SERVICE); - masterServiceIntentFilter.addAction(Constants.ACTION_READ_NOW); - masterServiceIntentFilter.addAction(Constants.ACTION_READ_PROFILE); - masterServiceIntentFilter.addAction(Constants.ACTION_URCHIN_UPDATE); - registerReceiver(masterServiceReceiver, masterServiceIntentFilter); - - registerReceiver( - userLogMessageReceiver, - new IntentFilter(Constants.ACTION_USERLOG_MESSAGE)); - - IntentFilter batteryIntentFilter = new IntentFilter(); - batteryIntentFilter.addAction(Intent.ACTION_BATTERY_LOW); - batteryIntentFilter.addAction(Intent.ACTION_BATTERY_CHANGED); - batteryIntentFilter.addAction(Intent.ACTION_BATTERY_OKAY); - registerReceiver(batteryReceiver, batteryIntentFilter); - - IntentFilter usbIntentFilter = new IntentFilter(); - usbIntentFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); - usbIntentFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); - usbIntentFilter.addAction(Constants.ACTION_HAS_USB_PERMISSION); - usbIntentFilter.addAction(Constants.ACTION_NO_USB_PERMISSION); - registerReceiver(usbReceiver, usbIntentFilter); - - MedtronicCnlAlarmManager.setContext(mContext); - - urchin = new Urchin(mContext); - - realm = Realm.getDefaultInstance(); storeRealm = Realm.getInstance(UploaderApplication.getStoreConfiguration()); dataStore = storeRealm.where(DataStore.class).findFirst(); - storeRealm.executeTransaction(new Realm.Transaction() { - @Override - public void execute(Realm realm) { - dataStore.setNightscoutReportTime(0); - dataStore.setNightscoutAvailable(false); - dataStore.clearAllCommsErrors(); - } - }); + // safety check: don't proceed until main ui has been run and datastore initialised + if (dataStore != null) { + + realm = Realm.getDefaultInstance(); + + storeRealm.executeTransaction(new Realm.Transaction() { + @Override + public void execute(Realm realm) { + dataStore.setNightscoutReportTime(0); + dataStore.setNightscoutAvailable(false); + dataStore.clearAllCommsErrors(); + } + }); + + IntentFilter masterServiceIntentFilter = new IntentFilter(); + masterServiceIntentFilter.addAction(Constants.ACTION_CNL_COMMS_ACTIVE); + masterServiceIntentFilter.addAction(Constants.ACTION_CNL_COMMS_FINISHED); + masterServiceIntentFilter.addAction(Constants.ACTION_STOP_SERVICE); + masterServiceIntentFilter.addAction(Constants.ACTION_READ_NOW); + masterServiceIntentFilter.addAction(Constants.ACTION_READ_PROFILE); + masterServiceIntentFilter.addAction(Constants.ACTION_URCHIN_UPDATE); + registerReceiver(masterServiceReceiver, masterServiceIntentFilter); + + registerReceiver( + userLogMessageReceiver, + new IntentFilter(Constants.ACTION_USERLOG_MESSAGE)); + + IntentFilter batteryIntentFilter = new IntentFilter(); + batteryIntentFilter.addAction(Intent.ACTION_BATTERY_LOW); + batteryIntentFilter.addAction(Intent.ACTION_BATTERY_CHANGED); + batteryIntentFilter.addAction(Intent.ACTION_BATTERY_OKAY); + registerReceiver(batteryReceiver, batteryIntentFilter); + + IntentFilter usbIntentFilter = new IntentFilter(); + usbIntentFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); + usbIntentFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); + usbIntentFilter.addAction(Constants.ACTION_HAS_USB_PERMISSION); + usbIntentFilter.addAction(Constants.ACTION_NO_USB_PERMISSION); + registerReceiver(usbReceiver, usbIntentFilter); + + MedtronicCnlAlarmManager.setContext(mContext); + + urchin = new Urchin(mContext); + } } @Override @@ -143,19 +148,18 @@ public class MasterService extends Service { super.onDestroy(); Log.d(TAG, "onDestroy called"); - urchin.close(); - - statusNotification.endNotification(); - MedtronicCnlAlarmManager.cancelAlarm(); - if (!storeRealm.isClosed()) storeRealm.close(); - if (!realm.isClosed()) realm.close(); + if (userLogMessageReceiver != null) unregisterReceiver(userLogMessageReceiver); + if (usbReceiver != null) unregisterReceiver(usbReceiver); + if (batteryReceiver != null) unregisterReceiver(batteryReceiver); + if (masterServiceReceiver != null) unregisterReceiver(masterServiceReceiver); + + if (urchin != null) urchin.close(); + if (statusNotification != null) statusNotification.endNotification(); - unregisterReceiver(userLogMessageReceiver); - unregisterReceiver(usbReceiver); - unregisterReceiver(batteryReceiver); - unregisterReceiver(masterServiceReceiver); + if (storeRealm != null && !storeRealm.isClosed()) storeRealm.close(); + if (realm != null && !realm.isClosed()) realm.close(); } @Override @@ -173,6 +177,11 @@ public class MasterService extends Service { Log.d(TAG, "Received start id " + startId + ": " + intent); //userLogMessage.add(TAG + " Received start id " + startId + ": " + (intent == null ? "null" : "")); + if (dataStore == null) { + // safety check: don't proceed until main ui has been run and datastore initialised + stopSelf(); + } + if (intent == null || startId == 1) { Log.i(TAG, "service start"); //userLogMessage.add(TAG + " service start"); @@ -354,12 +363,14 @@ public class MasterService extends Service { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - // received from UsbActivity via OS + // received from UsbActivity via OS / app requested permission dialog if (Constants.ACTION_HAS_USB_PERMISSION.equals(action)) { - statusNotification.updateNotification(StatusNotification.NOTIFICATION.NORMAL); - - startCgmServiceDelayed(MedtronicCnlService.USB_WARMUP_TIME_MS); + if (hasUsbPermission()) { + userLogMessage.add(ICON_INFO + "Got permission for USB."); + statusNotification.updateNotification(StatusNotification.NOTIFICATION.NORMAL); + startCgmServiceDelayed(MedtronicCnlService.USB_WARMUP_TIME_MS); + } // received from OS } else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) { @@ -397,12 +408,17 @@ public class MasterService extends Service { } else if (Constants.ACTION_NO_USB_PERMISSION.equals(action)) { Log.d(TAG, "No permission to read the USB device."); userLogMessage.add(ICON_WARN + "No permission to read the USB device."); - userLogMessage.add(ICON_HELP + "Unplug/plug the Contour Next Link and select 'use by default for this device' to make permission permanent."); MedtronicCnlAlarmManager.cancelAlarm(); statusNotification.updateNotification(StatusNotification.NOTIFICATION.ERROR); + if (dataStore.isSysEnableUsbPermissionDialog()) { + requestUsbPermission(); + } else { + userLogMessage.add(ICON_HELP + "Unplug/plug the Contour Next Link and select 'use by default for this device' to make permission permanent."); + } + } } } @@ -414,6 +430,13 @@ public class MasterService extends Service { return !(usbManager != null && cnlDevice != null && !usbManager.hasPermission(cnlDevice)); } + private void requestUsbPermission() { + UsbManager usbManager = (UsbManager) this.getSystemService(Context.USB_SERVICE); + UsbDevice cnlDevice = UsbHidDriver.getUsbDevice(usbManager, MedtronicCnlService.USB_VID, MedtronicCnlService.USB_PID); + PendingIntent permissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(Constants.ACTION_HAS_USB_PERMISSION), 0); + usbManager.requestPermission(cnlDevice, permissionIntent); + } + private void showDisconnectionNotification(String title, String message) { android.support.v7.app.NotificationCompat.Builder mBuilder = (android.support.v7.app.NotificationCompat.Builder) new android.support.v7.app.NotificationCompat.Builder(this) diff --git a/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlService.java b/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlService.java index 42916ec8aad4cadbf7bd0d8feb7066de98248e37..e0226a411edaab755738c4a57e5bf6eaeaf5dcfd 100644 --- a/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlService.java +++ b/app/src/main/java/info/nightscout/android/medtronic/service/MedtronicCnlService.java @@ -303,7 +303,7 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received cnl0x81 = 0; try { - +/* if (false) { userLogMessage("Adding history from file"); new HistoryDebug(mContext).run(); @@ -311,7 +311,7 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received nextpoll = 0; return; } - +*/ long pollInterval = dataStore.getPollInterval(); if (!openUsbDevice()) { @@ -524,6 +524,8 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received }); } + pumpHistoryHandler.checkGramsPerExchangeChanged(); + pumpHistoryHandler.update(cnlReader); } } @@ -651,9 +653,15 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received userLogMessage(ICON_HELP + "Past historical data can be uploaded to Nightscout via the History Sync option in Advanced Nightscout Settings."); } */ - if (dataStore.isNightscoutUpload() && dataStore.isNsEnableProfileUpload() && !pumpHistoryHandler.isProfileUploaded()) { - userLogMessage(ICON_INFO + "No basal pattern profile has been uploaded."); - userLogMessage(ICON_HELP + "Profiles can be uploaded from the main menu."); + if (dataStore.isNightscoutUpload() && dataStore.isNsEnableProfileUpload()) { + if (!pumpHistoryHandler.isProfileUploaded()) { + userLogMessage(ICON_INFO + "No basal pattern profile has been uploaded."); + userLogMessage(ICON_HELP + "Profiles can be uploaded from the main menu."); + } else if (dataStore.isNameBasalPatternChanged() && + (dataStore.isNsEnableProfileSingle() || dataStore.isNsEnableProfileOffset())) { + userLogMessage(ICON_INFO + "Basal pattern names have changed, your profile should be updated."); + userLogMessage(ICON_HELP + "Profiles can be updated from the main menu."); + } } if (PumpBatteryError >= ERROR_PUMPBATTERY_AT) { @@ -784,7 +792,7 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received // was temp ended before expected duration? if (!results.first().isTempBasalActive() && results.get(1).isTempBasalActive()) { int diff = results.get(1).getTempBasalMinutesRemaining() - ageMinutes; - if (diff < -2 || diff > 2) { + if (diff < -4 || diff > 4) { userLogMessage(info + "temp ended"); historyNeeded = true; } @@ -793,7 +801,7 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received // was a new temp started while one was in progress? if (results.first().isTempBasalActive() && results.get(1).isTempBasalActive()) { int diff = results.get(1).getTempBasalMinutesRemaining() - results.first().getTempBasalMinutesRemaining() - ageMinutes; - if (diff < -2 || diff > 2) { + if (diff < -4 || diff > 4) { userLogMessage(info + "temp extended"); historyNeeded = true; } @@ -805,7 +813,7 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received if (!results.first().isBolusingDual() && results.get(1).isBolusingDual()) { // was dual ended before expected duration? int diff = results.get(1).getBolusingMinutesRemaining() - ageMinutes; - if (diff < -2 || diff > 2) { + if (diff < -4 || diff > 4) { userLogMessage(info + "dual ended"); historyNeeded = true; } @@ -814,7 +822,7 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received else if (!results.first().isBolusingSquare() && results.get(1).isBolusingSquare() && !results.get(1).isBolusingNormal()) { // was square ended before expected duration? int diff = results.get(1).getBolusingMinutesRemaining() - ageMinutes; - if (diff < -2 || diff > 2) { + if (diff < -4 || diff > 4) { userLogMessage(info + "square ended"); historyNeeded = true; } @@ -833,7 +841,7 @@ CNL: unpaired PUMP: unpaired UPLOADER: unregistered = "Invalid message received } else if (!results.first().isBolusingDual() && results.get(1).isBolusingDual()) { // was dual ended before expected duration? int diff = results.get(1).getBolusingMinutesRemaining() - ageMinutes; - if (diff < -2 || diff > 2) { + if (diff < -4 || diff > 4) { userLogMessage(info + "dual ended"); historyNeeded = true; } diff --git a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryBolus.java b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryBolus.java index 09782845eaddfce47f9278f56169444535b2865d..82ea1707d90d0f27c6f0cf67028cb79dce8ef290 100644 --- a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryBolus.java +++ b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryBolus.java @@ -170,10 +170,11 @@ public class PumpHistoryBolus extends RealmObject implements PumpHistoryInterfac double carbInputAsGrams; double carbRatioAsGrams; String exchanges; + int gramsPerExchange = dataStore.getNsGramsPerExchange(); if (PumpHistoryParser.CARB_UNITS.EXCHANGES.equals(carbUnits)) { - carbInputAsGrams = 15 * carbInput; - carbRatioAsGrams = 15 / carbRatio; - exchanges = String.format(Locale.US, ", ex %.2fu/15g", carbRatio); + carbInputAsGrams = gramsPerExchange * carbInput; + carbRatioAsGrams = gramsPerExchange / carbRatio; + exchanges = String.format(Locale.US, ", ex %.2fu/%sg", carbRatio, gramsPerExchange); } else { carbInputAsGrams = carbInput; carbRatioAsGrams = carbRatio; diff --git a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryCGM.java b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryCGM.java index 5ff4f4b57eb29dc028ecb92816a7e4f25e209bb5..109738fa3622d3cea5c299102539c7f5596ae878 100644 --- a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryCGM.java +++ b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryCGM.java @@ -61,17 +61,30 @@ public class PumpHistoryCGM extends RealmObject implements PumpHistoryInterface public List nightscout(DataStore dataStore) { List<UploadItem> uploadItems = new ArrayList<>(); - UploadItem uploadItem = new UploadItem(); - uploadItems.add(uploadItem); - EntriesEndpoints.Entry entry = uploadItem.ack(uploadACK).entry(); + int sgvNS = sgv; + if (sgvNS == 0) { + if (sensorException == 2) // SENSOR_CAL_NEEDED + sgvNS = 5; // nightscout: '?NC' SENSOR_NOT_CALIBRATED + else if (sensorException == 5) // SENSOR_CHANGE_SENSOR_ERROR + sgvNS = 1; // nightscout: '?SN' SENSOR_NOT_ACTIVE + else if (sensorException == 6) // SENSOR_END_OF_LIFE + sgvNS = 1; // nightscout: '?SN' SENSOR_NOT_ACTIVE + } + + if (sgvNS > 0) { + UploadItem uploadItem = new UploadItem(); + uploadItems.add(uploadItem); + EntriesEndpoints.Entry entry = uploadItem.ack(uploadACK).entry(); - entry.setKey600(key); - entry.setType("sgv"); - entry.setDate(eventDate.getTime()); - entry.setDateString(eventDate.toString()); + entry.setKey600(key); + entry.setType("sgv"); + entry.setDate(eventDate.getTime()); + entry.setDateString(eventDate.toString()); - entry.setSgv(sgv); - if (cgmTrend != null) entry.setDirection(PumpHistoryParser.TextEN.valueOf("NS_TREND_" + cgmTrend).getText()); + entry.setSgv(sgvNS); + if (cgmTrend != null) + entry.setDirection(PumpHistoryParser.TextEN.valueOf("NS_TREND_" + cgmTrend).getText()); + } return uploadItems; } @@ -134,6 +147,7 @@ public class PumpHistoryCGM extends RealmObject implements PumpHistoryInterface public static void cgm(Realm realm, Date eventDate, int eventRTC, int eventOFFSET, int sgv, + byte sensorException, String trend) { PumpHistoryCGM object = realm.where(PumpHistoryCGM.class) @@ -148,6 +162,7 @@ public class PumpHistoryCGM extends RealmObject implements PumpHistoryInterface object.setCgmRTC(eventRTC); object.setCgmOFFSET(eventOFFSET); object.setSgv(sgv); + object.setSensorException(sensorException); object.setCgmTrend(trend); object.setUploadREQ(true); } diff --git a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryProfile.java b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryProfile.java index f912a6f7a11535c6017304cba4f51a09815cce1e..10886a783aa72223dbc2dc97d41c40d34a635407 100644 --- a/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryProfile.java +++ b/app/src/main/java/info/nightscout/android/model/medtronicNg/PumpHistoryProfile.java @@ -110,6 +110,7 @@ public class PumpHistoryProfile extends RealmObject implements PumpHistoryInterf bp.carbshr = carbshr; bp.dia = dia; bp.delay = delay; + bp.carbsPerExchange = dataStore.getNsGramsPerExchange(); bp.parseCarbRatios(); bp.parseSensitivity(); bp.parseTargets(); @@ -143,6 +144,7 @@ public class PumpHistoryProfile extends RealmObject implements PumpHistoryInterf String carbshr; String dia; String units; + int carbsPerExchange; int index = 0; private BasalProfile newProfile() { @@ -211,8 +213,8 @@ public class PumpHistoryProfile extends RealmObject implements PumpHistoryInterf for (int i = 0; i < items; i++) { // pump grams/unit rate rate1 = read32BEtoInt(carbRatios, index) / 10; - // pump units/exchange rate (converted by dividing into 15g, the standard amount of grams per exchange) - rate2 = (int) (15 / ((double) (read32BEtoInt(carbRatios, index + 4)) / 1000.0)); + // pump units/exchange rate (converted by dividing into carbsPerExchange default=15g, the standard amount of grams per exchange) + rate2 = (int) (carbsPerExchange / ((double) (read32BEtoInt(carbRatios, index + 4)) / 1000.0)); time = read8toUInt(carbRatios, index + 8) * 30; carbratio.add(addPeriod(time, "" + (rate1 > 0 ? rate1 : rate2))); index += 9; diff --git a/app/src/main/java/info/nightscout/android/model/store/DataStore.java b/app/src/main/java/info/nightscout/android/model/store/DataStore.java index 028d362f27726ac8ef275ed59ce7d1027773e17d..1e959602b4013c50b7e058d325f8329ed76aaaa7 100644 --- a/app/src/main/java/info/nightscout/android/model/store/DataStore.java +++ b/app/src/main/java/info/nightscout/android/model/store/DataStore.java @@ -59,6 +59,7 @@ public class DataStore extends RealmObject { private long sysPollErrorRetry; private long sysPollOldSgvRetry; private boolean sysEnableWait500ms; + private boolean sysEnableUsbPermissionDialog; private boolean dbgEnableExtendedErrors; private boolean dbgEnableUploadErrors; @@ -78,6 +79,8 @@ public class DataStore extends RealmObject { private int nsProfileDefault; private float nsActiveInsulinTime; private boolean nsEnablePatternChange; + private int nsGramsPerExchange; + private boolean nsGramsPerExchangeChanged = false; private boolean nsEnableInsertBGasCGM; private boolean urchinEnable; @@ -450,6 +453,14 @@ public class DataStore extends RealmObject { this.sysEnableWait500ms = sysEnableWait500ms; } + public boolean isSysEnableUsbPermissionDialog() { + return sysEnableUsbPermissionDialog; + } + + public void setSysEnableUsbPermissionDialog(boolean sysEnableUsbPermissionDialog) { + this.sysEnableUsbPermissionDialog = sysEnableUsbPermissionDialog; + } + public boolean isDbgEnableExtendedErrors() { return dbgEnableExtendedErrors; } @@ -586,6 +597,24 @@ public class DataStore extends RealmObject { this.nsEnablePatternChange = nsEnablePatternChange; } + public int getNsGramsPerExchange() { + return nsGramsPerExchange; + } + + public void setNsGramsPerExchange(int nsGramsPerExchange) { + if (this.nsGramsPerExchange != 0 && this.nsGramsPerExchange != nsGramsPerExchange) + nsGramsPerExchangeChanged = true; + this.nsGramsPerExchange = nsGramsPerExchange; + } + + public boolean isNsGramsPerExchangeChanged() { + return nsGramsPerExchangeChanged; + } + + public void setNsGramsPerExchangeChanged(boolean nsGramsPerExchangeChanged) { + this.nsGramsPerExchangeChanged = nsGramsPerExchangeChanged; + } + public boolean isNsEnableInsertBGasCGM() { return nsEnableInsertBGasCGM; } diff --git a/app/src/main/java/info/nightscout/android/upload/nightscout/NightScoutUpload.java b/app/src/main/java/info/nightscout/android/upload/nightscout/NightScoutUpload.java index 51199137b6d0c03d5ae289f85d47b608282d1652..5d7d7628e6fe7a3439d8d3422ddaf5ebce0cd206 100644 --- a/app/src/main/java/info/nightscout/android/upload/nightscout/NightScoutUpload.java +++ b/app/src/main/java/info/nightscout/android/upload/nightscout/NightScoutUpload.java @@ -9,6 +9,7 @@ import java.security.NoSuchAlgorithmException; import java.text.SimpleDateFormat; import java.math.BigDecimal; import java.util.ArrayList; +import java.util.Date; import java.util.List; import java.util.Locale; @@ -79,6 +80,15 @@ public class NightScoutUpload { uploadEvents(records); } + public static String formatDateForNS(Date date) { + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault()); + SimpleDateFormat dftz = new SimpleDateFormat("Z", Locale.getDefault()); + String tz = dftz.format(date); + if (tz.length() == 5 && (tz.startsWith("+") || tz.startsWith("-"))) + return df.format(date) + tz.subSequence(0,3) + ":" + tz.subSequence(3,5); + return df.format(date); + } + private void uploadEvents(List<PumpHistoryInterface> records) throws Exception { cleanupCheck(); @@ -256,8 +266,6 @@ public class NightScoutUpload { } } - private static final SimpleDateFormat ISO8601_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault()); - private void uploadStatus(List<PumpStatusEvent> records, int uploaderBatteryLevel) throws Exception { List<DeviceEndpoints.DeviceStatus> deviceEntries = new ArrayList<>(); @@ -344,7 +352,7 @@ public class NightScoutUpload { pumpstatus = new PumpStatus(false, false, statusPUMP + " " + statusCGM); PumpInfo pumpInfo = new PumpInfo( - ISO8601_DATE_FORMAT.format(record.getEventDate()), + formatDateForNS(record.getEventDate()), new BigDecimal(record.getReservoirAmount()).setScale(0, BigDecimal.ROUND_HALF_UP), iob, battery, @@ -354,7 +362,7 @@ public class NightScoutUpload { DeviceStatus deviceStatus = new DeviceStatus( uploaderBatteryLevel, record.getDeviceName(), - ISO8601_DATE_FORMAT.format(record.getEventDate()), + formatDateForNS(record.getEventDate()), pumpInfo ); diff --git a/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutStatus.java b/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutStatus.java index 4bd81d722eb608ca4cc215c74d20e8eeb4a0d93b..b9b431749af4ce443165340dd53ef2418b5d8c3f 100644 --- a/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutStatus.java +++ b/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutStatus.java @@ -4,8 +4,12 @@ import android.content.Context; import android.content.Intent; import android.net.ConnectivityManager; import android.net.NetworkInfo; +import android.support.annotation.NonNull; import android.util.Log; +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.List; import info.nightscout.android.UploaderApplication; @@ -55,9 +59,9 @@ public class NightscoutStatus { private String ns_version_code = "0.0.0"; private String ns_version_channel = ""; private String ns_version_date = ""; - private String ns_version_major = "0"; - private String ns_version_minor = "0"; - private String ns_version_point = "0"; + private int ns_version_major = 0; + private int ns_version_minor = 0; + private int ns_version_point = 0; private boolean available; private long reporttime; @@ -91,18 +95,21 @@ public class NightscoutStatus { if (!available || report) { String url = dataStore.getNightscoutURL(); - available = new getStatus().run(url); + String secret = dataStore.getNightscoutSECRET(); + available = new getStatus().run(url, secret); if (available && dataStore.isDbgEnableUploadErrors()) { - if (dataStore.getNightscoutReportTime() == 0) { + if (!dataStore.isNightscoutAvailable()) { userLogMessage(ICON_INFO + "Nightscout site is available"); } + /* This doesn't seem to be a requirement for newer versions of NS if (!ns_authDefaultRoles.contains("readable")) { userLogMessage(ICON_WARN + "Nightscout 'authDefaultRoles' check has failed, data can not be uploaded."); - userLogMessage(ICON_HELP + "Add 'authDefaultRoles' and set to 'readable devicestatus-upload' in Azure or Heroku."); + userLogMessage(ICON_HELP + "Add 'AUTH_DEFAULT_ROLES' and set to 'readable devicestatus-upload' in Azure or Heroku."); } + */ if (dataStore.isNsEnableTreatments() && !ns_careportal) { userLogMessage(ICON_WARN + "Careportal is not enabled in Nightscout, treatment data can not be uploaded."); @@ -112,8 +119,12 @@ public class NightscoutStatus { if (report) { reporttime = now; - if (Integer.parseInt(ns_version_major) <= NS_MAJOR && - (Integer.parseInt(ns_version_minor) < NS_MINOR || (Integer.parseInt(ns_version_minor) == NS_MINOR && Integer.parseInt(ns_version_point) < NS_POINT))) { + if (dataStore.isDbgEnableExtendedErrors()) { + userLogMessage("NS version: " + ns_version); + } + + if (ns_version_major <= NS_MAJOR && + (ns_version_minor < NS_MINOR || (ns_version_minor == NS_MINOR && ns_version_point < NS_POINT))) { userLogMessage(ICON_HELP + "Your version of Nightscout is out of date. It is recommended to use the latest release version of Nightscout with the 600 Series Uploader."); } @@ -159,19 +170,6 @@ public class NightscoutStatus { if (!(ns_enable.contains("IOB") || ns_enable.contains("iob"))) { userLogMessage(ICON_HELP + "IOB active insulin can be shown in Nightscout. Add 'iob' to your ENABLE string in Azure or Heroku."); } - - if (false) { - String note = ""; - note += "Nightscout Status:"; - note += "\nver: " + ns_version; - note += "\ncustomTitle: " + ns_customTitle; - note += "\ncareportal: " + ns_careportal; - note += "\nunits: " + ns_units; - note += "\nauthDefaultRoles: " + ns_authDefaultRoles; - note += "\nenable: " + ns_enable; - note += "\npump: " + ns_pumpFields; - userLogMessage(ICON_INFO + note); - } } } else if (!available && dataStore.isDbgEnableUploadErrors()) @@ -192,11 +190,11 @@ public class NightscoutStatus { } private class getStatus extends Thread { - public boolean run(String url) { + public boolean run(String url, String secret) { boolean available = false; try{ - UploadApi uploadApi = new UploadApi(url, null); + UploadApi uploadApi = new UploadApi(url, formToken(secret)); statusEndpoints = uploadApi.getStatusEndpoints(); @@ -220,16 +218,19 @@ public class NightscoutStatus { } String code[] = ns_version_code.split("\\."); if (code.length == 3) { - ns_version_major = code[0]; - ns_version_minor = code[1]; - ns_version_point = code[2]; + try { + ns_version_major = Integer.parseInt(code[0]); + ns_version_minor = Integer.parseInt(code[1]); + ns_version_point = Integer.parseInt(code[2]); + } catch (Exception ignored) { + } } } s = responseBody.body().getName(); if (s != null) ns_name = s; - ns_careportal = responseBody.body().isCareportalEnabled(); + if (responseBody.body().isCareportalEnabled() != null) ns_careportal = responseBody.body().isCareportalEnabled(); if (responseBody.body().getSettings() != null) { @@ -254,12 +255,13 @@ public class NightscoutStatus { StatusEndpoints.Pump pump = responseBody.body().getExtendedSettings().getPump(); if (pump != null) { - ns_pumpFields = pump.getFields(); + s = pump.getFields(); + if (s != null) ns_pumpFields = s; } StatusEndpoints.Devicestatus devicestatus = responseBody.body().getExtendedSettings().getDevicestatus(); if (devicestatus != null) { - ns_devicestatus = devicestatus.isAdvanced(); + if (devicestatus.isAdvanced() != null) ns_devicestatus = devicestatus.isAdvanced(); } } @@ -267,7 +269,7 @@ public class NightscoutStatus { } catch (Exception e) { Log.e(TAG, "Nightscout status check error", e); - if (dataStore.isDbgEnableExtendedErrors()) + if (dataStore.isDbgEnableUploadErrors()) userLogMessage(ICON_WARN + "Nightscout status is not available: " + e.getMessage()); } @@ -275,6 +277,19 @@ public class NightscoutStatus { } } + @NonNull + private String formToken(String secret) throws NoSuchAlgorithmException, UnsupportedEncodingException { + MessageDigest digest = MessageDigest.getInstance("SHA-1"); + byte[] bytes = secret.getBytes("UTF-8"); + digest.update(bytes, 0, bytes.length); + bytes = digest.digest(); + StringBuilder sb = new StringBuilder(bytes.length * 2); + for (byte b : bytes) { + sb.append(String.format("%02x", b & 0xff)); + } + return sb.toString(); + } + private boolean isOnline() { ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo netInfo = cm.getActiveNetworkInfo(); diff --git a/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadService.java b/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadService.java index acc8af81c1e098891f9f5402913569d147e45ea3..82fb6f171655f759252a307089ab4638a13617e8 100644 --- a/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadService.java +++ b/app/src/main/java/info/nightscout/android/upload/nightscout/NightscoutUploadService.java @@ -57,8 +57,10 @@ public class NightscoutUploadService extends Service { if (intent != null) { if (startId == 1) new Upload().start(); - else + else { Log.d(TAG, "Service already in progress with previous task"); + userLogMessage(ICON_WARN + "Uploading service is busy completing previous task. New records will be uploaded after the next poll."); + } } return START_NOT_STICKY; diff --git a/app/src/main/java/info/nightscout/api/ProfileEndpoints.java b/app/src/main/java/info/nightscout/api/ProfileEndpoints.java index 1b18827b42d38570027a6ac1e8c5da20710cc873..4f82c6087fc7fdda7d756899c8d73846fc6c9e1a 100644 --- a/app/src/main/java/info/nightscout/api/ProfileEndpoints.java +++ b/app/src/main/java/info/nightscout/api/ProfileEndpoints.java @@ -3,12 +3,11 @@ package info.nightscout.api; import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; -import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; -import java.util.Locale; import java.util.Map; +import info.nightscout.android.upload.nightscout.NightScoutUpload; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.http.Body; @@ -116,14 +115,12 @@ public interface ProfileEndpoints { } public void setCreated_at(Date created_at) { - this.created_at = ISO8601_DATE_FORMAT.format(created_at); + this.created_at = NightScoutUpload.formatDateForNS(created_at); } public void setStartDate(Date startDate) { - this.startDate = ISO8601_DATE_FORMAT.format(startDate); + this.startDate = NightScoutUpload.formatDateForNS(startDate); } - - private static final SimpleDateFormat ISO8601_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault()); } class BasalProfile { @@ -250,10 +247,8 @@ public interface ProfileEndpoints { } public void setStartDate(Date startDate) { - this.startDate = ISO8601_DATE_FORMAT.format(startDate); + this.startDate = NightScoutUpload.formatDateForNS(startDate); } - - private static final SimpleDateFormat ISO8601_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault()); } class TimePeriod { diff --git a/app/src/main/java/info/nightscout/api/StatusEndpoints.java b/app/src/main/java/info/nightscout/api/StatusEndpoints.java index 5d6cd1ae56202a39118c1735abeb778c00dde83f..66638bfb1b99543871b78ec13f0cbe3bf9f360c7 100644 --- a/app/src/main/java/info/nightscout/api/StatusEndpoints.java +++ b/app/src/main/java/info/nightscout/api/StatusEndpoints.java @@ -31,7 +31,7 @@ public interface StatusEndpoints { private String serverTime; @SerializedName("serverTimeEpoch") @Expose - private Long serverTimeEpoch; + private Float serverTimeEpoch; @SerializedName("apiEnabled") @Expose private Boolean apiEnabled; @@ -86,11 +86,11 @@ public interface StatusEndpoints { this.serverTime = serverTime; } - public Long getServerTimeEpoch() { + public Float getServerTimeEpoch() { return serverTimeEpoch; } - public void setServerTimeEpoch(Long serverTimeEpoch) { + public void setServerTimeEpoch(Float serverTimeEpoch) { this.serverTimeEpoch = serverTimeEpoch; } @@ -159,7 +159,7 @@ public interface StatusEndpoints { private String units; @SerializedName("timeFormat") @Expose - private Long timeFormat; + private Float timeFormat; @SerializedName("nightMode") @Expose private Boolean nightMode; @@ -180,43 +180,43 @@ public interface StatusEndpoints { private Boolean alarmUrgentHigh; @SerializedName("alarmUrgentHighMins") @Expose - private List<Long> alarmUrgentHighMins = null; + private List<Float> alarmUrgentHighMins = null; @SerializedName("alarmHigh") @Expose private Boolean alarmHigh; @SerializedName("alarmHighMins") @Expose - private List<Long> alarmHighMins = null; + private List<Float> alarmHighMins = null; @SerializedName("alarmLow") @Expose private Boolean alarmLow; @SerializedName("alarmLowMins") @Expose - private List<Long> alarmLowMins = null; + private List<Float> alarmLowMins = null; @SerializedName("alarmUrgentLow") @Expose private Boolean alarmUrgentLow; @SerializedName("alarmUrgentLowMins") @Expose - private List<Long> alarmUrgentLowMins = null; + private List<Float> alarmUrgentLowMins = null; @SerializedName("alarmUrgentMins") @Expose - private List<Long> alarmUrgentMins = null; + private List<Float> alarmUrgentMins = null; @SerializedName("alarmWarnMins") @Expose - private List<Long> alarmWarnMins = null; + private List<Float> alarmWarnMins = null; @SerializedName("alarmTimeagoWarn") @Expose private Boolean alarmTimeagoWarn; @SerializedName("alarmTimeagoWarnMins") @Expose - private Long alarmTimeagoWarnMins; + private Float alarmTimeagoWarnMins; @SerializedName("alarmTimeagoUrgent") @Expose private Boolean alarmTimeagoUrgent; @SerializedName("alarmTimeagoUrgentMins") @Expose - private Long alarmTimeagoUrgentMins; + private Float alarmTimeagoUrgentMins; @SerializedName("language") @Expose private String language; @@ -231,10 +231,10 @@ public interface StatusEndpoints { private String showForecast; @SerializedName("focusHours") @Expose - private Long focusHours; + private Float focusHours; @SerializedName("heartbeat") @Expose - private Long heartbeat; + private Float heartbeat; @SerializedName("baseURL") @Expose private String baseURL; @@ -262,11 +262,11 @@ public interface StatusEndpoints { this.units = units; } - public Long getTimeFormat() { + public Float getTimeFormat() { return timeFormat; } - public void setTimeFormat(Long timeFormat) { + public void setTimeFormat(Float timeFormat) { this.timeFormat = timeFormat; } @@ -318,11 +318,11 @@ public interface StatusEndpoints { this.alarmUrgentHigh = alarmUrgentHigh; } - public List<Long> getAlarmUrgentHighMins() { + public List<Float> getAlarmUrgentHighMins() { return alarmUrgentHighMins; } - public void setAlarmUrgentHighMins(List<Long> alarmUrgentHighMins) { + public void setAlarmUrgentHighMins(List<Float> alarmUrgentHighMins) { this.alarmUrgentHighMins = alarmUrgentHighMins; } @@ -334,11 +334,11 @@ public interface StatusEndpoints { this.alarmHigh = alarmHigh; } - public List<Long> getAlarmHighMins() { + public List<Float> getAlarmHighMins() { return alarmHighMins; } - public void setAlarmHighMins(List<Long> alarmHighMins) { + public void setAlarmHighMins(List<Float> alarmHighMins) { this.alarmHighMins = alarmHighMins; } @@ -350,11 +350,11 @@ public interface StatusEndpoints { this.alarmLow = alarmLow; } - public List<Long> getAlarmLowMins() { + public List<Float> getAlarmLowMins() { return alarmLowMins; } - public void setAlarmLowMins(List<Long> alarmLowMins) { + public void setAlarmLowMins(List<Float> alarmLowMins) { this.alarmLowMins = alarmLowMins; } @@ -366,27 +366,27 @@ public interface StatusEndpoints { this.alarmUrgentLow = alarmUrgentLow; } - public List<Long> getAlarmUrgentLowMins() { + public List<Float> getAlarmUrgentLowMins() { return alarmUrgentLowMins; } - public void setAlarmUrgentLowMins(List<Long> alarmUrgentLowMins) { + public void setAlarmUrgentLowMins(List<Float> alarmUrgentLowMins) { this.alarmUrgentLowMins = alarmUrgentLowMins; } - public List<Long> getAlarmUrgentMins() { + public List<Float> getAlarmUrgentMins() { return alarmUrgentMins; } - public void setAlarmUrgentMins(List<Long> alarmUrgentMins) { + public void setAlarmUrgentMins(List<Float> alarmUrgentMins) { this.alarmUrgentMins = alarmUrgentMins; } - public List<Long> getAlarmWarnMins() { + public List<Float> getAlarmWarnMins() { return alarmWarnMins; } - public void setAlarmWarnMins(List<Long> alarmWarnMins) { + public void setAlarmWarnMins(List<Float> alarmWarnMins) { this.alarmWarnMins = alarmWarnMins; } @@ -398,11 +398,11 @@ public interface StatusEndpoints { this.alarmTimeagoWarn = alarmTimeagoWarn; } - public Long getAlarmTimeagoWarnMins() { + public Float getAlarmTimeagoWarnMins() { return alarmTimeagoWarnMins; } - public void setAlarmTimeagoWarnMins(Long alarmTimeagoWarnMins) { + public void setAlarmTimeagoWarnMins(Float alarmTimeagoWarnMins) { this.alarmTimeagoWarnMins = alarmTimeagoWarnMins; } @@ -414,11 +414,11 @@ public interface StatusEndpoints { this.alarmTimeagoUrgent = alarmTimeagoUrgent; } - public Long getAlarmTimeagoUrgentMins() { + public Float getAlarmTimeagoUrgentMins() { return alarmTimeagoUrgentMins; } - public void setAlarmTimeagoUrgentMins(Long alarmTimeagoUrgentMins) { + public void setAlarmTimeagoUrgentMins(Float alarmTimeagoUrgentMins) { this.alarmTimeagoUrgentMins = alarmTimeagoUrgentMins; } @@ -454,19 +454,19 @@ public interface StatusEndpoints { this.showForecast = showForecast; } - public Long getFocusHours() { + public Float getFocusHours() { return focusHours; } - public void setFocusHours(Long focusHours) { + public void setFocusHours(Float focusHours) { this.focusHours = focusHours; } - public Long getHeartbeat() { + public Float getHeartbeat() { return heartbeat; } - public void setHeartbeat(Long heartbeat) { + public void setHeartbeat(Float heartbeat) { this.heartbeat = heartbeat; } @@ -524,46 +524,46 @@ public interface StatusEndpoints { @SerializedName("bgHigh") @Expose - private Long bgHigh; + private Float bgHigh; @SerializedName("bgTargetTop") @Expose - private Long bgTargetTop; + private Float bgTargetTop; @SerializedName("bgTargetBottom") @Expose - private Long bgTargetBottom; + private Float bgTargetBottom; @SerializedName("bgLow") @Expose - private Long bgLow; + private Float bgLow; - public Long getBgHigh() { + public Float getBgHigh() { return bgHigh; } - public void setBgHigh(Long bgHigh) { + public void setBgHigh(Float bgHigh) { this.bgHigh = bgHigh; } - public Long getBgTargetTop() { + public Float getBgTargetTop() { return bgTargetTop; } - public void setBgTargetTop(Long bgTargetTop) { + public void setBgTargetTop(Float bgTargetTop) { this.bgTargetTop = bgTargetTop; } - public Long getBgTargetBottom() { + public Float getBgTargetBottom() { return bgTargetBottom; } - public void setBgTargetBottom(Long bgTargetBottom) { + public void setBgTargetBottom(Float bgTargetBottom) { this.bgTargetBottom = bgTargetBottom; } - public Long getBgLow() { + public Float getBgLow() { return bgLow; } - public void setBgLow(Long bgLow) { + public void setBgLow(Float bgLow) { this.bgLow = bgLow; } diff --git a/app/src/main/java/info/nightscout/api/TreatmentsEndpoints.java b/app/src/main/java/info/nightscout/api/TreatmentsEndpoints.java index ebb7e9492fc9b1d82490459d8b3c1b50d6903797..3ba1261c8535f0e00df7eac7f44607afe1b65c81 100644 --- a/app/src/main/java/info/nightscout/api/TreatmentsEndpoints.java +++ b/app/src/main/java/info/nightscout/api/TreatmentsEndpoints.java @@ -3,11 +3,10 @@ package info.nightscout.api; import com.google.gson.annotations.SerializedName; import java.math.BigDecimal; -import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; -import java.util.Locale; +import info.nightscout.android.upload.nightscout.NightScoutUpload; import okhttp3.ResponseBody; import retrofit2.Call; import retrofit2.http.Body; @@ -269,10 +268,8 @@ public interface TreatmentsEndpoints { } public void setCreated_at(Date created_at) { - this.created_at = ISO8601_DATE_FORMAT.format(created_at); + this.created_at = NightScoutUpload.formatDateForNS(created_at); } - - private static final SimpleDateFormat ISO8601_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault()); } // find treatment using key diff --git a/app/src/main/java/info/nightscout/urchin/Urchin.java b/app/src/main/java/info/nightscout/urchin/Urchin.java index ccf1b17c0c6836090586d4254ed0399b236f5f6e..e0405497aeab4b84e7d196b67147ed2873aeb6c7 100644 --- a/app/src/main/java/info/nightscout/urchin/Urchin.java +++ b/app/src/main/java/info/nightscout/urchin/Urchin.java @@ -538,7 +538,7 @@ public class Urchin { insulin = results.first().getNormalProgrammedAmount(); } - text += new BigDecimal(insulin).setScale(1, BigDecimal.ROUND_HALF_UP).stripTrailingZeros() + text += new BigDecimal(insulin).setScale(1, BigDecimal.ROUND_HALF_UP).stripTrailingZeros().toPlainString() + styleUnits() + styleConcatenate() + styleTime(results.first().getProgrammedDate().getTime()); } @@ -640,7 +640,6 @@ public class Urchin { && pumpStatusEvent.isCgmActive() && pumpStatusEvent.getEventDate().getTime() >= timeNow - 15 *60000L) { - text = ""; if (pumpStatusEvent.isCgmCalibrating()) text = "C"; else { diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 8bd64359842148e86b01db983f79444acfb6bd13..a721247dee3f8ab397b26793fb1748c97980c7a3 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -170,6 +170,44 @@ <item>8</item> </string-array> + <string-array name="grams_per_exchange"> + <item>5 Grams</item> + <item>6 Grams</item> + <item>7 Grams</item> + <item>8 Grams</item> + <item>9 Grams</item> + <item>10 Grams</item> + <item>11 Grams</item> + <item>12 Grams</item> + <item>13 Grams</item> + <item>14 Grams</item> + <item>15 Grams</item> + <item>16 Grams</item> + <item>17 Grams</item> + <item>18 Grams</item> + <item>19 Grams</item> + <item>20 Grams</item> + </string-array> + + <string-array name="grams_per_exchange_values"> + <item>5</item> + <item>6</item> + <item>7</item> + <item>8</item> + <item>9</item> + <item>10</item> + <item>11</item> + <item>12</item> + <item>13</item> + <item>14</item> + <item>15</item> + <item>16</item> + <item>17</item> + <item>18</item> + <item>19</item> + <item>20</item> + </string-array> + <string-array name="poll_period"> <item>10 seconds</item> <item>15 seconds</item> @@ -296,14 +334,14 @@ </string-array> <string-array name="urchin_battery"> - <item>0-100</item> - <item>0-100﹪</item> - <item>0-99</item> - <item>0-99﹪</item> - <item>0-9</item> - <item>Hi-Lo</item> - <item>H-M-L</item> - <item>F-H-M-L-E</item> + <item>0–100</item> + <item>0–100﹪</item> + <item>0–99</item> + <item>0–99﹪</item> + <item>0–9</item> + <item>Hi–Lo</item> + <item>H–M–L</item> + <item>F–H–M–L–E</item> </string-array> <string-array name="urchin_units"> @@ -317,7 +355,7 @@ <item>space: \'2.55u 1h15m\'</item> <item>dot: \'2.5u·1h15m\'</item> <item>bullet: \'2.5u•1h15m\'</item> - <item>dash: \'2.5u-1h15m\'</item> + <item>dash: \'2.5u–1h15m\'</item> <item>slash: \'2.5u/1h15m\'</item> <item>slash: \'2.5u\\1h15m\'</item> <item>line: \'2.5u|1h15m\'</item> diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index e79774620b5153c642eac92db2ba29db1e352e31..32dafd6d2aaf229f2450770929c6c8a38b55b0aa 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -171,6 +171,15 @@ android:summary="Send basal pattern changes to Nightscout"/> </PreferenceCategory> + <PreferenceCategory android:title="Misc"> + <ListPreference android:title="Grams Per Exchange" + android:key="nsGramsPerExchange" + android:defaultValue="15" + android:summary="%s" + android:entries="@array/grams_per_exchange" + android:entryValues="@array/grams_per_exchange_values"/> + </PreferenceCategory> + <PreferenceCategory android:title="Pattern and Preset Naming"> <PreferenceScreen android:title="Basal Patterns" @@ -738,6 +747,18 @@ android:switchTextOn="on" android:title="Control / Passthrough"/> </PreferenceCategory> + + <PreferenceCategory android:title="USB"> + <info.nightscout.android.utils.CustomSwitchPreference + android:disableDependentsState="false" + android:key="sysEnableUsbPermissionDialog" + android:defaultValue="false" + android:summaryOff="Request permission via OS dialog" + android:summaryOn="Request permission via APP dialog" + android:switchTextOff="off" + android:switchTextOn="on" + android:title="Permission Request"/> + </PreferenceCategory> </PreferenceScreen> <PreferenceScreen