From b603fce8c7871b0370045ab85421fd01e4af2aa1 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Sun, 11 Jun 2017 12:44:42 +0200 Subject: [PATCH 001/212] do not add empty iob series --- .../androidaps/plugins/Overview/OverviewFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java index 7d81f307a2..2723dd7757 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java @@ -1391,7 +1391,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, iobGraph.getSeries().clear(); - if (showIobView.isChecked()) { + if (showIobView.isChecked() && iobData.length > 0) { addSeriesWithoutInvalidate(iobSeries, iobGraph); } if (showCobView.isChecked() && cobData.length > 0) { From 39224f421ab62c15770683f64b9868f3897fbadf Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Sun, 11 Jun 2017 13:37:38 +0200 Subject: [PATCH 002/212] fix extended bolus step --- .../nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java | 4 ++-- .../androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java index 6d337048d7..a901f2bb2a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java @@ -82,7 +82,7 @@ public class DanaRPlugin implements PluginBase, PumpInterface, ConstraintsInterf pumpDescription.bolusStep = 0.1d; pumpDescription.isExtendedBolusCapable = true; - pumpDescription.extendedBolusStep = 0.1d; + pumpDescription.extendedBolusStep = 0.05d; pumpDescription.extendedBolusDurationStep = 30; pumpDescription.extendedBolusMaxDuration = 8 * 60; @@ -438,7 +438,7 @@ public class DanaRPlugin implements PluginBase, PumpInterface, ConstraintsInterf Double extendedRateToSet = absoluteRate - getBaseBasalRate(); extendedRateToSet = configBuilderPlugin.applyBasalConstraints(extendedRateToSet); // needs to be rounded to 0.1 - extendedRateToSet = Round.roundTo(extendedRateToSet, 0.1d); + extendedRateToSet = Round.roundTo(extendedRateToSet, pumpDescription.extendedBolusStep); // What is current rate of extended bolusing in u/h? if (Config.logPumpActions) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java index 45330dc6d7..0e431b6a46 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java @@ -85,7 +85,7 @@ public class DanaRKoreanPlugin implements PluginBase, PumpInterface, Constraints pumpDescription.bolusStep = 0.1d; pumpDescription.isExtendedBolusCapable = true; - pumpDescription.extendedBolusStep = 0.1d; + pumpDescription.extendedBolusStep = 0.05d; pumpDescription.extendedBolusDurationStep = 30; pumpDescription.extendedBolusMaxDuration = 8 * 60; @@ -442,7 +442,7 @@ public class DanaRKoreanPlugin implements PluginBase, PumpInterface, Constraints Double extendedRateToSet = absoluteRate - getBaseBasalRate(); extendedRateToSet = configBuilderPlugin.applyBasalConstraints(extendedRateToSet); // needs to be rounded to 0.1 - extendedRateToSet = Round.roundTo(extendedRateToSet, 0.1d); + extendedRateToSet = Round.roundTo(extendedRateToSet, pumpDescription.extendedBolusStep); // What is current rate of extended bolusing in u/h? if (Config.logPumpActions) { From bf953f1240f694efe5ba394a9412d0b710154192 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Sun, 11 Jun 2017 14:29:02 +0200 Subject: [PATCH 003/212] fix maxdailybasal calculation --- .../main/java/info/nightscout/androidaps/data/Profile.java | 4 ++-- .../androidaps/plugins/PumpDanaR/DanaRPlugin.java | 6 +++--- .../plugins/PumpDanaRKorean/DanaRKoreanPlugin.java | 6 +++--- .../androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java | 5 ++--- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/data/Profile.java b/app/src/main/java/info/nightscout/androidaps/data/Profile.java index dd68eff35f..4c44f9ac2e 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/Profile.java +++ b/app/src/main/java/info/nightscout/androidaps/data/Profile.java @@ -112,7 +112,7 @@ public class Profile { public String log() { String ret = "\n"; for (Integer hour = 0; hour < 24; hour++) { - double value = getBasal(hour * 60 * 60); + double value = getBasal((Integer) (hour * 60 * 60)); ret += "NS basal value for " + hour + ":00 is " + value + "\n"; } ret += "NS units: " + getUnits(); @@ -285,7 +285,7 @@ public class Profile { public double getMaxDailyBasal() { Double max = 0d; for (Integer hour = 0; hour < 24; hour++) { - double value = getBasal(hour * 60 * 60); + double value = getBasal((Integer)(hour * 60 * 60)); if (value > max) max = value; } return max; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java index a901f2bb2a..e8c13c9c52 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java @@ -438,7 +438,7 @@ public class DanaRPlugin implements PluginBase, PumpInterface, ConstraintsInterf Double extendedRateToSet = absoluteRate - getBaseBasalRate(); extendedRateToSet = configBuilderPlugin.applyBasalConstraints(extendedRateToSet); // needs to be rounded to 0.1 - extendedRateToSet = Round.roundTo(extendedRateToSet, pumpDescription.extendedBolusStep); + extendedRateToSet = Round.roundTo(extendedRateToSet, pumpDescription.extendedBolusStep * 2); // *2 because of halfhours // What is current rate of extended bolusing in u/h? if (Config.logPumpActions) { @@ -534,7 +534,8 @@ public class DanaRPlugin implements PluginBase, PumpInterface, ConstraintsInterf ConfigBuilderPlugin configBuilderPlugin = MainApp.getConfigBuilder(); insulin = configBuilderPlugin.applyBolusConstraints(insulin); // needs to be rounded - insulin = Round.roundTo(insulin, getPumpDescription().extendedBolusStep); + int durationInHalfHours = Math.max(durationInMinutes / 30, 1); + insulin = Round.roundTo(insulin, getPumpDescription().extendedBolusStep * (1 + durationInHalfHours % 1)); PumpEnactResult result = new PumpEnactResult(); if (pump.isExtendedInProgress && Math.abs(pump.extendedBolusAmount - insulin) < getPumpDescription().extendedBolusStep) { @@ -549,7 +550,6 @@ public class DanaRPlugin implements PluginBase, PumpInterface, ConstraintsInterf log.debug("setExtendedBolus: Correct extended bolus already set. Current: " + pump.extendedBolusAmount + " Asked: " + insulin); return result; } - int durationInHalfHours = Math.max(durationInMinutes / 30, 1); boolean connectionOK = sExecutionService.extendedBolus(insulin, durationInHalfHours); if (connectionOK && pump.isExtendedInProgress && Math.abs(pump.extendedBolusAmount - insulin) < getPumpDescription().extendedBolusStep) { result.enacted = true; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java index 0e431b6a46..0b93b0593e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java @@ -442,7 +442,7 @@ public class DanaRKoreanPlugin implements PluginBase, PumpInterface, Constraints Double extendedRateToSet = absoluteRate - getBaseBasalRate(); extendedRateToSet = configBuilderPlugin.applyBasalConstraints(extendedRateToSet); // needs to be rounded to 0.1 - extendedRateToSet = Round.roundTo(extendedRateToSet, pumpDescription.extendedBolusStep); + extendedRateToSet = Round.roundTo(extendedRateToSet, pumpDescription.extendedBolusStep * 2); // *2 because of 30 min // What is current rate of extended bolusing in u/h? if (Config.logPumpActions) { @@ -538,7 +538,8 @@ public class DanaRKoreanPlugin implements PluginBase, PumpInterface, Constraints ConfigBuilderPlugin configBuilderPlugin = MainApp.getConfigBuilder(); insulin = configBuilderPlugin.applyBolusConstraints(insulin); // needs to be rounded - insulin = Round.roundTo(insulin, getPumpDescription().extendedBolusStep); + int durationInHalfHours = Math.max(durationInMinutes / 30, 1); + insulin = Round.roundTo(insulin, getPumpDescription().extendedBolusStep * (1 + durationInHalfHours % 1)); PumpEnactResult result = new PumpEnactResult(); if (pump.isExtendedInProgress && Math.abs(pump.extendedBolusAmount - insulin) < getPumpDescription().extendedBolusStep) { @@ -553,7 +554,6 @@ public class DanaRKoreanPlugin implements PluginBase, PumpInterface, Constraints log.debug("setExtendedBolus: Correct extended bolus already set. Current: " + pump.extendedBolusAmount + " Asked: " + insulin); return result; } - int durationInHalfHours = Math.max(durationInMinutes / 30, 1); boolean connectionOK = sExecutionService.extendedBolus(insulin, durationInHalfHours); if (connectionOK && pump.isExtendedInProgress && Math.abs(pump.extendedBolusAmount - insulin) < getPumpDescription().extendedBolusStep) { result.enacted = true; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java index cf2cad1527..b18a0a79ac 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java @@ -467,8 +467,8 @@ public class DanaRv2Plugin implements PluginBase, PumpInterface, ConstraintsInte ConfigBuilderPlugin configBuilderPlugin = MainApp.getConfigBuilder(); insulin = configBuilderPlugin.applyBolusConstraints(insulin); // needs to be rounded - insulin = Round.roundTo(insulin, getPumpDescription().extendedBolusStep); - + int durationInHalfHours = Math.max(durationInMinutes / 30, 1); + insulin = Round.roundTo(insulin, getPumpDescription().extendedBolusStep * (1 + durationInHalfHours % 1)); PumpEnactResult result = new PumpEnactResult(); if (pump.isExtendedInProgress && Math.abs(pump.extendedBolusAmount - insulin) < getPumpDescription().extendedBolusStep) { result.enacted = false; @@ -482,7 +482,6 @@ public class DanaRv2Plugin implements PluginBase, PumpInterface, ConstraintsInte log.debug("setExtendedBolus: Correct extended bolus already set. Current: " + pump.extendedBolusAmount + " Asked: " + insulin); return result; } - int durationInHalfHours = Math.max(durationInMinutes / 30, 1); boolean connectionOK = sExecutionService.extendedBolus(insulin, durationInHalfHours); if (connectionOK && pump.isExtendedInProgress && Math.abs(pump.extendedBolusAmount - insulin) < getPumpDescription().extendedBolusStep) { result.enacted = true; From 77659e20e7c03ac78a752020cf099be1ea10a58f Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Sun, 11 Jun 2017 17:22:54 +0200 Subject: [PATCH 004/212] ns alarms & getBasal fix --- app/src/main/AndroidManifest.xml | 37 ++++- .../nightscout/androidaps/MainActivity.java | 4 + .../Services/AlarmSoundService.java | 71 +++++++++ .../androidaps/Services/DataService.java | 2 +- .../androidaps/Services/Intents.java | 5 + .../broadcasts/NSClearAlarmBroadcast.java | 37 +++++ .../androidaps/db/CareportalEvent.java | 2 +- .../broadcasts/BroadcastAlarm.java | 33 ++++ .../broadcasts/BroadcastAnnouncement.java | 34 ++++ .../broadcasts/BroadcastCals.java | 2 +- .../broadcasts/BroadcastClearAlarm.java | 33 ++++ .../broadcasts/BroadcastDeviceStatus.java | 4 +- .../broadcasts/BroadcastMbgs.java | 2 +- .../broadcasts/BroadcastProfile.java | 2 +- .../broadcasts/BroadcastQueueStatus.java | 2 +- .../broadcasts/BroadcastSgvs.java | 4 +- .../broadcasts/BroadcastStatus.java | 2 +- .../broadcasts/BroadcastTreatment.java | 10 +- .../broadcasts/BroadcastUrgentAlarm.java | 33 ++++ .../NSClientInternal/data/AlarmAck.java | 11 ++ .../NSClientInternal/data/NSAlarm.java | 64 ++++++++ .../receivers/AckAlarmReceiver.java | 64 ++++++++ .../receivers/DBAccessReceiver.java | 4 +- .../services/NSClientService.java | 145 +++++++++++++++--- .../plugins/Overview/Notification.java | 33 +++- .../plugins/Overview/OverviewFragment.java | 6 + .../plugins/PumpDanaR/DanaRPlugin.java | 2 +- .../PumpDanaR/History/DanaRNSHistorySync.java | 20 +-- .../services/DanaRExecutionService.java | 2 +- .../PumpDanaRKorean/DanaRKoreanPlugin.java | 2 +- .../services/DanaRKoreanExecutionService.java | 2 +- .../plugins/PumpDanaRv2/DanaRv2Plugin.java | 2 +- .../services/DanaRv2ExecutionService.java | 2 +- .../androidaps/receivers/NSAlarmReceiver.java | 47 ++++++ .../info/nightscout/utils/BolusWizard.java | 2 +- .../java/info/nightscout/utils/NSUpload.java | 12 +- app/src/main/res/raw/bgalarm.mp3 | Bin 0 -> 70559 bytes app/src/main/res/raw/staledataalarm.mp3 | Bin 0 -> 65875 bytes app/src/main/res/values/colors.xml | 1 + 39 files changed, 669 insertions(+), 71 deletions(-) create mode 100644 app/src/main/java/info/nightscout/androidaps/Services/AlarmSoundService.java create mode 100644 app/src/main/java/info/nightscout/androidaps/broadcasts/NSClearAlarmBroadcast.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastAlarm.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastAnnouncement.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastClearAlarm.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastUrgentAlarm.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/data/AlarmAck.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/data/NSAlarm.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/receivers/AckAlarmReceiver.java create mode 100644 app/src/main/java/info/nightscout/androidaps/receivers/NSAlarmReceiver.java create mode 100644 app/src/main/res/raw/bgalarm.mp3 create mode 100644 app/src/main/res/raw/staledataalarm.mp3 diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c4f9bcfe04..72a2fb614f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -17,6 +17,7 @@ + @@ -58,7 +59,6 @@ android:enabled="true" android:exported="true"> - @@ -79,6 +79,19 @@ + + + + + + + + + + @@ -110,6 +123,14 @@ + + + + + + + - - \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/MainActivity.java b/app/src/main/java/info/nightscout/androidaps/MainActivity.java index c9445ed1f8..98b4294704 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/MainActivity.java @@ -34,6 +34,7 @@ import com.squareup.otto.Subscribe; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import info.nightscout.androidaps.Services.AlarmSoundService; import info.nightscout.androidaps.events.EventAppExit; import info.nightscout.androidaps.events.EventPreferenceChange; import info.nightscout.androidaps.events.EventRefreshGui; @@ -79,6 +80,9 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe registerBus(); setUpTabs(false); + Intent alarm = new Intent(this, AlarmSoundService.class); + alarm.putExtra("soundid", R.raw.staledataalarm); + //startService(alarm); } @Subscribe diff --git a/app/src/main/java/info/nightscout/androidaps/Services/AlarmSoundService.java b/app/src/main/java/info/nightscout/androidaps/Services/AlarmSoundService.java new file mode 100644 index 0000000000..79b647b5f5 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/Services/AlarmSoundService.java @@ -0,0 +1,71 @@ +package info.nightscout.androidaps.Services; + +import android.app.Service; +import android.content.Intent; +import android.content.res.AssetFileDescriptor; +import android.media.MediaPlayer; +import android.os.IBinder; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; + +public class AlarmSoundService extends Service { + private static Logger log = LoggerFactory.getLogger(AlarmSoundService.class); + + MediaPlayer player; + int resourceId = R.raw.bgalarm; + + public AlarmSoundService() { + } + + @Override + public IBinder onBind(Intent intent) { + // TODO: Return the communication channel to the service. + throw new UnsupportedOperationException("Not yet implemented"); + } + + @Override + public void onCreate() { + super.onCreate(); + log.debug("onCreate"); + } + + public int onStartCommand(Intent intent, int flags, int startId) { + log.debug("onStartCommand"); + if (intent != null && intent.hasExtra("soundid")) + resourceId = intent.getIntExtra("soundid", R.raw.bgalarm); + + player = new MediaPlayer(); + AssetFileDescriptor afd = MainApp.sResources.openRawResourceFd(resourceId); + if (afd == null) + return START_STICKY; + try { + player.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength()); + afd.close(); + } catch (IOException e) { + e.printStackTrace(); + } + player.setLooping(true); // Set looping + player.setVolume(100, 100); + + try { + player.prepare(); + player.start(); + } catch (IOException e) { + e.printStackTrace(); + } + + return START_STICKY; + } + + @Override + public void onDestroy() { + player.stop(); + player.release(); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/Services/DataService.java b/app/src/main/java/info/nightscout/androidaps/Services/DataService.java index def268bd35..1f968b7218 100644 --- a/app/src/main/java/info/nightscout/androidaps/Services/DataService.java +++ b/app/src/main/java/info/nightscout/androidaps/Services/DataService.java @@ -504,7 +504,7 @@ public class DataService extends IntentService { long date = trJson.getLong("mills"); long now = new Date().getTime(); if (date > now - 15 * 60 * 1000L && trJson.has("notes")) { - Notification announcement = new Notification(Notification.ANNOUNCEMENT, trJson.getString("notes"), Notification.URGENT); + Notification announcement = new Notification(Notification.NSANNOUNCEMENT, trJson.getString("notes"), Notification.ANNOUNCEMENT, 60); MainApp.bus().post(new EventNewNotification(announcement)); } } diff --git a/app/src/main/java/info/nightscout/androidaps/Services/Intents.java b/app/src/main/java/info/nightscout/androidaps/Services/Intents.java index 1b7b0875e5..77ef5139ae 100644 --- a/app/src/main/java/info/nightscout/androidaps/Services/Intents.java +++ b/app/src/main/java/info/nightscout/androidaps/Services/Intents.java @@ -12,12 +12,17 @@ public interface Intents { String ACTION_NEW_CAL = "info.nightscout.client.NEW_CAL"; String ACTION_NEW_STATUS = "info.nightscout.client.NEW_STATUS"; String ACTION_QUEUE_STATUS = "info.nightscout.client.QUEUE_STATUS"; + String ACTION_ANNOUNCEMENT = "info.nightscout.client.ANNOUNCEMENT"; + String ACTION_ALARM = "info.nightscout.client.ALARM"; + String ACTION_URGENT_ALARM = "info.nightscout.client.URGENT_ALARM"; + String ACTION_CLEAR_ALARM = "info.nightscout.client.CLEAR_ALARM"; // App -> NSClient String ACTION_DATABASE = "info.nightscout.client.DBACCESS"; String ACTION_RESTART = "info.nightscout.client.RESTART"; String ACTION_RESEND = "info.nightscout.client.RESEND"; + String ACTION_ACK_ALARM = "info.nightscout.client.ACK_ALARM"; // xDrip -> App String RECEIVER_PERMISSION = "com.eveningoutpost.dexdrip.permissions.RECEIVE_BG_ESTIMATE"; diff --git a/app/src/main/java/info/nightscout/androidaps/broadcasts/NSClearAlarmBroadcast.java b/app/src/main/java/info/nightscout/androidaps/broadcasts/NSClearAlarmBroadcast.java new file mode 100644 index 0000000000..b624b52843 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/broadcasts/NSClearAlarmBroadcast.java @@ -0,0 +1,37 @@ +package info.nightscout.androidaps.broadcasts; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.os.Bundle; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +import info.nightscout.androidaps.Services.Intents; +import info.nightscout.androidaps.plugins.NSClientInternal.data.NSAlarm; + +/** + * Created by mike on 11.06.2017. + */ + +public class NSClearAlarmBroadcast { + private static Logger log = LoggerFactory.getLogger(NSClearAlarmBroadcast.class); + + public static void handleClearAlarm(NSAlarm originalAlarm, Context context, long silenceTimeInMsec) { + Bundle bundle = new Bundle(); + bundle.putInt("level", originalAlarm.getLevel()); + bundle.putString("group", originalAlarm.getGroup()); + bundle.putLong("silenceTime", silenceTimeInMsec); + Intent intent = new Intent(Intents.ACTION_ACK_ALARM); + intent.putExtras(bundle); + intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); + context.sendBroadcast(intent); + List x = context.getPackageManager().queryBroadcastReceivers(intent, 0); + + log.debug("ACKALARM " + x.size() + " receivers"); + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java b/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java index 3d67abc597..17807443c3 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java +++ b/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java @@ -230,7 +230,7 @@ public class CareportalEvent implements DataPointWithLabelInterface { @Override public int getColor() { if (eventType.equals(ANNOUNCEMENT)) - return 0xFFFF8C00; + return MainApp.sResources.getColor(R.color.notificationAnnouncement); if (eventType.equals(MBG)) return Color.RED; if (eventType.equals(BGCHECK)) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastAlarm.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastAlarm.java new file mode 100644 index 0000000000..46e2ef1e68 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastAlarm.java @@ -0,0 +1,33 @@ +package info.nightscout.androidaps.plugins.NSClientInternal.broadcasts; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.os.Bundle; + +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +import info.nightscout.androidaps.Services.Intents; + +/** + * Created by mike on 26.06.2016. + */ +public class BroadcastAlarm { + private static Logger log = LoggerFactory.getLogger(BroadcastAlarm.class); + + public static void handleAlarm(JSONObject alarm, Context context) { + Bundle bundle = new Bundle(); + bundle.putString("data", alarm.toString()); + Intent intent = new Intent(Intents.ACTION_ALARM); + intent.putExtras(bundle); + intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); + context.sendBroadcast(intent); + List x = context.getPackageManager().queryBroadcastReceivers(intent, 0); + + log.debug("ALARM " + x.size() + " receivers"); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastAnnouncement.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastAnnouncement.java new file mode 100644 index 0000000000..bb4d3de726 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastAnnouncement.java @@ -0,0 +1,34 @@ +package info.nightscout.androidaps.plugins.NSClientInternal.broadcasts; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.os.Bundle; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +import info.nightscout.androidaps.Services.Intents; + +/** + * Created by mike on 26.06.2016. + */ +public class BroadcastAnnouncement { + private static Logger log = LoggerFactory.getLogger(BroadcastAnnouncement.class); + + public static void handleAnnouncement(JSONObject announcement, Context context) { + Bundle bundle = new Bundle(); + bundle.putString("data", announcement.toString()); + Intent intent = new Intent(Intents.ACTION_ANNOUNCEMENT); + intent.putExtras(bundle); + intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); + context.sendBroadcast(intent); + List x = context.getPackageManager().queryBroadcastReceivers(intent, 0); + + log.debug("ANNOUNCEMENT " + x.size() + " receivers"); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastCals.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastCals.java index 58a24e825c..6601207851 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastCals.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastCals.java @@ -19,7 +19,7 @@ import info.nightscout.androidaps.Services.Intents; public class BroadcastCals { private static Logger log = LoggerFactory.getLogger(BroadcastCals.class); - public void handleNewCal(JSONArray cals, Context context, boolean isDelta) { + public static void handleNewCal(JSONArray cals, Context context, boolean isDelta) { Bundle bundle = new Bundle(); bundle.putString("cals", cals.toString()); bundle.putBoolean("delta", isDelta); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastClearAlarm.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastClearAlarm.java new file mode 100644 index 0000000000..7272a81d95 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastClearAlarm.java @@ -0,0 +1,33 @@ +package info.nightscout.androidaps.plugins.NSClientInternal.broadcasts; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.os.Bundle; + +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +import info.nightscout.androidaps.Services.Intents; + +/** + * Created by mike on 26.06.2016. + */ +public class BroadcastClearAlarm { + private static Logger log = LoggerFactory.getLogger(BroadcastClearAlarm.class); + + public static void handleClearAlarm(JSONObject clearalarm, Context context) { + Bundle bundle = new Bundle(); + bundle.putString("data", clearalarm.toString()); + Intent intent = new Intent(Intents.ACTION_CLEAR_ALARM); + intent.putExtras(bundle); + intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); + context.sendBroadcast(intent); + List x = context.getPackageManager().queryBroadcastReceivers(intent, 0); + + log.debug("CLEARALARM " + x.size() + " receivers"); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastDeviceStatus.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastDeviceStatus.java index 64fd72b36a..f527892378 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastDeviceStatus.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastDeviceStatus.java @@ -18,7 +18,7 @@ import info.nightscout.androidaps.Services.Intents; public class BroadcastDeviceStatus { private static Logger log = LoggerFactory.getLogger(BroadcastDeviceStatus.class); - public void handleNewDeviceStatus(JSONObject status, Context context, boolean isDelta) { + public static void handleNewDeviceStatus(JSONObject status, Context context, boolean isDelta) { Bundle bundle = new Bundle(); bundle.putString("devicestatus", status.toString()); bundle.putBoolean("delta", isDelta); @@ -30,7 +30,7 @@ public class BroadcastDeviceStatus { log.debug("DEVICESTATUS " + x.size() + " receivers"); } - public void handleNewDeviceStatus(JSONArray statuses, Context context, boolean isDelta) { + public static void handleNewDeviceStatus(JSONArray statuses, Context context, boolean isDelta) { Bundle bundle = new Bundle(); bundle.putString("devicestatuses", statuses.toString()); bundle.putBoolean("delta", isDelta); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastMbgs.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastMbgs.java index 33669e1852..1430fbaf1a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastMbgs.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastMbgs.java @@ -19,7 +19,7 @@ import info.nightscout.androidaps.Services.Intents; public class BroadcastMbgs { private static Logger log = LoggerFactory.getLogger(BroadcastMbgs.class); - public void handleNewMbg(JSONArray mbgs, Context context, boolean isDelta) { + public static void handleNewMbg(JSONArray mbgs, Context context, boolean isDelta) { Bundle bundle = new Bundle(); bundle.putString("mbgs", mbgs.toString()); bundle.putBoolean("delta", isDelta); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastProfile.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastProfile.java index 641a4e6a77..4edad5db33 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastProfile.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastProfile.java @@ -20,7 +20,7 @@ import info.nightscout.androidaps.data.ProfileStore; public class BroadcastProfile { private static Logger log = LoggerFactory.getLogger(BroadcastProfile.class); - public void handleNewTreatment(ProfileStore profile, Context context, boolean isDelta) { + public static void handleNewTreatment(ProfileStore profile, Context context, boolean isDelta) { Bundle bundle = new Bundle(); bundle.putString("profile", profile.getData().toString()); bundle.putBoolean("delta", isDelta); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastQueueStatus.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastQueueStatus.java index ed2b01a27c..fbcc1ed7a3 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastQueueStatus.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastQueueStatus.java @@ -11,7 +11,7 @@ import info.nightscout.androidaps.Services.Intents; * Created by mike on 28.02.2016. */ public class BroadcastQueueStatus { - public void handleNewStatus(int size, Context context) { + public static void handleNewStatus(int size, Context context) { PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "sendQueue"); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastSgvs.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastSgvs.java index 81a2621a77..e846b8171b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastSgvs.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastSgvs.java @@ -20,7 +20,7 @@ import info.nightscout.androidaps.Services.Intents; public class BroadcastSgvs { private static Logger log = LoggerFactory.getLogger(BroadcastSgvs.class); - public void handleNewSgv(JSONObject sgv, Context context, boolean isDelta) { + public static void handleNewSgv(JSONObject sgv, Context context, boolean isDelta) { Bundle bundle = new Bundle(); bundle.putString("sgv", sgv.toString()); bundle.putBoolean("delta", isDelta); @@ -33,7 +33,7 @@ public class BroadcastSgvs { log.debug("SGV " + x.size() + " receivers"); } - public void handleNewSgv(JSONArray sgvs, Context context, boolean isDelta) { + public static void handleNewSgv(JSONArray sgvs, Context context, boolean isDelta) { Bundle bundle = new Bundle(); bundle.putString("sgvs", sgvs.toString()); bundle.putBoolean("delta", isDelta); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastStatus.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastStatus.java index b99950c89b..b3bd3cfac2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastStatus.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastStatus.java @@ -22,7 +22,7 @@ import info.nightscout.androidaps.plugins.NSClientInternal.services.NSClientServ public class BroadcastStatus { private static Logger log = LoggerFactory.getLogger(BroadcastStatus.class); - public void handleNewStatus(NSStatus status, Context context, boolean isDelta) { + public static void handleNewStatus(NSStatus status, Context context, boolean isDelta) { Bundle bundle = new Bundle(); try { bundle.putString("nsclientversionname", MainApp.instance().getPackageManager().getPackageInfo(MainApp.instance().getPackageName(), 0).versionName); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastTreatment.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastTreatment.java index 9d1abe43e3..0eeadf24c6 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastTreatment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastTreatment.java @@ -22,7 +22,7 @@ import info.nightscout.androidaps.plugins.NSClientInternal.data.NSTreatment; public class BroadcastTreatment { private static Logger log = LoggerFactory.getLogger(BroadcastTreatment.class); - public void handleNewTreatment(NSTreatment treatment, Context context, boolean isDelta) { + public static void handleNewTreatment(NSTreatment treatment, Context context, boolean isDelta) { Bundle bundle = new Bundle(); bundle.putString("treatment", treatment.getData().toString()); bundle.putBoolean("delta", isDelta); @@ -35,7 +35,7 @@ public class BroadcastTreatment { log.debug("TREAT_ADD " + treatment.getEventType() + " " + x.size() + " receivers"); } - public void handleNewTreatment(JSONArray treatments, Context context, boolean isDelta) { + public static void handleNewTreatment(JSONArray treatments, Context context, boolean isDelta) { Bundle bundle = new Bundle(); bundle.putString("treatments", treatments.toString()); bundle.putBoolean("delta", isDelta); @@ -63,7 +63,7 @@ public class BroadcastTreatment { } catch (JSONException e) {} } - public void handleChangedTreatment(JSONArray treatments, Context context, boolean isDelta) { + public static void handleChangedTreatment(JSONArray treatments, Context context, boolean isDelta) { Bundle bundle = new Bundle(); bundle.putString("treatments", treatments.toString()); bundle.putBoolean("delta", isDelta); @@ -76,7 +76,7 @@ public class BroadcastTreatment { log.debug("TREAT_CHANGE " + treatments.length() + " " + x.size() + " receivers"); } - public void handleRemovedTreatment(JSONObject treatment, Context context, boolean isDelta) { + public static void handleRemovedTreatment(JSONObject treatment, Context context, boolean isDelta) { Bundle bundle = new Bundle(); bundle.putString("treatment", treatment.toString()); bundle.putBoolean("delta", isDelta); @@ -91,7 +91,7 @@ public class BroadcastTreatment { } catch (JSONException e) {} } - public void handleRemovedTreatment(JSONArray treatments, Context context, boolean isDelta) { + public static void handleRemovedTreatment(JSONArray treatments, Context context, boolean isDelta) { Bundle bundle = new Bundle(); bundle.putString("treatments", treatments.toString()); bundle.putBoolean("delta", isDelta); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastUrgentAlarm.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastUrgentAlarm.java new file mode 100644 index 0000000000..10d0f068f5 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/broadcasts/BroadcastUrgentAlarm.java @@ -0,0 +1,33 @@ +package info.nightscout.androidaps.plugins.NSClientInternal.broadcasts; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.os.Bundle; + +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +import info.nightscout.androidaps.Services.Intents; + +/** + * Created by mike on 26.06.2016. + */ +public class BroadcastUrgentAlarm { + private static Logger log = LoggerFactory.getLogger(BroadcastUrgentAlarm.class); + + public static void handleUrgentAlarm(JSONObject urgentalarm, Context context) { + Bundle bundle = new Bundle(); + bundle.putString("data", urgentalarm.toString()); + Intent intent = new Intent(Intents.ACTION_URGENT_ALARM); + intent.putExtras(bundle); + intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); + context.sendBroadcast(intent); + List x = context.getPackageManager().queryBroadcastReceivers(intent, 0); + + log.debug("URGENTALARM " + x.size() + " receivers"); + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/data/AlarmAck.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/data/AlarmAck.java new file mode 100644 index 0000000000..109331b34b --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/data/AlarmAck.java @@ -0,0 +1,11 @@ +package info.nightscout.androidaps.plugins.NSClientInternal.data; + +/** + * Created by mike on 11.06.2017. + */ + +public class AlarmAck { + public Integer level = null; + public String group = null; + public Long silenceTime = null; +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/data/NSAlarm.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/data/NSAlarm.java new file mode 100644 index 0000000000..f7a7f79303 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/data/NSAlarm.java @@ -0,0 +1,64 @@ +package info.nightscout.androidaps.plugins.NSClientInternal.data; + +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Created by mike on 11.06.2017. + */ + +public class NSAlarm { + JSONObject data; + + public NSAlarm(JSONObject data) { + this.data = data; + } + + public int getLevel() { + int retval = 0; + if (data.has("level")) { + try { + retval = data.getInt("level"); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return retval; + } + + public String getGroup() { + String retval = "N/A"; + if (data.has("group")) { + try { + retval = data.getString("group"); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return retval; + } + + public String getTile() { + String retval = "N/A"; + if (data.has("title")) { + try { + retval = data.getString("title"); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return retval; + } + + public String getMessage() { + String retval = "N/A"; + if (data.has("message")) { + try { + retval = data.getString("message"); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return retval; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/receivers/AckAlarmReceiver.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/receivers/AckAlarmReceiver.java new file mode 100644 index 0000000000..413d37d55d --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/receivers/AckAlarmReceiver.java @@ -0,0 +1,64 @@ +package info.nightscout.androidaps.plugins.NSClientInternal.receivers; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.PowerManager; + +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Date; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.db.DbRequest; +import info.nightscout.androidaps.interfaces.PluginBase; +import info.nightscout.androidaps.plugins.NSClientInternal.NSClientInternalPlugin; +import info.nightscout.androidaps.plugins.NSClientInternal.UploadQueue; +import info.nightscout.androidaps.plugins.NSClientInternal.data.AlarmAck; +import info.nightscout.androidaps.plugins.NSClientInternal.services.NSClientService; +import info.nightscout.utils.SP; + +public class AckAlarmReceiver extends BroadcastReceiver { + private static Logger log = LoggerFactory.getLogger(AckAlarmReceiver.class); + + + @Override + public void onReceive(Context context, Intent intent) { + PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, + AckAlarmReceiver.class.getSimpleName()); + NSClientInternalPlugin nsClientInternalPlugin = (NSClientInternalPlugin) MainApp.getSpecificPlugin(NSClientInternalPlugin.class); + if (!nsClientInternalPlugin.isEnabled(PluginBase.GENERAL)) { + return; + } + if (SP.getBoolean(R.string.key_ns_noupload, false)) { + log.debug("Upload disabled. Message dropped"); + return; + } + wakeLock.acquire(); + try { + Bundle bundles = intent.getExtras(); + if (bundles == null) return; + if (!bundles.containsKey("level")) return; + if (!bundles.containsKey("group")) return; + if (!bundles.containsKey("silenceTime")) return; + + AlarmAck ack = new AlarmAck(); + ack.level = bundles.getInt("level"); + ack.group = bundles.getString("group"); + ack.silenceTime = bundles.getLong("silenceTime"); + + NSClientService nsClientService = nsClientInternalPlugin.nsClientService; + if (nsClientService != null) + nsClientService.sendAlarmAck(ack); + + } finally { + wakeLock.release(); + } + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/receivers/DBAccessReceiver.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/receivers/DBAccessReceiver.java index 91fca44d12..e2474c5040 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/receivers/DBAccessReceiver.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/receivers/DBAccessReceiver.java @@ -19,6 +19,8 @@ import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.plugins.NSClientInternal.NSClientInternalPlugin; import info.nightscout.androidaps.plugins.NSClientInternal.UploadQueue; import info.nightscout.androidaps.db.DbRequest; +import info.nightscout.androidaps.plugins.NSClientInternal.data.AlarmAck; +import info.nightscout.androidaps.plugins.NSClientInternal.services.NSClientService; import info.nightscout.utils.SP; public class DBAccessReceiver extends BroadcastReceiver { @@ -29,7 +31,7 @@ public class DBAccessReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, - "sendQueue"); + DBAccessReceiver.class.getSimpleName()); NSClientInternalPlugin nsClientInternalPlugin = (NSClientInternalPlugin) MainApp.getSpecificPlugin(NSClientInternalPlugin.class); if (!nsClientInternalPlugin.isEnabled(PluginBase.GENERAL)) { return; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/services/NSClientService.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/services/NSClientService.java index 901b29ad16..8158a37fc4 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/services/NSClientService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/services/NSClientService.java @@ -38,14 +38,18 @@ import info.nightscout.androidaps.plugins.NSClientInternal.UploadQueue; import info.nightscout.androidaps.plugins.NSClientInternal.acks.NSAddAck; import info.nightscout.androidaps.plugins.NSClientInternal.acks.NSAuthAck; import info.nightscout.androidaps.plugins.NSClientInternal.acks.NSUpdateAck; +import info.nightscout.androidaps.plugins.NSClientInternal.broadcasts.BroadcastAlarm; +import info.nightscout.androidaps.plugins.NSClientInternal.broadcasts.BroadcastAnnouncement; import info.nightscout.androidaps.plugins.NSClientInternal.broadcasts.BroadcastCals; +import info.nightscout.androidaps.plugins.NSClientInternal.broadcasts.BroadcastClearAlarm; import info.nightscout.androidaps.plugins.NSClientInternal.broadcasts.BroadcastDeviceStatus; import info.nightscout.androidaps.plugins.NSClientInternal.broadcasts.BroadcastMbgs; import info.nightscout.androidaps.plugins.NSClientInternal.broadcasts.BroadcastProfile; import info.nightscout.androidaps.plugins.NSClientInternal.broadcasts.BroadcastSgvs; import info.nightscout.androidaps.plugins.NSClientInternal.broadcasts.BroadcastStatus; import info.nightscout.androidaps.plugins.NSClientInternal.broadcasts.BroadcastTreatment; -import info.nightscout.androidaps.plugins.NSClientInternal.data.NSCal; +import info.nightscout.androidaps.plugins.NSClientInternal.broadcasts.BroadcastUrgentAlarm; +import info.nightscout.androidaps.plugins.NSClientInternal.data.AlarmAck; import info.nightscout.androidaps.plugins.NSClientInternal.data.NSSgv; import info.nightscout.androidaps.plugins.NSClientInternal.data.NSStatus; import info.nightscout.androidaps.plugins.NSClientInternal.data.NSTreatment; @@ -201,6 +205,10 @@ public class NSClientService extends Service { MainApp.bus().post(new EventNSClientNewLog("NSCLIENT", "do connect")); mSocket.connect(); mSocket.on("dataUpdate", onDataUpdate); + mSocket.on("announcement", onAnnouncement); + mSocket.on("alarm", onAlarm); + mSocket.on("urgent_alarm", onUrgentAlarm); + mSocket.on("clear_alarm", onClearAlarm); } catch (URISyntaxException | RuntimeException e) { MainApp.bus().post(new EventNSClientNewLog("NSCLIENT", "Wrong URL syntax")); MainApp.bus().post(new EventNSClientStatus("Wrong URL syntax")); @@ -297,6 +305,99 @@ public class NSClientService extends Service { } }; + private Emitter.Listener onAnnouncement = new Emitter.Listener() { +/* +{ +"level":0, +"title":"Announcement", +"message":"test", +"plugin":{"name":"treatmentnotify","label":"Treatment Notifications","pluginType":"notification","enabled":true}, +"group":"Announcement", +"isAnnouncement":true, +"key":"9ac46ad9a1dcda79dd87dae418fce0e7955c68da" +} + */ + @Override + public void call(final Object... args) { + JSONObject data = (JSONObject) args[0]; + if (Config.detailedLog) + try { + MainApp.bus().post(new EventNSClientNewLog("ANNOUNCEMENT", data.has("message") ? data.getString("message") : "received")); + } catch (JSONException e) { + e.printStackTrace(); + } + BroadcastAnnouncement.handleAnnouncement(data, getApplicationContext()); + log.debug(data.toString()); + } + }; + + private Emitter.Listener onAlarm = new Emitter.Listener() { +/* +{ +"level":1, +"title":"Warning HIGH", +"message":"BG Now: 5 -0.2 → mmol\/L\nRaw BG: 4.8 mmol\/L Čistý\nBG 15m: 4.8 mmol\/L\nIOB: -0.02U\nCOB: 0g", +"eventName":"high", +"plugin":{"name":"simplealarms","label":"Simple Alarms","pluginType":"notification","enabled":true}, +"pushoverSound":"climb", +"debug":{"lastSGV":5,"thresholds":{"bgHigh":180,"bgTargetTop":75,"bgTargetBottom":72,"bgLow":70}}, +"group":"default", +"key":"simplealarms_1" +} + */ + @Override + public void call(final Object... args) { + if (Config.detailedLog) + MainApp.bus().post(new EventNSClientNewLog("ALARM", "received")); + JSONObject data = (JSONObject) args[0]; + BroadcastAlarm.handleAlarm(data, getApplicationContext()); + log.debug(data.toString()); + } + }; + + private Emitter.Listener onUrgentAlarm = new Emitter.Listener() { +/* +{ +"level":2, +"title":"Urgent HIGH", +"message":"BG Now: 5.2 -0.1 → mmol\/L\nRaw BG: 5 mmol\/L Čistý\nBG 15m: 5 mmol\/L\nIOB: 0.00U\nCOB: 0g", +"eventName":"high", +"plugin":{"name":"simplealarms","label":"Simple Alarms","pluginType":"notification","enabled":true}, +"pushoverSound":"persistent", +"debug":{"lastSGV":5.2,"thresholds":{"bgHigh":80,"bgTargetTop":75,"bgTargetBottom":72,"bgLow":70}}, +"group":"default", +"key":"simplealarms_2" +} + */ + @Override + public void call(final Object... args) { + JSONObject data = (JSONObject) args[0]; + if (Config.detailedLog) + MainApp.bus().post(new EventNSClientNewLog("URGENTALARM", "received")); + BroadcastUrgentAlarm.handleUrgentAlarm(data, getApplicationContext()); + log.debug(data.toString()); + } + }; + + private Emitter.Listener onClearAlarm = new Emitter.Listener() { +/* +{ +"clear":true, +"title":"All Clear", +"message":"default - Urgent was ack'd", +"group":"default" +} + */ + @Override + public void call(final Object... args) { + if (Config.detailedLog) + MainApp.bus().post(new EventNSClientNewLog("CLEARALARM", "received")); + JSONObject data = (JSONObject) args[0]; + BroadcastClearAlarm.handleClearAlarm(data, getApplicationContext()); + log.debug(data.toString()); + } + }; + private Emitter.Listener onDataUpdate = new Emitter.Listener() { @Override public void call(final Object... args) { @@ -339,9 +440,7 @@ public class NSClientService extends Service { nightscoutVersionName = status.getString("version"); nightscoutVersionCode = status.getInt("versionNum"); } - - BroadcastStatus bs = new BroadcastStatus(); - bs.handleNewStatus(nsStatus, MainApp.instance().getApplicationContext(), isDelta); + BroadcastStatus.handleNewStatus(nsStatus, MainApp.instance().getApplicationContext(), isDelta); /* Other received data to 2016/02/10 { @@ -365,17 +464,15 @@ public class NSClientService extends Service { // If new profile received or change detected broadcast it if (broadcastProfile && profileStore != null) { - BroadcastProfile bp = new BroadcastProfile(); - bp.handleNewTreatment(profileStore, MainApp.instance().getApplicationContext(), isDelta); + BroadcastProfile.handleNewTreatment(profileStore, MainApp.instance().getApplicationContext(), isDelta); MainApp.bus().post(new EventNSClientNewLog("PROFILE", "broadcasting")); } if (data.has("treatments")) { - JSONArray treatments = (JSONArray) data.getJSONArray("treatments"); + JSONArray treatments = data.getJSONArray("treatments"); JSONArray removedTreatments = new JSONArray(); JSONArray updatedTreatments = new JSONArray(); JSONArray addedTreatments = new JSONArray(); - BroadcastTreatment bt = new BroadcastTreatment(); if (treatments.length() > 0) MainApp.bus().post(new EventNSClientNewLog("DATA", "received " + treatments.length() + " treatments")); for (Integer index = 0; index < treatments.length(); index++) { @@ -399,18 +496,17 @@ public class NSClientService extends Service { } } if (removedTreatments.length() > 0) { - bt.handleRemovedTreatment(removedTreatments, MainApp.instance().getApplicationContext(), isDelta); + BroadcastTreatment.handleRemovedTreatment(removedTreatments, MainApp.instance().getApplicationContext(), isDelta); } if (updatedTreatments.length() > 0) { - bt.handleChangedTreatment(updatedTreatments, MainApp.instance().getApplicationContext(), isDelta); + BroadcastTreatment.handleChangedTreatment(updatedTreatments, MainApp.instance().getApplicationContext(), isDelta); } if (addedTreatments.length() > 0) { - bt.handleNewTreatment(addedTreatments, MainApp.instance().getApplicationContext(), isDelta); + BroadcastTreatment.handleNewTreatment(addedTreatments, MainApp.instance().getApplicationContext(), isDelta); } } if (data.has("devicestatus")) { - BroadcastDeviceStatus bds = new BroadcastDeviceStatus(); - JSONArray devicestatuses = (JSONArray) data.getJSONArray("devicestatus"); + JSONArray devicestatuses = data.getJSONArray("devicestatus"); if (devicestatuses.length() > 0) { MainApp.bus().post(new EventNSClientNewLog("DATA", "received " + devicestatuses.length() + " devicestatuses")); for (Integer index = 0; index < devicestatuses.length(); index++) { @@ -419,12 +515,11 @@ public class NSClientService extends Service { UploadQueue.removeID(jsonStatus); } // send only last record - bds.handleNewDeviceStatus(devicestatuses.getJSONObject(devicestatuses.length() - 1), MainApp.instance().getApplicationContext(), isDelta); + BroadcastDeviceStatus.handleNewDeviceStatus(devicestatuses.getJSONObject(devicestatuses.length() - 1), MainApp.instance().getApplicationContext(), isDelta); } } if (data.has("mbgs")) { - BroadcastMbgs bmbg = new BroadcastMbgs(); - JSONArray mbgs = (JSONArray) data.getJSONArray("mbgs"); + JSONArray mbgs = data.getJSONArray("mbgs"); if (mbgs.length() > 0) MainApp.bus().post(new EventNSClientNewLog("DATA", "received " + mbgs.length() + " mbgs")); for (Integer index = 0; index < mbgs.length(); index++) { @@ -432,11 +527,10 @@ public class NSClientService extends Service { // remove from upload queue if Ack is failing UploadQueue.removeID(jsonMbg); } - bmbg.handleNewMbg(mbgs, MainApp.instance().getApplicationContext(), isDelta); + BroadcastMbgs.handleNewMbg(mbgs, MainApp.instance().getApplicationContext(), isDelta); } if (data.has("cals")) { - BroadcastCals bc = new BroadcastCals(); - JSONArray cals = (JSONArray) data.getJSONArray("cals"); + JSONArray cals = data.getJSONArray("cals"); if (cals.length() > 0) MainApp.bus().post(new EventNSClientNewLog("DATA", "received " + cals.length() + " cals")); // Retreive actual calibration @@ -444,11 +538,10 @@ public class NSClientService extends Service { // remove from upload queue if Ack is failing UploadQueue.removeID(cals.optJSONObject(index)); } - bc.handleNewCal(cals, MainApp.instance().getApplicationContext(), isDelta); + BroadcastCals.handleNewCal(cals, MainApp.instance().getApplicationContext(), isDelta); } if (data.has("sgvs")) { - BroadcastSgvs bs = new BroadcastSgvs(); - JSONArray sgvs = (JSONArray) data.getJSONArray("sgvs"); + JSONArray sgvs = data.getJSONArray("sgvs"); if (sgvs.length() > 0) MainApp.bus().post(new EventNSClientNewLog("DATA", "received " + sgvs.length() + " sgvs")); for (Integer index = 0; index < sgvs.length(); index++) { @@ -463,7 +556,7 @@ public class NSClientService extends Service { if (sgv.getMills() > latestDateInReceivedData) latestDateInReceivedData = sgv.getMills(); } - bs.handleNewSgv(sgvs, MainApp.instance().getApplicationContext(), isDelta); + BroadcastSgvs.handleNewSgv(sgvs, MainApp.instance().getApplicationContext(), isDelta); } MainApp.bus().post(new EventNSClientNewLog("LAST", DateUtil.dateAndTimeString(latestDateInReceivedData))); } catch (JSONException e) { @@ -543,6 +636,12 @@ public class NSClientService extends Service { } } + public void sendAlarmAck(AlarmAck alarmAck) { + if (!isConnected || !hasWriteAuth) return; + mSocket.emit("ack", alarmAck.level, alarmAck.group, alarmAck.silenceTime); + MainApp.bus().post(new EventNSClientNewLog("ALARMACK ", alarmAck.level + " " + alarmAck.group + " " + alarmAck.silenceTime)); + } + @Subscribe public void onStatusEvent(NSAddAck ack) { if (ack.nsClientID != null) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Notification.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Notification.java index c9a80dbaef..4b190f96bf 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Notification.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Notification.java @@ -2,6 +2,8 @@ package info.nightscout.androidaps.plugins.Overview; import java.util.Date; +import info.nightscout.androidaps.plugins.NSClientInternal.data.NSAlarm; + /** * Created by mike on 03.12.2016. */ @@ -11,6 +13,7 @@ public class Notification { public static final int NORMAL = 1; public static final int LOW = 2; public static final int INFO = 3; + public static final int ANNOUNCEMENT = 4; public static final int PROFILE_SET_FAILED = 0; public static final int PROFILE_SET_OK = 1; @@ -29,7 +32,9 @@ public class Notification { public static final int IC_MISSING = 14; public static final int BASAL_MISSING = 15; public static final int TARGET_MISSING = 16; - public static final int ANNOUNCEMENT = 17; + public static final int NSANNOUNCEMENT = 17; + public static final int NSALARM = 18; + public static final int NSURGENTALARM = 18; public int id; public Date date; @@ -37,6 +42,8 @@ public class Notification { public int level; public Date validTo = new Date(0); + public NSAlarm nsAlarm = null; + public Notification() { } @@ -63,4 +70,28 @@ public class Notification { this.level = level; this.validTo = new Date(0); } + + public Notification(NSAlarm nsAlarm) { + this.date = new Date(); + this.validTo = new Date(0); + this.nsAlarm = nsAlarm; + switch (nsAlarm.getLevel()) { + case 0: + this.id = NSANNOUNCEMENT; + this.level = ANNOUNCEMENT; + this.text = nsAlarm.getMessage(); + this.validTo = new Date(new Date().getTime() + 60 * 60 * 1000L); + break; + case 1: + this.id = NSALARM; + this.level = NORMAL; + this.text = nsAlarm.getTile(); + break; + case 2: + this.id = NSURGENTALARM; + this.level = URGENT; + this.text = nsAlarm.getTile(); + break; + } + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java index 2723dd7757..7d9c84a33d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java @@ -64,6 +64,7 @@ import info.nightscout.androidaps.Config; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.broadcasts.NSClearAlarmBroadcast; import info.nightscout.androidaps.data.DetailedBolusInfo; import info.nightscout.androidaps.data.GlucoseStatus; import info.nightscout.androidaps.data.IobTotal; @@ -1631,6 +1632,8 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, holder.cv.setBackgroundColor(ContextCompat.getColor(MainApp.instance(), R.color.notificationLow)); else if (notification.level == Notification.INFO) holder.cv.setBackgroundColor(ContextCompat.getColor(MainApp.instance(), R.color.notificationInfo)); + else if (notification.level == Notification.ANNOUNCEMENT) + holder.cv.setBackgroundColor(ContextCompat.getColor(MainApp.instance(), R.color.notificationAnnouncement)); } @Override @@ -1664,6 +1667,9 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, switch (v.getId()) { case R.id.notification_dismiss: MainApp.bus().post(new EventDismissNotification(notification.id)); + if (notification.nsAlarm != null) { + NSClearAlarmBroadcast.handleClearAlarm(notification.nsAlarm, MainApp.instance().getApplicationContext(), 60 * 60 * 1000L); + } break; } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java index e8c13c9c52..b3cdeb17db 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java @@ -265,7 +265,7 @@ public class DanaRPlugin implements PluginBase, PumpInterface, ConstraintsInterf int basalIncrement = pump.basal48Enable ? 30 * 60 : 60 * 60; for (int h = 0; h < basalValues; h++) { Double pumpValue = pump.pumpProfiles[pump.activeProfile][h]; - Double profileValue = profile.getBasal(h * basalIncrement); + Double profileValue = profile.getBasal((Integer) (h * basalIncrement)); if (profileValue == null) return true; if (Math.abs(pumpValue - profileValue) > getPumpDescription().basalStep) { log.debug("Diff found. Hour: " + h + " Pump: " + pumpValue + " Profile: " + profileValue); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/History/DanaRNSHistorySync.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/History/DanaRNSHistorySync.java index 72ecf91f0f..f491a40b8f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/History/DanaRNSHistorySync.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/History/DanaRNSHistorySync.java @@ -74,7 +74,7 @@ public class DanaRNSHistorySync { nsrec.put("eventType", "Meal Bolus"); nsrec.put("insulin", record.recordValue); nsrec.put("created_at", DateUtil.toISOString(record.recordDate)); - nsrec.put("enteredBy", MainApp.sResources.getString(R.string.app_name)); + nsrec.put("enteredBy", "openaps://" + MainApp.sResources.getString(R.string.app_name)); NSUpload.uploadCareportalEntryToNS(nsrec); uploaded++; ev.message += MainApp.sResources.getString(R.string.danar_sbolus); @@ -92,7 +92,7 @@ public class DanaRNSHistorySync { cal.setTimeInMillis(record.recordDate); cal.add(Calendar.MINUTE, -1 * record.recordDuration); nsrec.put("created_at", DateUtil.toISOString(cal.getTime())); - nsrec.put("enteredBy", MainApp.sResources.getString(R.string.app_name)); + nsrec.put("enteredBy", "openaps://" + MainApp.sResources.getString(R.string.app_name)); NSUpload.uploadCareportalEntryToNS(nsrec); uploaded++; ev.message += MainApp.sResources.getString(R.string.danar_ebolus); @@ -108,7 +108,7 @@ public class DanaRNSHistorySync { nsrec.put("splitNow", 100); nsrec.put("splitExt", 0); nsrec.put("created_at", DateUtil.toISOString(record.recordDate)); - nsrec.put("enteredBy", MainApp.sResources.getString(R.string.app_name)); + nsrec.put("enteredBy", "openaps://" + MainApp.sResources.getString(R.string.app_name)); NSUpload.uploadCareportalEntryToNS(nsrec); uploaded++; ev.message += MainApp.sResources.getString(R.string.danar_dsbolus); @@ -124,7 +124,7 @@ public class DanaRNSHistorySync { cal.setTimeInMillis(record.recordDate); cal.add(Calendar.MINUTE, -1 * record.recordDuration); nsrec.put("created_at", DateUtil.toISOString(cal.getTime())); - nsrec.put("enteredBy", MainApp.sResources.getString(R.string.app_name)); + nsrec.put("enteredBy", "openaps://" + MainApp.sResources.getString(R.string.app_name)); NSUpload.uploadCareportalEntryToNS(nsrec); uploaded++; ev.message += MainApp.sResources.getString(R.string.danar_debolus); @@ -141,7 +141,7 @@ public class DanaRNSHistorySync { nsrec.put("eventType", "Note"); nsrec.put("notes", "Error"); nsrec.put("created_at", DateUtil.toISOString(record.recordDate)); - nsrec.put("enteredBy", MainApp.sResources.getString(R.string.app_name)); + nsrec.put("enteredBy", "openaps://" + MainApp.sResources.getString(R.string.app_name)); NSUpload.uploadCareportalEntryToNS(nsrec); uploaded++; ev.message += MainApp.sResources.getString(R.string.danar_error); @@ -153,7 +153,7 @@ public class DanaRNSHistorySync { nsrec.put("eventType", "Insulin Change"); nsrec.put("notes", "Refill " + record.recordValue + "U"); nsrec.put("created_at", DateUtil.toISOString(record.recordDate)); - nsrec.put("enteredBy", MainApp.sResources.getString(R.string.app_name)); + nsrec.put("enteredBy", "openaps://" + MainApp.sResources.getString(R.string.app_name)); NSUpload.uploadCareportalEntryToNS(nsrec); uploaded++; ev.message += MainApp.sResources.getString(R.string.danar_refill); @@ -166,7 +166,7 @@ public class DanaRNSHistorySync { nsrec.put("absolute", record.recordValue); nsrec.put("duration", 60); nsrec.put("created_at", DateUtil.toISOString(record.recordDate)); - nsrec.put("enteredBy", MainApp.sResources.getString(R.string.app_name)); + nsrec.put("enteredBy", "openaps://" + MainApp.sResources.getString(R.string.app_name)); NSUpload.uploadCareportalEntryToNS(nsrec); uploaded++; ev.message += MainApp.sResources.getString(R.string.danar_basalhour); @@ -182,7 +182,7 @@ public class DanaRNSHistorySync { nsrec.put("glucose", Profile.fromMgdlToUnits(record.recordValue, profile.getUnits())); nsrec.put("glucoseType", "Finger"); nsrec.put("created_at", DateUtil.toISOString(record.recordDate)); - nsrec.put("enteredBy", MainApp.sResources.getString(R.string.app_name)); + nsrec.put("enteredBy", "openaps://" + MainApp.sResources.getString(R.string.app_name)); NSUpload.uploadCareportalEntryToNS(nsrec); uploaded++; ev.message += MainApp.sResources.getString(R.string.danar_glucose); @@ -194,7 +194,7 @@ public class DanaRNSHistorySync { nsrec.put("eventType", "Meal Bolus"); nsrec.put("carbs", record.recordValue); nsrec.put("created_at", DateUtil.toISOString(record.recordDate)); - nsrec.put("enteredBy", MainApp.sResources.getString(R.string.app_name)); + nsrec.put("enteredBy", "openaps://" + MainApp.sResources.getString(R.string.app_name)); NSUpload.uploadCareportalEntryToNS(nsrec); uploaded++; ev.message += MainApp.sResources.getString(R.string.danar_carbohydrate); @@ -206,7 +206,7 @@ public class DanaRNSHistorySync { nsrec.put("eventType", "Note"); nsrec.put("notes", "Alarm: " + record.recordAlarm); nsrec.put("created_at", DateUtil.toISOString(record.recordDate)); - nsrec.put("enteredBy", MainApp.sResources.getString(R.string.app_name)); + nsrec.put("enteredBy", "openaps://" + MainApp.sResources.getString(R.string.app_name)); NSUpload.uploadCareportalEntryToNS(nsrec); uploaded++; ev.message += MainApp.sResources.getString(R.string.danar_alarm); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/services/DanaRExecutionService.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/services/DanaRExecutionService.java index 957819aaf9..eaaf06bb16 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/services/DanaRExecutionService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/services/DanaRExecutionService.java @@ -531,7 +531,7 @@ public class DanaRExecutionService extends Service { for (Integer hour = 0; hour < 24; hour++) { //Some values get truncated to the next lower one. // -> round them to two decimals and make sure we are a small delta larger (that will get truncated) - double value = Math.round(100d * nsProfile.getBasal(hour * 60 * 60))/100d + 0.00001; + double value = Math.round(100d * nsProfile.getBasal((Integer) (hour * 60 * 60)))/100d + 0.00001; if (Config.logDanaMessageDetail) log.debug("NS basal value for " + hour + ":00 is " + value); record[hour] = value; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java index 0b93b0593e..3e5d9c2c48 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java @@ -268,7 +268,7 @@ public class DanaRKoreanPlugin implements PluginBase, PumpInterface, Constraints int basalIncrement = pump.basal48Enable ? 30 * 60 : 60 * 60; for (int h = 0; h < basalValues; h++) { Double pumpValue = pump.pumpProfiles[pump.activeProfile][h]; - Double profileValue = profile.getBasal(h * basalIncrement); + Double profileValue = profile.getBasal((Integer) (h * basalIncrement)); if (profileValue == null) return true; if (Math.abs(pumpValue - profileValue) > getPumpDescription().basalStep) { log.debug("Diff found. Hour: " + h + " Pump: " + pumpValue + " Profile: " + profileValue); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/services/DanaRKoreanExecutionService.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/services/DanaRKoreanExecutionService.java index 89976c6778..7062ee3ccc 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/services/DanaRKoreanExecutionService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/services/DanaRKoreanExecutionService.java @@ -504,7 +504,7 @@ public class DanaRKoreanExecutionService extends Service { private double[] buildDanaRProfileRecord(Profile nsProfile) { double[] record = new double[24]; for (Integer hour = 0; hour < 24; hour++) { - double value = Math.round(100d * nsProfile.getBasal(hour * 60 * 60))/100d + 0.00001; + double value = Math.round(100d * nsProfile.getBasal((Integer) (hour * 60 * 60)))/100d + 0.00001; if (Config.logDanaMessageDetail) log.debug("NS basal value for " + hour + ":00 is " + value); record[hour] = value; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java index b18a0a79ac..305598e2ca 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java @@ -253,7 +253,7 @@ public class DanaRv2Plugin implements PluginBase, PumpInterface, ConstraintsInte int basalIncrement = pump.basal48Enable ? 30 * 60 : 60 * 60; for (int h = 0; h < basalValues; h++) { Double pumpValue = pump.pumpProfiles[pump.activeProfile][h]; - Double profileValue = profile.getBasal(h * basalIncrement); + Double profileValue = profile.getBasal((Integer) (h * basalIncrement)); if (profileValue == null) return true; if (Math.abs(pumpValue - profileValue) > getPumpDescription().basalStep) { log.debug("Diff found. Hour: " + h + " Pump: " + pumpValue + " Profile: " + profileValue); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/services/DanaRv2ExecutionService.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/services/DanaRv2ExecutionService.java index 57e3607504..a4c550ccf9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/services/DanaRv2ExecutionService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/services/DanaRv2ExecutionService.java @@ -537,7 +537,7 @@ public class DanaRv2ExecutionService extends Service { private double[] buildDanaRProfileRecord(Profile nsProfile) { double[] record = new double[24]; for (Integer hour = 0; hour < 24; hour++) { - double value = Math.round(100d * nsProfile.getBasal(hour * 60 * 60))/100d + 0.00001; + double value = Math.round(100d * nsProfile.getBasal((Integer) (hour * 60 * 60)))/100d + 0.00001; if (Config.logDanaMessageDetail) log.debug("NS basal value for " + hour + ":00 is " + value); record[hour] = value; diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/NSAlarmReceiver.java b/app/src/main/java/info/nightscout/androidaps/receivers/NSAlarmReceiver.java new file mode 100644 index 0000000000..41f8eb5297 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/receivers/NSAlarmReceiver.java @@ -0,0 +1,47 @@ +package info.nightscout.androidaps.receivers; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; + +import org.json.JSONException; +import org.json.JSONObject; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.Services.Intents; +import info.nightscout.androidaps.plugins.NSClientInternal.data.NSAlarm; +import info.nightscout.androidaps.plugins.Overview.Notification; +import info.nightscout.androidaps.plugins.Overview.events.EventDismissNotification; +import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification; + +public class NSAlarmReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + if (intent == null) + return; + Bundle bundle = intent.getExtras(); + String data = bundle.getString("data"); + JSONObject json = null; + try { + json = new JSONObject(data); + } catch (JSONException e) { + e.printStackTrace(); + return; + } + NSAlarm nsAlarm = new NSAlarm(json); + switch (intent.getAction()) { + case Intents.ACTION_ANNOUNCEMENT: + case Intents.ACTION_ALARM: + case Intents.ACTION_URGENT_ALARM: + Notification notification = new Notification(nsAlarm); + MainApp.bus().post(new EventNewNotification(notification)); + break; + case Intents.ACTION_CLEAR_ALARM: + MainApp.bus().post(new EventDismissNotification(Notification.NSALARM)); + MainApp.bus().post(new EventDismissNotification(Notification.NSURGENTALARM)); + break; + } + } +} diff --git a/app/src/main/java/info/nightscout/utils/BolusWizard.java b/app/src/main/java/info/nightscout/utils/BolusWizard.java index ea34c7c8f9..2ee3cd30dc 100644 --- a/app/src/main/java/info/nightscout/utils/BolusWizard.java +++ b/app/src/main/java/info/nightscout/utils/BolusWizard.java @@ -99,7 +99,7 @@ public class BolusWizard { insulinFromSuperBolus = specificProfile.getBasal(); long timeAfter1h = new Date().getTime(); timeAfter1h += 60L * 60 * 1000; - insulinFromSuperBolus += specificProfile.getBasal(Profile.secondsFromMidnight(new Date(timeAfter1h))); + insulinFromSuperBolus += specificProfile.getBasal(timeAfter1h); } // Total diff --git a/app/src/main/java/info/nightscout/utils/NSUpload.java b/app/src/main/java/info/nightscout/utils/NSUpload.java index 86d15dc9b7..68c416b11f 100644 --- a/app/src/main/java/info/nightscout/utils/NSUpload.java +++ b/app/src/main/java/info/nightscout/utils/NSUpload.java @@ -47,7 +47,7 @@ public class NSUpload { if (temporaryBasal.pumpId != 0) data.put("pumpId", temporaryBasal.pumpId); data.put("created_at", DateUtil.toISOString(temporaryBasal.date)); - data.put("enteredBy", MainApp.instance().getString(R.string.app_name)); + data.put("enteredBy", "openaps://" + MainApp.instance().getString(R.string.app_name)); data.put("notes", MainApp.sResources.getString(R.string.androidaps_tempbasalstartnote) + " " + temporaryBasal.absoluteRate + "u/h " + temporaryBasal.durationInMinutes + " min"); // ECOR if (originalExtendedAmount != null) data.put("originalExtendedAmount", originalExtendedAmount); // for back synchronization @@ -86,7 +86,7 @@ public class NSUpload { if (temporaryBasal.pumpId != 0) data.put("pumpId", temporaryBasal.pumpId); data.put("created_at", DateUtil.toISOString(temporaryBasal.date)); - data.put("enteredBy", MainApp.instance().getString(R.string.app_name)); + data.put("enteredBy", "openaps://" + MainApp.instance().getString(R.string.app_name)); data.put("notes", MainApp.sResources.getString(R.string.androidaps_tempbasalstartnote) + " " + temporaryBasal.percentRate + "% " + temporaryBasal.durationInMinutes + " min"); // ECOR Bundle bundle = new Bundle(); bundle.putString("action", "dbAdd"); @@ -109,7 +109,7 @@ public class NSUpload { JSONObject data = new JSONObject(); data.put("eventType", CareportalEvent.TEMPBASAL); data.put("created_at", DateUtil.toISOString(time)); - data.put("enteredBy", MainApp.instance().getString(R.string.app_name)); + data.put("enteredBy", "openaps://" + MainApp.instance().getString(R.string.app_name)); data.put("notes", MainApp.sResources.getString(R.string.androidaps_tempbasalendnote)); // ECOR if (isFakedTempBasal) data.put("isFakedTempBasal", isFakedTempBasal); @@ -142,7 +142,7 @@ public class NSUpload { if (extendedBolus.pumpId != 0) data.put("pumpId", extendedBolus.pumpId); data.put("created_at", DateUtil.toISOString(extendedBolus.date)); - data.put("enteredBy", MainApp.instance().getString(R.string.app_name)); + data.put("enteredBy", "openaps://" + MainApp.instance().getString(R.string.app_name)); Bundle bundle = new Bundle(); bundle.putString("action", "dbAdd"); bundle.putString("collection", "treatments"); @@ -168,7 +168,7 @@ public class NSUpload { data.put("enteredinsulin", 0); data.put("relative", 0); data.put("created_at", DateUtil.toISOString(time)); - data.put("enteredBy", MainApp.instance().getString(R.string.app_name)); + data.put("enteredBy", "openaps://" + MainApp.instance().getString(R.string.app_name)); if (pumpId != 0) data.put("pumpId", pumpId); Bundle bundle = new Bundle(); @@ -346,7 +346,7 @@ public class NSUpload { data.put("eventType", "OpenAPS Offline"); data.put("duration", durationInMinutes); data.put("created_at", DateUtil.toISOString(new Date())); - data.put("enteredBy", MainApp.instance().getString(R.string.app_name)); + data.put("enteredBy", "openaps://" + MainApp.instance().getString(R.string.app_name)); Bundle bundle = new Bundle(); bundle.putString("action", "dbAdd"); bundle.putString("collection", "treatments"); diff --git a/app/src/main/res/raw/bgalarm.mp3 b/app/src/main/res/raw/bgalarm.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..381003f87d78014bdf141e4f64243c0b28f4fd47 GIT binary patch literal 70559 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"@Q69Frp!CkqNv;5BNg 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

eVrpT7< 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 R{~!KuPb$dCEB%*y{tpgKeW?Hd literal 0 HcmV?d00001 diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 33234e596d..ed64cb4d31 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -55,6 +55,7 @@ #FFDD7792 #ff0400 + #FF8C00 #ff0400 #ff5e55 #ff827c From 148f4d43f9b70ff30264371c02402fa987fe4e73 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 12 Jun 2017 09:15:51 +0200 Subject: [PATCH 005/212] detect faking by extended boluses in nsclient mode --- .../java/info/nightscout/androidaps/db/DatabaseHelper.java | 3 +++ .../androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java | 2 -- .../androidaps/plugins/PumpVirtual/VirtualPumpPlugin.java | 4 +++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java index cd216ef8e0..849f259443 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java +++ b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java @@ -44,6 +44,7 @@ import info.nightscout.androidaps.events.EventTempTargetChange; import info.nightscout.androidaps.events.EventTreatmentChange; import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventNewHistoryData; import info.nightscout.androidaps.plugins.PumpDanaR.History.DanaRNSHistorySync; +import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin; public class DatabaseHelper extends OrmLiteSqliteOpenHelper { private static Logger log = LoggerFactory.getLogger(DatabaseHelper.class); @@ -1068,6 +1069,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { extendedBolus.durationInMinutes = trJson.getInt("duration"); extendedBolus.insulin = trJson.getDouble("originalExtendedAmount"); extendedBolus._id = trJson.getString("_id"); + VirtualPumpPlugin.fromNSAreCommingFakedExtendedBoluses = true; createOrUpdate(extendedBolus); } else if (trJson.has("isFakedTempBasal")) { // extended bolus end uploaded as temp basal end ExtendedBolus extendedBolus = new ExtendedBolus(); @@ -1077,6 +1079,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { extendedBolus.durationInMinutes = 0; extendedBolus.insulin = 0; extendedBolus._id = trJson.getString("_id"); + VirtualPumpPlugin.fromNSAreCommingFakedExtendedBoluses = true; createOrUpdate(extendedBolus); } else { TemporaryBasal tempBasal = new TemporaryBasal(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java index b476577d06..5ebad71678 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java @@ -684,8 +684,6 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain @Override public boolean isFakingTempsByExtendedBoluses() { - if (Config.NSCLIENT) - return false; return activePump.isFakingTempsByExtendedBoluses(); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpVirtual/VirtualPumpPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpVirtual/VirtualPumpPlugin.java index 39dfcf7873..b89e050fe5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpVirtual/VirtualPumpPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpVirtual/VirtualPumpPlugin.java @@ -45,6 +45,8 @@ public class VirtualPumpPlugin implements PluginBase, PumpInterface { boolean fragmentEnabled = true; boolean fragmentVisible = true; + public static boolean fromNSAreCommingFakedExtendedBoluses = false; + PumpDescription pumpDescription = new PumpDescription(); static VirtualPumpPlugin instance = null; @@ -143,7 +145,7 @@ public class VirtualPumpPlugin implements PluginBase, PumpInterface { @Override public boolean isFakingTempsByExtendedBoluses() { - return false; + return Config.NSCLIENT && fromNSAreCommingFakedExtendedBoluses; } @Override From b41a942d11bbff8ae727dc4756e63475a2cc30f5 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 12 Jun 2017 09:30:04 +0200 Subject: [PATCH 006/212] update supported NS & NSclient versions --- .../info/nightscout/androidaps/Config.java | 2 ++ .../androidaps/Services/DataService.java | 17 +++++++++++--- .../plugins/Overview/Notification.java | 23 ++++++++++--------- app/src/main/res/values/strings.xml | 1 + 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/Config.java b/app/src/main/java/info/nightscout/androidaps/Config.java index 31031333d3..5b3a489129 100644 --- a/app/src/main/java/info/nightscout/androidaps/Config.java +++ b/app/src/main/java/info/nightscout/androidaps/Config.java @@ -4,6 +4,8 @@ package info.nightscout.androidaps; * Created by mike on 07.06.2016. */ public class Config { + public static int SUPPORTEDNSVERSION = 1000; // 0.10.00 + // MAIN FUCTIONALITY public static final boolean APS = BuildConfig.APS; // PLUGINS diff --git a/app/src/main/java/info/nightscout/androidaps/Services/DataService.java b/app/src/main/java/info/nightscout/androidaps/Services/DataService.java index 1f968b7218..7cb5dad0e0 100644 --- a/app/src/main/java/info/nightscout/androidaps/Services/DataService.java +++ b/app/src/main/java/info/nightscout/androidaps/Services/DataService.java @@ -2,6 +2,7 @@ package info.nightscout.androidaps.Services; import android.app.IntentService; import android.content.Intent; +import android.content.pm.PackageManager; import android.os.Bundle; import android.provider.Telephony; @@ -237,11 +238,21 @@ public class DataService extends IntentService { ConfigBuilderPlugin.nsClientVersionCode = bundles.getInt("nsclientversioncode"); // for ver 1.17 contains 117 ConfigBuilderPlugin.nsClientVersionName = bundles.getString("nsclientversionname"); log.debug("Got versions: NSClient: " + ConfigBuilderPlugin.nsClientVersionName + " Nightscout: " + ConfigBuilderPlugin.nightscoutVersionName); - if (ConfigBuilderPlugin.nsClientVersionCode < 121) { - Notification notification = new Notification(Notification.OLD_NSCLIENT, MainApp.sResources.getString(R.string.unsupportedclientver), Notification.URGENT); + try { + if (ConfigBuilderPlugin.nsClientVersionCode < MainApp.instance().getPackageManager().getPackageInfo(MainApp.instance().getPackageName(), 0).versionCode) { + Notification notification = new Notification(Notification.OLD_NSCLIENT, MainApp.sResources.getString(R.string.unsupportedclientver), Notification.URGENT); + MainApp.bus().post(new EventNewNotification(notification)); + } else { + MainApp.bus().post(new EventDismissNotification(Notification.OLD_NSCLIENT)); + } + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + if (ConfigBuilderPlugin.nightscoutVersionCode < Config.SUPPORTEDNSVERSION) { + Notification notification = new Notification(Notification.OLD_NS, MainApp.sResources.getString(R.string.unsupportednsversion), Notification.URGENT); MainApp.bus().post(new EventNewNotification(notification)); } else { - MainApp.bus().post(new EventDismissNotification(Notification.OLD_NSCLIENT)); + MainApp.bus().post(new EventDismissNotification(Notification.OLD_NS)); } } else { Notification notification = new Notification(Notification.OLD_NSCLIENT, MainApp.sResources.getString(R.string.unsupportedclientver), Notification.URGENT); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Notification.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Notification.java index 4b190f96bf..a9ab31e6c2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Notification.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Notification.java @@ -24,17 +24,18 @@ public class Notification { public static final int FAILED_UDPATE_PROFILE = 6; public static final int BASAL_VALUE_BELOW_MINIMUM = 7; public static final int OLD_NSCLIENT = 8; - public static final int INVALID_PHONE_NUMBER = 9; - public static final int APPROACHING_DAILY_LIMIT = 10; - public static final int NSCLIENT_NO_WRITE_PERMISSION = 11; - public static final int MISSING_SMS_PERMISSION = 12; - public static final int ISF_MISSING = 13; - public static final int IC_MISSING = 14; - public static final int BASAL_MISSING = 15; - public static final int TARGET_MISSING = 16; - public static final int NSANNOUNCEMENT = 17; - public static final int NSALARM = 18; - public static final int NSURGENTALARM = 18; + public static final int OLD_NS = 9; + public static final int INVALID_PHONE_NUMBER = 10; + public static final int APPROACHING_DAILY_LIMIT = 11; + public static final int NSCLIENT_NO_WRITE_PERMISSION = 12; + public static final int MISSING_SMS_PERMISSION = 13; + public static final int ISF_MISSING = 14; + public static final int IC_MISSING = 15; + public static final int BASAL_MISSING = 16; + public static final int TARGET_MISSING = 17; + public static final int NSANNOUNCEMENT = 18; + public static final int NSALARM = 19; + public static final int NSURGENTALARM = 20; public int id; public Date date; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9440bdf93f..f6631c14ea 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -142,6 +142,7 @@ New suggestion available Unsupported version of NSClient + Unsupported version of Nightscout NSClient not installed. Record lost! BG available in NS Pump status available in NS From 0f40670c249dd7dc46bc954828bbfc2c86067c20 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 12 Jun 2017 11:15:03 +0200 Subject: [PATCH 007/212] create singleton from NSstatus --- .../nightscout/androidaps/MainActivity.java | 2 +- .../androidaps/Services/DataService.java | 6 +- .../NSClientInternal/data/NSStatus.java | 195 +++++++++++++++--- .../services/NSClientService.java | 8 +- 4 files changed, 171 insertions(+), 40 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/MainActivity.java b/app/src/main/java/info/nightscout/androidaps/MainActivity.java index 98b4294704..6b3c69f4e3 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/MainActivity.java @@ -166,7 +166,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe if (!pm.isIgnoringBatteryOptimizations(packageName)) { log.debug("Requesting ignore battery optimization"); - OKDialog.show(getParent(), getString(R.string.pleaseallowpermission), String.format(getString(R.string.needwhitelisting), getString(R.string.app_name)), new Runnable() { + OKDialog.show(this, getString(R.string.pleaseallowpermission), String.format(getString(R.string.needwhitelisting), getString(R.string.app_name)), new Runnable() { @Override public void run() { diff --git a/app/src/main/java/info/nightscout/androidaps/Services/DataService.java b/app/src/main/java/info/nightscout/androidaps/Services/DataService.java index 7cb5dad0e0..355eb4545d 100644 --- a/app/src/main/java/info/nightscout/androidaps/Services/DataService.java +++ b/app/src/main/java/info/nightscout/androidaps/Services/DataService.java @@ -26,6 +26,7 @@ import info.nightscout.androidaps.plugins.ConstraintsObjectives.ObjectivesPlugin import info.nightscout.androidaps.plugins.NSClientInternal.data.NSMbg; import info.nightscout.androidaps.plugins.NSClientInternal.data.NSSgv; import info.nightscout.androidaps.data.ProfileStore; +import info.nightscout.androidaps.plugins.NSClientInternal.data.NSStatus; import info.nightscout.androidaps.plugins.Overview.Notification; import info.nightscout.androidaps.plugins.Overview.OverviewPlugin; import info.nightscout.androidaps.plugins.Overview.events.EventDismissNotification; @@ -230,8 +231,6 @@ public class DataService extends IntentService { if (intent.getAction().equals(Intents.ACTION_NEW_STATUS)) { - if (Config.logIncommingData) - log.debug("Received status: " + bundles); if (bundles.containsKey("nsclientversioncode")) { ConfigBuilderPlugin.nightscoutVersionCode = bundles.getInt("nightscoutversioncode"); // for ver 1.2.3 contains 10203 ConfigBuilderPlugin.nightscoutVersionName = bundles.getString("nightscoutversionname"); @@ -261,6 +260,9 @@ public class DataService extends IntentService { if (bundles.containsKey("status")) { try { JSONObject statusJson = new JSONObject(bundles.getString("status")); + NSStatus.getInstance().setData(statusJson); + if (Config.logIncommingData) + log.debug("Received status: " + statusJson.toString()); if (statusJson.has("settings")) { JSONObject settings = statusJson.getJSONObject("settings"); if (settings.has("thresholds")) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/data/NSStatus.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/data/NSStatus.java index fe20e90b1d..3261e32318 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/data/NSStatus.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/data/NSStatus.java @@ -5,40 +5,157 @@ import org.json.JSONObject; import java.util.Date; -/** +/* { - status: 'ok' - , name: env.name - , version: env.version - , versionNum: versionNum (for ver 1.2.3 contains 10203) - , serverTime: new Date().toISOString() - , apiEnabled: apiEnabled - , careportalEnabled: apiEnabled && env.settings.enable.indexOf('careportal') > -1 - , boluscalcEnabled: apiEnabled && env.settings.enable.indexOf('boluscalc') > -1 - , head: env.head - , settings: env.settings - , extendedSettings: ctx.plugins && ctx.plugins.extendedClientSettings ? ctx.plugins.extendedClientSettings(env.extendedSettings) : {} - , activeProfile ..... calculated from treatments or missing + "status": "ok", + "name": "Nightscout", + "version": "0.10.0-dev-20170423", + "versionNum": 1000, + "serverTime": "2017-06-12T07:46:56.006Z", + "apiEnabled": true, + "careportalEnabled": true, + "boluscalcEnabled": true, + "head": "96ee154", + "settings": { + "units": "mmol", + "timeFormat": 24, + "nightMode": false, + "editMode": true, + "showRawbg": "always", + "customTitle": "Bara's CGM", + "theme": "colors", + "alarmUrgentHigh": true, + "alarmUrgentHighMins": [30, 60, 90, 120], + "alarmHigh": true, + "alarmHighMins": [30, 60, 90, 120], + "alarmLow": true, + "alarmLowMins": [15, 30, 45, 60], + "alarmUrgentLow": true, + "alarmUrgentLowMins": [15, 30, 45], + "alarmUrgentMins": [30, 60, 90, 120], + "alarmWarnMins": [30, 60, 90, 120], + "alarmTimeagoWarn": true, + "alarmTimeagoWarnMins": 15, + "alarmTimeagoUrgent": true, + "alarmTimeagoUrgentMins": 30, + "language": "cs", + "scaleY": "linear", + "showPlugins": "careportal boluscalc food bwp cage sage iage iob cob basal ar2 delta direction upbat rawbg", + "showForecast": "ar2", + "focusHours": 3, + "heartbeat": 60, + "baseURL": "http:\/\/barascgm.sysop.cz:82", + "authDefaultRoles": "readable", + "thresholds": { + "bgHigh": 252, + "bgTargetTop": 180, + "bgTargetBottom": 72, + "bgLow": 71 + }, + "DEFAULT_FEATURES": ["bgnow", "delta", "direction", "timeago", "devicestatus", "upbat", "errorcodes", "profile"], + "alarmTypes": ["predict"], + "enable": ["careportal", "boluscalc", "food", "bwp", "cage", "sage", "iage", "iob", "cob", "basal", "ar2", "rawbg", "pushover", "bgi", "pump", "openaps", "pushover", "treatmentnotify", "bgnow", "delta", "direction", "timeago", "devicestatus", "upbat", "profile", "ar2"] + }, + "extendedSettings": { + "pump": { + "fields": "reservoir battery clock", + "urgentBattP": 26, + "warnBattP": 51 + }, + "openaps": { + "enableAlerts": true + }, + "cage": { + "alerts": true, + "display": "days", + "urgent": 96, + "warn": 72 + }, + "sage": { + "alerts": true, + "urgent": 336, + "warn": 168 + }, + "iage": { + "alerts": true, + "urgent": 150, + "warn": 120 + }, + "basal": { + "render": "default" + }, + "profile": { + "history": true, + "multiple": true + }, + "devicestatus": { + "advanced": true + } + }, + "activeProfile": "2016 +30%" } */ public class NSStatus { - private JSONObject data; + private static NSStatus instance = null; - public NSStatus(JSONObject obj) { - this.data = obj; + public static NSStatus getInstance() { + if (instance == null) + instance = new NSStatus(); + return instance; } - public String getName () { return getStringOrNull("name"); } - public String getVersion () { return getStringOrNull("version"); } - public Integer getVersionNum () { return getIntegerOrNull("versionNum"); } - public Date getServerTime () { return getDateOrNull("versionNum"); } - public boolean getApiEnabled () { return getBooleanOrNull("apiEnabled"); } - public boolean getCareportalEnabled () { return getBooleanOrNull("careportalEnabled"); } - public boolean getBoluscalcEnabled () { return getBooleanOrNull("boluscalcEnabled"); } - public String getHead () { return getStringOrNull("head"); } - public String getSettings () { return getStringOrNull("settings"); } - public String getExtendedSettings () { return getStringOrNull("extendedSettings"); } - public String getActiveProfile () { return getStringOrNull("activeProfile"); } + private JSONObject data; + + public NSStatus() {} + + public NSStatus setData(JSONObject obj) { + this.data = obj; + return this; + } + + public String getName() { + return getStringOrNull("name"); + } + + public String getVersion() { + return getStringOrNull("version"); + } + + public Integer getVersionNum() { + return getIntegerOrNull("versionNum"); + } + + public Date getServerTime() { + return getDateOrNull("versionNum"); + } + + public boolean getApiEnabled() { + return getBooleanOrNull("apiEnabled"); + } + + public boolean getCareportalEnabled() { + return getBooleanOrNull("careportalEnabled"); + } + + public boolean getBoluscalcEnabled() { + return getBooleanOrNull("boluscalcEnabled"); + } + + public String getHead() { + return getStringOrNull("head"); + } + + public String getSettings() { + return getStringOrNull("settings"); + } + + public String getExtendedSettings() { + return getStringOrNull("extendedSettings"); + } + + public String getActiveProfile() { + return getStringOrNull("activeProfile"); + } private String getStringOrNull(String key) { String ret = null; @@ -50,7 +167,9 @@ public class NSStatus { } } return ret; - }; + } + + ; private Integer getIntegerOrNull(String key) { Integer ret = null; @@ -62,7 +181,9 @@ public class NSStatus { } } return ret; - }; + } + + ; private Long getLongOrNull(String key) { Long ret = null; @@ -74,7 +195,9 @@ public class NSStatus { } } return ret; - }; + } + + ; private Date getDateOrNull(String key) { Date ret = null; @@ -86,7 +209,9 @@ public class NSStatus { } } return ret; - }; + } + + ; private boolean getBooleanOrNull(String key) { boolean ret = false; @@ -98,8 +223,12 @@ public class NSStatus { } } return ret; - }; + } - public JSONObject getData () { return data; } + ; + + public JSONObject getData() { + return data; + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/services/NSClientService.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/services/NSClientService.java index 8158a37fc4..c4316f0118 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/services/NSClientService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/services/NSClientService.java @@ -430,15 +430,15 @@ public class NSClientService extends Service { if (data.has("status")) { JSONObject status = data.getJSONObject("status"); - NSStatus nsStatus = new NSStatus(status); + NSStatus nsStatus = NSStatus.getInstance().setData(status); if (!status.has("versionNum")) { - if (status.getInt("versionNum") < 900) { + if (status.getInt("versionNum") < Config.SUPPORTEDNSVERSION) { MainApp.bus().post(new EventNSClientNewLog("ERROR", "Unsupported Nightscout version !!!!")); } } else { - nightscoutVersionName = status.getString("version"); - nightscoutVersionCode = status.getInt("versionNum"); + nightscoutVersionName = nsStatus.getVersion(); + nightscoutVersionCode = nsStatus.getVersionNum(); } BroadcastStatus.handleNewStatus(nsStatus, MainApp.instance().getApplicationContext(), isDelta); From 6fca923275cfc7310852ecc02cb3022aecf3e41c Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 12 Jun 2017 13:11:16 +0200 Subject: [PATCH 008/212] fix profileswitch reloads --- .../info/nightscout/androidaps/db/DatabaseHelper.java | 10 ++++++---- .../info/nightscout/androidaps/db/ProfileSwitch.java | 2 +- .../events/EventReloadProfileSwitchData.java | 8 ++++++++ .../Careportal/Dialogs/NewNSTreatmentDialog.java | 8 ++++---- .../androidaps/plugins/Overview/OverviewFragment.java | 2 +- .../plugins/Treatments/TreatmentsPlugin.java | 5 ++--- 6 files changed, 22 insertions(+), 13 deletions(-) create mode 100644 app/src/main/java/info/nightscout/androidaps/events/EventReloadProfileSwitchData.java diff --git a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java index 849f259443..b3972f623f 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java +++ b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java @@ -37,6 +37,7 @@ import info.nightscout.androidaps.events.EventExtendedBolusChange; import info.nightscout.androidaps.events.EventNewBG; import info.nightscout.androidaps.events.EventProfileSwitchChange; import info.nightscout.androidaps.events.EventRefreshGui; +import info.nightscout.androidaps.events.EventReloadProfileSwitchData; import info.nightscout.androidaps.events.EventReloadTempBasalData; import info.nightscout.androidaps.events.EventReloadTreatmentData; import info.nightscout.androidaps.events.EventTempBasalChange; @@ -1507,7 +1508,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { old.copyFrom(profileSwitch); getDaoProfileSwitch().create(old); log.debug("PROFILESWITCH: Updating record by date from: " + Source.getString(profileSwitch.source) + " " + old.toString()); - scheduleTemporaryTargetChange(); + scheduleProfileSwitchChange(); return true; } return false; @@ -1526,20 +1527,20 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { old.copyFrom(profileSwitch); getDaoProfileSwitch().create(old); log.debug("PROFILESWITCH: Updating record by _id from: " + Source.getString(profileSwitch.source) + " " + old.toString()); - scheduleTemporaryTargetChange(); + scheduleProfileSwitchChange(); return true; } } } getDaoProfileSwitch().create(profileSwitch); log.debug("PROFILESWITCH: New record from: " + Source.getString(profileSwitch.source) + " " + profileSwitch.toString()); - scheduleTemporaryTargetChange(); + scheduleProfileSwitchChange(); return true; } if (profileSwitch.source == Source.USER) { getDaoProfileSwitch().create(profileSwitch); log.debug("PROFILESWITCH: New record from: " + Source.getString(profileSwitch.source) + " " + profileSwitch.toString()); - scheduleTemporaryTargetChange(); + scheduleProfileSwitchChange(); return true; } } catch (SQLException e) { @@ -1561,6 +1562,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { class PostRunnable implements Runnable { public void run() { log.debug("Firing EventProfileSwitchChange"); + MainApp.bus().post(new EventReloadProfileSwitchData()); MainApp.bus().post(new EventProfileSwitchChange()); scheduledProfileSwitchEventPost = null; } diff --git a/app/src/main/java/info/nightscout/androidaps/db/ProfileSwitch.java b/app/src/main/java/info/nightscout/androidaps/db/ProfileSwitch.java index 448f6cdd51..25ba1cb051 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/ProfileSwitch.java +++ b/app/src/main/java/info/nightscout/androidaps/db/ProfileSwitch.java @@ -187,7 +187,7 @@ public class ProfileSwitch implements Interval, DataPointWithLabelInterface { return Color.CYAN; } - public String log() { + public String toString() { return "ProfileSwitch{" + "date=" + date + "date=" + DateUtil.dateAndTimeString(date) + diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventReloadProfileSwitchData.java b/app/src/main/java/info/nightscout/androidaps/events/EventReloadProfileSwitchData.java new file mode 100644 index 0000000000..20073a7cf0 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventReloadProfileSwitchData.java @@ -0,0 +1,8 @@ +package info.nightscout.androidaps.events; + +/** + * Created by mike on 12.06.2017. + */ + +public class EventReloadProfileSwitchData { +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Careportal/Dialogs/NewNSTreatmentDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/Careportal/Dialogs/NewNSTreatmentDialog.java index f1e5f09ebd..dad922a5c5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Careportal/Dialogs/NewNSTreatmentDialog.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Careportal/Dialogs/NewNSTreatmentDialog.java @@ -619,13 +619,13 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick String profileName = data.getString("profile"); ProfileSwitch profileSwitch = new ProfileSwitch(); profileSwitch.date = new Date().getTime(); - profileSwitch.source = Source.PUMP; + profileSwitch.source = Source.USER; profileSwitch.profileName = profileName; - profileSwitch.profileJson = profileStore.getSpecificProfile(profileName).toString(); - profileSwitch.profilePlugin = MainApp.getConfigBuilder().getActiveProfileInterface().getClass().getName(); + profileSwitch.profileJson = profileStore.getSpecificProfile(profileName).getData().toString(); + profileSwitch.profilePlugin = ConfigBuilderPlugin.getActiveProfileInterface().getClass().getName(); profileSwitch.durationInMinutes = data.getInt("duration"); if (ConfigBuilderPlugin.getActiveProfileInterface() instanceof CircadianPercentageProfilePlugin) { - CircadianPercentageProfilePlugin cpp = (CircadianPercentageProfilePlugin) MainApp.getConfigBuilder().getActiveProfileInterface(); + CircadianPercentageProfilePlugin cpp = (CircadianPercentageProfilePlugin) ConfigBuilderPlugin.getActiveProfileInterface(); profileSwitch.isCPP = true; profileSwitch.timeshift = cpp.timeshift; profileSwitch.percentage = cpp.percentage; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java index 7d9c84a33d..7ebdcc60f2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java @@ -1018,7 +1018,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, ExtendedBolus extendedBolus = MainApp.getConfigBuilder().getExtendedBolusFromHistory(new Date().getTime()); String extendedBolusText = ""; - if (extendedBolus != null) { + if (extendedBolus != null && !pump.isFakingTempsByExtendedBoluses()) { extendedBolusText = extendedBolus.toString(); } if (extendedBolusView != null) // must not exists in all layouts diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java index 50bb0c7892..d211a0eca4 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java @@ -24,11 +24,10 @@ import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.ProfileIntervals; import info.nightscout.androidaps.db.ExtendedBolus; import info.nightscout.androidaps.db.ProfileSwitch; -import info.nightscout.androidaps.db.Source; import info.nightscout.androidaps.db.TempTarget; import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.db.Treatment; -import info.nightscout.androidaps.events.EventProfileSwitchChange; +import info.nightscout.androidaps.events.EventReloadProfileSwitchData; import info.nightscout.androidaps.events.EventReloadTempBasalData; import info.nightscout.androidaps.events.EventReloadTreatmentData; import info.nightscout.androidaps.events.EventTempTargetChange; @@ -445,7 +444,7 @@ public class TreatmentsPlugin implements PluginBase, TreatmentsInterface { // Profile Switch @Subscribe - public void onStatusEvent(final EventProfileSwitchChange ev) { + public void onStatusEvent(final EventReloadProfileSwitchData ev) { initializeProfileSwitchData(); } From 1de64f06c21808a0529a560e459b7c091de69b76 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 12 Jun 2017 23:31:50 +0200 Subject: [PATCH 009/212] simplify danar activities, fix profiles --- app/src/main/AndroidManifest.xml | 7 +- .../androidaps/Services/DataService.java | 3 +- .../androidaps/db/DatabaseHelper.java | 2 +- .../androidaps/interfaces/DanaRInterface.java | 9 + .../ConfigBuilder/ConfigBuilderPlugin.java | 4 + .../CircadianPercentageProfilePlugin.java | 25 +- .../ProfileLocal/LocalProfilePlugin.java | 2 +- .../ProfileSimple/SimpleProfilePlugin.java | 10 +- .../plugins/PumpDanaR/DanaRFragment.java | 4 +- .../plugins/PumpDanaR/DanaRPlugin.java | 12 +- .../PumpDanaR/Dialogs/ProfileViewDialog.java | 5 +- .../DanaRHistoryActivity.java | 54 +- .../DanaRNSHistorySync.java | 2 +- .../DanaRStatsActivity.java | 42 +- .../PumpDanaRKorean/DanaRKoreanFragment.java | 4 +- .../PumpDanaRKorean/DanaRKoreanPlugin.java | 12 +- .../History/DanaRHistoryActivity.java | 430 -------------- .../History/DanaRStatsActivity.java | 539 ----------------- .../plugins/PumpDanaRv2/DanaRv2Fragment.java | 4 +- .../plugins/PumpDanaRv2/DanaRv2Plugin.java | 13 +- .../History/DanaRHistoryActivity.java | 431 -------------- .../History/DanaRStatsActivity.java | 546 ------------------ .../info/nightscout/utils/TimeListEdit.java | 8 + .../main/res/layout/danar_historyactivity.xml | 2 +- .../main/res/layout/danar_statsactivity.xml | 2 +- .../res/layout/nsprofileviewer_fragment.xml | 14 +- 26 files changed, 117 insertions(+), 2069 deletions(-) create mode 100644 app/src/main/java/info/nightscout/androidaps/interfaces/DanaRInterface.java rename app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/{History => activities}/DanaRHistoryActivity.java (91%) rename app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/{History => activities}/DanaRNSHistorySync.java (99%) rename app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/{History => activities}/DanaRStatsActivity.java (94%) delete mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/History/DanaRHistoryActivity.java delete mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/History/DanaRStatsActivity.java delete mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/History/DanaRHistoryActivity.java delete mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/History/DanaRStatsActivity.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 72a2fb614f..74271acb90 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -42,14 +42,11 @@ android:name=".plugins.Overview.Dialogs.BolusProgressHelperActivity" android:theme="@style/Theme.AppCompat.Translucent" /> - - - - + + - diff --git a/app/src/main/java/info/nightscout/androidaps/Services/DataService.java b/app/src/main/java/info/nightscout/androidaps/Services/DataService.java index 355eb4545d..c9c54d11c7 100644 --- a/app/src/main/java/info/nightscout/androidaps/Services/DataService.java +++ b/app/src/main/java/info/nightscout/androidaps/Services/DataService.java @@ -15,7 +15,6 @@ import org.slf4j.LoggerFactory; import java.util.Date; import info.nightscout.androidaps.Config; -import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.db.BgReading; @@ -32,7 +31,7 @@ import info.nightscout.androidaps.plugins.Overview.OverviewPlugin; import info.nightscout.androidaps.plugins.Overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification; import info.nightscout.androidaps.plugins.ProfileNS.NSProfilePlugin; -import info.nightscout.androidaps.plugins.PumpDanaR.History.DanaRNSHistorySync; +import info.nightscout.androidaps.plugins.PumpDanaR.activities.DanaRNSHistorySync; import info.nightscout.androidaps.plugins.SmsCommunicator.events.EventNewSMS; import info.nightscout.androidaps.plugins.SourceGlimp.SourceGlimpPlugin; import info.nightscout.androidaps.plugins.SourceMM640g.SourceMM640gPlugin; diff --git a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java index b3972f623f..832ddbca47 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java +++ b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java @@ -44,7 +44,7 @@ import info.nightscout.androidaps.events.EventTempBasalChange; import info.nightscout.androidaps.events.EventTempTargetChange; import info.nightscout.androidaps.events.EventTreatmentChange; import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventNewHistoryData; -import info.nightscout.androidaps.plugins.PumpDanaR.History.DanaRNSHistorySync; +import info.nightscout.androidaps.plugins.PumpDanaR.activities.DanaRNSHistorySync; import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin; public class DatabaseHelper extends OrmLiteSqliteOpenHelper { diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/DanaRInterface.java b/app/src/main/java/info/nightscout/androidaps/interfaces/DanaRInterface.java new file mode 100644 index 0000000000..abd1d8e304 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/DanaRInterface.java @@ -0,0 +1,9 @@ +package info.nightscout.androidaps.interfaces; + +/** + * Created by mike on 12.06.2017. + */ + +public interface DanaRInterface { + boolean loadHistory(byte type); +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java index 5ebad71678..992fcc0244 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java @@ -208,6 +208,10 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain return activeLoop; } + public static PumpInterface getActivePump() { + return activePump; + } + void logPluginStatus() { for (PluginBase p : pluginList) { log.debug(p.getName() + ":" + diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileCircadianPercentage/CircadianPercentageProfilePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileCircadianPercentage/CircadianPercentageProfilePlugin.java index 0fe6c30c78..40fbbf9eb3 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileCircadianPercentage/CircadianPercentageProfilePlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileCircadianPercentage/CircadianPercentageProfilePlugin.java @@ -9,6 +9,8 @@ import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.text.DecimalFormat; + import info.nightscout.androidaps.Config; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; @@ -179,26 +181,23 @@ public class CircadianPercentageProfilePlugin implements PluginBase, ProfileInte int offset = -(timeshift % 24) + 24; JSONArray icArray = new JSONArray(); - for (int i = 0; i < 24; i++) { - icArray.put(new JSONObject().put("timeAsSeconds", i * 60 * 60).put("value", baseic[(offset + i) % 24] * 100d / percentage)); - } - profile.put("carbratio", icArray); - JSONArray isfArray = new JSONArray(); - for (int i = 0; i < 24; i++) { - isfArray.put(new JSONObject().put("timeAsSeconds", i * 60 * 60).put("value", baseisf[(offset + i) % 24] * 100d / percentage)); - } - profile.put("sens", isfArray); - JSONArray basalArray = new JSONArray(); for (int i = 0; i < 24; i++) { - basalArray.put(new JSONObject().put("timeAsSeconds", i * 60 * 60).put("value", basebasal[(offset + i) % 24] * percentage / 100d)); + String time; + DecimalFormat df = new DecimalFormat("00"); + time = df.format(i) + ":00"; + icArray.put(new JSONObject().put("time", time).put("timeAsSeconds", i * 60 * 60).put("value", baseic[(offset + i) % 24] * 100d / percentage)); + isfArray.put(new JSONObject().put("time", time).put("timeAsSeconds", i * 60 * 60).put("value", baseisf[(offset + i) % 24] * 100d / percentage)); + basalArray.put(new JSONObject().put("time", time).put("timeAsSeconds", i * 60 * 60).put("value", basebasal[(offset + i) % 24] * percentage / 100d)); } + profile.put("carbratio", icArray); + profile.put("sens", isfArray); profile.put("basal", basalArray); - profile.put("target_low", new JSONArray().put(new JSONObject().put("timeAsSeconds", 0).put("value", targetLow))); - profile.put("target_high", new JSONArray().put(new JSONObject().put("timeAsSeconds", 0).put("value", targetHigh))); + profile.put("target_low", new JSONArray().put(new JSONObject().put("time", "00:00").put("timeAsSeconds", 0).put("value", targetLow))); + profile.put("target_high", new JSONArray().put(new JSONObject().put("time", "00:00").put("timeAsSeconds", 0).put("value", targetHigh))); profile.put("units", mgdl ? Constants.MGDL : Constants.MMOL); store.put(profileName, profile); } catch (JSONException e) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileLocal/LocalProfilePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileLocal/LocalProfilePlugin.java index d22361981c..d1fab7cbcf 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileLocal/LocalProfilePlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileLocal/LocalProfilePlugin.java @@ -30,7 +30,7 @@ public class LocalProfilePlugin implements PluginBase, ProfileInterface { private static ProfileStore convertedProfile = null; private static String convertedProfileName = null; - final private String DEFAULTARRAY = "[{\"timeAsSeconds\":0,\"value\":0}]"; + final private String DEFAULTARRAY = "[{\"time\":\"00:00\",\"timeAsSeconds\":0,\"value\":0}]"; boolean mgdl; boolean mmol; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileSimple/SimpleProfilePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileSimple/SimpleProfilePlugin.java index 40a53d0707..de7c903df3 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileSimple/SimpleProfilePlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileSimple/SimpleProfilePlugin.java @@ -183,11 +183,11 @@ public class SimpleProfilePlugin implements PluginBase, ProfileInterface { json.put("defaultProfile", "SimpleProfile"); json.put("store", store); profile.put("dia", dia); - profile.put("carbratio", new JSONArray().put(new JSONObject().put("timeAsSeconds", 0).put("value", ic))); - profile.put("sens", new JSONArray().put(new JSONObject().put("timeAsSeconds", 0).put("value", isf))); - profile.put("basal", new JSONArray().put(new JSONObject().put("timeAsSeconds", 0).put("value", basal))); - profile.put("target_low", new JSONArray().put(new JSONObject().put("timeAsSeconds", 0).put("value", targetLow))); - profile.put("target_high", new JSONArray().put(new JSONObject().put("timeAsSeconds", 0).put("value", targetHigh))); + profile.put("carbratio", new JSONArray().put(new JSONObject().put("time", "00:00").put("timeAsSeconds", 0).put("value", ic))); + profile.put("sens", new JSONArray().put(new JSONObject().put("time", "00:00").put("timeAsSeconds", 0).put("value", isf))); + profile.put("basal", new JSONArray().put(new JSONObject().put("time", "00:00").put("timeAsSeconds", 0).put("value", basal))); + profile.put("target_low", new JSONArray().put(new JSONObject().put("time", "00:00").put("timeAsSeconds", 0).put("value", targetLow))); + profile.put("target_high", new JSONArray().put(new JSONObject().put("time", "00:00").put("timeAsSeconds", 0).put("value", targetHigh))); profile.put("units", mgdl ? Constants.MGDL : Constants.MMOL); store.put("SimpleProfile", profile); } catch (JSONException e) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRFragment.java index 20c8843a31..d10c9bf861 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRFragment.java @@ -28,8 +28,8 @@ import info.nightscout.androidaps.events.EventExtendedBolusChange; import info.nightscout.androidaps.events.EventPumpStatusChanged; import info.nightscout.androidaps.events.EventTempBasalChange; import info.nightscout.androidaps.plugins.PumpDanaR.Dialogs.ProfileViewDialog; -import info.nightscout.androidaps.plugins.PumpDanaR.History.DanaRHistoryActivity; -import info.nightscout.androidaps.plugins.PumpDanaR.History.DanaRStatsActivity; +import info.nightscout.androidaps.plugins.PumpDanaR.activities.DanaRHistoryActivity; +import info.nightscout.androidaps.plugins.PumpDanaR.activities.DanaRStatsActivity; import info.nightscout.androidaps.plugins.PumpDanaR.events.EventDanaRNewStatus; import info.nightscout.utils.DateUtil; import info.nightscout.utils.DecimalFormatter; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java index b3cdeb17db..1188028492 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java @@ -30,6 +30,7 @@ import info.nightscout.androidaps.db.Treatment; import info.nightscout.androidaps.events.EventAppExit; import info.nightscout.androidaps.events.EventPreferenceChange; import info.nightscout.androidaps.interfaces.ConstraintsInterface; +import info.nightscout.androidaps.interfaces.DanaRInterface; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.ProfileInterface; import info.nightscout.androidaps.interfaces.PumpDescription; @@ -50,7 +51,7 @@ import info.nightscout.utils.SP; /** * Created by mike on 05.08.2016. */ -public class DanaRPlugin implements PluginBase, PumpInterface, ConstraintsInterface, ProfileInterface { +public class DanaRPlugin implements PluginBase, PumpInterface, DanaRInterface, ConstraintsInterface, ProfileInterface { private static Logger log = LoggerFactory.getLogger(DanaRPlugin.class); @Override @@ -706,6 +707,15 @@ public class DanaRPlugin implements PluginBase, PumpInterface, ConstraintsInterf return pumpDescription; } + /** + * DanaR interface + */ + + @Override + public boolean loadHistory(byte type) { + return sExecutionService.loadHistory(type); + } + /** * Constraint interface */ diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/Dialogs/ProfileViewDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/Dialogs/ProfileViewDialog.java index 9f05a7569a..ec27759b3f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/Dialogs/ProfileViewDialog.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/Dialogs/ProfileViewDialog.java @@ -19,6 +19,7 @@ import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.data.Profile; +import info.nightscout.androidaps.interfaces.ProfileInterface; import info.nightscout.androidaps.plugins.PumpDanaR.DanaRPlugin; import info.nightscout.androidaps.plugins.PumpDanaR.DanaRPump; import info.nightscout.androidaps.plugins.PumpDanaRKorean.DanaRKoreanPlugin; @@ -103,10 +104,10 @@ public class ProfileViewDialog extends DialogFragment { // } else { // noProfile.setVisibility(View.GONE); // } - Profile profile = MainApp.getConfigBuilder().getProfile(); + Profile profile = ((ProfileInterface)MainApp.getConfigBuilder().getActivePump()).getProfile().getDefaultProfile(); units.setText(profile.getUnits()); dia.setText(DecimalFormatter.to2Decimal(profile.getDia()) + " h"); - activeProfile.setText(MainApp.getConfigBuilder().getProfileName()); + activeProfile.setText(((ProfileInterface)MainApp.getConfigBuilder().getActivePump()).getProfileName()); ic.setText(profile.getIcList()); isf.setText(profile.getIsfList()); basal.setText(profile.getBasalList()); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/History/DanaRHistoryActivity.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/activities/DanaRHistoryActivity.java similarity index 91% rename from app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/History/DanaRHistoryActivity.java rename to app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/activities/DanaRHistoryActivity.java index 0116498233..74239807d5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/History/DanaRHistoryActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/activities/DanaRHistoryActivity.java @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.PumpDanaR.History; +package info.nightscout.androidaps.plugins.PumpDanaR.activities; import android.app.Activity; import android.content.ComponentName; @@ -35,9 +35,13 @@ import info.nightscout.androidaps.R; import info.nightscout.androidaps.db.DanaRHistoryRecord; import info.nightscout.androidaps.events.EventPumpStatusChanged; import info.nightscout.androidaps.data.Profile; +import info.nightscout.androidaps.interfaces.DanaRInterface; +import info.nightscout.androidaps.interfaces.PluginBase; +import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.plugins.PumpDanaR.services.DanaRExecutionService; import info.nightscout.androidaps.plugins.PumpDanaR.comm.RecordTypes; import info.nightscout.androidaps.plugins.PumpDanaR.events.EventDanaRSyncStatus; +import info.nightscout.androidaps.plugins.PumpDanaRKorean.DanaRKoreanPlugin; import info.nightscout.utils.DateUtil; import info.nightscout.utils.DecimalFormatter; import info.nightscout.utils.ToastUtils; @@ -46,7 +50,6 @@ public class DanaRHistoryActivity extends Activity { private static Logger log = LoggerFactory.getLogger(DanaRHistoryActivity.class); private boolean mBounded; - private static DanaRExecutionService mExecutionService; private Handler mHandler; private static HandlerThread mHandlerThread; @@ -86,13 +89,6 @@ public class DanaRHistoryActivity extends Activity { } - @Override - public void onStart() { - super.onStart(); - Intent intent = new Intent(this, DanaRExecutionService.class); - bindService(intent, mConnection, Context.BIND_AUTO_CREATE); - } - @Override protected void onResume() { super.onResume(); @@ -105,31 +101,6 @@ public class DanaRHistoryActivity extends Activity { MainApp.bus().unregister(this); } - @Override - public void onStop() { - super.onStop(); - if (mBounded) { - unbindService(mConnection); - mBounded = false; - } - } - - ServiceConnection mConnection = new ServiceConnection() { - - public void onServiceDisconnected(ComponentName name) { - log.debug("Service is disconnected"); - mBounded = false; - mExecutionService = null; - } - - public void onServiceConnected(ComponentName name, IBinder service) { - log.debug("Service is connected"); - mBounded = true; - DanaRExecutionService.LocalBinder mLocalBinder = (DanaRExecutionService.LocalBinder) service; - mExecutionService = mLocalBinder.getServiceInstance(); - } - }; - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -150,6 +121,8 @@ public class DanaRHistoryActivity extends Activity { statusView.setVisibility(View.GONE); + boolean isKorean = MainApp.getSpecificPlugin(DanaRKoreanPlugin.class).isEnabled(PluginBase.PUMP); + // Types ArrayList typeList = new ArrayList<>(); @@ -158,10 +131,12 @@ public class DanaRHistoryActivity extends Activity { typeList.add(new TypeList(RecordTypes.RECORD_TYPE_BOLUS, getString(R.string.danar_history_bolus))); typeList.add(new TypeList(RecordTypes.RECORD_TYPE_CARBO, getString(R.string.danar_history_carbohydrates))); typeList.add(new TypeList(RecordTypes.RECORD_TYPE_DAILY, getString(R.string.danar_history_dailyinsulin))); - typeList.add(new TypeList(RecordTypes.RECORD_TYPE_ERROR, getString(R.string.danar_history_errors))); typeList.add(new TypeList(RecordTypes.RECORD_TYPE_GLUCOSE, getString(R.string.danar_history_glucose))); - typeList.add(new TypeList(RecordTypes.RECORD_TYPE_REFILL, getString(R.string.danar_history_refill))); - typeList.add(new TypeList(RecordTypes.RECORD_TYPE_SUSPEND, getString(R.string.danar_history_syspend))); + if (!isKorean) { + typeList.add(new TypeList(RecordTypes.RECORD_TYPE_ERROR, getString(R.string.danar_history_errors))); + typeList.add(new TypeList(RecordTypes.RECORD_TYPE_REFILL, getString(R.string.danar_history_refill))); + typeList.add(new TypeList(RecordTypes.RECORD_TYPE_SUSPEND, getString(R.string.danar_history_syspend))); + } ArrayAdapter spinnerAdapter = new ArrayAdapter<>(this, R.layout.spinner_centered, typeList); historyTypeSpinner.setAdapter(spinnerAdapter); @@ -169,7 +144,8 @@ public class DanaRHistoryActivity extends Activity { reloadButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - if (mExecutionService.isConnected() || mExecutionService.isConnecting()) { + final PumpInterface pump = MainApp.getConfigBuilder().getActivePump(); + if (pump.isBusy()) { ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), getString(R.string.pumpbusy)); return; } @@ -186,7 +162,7 @@ public class DanaRHistoryActivity extends Activity { } }); clearCardView(); - mExecutionService.loadHistory(selected.type); + ((DanaRInterface)pump).loadHistory(RecordTypes.RECORD_TYPE_DAILY); loadDataFromDB(selected.type); runOnUiThread(new Runnable() { @Override diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/History/DanaRNSHistorySync.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/activities/DanaRNSHistorySync.java similarity index 99% rename from app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/History/DanaRNSHistorySync.java rename to app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/activities/DanaRNSHistorySync.java index f491a40b8f..ef5dc16b43 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/History/DanaRNSHistorySync.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/activities/DanaRNSHistorySync.java @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.PumpDanaR.History; +package info.nightscout.androidaps.plugins.PumpDanaR.activities; import org.json.JSONException; import org.json.JSONObject; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/History/DanaRStatsActivity.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/activities/DanaRStatsActivity.java similarity index 94% rename from app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/History/DanaRStatsActivity.java rename to app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/activities/DanaRStatsActivity.java index 6a360b9c26..808c827ccd 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/History/DanaRStatsActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/activities/DanaRStatsActivity.java @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.PumpDanaR.History; +package info.nightscout.androidaps.plugins.PumpDanaR.activities; import android.app.Activity; import android.content.ComponentName; @@ -42,7 +42,9 @@ import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.db.DanaRHistoryRecord; import info.nightscout.androidaps.events.EventPumpStatusChanged; +import info.nightscout.androidaps.interfaces.DanaRInterface; import info.nightscout.androidaps.interfaces.ProfileInterface; +import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.ProfileCircadianPercentage.CircadianPercentageProfilePlugin; import info.nightscout.androidaps.plugins.PumpDanaR.comm.RecordTypes; @@ -57,7 +59,6 @@ public class DanaRStatsActivity extends Activity { private static Logger log = LoggerFactory.getLogger(DanaRStatsActivity.class); private boolean mBounded; - private static DanaRExecutionService mExecutionService; private Handler mHandler; private static HandlerThread mHandlerThread; @@ -80,13 +81,6 @@ public class DanaRStatsActivity extends Activity { this.mHandler = new Handler(mHandlerThread.getLooper()); } - @Override - public void onStart() { - super.onStart(); - Intent intent = new Intent(this, DanaRExecutionService.class); - bindService(intent, mConnection, Context.BIND_AUTO_CREATE); - } - @Override protected void onResume() { super.onResume(); @@ -99,15 +93,6 @@ public class DanaRStatsActivity extends Activity { MainApp.bus().unregister(this); } - @Override - public void onStop() { - super.onStop(); - if (mBounded) { - unbindService(mConnection); - mBounded = false; - } - } - @Override public boolean dispatchTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { @@ -123,22 +108,6 @@ public class DanaRStatsActivity extends Activity { return super.dispatchTouchEvent(event); } - ServiceConnection mConnection = new ServiceConnection() { - - public void onServiceDisconnected(ComponentName name) { - log.debug("Service is disconnected"); - mBounded = false; - mExecutionService = null; - } - - public void onServiceConnected(ComponentName name, IBinder service) { - log.debug("Service is connected"); - mBounded = true; - DanaRExecutionService.LocalBinder mLocalBinder = (DanaRExecutionService.LocalBinder) service; - mExecutionService = mLocalBinder.getServiceInstance(); - } - }; - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -269,7 +238,8 @@ public class DanaRStatsActivity extends Activity { reloadButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - if (mExecutionService.isConnected() || mExecutionService.isConnecting()) { + final PumpInterface pump = MainApp.getConfigBuilder().getActivePump(); + if (pump.isBusy()) { ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), getString(R.string.pumpbusy)); return; } @@ -285,7 +255,7 @@ public class DanaRStatsActivity extends Activity { statsMessage.setText(getString(R.string.danar_stats_warning_Message)); } }); - mExecutionService.loadHistory(RecordTypes.RECORD_TYPE_DAILY); + ((DanaRInterface)pump).loadHistory(RecordTypes.RECORD_TYPE_DAILY); loadDataFromDB(RecordTypes.RECORD_TYPE_DAILY); runOnUiThread(new Runnable() { @Override diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanFragment.java index 5a3a5a4f36..feec56e093 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanFragment.java @@ -29,9 +29,9 @@ import info.nightscout.androidaps.events.EventPumpStatusChanged; import info.nightscout.androidaps.events.EventTempBasalChange; import info.nightscout.androidaps.plugins.PumpDanaR.DanaRPump; import info.nightscout.androidaps.plugins.PumpDanaR.Dialogs.ProfileViewDialog; +import info.nightscout.androidaps.plugins.PumpDanaR.activities.DanaRHistoryActivity; +import info.nightscout.androidaps.plugins.PumpDanaR.activities.DanaRStatsActivity; import info.nightscout.androidaps.plugins.PumpDanaR.events.EventDanaRNewStatus; -import info.nightscout.androidaps.plugins.PumpDanaRKorean.History.DanaRHistoryActivity; -import info.nightscout.androidaps.plugins.PumpDanaRKorean.History.DanaRStatsActivity; import info.nightscout.utils.DateUtil; import info.nightscout.utils.DecimalFormatter; import info.nightscout.utils.SetWarnColor; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java index 3e5d9c2c48..6d72fe9684 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java @@ -30,6 +30,7 @@ import info.nightscout.androidaps.db.Treatment; import info.nightscout.androidaps.events.EventAppExit; import info.nightscout.androidaps.events.EventPreferenceChange; import info.nightscout.androidaps.interfaces.ConstraintsInterface; +import info.nightscout.androidaps.interfaces.DanaRInterface; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.ProfileInterface; import info.nightscout.androidaps.interfaces.PumpDescription; @@ -51,7 +52,7 @@ import info.nightscout.utils.SP; /** * Created by mike on 05.08.2016. */ -public class DanaRKoreanPlugin implements PluginBase, PumpInterface, ConstraintsInterface, ProfileInterface { +public class DanaRKoreanPlugin implements PluginBase, PumpInterface, DanaRInterface, ConstraintsInterface, ProfileInterface { private static Logger log = LoggerFactory.getLogger(DanaRKoreanPlugin.class); @Override @@ -710,6 +711,15 @@ public class DanaRKoreanPlugin implements PluginBase, PumpInterface, Constraints return pumpDescription; } + /** + * DanaR interface + */ + + @Override + public boolean loadHistory(byte type) { + return sExecutionService.loadHistory(type); + } + /** * Constraint interface */ diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/History/DanaRHistoryActivity.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/History/DanaRHistoryActivity.java deleted file mode 100644 index d9802c016c..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/History/DanaRHistoryActivity.java +++ /dev/null @@ -1,430 +0,0 @@ -package info.nightscout.androidaps.plugins.PumpDanaRKorean.History; - -import android.app.Activity; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.os.Bundle; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; -import android.support.v7.widget.CardView; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.Spinner; -import android.widget.TextView; - -import com.squareup.otto.Subscribe; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.List; - -import info.nightscout.androidaps.Constants; -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.db.DanaRHistoryRecord; -import info.nightscout.androidaps.events.EventPumpStatusChanged; -import info.nightscout.androidaps.data.Profile; -import info.nightscout.androidaps.plugins.PumpDanaR.History.DanaRNSHistorySync; -import info.nightscout.androidaps.plugins.PumpDanaR.comm.RecordTypes; -import info.nightscout.androidaps.plugins.PumpDanaR.events.EventDanaRSyncStatus; -import info.nightscout.androidaps.plugins.PumpDanaRKorean.services.DanaRKoreanExecutionService; -import info.nightscout.utils.DateUtil; -import info.nightscout.utils.DecimalFormatter; -import info.nightscout.utils.ToastUtils; - -public class DanaRHistoryActivity extends Activity { - private static Logger log = LoggerFactory.getLogger(DanaRHistoryActivity.class); - - private boolean mBounded; - private static DanaRKoreanExecutionService mExecutionService; - - private Handler mHandler; - private static HandlerThread mHandlerThread; - - static Profile profile = null; - - Spinner historyTypeSpinner; - TextView statusView; - Button reloadButton; - Button syncButton; - RecyclerView recyclerView; - LinearLayoutManager llm; - - static byte showingType = RecordTypes.RECORD_TYPE_ALARM; - List historyList = new ArrayList<>(); - - public static class TypeList { - public byte type; - String name; - - public TypeList(byte type, String name) { - this.type = type; - this.name = name; - } - - @Override - public String toString() { - return name; - } - } - - public DanaRHistoryActivity() { - super(); - mHandlerThread = new HandlerThread(DanaRHistoryActivity.class.getSimpleName()); - mHandlerThread.start(); - this.mHandler = new Handler(mHandlerThread.getLooper()); - } - - - @Override - public void onStart() { - super.onStart(); - Intent intent = new Intent(this, DanaRKoreanExecutionService.class); - bindService(intent, mConnection, Context.BIND_AUTO_CREATE); - } - - @Override - protected void onResume() { - super.onResume(); - MainApp.bus().register(this); - } - - @Override - protected void onPause() { - super.onPause(); - MainApp.bus().unregister(this); - } - - @Override - public void onStop() { - super.onStop(); - if (mBounded) { - unbindService(mConnection); - mBounded = false; - } - } - - ServiceConnection mConnection = new ServiceConnection() { - - public void onServiceDisconnected(ComponentName name) { - log.debug("Service is disconnected"); - mBounded = false; - mExecutionService = null; - } - - public void onServiceConnected(ComponentName name, IBinder service) { - log.debug("Service is connected"); - mBounded = true; - DanaRKoreanExecutionService.LocalBinder mLocalBinder = (DanaRKoreanExecutionService.LocalBinder) service; - mExecutionService = mLocalBinder.getServiceInstance(); - } - }; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.danar_historyactivity); - - historyTypeSpinner = (Spinner) findViewById(R.id.danar_historytype); - statusView = (TextView) findViewById(R.id.danar_historystatus); - reloadButton = (Button) findViewById(R.id.danar_historyreload); - syncButton = (Button) findViewById(R.id.danar_historysync); - recyclerView = (RecyclerView) findViewById(R.id.danar_history_recyclerview); - - recyclerView.setHasFixedSize(true); - llm = new LinearLayoutManager(this); - recyclerView.setLayoutManager(llm); - - RecyclerViewAdapter adapter = new RecyclerViewAdapter(historyList); - recyclerView.setAdapter(adapter); - - statusView.setVisibility(View.GONE); - - // Types - - ArrayList typeList = new ArrayList<>(); - typeList.add(new TypeList(RecordTypes.RECORD_TYPE_ALARM, getString(R.string.danar_history_alarm))); - typeList.add(new TypeList(RecordTypes.RECORD_TYPE_BASALHOUR, getString(R.string.danar_history_basalhours))); - typeList.add(new TypeList(RecordTypes.RECORD_TYPE_BOLUS, getString(R.string.danar_history_bolus))); - typeList.add(new TypeList(RecordTypes.RECORD_TYPE_CARBO, getString(R.string.danar_history_carbohydrates))); - typeList.add(new TypeList(RecordTypes.RECORD_TYPE_DAILY, getString(R.string.danar_history_dailyinsulin))); - typeList.add(new TypeList(RecordTypes.RECORD_TYPE_GLUCOSE, getString(R.string.danar_history_glucose))); - - ArrayAdapter spinnerAdapter = new ArrayAdapter<>(this, - R.layout.spinner_centered, typeList); - historyTypeSpinner.setAdapter(spinnerAdapter); - - reloadButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (mExecutionService.isConnected() || mExecutionService.isConnecting()) { - ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), getString(R.string.pumpbusy)); - return; - } - mHandler.post(new Runnable() { - @Override - public void run() { - TypeList selected = (TypeList) historyTypeSpinner.getSelectedItem(); - runOnUiThread(new Runnable() { - @Override - public void run() { - reloadButton.setVisibility(View.GONE); - syncButton.setVisibility(View.GONE); - statusView.setVisibility(View.VISIBLE); - } - }); - clearCardView(); - mExecutionService.loadHistory(selected.type); - loadDataFromDB(selected.type); - runOnUiThread(new Runnable() { - @Override - public void run() { - reloadButton.setVisibility(View.VISIBLE); - syncButton.setVisibility(View.VISIBLE); - statusView.setVisibility(View.GONE); - } - }); - } - }); - } - }); - - syncButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - mHandler.post(new Runnable() { - @Override - public void run() { - runOnUiThread(new Runnable() { - @Override - public void run() { - reloadButton.setVisibility(View.GONE); - syncButton.setVisibility(View.GONE); - statusView.setVisibility(View.VISIBLE); - } - }); - DanaRNSHistorySync sync = new DanaRNSHistorySync(historyList); - sync.sync(DanaRNSHistorySync.SYNC_ALL); - runOnUiThread(new Runnable() { - @Override - public void run() { - reloadButton.setVisibility(View.VISIBLE); - syncButton.setVisibility(View.VISIBLE); - statusView.setVisibility(View.GONE); - } - }); - } - }); - } - }); - - historyTypeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - TypeList selected = (TypeList) historyTypeSpinner.getSelectedItem(); - loadDataFromDB(selected.type); - showingType = selected.type; - } - - @Override - public void onNothingSelected(AdapterView parent) { - clearCardView(); - } - }); - profile = MainApp.getConfigBuilder().getProfile(); - if (profile == null) { - ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.sResources.getString(R.string.noprofile)); - finish(); - } - } - - public static class RecyclerViewAdapter extends RecyclerView.Adapter { - - List historyList; - - RecyclerViewAdapter(List historyList) { - this.historyList = historyList; - } - - @Override - public HistoryViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { - View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.danar_history_item, viewGroup, false); - return new HistoryViewHolder(v); - } - - @Override - public void onBindViewHolder(HistoryViewHolder holder, int position) { - DanaRHistoryRecord record = historyList.get(position); - holder.time.setText(DateUtil.dateAndTimeString(record.recordDate)); - holder.value.setText(DecimalFormatter.to2Decimal(record.recordValue)); - holder.stringvalue.setText(record.stringRecordValue); - holder.bolustype.setText(record.bolusType); - holder.duration.setText(DecimalFormatter.to0Decimal(record.recordDuration)); - holder.alarm.setText(record.recordAlarm); - switch (showingType) { - case RecordTypes.RECORD_TYPE_ALARM: - holder.time.setVisibility(View.VISIBLE); - holder.value.setVisibility(View.VISIBLE); - holder.stringvalue.setVisibility(View.GONE); - holder.bolustype.setVisibility(View.GONE); - holder.duration.setVisibility(View.GONE); - holder.dailybasal.setVisibility(View.GONE); - holder.dailybolus.setVisibility(View.GONE); - holder.dailytotal.setVisibility(View.GONE); - holder.alarm.setVisibility(View.VISIBLE); - break; - case RecordTypes.RECORD_TYPE_BOLUS: - holder.time.setVisibility(View.VISIBLE); - holder.value.setVisibility(View.VISIBLE); - holder.stringvalue.setVisibility(View.GONE); - holder.bolustype.setVisibility(View.VISIBLE); - holder.duration.setVisibility(View.VISIBLE); - holder.dailybasal.setVisibility(View.GONE); - holder.dailybolus.setVisibility(View.GONE); - holder.dailytotal.setVisibility(View.GONE); - holder.alarm.setVisibility(View.GONE); - break; - case RecordTypes.RECORD_TYPE_DAILY: - holder.dailybasal.setText(DecimalFormatter.to2Decimal(record.recordDailyBasal) + "U"); - holder.dailybolus.setText(DecimalFormatter.to2Decimal(record.recordDailyBolus) + "U"); - holder.dailytotal.setText(DecimalFormatter.to2Decimal(record.recordDailyBolus + record.recordDailyBasal) + "U"); - holder.time.setText(DateUtil.dateString(record.recordDate)); - holder.time.setVisibility(View.VISIBLE); - holder.value.setVisibility(View.GONE); - holder.stringvalue.setVisibility(View.GONE); - holder.bolustype.setVisibility(View.GONE); - holder.duration.setVisibility(View.GONE); - holder.dailybasal.setVisibility(View.VISIBLE); - holder.dailybolus.setVisibility(View.VISIBLE); - holder.dailytotal.setVisibility(View.VISIBLE); - holder.alarm.setVisibility(View.GONE); - break; - case RecordTypes.RECORD_TYPE_GLUCOSE: - holder.value.setText(Profile.toUnitsString(record.recordValue, record.recordValue * Constants.MGDL_TO_MMOLL, profile.getUnits())); - // rest is the same - case RecordTypes.RECORD_TYPE_CARBO: - case RecordTypes.RECORD_TYPE_BASALHOUR: - case RecordTypes.RECORD_TYPE_ERROR: - case RecordTypes.RECORD_TYPE_PRIME: - case RecordTypes.RECORD_TYPE_REFILL: - case RecordTypes.RECORD_TYPE_TB: - holder.time.setVisibility(View.VISIBLE); - holder.value.setVisibility(View.VISIBLE); - holder.stringvalue.setVisibility(View.GONE); - holder.bolustype.setVisibility(View.GONE); - holder.duration.setVisibility(View.GONE); - holder.dailybasal.setVisibility(View.GONE); - holder.dailybolus.setVisibility(View.GONE); - holder.dailytotal.setVisibility(View.GONE); - holder.alarm.setVisibility(View.GONE); - break; - case RecordTypes.RECORD_TYPE_SUSPEND: - holder.time.setVisibility(View.VISIBLE); - holder.value.setVisibility(View.GONE); - holder.stringvalue.setVisibility(View.VISIBLE); - holder.bolustype.setVisibility(View.GONE); - holder.duration.setVisibility(View.GONE); - holder.dailybasal.setVisibility(View.GONE); - holder.dailybolus.setVisibility(View.GONE); - holder.dailytotal.setVisibility(View.GONE); - holder.alarm.setVisibility(View.GONE); - break; - } - } - - @Override - public int getItemCount() { - return historyList.size(); - } - - @Override - public void onAttachedToRecyclerView(RecyclerView recyclerView) { - super.onAttachedToRecyclerView(recyclerView); - } - - public static class HistoryViewHolder extends RecyclerView.ViewHolder { - CardView cv; - TextView time; - TextView value; - TextView bolustype; - TextView stringvalue; - TextView duration; - TextView dailybasal; - TextView dailybolus; - TextView dailytotal; - TextView alarm; - - HistoryViewHolder(View itemView) { - super(itemView); - cv = (CardView) itemView.findViewById(R.id.danar_history_cardview); - time = (TextView) itemView.findViewById(R.id.danar_history_time); - value = (TextView) itemView.findViewById(R.id.danar_history_value); - bolustype = (TextView) itemView.findViewById(R.id.danar_history_bolustype); - stringvalue = (TextView) itemView.findViewById(R.id.danar_history_stringvalue); - duration = (TextView) itemView.findViewById(R.id.danar_history_duration); - dailybasal = (TextView) itemView.findViewById(R.id.danar_history_dailybasal); - dailybolus = (TextView) itemView.findViewById(R.id.danar_history_dailybolus); - dailytotal = (TextView) itemView.findViewById(R.id.danar_history_dailytotal); - alarm = (TextView) itemView.findViewById(R.id.danar_history_alarm); - } - } - } - - private void loadDataFromDB(byte type) { - historyList = MainApp.getDbHelper().getDanaRHistoryRecordsByType(type); - - runOnUiThread(new Runnable() { - @Override - public void run() { - recyclerView.swapAdapter(new RecyclerViewAdapter(historyList), false); - } - }); - } - - private void clearCardView() { - historyList = new ArrayList<>(); - runOnUiThread(new Runnable() { - @Override - public void run() { - recyclerView.swapAdapter(new RecyclerViewAdapter(historyList), false); - } - }); - } - - @Subscribe - public void onStatusEvent(final EventDanaRSyncStatus s) { - log.debug("EventDanaRSyncStatus: " + s.message); - runOnUiThread( - new Runnable() { - @Override - public void run() { - statusView.setText(s.message); - } - }); - } - - @Subscribe - public void onStatusEvent(final EventPumpStatusChanged c) { - runOnUiThread( - new Runnable() { - @Override - public void run() { - statusView.setText(c.textStatus()); - } - } - ); - } - - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/History/DanaRStatsActivity.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/History/DanaRStatsActivity.java deleted file mode 100644 index dc014b5eaa..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/History/DanaRStatsActivity.java +++ /dev/null @@ -1,539 +0,0 @@ -package info.nightscout.androidaps.plugins.PumpDanaRKorean.History; - -import android.app.Activity; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.graphics.Color; -import android.graphics.Rect; -import android.os.Bundle; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; -import android.support.v7.widget.LinearLayoutManager; -import android.text.TextUtils; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.View; -import android.view.WindowManager; -import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputMethodManager; -import android.widget.Button; -import android.widget.EditText; -import android.widget.TableLayout; -import android.widget.TableRow; -import android.widget.TextView; - -import com.squareup.otto.Subscribe; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.text.DateFormat; -import java.text.DecimalFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.List; - -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.db.DanaRHistoryRecord; -import info.nightscout.androidaps.events.EventPumpStatusChanged; -import info.nightscout.androidaps.interfaces.ProfileInterface; -import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.ProfileCircadianPercentage.CircadianPercentageProfilePlugin; -import info.nightscout.androidaps.plugins.PumpDanaR.comm.RecordTypes; -import info.nightscout.androidaps.plugins.PumpDanaR.events.EventDanaRSyncStatus; -import info.nightscout.androidaps.plugins.PumpDanaRKorean.services.DanaRKoreanExecutionService; -import info.nightscout.utils.DecimalFormatter; -import info.nightscout.utils.SP; -import info.nightscout.utils.SafeParse; -import info.nightscout.utils.ToastUtils; - -public class DanaRStatsActivity extends Activity { - private static Logger log = LoggerFactory.getLogger(DanaRStatsActivity.class); - - private boolean mBounded; - private static DanaRKoreanExecutionService mExecutionService; - - private Handler mHandler; - private static HandlerThread mHandlerThread; - - TextView statusView, statsMessage, totalBaseBasal2; - EditText totalBaseBasal; - Button reloadButton; - LinearLayoutManager llm; - TableLayout tl, ctl, etl; - String TBB; - double magicNumber; - DecimalFormat decimalFormat; - - List historyList = new ArrayList<>(); - - public DanaRStatsActivity() { - super(); - mHandlerThread = new HandlerThread(DanaRStatsActivity.class.getSimpleName()); - mHandlerThread.start(); - this.mHandler = new Handler(mHandlerThread.getLooper()); - } - - @Override - public void onStart() { - super.onStart(); - Intent intent = new Intent(this, DanaRKoreanExecutionService.class); - bindService(intent, mConnection, Context.BIND_AUTO_CREATE); - } - - @Override - protected void onResume() { - super.onResume(); - MainApp.bus().register(this); - } - - @Override - protected void onPause() { - super.onPause(); - MainApp.bus().unregister(this); - } - - @Override - public void onStop() { - super.onStop(); - if (mBounded) { - unbindService(mConnection); - mBounded = false; - } - } - - @Override - public boolean dispatchTouchEvent(MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_DOWN) { - View myView = getCurrentFocus(); - if (myView instanceof EditText) { - Rect rect = new Rect(); - myView.getGlobalVisibleRect(rect); - if (!rect.contains((int) event.getRawX(), (int) event.getRawY())) { - myView.clearFocus(); - } - } - } - return super.dispatchTouchEvent(event); - } - - ServiceConnection mConnection = new ServiceConnection() { - - public void onServiceDisconnected(ComponentName name) { - log.debug("Service is disconnected"); - mBounded = false; - mExecutionService = null; - } - - public void onServiceConnected(ComponentName name, IBinder service) { - log.debug("Service is connected"); - mBounded = true; - DanaRKoreanExecutionService.LocalBinder mLocalBinder = (DanaRKoreanExecutionService.LocalBinder) service; - mExecutionService = mLocalBinder.getServiceInstance(); - } - }; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.danar_statsactivity); - getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); - statusView = (TextView) findViewById(R.id.danar_stats_connection_status); - reloadButton = (Button) findViewById(R.id.danar_statsreload); - totalBaseBasal = (EditText) findViewById(R.id.danar_stats_editTotalBaseBasal); - totalBaseBasal2 = (TextView) findViewById(R.id.danar_stats_editTotalBaseBasal2); - statsMessage = (TextView) findViewById(R.id.danar_stats_Message); - - statusView.setVisibility(View.GONE); - statsMessage.setVisibility(View.GONE); - - totalBaseBasal2.setEnabled(false); - totalBaseBasal2.setClickable(false); - totalBaseBasal2.setFocusable(false); - totalBaseBasal2.setInputType(0); - - decimalFormat = new DecimalFormat("0.000"); - llm = new LinearLayoutManager(this); - - TBB = SP.getString("TBB", "10.00"); - totalBaseBasal.setText(TBB); - - ProfileInterface pi = ConfigBuilderPlugin.getActiveProfileInterface(); - if (pi != null && pi instanceof CircadianPercentageProfilePlugin) { - double cppTBB = ((CircadianPercentageProfilePlugin) pi).baseBasalSum(); - totalBaseBasal.setText(decimalFormat.format(cppTBB)); - SP.putString("TBB", totalBaseBasal.getText().toString()); - TBB = SP.getString("TBB", ""); - } - - // stats table - tl = (TableLayout) findViewById(R.id.main_table); - TableRow tr_head = new TableRow(this); - tr_head.setBackgroundColor(Color.DKGRAY); - tr_head.setLayoutParams(new TableLayout.LayoutParams( - TableLayout.LayoutParams.MATCH_PARENT, - TableLayout.LayoutParams.WRAP_CONTENT)); - - TextView label_date = new TextView(this); - label_date.setText(getString(R.string.danar_stats_date)); - label_date.setTextColor(Color.WHITE); - tr_head.addView(label_date); - - TextView label_basalrate = new TextView(this); - label_basalrate.setText(getString(R.string.danar_stats_basalrate)); - label_basalrate.setTextColor(Color.WHITE); - tr_head.addView(label_basalrate); - - TextView label_bolus = new TextView(this); - label_bolus.setText(getString(R.string.danar_stats_bolus)); - label_bolus.setTextColor(Color.WHITE); - tr_head.addView(label_bolus); - - TextView label_tdd = new TextView(this); - label_tdd.setText(getString(R.string.danar_stats_tdd)); - label_tdd.setTextColor(Color.WHITE); - tr_head.addView(label_tdd); - - TextView label_ratio = new TextView(this); - label_ratio.setText(getString(R.string.danar_stats_ratio)); - label_ratio.setTextColor(Color.WHITE); - tr_head.addView(label_ratio); - - // add stats headers to tables - tl.addView(tr_head, new TableLayout.LayoutParams( - TableLayout.LayoutParams.MATCH_PARENT, - TableLayout.LayoutParams.WRAP_CONTENT)); - - // cumulative table - ctl = (TableLayout) findViewById(R.id.cumulative_table); - TableRow ctr_head = new TableRow(this); - ctr_head.setBackgroundColor(Color.DKGRAY); - ctr_head.setLayoutParams(new TableLayout.LayoutParams( - TableLayout.LayoutParams.MATCH_PARENT, - TableLayout.LayoutParams.WRAP_CONTENT)); - - TextView label_cum_amount_days = new TextView(this); - label_cum_amount_days.setText(getString(R.string.danar_stats_amount_days)); - label_cum_amount_days.setTextColor(Color.WHITE); - ctr_head.addView(label_cum_amount_days); - - TextView label_cum_tdd = new TextView(this); - label_cum_tdd.setText(getString(R.string.danar_stats_tdd)); - label_cum_tdd.setTextColor(Color.WHITE); - ctr_head.addView(label_cum_tdd); - - TextView label_cum_ratio = new TextView(this); - label_cum_ratio.setText(getString(R.string.danar_stats_ratio)); - label_cum_ratio.setTextColor(Color.WHITE); - ctr_head.addView(label_cum_ratio); - - // add cummulative headers to tables - ctl.addView(ctr_head, new TableLayout.LayoutParams( - TableLayout.LayoutParams.MATCH_PARENT, - TableLayout.LayoutParams.WRAP_CONTENT)); - - // expontial table - etl = (TableLayout) findViewById(R.id.expweight_table); - TableRow etr_head = new TableRow(this); - etr_head.setBackgroundColor(Color.DKGRAY); - etr_head.setLayoutParams(new TableLayout.LayoutParams( - TableLayout.LayoutParams.MATCH_PARENT, - TableLayout.LayoutParams.WRAP_CONTENT)); - - TextView label_exp_weight = new TextView(this); - label_exp_weight.setText(getString(R.string.danar_stats_weight)); - label_exp_weight.setTextColor(Color.WHITE); - etr_head.addView(label_exp_weight); - - TextView label_exp_tdd = new TextView(this); - label_exp_tdd.setText(getString(R.string.danar_stats_tdd)); - label_exp_tdd.setTextColor(Color.WHITE); - etr_head.addView(label_exp_tdd); - - TextView label_exp_ratio = new TextView(this); - label_exp_ratio.setText(getString(R.string.danar_stats_ratio)); - label_exp_ratio.setTextColor(Color.WHITE); - etr_head.addView(label_exp_ratio); - - // add expontial headers to tables - etl.addView(etr_head, new TableLayout.LayoutParams( - TableLayout.LayoutParams.MATCH_PARENT, - TableLayout.LayoutParams.WRAP_CONTENT)); - - reloadButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (mExecutionService.isConnected() || mExecutionService.isConnecting()) { - ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), getString(R.string.pumpbusy)); - return; - } - mHandler.post(new Runnable() { - @Override - public void run() { - runOnUiThread(new Runnable() { - @Override - public void run() { - reloadButton.setVisibility(View.GONE); - statusView.setVisibility(View.VISIBLE); - statsMessage.setVisibility(View.VISIBLE); - statsMessage.setText(getString(R.string.danar_stats_warning_Message)); - } - }); - mExecutionService.loadHistory(RecordTypes.RECORD_TYPE_DAILY); - loadDataFromDB(RecordTypes.RECORD_TYPE_DAILY); - runOnUiThread(new Runnable() { - @Override - public void run() { - reloadButton.setVisibility(View.VISIBLE); - statusView.setVisibility(View.GONE); - statsMessage.setVisibility(View.GONE); - } - }); - } - }); - } - }); - - totalBaseBasal.setOnEditorActionListener(new TextView.OnEditorActionListener() { - @Override - public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - if (actionId == EditorInfo.IME_ACTION_DONE) { - totalBaseBasal.clearFocus(); - return true; - } - return false; - } - }); - - totalBaseBasal.setOnFocusChangeListener(new View.OnFocusChangeListener() { - @Override - public void onFocusChange(View v, boolean hasFocus) { - if (hasFocus) { - totalBaseBasal.getText().clear(); - } else { - SP.putString("TBB", totalBaseBasal.getText().toString()); - TBB = SP.getString("TBB", ""); - loadDataFromDB(RecordTypes.RECORD_TYPE_DAILY); - InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(totalBaseBasal.getWindowToken(), 0); - } - } - }); - - loadDataFromDB(RecordTypes.RECORD_TYPE_DAILY); - } - - private void loadDataFromDB(byte type) { - historyList = MainApp.getDbHelper().getDanaRHistoryRecordsByType(type); - - runOnUiThread(new Runnable() { - @Override - public void run() { - cleanTable(tl); - cleanTable(ctl); - cleanTable(etl); - DateFormat df = new SimpleDateFormat("dd.MM."); - - if (TextUtils.isEmpty(TBB)) { - totalBaseBasal.setError("Please Enter Total Base Basal"); - return; - } else { - magicNumber = SafeParse.stringToDouble(TBB); - } - - magicNumber *= 2; - totalBaseBasal2.setText(decimalFormat.format(magicNumber)); - - int i = 0; - double sum = 0d; - double weighted03 = 0d; - double weighted05 = 0d; - double weighted07 = 0d; - - for (DanaRHistoryRecord record : historyList) { - double tdd = record.recordDailyBolus + record.recordDailyBasal; - - // Create the table row - TableRow tr = new TableRow(DanaRStatsActivity.this); - if (i % 2 != 0) tr.setBackgroundColor(Color.DKGRAY); - tr.setId(100 + i); - tr.setLayoutParams(new TableLayout.LayoutParams( - TableLayout.LayoutParams.MATCH_PARENT, - TableLayout.LayoutParams.WRAP_CONTENT)); - - // Here create the TextView dynamically - TextView labelDATE = new TextView(DanaRStatsActivity.this); - labelDATE.setId(200 + i); - labelDATE.setText(df.format(new Date(record.recordDate))); - labelDATE.setTextColor(Color.WHITE); - tr.addView(labelDATE); - - TextView labelBASAL = new TextView(DanaRStatsActivity.this); - labelBASAL.setId(300 + i); - labelBASAL.setText(DecimalFormatter.to2Decimal(record.recordDailyBasal) + " U"); - labelBASAL.setTextColor(Color.WHITE); - tr.addView(labelBASAL); - - TextView labelBOLUS = new TextView(DanaRStatsActivity.this); - labelBOLUS.setId(400 + i); - labelBOLUS.setText(DecimalFormatter.to2Decimal(record.recordDailyBolus) + " U"); - labelBOLUS.setTextColor(Color.WHITE); - tr.addView(labelBOLUS); - - TextView labelTDD = new TextView(DanaRStatsActivity.this); - labelTDD.setId(500 + i); - labelTDD.setText(DecimalFormatter.to2Decimal(tdd) + " U"); - labelTDD.setTextColor(Color.WHITE); - tr.addView(labelTDD); - - TextView labelRATIO = new TextView(DanaRStatsActivity.this); - labelRATIO.setId(600 + i); - labelRATIO.setText(Math.round(100 * tdd / magicNumber) + " %"); - labelRATIO.setTextColor(Color.WHITE); - tr.addView(labelRATIO); - - // add stats rows to tables - tl.addView(tr, new TableLayout.LayoutParams( - TableLayout.LayoutParams.MATCH_PARENT, - TableLayout.LayoutParams.WRAP_CONTENT)); - - sum = sum + tdd; - i++; - - // Create the cumtable row - TableRow ctr = new TableRow(DanaRStatsActivity.this); - if (i % 2 == 0) ctr.setBackgroundColor(Color.DKGRAY); - ctr.setId(700 + i); - ctr.setLayoutParams(new TableLayout.LayoutParams( - TableLayout.LayoutParams.MATCH_PARENT, - TableLayout.LayoutParams.WRAP_CONTENT)); - - // Here create the TextView dynamically - TextView labelDAYS = new TextView(DanaRStatsActivity.this); - labelDAYS.setId(800 + i); - labelDAYS.setText("" + i); - labelDAYS.setTextColor(Color.WHITE); - ctr.addView(labelDAYS); - - TextView labelCUMTDD = new TextView(DanaRStatsActivity.this); - labelCUMTDD.setId(900 + i); - labelCUMTDD.setText(DecimalFormatter.to2Decimal(sum / i) + " U"); - labelCUMTDD.setTextColor(Color.WHITE); - ctr.addView(labelCUMTDD); - - TextView labelCUMRATIO = new TextView(DanaRStatsActivity.this); - labelCUMRATIO.setId(1000 + i); - labelCUMRATIO.setText(Math.round(100 * sum / i / magicNumber) + " %"); - labelCUMRATIO.setTextColor(Color.WHITE); - ctr.addView(labelCUMRATIO); - - // add cummulative rows to tables - ctl.addView(ctr, new TableLayout.LayoutParams( - TableLayout.LayoutParams.MATCH_PARENT, - TableLayout.LayoutParams.WRAP_CONTENT)); - } - - if (historyList.size() < 3 || !(df.format(new Date(historyList.get(0).recordDate)).equals(df.format(new Date(System.currentTimeMillis() - 1000 * 60 * 60 * 24))))) { - statsMessage.setVisibility(View.VISIBLE); - statsMessage.setText(getString(R.string.danar_stats_olddata_Message)); - - } else { - tl.setBackgroundColor(Color.TRANSPARENT); - } - - Collections.reverse(historyList); - - i = 0; - - for (DanaRHistoryRecord record : historyList) { - double tdd = record.recordDailyBolus + record.recordDailyBasal; - if (i == 0) { - weighted03 = tdd; - weighted05 = tdd; - weighted07 = tdd; - - } else { - weighted07 = (weighted07 * 0.3 + tdd * 0.7); - weighted05 = (weighted05 * 0.5 + tdd * 0.5); - weighted03 = (weighted03 * 0.7 + tdd * 0.3); - } - i++; - } - - // Create the exptable row - TableRow etr = new TableRow(DanaRStatsActivity.this); - if (i % 2 != 0) etr.setBackgroundColor(Color.DKGRAY); - etr.setId(1100 + i); - etr.setLayoutParams(new TableLayout.LayoutParams( - TableLayout.LayoutParams.MATCH_PARENT, - TableLayout.LayoutParams.WRAP_CONTENT)); - - // Here create the TextView dynamically - TextView labelWEIGHT = new TextView(DanaRStatsActivity.this); - labelWEIGHT.setId(1200 + i); - labelWEIGHT.setText("0.3\n" + "0.5\n" + "0.7"); - labelWEIGHT.setTextColor(Color.WHITE); - etr.addView(labelWEIGHT); - - TextView labelEXPTDD = new TextView(DanaRStatsActivity.this); - labelEXPTDD.setId(1300 + i); - labelEXPTDD.setText(DecimalFormatter.to2Decimal(weighted03) - + " U\n" + DecimalFormatter.to2Decimal(weighted05) - + " U\n" + DecimalFormatter.to2Decimal(weighted07) + " U"); - labelEXPTDD.setTextColor(Color.WHITE); - etr.addView(labelEXPTDD); - - TextView labelEXPRATIO = new TextView(DanaRStatsActivity.this); - labelEXPRATIO.setId(1400 + i); - labelEXPRATIO.setText(Math.round(100 * weighted03 / magicNumber) + " %\n" - + Math.round(100 * weighted05 / magicNumber) + " %\n" - + Math.round(100 * weighted07 / magicNumber) + " %"); - labelEXPRATIO.setTextColor(Color.WHITE); - etr.addView(labelEXPRATIO); - - // add exponentail rows to tables - etl.addView(etr, new TableLayout.LayoutParams( - TableLayout.LayoutParams.MATCH_PARENT, - TableLayout.LayoutParams.WRAP_CONTENT)); - } - }); - } - - private void cleanTable(TableLayout table) { - int childCount = table.getChildCount(); - // Remove all rows except the first one - if (childCount > 1) { - table.removeViews(1, childCount - 1); - } - } - - @Subscribe - public void onStatusEvent(final EventDanaRSyncStatus s) { - log.debug("EventDanaRSyncStatus: " + s.message); - runOnUiThread( - new Runnable() { - @Override - public void run() { - statusView.setText(s.message); - } - }); - } - - @Subscribe - public void onStatusEvent(final EventPumpStatusChanged c) { - runOnUiThread( - new Runnable() { - @Override - public void run() { - statusView.setText(c.textStatus()); - } - } - ); - } -} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Fragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Fragment.java index a0480685ad..a1132ce8b4 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Fragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Fragment.java @@ -29,9 +29,9 @@ import info.nightscout.androidaps.events.EventPumpStatusChanged; import info.nightscout.androidaps.events.EventTempBasalChange; import info.nightscout.androidaps.plugins.PumpDanaR.DanaRPump; import info.nightscout.androidaps.plugins.PumpDanaR.Dialogs.ProfileViewDialog; +import info.nightscout.androidaps.plugins.PumpDanaR.activities.DanaRHistoryActivity; +import info.nightscout.androidaps.plugins.PumpDanaR.activities.DanaRStatsActivity; import info.nightscout.androidaps.plugins.PumpDanaR.events.EventDanaRNewStatus; -import info.nightscout.androidaps.plugins.PumpDanaRv2.History.DanaRHistoryActivity; -import info.nightscout.androidaps.plugins.PumpDanaRv2.History.DanaRStatsActivity; import info.nightscout.utils.DateUtil; import info.nightscout.utils.DecimalFormatter; import info.nightscout.utils.SetWarnColor; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java index 305598e2ca..c79100ff81 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java @@ -31,6 +31,7 @@ import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.db.Treatment; import info.nightscout.androidaps.events.EventAppExit; import info.nightscout.androidaps.interfaces.ConstraintsInterface; +import info.nightscout.androidaps.interfaces.DanaRInterface; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.ProfileInterface; import info.nightscout.androidaps.interfaces.PumpDescription; @@ -51,7 +52,7 @@ import info.nightscout.utils.Round; /** * Created by mike on 05.08.2016. */ -public class DanaRv2Plugin implements PluginBase, PumpInterface, ConstraintsInterface, ProfileInterface { +public class DanaRv2Plugin implements PluginBase, PumpInterface, DanaRInterface, ConstraintsInterface, ProfileInterface { private static Logger log = LoggerFactory.getLogger(DanaRv2Plugin.class); @Override @@ -623,6 +624,15 @@ public class DanaRv2Plugin implements PluginBase, PumpInterface, ConstraintsInte return pumpDescription; } + /** + * DanaR interface + */ + + @Override + public boolean loadHistory(byte type) { + return sExecutionService.loadHistory(type); + } + /** * Constraint interface */ @@ -735,6 +745,7 @@ public class DanaRv2Plugin implements PluginBase, PumpInterface, ConstraintsInte ret += "Batt: " + pump.batteryRemaining + "\n"; return ret; } + // TODO: daily total constraint } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/History/DanaRHistoryActivity.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/History/DanaRHistoryActivity.java deleted file mode 100644 index 1391b753f8..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/History/DanaRHistoryActivity.java +++ /dev/null @@ -1,431 +0,0 @@ -package info.nightscout.androidaps.plugins.PumpDanaRv2.History; - -import android.app.Activity; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.os.Bundle; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; -import android.support.v7.widget.CardView; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.Spinner; -import android.widget.TextView; - -import com.squareup.otto.Subscribe; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.List; - -import info.nightscout.androidaps.Constants; -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.db.DanaRHistoryRecord; -import info.nightscout.androidaps.events.EventPumpStatusChanged; -import info.nightscout.androidaps.data.Profile; -import info.nightscout.androidaps.plugins.PumpDanaR.History.DanaRNSHistorySync; -import info.nightscout.androidaps.plugins.PumpDanaR.comm.RecordTypes; -import info.nightscout.androidaps.plugins.PumpDanaR.events.EventDanaRSyncStatus; -import info.nightscout.androidaps.plugins.PumpDanaRv2.services.DanaRv2ExecutionService; -import info.nightscout.utils.DateUtil; -import info.nightscout.utils.DecimalFormatter; -import info.nightscout.utils.ToastUtils; - -public class DanaRHistoryActivity extends Activity { - private static Logger log = LoggerFactory.getLogger(DanaRHistoryActivity.class); - - private boolean mBounded; - private static DanaRv2ExecutionService mExecutionService; - - private Handler mHandler; - private static HandlerThread mHandlerThread; - - static Profile profile = null; - - Spinner historyTypeSpinner; - TextView statusView; - Button reloadButton; - Button syncButton; - RecyclerView recyclerView; - LinearLayoutManager llm; - - static byte showingType = RecordTypes.RECORD_TYPE_ALARM; - List historyList = new ArrayList<>(); - - public static class TypeList { - public byte type; - String name; - - public TypeList(byte type, String name) { - this.type = type; - this.name = name; - } - - @Override - public String toString() { - return name; - } - } - - public DanaRHistoryActivity() { - super(); - mHandlerThread = new HandlerThread(DanaRHistoryActivity.class.getSimpleName()); - mHandlerThread.start(); - this.mHandler = new Handler(mHandlerThread.getLooper()); - } - - - @Override - public void onStart() { - super.onStart(); - Intent intent = new Intent(this, DanaRv2ExecutionService.class); - bindService(intent, mConnection, Context.BIND_AUTO_CREATE); - } - - @Override - protected void onResume() { - super.onResume(); - MainApp.bus().register(this); - } - - @Override - protected void onPause() { - super.onPause(); - MainApp.bus().unregister(this); - } - - @Override - public void onStop() { - super.onStop(); - if (mBounded) { - unbindService(mConnection); - mBounded = false; - } - } - - ServiceConnection mConnection = new ServiceConnection() { - - public void onServiceDisconnected(ComponentName name) { - log.debug("Service is disconnected"); - mBounded = false; - mExecutionService = null; - } - - public void onServiceConnected(ComponentName name, IBinder service) { - log.debug("Service is connected"); - mBounded = true; - DanaRv2ExecutionService.LocalBinder mLocalBinder = (DanaRv2ExecutionService.LocalBinder) service; - mExecutionService = mLocalBinder.getServiceInstance(); - } - }; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.danar_historyactivity); - - historyTypeSpinner = (Spinner) findViewById(R.id.danar_historytype); - statusView = (TextView) findViewById(R.id.danar_historystatus); - reloadButton = (Button) findViewById(R.id.danar_historyreload); - syncButton = (Button) findViewById(R.id.danar_historysync); - recyclerView = (RecyclerView) findViewById(R.id.danar_history_recyclerview); - - recyclerView.setHasFixedSize(true); - llm = new LinearLayoutManager(this); - recyclerView.setLayoutManager(llm); - - RecyclerViewAdapter adapter = new RecyclerViewAdapter(historyList); - recyclerView.setAdapter(adapter); - - statusView.setVisibility(View.GONE); - - // Types - - ArrayList typeList = new ArrayList<>(); - typeList.add(new TypeList(RecordTypes.RECORD_TYPE_ALARM, getString(R.string.danar_history_alarm))); - typeList.add(new TypeList(RecordTypes.RECORD_TYPE_BASALHOUR, getString(R.string.danar_history_basalhours))); - typeList.add(new TypeList(RecordTypes.RECORD_TYPE_BOLUS, getString(R.string.danar_history_bolus))); - typeList.add(new TypeList(RecordTypes.RECORD_TYPE_CARBO, getString(R.string.danar_history_carbohydrates))); - typeList.add(new TypeList(RecordTypes.RECORD_TYPE_DAILY, getString(R.string.danar_history_dailyinsulin))); - typeList.add(new TypeList(RecordTypes.RECORD_TYPE_GLUCOSE, getString(R.string.danar_history_glucose))); - - ArrayAdapter spinnerAdapter = new ArrayAdapter<>(this, - android.R.layout.simple_spinner_item, typeList); - spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - historyTypeSpinner.setAdapter(spinnerAdapter); - - reloadButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (mExecutionService.isConnected() || mExecutionService.isConnecting()) { - ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), getString(R.string.pumpbusy)); - return; - } - mHandler.post(new Runnable() { - @Override - public void run() { - TypeList selected = (TypeList) historyTypeSpinner.getSelectedItem(); - runOnUiThread(new Runnable() { - @Override - public void run() { - reloadButton.setVisibility(View.GONE); - syncButton.setVisibility(View.GONE); - statusView.setVisibility(View.VISIBLE); - } - }); - clearCardView(); - mExecutionService.loadHistory(selected.type); - loadDataFromDB(selected.type); - runOnUiThread(new Runnable() { - @Override - public void run() { - reloadButton.setVisibility(View.VISIBLE); - syncButton.setVisibility(View.VISIBLE); - statusView.setVisibility(View.GONE); - } - }); - } - }); - } - }); - - syncButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - mHandler.post(new Runnable() { - @Override - public void run() { - runOnUiThread(new Runnable() { - @Override - public void run() { - reloadButton.setVisibility(View.GONE); - syncButton.setVisibility(View.GONE); - statusView.setVisibility(View.VISIBLE); - } - }); - DanaRNSHistorySync sync = new DanaRNSHistorySync(historyList); - sync.sync(DanaRNSHistorySync.SYNC_ALL); - runOnUiThread(new Runnable() { - @Override - public void run() { - reloadButton.setVisibility(View.VISIBLE); - syncButton.setVisibility(View.VISIBLE); - statusView.setVisibility(View.GONE); - } - }); - } - }); - } - }); - - historyTypeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - TypeList selected = (TypeList) historyTypeSpinner.getSelectedItem(); - loadDataFromDB(selected.type); - showingType = selected.type; - } - - @Override - public void onNothingSelected(AdapterView parent) { - clearCardView(); - } - }); - profile = MainApp.getConfigBuilder().getProfile(); - if (profile == null) { - ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.sResources.getString(R.string.noprofile)); - finish(); - } - } - - public static class RecyclerViewAdapter extends RecyclerView.Adapter { - - List historyList; - - RecyclerViewAdapter(List historyList) { - this.historyList = historyList; - } - - @Override - public HistoryViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { - View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.danar_history_item, viewGroup, false); - return new HistoryViewHolder(v); - } - - @Override - public void onBindViewHolder(HistoryViewHolder holder, int position) { - DanaRHistoryRecord record = historyList.get(position); - holder.time.setText(DateUtil.dateAndTimeString(record.recordDate)); - holder.value.setText(DecimalFormatter.to2Decimal(record.recordValue)); - holder.stringvalue.setText(record.stringRecordValue); - holder.bolustype.setText(record.bolusType); - holder.duration.setText(DecimalFormatter.to0Decimal(record.recordDuration)); - holder.alarm.setText(record.recordAlarm); - switch (showingType) { - case RecordTypes.RECORD_TYPE_ALARM: - holder.time.setVisibility(View.VISIBLE); - holder.value.setVisibility(View.VISIBLE); - holder.stringvalue.setVisibility(View.GONE); - holder.bolustype.setVisibility(View.GONE); - holder.duration.setVisibility(View.GONE); - holder.dailybasal.setVisibility(View.GONE); - holder.dailybolus.setVisibility(View.GONE); - holder.dailytotal.setVisibility(View.GONE); - holder.alarm.setVisibility(View.VISIBLE); - break; - case RecordTypes.RECORD_TYPE_BOLUS: - holder.time.setVisibility(View.VISIBLE); - holder.value.setVisibility(View.VISIBLE); - holder.stringvalue.setVisibility(View.GONE); - holder.bolustype.setVisibility(View.VISIBLE); - holder.duration.setVisibility(View.VISIBLE); - holder.dailybasal.setVisibility(View.GONE); - holder.dailybolus.setVisibility(View.GONE); - holder.dailytotal.setVisibility(View.GONE); - holder.alarm.setVisibility(View.GONE); - break; - case RecordTypes.RECORD_TYPE_DAILY: - holder.dailybasal.setText(DecimalFormatter.to2Decimal(record.recordDailyBasal) + "U"); - holder.dailybolus.setText(DecimalFormatter.to2Decimal(record.recordDailyBolus) + "U"); - holder.dailytotal.setText(DecimalFormatter.to2Decimal(record.recordDailyBolus + record.recordDailyBasal) + "U"); - holder.time.setText(DateUtil.dateString(record.recordDate)); - holder.time.setVisibility(View.VISIBLE); - holder.value.setVisibility(View.GONE); - holder.stringvalue.setVisibility(View.GONE); - holder.bolustype.setVisibility(View.GONE); - holder.duration.setVisibility(View.GONE); - holder.dailybasal.setVisibility(View.VISIBLE); - holder.dailybolus.setVisibility(View.VISIBLE); - holder.dailytotal.setVisibility(View.VISIBLE); - holder.alarm.setVisibility(View.GONE); - break; - case RecordTypes.RECORD_TYPE_GLUCOSE: - holder.value.setText(Profile.toUnitsString(record.recordValue, record.recordValue * Constants.MGDL_TO_MMOLL, profile.getUnits())); - // rest is the same - case RecordTypes.RECORD_TYPE_CARBO: - case RecordTypes.RECORD_TYPE_BASALHOUR: - case RecordTypes.RECORD_TYPE_ERROR: - case RecordTypes.RECORD_TYPE_PRIME: - case RecordTypes.RECORD_TYPE_REFILL: - case RecordTypes.RECORD_TYPE_TB: - holder.time.setVisibility(View.VISIBLE); - holder.value.setVisibility(View.VISIBLE); - holder.stringvalue.setVisibility(View.GONE); - holder.bolustype.setVisibility(View.GONE); - holder.duration.setVisibility(View.GONE); - holder.dailybasal.setVisibility(View.GONE); - holder.dailybolus.setVisibility(View.GONE); - holder.dailytotal.setVisibility(View.GONE); - holder.alarm.setVisibility(View.GONE); - break; - case RecordTypes.RECORD_TYPE_SUSPEND: - holder.time.setVisibility(View.VISIBLE); - holder.value.setVisibility(View.GONE); - holder.stringvalue.setVisibility(View.VISIBLE); - holder.bolustype.setVisibility(View.GONE); - holder.duration.setVisibility(View.GONE); - holder.dailybasal.setVisibility(View.GONE); - holder.dailybolus.setVisibility(View.GONE); - holder.dailytotal.setVisibility(View.GONE); - holder.alarm.setVisibility(View.GONE); - break; - } - } - - @Override - public int getItemCount() { - return historyList.size(); - } - - @Override - public void onAttachedToRecyclerView(RecyclerView recyclerView) { - super.onAttachedToRecyclerView(recyclerView); - } - - public static class HistoryViewHolder extends RecyclerView.ViewHolder { - CardView cv; - TextView time; - TextView value; - TextView bolustype; - TextView stringvalue; - TextView duration; - TextView dailybasal; - TextView dailybolus; - TextView dailytotal; - TextView alarm; - - HistoryViewHolder(View itemView) { - super(itemView); - cv = (CardView) itemView.findViewById(R.id.danar_history_cardview); - time = (TextView) itemView.findViewById(R.id.danar_history_time); - value = (TextView) itemView.findViewById(R.id.danar_history_value); - bolustype = (TextView) itemView.findViewById(R.id.danar_history_bolustype); - stringvalue = (TextView) itemView.findViewById(R.id.danar_history_stringvalue); - duration = (TextView) itemView.findViewById(R.id.danar_history_duration); - dailybasal = (TextView) itemView.findViewById(R.id.danar_history_dailybasal); - dailybolus = (TextView) itemView.findViewById(R.id.danar_history_dailybolus); - dailytotal = (TextView) itemView.findViewById(R.id.danar_history_dailytotal); - alarm = (TextView) itemView.findViewById(R.id.danar_history_alarm); - } - } - } - - private void loadDataFromDB(byte type) { - historyList = MainApp.getDbHelper().getDanaRHistoryRecordsByType(type); - - runOnUiThread(new Runnable() { - @Override - public void run() { - recyclerView.swapAdapter(new RecyclerViewAdapter(historyList), false); - } - }); - } - - private void clearCardView() { - historyList = new ArrayList<>(); - runOnUiThread(new Runnable() { - @Override - public void run() { - recyclerView.swapAdapter(new RecyclerViewAdapter(historyList), false); - } - }); - } - - @Subscribe - public void onStatusEvent(final EventDanaRSyncStatus s) { - log.debug("EventDanaRSyncStatus: " + s.message); - runOnUiThread( - new Runnable() { - @Override - public void run() { - statusView.setText(s.message); - } - }); - } - - @Subscribe - public void onStatusEvent(final EventPumpStatusChanged c) { - runOnUiThread( - new Runnable() { - @Override - public void run() { - statusView.setText(c.textStatus()); - } - } - ); - } - - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/History/DanaRStatsActivity.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/History/DanaRStatsActivity.java deleted file mode 100644 index d366c42df7..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/History/DanaRStatsActivity.java +++ /dev/null @@ -1,546 +0,0 @@ -package info.nightscout.androidaps.plugins.PumpDanaRv2.History; - -import android.app.Activity; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.content.SharedPreferences; -import android.graphics.Color; -import android.graphics.Rect; -import android.os.Bundle; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; -import android.preference.PreferenceManager; -import android.support.v7.widget.LinearLayoutManager; -import android.text.TextUtils; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.View; -import android.view.WindowManager; -import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputMethodManager; -import android.widget.Button; -import android.widget.EditText; -import android.widget.TableLayout; -import android.widget.TableRow; -import android.widget.TextView; - -import com.j256.ormlite.dao.Dao; -import com.j256.ormlite.stmt.PreparedQuery; -import com.j256.ormlite.stmt.QueryBuilder; -import com.j256.ormlite.stmt.Where; -import com.squareup.otto.Subscribe; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.sql.SQLException; -import java.text.DateFormat; -import java.text.DecimalFormat; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.List; - -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.db.DanaRHistoryRecord; -import info.nightscout.androidaps.events.EventPumpStatusChanged; -import info.nightscout.androidaps.interfaces.ProfileInterface; -import info.nightscout.androidaps.plugins.ProfileCircadianPercentage.CircadianPercentageProfilePlugin; -import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.PumpDanaR.comm.RecordTypes; -import info.nightscout.androidaps.plugins.PumpDanaR.events.EventDanaRSyncStatus; -import info.nightscout.androidaps.plugins.PumpDanaRv2.services.DanaRv2ExecutionService; -import info.nightscout.utils.DecimalFormatter; -import info.nightscout.utils.SP; -import info.nightscout.utils.SafeParse; -import info.nightscout.utils.ToastUtils; - -public class DanaRStatsActivity extends Activity { - private static Logger log = LoggerFactory.getLogger(DanaRStatsActivity.class); - - private boolean mBounded; - private static DanaRv2ExecutionService mExecutionService; - - private Handler mHandler; - private static HandlerThread mHandlerThread; - - TextView statusView, statsMessage, totalBaseBasal2; - EditText totalBaseBasal; - Button reloadButton; - LinearLayoutManager llm; - TableLayout tl, ctl, etl; - String TBB; - double magicNumber; - DecimalFormat decimalFormat; - - List historyList = new ArrayList<>(); - - public DanaRStatsActivity() { - super(); - mHandlerThread = new HandlerThread(DanaRStatsActivity.class.getSimpleName()); - mHandlerThread.start(); - this.mHandler = new Handler(mHandlerThread.getLooper()); - } - - @Override - public void onStart() { - super.onStart(); - Intent intent = new Intent(this, DanaRv2ExecutionService.class); - bindService(intent, mConnection, Context.BIND_AUTO_CREATE); - } - - @Override - protected void onResume() { - super.onResume(); - MainApp.bus().register(this); - } - - @Override - protected void onPause() { - super.onPause(); - MainApp.bus().unregister(this); - } - - @Override - public void onStop() { - super.onStop(); - if (mBounded) { - unbindService(mConnection); - mBounded = false; - } - } - - @Override - public boolean dispatchTouchEvent(MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_DOWN) { - View myView = getCurrentFocus(); - if (myView instanceof EditText) { - Rect rect = new Rect(); - myView.getGlobalVisibleRect(rect); - if (!rect.contains((int) event.getRawX(), (int) event.getRawY())) { - myView.clearFocus(); - } - } - } - return super.dispatchTouchEvent(event); - } - - ServiceConnection mConnection = new ServiceConnection() { - - public void onServiceDisconnected(ComponentName name) { - log.debug("Service is disconnected"); - mBounded = false; - mExecutionService = null; - } - - public void onServiceConnected(ComponentName name, IBinder service) { - log.debug("Service is connected"); - mBounded = true; - DanaRv2ExecutionService.LocalBinder mLocalBinder = (DanaRv2ExecutionService.LocalBinder) service; - mExecutionService = mLocalBinder.getServiceInstance(); - } - }; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.danar_statsactivity); - getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); - statusView = (TextView) findViewById(R.id.danar_stats_connection_status); - reloadButton = (Button) findViewById(R.id.danar_statsreload); - totalBaseBasal = (EditText) findViewById(R.id.danar_stats_editTotalBaseBasal); - totalBaseBasal2 = (TextView) findViewById(R.id.danar_stats_editTotalBaseBasal2); - statsMessage = (TextView) findViewById(R.id.danar_stats_Message); - - statusView.setVisibility(View.GONE); - statsMessage.setVisibility(View.GONE); - - totalBaseBasal2.setEnabled(false); - totalBaseBasal2.setClickable(false); - totalBaseBasal2.setFocusable(false); - totalBaseBasal2.setInputType(0); - - decimalFormat = new DecimalFormat("0.000"); - llm = new LinearLayoutManager(this); - - TBB = SP.getString("TBB", "10.00"); - totalBaseBasal.setText(TBB); - - ProfileInterface pi = ConfigBuilderPlugin.getActiveProfileInterface(); - if (pi != null && pi instanceof CircadianPercentageProfilePlugin) { - double cppTBB = ((CircadianPercentageProfilePlugin) pi).baseBasalSum(); - totalBaseBasal.setText(decimalFormat.format(cppTBB)); - SP.putString("TBB", totalBaseBasal.getText().toString()); - TBB = SP.getString("TBB", ""); - } - - // stats table - tl = (TableLayout) findViewById(R.id.main_table); - TableRow tr_head = new TableRow(this); - tr_head.setBackgroundColor(Color.DKGRAY); - tr_head.setLayoutParams(new TableLayout.LayoutParams( - TableLayout.LayoutParams.MATCH_PARENT, - TableLayout.LayoutParams.WRAP_CONTENT)); - - TextView label_date = new TextView(this); - label_date.setText(getString(R.string.danar_stats_date)); - label_date.setTextColor(Color.WHITE); - tr_head.addView(label_date); - - TextView label_basalrate = new TextView(this); - label_basalrate.setText(getString(R.string.danar_stats_basalrate)); - label_basalrate.setTextColor(Color.WHITE); - tr_head.addView(label_basalrate); - - TextView label_bolus = new TextView(this); - label_bolus.setText(getString(R.string.danar_stats_bolus)); - label_bolus.setTextColor(Color.WHITE); - tr_head.addView(label_bolus); - - TextView label_tdd = new TextView(this); - label_tdd.setText(getString(R.string.danar_stats_tdd)); - label_tdd.setTextColor(Color.WHITE); - tr_head.addView(label_tdd); - - TextView label_ratio = new TextView(this); - label_ratio.setText(getString(R.string.danar_stats_ratio)); - label_ratio.setTextColor(Color.WHITE); - tr_head.addView(label_ratio); - - // add stats headers to tables - tl.addView(tr_head, new TableLayout.LayoutParams( - TableLayout.LayoutParams.MATCH_PARENT, - TableLayout.LayoutParams.WRAP_CONTENT)); - - // cumulative table - ctl = (TableLayout) findViewById(R.id.cumulative_table); - TableRow ctr_head = new TableRow(this); - ctr_head.setBackgroundColor(Color.DKGRAY); - ctr_head.setLayoutParams(new TableLayout.LayoutParams( - TableLayout.LayoutParams.MATCH_PARENT, - TableLayout.LayoutParams.WRAP_CONTENT)); - - TextView label_cum_amount_days = new TextView(this); - label_cum_amount_days.setText(getString(R.string.danar_stats_amount_days)); - label_cum_amount_days.setTextColor(Color.WHITE); - ctr_head.addView(label_cum_amount_days); - - TextView label_cum_tdd = new TextView(this); - label_cum_tdd.setText(getString(R.string.danar_stats_tdd)); - label_cum_tdd.setTextColor(Color.WHITE); - ctr_head.addView(label_cum_tdd); - - TextView label_cum_ratio = new TextView(this); - label_cum_ratio.setText(getString(R.string.danar_stats_ratio)); - label_cum_ratio.setTextColor(Color.WHITE); - ctr_head.addView(label_cum_ratio); - - // add cummulative headers to tables - ctl.addView(ctr_head, new TableLayout.LayoutParams( - TableLayout.LayoutParams.MATCH_PARENT, - TableLayout.LayoutParams.WRAP_CONTENT)); - - // expontial table - etl = (TableLayout) findViewById(R.id.expweight_table); - TableRow etr_head = new TableRow(this); - etr_head.setBackgroundColor(Color.DKGRAY); - etr_head.setLayoutParams(new TableLayout.LayoutParams( - TableLayout.LayoutParams.MATCH_PARENT, - TableLayout.LayoutParams.WRAP_CONTENT)); - - TextView label_exp_weight = new TextView(this); - label_exp_weight.setText(getString(R.string.danar_stats_weight)); - label_exp_weight.setTextColor(Color.WHITE); - etr_head.addView(label_exp_weight); - - TextView label_exp_tdd = new TextView(this); - label_exp_tdd.setText(getString(R.string.danar_stats_tdd)); - label_exp_tdd.setTextColor(Color.WHITE); - etr_head.addView(label_exp_tdd); - - TextView label_exp_ratio = new TextView(this); - label_exp_ratio.setText(getString(R.string.danar_stats_ratio)); - label_exp_ratio.setTextColor(Color.WHITE); - etr_head.addView(label_exp_ratio); - - // add expontial headers to tables - etl.addView(etr_head, new TableLayout.LayoutParams( - TableLayout.LayoutParams.MATCH_PARENT, - TableLayout.LayoutParams.WRAP_CONTENT)); - - reloadButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (mExecutionService.isConnected() || mExecutionService.isConnecting()) { - ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), getString(R.string.pumpbusy)); - return; - } - mHandler.post(new Runnable() { - @Override - public void run() { - runOnUiThread(new Runnable() { - @Override - public void run() { - reloadButton.setVisibility(View.GONE); - statusView.setVisibility(View.VISIBLE); - statsMessage.setVisibility(View.VISIBLE); - statsMessage.setText(getString(R.string.danar_stats_warning_Message)); - } - }); - mExecutionService.loadHistory(RecordTypes.RECORD_TYPE_DAILY); - loadDataFromDB(RecordTypes.RECORD_TYPE_DAILY); - runOnUiThread(new Runnable() { - @Override - public void run() { - reloadButton.setVisibility(View.VISIBLE); - statusView.setVisibility(View.GONE); - statsMessage.setVisibility(View.GONE); - } - }); - } - }); - } - }); - - totalBaseBasal.setOnEditorActionListener(new TextView.OnEditorActionListener() { - @Override - public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - if (actionId == EditorInfo.IME_ACTION_DONE) { - totalBaseBasal.clearFocus(); - return true; - } - return false; - } - }); - - totalBaseBasal.setOnFocusChangeListener(new View.OnFocusChangeListener() { - @Override - public void onFocusChange(View v, boolean hasFocus) { - if (hasFocus) { - totalBaseBasal.getText().clear(); - } else { - SP.putString("TBB", totalBaseBasal.getText().toString()); - TBB = SP.getString("TBB", ""); - loadDataFromDB(RecordTypes.RECORD_TYPE_DAILY); - InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(totalBaseBasal.getWindowToken(), 0); - } - } - }); - - loadDataFromDB(RecordTypes.RECORD_TYPE_DAILY); - } - - private void loadDataFromDB(byte type) { - historyList = MainApp.getDbHelper().getDanaRHistoryRecordsByType(type); - - runOnUiThread(new Runnable() { - @Override - public void run() { - cleanTable(tl); - cleanTable(ctl); - cleanTable(etl); - DateFormat df = new SimpleDateFormat("dd.MM."); - - if (TextUtils.isEmpty(TBB)) { - totalBaseBasal.setError("Please Enter Total Base Basal"); - return; - } else { - magicNumber = SafeParse.stringToDouble(TBB); - } - - magicNumber *= 2; - totalBaseBasal2.setText(decimalFormat.format(magicNumber)); - - int i = 0; - double sum = 0d; - double weighted03 = 0d; - double weighted05 = 0d; - double weighted07 = 0d; - - for (DanaRHistoryRecord record : historyList) { - double tdd = record.recordDailyBolus + record.recordDailyBasal; - - // Create the table row - TableRow tr = new TableRow(DanaRStatsActivity.this); - if (i % 2 != 0) tr.setBackgroundColor(Color.DKGRAY); - tr.setId(100 + i); - tr.setLayoutParams(new TableLayout.LayoutParams( - TableLayout.LayoutParams.MATCH_PARENT, - TableLayout.LayoutParams.WRAP_CONTENT)); - - // Here create the TextView dynamically - TextView labelDATE = new TextView(DanaRStatsActivity.this); - labelDATE.setId(200 + i); - labelDATE.setText(df.format(new Date(record.recordDate))); - labelDATE.setTextColor(Color.WHITE); - tr.addView(labelDATE); - - TextView labelBASAL = new TextView(DanaRStatsActivity.this); - labelBASAL.setId(300 + i); - labelBASAL.setText(DecimalFormatter.to2Decimal(record.recordDailyBasal) + " U"); - labelBASAL.setTextColor(Color.WHITE); - tr.addView(labelBASAL); - - TextView labelBOLUS = new TextView(DanaRStatsActivity.this); - labelBOLUS.setId(400 + i); - labelBOLUS.setText(DecimalFormatter.to2Decimal(record.recordDailyBolus) + " U"); - labelBOLUS.setTextColor(Color.WHITE); - tr.addView(labelBOLUS); - - TextView labelTDD = new TextView(DanaRStatsActivity.this); - labelTDD.setId(500 + i); - labelTDD.setText(DecimalFormatter.to2Decimal(tdd) + " U"); - labelTDD.setTextColor(Color.WHITE); - tr.addView(labelTDD); - - TextView labelRATIO = new TextView(DanaRStatsActivity.this); - labelRATIO.setId(600 + i); - labelRATIO.setText(Math.round(100 * tdd / magicNumber) + " %"); - labelRATIO.setTextColor(Color.WHITE); - tr.addView(labelRATIO); - - // add stats rows to tables - tl.addView(tr, new TableLayout.LayoutParams( - TableLayout.LayoutParams.MATCH_PARENT, - TableLayout.LayoutParams.WRAP_CONTENT)); - - sum = sum + tdd; - i++; - - // Create the cumtable row - TableRow ctr = new TableRow(DanaRStatsActivity.this); - if (i % 2 == 0) ctr.setBackgroundColor(Color.DKGRAY); - ctr.setId(700 + i); - ctr.setLayoutParams(new TableLayout.LayoutParams( - TableLayout.LayoutParams.MATCH_PARENT, - TableLayout.LayoutParams.WRAP_CONTENT)); - - // Here create the TextView dynamically - TextView labelDAYS = new TextView(DanaRStatsActivity.this); - labelDAYS.setId(800 + i); - labelDAYS.setText("" + i); - labelDAYS.setTextColor(Color.WHITE); - ctr.addView(labelDAYS); - - TextView labelCUMTDD = new TextView(DanaRStatsActivity.this); - labelCUMTDD.setId(900 + i); - labelCUMTDD.setText(DecimalFormatter.to2Decimal(sum / i) + " U"); - labelCUMTDD.setTextColor(Color.WHITE); - ctr.addView(labelCUMTDD); - - TextView labelCUMRATIO = new TextView(DanaRStatsActivity.this); - labelCUMRATIO.setId(1000 + i); - labelCUMRATIO.setText(Math.round(100 * sum / i / magicNumber) + " %"); - labelCUMRATIO.setTextColor(Color.WHITE); - ctr.addView(labelCUMRATIO); - - // add cummulative rows to tables - ctl.addView(ctr, new TableLayout.LayoutParams( - TableLayout.LayoutParams.MATCH_PARENT, - TableLayout.LayoutParams.WRAP_CONTENT)); - } - - if (historyList.size() < 3 || !(df.format(new Date(historyList.get(0).recordDate)).equals(df.format(new Date(System.currentTimeMillis() - 1000 * 60 * 60 * 24))))) { - statsMessage.setVisibility(View.VISIBLE); - statsMessage.setText(getString(R.string.danar_stats_olddata_Message)); - - } else { - tl.setBackgroundColor(Color.TRANSPARENT); - } - - Collections.reverse(historyList); - - i = 0; - - for (DanaRHistoryRecord record : historyList) { - double tdd = record.recordDailyBolus + record.recordDailyBasal; - if (i == 0) { - weighted03 = tdd; - weighted05 = tdd; - weighted07 = tdd; - - } else { - weighted07 = (weighted07 * 0.3 + tdd * 0.7); - weighted05 = (weighted05 * 0.5 + tdd * 0.5); - weighted03 = (weighted03 * 0.7 + tdd * 0.3); - } - i++; - } - - // Create the exptable row - TableRow etr = new TableRow(DanaRStatsActivity.this); - if (i % 2 != 0) etr.setBackgroundColor(Color.DKGRAY); - etr.setId(1100 + i); - etr.setLayoutParams(new TableLayout.LayoutParams( - TableLayout.LayoutParams.MATCH_PARENT, - TableLayout.LayoutParams.WRAP_CONTENT)); - - // Here create the TextView dynamically - TextView labelWEIGHT = new TextView(DanaRStatsActivity.this); - labelWEIGHT.setId(1200 + i); - labelWEIGHT.setText("0.3\n" + "0.5\n" + "0.7"); - labelWEIGHT.setTextColor(Color.WHITE); - etr.addView(labelWEIGHT); - - TextView labelEXPTDD = new TextView(DanaRStatsActivity.this); - labelEXPTDD.setId(1300 + i); - labelEXPTDD.setText(DecimalFormatter.to2Decimal(weighted03) - + " U\n" + DecimalFormatter.to2Decimal(weighted05) - + " U\n" + DecimalFormatter.to2Decimal(weighted07) + " U"); - labelEXPTDD.setTextColor(Color.WHITE); - etr.addView(labelEXPTDD); - - TextView labelEXPRATIO = new TextView(DanaRStatsActivity.this); - labelEXPRATIO.setId(1400 + i); - labelEXPRATIO.setText(Math.round(100 * weighted03 / magicNumber) + " %\n" - + Math.round(100 * weighted05 / magicNumber) + " %\n" - + Math.round(100 * weighted07 / magicNumber) + " %"); - labelEXPRATIO.setTextColor(Color.WHITE); - etr.addView(labelEXPRATIO); - - // add exponentail rows to tables - etl.addView(etr, new TableLayout.LayoutParams( - TableLayout.LayoutParams.MATCH_PARENT, - TableLayout.LayoutParams.WRAP_CONTENT)); - } - }); - } - - private void cleanTable(TableLayout table) { - int childCount = table.getChildCount(); - // Remove all rows except the first one - if (childCount > 1) { - table.removeViews(1, childCount - 1); - } - } - - @Subscribe - public void onStatusEvent(final EventDanaRSyncStatus s) { - log.debug("EventDanaRSyncStatus: " + s.message); - runOnUiThread( - new Runnable() { - @Override - public void run() { - statusView.setText(s.message); - } - }); - } - - @Subscribe - public void onStatusEvent(final EventPumpStatusChanged c) { - runOnUiThread( - new Runnable() { - @Override - public void run() { - statusView.setText(c.textStatus()); - } - } - ); - } -} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/utils/TimeListEdit.java b/app/src/main/java/info/nightscout/utils/TimeListEdit.java index ce1f604060..2ad591b1ef 100644 --- a/app/src/main/java/info/nightscout/utils/TimeListEdit.java +++ b/app/src/main/java/info/nightscout/utils/TimeListEdit.java @@ -25,6 +25,7 @@ import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; @@ -285,12 +286,19 @@ public class TimeListEdit { public void editItem(int index, int timeAsSeconds, double value1, double value2) { try { + String time; + int hour = timeAsSeconds / 60 / 60; + DecimalFormat df = new DecimalFormat("00"); + time = df.format(hour) + ":00"; + JSONObject newObject1 = new JSONObject(); + newObject1.put("time", time); newObject1.put("timeAsSeconds", timeAsSeconds); newObject1.put("value", value1); data1.put(index, newObject1); if (data2 != null) { JSONObject newObject2 = new JSONObject(); + newObject1.put("time", time); newObject2.put("timeAsSeconds", timeAsSeconds); newObject2.put("value", value2); data2.put(index, newObject2); diff --git a/app/src/main/res/layout/danar_historyactivity.xml b/app/src/main/res/layout/danar_historyactivity.xml index e3d8ea2f2d..805cba6e1c 100644 --- a/app/src/main/res/layout/danar_historyactivity.xml +++ b/app/src/main/res/layout/danar_historyactivity.xml @@ -5,7 +5,7 @@ android:layout_height="fill_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingTop="@dimen/activity_vertical_margin" - tools:context=".plugins.PumpDanaR.History.DanaRHistoryActivity"> + tools:context=".plugins.PumpDanaR.activities.DanaRHistoryActivity"> + tools:context=".plugins.PumpDanaR.activities.DanaRHistoryActivity"> Date: Tue, 13 Jun 2017 12:08:30 +0200 Subject: [PATCH 010/212] optimize getUnits() & detecting faked extended --- .../androidaps/data/ProfileStore.java | 10 +++++++- .../nightscout/androidaps/db/BgReading.java | 4 +-- .../androidaps/db/CareportalEvent.java | 23 ++++++++--------- .../androidaps/db/DatabaseHelper.java | 17 ++++++++++--- .../interfaces/ProfileInterface.java | 1 + .../Dialogs/NewNSTreatmentDialog.java | 1 - .../ConfigBuilder/ConfigBuilderPlugin.java | 4 +++ .../IobCobCalculatorPlugin.java | 2 ++ .../services/NSClientService.java | 2 ++ .../Overview/Dialogs/CalibrationDialog.java | 10 +++----- .../plugins/Overview/OverviewFragment.java | 25 +++++++++---------- .../PersistentNotificationPlugin.java | 10 ++++---- .../CircadianPercentageProfilePlugin.java | 5 ++++ .../ProfileLocal/LocalProfilePlugin.java | 5 ++++ .../plugins/ProfileNS/NSProfilePlugin.java | 5 ++++ .../ProfileSimple/SimpleProfilePlugin.java | 5 ++++ .../plugins/PumpDanaR/DanaRPlugin.java | 5 ++++ .../plugins/PumpDanaR/DanaRPump.java | 4 +++ .../activities/DanaRNSHistorySync.java | 8 +----- .../PumpDanaRKorean/DanaRKoreanPlugin.java | 5 ++++ .../plugins/PumpDanaRv2/DanaRv2Plugin.java | 5 ++++ .../PumpVirtual/VirtualPumpPlugin.java | 17 ++++++++++++- .../SmsCommunicatorPlugin.java | 3 +-- .../TreatmentsTempTargetFragment.java | 7 +++--- .../wearintegration/WatchUpdaterService.java | 11 ++++---- .../nightscout/utils/XdripCalibrations.java | 4 +-- 26 files changed, 129 insertions(+), 69 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/data/ProfileStore.java b/app/src/main/java/info/nightscout/androidaps/data/ProfileStore.java index 466351076b..bcc778485b 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/ProfileStore.java +++ b/app/src/main/java/info/nightscout/androidaps/data/ProfileStore.java @@ -10,6 +10,8 @@ import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Iterator; +import info.nightscout.androidaps.Constants; + /** * Created by mike on 01.06.2017. */ @@ -17,9 +19,11 @@ import java.util.Iterator; public class ProfileStore { private static Logger log = LoggerFactory.getLogger(ProfileStore.class); private JSONObject json = null; + private String units = Constants.MGDL; public ProfileStore(JSONObject json) { this.json = json; + getDefaultProfile(); // initialize units } public JSONObject getData() { @@ -33,10 +37,10 @@ public class ProfileStore { String defaultProfileName = json.getString("defaultProfile"); JSONObject store = json.getJSONObject("store"); if (store.has(defaultProfileName)) { - String units = null; if (store.has("units")) units = store.getString("units"); profile = new Profile(store.getJSONObject(defaultProfileName), units); + units = profile.getUnits(); } } catch (JSONException e) { e.printStackTrace(); @@ -59,6 +63,10 @@ public class ProfileStore { return defaultProfileName; } + public String getUnits() { + return units; + } + @Nullable public Profile getSpecificProfile(String profileName) { Profile profile = null; diff --git a/app/src/main/java/info/nightscout/androidaps/db/BgReading.java b/app/src/main/java/info/nightscout/androidaps/db/BgReading.java index fa5cd00c54..da129b41cc 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/BgReading.java +++ b/app/src/main/java/info/nightscout/androidaps/db/BgReading.java @@ -158,7 +158,7 @@ public class BgReading implements DataPointWithLabelInterface { @Override public double getY() { - String units = MainApp.getConfigBuilder().getProfile().getUnits(); + String units = MainApp.getConfigBuilder().getProfileUnits(); return valueToUnits(units); } @@ -190,7 +190,7 @@ public class BgReading implements DataPointWithLabelInterface { @Override public int getColor() { - String units = MainApp.getConfigBuilder().getProfile().getUnits(); + String units = MainApp.getConfigBuilder().getProfileUnits(); Double lowLine = SP.getDouble("low_mark", 0d); Double highLine = SP.getDouble("high_mark", 0d); if (lowLine < 1) { diff --git a/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java b/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java index 17807443c3..aec38ea297 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java +++ b/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java @@ -89,7 +89,7 @@ public class CareportalEvent implements DataPointWithLabelInterface { } public String age() { - Map diff = computeDiff(date, new Date().getTime()); + Map diff = computeDiff(date, new Date().getTime()); return diff.get(TimeUnit.DAYS) + " " + MainApp.sResources.getString(R.string.days) + " " + diff.get(TimeUnit.HOURS) + " " + MainApp.sResources.getString(R.string.hours); } @@ -105,17 +105,17 @@ public class CareportalEvent implements DataPointWithLabelInterface { } //Map:{DAYS=1, HOURS=3, MINUTES=46, SECONDS=40, MILLISECONDS=0, MICROSECONDS=0, NANOSECONDS=0} - public static Map computeDiff(long date1, long date2) { + public static Map computeDiff(long date1, long date2) { long diffInMillies = date2 - date1; List units = new ArrayList(EnumSet.allOf(TimeUnit.class)); Collections.reverse(units); - Map result = new LinkedHashMap(); + Map result = new LinkedHashMap(); long milliesRest = diffInMillies; - for ( TimeUnit unit : units ) { - long diff = unit.convert(milliesRest,TimeUnit.MILLISECONDS); + for (TimeUnit unit : units) { + long diff = unit.convert(milliesRest, TimeUnit.MILLISECONDS); long diffInMilliesForUnit = unit.toMillis(diff); milliesRest = milliesRest - diffInMilliesForUnit; - result.put(unit,diff); + result.put(unit, diff); } return result; } @@ -131,7 +131,7 @@ public class CareportalEvent implements DataPointWithLabelInterface { @Override public double getY() { - Profile profile = MainApp.getConfigBuilder().getProfile(); + String units = MainApp.getConfigBuilder().getProfileUnits(); if (eventType.equals(MBG)) { double mbg = 0d; try { @@ -140,13 +140,10 @@ public class CareportalEvent implements DataPointWithLabelInterface { } catch (JSONException e) { e.printStackTrace(); } - if (profile != null) - return profile.fromMgdlToUnits(mbg, profile.getUnits()); - return 0d; + return Profile.fromMgdlToUnits(mbg, units); } double glucose = 0d; - String units = Constants.MGDL; try { JSONObject object = new JSONObject(json); if (object.has("glucose")) { @@ -156,7 +153,7 @@ public class CareportalEvent implements DataPointWithLabelInterface { } catch (JSONException e) { e.printStackTrace(); } - if (profile != null && glucose != 0d) { + if (glucose != 0d) { double mmol = 0d; double mgdl = 0; if (units.equals(Constants.MGDL)) { @@ -167,7 +164,7 @@ public class CareportalEvent implements DataPointWithLabelInterface { mmol = glucose; mgdl = glucose * Constants.MMOLL_TO_MGDL; } - return profile.toUnits(mgdl, mmol, profile.getUnits()); + return Profile.toUnits(mgdl, mmol, units); } return yValue; diff --git a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java index 832ddbca47..e98946c8da 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java +++ b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java @@ -204,6 +204,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { } catch (SQLException e) { e.printStackTrace(); } + VirtualPumpPlugin.setFakingStatus(true); scheduleBgChange(); // trigger refresh scheduleTemporaryBasalChange(); scheduleTreatmentChange(); @@ -251,6 +252,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { } catch (SQLException e) { e.printStackTrace(); } + VirtualPumpPlugin.setFakingStatus(false); scheduleTemporaryBasalChange(); } @@ -817,8 +819,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { public void createTemptargetFromJsonIfNotExists(JSONObject trJson) { try { - Profile profile = MainApp.getConfigBuilder().getProfile(); - String units = profile.getUnits(); + String units = MainApp.getConfigBuilder().getProfileUnits(); TempTarget tempTarget = new TempTarget(); tempTarget.date = trJson.getLong("mills"); tempTarget.durationInMinutes = trJson.getInt("duration"); @@ -1070,7 +1071,11 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { extendedBolus.durationInMinutes = trJson.getInt("duration"); extendedBolus.insulin = trJson.getDouble("originalExtendedAmount"); extendedBolus._id = trJson.getString("_id"); - VirtualPumpPlugin.fromNSAreCommingFakedExtendedBoluses = true; + if (!VirtualPumpPlugin.getFakingStatus()) { + VirtualPumpPlugin.setFakingStatus(true); + updateEarliestDataChange(0); + scheduleTemporaryBasalChange(); + } createOrUpdate(extendedBolus); } else if (trJson.has("isFakedTempBasal")) { // extended bolus end uploaded as temp basal end ExtendedBolus extendedBolus = new ExtendedBolus(); @@ -1080,7 +1085,11 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { extendedBolus.durationInMinutes = 0; extendedBolus.insulin = 0; extendedBolus._id = trJson.getString("_id"); - VirtualPumpPlugin.fromNSAreCommingFakedExtendedBoluses = true; + if (!VirtualPumpPlugin.getFakingStatus()) { + VirtualPumpPlugin.setFakingStatus(true); + updateEarliestDataChange(0); + scheduleTemporaryBasalChange(); + } createOrUpdate(extendedBolus); } else { TemporaryBasal tempBasal = new TemporaryBasal(); diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/ProfileInterface.java b/app/src/main/java/info/nightscout/androidaps/interfaces/ProfileInterface.java index 64fffa3e57..e3b368fe86 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/ProfileInterface.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/ProfileInterface.java @@ -10,5 +10,6 @@ import info.nightscout.androidaps.data.ProfileStore; public interface ProfileInterface { @Nullable ProfileStore getProfile(); + String getUnits(); String getProfileName(); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Careportal/Dialogs/NewNSTreatmentDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/Careportal/Dialogs/NewNSTreatmentDialog.java index dad922a5c5..635f689fb6 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Careportal/Dialogs/NewNSTreatmentDialog.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Careportal/Dialogs/NewNSTreatmentDialog.java @@ -231,7 +231,6 @@ public class NewNSTreatmentDialog extends DialogFragment implements View.OnClick profile = MainApp.getConfigBuilder().getProfile(); profileStore = MainApp.getConfigBuilder().getActiveProfileInterface().getProfile(); ArrayList profileList; - units = Constants.MGDL; units = profile.getUnits(); profileList = profileStore.getProfileList(); ArrayAdapter adapter = new ArrayAdapter(getContext(), diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java index 992fcc0244..c5d27a7e11 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java @@ -1008,6 +1008,10 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain return getProfile(new Date().getTime()); } + public String getProfileUnits() { + return activeProfile.getUnits(); + } + public Profile getProfile(long time) { //log.debug("Profile for: " + new Date(time).toLocaleString() + " : " + getProfileName(time)); ProfileSwitch profileSwitch = getProfileSwitchFromHistory(time); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java index 417322a6b4..4b7060e52f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java @@ -345,6 +345,7 @@ public class IobCobCalculatorPlugin implements PluginBase { return iobTotal; } + @Nullable private static Long findPreviousTimeFromBucketedData(long time) { if (bucketed_data == null) return null; @@ -413,6 +414,7 @@ public class IobCobCalculatorPlugin implements PluginBase { Profile profile = MainApp.getConfigBuilder().getProfile(); // predict IOB out to DIA plus 30m long time = new Date().getTime(); + time = roundUpTime(time); int len = (int) ((profile.getDia() * 60 + 30) / 5); IobTotal[] array = new IobTotal[len]; int pos = 0; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/services/NSClientService.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/services/NSClientService.java index c4316f0118..6e4038bc37 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/services/NSClientService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/services/NSClientService.java @@ -156,6 +156,7 @@ public class NSClientService extends Service { ev.isChanged(R.string.key_nsclientinternal_api_secret) || ev.isChanged(R.string.key_nsclientinternal_paused) ) { + latestDateInReceivedData = 0; destroy(); initialize(); } @@ -164,6 +165,7 @@ public class NSClientService extends Service { @Subscribe public void onStatusEvent(EventConfigBuilderChange ev) { if (nsEnabled != MainApp.getSpecificPlugin(NSClientInternalPlugin.class).isEnabled(PluginBase.GENERAL)) { + latestDateInReceivedData = 0; destroy(); initialize(); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/CalibrationDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/CalibrationDialog.java index d1b25fff3a..aa1d51f4dd 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/CalibrationDialog.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/CalibrationDialog.java @@ -60,14 +60,10 @@ public class CalibrationDialog extends DialogFragment implements View.OnClickLis okButton = (Button) view.findViewById(R.id.overview_calibration_okbutton); okButton.setOnClickListener(this); - Profile profile = MainApp.getConfigBuilder().getProfile(); - Double bg = profile != null ? Profile.fromMgdlToUnits(GlucoseStatus.getGlucoseStatusData() != null ? GlucoseStatus.getGlucoseStatusData().glucose : 0d, profile.getUnits()) : 0d; + String units = MainApp.getConfigBuilder().getProfileUnits(); + Double bg = Profile.fromMgdlToUnits(GlucoseStatus.getGlucoseStatusData() != null ? GlucoseStatus.getGlucoseStatusData().glucose : 0d, units); - String units = Constants.MGDL; - if (profile != null) - units = profile.getUnits(); - - if (units.equals(Constants.MMOL)) + if (units.equals(Constants.MMOL)) bgText = new PlusMinusEditText(view, R.id.overview_calibration_bg, R.id.overview_calibration_bg_plus, R.id.overview_calibration_bg_minus, bg, 0d, 30d, 0.1d, new DecimalFormat("0.0"), false); else bgText = new PlusMinusEditText(view, R.id.overview_calibration_bg, R.id.overview_calibration_bg_plus, R.id.overview_calibration_bg_minus, bg, 0d, 500d, 1d, new DecimalFormat("0"), false); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java index 7ebdcc60f2..48550727c3 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java @@ -916,6 +916,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, PumpInterface pump = MainApp.getConfigBuilder(); Profile profile = MainApp.getConfigBuilder().getProfile(); + String units = profile.getUnits(); // open loop mode final LoopPlugin.LastRun finalLastRun = LoopPlugin.lastRun; @@ -958,13 +959,13 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, tempTargetView.setBackgroundColor(MainApp.sResources.getColor(R.color.tempTargetBackground)); tempTargetView.setVisibility(View.VISIBLE); if (tempTarget.low == tempTarget.high) - tempTargetView.setText(Profile.toUnitsString(tempTarget.low, Profile.fromMgdlToUnits(tempTarget.low, profile.getUnits()), profile.getUnits())); + tempTargetView.setText(Profile.toUnitsString(tempTarget.low, Profile.fromMgdlToUnits(tempTarget.low, units), units)); else - tempTargetView.setText(Profile.toUnitsString(tempTarget.low, Profile.fromMgdlToUnits(tempTarget.low, profile.getUnits()), profile.getUnits()) + " - " + Profile.toUnitsString(tempTarget.high, Profile.fromMgdlToUnits(tempTarget.high, profile.getUnits()), profile.getUnits())); + tempTargetView.setText(Profile.toUnitsString(tempTarget.low, Profile.fromMgdlToUnits(tempTarget.low, units), units) + " - " + Profile.toUnitsString(tempTarget.high, Profile.fromMgdlToUnits(tempTarget.high, units), units)); } else { Double maxBgDefault = Constants.MAX_BG_DEFAULT_MGDL; Double minBgDefault = Constants.MIN_BG_DEFAULT_MGDL; - if (!profile.getUnits().equals(Constants.MGDL)) { + if (!units.equals(Constants.MGDL)) { maxBgDefault = Constants.MAX_BG_DEFAULT_MMOL; minBgDefault = Constants.MIN_BG_DEFAULT_MMOL; } @@ -1062,7 +1063,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, quickWizardButton.setVisibility(View.VISIBLE); String text = quickWizardEntry.buttonText() + "\n" + DecimalFormatter.to0Decimal(quickWizardEntry.carbs()) + "g"; BolusWizard wizard = new BolusWizard(); - wizard.doCalc(profile, quickWizardEntry.carbs(), 0d, lastBG.valueToUnits(profile.getUnits()), 0d, true, true, false, false); + wizard.doCalc(profile, quickWizardEntry.carbs(), 0d, lastBG.valueToUnits(units), 0d, true, true, false, false); text += " " + DecimalFormatter.to2Decimal(wizard.calculatedTotalInsulin) + "U"; quickWizardButton.setText(text); if (wizard.calculatedTotalInsulin <= 0) @@ -1079,8 +1080,6 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, treatmentButton.setVisibility(View.GONE); } - String units = profile.getUnits(); - Double lowLine = SP.getDouble("low_mark", 0d); Double highLine = SP.getDouble("high_mark", 0d); if (lowLine < 1) { @@ -1097,7 +1096,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, color = MainApp.sResources.getColor(R.color.low); else if (lastBG.valueToUnits(units) > highLine) color = MainApp.sResources.getColor(R.color.high); - bgView.setText(lastBG.valueToUnitsToString(profile.getUnits())); + bgView.setText(lastBG.valueToUnitsToString(units)); arrowView.setText(lastBG.directionToSymbol()); bgView.setTextColor(color); arrowView.setTextColor(color); @@ -1156,7 +1155,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, } // ****** GRAPH ******* - //log.debug("updateGUI checkpoint 1"); + log.debug("updateGUI checkpoint 1"); // allign to hours Calendar calendar = Calendar.getInstance(); @@ -1287,7 +1286,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, absoluteBasalsLineSeries.setCustomPaint(absolutePaint); } - //log.debug("updateGUI checkpoint 2"); + log.debug("updateGUI checkpoint 2"); // **** IOB COB DEV graph **** class DeviationDataPoint extends DataPoint { @@ -1405,12 +1404,12 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, } else { iobGraph.setVisibility(View.GONE); } - //log.debug("updateGUI checkpoint 3"); + log.debug("updateGUI checkpoint 3"); // remove old data from graph bgGraph.getSecondScale().getSeries().clear(); bgGraph.getSeries().clear(); - //log.debug("updateGUI checkpoint 4"); + log.debug("updateGUI checkpoint 4"); // **** Area **** DoubleDataPoint[] areaDataPoints = new DoubleDataPoint[]{ @@ -1586,11 +1585,11 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, public double getNearestBg(long date, List bgReadingsArray) { double bg = 0; - Profile profile = MainApp.getConfigBuilder().getProfile(); + String units = MainApp.getConfigBuilder().getProfileUnits(); for (int r = bgReadingsArray.size() - 1; r >= 0; r--) { BgReading reading = bgReadingsArray.get(r); if (reading.date > date) continue; - bg = Profile.fromMgdlToUnits(reading.value, profile.getUnits()); + bg = Profile.fromMgdlToUnits(reading.value, units); break; } return bg; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Persistentnotification/PersistentNotificationPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Persistentnotification/PersistentNotificationPlugin.java index 7e7f2ab7a2..1484eee396 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Persistentnotification/PersistentNotificationPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Persistentnotification/PersistentNotificationPlugin.java @@ -118,17 +118,17 @@ public class PersistentNotificationPlugin implements PluginBase { String line1 = ctx.getString(R.string.noprofile); - Profile profile = MainApp.getConfigBuilder().getProfile(); + String units = MainApp.getConfigBuilder().getProfileUnits(); BgReading lastBG = DatabaseHelper.lastBg(); GlucoseStatus glucoseStatus = GlucoseStatus.getGlucoseStatusData(); - if (profile != null && lastBG != null) { - line1 = lastBG.valueToUnitsToString(profile.getUnits()); + if (lastBG != null) { + line1 = lastBG.valueToUnitsToString(units); if (glucoseStatus != null) { - line1 += " Δ" + deltastring(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, profile.getUnits()) - + " avgΔ" + deltastring(glucoseStatus.avgdelta, glucoseStatus.avgdelta * Constants.MGDL_TO_MMOLL, profile.getUnits()); + line1 += " Δ" + deltastring(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units) + + " avgΔ" + deltastring(glucoseStatus.avgdelta, glucoseStatus.avgdelta * Constants.MGDL_TO_MMOLL, units); } else { line1 += " " + ctx.getString(R.string.old_data) + diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileCircadianPercentage/CircadianPercentageProfilePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileCircadianPercentage/CircadianPercentageProfilePlugin.java index 40fbbf9eb3..4137b0263b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileCircadianPercentage/CircadianPercentageProfilePlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileCircadianPercentage/CircadianPercentageProfilePlugin.java @@ -213,6 +213,11 @@ public class CircadianPercentageProfilePlugin implements PluginBase, ProfileInte return convertedProfile; } + @Override + public String getUnits() { + return mgdl ? Constants.MGDL : Constants.MMOL; + } + @Override public String getProfileName() { performLimitCheck(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileLocal/LocalProfilePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileLocal/LocalProfilePlugin.java index d1fab7cbcf..8aca0a8dbc 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileLocal/LocalProfilePlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileLocal/LocalProfilePlugin.java @@ -240,6 +240,11 @@ public class LocalProfilePlugin implements PluginBase, ProfileInterface { return convertedProfile; } + @Override + public String getUnits() { + return mgdl ? Constants.MGDL : Constants.MMOL; + } + @Override public String getProfileName() { return convertedProfileName; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileNS/NSProfilePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileNS/NSProfilePlugin.java index b310993584..68bd2c6a80 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileNS/NSProfilePlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileNS/NSProfilePlugin.java @@ -153,6 +153,11 @@ public class NSProfilePlugin implements PluginBase, ProfileInterface { return profile; } + @Override + public String getUnits() { + return profile.getUnits(); + } + @Override public String getProfileName() { return profile.getDefaultProfileName(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileSimple/SimpleProfilePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileSimple/SimpleProfilePlugin.java index de7c903df3..e02f4918c2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ProfileSimple/SimpleProfilePlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ProfileSimple/SimpleProfilePlugin.java @@ -201,6 +201,11 @@ public class SimpleProfilePlugin implements PluginBase, ProfileInterface { return convertedProfile; } + @Override + public String getUnits() { + return mgdl ? Constants.MGDL : Constants.MMOL; + } + @Override public String getProfileName() { return "SimpleProfile"; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java index 1188028492..0707cc842c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java @@ -798,6 +798,11 @@ public class DanaRPlugin implements PluginBase, PumpInterface, DanaRInterface, C return pump.createConvertedProfile(); } + @Override + public String getUnits() { + return pump.getUnits(); + } + @Override public String getProfileName() { return pump.createConvertedProfileName(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPump.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPump.java index af36e4059f..55fdc7ad04 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPump.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPump.java @@ -132,6 +132,10 @@ public class DanaRPump { public double maxBolus; public double maxBasal; + public String getUnits() { + return units == UNITS_MGDL ? Constants.MGDL : Constants.MMOL; + } + public ProfileStore createConvertedProfile() { JSONObject json = new JSONObject(); JSONObject store = new JSONObject(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/activities/DanaRNSHistorySync.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/activities/DanaRNSHistorySync.java index ef5dc16b43..0668a74c1d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/activities/DanaRNSHistorySync.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/activities/DanaRNSHistorySync.java @@ -46,12 +46,6 @@ public class DanaRNSHistorySync { public void sync(int what) { try { - ConfigBuilderPlugin ConfigBuilderPlugin = MainApp.getConfigBuilder(); - Profile profile = MainApp.getConfigBuilder().getProfile(); - if (profile == null) { - ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), MainApp.sResources.getString(R.string.noprofile)); - return; - } Calendar cal = Calendar.getInstance(); long records = historyRecords.size(); long processing = 0; @@ -179,7 +173,7 @@ public class DanaRNSHistorySync { log.debug("Syncing glucose record " + record.recordValue + " " + DateUtil.toISOString(record.recordDate)); nsrec.put(DANARSIGNATURE, record.bytes); nsrec.put("eventType", "BG Check"); - nsrec.put("glucose", Profile.fromMgdlToUnits(record.recordValue, profile.getUnits())); + nsrec.put("glucose", Profile.fromMgdlToUnits(record.recordValue, MainApp.getConfigBuilder().getProfileUnits())); nsrec.put("glucoseType", "Finger"); nsrec.put("created_at", DateUtil.toISOString(record.recordDate)); nsrec.put("enteredBy", "openaps://" + MainApp.sResources.getString(R.string.app_name)); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java index 6d72fe9684..8885056f5b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java @@ -802,6 +802,11 @@ public class DanaRKoreanPlugin implements PluginBase, PumpInterface, DanaRInterf return pump.createConvertedProfile(); } + @Override + public String getUnits() { + return pump.getUnits(); + } + @Override public String getProfileName() { return pump.createConvertedProfileName(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java index c79100ff81..4d332824aa 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java @@ -715,6 +715,11 @@ public class DanaRv2Plugin implements PluginBase, PumpInterface, DanaRInterface, return pump.createConvertedProfile(); } + @Override + public String getUnits() { + return pump.getUnits(); + } + @Override public String getProfileName() { return pump.createConvertedProfileName(); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpVirtual/VirtualPumpPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpVirtual/VirtualPumpPlugin.java index b89e050fe5..cdf5ff0c7e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpVirtual/VirtualPumpPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpVirtual/VirtualPumpPlugin.java @@ -28,6 +28,7 @@ import info.nightscout.androidaps.plugins.Overview.events.EventOverviewBolusProg import info.nightscout.androidaps.plugins.PumpVirtual.events.EventVirtualPumpUpdateGui; import info.nightscout.utils.DateUtil; import info.nightscout.utils.NSUpload; +import info.nightscout.utils.SP; /** * Created by mike on 05.08.2016. @@ -45,12 +46,26 @@ public class VirtualPumpPlugin implements PluginBase, PumpInterface { boolean fragmentEnabled = true; boolean fragmentVisible = true; - public static boolean fromNSAreCommingFakedExtendedBoluses = false; + private static boolean fromNSAreCommingFakedExtendedBoluses = false; PumpDescription pumpDescription = new PumpDescription(); + static void loadFakingStatus() { + fromNSAreCommingFakedExtendedBoluses = SP.getBoolean("fromNSAreCommingFakedExtendedBoluses", false); + } + + public static void setFakingStatus(boolean newStatus) { + fromNSAreCommingFakedExtendedBoluses = newStatus; + SP.putBoolean("fromNSAreCommingFakedExtendedBoluses", fromNSAreCommingFakedExtendedBoluses); + } + + public static boolean getFakingStatus() { + return fromNSAreCommingFakedExtendedBoluses; + } + static VirtualPumpPlugin instance = null; public static VirtualPumpPlugin getInstance() { + loadFakingStatus(); if (instance == null) instance = new VirtualPumpPlugin(); return instance; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/SmsCommunicator/SmsCommunicatorPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/SmsCommunicator/SmsCommunicatorPlugin.java index 4e81aa49d9..280bb21912 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/SmsCommunicator/SmsCommunicatorPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/SmsCommunicator/SmsCommunicatorPlugin.java @@ -238,8 +238,7 @@ public class SmsCommunicatorPlugin implements PluginBase { BgReading actualBG = DatabaseHelper.actualBg(); BgReading lastBG = DatabaseHelper.lastBg(); - Profile profile = MainApp.getConfigBuilder().getProfile(); - String units = profile.getUnits(); + String units = MainApp.getConfigBuilder().getProfileUnits(); if (actualBG != null) { reply = MainApp.sResources.getString(R.string.sms_actualbg) + " " + actualBG.valueToUnitsToString(units) + ", "; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsTempTargetFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsTempTargetFragment.java index 9b2603ef4f..7a74ca4a86 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsTempTargetFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/fragments/TreatmentsTempTargetFragment.java @@ -62,16 +62,15 @@ public class TreatmentsTempTargetFragment extends Fragment implements View.OnCli @Override public void onBindViewHolder(TempTargetsViewHolder holder, int position) { - Profile profile = MainApp.getConfigBuilder().getProfile(); - if (profile == null) return; + String units = MainApp.getConfigBuilder().getProfileUnits(); TempTarget tempTarget = tempTargetList.getReversed(position); holder.ph.setVisibility(tempTarget.source == Source.PUMP ? View.VISIBLE : View.GONE); holder.ns.setVisibility(tempTarget._id != null ? View.VISIBLE : View.GONE); if (!tempTarget.isEndingEvent()) { holder.date.setText(DateUtil.dateAndTimeString(tempTarget.date) + " - " + DateUtil.timeString(tempTarget.originalEnd())); holder.duration.setText(DecimalFormatter.to0Decimal(tempTarget.durationInMinutes) + " min"); - holder.low.setText(tempTarget.lowValueToUnitsToString(profile.getUnits())); - holder.high.setText(tempTarget.highValueToUnitsToString(profile.getUnits())); + holder.low.setText(tempTarget.lowValueToUnitsToString(units)); + holder.high.setText(tempTarget.highValueToUnitsToString(units)); holder.reason.setText(tempTarget.reason); } else { holder.date.setText(DateUtil.dateAndTimeString(tempTarget.date)); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Wear/wearintegration/WatchUpdaterService.java b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/wearintegration/WatchUpdaterService.java index d58742d32a..6fa3a54370 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Wear/wearintegration/WatchUpdaterService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Wear/wearintegration/WatchUpdaterService.java @@ -209,14 +209,13 @@ public class WatchUpdaterService extends WearableListenerService implements } private DataMap dataMapSingleBG(BgReading lastBG, GlucoseStatus glucoseStatus) { - Profile profile = MainApp.getConfigBuilder().getProfile(); - if (profile == null) return null; + String units = MainApp.getConfigBuilder().getProfileUnits(); Double lowLine = SafeParse.stringToDouble(mPrefs.getString("low_mark", "0")); Double highLine = SafeParse.stringToDouble(mPrefs.getString("high_mark", "0")); //convert to mg/dl - if (!profile.getUnits().equals(Constants.MGDL)) { + if (!units.equals(Constants.MGDL)) { lowLine *= Constants.MMOLL_TO_MGDL; highLine *= Constants.MMOLL_TO_MGDL; @@ -239,7 +238,7 @@ public class WatchUpdaterService extends WearableListenerService implements DataMap dataMap = new DataMap(); int battery = getBatteryLevel(getApplicationContext()); - dataMap.putString("sgvString", lastBG.valueToUnitsToString(profile.getUnits())); + dataMap.putString("sgvString", lastBG.valueToUnitsToString(units)); dataMap.putDouble("timestamp", lastBG.date); if (glucoseStatus == null) { dataMap.putString("slopeArrow", ""); @@ -247,8 +246,8 @@ public class WatchUpdaterService extends WearableListenerService implements dataMap.putString("avgDelta", ""); } else { dataMap.putString("slopeArrow", slopeArrow(glucoseStatus.delta)); - dataMap.putString("delta", deltastring(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, profile.getUnits())); - dataMap.putString("avgDelta", deltastring(glucoseStatus.avgdelta, glucoseStatus.avgdelta * Constants.MGDL_TO_MMOLL, profile.getUnits())); + dataMap.putString("delta", deltastring(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units)); + dataMap.putString("avgDelta", deltastring(glucoseStatus.avgdelta, glucoseStatus.avgdelta * Constants.MGDL_TO_MMOLL, units)); } dataMap.putString("battery", "" + battery); dataMap.putLong("sgvLevel", sgvLevel); diff --git a/app/src/main/java/info/nightscout/utils/XdripCalibrations.java b/app/src/main/java/info/nightscout/utils/XdripCalibrations.java index 2bc60d3693..29de7d9ee3 100644 --- a/app/src/main/java/info/nightscout/utils/XdripCalibrations.java +++ b/app/src/main/java/info/nightscout/utils/XdripCalibrations.java @@ -44,12 +44,10 @@ public class XdripCalibrations { } public static boolean sendIntent(Double bg) { - final Profile profile = MainApp.getConfigBuilder().getProfile(); - Context context = MainApp.instance().getApplicationContext(); Bundle bundle = new Bundle(); bundle.putDouble("glucose_number", bg); - bundle.putString("units", profile.getUnits().equals(Constants.MGDL) ? "mgdl" : "mmol"); + bundle.putString("units", MainApp.getConfigBuilder().getProfileUnits().equals(Constants.MGDL) ? "mgdl" : "mmol"); bundle.putLong("timestamp", new Date().getTime()); Intent intent = new Intent(Intents.ACTION_REMOTE_CALIBRATION); intent.putExtras(bundle); From 2a1fe205eef55c6e4730d05e598294387fff22b1 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Tue, 13 Jun 2017 12:17:54 +0200 Subject: [PATCH 011/212] optimize nsclient logging --- .../plugins/NSClientInternal/NSClientInternalPlugin.java | 6 +++--- .../NSClientInternal/events/EventNSClientNewLog.java | 5 +++++ .../androidaps/plugins/Overview/OverviewFragment.java | 8 ++++---- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/NSClientInternalPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/NSClientInternalPlugin.java index a37d5442ca..4b3e059b6e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/NSClientInternalPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/NSClientInternalPlugin.java @@ -189,11 +189,11 @@ public class NSClientInternalPlugin implements PluginBase { private void updateLog() { try { - Spanned newTextLog = Html.fromHtml(""); + String newTextLog = ""; for (EventNSClientNewLog log : listLog) { - newTextLog = (Spanned) TextUtils.concat(newTextLog, log.toHtml()); + newTextLog = newTextLog + log.toPreparedHtml(); } - textLog = newTextLog; + textLog = Html.fromHtml(newTextLog); MainApp.bus().post(new EventNSClientUpdateGUI()); } catch (OutOfMemoryError e) { ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), "Out of memory!\nStop using this phone !!!", R.raw.error); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/events/EventNSClientNewLog.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/events/EventNSClientNewLog.java index 7f576fde7e..3af3333d65 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/events/EventNSClientNewLog.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/events/EventNSClientNewLog.java @@ -27,4 +27,9 @@ public class EventNSClientNewLog { Spanned line = Html.fromHtml(timeFormat.format(date) + " " + action + " " + logText + "
"); return line; } + + public String toPreparedHtml() { + SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss"); + return timeFormat.format(date) + " " + action + " " + logText + "
"; + } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java index 48550727c3..cea7d639b6 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java @@ -1155,7 +1155,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, } // ****** GRAPH ******* - log.debug("updateGUI checkpoint 1"); + //log.debug("updateGUI checkpoint 1"); // allign to hours Calendar calendar = Calendar.getInstance(); @@ -1286,7 +1286,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, absoluteBasalsLineSeries.setCustomPaint(absolutePaint); } - log.debug("updateGUI checkpoint 2"); + //log.debug("updateGUI checkpoint 2"); // **** IOB COB DEV graph **** class DeviationDataPoint extends DataPoint { @@ -1404,12 +1404,12 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, } else { iobGraph.setVisibility(View.GONE); } - log.debug("updateGUI checkpoint 3"); + //log.debug("updateGUI checkpoint 3"); // remove old data from graph bgGraph.getSecondScale().getSeries().clear(); bgGraph.getSeries().clear(); - log.debug("updateGUI checkpoint 4"); + //log.debug("updateGUI checkpoint 4"); // **** Area **** DoubleDataPoint[] areaDataPoints = new DoubleDataPoint[]{ From d3de51cc8802e113b4baa3a7d7efda6a3a33342f Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Tue, 13 Jun 2017 15:06:41 +0200 Subject: [PATCH 012/212] optimize dia --- .../nightscout/androidaps/data/Profile.java | 47 +++++++++++++++++-- .../androidaps/db/ExtendedBolus.java | 6 +-- .../androidaps/db/TemporaryBasal.java | 8 ++-- .../nightscout/androidaps/db/Treatment.java | 5 ++ .../InsulinFastacting/ActivityGraph.java | 2 +- .../NSClientInternalPlugin.java | 6 +-- .../events/EventNSClientNewLog.java | 17 +++---- 7 files changed, 69 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/data/Profile.java b/app/src/main/java/info/nightscout/androidaps/data/Profile.java index 4c44f9ac2e..9d5c19bdf3 100644 --- a/app/src/main/java/info/nightscout/androidaps/data/Profile.java +++ b/app/src/main/java/info/nightscout/androidaps/data/Profile.java @@ -1,5 +1,7 @@ package info.nightscout.androidaps.data; +import android.support.v4.util.LongSparseArray; + import com.crashlytics.android.Crashlytics; import org.json.JSONArray; @@ -30,8 +32,11 @@ public class Profile { double dia = Constants.defaultDIA; TimeZone timeZone = TimeZone.getDefault(); JSONArray isf; + private LongSparseArray isf_v = null; // oldest at index 0 JSONArray ic; + private LongSparseArray ic_v = null; // oldest at index 0 JSONArray basal; + private LongSparseArray basal_v = null; // oldest at index 0 JSONArray targetLow; JSONArray targetHigh; @@ -136,6 +141,21 @@ public class Profile { return timeZone; } + private LongSparseArray convertToSparseArray(JSONArray array) { + LongSparseArray sparse = new LongSparseArray<>(); + for (Integer index = 0; index < array.length(); index++) { + try { + JSONObject o = array.getJSONObject(index); + long tas = o.getLong("timeAsSeconds"); + Double value = o.getDouble("value"); + sparse.put(tas, value); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return sparse; + } + private Double getValueToTime(JSONArray array, Integer timeAsSeconds) { Double lastValue = null; @@ -156,6 +176,21 @@ public class Profile { return lastValue; } + private Double getValueToTime(LongSparseArray array, long timeAsSeconds) { + Double lastValue = null; + + for (Integer index = 0; index < array.size(); index++) { + long tas = array.keyAt(index); + double value = array.valueAt(index); + if (lastValue == null) lastValue = value; + if (timeAsSeconds < tas) { + break; + } + lastValue = value; + } + return lastValue; + } + private String getValuesList(JSONArray array, JSONArray array2, DecimalFormat format, String units) { String retValue = ""; @@ -188,7 +223,9 @@ public class Profile { } public Double getIsf(Integer timeAsSeconds) { - return getValueToTime(isf, timeAsSeconds); + if (isf_v == null) + isf_v = convertToSparseArray(isf); + return getValueToTime(isf_v, timeAsSeconds); } public String getIsfList() { @@ -204,7 +241,9 @@ public class Profile { } public Double getIc(Integer timeAsSeconds) { - return getValueToTime(ic, timeAsSeconds); + if (ic_v == null) + ic_v = convertToSparseArray(ic); + return getValueToTime(ic_v, timeAsSeconds); } public String getIcList() { @@ -220,7 +259,9 @@ public class Profile { } public Double getBasal(Integer timeAsSeconds) { - return getValueToTime(basal, timeAsSeconds); + if (basal_v == null) + basal_v = convertToSparseArray(basal); + return getValueToTime(basal_v, timeAsSeconds); } public String getBasalList() { diff --git a/app/src/main/java/info/nightscout/androidaps/db/ExtendedBolus.java b/app/src/main/java/info/nightscout/androidaps/db/ExtendedBolus.java index d422a94211..05c5360e22 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/ExtendedBolus.java +++ b/app/src/main/java/info/nightscout/androidaps/db/ExtendedBolus.java @@ -180,7 +180,7 @@ public class ExtendedBolus implements Interval, DataPointWithLabelInterface { int realDuration = getDurationToTime(time); if (realDuration > 0) { - Double dia_ago = time - profile.getDia() * 60 * 60 * 1000; + Double dia_ago = time - dia * 60 * 60 * 1000; int aboutFiveMinIntervals = (int) Math.ceil(realDuration / 5d); double spacing = realDuration / aboutFiveMinIntervals; @@ -191,11 +191,11 @@ public class ExtendedBolus implements Interval, DataPointWithLabelInterface { if (calcdate > dia_ago && calcdate <= time) { double tempBolusSize = absoluteRate() * spacing / 60d; - Treatment tempBolusPart = new Treatment(insulinInterface); + Treatment tempBolusPart = new Treatment(insulinInterface, dia); tempBolusPart.insulin = tempBolusSize; tempBolusPart.date = calcdate; - Iob aIOB = insulinInterface.iobCalcForTreatment(tempBolusPart, time, profile.getDia()); + Iob aIOB = insulinInterface.iobCalcForTreatment(tempBolusPart, time, dia); result.iob += aIOB.iobContrib; result.activity += aIOB.activityContrib; result.extendedBolusInsulin += tempBolusPart.insulin; diff --git a/app/src/main/java/info/nightscout/androidaps/db/TemporaryBasal.java b/app/src/main/java/info/nightscout/androidaps/db/TemporaryBasal.java index 4bf668411d..b1798b380b 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/TemporaryBasal.java +++ b/app/src/main/java/info/nightscout/androidaps/db/TemporaryBasal.java @@ -179,8 +179,8 @@ public class TemporaryBasal implements Interval { if (realDuration > 0) { Double netBasalRate = 0d; - - Double dia_ago = time - profile.getDia() * 60 * 60 * 1000; + double dia = profile.getDia(); + Double dia_ago = time - dia * 60 * 60 * 1000; int aboutFiveMinIntervals = (int) Math.ceil(realDuration / 5d); double tempBolusSpacing = realDuration / aboutFiveMinIntervals; @@ -202,11 +202,11 @@ public class TemporaryBasal implements Interval { double tempBolusSize = netBasalRate * tempBolusSpacing / 60d; netBasalAmount += tempBolusSize; - Treatment tempBolusPart = new Treatment(insulinInterface); + Treatment tempBolusPart = new Treatment(insulinInterface, dia); tempBolusPart.insulin = tempBolusSize; tempBolusPart.date = calcdate; - Iob aIOB = insulinInterface.iobCalcForTreatment(tempBolusPart, time, profile.getDia()); + Iob aIOB = insulinInterface.iobCalcForTreatment(tempBolusPart, time, dia); result.basaliob += aIOB.iobContrib; result.activity += aIOB.activityContrib; result.netbasalinsulin += tempBolusPart.insulin; diff --git a/app/src/main/java/info/nightscout/androidaps/db/Treatment.java b/app/src/main/java/info/nightscout/androidaps/db/Treatment.java index e386a195b8..5a73fa0d18 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/Treatment.java +++ b/app/src/main/java/info/nightscout/androidaps/db/Treatment.java @@ -65,6 +65,11 @@ public class Treatment implements DataPointWithLabelInterface { dia = insulin.getDia(); } + public Treatment(InsulinInterface insulin, double dia) { + insulinInterfaceID = insulin.getId(); + this.dia = dia; + } + public long getMillisecondsFromStart() { return new Date().getTime() - date; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/ActivityGraph.java b/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/ActivityGraph.java index 2f98e45272..2d4795a3ec 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/ActivityGraph.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/InsulinFastacting/ActivityGraph.java @@ -38,7 +38,7 @@ public class ActivityGraph extends GraphView { double dia = insulin.getDia(); int hours = (int) Math.floor(dia + 1); - Treatment t = new Treatment(insulin); + Treatment t = new Treatment(insulin, dia); t.date = 0; t.insulin = 1d; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/NSClientInternalPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/NSClientInternalPlugin.java index 4b3e059b6e..df88174b79 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/NSClientInternalPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/NSClientInternalPlugin.java @@ -189,11 +189,11 @@ public class NSClientInternalPlugin implements PluginBase { private void updateLog() { try { - String newTextLog = ""; + StringBuilder newTextLog = new StringBuilder(); for (EventNSClientNewLog log : listLog) { - newTextLog = newTextLog + log.toPreparedHtml(); + newTextLog.append(log.toPreparedHtml()); } - textLog = Html.fromHtml(newTextLog); + textLog = Html.fromHtml(newTextLog.toString()); MainApp.bus().post(new EventNSClientUpdateGUI()); } catch (OutOfMemoryError e) { ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), "Out of memory!\nStop using this phone !!!", R.raw.error); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/events/EventNSClientNewLog.java b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/events/EventNSClientNewLog.java index 3af3333d65..274a7ad828 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/events/EventNSClientNewLog.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/NSClientInternal/events/EventNSClientNewLog.java @@ -22,14 +22,15 @@ public class EventNSClientNewLog { this.logText = logText; } - public Spanned toHtml() { + public StringBuilder toPreparedHtml() { + StringBuilder stringBuilder = new StringBuilder(); SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss"); - Spanned line = Html.fromHtml(timeFormat.format(date) + " " + action + " " + logText + "
"); - return line; - } - - public String toPreparedHtml() { - SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss"); - return timeFormat.format(date) + " " + action + " " + logText + "
"; + stringBuilder.append(timeFormat.format(date)); + stringBuilder.append(" "); + stringBuilder.append(action); + stringBuilder.append(" "); + stringBuilder.append(logText); + stringBuilder.append("
"); + return stringBuilder; } } From 3a95325039da74b5e72e3bcb5e02f87781c45332 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Tue, 13 Jun 2017 16:51:59 +0200 Subject: [PATCH 013/212] fix sms reply text --- .../nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java | 2 ++ .../androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java | 2 ++ .../androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java | 2 ++ 3 files changed, 6 insertions(+) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java index 0707cc842c..14361ca106 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java @@ -504,6 +504,7 @@ public class DanaRPlugin implements PluginBase, PumpInterface, DanaRInterface, C result.comment = MainApp.instance().getString(R.string.virtualpump_resultok); result.duration = pump.tempBasalRemainingMin; result.percent = pump.tempBasalPercent; + result.absolute = MainApp.getConfigBuilder().getTempBasalAbsoluteRateHistory(); result.isPercent = true; if (Config.logPumpActions) log.debug("setTempBasalPercent: Correct value already set"); @@ -518,6 +519,7 @@ public class DanaRPlugin implements PluginBase, PumpInterface, DanaRInterface, C result.isTempCancel = false; result.duration = pump.tempBasalRemainingMin; result.percent = pump.tempBasalPercent; + result.absolute = MainApp.getConfigBuilder().getTempBasalAbsoluteRateHistory(); result.isPercent = true; if (Config.logPumpActions) log.debug("setTempBasalPercent: OK"); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java index 8885056f5b..22a3a2ef65 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java @@ -508,6 +508,7 @@ public class DanaRKoreanPlugin implements PluginBase, PumpInterface, DanaRInterf result.comment = MainApp.instance().getString(R.string.virtualpump_resultok); result.duration = pump.tempBasalRemainingMin; result.percent = pump.tempBasalPercent; + result.absolute = MainApp.getConfigBuilder().getTempBasalAbsoluteRateHistory(); result.isPercent = true; if (Config.logPumpActions) log.debug("setTempBasalPercent: Correct value already set"); @@ -522,6 +523,7 @@ public class DanaRKoreanPlugin implements PluginBase, PumpInterface, DanaRInterf result.isTempCancel = false; result.duration = pump.tempBasalRemainingMin; result.percent = pump.tempBasalPercent; + result.absolute = MainApp.getConfigBuilder().getTempBasalAbsoluteRateHistory(); result.isPercent = true; if (Config.logPumpActions) log.debug("setTempBasalPercent: OK"); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java index 4d332824aa..68518fc20f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRv2/DanaRv2Plugin.java @@ -415,6 +415,7 @@ public class DanaRv2Plugin implements PluginBase, PumpInterface, DanaRInterface, result.comment = MainApp.instance().getString(R.string.virtualpump_resultok); result.duration = pump.tempBasalRemainingMin; result.percent = pump.tempBasalPercent; + result.absolute = MainApp.getConfigBuilder().getTempBasalAbsoluteRateHistory(); result.isPercent = true; if (Config.logPumpActions) log.debug("setTempBasalPercent: Correct value already set"); @@ -429,6 +430,7 @@ public class DanaRv2Plugin implements PluginBase, PumpInterface, DanaRInterface, result.isTempCancel = false; result.duration = pump.tempBasalRemainingMin; result.percent = pump.tempBasalPercent; + result.absolute = MainApp.getConfigBuilder().getTempBasalAbsoluteRateHistory(); result.isPercent = true; if (Config.logPumpActions) log.debug("setTempBasalPercent: OK"); From 2c2b6c21a1ca44e3d6964cdd31468f5fc8201c12 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Tue, 13 Jun 2017 18:47:54 +0200 Subject: [PATCH 014/212] cancel temp basal in the same connection --- .../androidaps/plugins/PumpDanaR/DanaRPlugin.java | 9 --------- .../PumpDanaR/services/DanaRExecutionService.java | 5 +++++ .../plugins/PumpDanaRKorean/DanaRKoreanPlugin.java | 9 --------- .../services/DanaRKoreanExecutionService.java | 5 +++++ 4 files changed, 10 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java index 14361ca106..be1d3cbf9f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/DanaRPlugin.java @@ -404,15 +404,6 @@ public class DanaRPlugin implements PluginBase, PumpInterface, DanaRInterface, C if (Config.logPumpActions) log.debug("setTempBasalAbsolute: Correct temp basal already set (doLowTemp || doHighTemp)"); return result; - } else { - if (Config.logPumpActions) - log.debug("setTempBasalAbsolute: Stopping temp basal (doLowTemp || doHighTemp)"); - result = cancelRealTempBasal(); - // Check for proper result - if (!result.success) { - log.error("setTempBasalAbsolute: Failed to stop previous temp basal (doLowTemp || doHighTemp)"); - return result; - } } } // Convert duration from minutes to hours diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/services/DanaRExecutionService.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/services/DanaRExecutionService.java index eaaf06bb16..439cc0a8df 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/services/DanaRExecutionService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaR/services/DanaRExecutionService.java @@ -353,6 +353,11 @@ public class DanaRExecutionService extends Service { public boolean tempBasal(int percent, int durationInHours) { connect("tempBasal"); if (!isConnected()) return false; + if (danaRPump.isTempBasalInProgress) { + MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.stoppingtempbasal))); + mSerialIOThread.sendMessage(new MsgSetTempBasalStop()); + waitMsec(500); + } MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.settingtempbasal))); mSerialIOThread.sendMessage(new MsgSetTempBasalStart(percent, durationInHours)); mSerialIOThread.sendMessage(new MsgStatusTempBasal()); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java index 22a3a2ef65..abb19246e4 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/DanaRKoreanPlugin.java @@ -408,15 +408,6 @@ public class DanaRKoreanPlugin implements PluginBase, PumpInterface, DanaRInterf if (Config.logPumpActions) log.debug("setTempBasalAbsolute: Correct temp basal already set (doLowTemp || doHighTemp)"); return result; - } else { - if (Config.logPumpActions) - log.debug("setTempBasalAbsolute: Stopping temp basal (doLowTemp || doHighTemp)"); - result = cancelRealTempBasal(); - // Check for proper result - if (!result.success) { - log.error("setTempBasalAbsolute: Failed to stop previous temp basal (doLowTemp || doHighTemp)"); - return result; - } } } // Convert duration from minutes to hours diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/services/DanaRKoreanExecutionService.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/services/DanaRKoreanExecutionService.java index 7062ee3ccc..e549c668a3 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/services/DanaRKoreanExecutionService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpDanaRKorean/services/DanaRKoreanExecutionService.java @@ -347,6 +347,11 @@ public class DanaRKoreanExecutionService extends Service { public boolean tempBasal(int percent, int durationInHours) { connect("tempBasal"); if (!isConnected()) return false; + if (danaRPump.isTempBasalInProgress) { + MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.stoppingtempbasal))); + mSerialIOThread.sendMessage(new MsgSetTempBasalStop()); + waitMsec(500); + } MainApp.bus().post(new EventPumpStatusChanged(MainApp.sResources.getString(R.string.settingtempbasal))); mSerialIOThread.sendMessage(new MsgSetTempBasalStart(percent, durationInHours)); mSerialIOThread.sendMessage(new MsgStatusTempBasal()); From 745a3cf87450090f412118db2737e0fab043b1e4 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Tue, 13 Jun 2017 19:37:31 +0200 Subject: [PATCH 015/212] reuse profile object --- .../androidaps/db/ExtendedBolus.java | 4 ---- .../androidaps/db/ProfileSwitch.java | 15 +++++++++++++++ .../ConfigBuilder/ConfigBuilderPlugin.java | 6 +----- .../IobCobCalculatorPlugin.java | 18 +++++++----------- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/db/ExtendedBolus.java b/app/src/main/java/info/nightscout/androidaps/db/ExtendedBolus.java index 05c5360e22..8fdd41e220 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/ExtendedBolus.java +++ b/app/src/main/java/info/nightscout/androidaps/db/ExtendedBolus.java @@ -171,12 +171,8 @@ public class ExtendedBolus implements Interval, DataPointWithLabelInterface { public IobTotal iobCalc(long time) { IobTotal result = new IobTotal(time); - Profile profile = MainApp.getConfigBuilder().getProfile(time); InsulinInterface insulinInterface = ConfigBuilderPlugin.getActiveInsulin(); - if (profile == null) - return result; - int realDuration = getDurationToTime(time); if (realDuration > 0) { diff --git a/app/src/main/java/info/nightscout/androidaps/db/ProfileSwitch.java b/app/src/main/java/info/nightscout/androidaps/db/ProfileSwitch.java index 25ba1cb051..8c257141c5 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/ProfileSwitch.java +++ b/app/src/main/java/info/nightscout/androidaps/db/ProfileSwitch.java @@ -5,12 +5,15 @@ import android.graphics.Color; import com.j256.ormlite.field.DatabaseField; import com.j256.ormlite.table.DatabaseTable; +import org.json.JSONException; +import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Date; import java.util.Objects; +import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.interfaces.Interval; import info.nightscout.androidaps.plugins.Overview.graphExtensions.DataPointWithLabelInterface; import info.nightscout.androidaps.plugins.Overview.graphExtensions.PointsWithLabelGraphSeries; @@ -50,6 +53,18 @@ public class ProfileSwitch implements Interval, DataPointWithLabelInterface { @DatabaseField public int durationInMinutes = 0; + private Profile profile = null; + + public Profile getProfileObject() { + if (profile == null) + try { + profile = new Profile(new JSONObject(profileJson)); + } catch (JSONException e) { + e.printStackTrace(); + } + return profile; + } + public boolean isEqual(ProfileSwitch other) { if (date != other.date) { return false; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java index c5d27a7e11..d7d808ea42 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java @@ -1017,11 +1017,7 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain ProfileSwitch profileSwitch = getProfileSwitchFromHistory(time); if (profileSwitch != null) { if (profileSwitch.profileJson != null) { - try { - return new Profile(new JSONObject(profileSwitch.profileJson)); - } catch (JSONException e) { - e.printStackTrace(); - } + return profileSwitch.getProfileObject(); } else { Profile profile = activeProfile.getProfile().getSpecificProfile(profileSwitch.profileName); if (profile != null) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java index 4b7060e52f..2759099995 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java @@ -326,18 +326,14 @@ public class IobCobCalculatorPlugin implements PluginBase { long now = new Date().getTime(); time = roundUpTime(time); if (time < now && iobTable.get(time) != null) { - //log.debug(">>> Cache hit"); + //og.debug(">>> calulateFromTreatmentsAndTemps Cache hit " + new Date(time).toLocaleString()); return iobTable.get(time); } else { - //log.debug(">>> Cache miss " + new Date(time).toLocaleString()); + //log.debug(">>> calulateFromTreatmentsAndTemps Cache miss " + new Date(time).toLocaleString()); } IobTotal bolusIob = MainApp.getConfigBuilder().getCalculationToTimeTreatments(time).round(); IobTotal basalIob = MainApp.getConfigBuilder().getCalculationToTimeTempBasals(time).round(); -/* - if (basalIob.basaliob > 0) { - log.debug(new Date(time).toLocaleString() + " basaliob: " + basalIob.basaliob ); - } -*/ + IobTotal iobTotal = IobTotal.combine(bolusIob, basalIob).round(); if (time < new Date().getTime()) { iobTable.put(time, iobTotal); @@ -374,9 +370,9 @@ public class IobCobCalculatorPlugin implements PluginBase { if (time < now) { basalDataTable.append(time, retval); } - //log.debug(">>> Cache miss " + new Date(time).toLocaleString()); + //log.debug(">>> getBasalData Cache miss " + new Date(time).toLocaleString()); } else { - //log.debug(">>> Cache hit " + new Date(time).toLocaleString()); + //log.debug(">>> getBasalData Cache hit " + new Date(time).toLocaleString()); } return retval; } @@ -391,10 +387,10 @@ public class IobCobCalculatorPlugin implements PluginBase { time = roundUpTime(previous); AutosensData data = autosensDataTable.get(time); if (data != null) { - //log.debug(">>> Cache hit " + data.log(time)); + //log.debug(">>> getAutosensData Cache hit " + data.log(time)); return data; } else { - //log.debug(">>> Cache miss " + new Date(time).toLocaleString()); + //log.debug(">>> getAutosensData Cache miss " + new Date(time).toLocaleString()); return null; } } From 43b70f9efc51d09b497a47810162642189ab573e Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Tue, 13 Jun 2017 20:48:53 +0200 Subject: [PATCH 016/212] loop, MA, AMA new design --- app/src/main/res/layout/loop_fragment.xml | 303 ++++++++--- .../main/res/layout/openapsama_fragment.xml | 490 +++++++++++++++--- .../main/res/layout/openapsma_fragment.xml | 390 +++++++++++--- 3 files changed, 965 insertions(+), 218 deletions(-) diff --git a/app/src/main/res/layout/loop_fragment.xml b/app/src/main/res/layout/loop_fragment.xml index d1088940a3..23a2f93d18 100644 --- a/app/src/main/res/layout/loop_fragment.xml +++ b/app/src/main/res/layout/loop_fragment.xml @@ -5,133 +5,294 @@ tools:context="info.nightscout.androidaps.plugins.Loop.LoopFragment"> + android:layout_height="match_parent"> - + android:layout_height="wrap_content" + android:orientation="vertical"> +