From ff2f6a8bfb352775c076113c3b7c88c825ca1ea3 Mon Sep 17 00:00:00 2001 From: Tim Gunn Date: Thu, 25 Oct 2018 16:33:29 +1300 Subject: [PATCH 01/36] Add UAMSMB Basal Minutes with Min Bolus Increment --- .../main/assets/OpenAPSSMB/determine-basal.js | 18 ++++++++++++------ .../OpenAPSSMB/DetermineBasalAdapterSMBJS.java | 3 +++ .../plugins/OpenAPSSMB/SMBDefaults.java | 2 ++ app/src/main/res/values/strings.xml | 2 ++ app/src/main/res/xml/pref_openapssmb.xml | 16 +++++++++++++++- 5 files changed, 34 insertions(+), 7 deletions(-) diff --git a/app/src/main/assets/OpenAPSSMB/determine-basal.js b/app/src/main/assets/OpenAPSSMB/determine-basal.js index c2db0f270b..bb620bf5c9 100644 --- a/app/src/main/assets/OpenAPSSMB/determine-basal.js +++ b/app/src/main/assets/OpenAPSSMB/determine-basal.js @@ -995,8 +995,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // eventual BG is at/above target // if iob is over max, just cancel any temps - // if we're not here because of SMB, eventual BG is at/above target - if (! (microBolusAllowed && rT.COB)) { + if ( eventualBG >= max_bg ) { rT.reason += "Eventual BG " + convert_bg(eventualBG, profile) + " >= " + convert_bg(max_bg, profile) + ", "; } if (iob_data.iob > max_iob) { @@ -1047,20 +1046,27 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // if IOB covers more than COB, limit maxBolus to 30m of basal } else if ( iob_data.iob > mealInsulinReq && iob_data.iob > 0 ) { console.error("IOB",iob_data.iob,"> COB",meal_data.mealCOB+"; mealInsulinReq =",mealInsulinReq); - maxBolus = round( profile.current_basal * 30 / 60 ,1); + if (profile.maxUAMSMBBasalMinutes) { + console.error("profile.maxUAMSMBBasalMinutes:",profile.maxUAMSMBBasalMinutes,"profile.current_basal:",profile.current_basal); + maxBolus = round( profile.current_basal * profile.maxUAMSMBBasalMinutes / 60 ,1); + } else { + console.error("profile.maxUAMSMBBasalMinutes undefined: defaulting to 30m"); + maxBolus = round( profile.current_basal * 30 / 60 ,1); + } } else { console.error("profile.maxSMBBasalMinutes:",profile.maxSMBBasalMinutes,"profile.current_basal:",profile.current_basal); maxBolus = round( profile.current_basal * profile.maxSMBBasalMinutes / 60 ,1); } - // bolus 1/2 the insulinReq, up to maxBolus, rounding down to nearest 0.1U - microBolus = Math.floor(Math.min(insulinReq/2,maxBolus)*10)/10; + // bolus 1/2 the insulinReq, up to maxBolus, rounding down to nearest bolus increment + var roundSMBTo = 1 / profile.bolus_increment; + microBolus = Math.floor(Math.min(insulinReq/2,maxBolus)*roundSMBTo)/roundSMBTo; // calculate a long enough zero temp to eventually correct back up to target var smbTarget = target_bg; var worstCaseInsulinReq = (smbTarget - (naive_eventualBG + minIOBPredBG)/2 ) / sens; var durationReq = round(60*worstCaseInsulinReq / profile.current_basal); // if insulinReq > 0 but not enough for a microBolus, don't set an SMB zero temp - if (insulinReq > 0 && microBolus < 0.1) { + if (insulinReq > 0 && microBolus < profile.bolus_increment) { durationReq = 0; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java index 7ede714c10..b284d258b0 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java @@ -44,6 +44,7 @@ public class DetermineBasalAdapterSMBJS { private JSONObject mMealData; private JSONObject mCurrentTemp; private JSONObject mAutosensData = null; + private long mCurrentTime; private boolean mMicrobolusAllowed; private boolean mSMBAlwaysAllowed; @@ -255,6 +256,8 @@ public class DetermineBasalAdapterSMBJS { mProfile.put("enableSMB_always", SP.getBoolean(R.string.key_enableSMB_always, false) && advancedFiltering); mProfile.put("enableSMB_after_carbs", SP.getBoolean(R.string.key_enableSMB_after_carbs, false) && advancedFiltering); mProfile.put("maxSMBBasalMinutes", SP.getInt("key_smbmaxminutes", SMBDefaults.maxSMBBasalMinutes)); + mProfile.put("maxUAMSMBBasalMinutes", SP.getInt("key_uamsmbmaxminutes", SMBDefaults.maxUAMSMBBasalMinutes)); + mProfile.put("bolus_increment", SMBDefaults.bolus_increment); mProfile.put("carbsReqThreshold", SMBDefaults.carbsReqThreshold); mProfile.put("current_basal", basalrate); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/SMBDefaults.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/SMBDefaults.java index 0ce8d2bb94..234aa4e12a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/SMBDefaults.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/SMBDefaults.java @@ -56,9 +56,11 @@ public class SMBDefaults { //public final static boolean enableSMB_after_carbs = false; // enable supermicrobolus for 6h after carbs, even with 0 COB //public final static boolean allowSMB_with_high_temptarget = false; // allow supermicrobolus (if otherwise enabled) even with high temp targets public final static int maxSMBBasalMinutes = 30; // maximum minutes of basal that can be delivered as a single SMB with uncovered COB + public final static int maxUAMSMBBasalMinutes = 30; // maximum minutes of basal that can be delivered as a single SMB when IOB exceeds COB // curve:"rapid-acting" // Supported curves: "bilinear", "rapid-acting" (Novolog, Novorapid, Humalog, Apidra) and "ultra-rapid" (Fiasp) // useCustomPeakTime:false // allows changing insulinPeakTime // insulinPeakTime:75 // number of minutes after a bolus activity peaks. defaults to 55m for Fiasp if useCustomPeakTime: false public final static int carbsReqThreshold = 1; // grams of carbsReq to trigger a pushover // offline_hotspot:false // enabled an offline-only local wifi hotspot if no Internet available + public final static double bolus_increment = 0.1; // minimum bolus that can be delivered as an SMB } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ae4e572392..46c583c847 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -802,6 +802,8 @@ Show delta with one more decimal place 45 60 75 90 105 120 Max minutes of basal to limit SMB to + 45 60 75 90 105 120 + Max minutes of basal to limit SMB to when UAM is active Unsupported pump firmware Send BG data to xDrip+ dexcomg5_xdripupload diff --git a/app/src/main/res/xml/pref_openapssmb.xml b/app/src/main/res/xml/pref_openapssmb.xml index e23a364cb8..53e1deef17 100644 --- a/app/src/main/res/xml/pref_openapssmb.xml +++ b/app/src/main/res/xml/pref_openapssmb.xml @@ -79,6 +79,20 @@ validate:minNumber="15" validate:testType="numericRange" /> + + - \ No newline at end of file + From 4bc8025ad4f85391ce7e2453d4ee59fe6a144475 Mon Sep 17 00:00:00 2001 From: Tim Gunn Date: Thu, 25 Oct 2018 18:27:21 +1300 Subject: [PATCH 02/36] Make min SMB Bolus configurable --- .../OpenAPSSMB/DetermineBasalAdapterSMBJS.java | 2 +- app/src/main/res/values/strings.xml | 3 ++- app/src/main/res/xml/pref_openapssmb.xml | 14 ++++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java index b284d258b0..a05b5aae45 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java @@ -257,7 +257,7 @@ public class DetermineBasalAdapterSMBJS { mProfile.put("enableSMB_after_carbs", SP.getBoolean(R.string.key_enableSMB_after_carbs, false) && advancedFiltering); mProfile.put("maxSMBBasalMinutes", SP.getInt("key_smbmaxminutes", SMBDefaults.maxSMBBasalMinutes)); mProfile.put("maxUAMSMBBasalMinutes", SP.getInt("key_uamsmbmaxminutes", SMBDefaults.maxUAMSMBBasalMinutes)); - mProfile.put("bolus_increment", SMBDefaults.bolus_increment); + mProfile.put("bolus_increment", SP.getDouble("key_bolus_increment", SMBDefaults.bolus_increment)); mProfile.put("carbsReqThreshold", SMBDefaults.carbsReqThreshold); mProfile.put("current_basal", basalrate); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 46c583c847..f675c3b613 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -803,7 +803,8 @@ 45 60 75 90 105 120 Max minutes of basal to limit SMB to 45 60 75 90 105 120 - Max minutes of basal to limit SMB to when UAM is active + Max minutes of basal to limit SMB to for UAM + Minimum bolus that can be delivered as an SMB Unsupported pump firmware Send BG data to xDrip+ dexcomg5_xdripupload diff --git a/app/src/main/res/xml/pref_openapssmb.xml b/app/src/main/res/xml/pref_openapssmb.xml index 53e1deef17..cc09aa707d 100644 --- a/app/src/main/res/xml/pref_openapssmb.xml +++ b/app/src/main/res/xml/pref_openapssmb.xml @@ -93,6 +93,20 @@ validate:minNumber="15" validate:testType="numericRange" /> + + + Date: Thu, 25 Oct 2018 18:47:50 +1300 Subject: [PATCH 03/36] use number as numberDecimal does not apply restrictions --- app/src/main/res/xml/pref_openapssmb.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/xml/pref_openapssmb.xml b/app/src/main/res/xml/pref_openapssmb.xml index cc09aa707d..87bb5bb5ce 100644 --- a/app/src/main/res/xml/pref_openapssmb.xml +++ b/app/src/main/res/xml/pref_openapssmb.xml @@ -96,7 +96,7 @@ Date: Thu, 25 Oct 2018 20:02:02 +1300 Subject: [PATCH 04/36] get SMB bolus limits working --- .../plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java | 2 +- app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/pref_openapssmb.xml | 9 ++++----- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java index a05b5aae45..311aa12bd2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java @@ -257,7 +257,7 @@ public class DetermineBasalAdapterSMBJS { mProfile.put("enableSMB_after_carbs", SP.getBoolean(R.string.key_enableSMB_after_carbs, false) && advancedFiltering); mProfile.put("maxSMBBasalMinutes", SP.getInt("key_smbmaxminutes", SMBDefaults.maxSMBBasalMinutes)); mProfile.put("maxUAMSMBBasalMinutes", SP.getInt("key_uamsmbmaxminutes", SMBDefaults.maxUAMSMBBasalMinutes)); - mProfile.put("bolus_increment", SP.getDouble("key_bolus_increment", SMBDefaults.bolus_increment)); + mProfile.put("bolus_increment", SP.getDouble("bolus_increment", SMBDefaults.bolus_increment)); mProfile.put("carbsReqThreshold", SMBDefaults.carbsReqThreshold); mProfile.put("current_basal", basalrate); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f675c3b613..11960880b5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -804,6 +804,7 @@ Max minutes of basal to limit SMB to 45 60 75 90 105 120 Max minutes of basal to limit SMB to for UAM + 0.1 Minimum bolus that can be delivered as an SMB Unsupported pump firmware Send BG data to xDrip+ diff --git a/app/src/main/res/xml/pref_openapssmb.xml b/app/src/main/res/xml/pref_openapssmb.xml index 87bb5bb5ce..d74c9ef9aa 100644 --- a/app/src/main/res/xml/pref_openapssmb.xml +++ b/app/src/main/res/xml/pref_openapssmb.xml @@ -95,15 +95,14 @@ From 4592f290db4097e017ca34ebe1c4b157ae7c88d0 Mon Sep 17 00:00:00 2001 From: Tim Gunn Date: Thu, 25 Oct 2018 20:13:19 +1300 Subject: [PATCH 05/36] remove stuff thats not needed --- .../plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java index 311aa12bd2..7e17c387a0 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java @@ -44,7 +44,6 @@ public class DetermineBasalAdapterSMBJS { private JSONObject mMealData; private JSONObject mCurrentTemp; private JSONObject mAutosensData = null; - private long mCurrentTime; private boolean mMicrobolusAllowed; private boolean mSMBAlwaysAllowed; From 84716eb7775f8cb4c1979ea9b3f5ffd7c1bb47ac Mon Sep 17 00:00:00 2001 From: Tim Gunn Date: Fri, 26 Oct 2018 18:49:18 +1300 Subject: [PATCH 06/36] change to using key convention --- .../plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java | 2 +- app/src/main/res/values/strings.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java index 7e17c387a0..ffa1954bad 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java @@ -256,7 +256,7 @@ public class DetermineBasalAdapterSMBJS { mProfile.put("enableSMB_after_carbs", SP.getBoolean(R.string.key_enableSMB_after_carbs, false) && advancedFiltering); mProfile.put("maxSMBBasalMinutes", SP.getInt("key_smbmaxminutes", SMBDefaults.maxSMBBasalMinutes)); mProfile.put("maxUAMSMBBasalMinutes", SP.getInt("key_uamsmbmaxminutes", SMBDefaults.maxUAMSMBBasalMinutes)); - mProfile.put("bolus_increment", SP.getDouble("bolus_increment", SMBDefaults.bolus_increment)); + mProfile.put("bolus_increment", SP.getDouble("key_bolus_increment", SMBDefaults.bolus_increment)); mProfile.put("carbsReqThreshold", SMBDefaults.carbsReqThreshold); mProfile.put("current_basal", basalrate); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 11960880b5..e640191b23 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -804,7 +804,7 @@ Max minutes of basal to limit SMB to 45 60 75 90 105 120 Max minutes of basal to limit SMB to for UAM - 0.1 + bolus_increment Minimum bolus that can be delivered as an SMB Unsupported pump firmware Send BG data to xDrip+ From 7c4c42669087a858c64503ba0631fe6e2d41bfa9 Mon Sep 17 00:00:00 2001 From: Tim Gunn Date: Fri, 26 Oct 2018 18:50:01 +1300 Subject: [PATCH 07/36] remove string --- app/src/main/res/xml/pref_openapssmb.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/xml/pref_openapssmb.xml b/app/src/main/res/xml/pref_openapssmb.xml index d74c9ef9aa..f26a58ffd0 100644 --- a/app/src/main/res/xml/pref_openapssmb.xml +++ b/app/src/main/res/xml/pref_openapssmb.xml @@ -96,7 +96,7 @@ Date: Fri, 26 Oct 2018 20:23:25 +1300 Subject: [PATCH 08/36] SMB Interval addition --- app/src/main/assets/OpenAPSSMB/determine-basal.js | 14 ++++++++++---- .../OpenAPSSMB/DetermineBasalAdapterSMBJS.java | 1 + .../plugins/OpenAPSSMB/SMBDefaults.java | 1 + app/src/main/res/values/strings.xml | 2 ++ app/src/main/res/xml/pref_openapssmb.xml | 15 ++++++++++++++- 5 files changed, 28 insertions(+), 5 deletions(-) diff --git a/app/src/main/assets/OpenAPSSMB/determine-basal.js b/app/src/main/assets/OpenAPSSMB/determine-basal.js index c2db0f270b..1f6499ee87 100644 --- a/app/src/main/assets/OpenAPSSMB/determine-basal.js +++ b/app/src/main/assets/OpenAPSSMB/determine-basal.js @@ -1085,17 +1085,23 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ } rT.reason += ". "; - //allow SMBs every 3 minutes - var nextBolusMins = round(3-lastBolusAge,1); + //allow SMBs every 3 minutes by default + var SMBInterval = 3; + if (profile.SMBInterval) { + // allow SMBIntervals between 1 and 10 minutes + SMBInterval = Math.min(10,Math.max(1,profile.SMBInterval)); + } + var nextBolusMins = round(SMBInterval-lastBolusAge,0); + var nextBolusSeconds = round((SMBInterval - lastBolusAge) * 60, 0) % 60; //console.error(naive_eventualBG, insulinReq, worstCaseInsulinReq, durationReq); console.error("naive_eventualBG",naive_eventualBG+",",durationReq+"m "+smbLowTempReq+"U/h temp needed; last bolus",lastBolusAge+"m ago; maxBolus: "+maxBolus); - if (lastBolusAge > 3) { + if (lastBolusAge > SMBInterval) { if (microBolus > 0) { rT.units = microBolus; rT.reason += "Microbolusing " + microBolus + "U. "; } } else { - rT.reason += "Waiting " + nextBolusMins + "m to microbolus again. "; + rT.reason += "Waiting " + nextBolusMins + "m " + nextBolusSeconds + "s to microbolus again. "; } //rT.reason += ". "; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java index 7ede714c10..d82e98a7b6 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java @@ -249,6 +249,7 @@ public class DetermineBasalAdapterSMBJS { mProfile.put("remainingCarbsCap", SMBDefaults.remainingCarbsCap); mProfile.put("enableUAM", SP.getBoolean(R.string.key_use_uam, false)); mProfile.put("A52_risk_enable", SMBDefaults.A52_risk_enable); + mProfile.put("SMBInterval", SP.getInt("key_smbinterval", SMBDefaults.SMBInterval)); mProfile.put("enableSMB_with_COB", SP.getBoolean(R.string.key_enableSMB_with_COB, false)); mProfile.put("enableSMB_with_temptarget", SP.getBoolean(R.string.key_enableSMB_with_temptarget, false)); mProfile.put("allowSMB_with_high_temptarget", SP.getBoolean(R.string.key_allowSMB_with_high_temptarget, false)); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/SMBDefaults.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/SMBDefaults.java index 0ce8d2bb94..b203add063 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/SMBDefaults.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/SMBDefaults.java @@ -55,6 +55,7 @@ public class SMBDefaults { // *** WARNING *** DO NOT USE enableSMB_always or enableSMB_after_carbs with xDrip+, Libre, or similar //public final static boolean enableSMB_after_carbs = false; // enable supermicrobolus for 6h after carbs, even with 0 COB //public final static boolean allowSMB_with_high_temptarget = false; // allow supermicrobolus (if otherwise enabled) even with high temp targets + public final static int SMBInterval = 3; // minimum interval between SMBs, in minutes. (limited between 1 and 10 min) public final static int maxSMBBasalMinutes = 30; // maximum minutes of basal that can be delivered as a single SMB with uncovered COB // curve:"rapid-acting" // Supported curves: "bilinear", "rapid-acting" (Novolog, Novorapid, Humalog, Apidra) and "ultra-rapid" (Fiasp) // useCustomPeakTime:false // allows changing insulinPeakTime diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ae4e572392..b465213583 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -800,6 +800,8 @@ Poctech upload settings Show detailed delta Show delta with one more decimal place + key_smbinterval + How frequently SMBs will be given in min 45 60 75 90 105 120 Max minutes of basal to limit SMB to Unsupported pump firmware diff --git a/app/src/main/res/xml/pref_openapssmb.xml b/app/src/main/res/xml/pref_openapssmb.xml index e23a364cb8..1b22e4322b 100644 --- a/app/src/main/res/xml/pref_openapssmb.xml +++ b/app/src/main/res/xml/pref_openapssmb.xml @@ -65,6 +65,19 @@ android:summary="@string/enablesmbaftercarbs_summary" android:title="@string/enablesmbaftercarbs" /> + + - \ No newline at end of file + From 7a95adca299568ccf798293908a1ca7cf7a5a022 Mon Sep 17 00:00:00 2001 From: Tim Gunn Date: Sat, 27 Oct 2018 12:33:32 +1300 Subject: [PATCH 09/36] Find Bolus incrument on pump, make sure the pump can support the minSMB bolus setting --- .../plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java index ffa1954bad..e307156223 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java @@ -25,6 +25,7 @@ import info.nightscout.androidaps.data.IobTotal; import info.nightscout.androidaps.data.MealData; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.db.TemporaryBasal; +import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin; import info.nightscout.androidaps.plugins.Loop.ScriptReader; @@ -215,7 +216,8 @@ public class DetermineBasalAdapterSMBJS { ) throws JSONException { String units = profile.getUnits(); - + Double bolusincrument = SP.getDouble("key_bolus_increment", SMBDefaults.bolus_increment); + Double pumpbolusstep = MainApp.getConfigBuilder().getActivePump().getPumpDescription().bolusStep; mProfile = new JSONObject(); mProfile.put("max_iob", maxIob); @@ -256,7 +258,11 @@ public class DetermineBasalAdapterSMBJS { mProfile.put("enableSMB_after_carbs", SP.getBoolean(R.string.key_enableSMB_after_carbs, false) && advancedFiltering); mProfile.put("maxSMBBasalMinutes", SP.getInt("key_smbmaxminutes", SMBDefaults.maxSMBBasalMinutes)); mProfile.put("maxUAMSMBBasalMinutes", SP.getInt("key_uamsmbmaxminutes", SMBDefaults.maxUAMSMBBasalMinutes)); - mProfile.put("bolus_increment", SP.getDouble("key_bolus_increment", SMBDefaults.bolus_increment)); + if (bolusincrument < pumpbolusstep){ + //the bolus incrument is less than what the pump can support (by pump settings or pump restriction), set to value supported by the pump + bolusincrument = pumpbolusstep; + } + mProfile.put("bolus_increment", bolusincrument); mProfile.put("carbsReqThreshold", SMBDefaults.carbsReqThreshold); mProfile.put("current_basal", basalrate); From f24318c105812b54f213759dbae9e9a59db8b877 Mon Sep 17 00:00:00 2001 From: Tim Gunn Date: Thu, 1 Nov 2018 20:05:14 +1300 Subject: [PATCH 10/36] use diff plugin --- .../plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java index e307156223..e8c7bc62cc 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java @@ -27,6 +27,7 @@ import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin; import info.nightscout.androidaps.plugins.Loop.ScriptReader; import info.nightscout.androidaps.plugins.OpenAPSMA.LoggerCallback; @@ -217,7 +218,7 @@ public class DetermineBasalAdapterSMBJS { String units = profile.getUnits(); Double bolusincrument = SP.getDouble("key_bolus_increment", SMBDefaults.bolus_increment); - Double pumpbolusstep = MainApp.getConfigBuilder().getActivePump().getPumpDescription().bolusStep; + Double pumpbolusstep = ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().bolusStep; mProfile = new JSONObject(); mProfile.put("max_iob", maxIob); From 0f3d9327475f4d1519daf8856d3e47a6ad9ac1df Mon Sep 17 00:00:00 2001 From: Tim Gunn Date: Thu, 1 Nov 2018 20:16:01 +1300 Subject: [PATCH 11/36] correct var spelling --- .../plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java index e8c7bc62cc..477a8dba71 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalAdapterSMBJS.java @@ -217,7 +217,7 @@ public class DetermineBasalAdapterSMBJS { ) throws JSONException { String units = profile.getUnits(); - Double bolusincrument = SP.getDouble("key_bolus_increment", SMBDefaults.bolus_increment); + Double bolusincrement = SP.getDouble("key_bolus_increment", SMBDefaults.bolus_increment); Double pumpbolusstep = ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().bolusStep; mProfile = new JSONObject(); @@ -259,11 +259,11 @@ public class DetermineBasalAdapterSMBJS { mProfile.put("enableSMB_after_carbs", SP.getBoolean(R.string.key_enableSMB_after_carbs, false) && advancedFiltering); mProfile.put("maxSMBBasalMinutes", SP.getInt("key_smbmaxminutes", SMBDefaults.maxSMBBasalMinutes)); mProfile.put("maxUAMSMBBasalMinutes", SP.getInt("key_uamsmbmaxminutes", SMBDefaults.maxUAMSMBBasalMinutes)); - if (bolusincrument < pumpbolusstep){ + if (bolusincrement < pumpbolusstep){ //the bolus incrument is less than what the pump can support (by pump settings or pump restriction), set to value supported by the pump - bolusincrument = pumpbolusstep; + bolusincrement = pumpbolusstep; } - mProfile.put("bolus_increment", bolusincrument); + mProfile.put("bolus_increment", bolusincrement); mProfile.put("carbsReqThreshold", SMBDefaults.carbsReqThreshold); mProfile.put("current_basal", basalrate); From 354071348ceb590f9f0ca15c54603d0ce8eff545 Mon Sep 17 00:00:00 2001 From: Tim Gunn <2896311+Tornado-Tim@users.noreply.github.com> Date: Sat, 10 Aug 2019 20:16:30 +1200 Subject: [PATCH 12/36] prelim commit --- .../main/assets/OpenAPSSMB/determine-basal.js | 335 +++++++++--------- .../DetermineBasalAdapterSMBJS.java | 13 +- 2 files changed, 178 insertions(+), 170 deletions(-) diff --git a/app/src/main/assets/OpenAPSSMB/determine-basal.js b/app/src/main/assets/OpenAPSSMB/determine-basal.js index 9839f6bb1d..4d9ed4122c 100644 --- a/app/src/main/assets/OpenAPSSMB/determine-basal.js +++ b/app/src/main/assets/OpenAPSSMB/determine-basal.js @@ -31,14 +31,13 @@ function calculate_expected_delta(target_bg, eventual_bg, bgi) { // (hours * mins_per_hour) / 5 = how many 5 minute periods in 2h = 24 var five_min_blocks = (2 * 60) / 5; var target_delta = target_bg - eventual_bg; - var expectedDelta = round(bgi + (target_delta / five_min_blocks), 1); - return expectedDelta; + return /* expectedDelta */ round(bgi + (target_delta / five_min_blocks), 1); } function convert_bg(value, profile) { - if (profile.out_units == "mmol/L") + if (profile.out_units === "mmol/L") { return round(value / 18, 1).toFixed(1); } @@ -48,10 +47,13 @@ function convert_bg(value, profile) } } -var determine_basal = function determine_basal(glucose_status, currenttemp, iob_data, profile, autosens_data, meal_data, tempBasalFunctions, microBolusAllowed, reservoir_data) { +var determine_basal = function determine_basal(glucose_status, currenttemp, iob_data, profile, autosens_data, meal_data, tempBasalFunctions, microBolusAllowed, reservoir_data, currentTime) { var rT = {}; //short for requestedTemp var deliverAt = new Date(); + if (currentTime) { + deliverAt = currentTime; + } if (typeof profile === 'undefined' || typeof profile.current_basal === 'undefined') { rT.error ='Error: could not get current basal rate'; @@ -61,26 +63,39 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ var basal = profile_current_basal; var systemTime = new Date(); + if (currentTime) { + systemTime = currentTime; + } var bgTime = new Date(glucose_status.date); var minAgo = round( (systemTime - bgTime) / 60 / 1000 ,1); var bg = glucose_status.glucose; - if (bg < 39) { //Dexcom is in ??? mode or calibrating - rT.reason = "CGM is calibrating or in ??? state"; + var noise = glucose_status.noise; + // 38 is an xDrip error state that usually indicates sensor failure + // all other BG values between 11 and 37 mg/dL reflect non-error-code BG values, so we should zero temp for those + if (bg <= 10 || bg === 38 || noise >= 3) { //Dexcom is in ??? mode or calibrating, or xDrip reports high noise + rT.reason = "CGM is calibrating, in ??? state, or noise is high"; } if (minAgo > 12 || minAgo < -5) { // Dexcom data is too old, or way in the future rT.reason = "If current system time "+systemTime+" is correct, then BG data is too old. The last BG data was read "+minAgo+"m ago at "+bgTime; + // if BG is too old/noisy, or is changing less than 1 mg/dL/5m for 45m, cancel any high temps and shorten any long zero temps + } else if ( bg > 60 && glucose_status == 0 && glucose_status.short_avgdelta > -1 && glucose_status.short_avgdelta < 1 && glucose_status.long_avgdelta > -1 && glucose_status.long_avgdelta < 1 ) { + if ( glucose_status.last_cal && glucose_status.last_cal < 3 ) { + rT.reason = "CGM was just calibrated"; + } else { + rT.reason = "Error: CGM data is unchanged for the past ~45m"; + } } - if (bg < 39 || minAgo > 12 || minAgo < -5) { - if (currenttemp.rate >= basal) { // high temp is running - rT.reason += ". Canceling high temp basal of "+currenttemp.rate; + if (bg <= 10 || bg === 38 || noise >= 3 || minAgo > 12 || minAgo < -5 || ( bg > 60 && glucose_status == 0 && glucose_status.short_avgdelta > -1 && glucose_status.short_avgdelta < 1 && glucose_status.long_avgdelta > -1 && glucose_status.long_avgdelta < 1 ) ) { + if (currenttemp.rate > basal) { // high temp is running + rT.reason += ". Replacing high temp basal of "+currenttemp.rate+" with neutral temp of "+basal; rT.deliverAt = deliverAt; rT.temp = 'absolute'; - rT.duration = 0; - rT.rate = 0; + rT.duration = 30; + rT.rate = basal; return rT; //return tempBasalFunctions.setTempBasal(basal, 30, profile, rT, currenttemp); - } else if ( currenttemp.rate == 0 && currenttemp.duration > 30 ) { //shorten long zero temps to 30m + } else if ( currenttemp.rate === 0 && currenttemp.duration > 30 ) { //shorten long zero temps to 30m rT.reason += ". Shortening " + currenttemp.duration + "m long zero temp to 30m. "; rT.deliverAt = deliverAt; rT.temp = 'absolute'; @@ -119,10 +134,10 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ if ( profile.half_basal_exercise_target ) { var halfBasalTarget = profile.half_basal_exercise_target; } else { - var halfBasalTarget = 160; // when temptarget is 160 mg/dL, run 50% basal (120 = 75%; 140 = 60%) + halfBasalTarget = 160; // when temptarget is 160 mg/dL, run 50% basal (120 = 75%; 140 = 60%) // 80 mg/dL with low_temptarget_lowers_sensitivity would give 1.5x basal, but is limited to autosens_max (1.2x by default) } - if ( high_temptarget_raises_sensitivity && profile.temptargetSet && target_bg > normalTarget + 10 + if ( high_temptarget_raises_sensitivity && profile.temptargetSet && target_bg > normalTarget || profile.low_temptarget_lowers_sensitivity && profile.temptargetSet && target_bg < normalTarget ) { // w/ target 100, temp target 110 = .89, 120 = 0.8, 140 = 0.67, 160 = .57, and 200 = .44 // e.g.: Sensitivity ratio set to 0.8 based on temp target of 120; Adjusting basal from 1.65 to 1.35; ISF from 58.9 to 73.6 @@ -132,36 +147,36 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // limit sensitivityRatio to profile.autosens_max (1.2x by default) sensitivityRatio = Math.min(sensitivityRatio, profile.autosens_max); sensitivityRatio = round(sensitivityRatio,2); - console.error("Sensitivity ratio set to "+sensitivityRatio+" based on temp target of "+target_bg+"; "); - } else if (typeof autosens_data !== 'undefined' ) { + process.stderr.write("Sensitivity ratio set to "+sensitivityRatio+" based on temp target of "+target_bg+"; "); + } else if (typeof autosens_data !== 'undefined' && autosens_data) { sensitivityRatio = autosens_data.ratio; - console.error("Autosens ratio: "+sensitivityRatio+"; "); + process.stderr.write("Autosens ratio: "+sensitivityRatio+"; "); } if (sensitivityRatio) { basal = profile.current_basal * sensitivityRatio; basal = round_basal(basal, profile); - if (basal != profile_current_basal) { - console.error("Adjusting basal from "+profile_current_basal+" to "+basal+"; "); + if (basal !== profile_current_basal) { + process.stderr.write("Adjusting basal from "+profile_current_basal+" to "+basal+"; "); } else { - console.error("Basal unchanged: "+basal+"; "); + process.stderr.write("Basal unchanged: "+basal+"; "); } } // adjust min, max, and target BG for sensitivity, such that 50% increase in ISF raises target from 100 to 120 if (profile.temptargetSet) { - //console.error("Temp Target set, not adjusting with autosens; "); - } else if (typeof autosens_data !== 'undefined' ) { + //process.stderr.write("Temp Target set, not adjusting with autosens; "); + } else if (typeof autosens_data !== 'undefined' && autosens_data) { if ( profile.sensitivity_raises_target && autosens_data.ratio < 1 || profile.resistance_lowers_target && autosens_data.ratio > 1 ) { // with a target of 100, default 0.7-1.2 autosens min/max range would allow a 93-117 target range min_bg = round((min_bg - 60) / autosens_data.ratio) + 60; max_bg = round((max_bg - 60) / autosens_data.ratio) + 60; - new_target_bg = round((target_bg - 60) / autosens_data.ratio) + 60; + var new_target_bg = round((target_bg - 60) / autosens_data.ratio) + 60; // don't allow target_bg below 80 new_target_bg = Math.max(80, new_target_bg); - if (target_bg == new_target_bg) { - console.error("target_bg unchanged: "+new_target_bg+"; "); + if (target_bg === new_target_bg) { + process.stderr.write("target_bg unchanged: "+new_target_bg+"; "); } else { - console.error("target_bg from "+target_bg+" to "+new_target_bg+"; "); + process.stderr.write("target_bg from "+target_bg+" to "+new_target_bg+"; "); } target_bg = new_target_bg; } @@ -197,34 +212,33 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ var profile_sens = round(profile.sens,1) var sens = profile.sens; - if (typeof autosens_data !== 'undefined' ) { + if (typeof autosens_data !== 'undefined' && autosens_data) { sens = profile.sens / sensitivityRatio; sens = round(sens, 1); - if (sens != profile_sens) { - console.error("ISF from "+profile_sens+" to "+sens); + if (sens !== profile_sens) { + process.stderr.write("ISF from "+profile_sens+" to "+sens); } else { - console.error("ISF unchanged: "+sens); + process.stderr.write("ISF unchanged: "+sens); } - //console.error(" (autosens ratio "+sensitivityRatio+")"); + //process.stderr.write(" (autosens ratio "+sensitivityRatio+")"); } console.error("; CR:",profile.carb_ratio); // compare currenttemp to iob_data.lastTemp and cancel temp if they don't match var lastTempAge; if (typeof iob_data.lastTemp !== 'undefined' ) { - lastTempAge = round(( new Date().getTime() - iob_data.lastTemp.date ) / 60000); // in minutes - // } ---- added to not produce errors + lastTempAge = round(( new Date(systemTime).getTime() - iob_data.lastTemp.date ) / 60000); // in minutes } else { lastTempAge = 0; } //console.error("currenttemp:",currenttemp,"lastTemp:",JSON.stringify(iob_data.lastTemp),"lastTempAge:",lastTempAge,"m"); - tempModulus = (lastTempAge + currenttemp.duration) % 30; + var tempModulus = (lastTempAge + currenttemp.duration) % 30; console.error("currenttemp:",currenttemp,"lastTempAge:",lastTempAge,"m","tempModulus:",tempModulus,"m"); rT.temp = 'absolute'; rT.deliverAt = deliverAt; - if ( microBolusAllowed && currenttemp && iob_data.lastTemp && currenttemp.rate != iob_data.lastTemp.rate ) { - rT.reason = "Warning: currenttemp rate "+currenttemp.rate+" != lastTemp rate "+iob_data.lastTemp.rate+" from pumphistory; setting neutral temp of "+basal+"."; - return tempBasalFunctions.setTempBasal(basal, 30, profile, rT, currenttemp); + if ( microBolusAllowed && currenttemp && iob_data.lastTemp && currenttemp.rate !== iob_data.lastTemp.rate && lastTempAge > 10 && currenttemp.duration ) { + rT.reason = "Warning: currenttemp rate "+currenttemp.rate+" != lastTemp rate "+iob_data.lastTemp.rate+" from pumphistory; canceling temp"; + return tempBasalFunctions.setTempBasal(0, 0, profile, rT, currenttemp); } if ( currenttemp && iob_data.lastTemp && currenttemp.duration > 0 ) { // TODO: fix this (lastTemp.duration is how long it has run; currenttemp.duration is time left @@ -234,10 +248,10 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ //} //console.error(lastTempAge, round(iob_data.lastTemp.duration,1), round(lastTempAge - iob_data.lastTemp.duration,1)); var lastTempEnded = lastTempAge - iob_data.lastTemp.duration - if ( lastTempEnded > 5 ) { - rT.reason = "Warning: currenttemp running but lastTemp from pumphistory ended "+lastTempEnded+"m ago; setting neutral temp of "+basal+"."; + if ( lastTempEnded > 5 && lastTempAge > 10 ) { + rT.reason = "Warning: currenttemp running but lastTemp from pumphistory ended "+lastTempEnded+"m ago; canceling temp"; //console.error(currenttemp, round(iob_data.lastTemp,1), round(lastTempAge,1)); - return tempBasalFunctions.setTempBasal(basal, 30, profile, rT, currenttemp); + return tempBasalFunctions.setTempBasal(0, 0, profile, rT, currenttemp); } // TODO: figure out a way to do this check that doesn't fail across basal schedule boundaries //if ( tempModulus < 25 && tempModulus > 5 ) { @@ -264,37 +278,44 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ if (iob_data.iob > 0) { var naive_eventualBG = round( bg - (iob_data.iob * sens) ); } else { // if IOB is negative, be more conservative and use the lower of sens, profile.sens - var naive_eventualBG = round( bg - (iob_data.iob * Math.min(sens, profile.sens) ) ); + naive_eventualBG = round( bg - (iob_data.iob * Math.min(sens, profile.sens) ) ); } // and adjust it for the deviation above var eventualBG = naive_eventualBG + deviation; - // calculate what portion of that is due to bolussnooze - //var bolusContrib = iob_data.bolussnooze * sens; - // and add it back in to get snoozeBG, plus another 50% to avoid low-temping at mealtime - //var naive_snoozeBG = round( naive_eventualBG + 1.5 * bolusContrib ); - // adjust that for deviation like we did eventualBG - //var snoozeBG = naive_snoozeBG + deviation; - // adjust target BG range if needed to safely bring down high BG faster without causing lows - if ( bg > max_bg && profile.adv_target_adjustments && ! profile.temptargetSet ) { + // raise target for noisy / raw CGM data + if (glucose_status.noise >= 2) { + // increase target at least 10% (default 30%) for raw / noisy data + var noisyCGMTargetMultiplier = Math.max( 1.1, profile.noisyCGMTargetMultiplier ); + // don't allow maxRaw above 250 + var maxRaw = Math.min( 250, profile.maxRaw ); + var adjustedMinBG = round(Math.min(200, min_bg * noisyCGMTargetMultiplier )); + var adjustedTargetBG = round(Math.min(200, target_bg * noisyCGMTargetMultiplier )); + var adjustedMaxBG = round(Math.min(200, max_bg * noisyCGMTargetMultiplier )); + process.stderr.write("Raising target_bg for noisy / raw CGM data, from "+target_bg+" to "+adjustedTargetBG+"; "); + min_bg = adjustedMinBG; + target_bg = adjustedTargetBG; + max_bg = adjustedMaxBG; + // adjust target BG range if configured to bring down high BG faster + } else if ( bg > max_bg && profile.adv_target_adjustments && ! profile.temptargetSet ) { // with target=100, as BG rises from 100 to 160, adjustedTarget drops from 100 to 80 - var adjustedMinBG = round(Math.max(80, min_bg - (bg - min_bg)/3 ),0); - var adjustedTargetBG =round( Math.max(80, target_bg - (bg - target_bg)/3 ),0); - var adjustedMaxBG = round(Math.max(80, max_bg - (bg - max_bg)/3 ),0); + adjustedMinBG = round(Math.max(80, min_bg - (bg - min_bg)/3 ),0); + adjustedTargetBG =round( Math.max(80, target_bg - (bg - target_bg)/3 ),0); + adjustedMaxBG = round(Math.max(80, max_bg - (bg - max_bg)/3 ),0); // if eventualBG, naive_eventualBG, and target_bg aren't all above adjustedMinBG, don’t use it //console.error("naive_eventualBG:",naive_eventualBG+", eventualBG:",eventualBG); if (eventualBG > adjustedMinBG && naive_eventualBG > adjustedMinBG && min_bg > adjustedMinBG) { - console.error("Adjusting targets for high BG: min_bg from "+min_bg+" to "+adjustedMinBG+"; "); + process.stderr.write("Adjusting targets for high BG: min_bg from "+min_bg+" to "+adjustedMinBG+"; "); min_bg = adjustedMinBG; } else { - console.error("min_bg unchanged: "+min_bg+"; "); + process.stderr.write("min_bg unchanged: "+min_bg+"; "); } // if eventualBG, naive_eventualBG, and target_bg aren't all above adjustedTargetBG, don’t use it if (eventualBG > adjustedTargetBG && naive_eventualBG > adjustedTargetBG && target_bg > adjustedTargetBG) { - console.error("target_bg from "+target_bg+" to "+adjustedTargetBG+"; "); + process.stderr.write("target_bg from "+target_bg+" to "+adjustedTargetBG+"; "); target_bg = adjustedTargetBG; } else { - console.error("target_bg unchanged: "+target_bg+"; "); + process.stderr.write("target_bg unchanged: "+target_bg+"; "); } // if eventualBG, naive_eventualBG, and max_bg aren't all above adjustedMaxBG, don’t use it if (eventualBG > adjustedMaxBG && naive_eventualBG > adjustedMaxBG && max_bg > adjustedMaxBG) { @@ -321,7 +342,6 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ , 'bg': bg , 'tick': tick , 'eventualBG': eventualBG - //, 'snoozeBG': snoozeBG , 'insulinReq': 0 , 'reservoir' : reservoir_data // The expected reservoir volume at which to deliver the microbolus (the reservoir volume from right before the last pumphistory run) , 'deliverAt' : deliverAt // The time at which the microbolus should be delivered @@ -353,7 +373,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // enable SMB/UAM (if enabled in preferences) while we have COB } else if (profile.enableSMB_with_COB === true && meal_data.mealCOB) { if (meal_data.bwCarbs) { - if (profile.A52_risk_enable) { + if (profile.A52_risk_enable === true) { console.error("Warning: SMB enabled with Bolus Wizard carbs: be sure to easy bolus 30s before using Bolus Wizard") enableSMB=true; } else { @@ -365,9 +385,9 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ } // enable SMB/UAM (if enabled in preferences) for a full 6 hours after any carb entry // (6 hours is defined in carbWindow in lib/meal/total.js) - } else if (profile.enableSMB_after_carbs === true && meal_data.carbs ) { + } else if ((profile.enableSMB_after_carbs === true || profile.enableSMB_with_carbs === true) && meal_data.carbs ) { if (meal_data.bwCarbs) { - if (profile.A52_risk_enable) { + if (profile.A52_risk_enable === true) { console.error("Warning: SMB enabled with Bolus Wizard carbs: be sure to easy bolus 30s before using Bolus Wizard") enableSMB=true; } else { @@ -380,7 +400,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // enable SMB/UAM (if enabled in preferences) if a low temptarget is set } else if (profile.enableSMB_with_temptarget === true && (profile.temptargetSet && target_bg < 100)) { if (meal_data.bwFound) { - if (profile.A52_risk_enable) { + if (profile.A52_risk_enable === true) { console.error("Warning: SMB enabled within 6h of using Bolus Wizard: be sure to easy bolus 30s before using Bolus Wizard") enableSMB=true; } else { @@ -404,7 +424,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ enableSMB=true; } } else { - console.error("SMB disabled (no enableSMB preferences active)"); + console.error("SMB disabled for this run (no selected enableSMB criteria met)"); } // enable UAM (if enabled in preferences) var enableUAM=(profile.enableUAM); @@ -417,43 +437,48 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // calculate current carb absorption rate, and how long to absorb all carbs // CI = current carb impact on BG in mg/dL/5m ci = round((minDelta - bgi),1); - uci = round((minDelta - bgi),1); + var uci = round((minDelta - bgi),1); // ISF (mg/dL/U) / CR (g/U) = CSF (mg/dL/g) - if (profile.temptargetSet) { + + // TODO: remove commented-out code for old behavior + //if (profile.temptargetSet) { // if temptargetSet, use unadjusted profile.sens to allow activity mode sensitivityRatio to adjust CR - var csf = profile.sens / profile.carb_ratio; - } else { + //var csf = profile.sens / profile.carb_ratio; + //} else { // otherwise, use autosens-adjusted sens to counteract autosens meal insulin dosing adjustments // so that autotuned CR is still in effect even when basals and ISF are being adjusted by autosens - var csf = sens / profile.carb_ratio; - } + //var csf = sens / profile.carb_ratio; + //} + // use autosens-adjusted sens to counteract autosens meal insulin dosing adjustments so that + // autotuned CR is still in effect even when basals and ISF are being adjusted by TT or autosens + // this avoids overdosing insulin for large meals when low temp targets are active + csf = sens / profile.carb_ratio; + console.error("profile.sens:",profile.sens,"sens:",sens,"CSF:",csf); + var maxCarbAbsorptionRate = 30; // g/h; maximum rate to assume carbs will absorb if no CI observed // limit Carb Impact to maxCarbAbsorptionRate * csf in mg/dL per 5m - maxCI = round(maxCarbAbsorptionRate*csf*5/60,1) + var maxCI = round(maxCarbAbsorptionRate*csf*5/60,1) if (ci > maxCI) { console.error("Limiting carb impact from",ci,"to",maxCI,"mg/dL/5m (",maxCarbAbsorptionRate,"g/h )"); ci = maxCI; } - // set meal_carbimpact high enough to absorb all meal carbs over 6 hours - // total_impact (mg/dL) = CSF (mg/dL/g) * carbs (g) - //console.error(csf * meal_data.carbs); - // meal_carbimpact (mg/dL/5m) = CSF (mg/dL/g) * carbs (g) / 6 (h) * (1h/60m) * 5 (m/5m) * 2 (for linear decay) - //var meal_carbimpact = round((csf * meal_data.carbs / 6 / 60 * 5 * 2),1) - var remainingCATimeMin = 3; // h; before carb absorption starts - // adjust remainingCATime (instead of CR) for autosens - remainingCATimeMin = remainingCATimeMin / sensitivityRatio; + var remainingCATimeMin = 3; // h; duration of expected not-yet-observed carb absorption + // adjust remainingCATime (instead of CR) for autosens if sensitivityRatio defined + if (sensitivityRatio){ + remainingCATimeMin = remainingCATimeMin / sensitivityRatio; + } // 20 g/h means that anything <= 60g will get a remainingCATimeMin, 80g will get 4h, and 120g 6h // when actual absorption ramps up it will take over from remainingCATime var assumedCarbAbsorptionRate = 20; // g/h; maximum rate to assume carbs will absorb if no CI observed - var remainingCATime = remainingCATimeMin; // added by mike https://github.com/openaps/oref0/issues/884 + var remainingCATime = remainingCATimeMin; if (meal_data.carbs) { // if carbs * assumedCarbAbsorptionRate > remainingCATimeMin, raise it // so <= 90g is assumed to take 3h, and 120g=4h remainingCATimeMin = Math.max(remainingCATimeMin, meal_data.mealCOB/assumedCarbAbsorptionRate); - var lastCarbAge = round(( new Date().getTime() - meal_data.lastCarbTime ) / 60000); + var lastCarbAge = round(( new Date(systemTime).getTime() - meal_data.lastCarbTime ) / 60000); //console.error(meal_data.lastCarbTime, lastCarbAge); - fractionCOBAbsorbed = ( meal_data.carbs - meal_data.mealCOB ) / meal_data.carbs; + var fractionCOBAbsorbed = ( meal_data.carbs - meal_data.mealCOB ) / meal_data.carbs; remainingCATime = remainingCATimeMin + 1.5 * lastCarbAge/60; remainingCATime = round(remainingCATime,1); //console.error(fractionCOBAbsorbed, remainingCATimeAdjustment, remainingCATime) @@ -478,7 +503,6 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // remainingCIpeak (mg/dL/5m) = remainingCarbs (g) * CSF (mg/dL/g) * 5 (m/5m) * 1h/60m / (remainingCATime/2) (h) var remainingCIpeak = remainingCarbs * csf * 5 / 60 / (remainingCATime/2); //console.error(profile.min_5m_carbimpact,ci,totalCI,totalCA,remainingCarbs,remainingCI,remainingCATime); - //if (meal_data.mealCOB * 3 > meal_data.carbs) { } // calculate peak deviation in last hour, and slope from that to current deviation var slopeFromMaxDeviation = round(meal_data.slopeFromMaxDeviation,2); @@ -488,17 +512,17 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ var slopeFromDeviations = Math.min(slopeFromMaxDeviation,-slopeFromMinDeviation/3); //console.error(slopeFromMaxDeviation); - aci = 10; + var aci = 10; //5m data points = g * (1U/10g) * (40mg/dL/1U) / (mg/dL/5m) // duration (in 5m data points) = COB (g) * CSF (mg/dL/g) / ci (mg/dL/5m) // limit cid to remainingCATime hours: the reset goes to remainingCI - if (ci == 0) { + if (ci === 0) { // avoid divide by zero cid = 0; } else { cid = Math.min(remainingCATime*60/5/2,Math.max(0, meal_data.mealCOB * csf / ci )); } - acid = Math.max(0, meal_data.mealCOB * csf / aci ); + var acid = Math.max(0, meal_data.mealCOB * csf / aci ); // duration (hours) = duration (5m) * 5 / 60 * 2 (to account for linear decay) console.error("Carb Impact:",ci,"mg/dL per 5m; CI Duration:",round(cid*5/60*2,1),"hours; remaining CI (~2h peak):",round(remainingCIpeak,1),"mg/dL per 5m"); //console.error("Accel. Carb Impact:",aci,"mg/dL per 5m; ACI Duration:",round(acid*5/60*2,1),"hours"); @@ -529,18 +553,18 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ try { iobArray.forEach(function(iobTick) { //console.error(iobTick); - predBGI = round(( -iobTick.activity * sens * 5 ), 2); - predZTBGI = round(( -iobTick.iobWithZeroTemp.activity * sens * 5 ), 2); + var predBGI = round(( -iobTick.activity * sens * 5 ), 2); + var predZTBGI = round(( -iobTick.iobWithZeroTemp.activity * sens * 5 ), 2); // for IOBpredBGs, predicted deviation impact drops linearly from current deviation down to zero // over 60 minutes (data points every 5m) - predDev = ci * ( 1 - Math.min(1,IOBpredBGs.length/(60/5)) ); + var predDev = ci * ( 1 - Math.min(1,IOBpredBGs.length/(60/5)) ); IOBpredBG = IOBpredBGs[IOBpredBGs.length-1] + predBGI + predDev; // calculate predBGs with long zero temp without deviations - ZTpredBG = ZTpredBGs[ZTpredBGs.length-1] + predZTBGI; + var ZTpredBG = ZTpredBGs[ZTpredBGs.length-1] + predZTBGI; // for COBpredBGs, predicted carb impact drops linearly from current carb impact down to zero // eventually accounting for all carbs (if they can be absorbed over DIA) - predCI = Math.max(0, Math.max(0,ci) * ( 1 - COBpredBGs.length/Math.max(cid*2,1) ) ); - predACI = Math.max(0, Math.max(0,aci) * ( 1 - COBpredBGs.length/Math.max(acid*2,1) ) ); + var predCI = Math.max(0, Math.max(0,ci) * ( 1 - COBpredBGs.length/Math.max(cid*2,1) ) ); + var predACI = Math.max(0, Math.max(0,aci) * ( 1 - COBpredBGs.length/Math.max(acid*2,1) ) ); // if any carbs aren't absorbed after remainingCATime hours, assume they'll absorb in a /\ shaped // bilinear curve peaking at remainingCIpeak at remainingCATime/2 hours (remainingCATime/2*12 * 5m) // and ending at remainingCATime h (remainingCATime*12 * 5m intervals) @@ -549,18 +573,18 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ remainingCItotal += predCI+remainingCI; remainingCIs.push(round(remainingCI,0)); predCIs.push(round(predCI,0)); - //console.error(round(predCI,1)+"+"+round(remainingCI,1)+" "); + //process.stderr.write(round(predCI,1)+"+"+round(remainingCI,1)+" "); COBpredBG = COBpredBGs[COBpredBGs.length-1] + predBGI + Math.min(0,predDev) + predCI + remainingCI; - aCOBpredBG = aCOBpredBGs[aCOBpredBGs.length-1] + predBGI + Math.min(0,predDev) + predACI; + var aCOBpredBG = aCOBpredBGs[aCOBpredBGs.length-1] + predBGI + Math.min(0,predDev) + predACI; // for UAMpredBGs, predicted carb impact drops at slopeFromDeviations // calculate predicted CI from UAM based on slopeFromDeviations - predUCIslope = Math.max(0, uci + ( UAMpredBGs.length*slopeFromDeviations ) ); + var predUCIslope = Math.max(0, uci + ( UAMpredBGs.length*slopeFromDeviations ) ); // if slopeFromDeviations is too flat, predicted deviation impact drops linearly from // current deviation down to zero over 3h (data points every 5m) - predUCImax = Math.max(0, uci * ( 1 - UAMpredBGs.length/Math.max(3*60/5,1) ) ); + var predUCImax = Math.max(0, uci * ( 1 - UAMpredBGs.length/Math.max(3*60/5,1) ) ); //console.error(predUCIslope, predUCImax); // predicted CI from UAM is the lesser of CI based on deviationSlope or DIA - predUCI = Math.min(predUCIslope, predUCImax); + var predUCI = Math.min(predUCIslope, predUCImax); if(predUCI>0) { //console.error(UAMpredBGs.length,slopeFromDeviations, predUCI); UAMduration=round((UAMpredBGs.length+1)*5/60,1); @@ -599,19 +623,18 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // set eventualBG to include effect of carbs //console.error("PredBGs:",JSON.stringify(predBGs)); } catch (e) { - console.error("Problem with iobArray. Optional feature Advanced Meal Assist disabled:",e); + console.error("Problem with iobArray. Optional feature Advanced Meal Assist disabled"); } if (meal_data.mealCOB) { console.error("predCIs (mg/dL/5m):",predCIs.join(" ")); console.error("remainingCIs: ",remainingCIs.join(" ")); } - //,"totalCA:",round(totalCA,2),"remainingCItotal/csf+totalCA:",round(remainingCItotal/csf+totalCA,2)); rT.predBGs = {}; IOBpredBGs.forEach(function(p, i, theArray) { theArray[i] = round(Math.min(401,Math.max(39,p))); }); for (var i=IOBpredBGs.length-1; i > 12; i--) { - if (IOBpredBGs[i-1] != IOBpredBGs[i]) { break; } + if (IOBpredBGs[i-1] !== IOBpredBGs[i]) { break; } else { IOBpredBGs.pop(); } } rT.predBGs.IOB = IOBpredBGs; @@ -619,10 +642,9 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ ZTpredBGs.forEach(function(p, i, theArray) { theArray[i] = round(Math.min(401,Math.max(39,p))); }); - for (var i=ZTpredBGs.length-1; i > 6; i--) { - //if (ZTpredBGs[i-1] != ZTpredBGs[i]) { break; } + for (i=ZTpredBGs.length-1; i > 6; i--) { // stop displaying ZTpredBGs once they're rising and above target - if (ZTpredBGs[i-1] >= ZTpredBGs[i] || ZTpredBGs[i] < target_bg) { break; } + if (ZTpredBGs[i-1] >= ZTpredBGs[i] || ZTpredBGs[i] <= target_bg) { break; } else { ZTpredBGs.pop(); } } rT.predBGs.ZT = ZTpredBGs; @@ -631,19 +653,17 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ aCOBpredBGs.forEach(function(p, i, theArray) { theArray[i] = round(Math.min(401,Math.max(39,p))); }); - for (var i=aCOBpredBGs.length-1; i > 12; i--) { - if (aCOBpredBGs[i-1] != aCOBpredBGs[i]) { break; } + for (i=aCOBpredBGs.length-1; i > 12; i--) { + if (aCOBpredBGs[i-1] !== aCOBpredBGs[i]) { break; } else { aCOBpredBGs.pop(); } } - // disable for now. may want to add a preference to re-enable - //rT.predBGs.aCOB = aCOBpredBGs; } if (meal_data.mealCOB > 0 && ( ci > 0 || remainingCIpeak > 0 )) { COBpredBGs.forEach(function(p, i, theArray) { theArray[i] = round(Math.min(401,Math.max(39,p))); }); - for (var i=COBpredBGs.length-1; i > 12; i--) { - if (COBpredBGs[i-1] != COBpredBGs[i]) { break; } + for (i=COBpredBGs.length-1; i > 12; i--) { + if (COBpredBGs[i-1] !== COBpredBGs[i]) { break; } else { COBpredBGs.pop(); } } rT.predBGs.COB = COBpredBGs; @@ -655,8 +675,8 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ UAMpredBGs.forEach(function(p, i, theArray) { theArray[i] = round(Math.min(401,Math.max(39,p))); }); - for (var i=UAMpredBGs.length-1; i > 12; i--) { - if (UAMpredBGs[i-1] != UAMpredBGs[i]) { break; } + for (i=UAMpredBGs.length-1; i > 12; i--) { + if (UAMpredBGs[i-1] !== UAMpredBGs[i]) { break; } else { UAMpredBGs.pop(); } } rT.predBGs.UAM = UAMpredBGs; @@ -666,7 +686,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ } } - // set eventualBG and snoozeBG based on COB or UAM predBGs + // set eventualBG based on COB or UAM predBGs rT.eventualBG = eventualBG; } @@ -733,14 +753,6 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ //console.error("minUAMPredBG:",minUAMPredBG,"minZTGuardBG:",minZTGuardBG,"minZTUAMPredBG:",minZTUAMPredBG); // if any carbs have been entered recently if (meal_data.carbs) { - // average the minIOBPredBG and minUAMPredBG if available - /* - if ( minUAMPredBG < 999 ) { - avgMinPredBG = round( (minIOBPredBG+minUAMPredBG)/2 ); - } else { - avgMinPredBG = minIOBPredBG; - } - */ // if UAM is disabled, use max of minIOBPredBG, minCOBPredBG if ( ! enableUAM && minCOBPredBG < 999 ) { @@ -748,30 +760,27 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // if we have COB, use minCOBPredBG, or blendedMinPredBG if it's higher } else if ( minCOBPredBG < 999 ) { // calculate blendedMinPredBG based on how many carbs remain as COB - //blendedMinPredBG = fractionCarbsLeft*minCOBPredBG + (1-fractionCarbsLeft)*minUAMPredBG; - blendedMinPredBG = fractionCarbsLeft*minCOBPredBG + (1-fractionCarbsLeft)*minZTUAMPredBG; + var blendedMinPredBG = fractionCarbsLeft*minCOBPredBG + (1-fractionCarbsLeft)*minZTUAMPredBG; // if blendedMinPredBG > minCOBPredBG, use that instead minPredBG = round(Math.max(minIOBPredBG, minCOBPredBG, blendedMinPredBG)); // if carbs have been entered, but have expired, use minUAMPredBG } else { - //minPredBG = minUAMPredBG; minPredBG = minZTUAMPredBG; } // in pure UAM mode, use the higher of minIOBPredBG,minUAMPredBG } else if ( enableUAM ) { - //minPredBG = round(Math.max(minIOBPredBG,minUAMPredBG)); minPredBG = round(Math.max(minIOBPredBG,minZTUAMPredBG)); } // make sure minPredBG isn't higher than avgPredBG minPredBG = Math.min( minPredBG, avgPredBG ); - console.error("minPredBG: "+minPredBG+" minIOBPredBG: "+minIOBPredBG+" minZTGuardBG: "+minZTGuardBG); + process.stderr.write("minPredBG: "+minPredBG+" minIOBPredBG: "+minIOBPredBG+" minZTGuardBG: "+minZTGuardBG); if (minCOBPredBG < 999) { - console.error(" minCOBPredBG: "+minCOBPredBG); + process.stderr.write(" minCOBPredBG: "+minCOBPredBG); } if (minUAMPredBG < 999) { - console.error(" minUAMPredBG: "+minUAMPredBG); + process.stderr.write(" minUAMPredBG: "+minUAMPredBG); } console.error(" avgPredBG:",avgPredBG,"COB:",meal_data.mealCOB,"/",meal_data.carbs); // But if the COB line falls off a cliff, don't trust UAM too much: @@ -790,9 +799,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ rT.reason += ", UAMpredBG " + convert_bg(lastUAMpredBG, profile) } rT.reason += "; "; - //var bgUndershoot = threshold - Math.min(minGuardBG, Math.max( naive_eventualBG, eventualBG )); // use naive_eventualBG if above 40, but switch to minGuardBG if both eventualBGs hit floor of 39 - //var carbsReqBG = Math.max( naive_eventualBG, eventualBG ); var carbsReqBG = naive_eventualBG; if ( carbsReqBG < 40 ) { carbsReqBG = Math.min( minGuardBG, carbsReqBG ); @@ -802,14 +809,14 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ var minutesAboveMinBG = 240; var minutesAboveThreshold = 240; if (meal_data.mealCOB > 0 && ( ci > 0 || remainingCIpeak > 0 )) { - for (var i=0; i= 55 ) { + rT.reason += "; Canceling temp at " + rT.deliverAt.getMinutes() + "m past the hour. "; + return tempBasalFunctions.setTempBasal(0, 0, profile, rT, currenttemp); + } + if (eventualBG < min_bg) { // if eventual BG is below target: rT.reason += "Eventual BG " + convert_bg(eventualBG, profile) + " < " + convert_bg(min_bg, profile); // if 5m or 30m avg BG is rising faster than expected delta @@ -904,9 +917,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ } // calculate 30m low-temp required to get projected BG up to target - // use snoozeBG to more gradually ramp in any counteraction of the user's boluses // multiply by 2 to low-temp faster for increased hypo safety - //var insulinReq = 2 * Math.min(0, (snoozeBG - target_bg) / sens); var insulinReq = 2 * Math.min(0, (eventualBG - target_bg) / sens); insulinReq = round( insulinReq , 2); // calculate naiveInsulinReq based on naive_eventualBG @@ -914,7 +925,6 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ naiveInsulinReq = round( naiveInsulinReq , 2); if (minDelta < 0 && minDelta > expectedDelta) { // if we're barely falling, newinsulinReq should be barely negative - //rT.reason += ", Snooze BG " + convert_bg(snoozeBG, profile); var newinsulinReq = round(( insulinReq * (minDelta / expectedDelta) ), 2); //console.error("Increasing insulinReq from " + insulinReq + " to " + newinsulinReq); insulinReq = newinsulinReq; @@ -922,6 +932,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // rate required to deliver insulinReq less insulin over 30m: var rate = basal + (2 * insulinReq); rate = round_basal(rate, profile); + // if required temp < existing temp basal var insulinScheduled = currenttemp.duration * (currenttemp.rate - basal) / 60; // if current temp would deliver a lot (30% of basal) less than the required insulin, @@ -937,18 +948,17 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ } else { // calculate a long enough zero temp to eventually correct back up to target if ( rate <=0 ) { - var bgUndershoot = target_bg - naive_eventualBG; - var worstCaseInsulinReq = bgUndershoot / sens; - var durationReq = round(60*worstCaseInsulinReq / profile.current_basal); + bgUndershoot = target_bg - naive_eventualBG; + worstCaseInsulinReq = bgUndershoot / sens; + durationReq = round(60*worstCaseInsulinReq / profile.current_basal); if (durationReq < 0) { durationReq = 0; - // don't set an SMB zero temp longer than 60 minutess + // don't set a temp longer than 120 minutes } else { durationReq = round(durationReq/30)*30; - durationReq = Math.min(60,Math.max(0,durationReq)); + durationReq = Math.min(120,Math.max(0,durationReq)); } //console.error(durationReq); - //rT.reason += "insulinReq " + insulinReq + "; " if (durationReq > 0) { rT.reason += ", setting " + durationReq + "m zero temp. "; return tempBasalFunctions.setTempBasal(rate, durationReq, profile, rT, currenttemp); @@ -1011,15 +1021,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // insulinReq is the additional insulin required to get minPredBG down to target_bg //console.error(minPredBG,eventualBG); - //var insulinReq = round( (Math.min(minPredBG,eventualBG) - target_bg) / sens, 2); - var insulinReq = round( (Math.min(minPredBG,eventualBG) - target_bg) / sens, 2); - // when dropping, but not as fast as expected, reduce insulinReq proportionally - // to the what fraction of expectedDelta we're dropping at - //if (minDelta < 0 && minDelta > expectedDelta) { - //var newinsulinReq = round(( insulinReq * (1 - (minDelta / expectedDelta)) ), 2); - //console.error("Reducing insulinReq from " + insulinReq + " to " + newinsulinReq + " for minDelta " + minDelta + " vs. expectedDelta " + expectedDelta); - //insulinReq = newinsulinReq; - //} + insulinReq = round( (Math.min(minPredBG,eventualBG) - target_bg) / sens, 2); // if that would put us over max_iob, then reduce accordingly if (insulinReq > max_iob-iob_data.iob) { rT.reason += "max_iob " + max_iob + ", "; @@ -1027,21 +1029,21 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ } // rate required to deliver insulinReq more insulin over 30m: - var rate = basal + (2 * insulinReq); + rate = basal + (2 * insulinReq); rate = round_basal(rate, profile); insulinReq = round(insulinReq,3); rT.insulinReq = insulinReq; //console.error(iob_data.lastBolusTime); // minutes since last bolus - var lastBolusAge = round(( new Date().getTime() - iob_data.lastBolusTime ) / 60000,1); + var lastBolusAge = round(( new Date(systemTime).getTime() - iob_data.lastBolusTime ) / 60000,1); //console.error(lastBolusAge); //console.error(profile.temptargetSet, target_bg, rT.COB); // only allow microboluses with COB or low temp targets, or within DIA hours of a bolus if (microBolusAllowed && enableSMB && bg > threshold) { // never bolus more than maxSMBBasalMinutes worth of basal - mealInsulinReq = round( meal_data.mealCOB / profile.carb_ratio ,3); - if (typeof profile.maxSMBBasalMinutes == 'undefined' ) { - maxBolus = round( profile.current_basal * 30 / 60 ,1); + var mealInsulinReq = round( meal_data.mealCOB / profile.carb_ratio ,3); + if (typeof profile.maxSMBBasalMinutes === 'undefined' ) { + var maxBolus = round( profile.current_basal * 30 / 60 ,1); console.error("profile.maxSMBBasalMinutes undefined: defaulting to 30m"); // if IOB covers more than COB, limit maxBolus to 30m of basal } else if ( iob_data.iob > mealInsulinReq && iob_data.iob > 0 ) { @@ -1059,11 +1061,11 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ } // bolus 1/2 the insulinReq, up to maxBolus, rounding down to nearest bolus increment var roundSMBTo = 1 / profile.bolus_increment; - microBolus = Math.floor(Math.min(insulinReq/2,maxBolus)*roundSMBTo)/roundSMBTo; + var microBolus = Math.floor(Math.min(insulinReq/2,maxBolus)*roundSMBTo)/roundSMBTo; // calculate a long enough zero temp to eventually correct back up to target var smbTarget = target_bg; - var worstCaseInsulinReq = (smbTarget - (naive_eventualBG + minIOBPredBG)/2 ) / sens; - var durationReq = round(60*worstCaseInsulinReq / profile.current_basal); + worstCaseInsulinReq = (smbTarget - (naive_eventualBG + minIOBPredBG)/2 ) / sens; + durationReq = round(60*worstCaseInsulinReq / profile.current_basal); // if insulinReq > 0 but not enough for a microBolus, don't set an SMB zero temp if (insulinReq > 0 && microBolus < profile.bolus_increment) { @@ -1073,10 +1075,10 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ var smbLowTempReq = 0; if (durationReq <= 0) { durationReq = 0; - // don't set a temp longer than 120 minutes + // don't set an SMB zero temp longer than 60 minutes } else if (durationReq >= 30) { durationReq = round(durationReq/30)*30; - durationReq = Math.min(120,Math.max(0,durationReq)); + durationReq = Math.min(60,Math.max(0,durationReq)); } else { // if SMB durationReq is less than 30m, set a nonzero low temp smbLowTempReq = round( basal * durationReq/30 ,2); @@ -1118,11 +1120,6 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ return rT; } - // if insulinReq is negative, snoozeBG > target_bg, and lastCOBpredBG > target_bg, set a neutral temp - //if (insulinReq < 0 && snoozeBG > target_bg && lastCOBpredBG > target_bg) { - //rT.reason += "; SMB bolus snooze: setting current basal of " + basal + " as temp. "; - //return tempBasalFunctions.setTempBasal(basal, 30, profile, rT, currenttemp); - //} } var maxSafeBasal = tempBasalFunctions.getMaxSafeBasal(profile); @@ -1132,13 +1129,13 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ rate = round_basal(maxSafeBasal, profile); } - var insulinScheduled = currenttemp.duration * (currenttemp.rate - basal) / 60; + insulinScheduled = currenttemp.duration * (currenttemp.rate - basal) / 60; if (insulinScheduled >= insulinReq * 2) { // if current temp would deliver >2x more than the required insulin, lower the rate rT.reason += currenttemp.duration + "m@" + (currenttemp.rate).toFixed(2) + " > 2 * insulinReq. Setting temp basal of " + rate + "U/hr. "; return tempBasalFunctions.setTempBasal(rate, 30, profile, rT, currenttemp); } - if (typeof currenttemp.duration == 'undefined' || currenttemp.duration == 0) { // no temp is set + if (typeof currenttemp.duration === 'undefined' || currenttemp.duration === 0) { // no temp is set rT.reason += "no temp, setting " + rate + "U/hr. "; return tempBasalFunctions.setTempBasal(rate, 30, profile, rT, currenttemp); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java index e445a1043d..4701665f0a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java @@ -48,6 +48,7 @@ public class DetermineBasalAdapterSMBJS { private JSONObject mAutosensData = null; private boolean mMicrobolusAllowed; private boolean mSMBAlwaysAllowed; + private long mCurrentTime; private String storedCurrentTemp = null; private String storedIobData = null; @@ -58,6 +59,7 @@ public class DetermineBasalAdapterSMBJS { private String storedAutosens_data = null; private String storedMicroBolusAllowed = null; private String storedSMBAlwaysAllowed = null; + private String storedCurrentTime = null; private String scriptDebug = ""; @@ -87,6 +89,7 @@ public class DetermineBasalAdapterSMBJS { log.debug("Reservoir data: " + "undefined"); log.debug("MicroBolusAllowed: " + (storedMicroBolusAllowed = "" + mMicrobolusAllowed)); log.debug("SMBAlwaysAllowed: " + (storedSMBAlwaysAllowed = "" + mSMBAlwaysAllowed)); +// log.debug("Current Time: " + (storedCurrentTime = "" + mCurrentTime)); } DetermineBasalResultSMB determineBasalResultSMB = null; @@ -129,7 +132,8 @@ public class DetermineBasalAdapterSMBJS { makeParam(mMealData, rhino, scope), setTempBasalFunctionsObj, new Boolean(mMicrobolusAllowed), - makeParam(null, rhino, scope) // reservoir data as undefined + makeParam(null, rhino, scope), // reservoir data as undefined + mCurrentTime }; @@ -163,6 +167,7 @@ public class DetermineBasalAdapterSMBJS { storedCurrentTemp = mCurrentTemp.toString(); storedProfile = mProfile.toString(); storedMeal_data = mMealData.toString(); + //storedCurrentTime = "" + mCurrentTime; return determineBasalResultSMB; @@ -172,6 +177,10 @@ public class DetermineBasalAdapterSMBJS { return storedGlucoseStatus; } + // String getCurrentTimeParam() { + // return storedCurrentTime; + // } + String getCurrentTempParam() { return storedCurrentTemp; } @@ -332,6 +341,8 @@ public class DetermineBasalAdapterSMBJS { mMicrobolusAllowed = microBolusAllowed; mSMBAlwaysAllowed = advancedFiltering; + mCurrentTime = System.currentTimeMillis(); + } private Object makeParam(JSONObject jsonObject, Context rhino, Scriptable scope) { From 724c1edfd59a5d54a6022ceb17972e103b1df69b Mon Sep 17 00:00:00 2001 From: Tim Gunn <2896311+Tornado-Tim@users.noreply.github.com> Date: Fri, 29 Nov 2019 20:23:30 +1300 Subject: [PATCH 13/36] Add Noise to be used later when needed --- .../plugins/iob/iobCobCalculator/GlucoseStatus.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/GlucoseStatus.java b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/GlucoseStatus.java index 159c5865af..512c22d2e4 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/GlucoseStatus.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/GlucoseStatus.java @@ -21,6 +21,7 @@ import info.nightscout.androidaps.utils.Round; public class GlucoseStatus { private static Logger log = LoggerFactory.getLogger(GlucoseStatus.class); public double glucose = 0d; + public double noise = 0d; public double delta = 0d; public double avgdelta = 0d; public double short_avgdelta = 0d; @@ -30,6 +31,7 @@ public class GlucoseStatus { public String log() { return "Glucose: " + DecimalFormatter.to0Decimal(glucose) + " mg/dl " + + "Noise: " + DecimalFormatter.to0Decimal(noise) + " " + "Delta: " + DecimalFormatter.to0Decimal(delta) + " mg/dl" + "Short avg. delta: " + " " + DecimalFormatter.to2Decimal(short_avgdelta) + " mg/dl " + "Long avg. delta: " + DecimalFormatter.to2Decimal(long_avgdelta) + " mg/dl"; @@ -40,6 +42,7 @@ public class GlucoseStatus { public GlucoseStatus round() { this.glucose = Round.roundTo(this.glucose, 0.1); + this.noise = Round.roundTo(this.noise, 0.1); this.delta = Round.roundTo(this.delta, 0.01); this.avgdelta = Round.roundTo(this.avgdelta, 0.01); this.short_avgdelta = Round.roundTo(this.short_avgdelta, 0.01); @@ -89,6 +92,7 @@ public class GlucoseStatus { if (sizeRecords == 1) { GlucoseStatus status = new GlucoseStatus(); status.glucose = now.value; + status.noise = 0d; status.short_avgdelta = 0d; status.delta = 0d; status.long_avgdelta = 0d; @@ -148,6 +152,7 @@ public class GlucoseStatus { GlucoseStatus status = new GlucoseStatus(); status.glucose = now.value; status.date = now_date; + status.noise = 0d; //for now set to nothing as not all CGMs report noise status.short_avgdelta = average(short_deltas); From 39835d3481294b7c974da46ca16dd734bc0dc534 Mon Sep 17 00:00:00 2001 From: Tim Gunn <2896311+Tornado-Tim@users.noreply.github.com> Date: Fri, 29 Nov 2019 21:08:17 +1300 Subject: [PATCH 14/36] bring determine_basal up to date with 0.7.0 --- .../main/assets/OpenAPSSMB/determine-basal.js | 183 +++++++++--------- 1 file changed, 95 insertions(+), 88 deletions(-) diff --git a/app/src/main/assets/OpenAPSSMB/determine-basal.js b/app/src/main/assets/OpenAPSSMB/determine-basal.js index 4d9ed4122c..0ddbd9d601 100644 --- a/app/src/main/assets/OpenAPSSMB/determine-basal.js +++ b/app/src/main/assets/OpenAPSSMB/determine-basal.js @@ -47,6 +47,69 @@ function convert_bg(value, profile) } } +function enable_smb( + profile, + microBolusAllowed, + meal_data, + target_bg +) { + // disable SMB when a high temptarget is set + if (! microBolusAllowed) { + console.error("SMB disabled (!microBolusAllowed)"); + return false; + } else if (! profile.allowSMB_with_high_temptarget && profile.temptargetSet && target_bg > 100) { + console.error("SMB disabled due to high temptarget of",target_bg); + return false; + } else if (meal_data.bwFound === true && profile.A52_risk_enable === false) { + console.error("SMB disabled due to Bolus Wizard activity in the last 6 hours."); + return false; + } + + // enable SMB/UAM if always-on (unless previously disabled for high temptarget) + if (profile.enableSMB_always === true) { + if (meal_data.bwFound) { + console.error("Warning: SMB enabled within 6h of using Bolus Wizard: be sure to easy bolus 30s before using Bolus Wizard"); + } else { + console.error("SMB enabled due to enableSMB_always"); + } + return true; + } + + // enable SMB/UAM (if enabled in preferences) while we have COB + if (profile.enableSMB_with_COB === true && meal_data.mealCOB) { + if (meal_data.bwCarbs) { + console.error("Warning: SMB enabled with Bolus Wizard carbs: be sure to easy bolus 30s before using Bolus Wizard"); + } else { + console.error("SMB enabled for COB of",meal_data.mealCOB); + } + return true; + } + + // enable SMB/UAM (if enabled in preferences) for a full 6 hours after any carb entry + // (6 hours is defined in carbWindow in lib/meal/total.js) + if (profile.enableSMB_after_carbs === true && meal_data.carbs ) { + if (meal_data.bwCarbs) { + console.error("Warning: SMB enabled with Bolus Wizard carbs: be sure to easy bolus 30s before using Bolus Wizard"); + } else { + console.error("SMB enabled for 6h after carb entry"); + } + return true; + } + + // enable SMB/UAM (if enabled in preferences) if a low temptarget is set + if (profile.enableSMB_with_temptarget === true && (profile.temptargetSet && target_bg < 100)) { + if (meal_data.bwFound) { + console.error("Warning: SMB enabled within 6h of using Bolus Wizard: be sure to easy bolus 30s before using Bolus Wizard"); + } else { + console.error("SMB enabled for temptarget of",convert_bg(target_bg, profile)); + } + return true; + } + + console.error("SMB disabled (no enableSMB preferences active or no condition satisfied)"); + return false; +} + var determine_basal = function determine_basal(glucose_status, currenttemp, iob_data, profile, autosens_data, meal_data, tempBasalFunctions, microBolusAllowed, reservoir_data, currentTime) { var rT = {}; //short for requestedTemp @@ -86,7 +149,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ rT.reason = "Error: CGM data is unchanged for the past ~45m"; } } - if (bg <= 10 || bg === 38 || noise >= 3 || minAgo > 12 || minAgo < -5 || ( bg > 60 && glucose_status == 0 && glucose_status.short_avgdelta > -1 && glucose_status.short_avgdelta < 1 && glucose_status.long_avgdelta > -1 && glucose_status.long_avgdelta < 1 ) ) { + if (bg <= 10 || bg === 38 || noise >= 3 || minAgo > 12 || minAgo < -5 || ( bg > 60 && glucose_status == 0 && glucose_status.short_avgdelta > -1 && glucose_status.short_avgdelta < 1 && glucose_status.long_avgdelta > -1 && glucose_status.long_avgdelta < 1 ) ) { if (currenttemp.rate > basal) { // high temp is running rT.reason += ". Replacing high temp basal of "+currenttemp.rate+" with neutral temp of "+basal; rT.deliverAt = deliverAt; @@ -130,7 +193,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ var sensitivityRatio; var high_temptarget_raises_sensitivity = profile.exercise_mode || profile.high_temptarget_raises_sensitivity; - var normalTarget = 100; // evaluate high/low temptarget against 100, not scheduled basal (which might change) + var normalTarget = 100; // evaluate high/low temptarget against 100, not scheduled target (which might change) if ( profile.half_basal_exercise_target ) { var halfBasalTarget = profile.half_basal_exercise_target; } else { @@ -147,24 +210,24 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // limit sensitivityRatio to profile.autosens_max (1.2x by default) sensitivityRatio = Math.min(sensitivityRatio, profile.autosens_max); sensitivityRatio = round(sensitivityRatio,2); - process.stderr.write("Sensitivity ratio set to "+sensitivityRatio+" based on temp target of "+target_bg+"; "); + console.log("Sensitivity ratio set to "+sensitivityRatio+" based on temp target of "+target_bg+"; "); } else if (typeof autosens_data !== 'undefined' && autosens_data) { sensitivityRatio = autosens_data.ratio; - process.stderr.write("Autosens ratio: "+sensitivityRatio+"; "); + console.log("Autosens ratio: "+sensitivityRatio+"; "); } if (sensitivityRatio) { basal = profile.current_basal * sensitivityRatio; basal = round_basal(basal, profile); if (basal !== profile_current_basal) { - process.stderr.write("Adjusting basal from "+profile_current_basal+" to "+basal+"; "); + console.log("Adjusting basal from "+profile_current_basal+" to "+basal+"; "); } else { - process.stderr.write("Basal unchanged: "+basal+"; "); + console.log("Basal unchanged: "+basal+"; "); } } // adjust min, max, and target BG for sensitivity, such that 50% increase in ISF raises target from 100 to 120 if (profile.temptargetSet) { - //process.stderr.write("Temp Target set, not adjusting with autosens; "); + //console.log("Temp Target set, not adjusting with autosens; "); } else if (typeof autosens_data !== 'undefined' && autosens_data) { if ( profile.sensitivity_raises_target && autosens_data.ratio < 1 || profile.resistance_lowers_target && autosens_data.ratio > 1 ) { // with a target of 100, default 0.7-1.2 autosens min/max range would allow a 93-117 target range @@ -174,9 +237,9 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // don't allow target_bg below 80 new_target_bg = Math.max(80, new_target_bg); if (target_bg === new_target_bg) { - process.stderr.write("target_bg unchanged: "+new_target_bg+"; "); + console.log("target_bg unchanged: "+new_target_bg+"; "); } else { - process.stderr.write("target_bg from "+target_bg+" to "+new_target_bg+"; "); + console.log("target_bg from "+target_bg+" to "+new_target_bg+"; "); } target_bg = new_target_bg; } @@ -216,11 +279,11 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ sens = profile.sens / sensitivityRatio; sens = round(sens, 1); if (sens !== profile_sens) { - process.stderr.write("ISF from "+profile_sens+" to "+sens); + console.log("ISF from "+profile_sens+" to "+sens); } else { - process.stderr.write("ISF unchanged: "+sens); + console.log("ISF unchanged: "+sens); } - //process.stderr.write(" (autosens ratio "+sensitivityRatio+")"); + //console.log(" (autosens ratio "+sensitivityRatio+")"); } console.error("; CR:",profile.carb_ratio); @@ -292,7 +355,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ var adjustedMinBG = round(Math.min(200, min_bg * noisyCGMTargetMultiplier )); var adjustedTargetBG = round(Math.min(200, target_bg * noisyCGMTargetMultiplier )); var adjustedMaxBG = round(Math.min(200, max_bg * noisyCGMTargetMultiplier )); - process.stderr.write("Raising target_bg for noisy / raw CGM data, from "+target_bg+" to "+adjustedTargetBG+"; "); + console.log("Raising target_bg for noisy / raw CGM data, from "+target_bg+" to "+adjustedTargetBG+"; "); min_bg = adjustedMinBG; target_bg = adjustedTargetBG; max_bg = adjustedMaxBG; @@ -305,17 +368,17 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // if eventualBG, naive_eventualBG, and target_bg aren't all above adjustedMinBG, don’t use it //console.error("naive_eventualBG:",naive_eventualBG+", eventualBG:",eventualBG); if (eventualBG > adjustedMinBG && naive_eventualBG > adjustedMinBG && min_bg > adjustedMinBG) { - process.stderr.write("Adjusting targets for high BG: min_bg from "+min_bg+" to "+adjustedMinBG+"; "); + console.log("Adjusting targets for high BG: min_bg from "+min_bg+" to "+adjustedMinBG+"; "); min_bg = adjustedMinBG; } else { - process.stderr.write("min_bg unchanged: "+min_bg+"; "); + console.log("min_bg unchanged: "+min_bg+"; "); } // if eventualBG, naive_eventualBG, and target_bg aren't all above adjustedTargetBG, don’t use it if (eventualBG > adjustedTargetBG && naive_eventualBG > adjustedTargetBG && target_bg > adjustedTargetBG) { - process.stderr.write("target_bg from "+target_bg+" to "+adjustedTargetBG+"; "); + console.log("target_bg from "+target_bg+" to "+adjustedTargetBG+"; "); target_bg = adjustedTargetBG; } else { - process.stderr.write("target_bg unchanged: "+target_bg+"; "); + console.log("target_bg unchanged: "+target_bg+"; "); } // if eventualBG, naive_eventualBG, and max_bg aren't all above adjustedMaxBG, don’t use it if (eventualBG > adjustedMaxBG && naive_eventualBG > adjustedMaxBG && max_bg > adjustedMaxBG) { @@ -361,71 +424,13 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ ZTpredBGs.push(bg); UAMpredBGs.push(bg); - // enable SMB whenever we have COB or UAM is enabled - // SMB is disabled by default, unless explicitly enabled in preferences.json - var enableSMB=false; - // disable SMB when a high temptarget is set - if (! microBolusAllowed) { - console.error("SMB disabled (!microBolusAllowed)") - } else if (! profile.allowSMB_with_high_temptarget && profile.temptargetSet && target_bg > 100) { - console.error("SMB disabled due to high temptarget of",target_bg); - enableSMB=false; - // enable SMB/UAM (if enabled in preferences) while we have COB - } else if (profile.enableSMB_with_COB === true && meal_data.mealCOB) { - if (meal_data.bwCarbs) { - if (profile.A52_risk_enable === true) { - console.error("Warning: SMB enabled with Bolus Wizard carbs: be sure to easy bolus 30s before using Bolus Wizard") - enableSMB=true; - } else { - console.error("SMB not enabled for Bolus Wizard COB"); - } - } else { - console.error("SMB enabled for COB of",meal_data.mealCOB); - enableSMB=true; - } - // enable SMB/UAM (if enabled in preferences) for a full 6 hours after any carb entry - // (6 hours is defined in carbWindow in lib/meal/total.js) - } else if ((profile.enableSMB_after_carbs === true || profile.enableSMB_with_carbs === true) && meal_data.carbs ) { - if (meal_data.bwCarbs) { - if (profile.A52_risk_enable === true) { - console.error("Warning: SMB enabled with Bolus Wizard carbs: be sure to easy bolus 30s before using Bolus Wizard") - enableSMB=true; - } else { - console.error("SMB not enabled for Bolus Wizard carbs"); - } - } else { - console.error("SMB enabled for 6h after carb entry"); - enableSMB=true; - } - // enable SMB/UAM (if enabled in preferences) if a low temptarget is set - } else if (profile.enableSMB_with_temptarget === true && (profile.temptargetSet && target_bg < 100)) { - if (meal_data.bwFound) { - if (profile.A52_risk_enable === true) { - console.error("Warning: SMB enabled within 6h of using Bolus Wizard: be sure to easy bolus 30s before using Bolus Wizard") - enableSMB=true; - } else { - console.error("enableSMB_with_temptarget not supported within 6h of using Bolus Wizard"); - } - } else { - console.error("SMB enabled for temptarget of",convert_bg(target_bg, profile)); - enableSMB=true; - } - // enable SMB/UAM if always-on (unless previously disabled for high temptarget) - } else if (profile.enableSMB_always === true) { - if (meal_data.bwFound) { - if (profile.A52_risk_enable === true) { - console.error("Warning: SMB enabled within 6h of using Bolus Wizard: be sure to easy bolus 30s before using Bolus Wizard") - enableSMB=true; - } else { - console.error("enableSMB_always not supported within 6h of using Bolus Wizard"); - } - } else { - console.error("SMB enabled due to enableSMB_always"); - enableSMB=true; - } - } else { - console.error("SMB disabled for this run (no selected enableSMB criteria met)"); - } + var enableSMB = enable_smb( + profile, + microBolusAllowed, + meal_data, + target_bg + ); + // enable UAM (if enabled in preferences) var enableUAM=(profile.enableUAM); @@ -573,7 +578,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ remainingCItotal += predCI+remainingCI; remainingCIs.push(round(remainingCI,0)); predCIs.push(round(predCI,0)); - //process.stderr.write(round(predCI,1)+"+"+round(remainingCI,1)+" "); + //console.log(round(predCI,1)+"+"+round(remainingCI,1)+" "); COBpredBG = COBpredBGs[COBpredBGs.length-1] + predBGI + Math.min(0,predDev) + predCI + remainingCI; var aCOBpredBG = aCOBpredBGs[aCOBpredBGs.length-1] + predBGI + Math.min(0,predDev) + predACI; // for UAMpredBGs, predicted carb impact drops at slopeFromDeviations @@ -606,7 +611,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // set minPredBGs starting when currently-dosed insulin activity will peak // look ahead 60m (regardless of insulin type) so as to be less aggressive on slower insulins var insulinPeakTime = 60; - // add 30m to allow for insluin delivery (SMBs or temps) + // add 30m to allow for insulin delivery (SMBs or temps) insulinPeakTime = 90; var insulinPeak5m = (insulinPeakTime/60)*12; //console.error(insulinPeakTime, insulinPeak5m, profile.insulinPeakTime, profile.curve); @@ -764,8 +769,10 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // if blendedMinPredBG > minCOBPredBG, use that instead minPredBG = round(Math.max(minIOBPredBG, minCOBPredBG, blendedMinPredBG)); // if carbs have been entered, but have expired, use minUAMPredBG - } else { + } else if ( enableUAM ) { minPredBG = minZTUAMPredBG; + } else { + minPredBG = minGuardBG; } // in pure UAM mode, use the higher of minIOBPredBG,minUAMPredBG } else if ( enableUAM ) { @@ -775,12 +782,12 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ // make sure minPredBG isn't higher than avgPredBG minPredBG = Math.min( minPredBG, avgPredBG ); - process.stderr.write("minPredBG: "+minPredBG+" minIOBPredBG: "+minIOBPredBG+" minZTGuardBG: "+minZTGuardBG); + console.log("minPredBG: "+minPredBG+" minIOBPredBG: "+minIOBPredBG+" minZTGuardBG: "+minZTGuardBG); if (minCOBPredBG < 999) { - process.stderr.write(" minCOBPredBG: "+minCOBPredBG); + console.log(" minCOBPredBG: "+minCOBPredBG); } if (minUAMPredBG < 999) { - process.stderr.write(" minUAMPredBG: "+minUAMPredBG); + console.log(" minUAMPredBG: "+minUAMPredBG); } console.error(" avgPredBG:",avgPredBG,"COB:",meal_data.mealCOB,"/",meal_data.carbs); // But if the COB line falls off a cliff, don't trust UAM too much: From 243b7a12a502e21935481d1bfb025cb2f91f4884 Mon Sep 17 00:00:00 2001 From: Tim Gunn <2896311+Tornado-Tim@users.noreply.github.com> Date: Fri, 29 Nov 2019 21:22:26 +1300 Subject: [PATCH 15/36] Add new preferences in and the ability to ingest noise --- .../DetermineBasalAdapterSMBJS.java | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java index abc89f25de..82e83ed78a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java @@ -28,14 +28,17 @@ import info.nightscout.androidaps.data.IobTotal; import info.nightscout.androidaps.data.MealData; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.db.TemporaryBasal; +import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.logging.L; +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.aps.loop.ScriptReader; import info.nightscout.androidaps.plugins.aps.openAPSMA.LoggerCallback; import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.utils.SP; import info.nightscout.androidaps.utils.SafeParse; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; + public class DetermineBasalAdapterSMBJS { private static Logger log = LoggerFactory.getLogger(L.APS); @@ -92,7 +95,7 @@ public class DetermineBasalAdapterSMBJS { log.debug("Reservoir data: " + "undefined"); log.debug("MicroBolusAllowed: " + (storedMicroBolusAllowed = "" + mMicrobolusAllowed)); log.debug("SMBAlwaysAllowed: " + (storedSMBAlwaysAllowed = "" + mSMBAlwaysAllowed)); -// log.debug("Current Time: " + (storedCurrentTime = "" + mCurrentTime)); + log.debug("CurrentTime: " + (storedCurrentTime = "" + mCurrentTime)); } DetermineBasalResultSMB determineBasalResultSMB = null; @@ -136,7 +139,7 @@ public class DetermineBasalAdapterSMBJS { setTempBasalFunctionsObj, new Boolean(mMicrobolusAllowed), makeParam(null, rhino, scope), // reservoir data as undefined - mCurrentTime + new Long(mCurrentTime) }; @@ -170,7 +173,6 @@ public class DetermineBasalAdapterSMBJS { storedCurrentTemp = mCurrentTemp.toString(); storedProfile = mProfile.toString(); storedMeal_data = mMealData.toString(); - //storedCurrentTime = "" + mCurrentTime; return determineBasalResultSMB; @@ -180,10 +182,6 @@ public class DetermineBasalAdapterSMBJS { return storedGlucoseStatus; } - // String getCurrentTimeParam() { - // return storedCurrentTime; - // } - String getCurrentTempParam() { return storedCurrentTemp; } @@ -247,15 +245,13 @@ public class DetermineBasalAdapterSMBJS { mProfile.put("max_daily_safety_multiplier", SP.getInt(R.string.key_openapsama_max_daily_safety_multiplier, 3)); mProfile.put("current_basal_safety_multiplier", SP.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4d)); - // TODO AS-FIX - // mProfile.put("high_temptarget_raises_sensitivity", SP.getBoolean(R.string.key_high_temptarget_raises_sensitivity, SMBDefaults.high_temptarget_raises_sensitivity)); - mProfile.put("high_temptarget_raises_sensitivity", false); - //mProfile.put("low_temptarget_lowers_sensitivity", SP.getBoolean(R.string.key_low_temptarget_lowers_sensitivity, SMBDefaults.low_temptarget_lowers_sensitivity)); - mProfile.put("low_temptarget_lowers_sensitivity", false); + mProfile.put("high_temptarget_raises_sensitivity", SP.getBoolean(R.string.key_high_temptarget_raises_sensitivity, SMBDefaults.high_temptarget_raises_sensitivity)); + mProfile.put("low_temptarget_lowers_sensitivity", SP.getBoolean(R.string.key_low_temptarget_lowers_sensitivity, SMBDefaults.low_temptarget_lowers_sensitivity)); - mProfile.put("sensitivity_raises_target", SMBDefaults.sensitivity_raises_target); - mProfile.put("resistance_lowers_target", SMBDefaults.resistance_lowers_target); + + mProfile.put("sensitivity_raises_target", SP.getBoolean(R.string.key_sensitivity_raises_target, SMBDefaults.sensitivity_raises_target)); + mProfile.put("resistance_lowers_target", SP.getBoolean(R.string.key_resistance_lowers_target, SMBDefaults.resistance_lowers_target)); mProfile.put("adv_target_adjustments", SMBDefaults.adv_target_adjustments); mProfile.put("exercise_mode", SMBDefaults.exercise_mode); mProfile.put("half_basal_exercise_target", SMBDefaults.half_basal_exercise_target); @@ -272,20 +268,20 @@ public class DetermineBasalAdapterSMBJS { mProfile.put("A52_risk_enable", SMBDefaults.A52_risk_enable); boolean smbEnabled = SP.getBoolean(MainApp.gs(R.string.key_use_smb), false); + mProfile.put("SMBInterval", SP.getInt("key_smbinterval", SMBDefaults.SMBInterval)); mProfile.put("enableSMB_with_COB", smbEnabled && SP.getBoolean(R.string.key_enableSMB_with_COB, false)); mProfile.put("enableSMB_with_temptarget", smbEnabled && SP.getBoolean(R.string.key_enableSMB_with_temptarget, false)); mProfile.put("allowSMB_with_high_temptarget", smbEnabled && SP.getBoolean(R.string.key_allowSMB_with_high_temptarget, false)); mProfile.put("enableSMB_always", smbEnabled && SP.getBoolean(R.string.key_enableSMB_always, false) && advancedFiltering); mProfile.put("enableSMB_after_carbs", smbEnabled && SP.getBoolean(R.string.key_enableSMB_after_carbs, false) && advancedFiltering); mProfile.put("maxSMBBasalMinutes", SP.getInt(R.string.key_smbmaxminutes, SMBDefaults.maxSMBBasalMinutes)); - mProfile.put("maxUAMSMBBasalMinutes", SP.getInt("key_uamsmbmaxminutes", SMBDefaults.maxUAMSMBBasalMinutes)); - mProfile.put("SMBInterval", SP.getInt("key_smbinterval", SMBDefaults.SMBInterval)); + mProfile.put("maxUAMSMBBasalMinutes", SP.getInt(R.string.key_uamsmbmaxminutes, SMBDefaults.maxUAMSMBBasalMinutes)); if (bolusincrement < pumpbolusstep){ //the bolus incrument is less than what the pump can support (by pump settings or pump restriction), set to value supported by the pump bolusincrement = pumpbolusstep; } mProfile.put("bolus_increment", bolusincrement); - mProfile.put("carbsReqThreshold", SMBDefaults.carbsReqThreshold); + mProfile.put("carbsReqThreshold", SP.getInt(R.string.key_carbsReqThreshold, SMBDefaults.carbsReqThreshold)); mProfile.put("current_basal", basalrate); mProfile.put("temptargetSet", tempTargetSet); @@ -305,7 +301,7 @@ public class DetermineBasalAdapterSMBJS { mCurrentTemp.put("rate", tb != null ? tb.tempBasalConvertedToAbsolute(now, profile) : 0d); // as we have non default temps longer than 30 mintues - TemporaryBasal tempBasal = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(System.currentTimeMillis()); + TemporaryBasal tempBasal = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(now); if (tempBasal != null) { mCurrentTemp.put("minutesrunning", tempBasal.getRealDuration()); } @@ -314,6 +310,7 @@ public class DetermineBasalAdapterSMBJS { mGlucoseStatus = new JSONObject(); mGlucoseStatus.put("glucose", glucoseStatus.glucose); + mGlucoseStatus.put("noise", glucoseStatus.noise); if (SP.getBoolean(R.string.key_always_use_shortavg, false)) { mGlucoseStatus.put("delta", glucoseStatus.short_avgdelta); From 8eeb91a0a80efb1d79856c41bf6be8e14527bccd Mon Sep 17 00:00:00 2001 From: Tim Gunn <2896311+Tornado-Tim@users.noreply.github.com> Date: Fri, 29 Nov 2019 21:23:12 +1300 Subject: [PATCH 16/36] Add New preferences for oref 0.7.0 --- app/src/main/res/values/strings.xml | 10 ++++ app/src/main/res/xml/pref_openapssmb.xml | 70 ++++++++++++++++-------- 2 files changed, 58 insertions(+), 22 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ccc91c699b..e8a8888ec2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -837,6 +837,9 @@ Max minutes of basal to limit SMB to UAM SMB max minutes Max minutes of basal to limit SMB to for UAM + carbsReqThreshold + Carb suggestion threshold + When Carbs are suggested, how many carbs will prompt a notification bolus_increment Minimum bolus that can be delivered as an SMB Unsupported pump firmware @@ -1152,6 +1155,12 @@ = 100]]> Low temptarget lowers sensitivity + resistance_lowers_target + Resistance lowers target + When resistance is detected, lower the target glucose + sensitivity_raises_target + Sensitivity raises target + When sensitivity is detected, raise the target glucose Invalid pump setup, check the docs and verify that the Quick Info menu is named QUICK INFO using the 360 configuration software. Custom Large Time Difference @@ -1354,6 +1363,7 @@ Upload BG tests smbmaxminutes + uamsmbmaxminutes Daylight Saving time Daylight Saving time change in 24h or less Daylight Saving time change less than 3 hours ago - Closed loop disabled diff --git a/app/src/main/res/xml/pref_openapssmb.xml b/app/src/main/res/xml/pref_openapssmb.xml index 45375df376..99e3332bb7 100644 --- a/app/src/main/res/xml/pref_openapssmb.xml +++ b/app/src/main/res/xml/pref_openapssmb.xml @@ -93,30 +93,30 @@ validate:testType="numericRange" /> + android:defaultValue="30" + android:dialogMessage="@string/uamsmbmaxminutes" + android:digits="0123456789" + android:inputType="number" + android:key="@string/key_uamsmbmaxminutes" + android:maxLines="20" + android:selectAllOnFocus="true" + android:singleLine="true" + android:title="@string/uamsmbmaxminutes_summary" + validate:maxNumber="120" + validate:minNumber="15" + validate:testType="numericRange" /> + android:defaultValue="0.1" + android:inputType="numberDecimal" + android:key="key_bolus_increment" + android:maxLines="20" + android:selectAllOnFocus="true" + android:singleLine="true" + android:title="@string/bolus_increment_summary" + validate:floatmaxNumber="1.0" + validate:floatminNumber="0.05" + validate:testType="floatNumericRange" /> + + + + From 0a5bd7f935f416257589defa57838a2ea5ec74fe Mon Sep 17 00:00:00 2001 From: Tim Gunn <2896311+Tornado-Tim@users.noreply.github.com> Date: Fri, 29 Nov 2019 21:24:05 +1300 Subject: [PATCH 17/36] Hack determine_basal so a date string could be converted to date object --- app/src/main/assets/OpenAPSSMB/determine-basal.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/assets/OpenAPSSMB/determine-basal.js b/app/src/main/assets/OpenAPSSMB/determine-basal.js index 0ddbd9d601..363e46da73 100644 --- a/app/src/main/assets/OpenAPSSMB/determine-basal.js +++ b/app/src/main/assets/OpenAPSSMB/determine-basal.js @@ -115,7 +115,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ var deliverAt = new Date(); if (currentTime) { - deliverAt = currentTime; + deliverAt = new Date(currentTime); } if (typeof profile === 'undefined' || typeof profile.current_basal === 'undefined') { From 280e075adab8a982c1ad67d7db65f69948bcce88 Mon Sep 17 00:00:00 2001 From: Tim Gunn <2896311+Tornado-Tim@users.noreply.github.com> Date: Fri, 6 Dec 2019 18:16:09 +1300 Subject: [PATCH 18/36] Remove Bolus Increment setting --- app/src/main/res/values/strings.xml | 2 -- app/src/main/res/xml/pref_openapssmb.xml | 12 ------------ 2 files changed, 14 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e8a8888ec2..f03fbcef92 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -840,8 +840,6 @@ carbsReqThreshold Carb suggestion threshold When Carbs are suggested, how many carbs will prompt a notification - bolus_increment - Minimum bolus that can be delivered as an SMB Unsupported pump firmware Send BG data to xDrip+ dexcomg5_xdripupload diff --git a/app/src/main/res/xml/pref_openapssmb.xml b/app/src/main/res/xml/pref_openapssmb.xml index 99e3332bb7..befec72ec4 100644 --- a/app/src/main/res/xml/pref_openapssmb.xml +++ b/app/src/main/res/xml/pref_openapssmb.xml @@ -106,18 +106,6 @@ validate:minNumber="15" validate:testType="numericRange" /> - - Date: Fri, 6 Dec 2019 18:16:46 +1300 Subject: [PATCH 19/36] Set the Bolus Increment to the min amount supported by the pump --- .../aps/openAPSSMB/DetermineBasalAdapterSMBJS.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java index 82e83ed78a..cd86aae46e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java @@ -228,7 +228,6 @@ public class DetermineBasalAdapterSMBJS { ) throws JSONException { String units = profile.getUnits(); - Double bolusincrement = SP.getDouble("key_bolus_increment", SMBDefaults.bolus_increment); Double pumpbolusstep = ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().bolusStep; mProfile = new JSONObject(); @@ -276,11 +275,8 @@ public class DetermineBasalAdapterSMBJS { mProfile.put("enableSMB_after_carbs", smbEnabled && SP.getBoolean(R.string.key_enableSMB_after_carbs, false) && advancedFiltering); mProfile.put("maxSMBBasalMinutes", SP.getInt(R.string.key_smbmaxminutes, SMBDefaults.maxSMBBasalMinutes)); mProfile.put("maxUAMSMBBasalMinutes", SP.getInt(R.string.key_uamsmbmaxminutes, SMBDefaults.maxUAMSMBBasalMinutes)); - if (bolusincrement < pumpbolusstep){ - //the bolus incrument is less than what the pump can support (by pump settings or pump restriction), set to value supported by the pump - bolusincrement = pumpbolusstep; - } - mProfile.put("bolus_increment", bolusincrement); + //set the min SMB amount to be the amount set by the pump. + mProfile.put("bolus_increment", pumpbolusstep); mProfile.put("carbsReqThreshold", SP.getInt(R.string.key_carbsReqThreshold, SMBDefaults.carbsReqThreshold)); mProfile.put("current_basal", basalrate); From bf45338eb94a97db538c05ce206ef860ca22fbbc Mon Sep 17 00:00:00 2001 From: Tim Gunn <2896311+Tornado-Tim@users.noreply.github.com> Date: Fri, 6 Dec 2019 18:22:51 +1300 Subject: [PATCH 20/36] Disable high_temptarget_raises_sensitivity and low_temptarget_lowers_sensitivity --- .../plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java index cd86aae46e..bd142bfecd 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java @@ -244,9 +244,10 @@ public class DetermineBasalAdapterSMBJS { mProfile.put("max_daily_safety_multiplier", SP.getInt(R.string.key_openapsama_max_daily_safety_multiplier, 3)); mProfile.put("current_basal_safety_multiplier", SP.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4d)); - mProfile.put("high_temptarget_raises_sensitivity", SP.getBoolean(R.string.key_high_temptarget_raises_sensitivity, SMBDefaults.high_temptarget_raises_sensitivity)); - mProfile.put("low_temptarget_lowers_sensitivity", SP.getBoolean(R.string.key_low_temptarget_lowers_sensitivity, SMBDefaults.low_temptarget_lowers_sensitivity)); - + //mProfile.put("high_temptarget_raises_sensitivity", SP.getBoolean(R.string.key_high_temptarget_raises_sensitivity, SMBDefaults.high_temptarget_raises_sensitivity)); + mProfile.put("high_temptarget_raises_sensitivity", false); + //mProfile.put("low_temptarget_lowers_sensitivity", SP.getBoolean(R.string.key_low_temptarget_lowers_sensitivity, SMBDefaults.low_temptarget_lowers_sensitivity)); + mProfile.put("low_temptarget_lowers_sensitivity", false); mProfile.put("sensitivity_raises_target", SP.getBoolean(R.string.key_sensitivity_raises_target, SMBDefaults.sensitivity_raises_target)); From 457f43b1fe7298a649edc3c57f74840bf732b2b1 Mon Sep 17 00:00:00 2001 From: Tim Gunn <2896311+Tornado-Tim@users.noreply.github.com> Date: Fri, 6 Dec 2019 18:25:09 +1300 Subject: [PATCH 21/36] Reuse Variable --- .../plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java index bd142bfecd..5769196bb7 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java @@ -338,7 +338,7 @@ public class DetermineBasalAdapterSMBJS { mMicrobolusAllowed = microBolusAllowed; mSMBAlwaysAllowed = advancedFiltering; - mCurrentTime = System.currentTimeMillis(); + mCurrentTime = now; } From f7b40854450c084648eb07c57e2b3cb6130bc597 Mon Sep 17 00:00:00 2001 From: Tim Gunn <2896311+Tornado-Tim@users.noreply.github.com> Date: Fri, 6 Dec 2019 18:28:44 +1300 Subject: [PATCH 22/36] Round to 2DP --- .../androidaps/plugins/iob/iobCobCalculator/GlucoseStatus.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/GlucoseStatus.java b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/GlucoseStatus.java index 512c22d2e4..d404603f6e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/GlucoseStatus.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/GlucoseStatus.java @@ -42,7 +42,7 @@ public class GlucoseStatus { public GlucoseStatus round() { this.glucose = Round.roundTo(this.glucose, 0.1); - this.noise = Round.roundTo(this.noise, 0.1); + this.noise = Round.roundTo(this.noise, 0.01); this.delta = Round.roundTo(this.delta, 0.01); this.avgdelta = Round.roundTo(this.avgdelta, 0.01); this.short_avgdelta = Round.roundTo(this.short_avgdelta, 0.01); From 843fccb86cc413bb88004fe0c2f3cb1f17f56777 Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Sun, 22 Mar 2020 11:54:31 +0100 Subject: [PATCH 23/36] SmsCommunicatorPlugin.processCARBS: set source on record. --- .../plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt index 0021574812..41c28bbf21 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt @@ -713,6 +713,7 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() override fun run() { val detailedBolusInfo = DetailedBolusInfo() detailedBolusInfo.carbs = anInteger().toDouble() + detailedBolusInfo.source = Source.USER detailedBolusInfo.date = secondLong() ConfigBuilderPlugin.getPlugin().commandQueue.bolus(detailedBolusInfo, object : Callback() { override fun run() { From e7c7ce32bb085ed228cc0663b58061dd0d7aa4d0 Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Sun, 22 Mar 2020 11:57:29 +0100 Subject: [PATCH 24/36] TreatmentService.createOrUpdate: log invalid argument. --- .../androidaps/plugins/treatments/TreatmentService.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentService.java b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentService.java index 1b118da1d3..eae623267e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/treatments/TreatmentService.java @@ -280,6 +280,10 @@ public class TreatmentService extends OrmLiteBaseService { // return true if new record is created public UpdateReturn createOrUpdate(Treatment treatment) { + if (treatment != null && treatment.source == Source.NONE) { + log.error("Coder error: source is not set for treatment: " + treatment, new Exception()); + FabricPrivacy.logException(new Exception("Coder error: source is not set for treatment: " + treatment)); + } try { Treatment old; treatment.date = DatabaseHelper.roundDateToSec(treatment.date); From fce6ec8d2581ed3becfa82a6c17ab3c42a0c88cf Mon Sep 17 00:00:00 2001 From: Johannes Mockenhaupt Date: Tue, 24 Mar 2020 21:27:38 +0100 Subject: [PATCH 25/36] SmsCommunicator: only ask pump with support to add carbs. This aligns with how this is generally handles elsewhere: check if a pump supports carbs and if not, don't bother the driver but add the carbs directly via TreatmentsPlugin. --- .../smsCommunicator/SmsCommunicatorPlugin.kt | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt index 41c28bbf21..433acf9403 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt @@ -715,19 +715,26 @@ object SmsCommunicatorPlugin : PluginBase(PluginDescription() detailedBolusInfo.carbs = anInteger().toDouble() detailedBolusInfo.source = Source.USER detailedBolusInfo.date = secondLong() - ConfigBuilderPlugin.getPlugin().commandQueue.bolus(detailedBolusInfo, object : Callback() { - override fun run() { - if (result.success) { - var replyText = String.format(MainApp.gs(R.string.smscommunicator_carbsset), anInteger) - replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) - sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) - } else { - var replyText = MainApp.gs(R.string.smscommunicator_carbsfailed) - replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) - sendSMS(Sms(receivedSms.phoneNumber, replyText)) + if (ConfigBuilderPlugin.getPlugin().activePump?.pumpDescription?.storesCarbInfo == true) { + ConfigBuilderPlugin.getPlugin().commandQueue.bolus(detailedBolusInfo, object : Callback() { + override fun run() { + if (result.success) { + var replyText = String.format(MainApp.gs(R.string.smscommunicator_carbsset), anInteger) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) + } else { + var replyText = MainApp.gs(R.string.smscommunicator_carbsfailed) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMS(Sms(receivedSms.phoneNumber, replyText)) + } } - } - }) + }) + } else { + TreatmentsPlugin.getPlugin().addToHistoryTreatment(detailedBolusInfo, true) + var replyText = String.format(MainApp.gs(R.string.smscommunicator_carbsset), anInteger) + replyText += "\n" + ConfigBuilderPlugin.getPlugin().activePump?.shortStatus(true) + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) + } } }) } From ced46f6e1d10790d2d94e5a658dda6f29e7ca0df Mon Sep 17 00:00:00 2001 From: Tim Gunn <2896311+Tornado-Tim@users.noreply.github.com> Date: Thu, 26 Mar 2020 12:18:02 +1300 Subject: [PATCH 26/36] Final merge --- .../aps/openAPSSMB/DetermineBasalAdapterSMBJS.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java index 5be89b8764..af56162640 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java @@ -34,6 +34,8 @@ import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction; import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus; import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; +import info.nightscout.androidaps.interfaces.ActivePluginProvider; + import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; import info.nightscout.androidaps.utils.SafeParse; import info.nightscout.androidaps.utils.resources.ResourceHelper; @@ -48,6 +50,8 @@ public class DetermineBasalAdapterSMBJS { @Inject ResourceHelper resourceHelper; @Inject ProfileFunction profileFunction; @Inject TreatmentsPlugin treatmentsPlugin; + @Inject ActivePluginProvider activePluginProvider; + private ScriptReader mScriptReader; private JSONObject mProfile; @@ -233,7 +237,7 @@ public class DetermineBasalAdapterSMBJS { ) throws JSONException { String units = profile.getUnits(); - Double pumpbolusstep = ConfigBuilderPlugin.getPlugin().getActivePump().getPumpDescription().bolusStep; + Double pumpbolusstep = activePluginProvider.getActivePump().getPumpDescription().bolusStep; mProfile = new JSONObject(); mProfile.put("max_iob", maxIob); @@ -272,7 +276,7 @@ public class DetermineBasalAdapterSMBJS { mProfile.put("enableUAM", uamAllowed); mProfile.put("A52_risk_enable", SMBDefaults.A52_risk_enable); - boolean smbEnabled = sp.getBoolean(MainApp.gs(R.string.key_use_smb), false); + boolean smbEnabled = sp.getBoolean(resourceHelper.gs(R.string.key_use_smb), false); mProfile.put("SMBInterval", sp.getInt("key_smbinterval", SMBDefaults.SMBInterval)); mProfile.put("enableSMB_with_COB", smbEnabled && sp.getBoolean(R.string.key_enableSMB_with_COB, false)); mProfile.put("enableSMB_with_temptarget", smbEnabled && sp.getBoolean(R.string.key_enableSMB_with_temptarget, false)); From aeb18e0b7c263213a4826a52d594d6a72c161967 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Fri, 3 Apr 2020 12:09:57 +0200 Subject: [PATCH 27/36] Fix others flavors --- .../dependencyInjection/PluginsModule.kt | 2 +- .../configBuilder/ConfigBuilderFragment.kt | 1 + .../configBuilder/ConfigBuilderPlugin.java | 10 +--------- .../plugins/configBuilder/PluginStore.kt | 20 ++++++++++--------- .../EventConfigBuilderUpdateGui.kt | 2 +- .../androidaps/receivers/KeepAliveReceiver.kt | 5 ++--- .../androidaps/utils/FabricPrivacy.kt | 4 +++- 7 files changed, 20 insertions(+), 24 deletions(-) rename app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/{ => events}/EventConfigBuilderUpdateGui.kt (64%) diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PluginsModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PluginsModule.kt index 928fed610c..cc3694874f 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PluginsModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PluginsModule.kt @@ -167,7 +167,7 @@ abstract class PluginsModule { abstract fun bindMDIPlugin(plugin: MDIPlugin): PluginBase @Binds - @NotNSClient + @AllConfigs @IntoMap @IntKey(180) abstract fun bindVirtualPumpPlugin(plugin: VirtualPumpPlugin): PluginBase diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt index f9cba58951..c864313b27 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt @@ -17,6 +17,7 @@ import info.nightscout.androidaps.activities.PreferencesActivity import info.nightscout.androidaps.events.EventRebuildTabs import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.configBuilder.events.EventConfigBuilderUpdateGui import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.extensions.plusAssign import info.nightscout.androidaps.utils.protection.ProtectionCheck 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 b779943cdb..a6608b07c0 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 @@ -9,7 +9,6 @@ import java.util.ArrayList; import javax.inject.Inject; import javax.inject.Singleton; -import dagger.Lazy; import dagger.android.HasAndroidInjector; import info.nightscout.androidaps.R; import info.nightscout.androidaps.events.EventAppInitialized; @@ -18,7 +17,6 @@ import info.nightscout.androidaps.events.EventRebuildTabs; import info.nightscout.androidaps.interfaces.APSInterface; import info.nightscout.androidaps.interfaces.ActivePluginProvider; import info.nightscout.androidaps.interfaces.BgSourceInterface; -import info.nightscout.androidaps.interfaces.CommandQueueProvider; import info.nightscout.androidaps.interfaces.InsulinInterface; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginDescription; @@ -30,13 +28,7 @@ import info.nightscout.androidaps.interfaces.TreatmentsInterface; import info.nightscout.androidaps.logging.AAPSLogger; import info.nightscout.androidaps.logging.LTag; import info.nightscout.androidaps.plugins.bus.RxBusWrapper; -import info.nightscout.androidaps.plugins.insulin.InsulinOrefRapidActingPlugin; -import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin; -import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin; -import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin; -import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref0Plugin; -import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin; -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; +import info.nightscout.androidaps.plugins.configBuilder.events.EventConfigBuilderUpdateGui; import info.nightscout.androidaps.utils.OKDialog; import info.nightscout.androidaps.utils.resources.ResourceHelper; import info.nightscout.androidaps.utils.sharedPreferences.SP; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginStore.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginStore.kt index 31692b93bb..558e23df2b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginStore.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/PluginStore.kt @@ -1,12 +1,11 @@ package info.nightscout.androidaps.plugins.configBuilder +import info.nightscout.androidaps.Config import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag -import java.util.* import javax.inject.Inject import javax.inject.Singleton -import kotlin.collections.ArrayList @Singleton class PluginStore @Inject constructor( @@ -27,6 +26,7 @@ class PluginStore @Inject constructor( return pluginStore!! } } + lateinit var plugins: List<@JvmSuppressWildcards PluginBase> private var activeBgSource: BgSourceInterface? = null @@ -83,14 +83,16 @@ class PluginStore @Inject constructor( var pluginsInCategory: ArrayList? // PluginType.APS - pluginsInCategory = getSpecificPluginsList(PluginType.APS) - activeAPS = getTheOneEnabledInArray(pluginsInCategory, PluginType.APS) as APSInterface? - if (activeAPS == null) { - activeAPS = getDefaultPlugin(PluginType.APS) as APSInterface - (activeAPS as PluginBase).setPluginEnabled(PluginType.APS, true) - aapsLogger.debug(LTag.CONFIGBUILDER, "Defaulting APSInterface") + if (!Config.NSCLIENT && !Config.PUMPCONTROL) { + pluginsInCategory = getSpecificPluginsList(PluginType.APS) + activeAPS = getTheOneEnabledInArray(pluginsInCategory, PluginType.APS) as APSInterface? + if (activeAPS == null) { + activeAPS = getDefaultPlugin(PluginType.APS) as APSInterface + (activeAPS as PluginBase).setPluginEnabled(PluginType.APS, true) + aapsLogger.debug(LTag.CONFIGBUILDER, "Defaulting APSInterface") + } + setFragmentVisiblities((activeAPS as PluginBase).name, pluginsInCategory, PluginType.APS) } - setFragmentVisiblities((activeAPS as PluginBase).name, pluginsInCategory, PluginType.APS) // PluginType.INSULIN pluginsInCategory = getSpecificPluginsList(PluginType.INSULIN) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/EventConfigBuilderUpdateGui.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/events/EventConfigBuilderUpdateGui.kt similarity index 64% rename from app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/EventConfigBuilderUpdateGui.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/events/EventConfigBuilderUpdateGui.kt index e57fd79983..26904127dd 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/EventConfigBuilderUpdateGui.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/events/EventConfigBuilderUpdateGui.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.configBuilder +package info.nightscout.androidaps.plugins.configBuilder.events import info.nightscout.androidaps.events.EventUpdateGui diff --git a/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.kt b/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.kt index a7d2e4e145..521537e2d4 100644 --- a/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.kt +++ b/app/src/main/java/info/nightscout/androidaps/receivers/KeepAliveReceiver.kt @@ -98,13 +98,12 @@ class KeepAliveReceiver : DaggerBroadcastReceiver() { // if there is no BG available, we have to upload anyway to have correct // IOB displayed in NS private fun checkAPS() { - val usedAPS = activePlugin.activeAPS var shouldUploadStatus = false if (Config.NSCLIENT) return if (Config.PUMPCONTROL) shouldUploadStatus = true - if (!loopPlugin.isEnabled() || iobCobCalculatorPlugin.actualBg() == null) + else if (!loopPlugin.isEnabled() || iobCobCalculatorPlugin.actualBg() == null) shouldUploadStatus = true - else if (DateUtil.isOlderThan(usedAPS.lastAPSRun, 5)) shouldUploadStatus = true + else if (DateUtil.isOlderThan(activePlugin.activeAPS.lastAPSRun, 5)) shouldUploadStatus = true if (DateUtil.isOlderThan(lastIobUpload, IOB_UPDATE_FREQUENCY) && shouldUploadStatus) { lastIobUpload = DateUtil.now() NSUpload.uploadDeviceStatus(loopPlugin, iobCobCalculatorPlugin, profileFunction, activePlugin.activePump) diff --git a/app/src/main/java/info/nightscout/androidaps/utils/FabricPrivacy.kt b/app/src/main/java/info/nightscout/androidaps/utils/FabricPrivacy.kt index 3ec3f1f451..19d25e008b 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/FabricPrivacy.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/FabricPrivacy.kt @@ -4,6 +4,7 @@ import android.os.Bundle import com.crashlytics.android.Crashlytics import com.google.firebase.analytics.FirebaseAnalytics import info.nightscout.androidaps.BuildConfig +import info.nightscout.androidaps.Config import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.R import info.nightscout.androidaps.interfaces.ActivePluginProvider @@ -132,7 +133,8 @@ class FabricPrivacy @Inject constructor( val hashes: List = signatureVerifierPlugin.shortHashes() if (hashes.isNotEmpty()) mainApp.firebaseAnalytics.setUserProperty("Hash", hashes[0]) activePlugin.activePump.let { mainApp.firebaseAnalytics.setUserProperty("Pump", it::class.java.simpleName) } - activePlugin.activeAPS.let { mainApp.firebaseAnalytics.setUserProperty("Aps", it::class.java.simpleName) } + if (!Config.NSCLIENT && !Config.PUMPCONTROL) + activePlugin.activeAPS.let { mainApp.firebaseAnalytics.setUserProperty("Aps", it::class.java.simpleName) } activePlugin.activeBgSource.let { mainApp.firebaseAnalytics.setUserProperty("BgSource", it::class.java.simpleName) } mainApp.firebaseAnalytics.setUserProperty("Profile", activePlugin.activeProfileInterface.javaClass.simpleName) activePlugin.activeSensitivity.let { mainApp.firebaseAnalytics.setUserProperty("Sensitivity", it::class.java.simpleName) } From 3a2d4869769a9b6a098b6807267c9e6686bd1fce Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Fri, 3 Apr 2020 21:47:23 +0200 Subject: [PATCH 28/36] Move Overview menus to separate class --- .../historyBrowser/HistoryBrowseActivity.java | 132 +----- .../general/overview/OverviewFragment.java | 409 +---------------- .../plugins/general/overview/OverviewMenus.kt | 433 ++++++++++++++++++ 3 files changed, 467 insertions(+), 507 deletions(-) create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewMenus.kt diff --git a/app/src/main/java/info/nightscout/androidaps/historyBrowser/HistoryBrowseActivity.java b/app/src/main/java/info/nightscout/androidaps/historyBrowser/HistoryBrowseActivity.java index 6a47e3749b..4ed1c3b8b7 100644 --- a/app/src/main/java/info/nightscout/androidaps/historyBrowser/HistoryBrowseActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/historyBrowser/HistoryBrowseActivity.java @@ -28,6 +28,7 @@ import info.nightscout.androidaps.R; import info.nightscout.androidaps.activities.NoSplashAppCompatActivity; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.events.EventCustomCalculationFinished; +import info.nightscout.androidaps.events.EventRefreshOverview; import info.nightscout.androidaps.interfaces.ActivePluginProvider; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.logging.AAPSLogger; @@ -35,6 +36,7 @@ import info.nightscout.androidaps.logging.LTag; import info.nightscout.androidaps.plugins.bus.RxBusWrapper; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction; import info.nightscout.androidaps.plugins.general.overview.OverviewFragment; +import info.nightscout.androidaps.plugins.general.overview.OverviewMenus; import info.nightscout.androidaps.plugins.general.overview.graphData.GraphData; import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished; import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress; @@ -60,6 +62,7 @@ public class HistoryBrowseActivity extends NoSplashAppCompatActivity { @Inject ActivePluginProvider activePlugin; @Inject BuildHelper buildHelper; @Inject FabricPrivacy fabricPrivacy; + @Inject OverviewMenus overviewMenus; private CompositeDisposable disposable = new CompositeDisposable(); @@ -171,7 +174,7 @@ public class HistoryBrowseActivity extends NoSplashAppCompatActivity { iobGraph.getGridLabelRenderer().setLabelVerticalWidth(50); iobGraph.getGridLabelRenderer().setNumVerticalLabels(5); - setupChartMenu(); + overviewMenus.setupChartMenu(findViewById(R.id.overview_chartMenuButton)); } @Override @@ -194,7 +197,7 @@ public class HistoryBrowseActivity extends NoSplashAppCompatActivity { updateGUI("EventAutosensCalculationFinished"); } } - }, exception -> fabricPrivacy.logException(exception)) + }, fabricPrivacy::logException) ); disposable.add(rxBus .toObservable(EventIobCalculationProgress.class) @@ -204,6 +207,11 @@ public class HistoryBrowseActivity extends NoSplashAppCompatActivity { iobCalculationProgressView.setText(event.getProgress()); }, exception -> fabricPrivacy.logException(exception)) ); + disposable.add(rxBus + .toObservable(EventRefreshOverview.class) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(event -> updateGUI("EventRefreshOverview") , fabricPrivacy::logException) + ); // set start of current day Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(System.currentTimeMillis()); @@ -367,124 +375,4 @@ public class HistoryBrowseActivity extends NoSplashAppCompatActivity { }); }).start(); } - - private void setupChartMenu() { - chartButton = findViewById(R.id.overview_chartMenuButton); - chartButton.setOnClickListener(v -> { - MenuItem item, dividerItem; - CharSequence title; - int titleMaxChars = 0; - SpannableString s; - PopupMenu popup = new PopupMenu(v.getContext(), v); - - - item = popup.getMenu().add(Menu.NONE, OverviewFragment.CHARTTYPE.BAS.ordinal(), Menu.NONE, resourceHelper.gs(R.string.overview_show_basals)); - title = item.getTitle(); - if (titleMaxChars < title.length()) titleMaxChars = title.length(); - s = new SpannableString(title); - s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.basal, null)), 0, s.length(), 0); - item.setTitle(s); - item.setCheckable(true); - item.setChecked(showBasal); - - item = popup.getMenu().add(Menu.NONE, OverviewFragment.CHARTTYPE.ACTPRIM.ordinal(), Menu.NONE, resourceHelper.gs(R.string.overview_show_activity)); - title = item.getTitle(); - if (titleMaxChars < title.length()) titleMaxChars = title.length(); - s = new SpannableString(title); - s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.activity, null)), 0, s.length(), 0); - item.setTitle(s); - item.setCheckable(true); - item.setChecked(showActPrim); - - dividerItem = popup.getMenu().add(""); - dividerItem.setEnabled(false); - - item = popup.getMenu().add(Menu.NONE, OverviewFragment.CHARTTYPE.IOB.ordinal(), Menu.NONE, resourceHelper.gs(R.string.overview_show_iob)); - title = item.getTitle(); - if (titleMaxChars < title.length()) titleMaxChars = title.length(); - s = new SpannableString(title); - s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.iob, null)), 0, s.length(), 0); - item.setTitle(s); - item.setCheckable(true); - item.setChecked(showIob); - - item = popup.getMenu().add(Menu.NONE, OverviewFragment.CHARTTYPE.COB.ordinal(), Menu.NONE, resourceHelper.gs(R.string.overview_show_cob)); - title = item.getTitle(); - if (titleMaxChars < title.length()) titleMaxChars = title.length(); - s = new SpannableString(title); - s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.cob, null)), 0, s.length(), 0); - item.setTitle(s); - item.setCheckable(true); - item.setChecked(showCob); - - item = popup.getMenu().add(Menu.NONE, OverviewFragment.CHARTTYPE.DEV.ordinal(), Menu.NONE, resourceHelper.gs(R.string.overview_show_deviations)); - title = item.getTitle(); - if (titleMaxChars < title.length()) titleMaxChars = title.length(); - s = new SpannableString(title); - s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.deviations, null)), 0, s.length(), 0); - item.setTitle(s); - item.setCheckable(true); - item.setChecked(showDev); - - item = popup.getMenu().add(Menu.NONE, OverviewFragment.CHARTTYPE.SEN.ordinal(), Menu.NONE, resourceHelper.gs(R.string.overview_show_sensitivity)); - title = item.getTitle(); - if (titleMaxChars < title.length()) titleMaxChars = title.length(); - s = new SpannableString(title); - s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.ratio, null)), 0, s.length(), 0); - item.setTitle(s); - item.setCheckable(true); - item.setChecked(showRat); - - item = popup.getMenu().add(Menu.NONE, OverviewFragment.CHARTTYPE.ACTSEC.ordinal(), Menu.NONE, resourceHelper.gs(R.string.overview_show_activity)); - title = item.getTitle(); - if (titleMaxChars < title.length()) titleMaxChars = title.length(); - s = new SpannableString(title); - s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.activity, null)), 0, s.length(), 0); - item.setTitle(s); - item.setCheckable(true); - item.setChecked(showActSec); - - - if (buildHelper.isDev()) { - item = popup.getMenu().add(Menu.NONE, OverviewFragment.CHARTTYPE.DEVSLOPE.ordinal(), Menu.NONE, "Deviation slope"); - title = item.getTitle(); - if (titleMaxChars < title.length()) titleMaxChars = title.length(); - s = new SpannableString(title); - s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.devslopepos, null)), 0, s.length(), 0); - item.setTitle(s); - item.setCheckable(true); - item.setChecked(showDevslope); - } - - // Fairly good guestimate for required divider text size... - title = new String(new char[titleMaxChars + 10]).replace("\0", "_"); - dividerItem.setTitle(title); - - popup.setOnMenuItemClickListener(item1 -> { - if (item1.getItemId() == OverviewFragment.CHARTTYPE.BAS.ordinal()) { - sp.putBoolean("hist_showbasals", !item1.isChecked()); - } else if (item1.getItemId() == OverviewFragment.CHARTTYPE.IOB.ordinal()) { - sp.putBoolean("hist_showiob", !item1.isChecked()); - } else if (item1.getItemId() == OverviewFragment.CHARTTYPE.COB.ordinal()) { - sp.putBoolean("hist_showcob", !item1.isChecked()); - } else if (item1.getItemId() == OverviewFragment.CHARTTYPE.DEV.ordinal()) { - sp.putBoolean("hist_showdeviations", !item1.isChecked()); - } else if (item1.getItemId() == OverviewFragment.CHARTTYPE.SEN.ordinal()) { - sp.putBoolean("hist_showratios", !item1.isChecked()); - } else if (item1.getItemId() == OverviewFragment.CHARTTYPE.ACTPRIM.ordinal()) { - sp.putBoolean("hist_showactivityprimary", !item1.isChecked()); - } else if (item1.getItemId() == OverviewFragment.CHARTTYPE.ACTSEC.ordinal()) { - sp.putBoolean("hist_showactivitysecondary", !item1.isChecked()); - } else if (item1.getItemId() == OverviewFragment.CHARTTYPE.DEVSLOPE.ordinal()) { - sp.putBoolean("hist_showdevslope", !item1.isChecked()); - } - updateGUI("onGraphCheckboxesCheckedChanged"); - return true; - }); - chartButton.setImageResource(R.drawable.ic_arrow_drop_up_white_24dp); - popup.setOnDismissListener(menu -> chartButton.setImageResource(R.drawable.ic_arrow_drop_down_white_24dp)); - popup.show(); - }); - } - } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java index a3d86716bb..d7bb8e35a5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java @@ -10,13 +10,10 @@ import android.content.pm.PackageManager; import android.graphics.Paint; import android.os.Bundle; import android.os.Handler; -import android.text.SpannableString; -import android.text.style.ForegroundColorSpan; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.ContextMenu; import android.view.LayoutInflater; -import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; @@ -25,9 +22,7 @@ import android.widget.LinearLayout; import android.widget.TextView; import androidx.annotation.NonNull; -import androidx.appcompat.widget.PopupMenu; import androidx.constraintlayout.widget.ConstraintLayout; -import androidx.core.content.res.ResourcesCompat; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import androidx.recyclerview.widget.LinearLayoutManager; @@ -35,6 +30,8 @@ import androidx.recyclerview.widget.RecyclerView; import com.jjoe64.graphview.GraphView; +import org.jetbrains.annotations.NotNull; + import java.util.Calendar; import java.util.Date; import java.util.Locale; @@ -55,15 +52,11 @@ import info.nightscout.androidaps.data.IobTotal; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.db.BgReading; import info.nightscout.androidaps.db.ExtendedBolus; -import info.nightscout.androidaps.db.Source; import info.nightscout.androidaps.db.TempTarget; import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.dialogs.CalibrationDialog; import info.nightscout.androidaps.dialogs.CarbsDialog; import info.nightscout.androidaps.dialogs.InsulinDialog; -import info.nightscout.androidaps.dialogs.ProfileSwitchDialog; -import info.nightscout.androidaps.dialogs.ProfileViewerDialog; -import info.nightscout.androidaps.dialogs.TempTargetDialog; import info.nightscout.androidaps.dialogs.TreatmentDialog; import info.nightscout.androidaps.dialogs.WizardDialog; import info.nightscout.androidaps.events.EventAcceptOpenLoopChange; @@ -80,7 +73,6 @@ import info.nightscout.androidaps.events.EventTreatmentChange; import info.nightscout.androidaps.interfaces.ActivePluginProvider; import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.logging.AAPSLogger; import info.nightscout.androidaps.logging.LTag; @@ -105,7 +97,6 @@ import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCa import info.nightscout.androidaps.plugins.source.DexcomPlugin; import info.nightscout.androidaps.plugins.source.XdripPlugin; import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; -import info.nightscout.androidaps.queue.Callback; import info.nightscout.androidaps.queue.CommandQueue; import info.nightscout.androidaps.utils.DateUtil; import info.nightscout.androidaps.utils.DecimalFormatter; @@ -127,8 +118,6 @@ import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.schedulers.Schedulers; -import static info.nightscout.androidaps.utils.DateUtil.now; - public class OverviewFragment extends DaggerFragment implements View.OnClickListener, View.OnLongClickListener { @Inject HasAndroidInjector injector; @Inject AAPSLogger aapsLogger; @@ -154,6 +143,8 @@ public class OverviewFragment extends DaggerFragment implements View.OnClickList @Inject BuildHelper buildHelper; @Inject CommandQueue commandQueue; @Inject ProtectionCheck protectionCheck; + @Inject FabricPrivacy fabricPrivacy; + @Inject OverviewMenus overviewMenus; private CompositeDisposable disposable = new CompositeDisposable(); @@ -182,7 +173,6 @@ public class OverviewFragment extends DaggerFragment implements View.OnClickList LinearLayout pumpStatusLayout; GraphView bgGraph; GraphView iobGraph; - ImageButton chartButton; TextView iage; TextView cage; @@ -214,8 +204,6 @@ public class OverviewFragment extends DaggerFragment implements View.OnClickList public static boolean shorttextmode = false; - private boolean accepted; - private int rangeToDisplay = 6; // for graph Handler sLoopHandler = new Handler(); @@ -364,7 +352,7 @@ public class OverviewFragment extends DaggerFragment implements View.OnClickList return false; }); - setupChartMenu(view); + overviewMenus.setupChartMenu(view.findViewById(R.id.overview_chartMenuButton)); return view; } @@ -386,79 +374,79 @@ public class OverviewFragment extends DaggerFragment implements View.OnClickList .toObservable(EventRefreshOverview.class) .observeOn(Schedulers.io()) .subscribe(eventOpenAPSUpdateGui -> scheduleUpdateGUI(eventOpenAPSUpdateGui.getFrom()), - exception -> FabricPrivacy.getInstance().logException(exception) + fabricPrivacy::logException )); disposable.add(rxBus .toObservable(EventExtendedBolusChange.class) .observeOn(Schedulers.io()) .subscribe(event -> scheduleUpdateGUI("EventExtendedBolusChange"), - exception -> FabricPrivacy.getInstance().logException(exception) + fabricPrivacy::logException )); disposable.add(rxBus .toObservable(EventTempBasalChange.class) .observeOn(Schedulers.io()) .subscribe(event -> scheduleUpdateGUI("EventTempBasalChange"), - exception -> FabricPrivacy.getInstance().logException(exception) + fabricPrivacy::logException )); disposable.add(rxBus .toObservable(EventTreatmentChange.class) .observeOn(Schedulers.io()) .subscribe(event -> scheduleUpdateGUI("EventTreatmentChange"), - exception -> FabricPrivacy.getInstance().logException(exception) + fabricPrivacy::logException )); disposable.add(rxBus .toObservable(EventTempTargetChange.class) .observeOn(Schedulers.io()) .subscribe(event -> scheduleUpdateGUI("EventTempTargetChange"), - exception -> FabricPrivacy.getInstance().logException(exception) + fabricPrivacy::logException )); disposable.add(rxBus .toObservable(EventAcceptOpenLoopChange.class) .observeOn(Schedulers.io()) .subscribe(event -> scheduleUpdateGUI("EventAcceptOpenLoopChange"), - exception -> FabricPrivacy.getInstance().logException(exception) + fabricPrivacy::logException )); disposable.add(rxBus .toObservable(EventCareportalEventChange.class) .observeOn(Schedulers.io()) .subscribe(event -> scheduleUpdateGUI("EventCareportalEventChange"), - exception -> FabricPrivacy.getInstance().logException(exception) + fabricPrivacy::logException )); disposable.add(rxBus .toObservable(EventInitializationChanged.class) .observeOn(Schedulers.io()) .subscribe(event -> scheduleUpdateGUI("EventInitializationChanged"), - exception -> FabricPrivacy.getInstance().logException(exception) + fabricPrivacy::logException )); disposable.add(rxBus .toObservable(EventAutosensCalculationFinished.class) .observeOn(Schedulers.io()) .subscribe(event -> scheduleUpdateGUI("EventAutosensCalculationFinished"), - exception -> FabricPrivacy.getInstance().logException(exception) + fabricPrivacy::logException )); disposable.add(rxBus .toObservable(EventProfileNeedsUpdate.class) .observeOn(Schedulers.io()) .subscribe(event -> scheduleUpdateGUI("EventProfileNeedsUpdate"), - exception -> FabricPrivacy.getInstance().logException(exception) + fabricPrivacy::logException )); disposable.add(rxBus .toObservable(EventPreferenceChange.class) .observeOn(Schedulers.io()) .subscribe(event -> scheduleUpdateGUI("EventPreferenceChange"), - exception -> FabricPrivacy.getInstance().logException(exception) + fabricPrivacy::logException )); disposable.add(rxBus .toObservable(EventNewOpenLoopNotification.class) .observeOn(Schedulers.io()) .subscribe(event -> scheduleUpdateGUI("EventNewOpenLoopNotification"), - exception -> FabricPrivacy.getInstance().logException(exception) + fabricPrivacy::logException )); disposable.add(rxBus .toObservable(EventPumpStatusChanged.class) .observeOn(AndroidSchedulers.mainThread()) .subscribe(event -> updatePumpStatus(event), - exception -> FabricPrivacy.getInstance().logException(exception) + fabricPrivacy::logException )); disposable.add(rxBus .toObservable(EventIobCalculationProgress.class) @@ -467,7 +455,7 @@ public class OverviewFragment extends DaggerFragment implements View.OnClickList if (iobCalculationProgressView != null) iobCalculationProgressView.setText(event.getProgress()); }, - exception -> FabricPrivacy.getInstance().logException(exception) + fabricPrivacy::logException )); sRefreshLoop = () -> { scheduleUpdateGUI("refreshLoop"); @@ -480,367 +468,18 @@ public class OverviewFragment extends DaggerFragment implements View.OnClickList updateGUI("onResume"); } - private void setupChartMenu(View view) { - chartButton = (ImageButton) view.findViewById(R.id.overview_chartMenuButton); - chartButton.setOnClickListener(v -> { - boolean predictionsAvailable; - if (Config.APS) - predictionsAvailable = loopPlugin.lastRun != null && loopPlugin.lastRun.request.hasPredictions; - else if (Config.NSCLIENT) - predictionsAvailable = true; - else - predictionsAvailable = false; - - MenuItem item, dividerItem; - CharSequence title; - int titleMaxChars = 0; - SpannableString s; - PopupMenu popup = new PopupMenu(v.getContext(), v); - if (predictionsAvailable) { - item = popup.getMenu().add(Menu.NONE, CHARTTYPE.PRE.ordinal(), Menu.NONE, "Predictions"); - title = item.getTitle(); - if (titleMaxChars < title.length()) titleMaxChars = title.length(); - s = new SpannableString(title); - s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.prediction, null)), 0, s.length(), 0); - item.setTitle(s); - item.setCheckable(true); - item.setChecked(sp.getBoolean("showprediction", true)); - } - - item = popup.getMenu().add(Menu.NONE, CHARTTYPE.BAS.ordinal(), Menu.NONE, resourceHelper.gs(R.string.overview_show_basals)); - title = item.getTitle(); - if (titleMaxChars < title.length()) titleMaxChars = title.length(); - s = new SpannableString(title); - s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.basal, null)), 0, s.length(), 0); - item.setTitle(s); - item.setCheckable(true); - item.setChecked(sp.getBoolean("showbasals", true)); - - item = popup.getMenu().add(Menu.NONE, CHARTTYPE.ACTPRIM.ordinal(), Menu.NONE, resourceHelper.gs(R.string.overview_show_activity)); - title = item.getTitle(); - if (titleMaxChars < title.length()) titleMaxChars = title.length(); - s = new SpannableString(title); - s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.activity, null)), 0, s.length(), 0); - item.setTitle(s); - item.setCheckable(true); - item.setChecked(sp.getBoolean("showactivityprimary", true)); - - dividerItem = popup.getMenu().add(""); - dividerItem.setEnabled(false); - - item = popup.getMenu().add(Menu.NONE, CHARTTYPE.IOB.ordinal(), Menu.NONE, resourceHelper.gs(R.string.overview_show_iob)); - title = item.getTitle(); - if (titleMaxChars < title.length()) titleMaxChars = title.length(); - s = new SpannableString(title); - s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.iob, null)), 0, s.length(), 0); - item.setTitle(s); - item.setCheckable(true); - item.setChecked(sp.getBoolean("showiob", true)); - - item = popup.getMenu().add(Menu.NONE, CHARTTYPE.COB.ordinal(), Menu.NONE, resourceHelper.gs(R.string.overview_show_cob)); - title = item.getTitle(); - if (titleMaxChars < title.length()) titleMaxChars = title.length(); - s = new SpannableString(title); - s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.cob, null)), 0, s.length(), 0); - item.setTitle(s); - item.setCheckable(true); - item.setChecked(sp.getBoolean("showcob", true)); - - item = popup.getMenu().add(Menu.NONE, CHARTTYPE.DEV.ordinal(), Menu.NONE, resourceHelper.gs(R.string.overview_show_deviations)); - title = item.getTitle(); - if (titleMaxChars < title.length()) titleMaxChars = title.length(); - s = new SpannableString(title); - s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.deviations, null)), 0, s.length(), 0); - item.setTitle(s); - item.setCheckable(true); - item.setChecked(sp.getBoolean("showdeviations", false)); - - item = popup.getMenu().add(Menu.NONE, CHARTTYPE.SEN.ordinal(), Menu.NONE, resourceHelper.gs(R.string.overview_show_sensitivity)); - title = item.getTitle(); - if (titleMaxChars < title.length()) titleMaxChars = title.length(); - s = new SpannableString(title); - s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.ratio, null)), 0, s.length(), 0); - item.setTitle(s); - item.setCheckable(true); - item.setChecked(sp.getBoolean("showratios", false)); - - item = popup.getMenu().add(Menu.NONE, CHARTTYPE.ACTSEC.ordinal(), Menu.NONE, resourceHelper.gs(R.string.overview_show_activity)); - title = item.getTitle(); - if (titleMaxChars < title.length()) titleMaxChars = title.length(); - s = new SpannableString(title); - s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.activity, null)), 0, s.length(), 0); - item.setTitle(s); - item.setCheckable(true); - item.setChecked(sp.getBoolean("showactivitysecondary", true)); - - if (buildHelper.isDev()) { - item = popup.getMenu().add(Menu.NONE, CHARTTYPE.DEVSLOPE.ordinal(), Menu.NONE, "Deviation slope"); - title = item.getTitle(); - if (titleMaxChars < title.length()) titleMaxChars = title.length(); - s = new SpannableString(title); - s.setSpan(new ForegroundColorSpan(ResourcesCompat.getColor(getResources(), R.color.devslopepos, null)), 0, s.length(), 0); - item.setTitle(s); - item.setCheckable(true); - item.setChecked(sp.getBoolean("showdevslope", false)); - } - - // Fairly good guestimate for required divider text size... - title = new String(new char[titleMaxChars + 10]).replace("\0", "_"); - dividerItem.setTitle(title); - - popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem item) { - if (item.getItemId() == CHARTTYPE.PRE.ordinal()) { - sp.putBoolean("showprediction", !item.isChecked()); - } else if (item.getItemId() == CHARTTYPE.BAS.ordinal()) { - sp.putBoolean("showbasals", !item.isChecked()); - } else if (item.getItemId() == CHARTTYPE.IOB.ordinal()) { - sp.putBoolean("showiob", !item.isChecked()); - } else if (item.getItemId() == CHARTTYPE.COB.ordinal()) { - sp.putBoolean("showcob", !item.isChecked()); - } else if (item.getItemId() == CHARTTYPE.DEV.ordinal()) { - sp.putBoolean("showdeviations", !item.isChecked()); - } else if (item.getItemId() == CHARTTYPE.SEN.ordinal()) { - sp.putBoolean("showratios", !item.isChecked()); - } else if (item.getItemId() == CHARTTYPE.ACTPRIM.ordinal()) { - sp.putBoolean("showactivityprimary", !item.isChecked()); - } else if (item.getItemId() == CHARTTYPE.ACTSEC.ordinal()) { - sp.putBoolean("showactivitysecondary", !item.isChecked()); - } else if (item.getItemId() == CHARTTYPE.DEVSLOPE.ordinal()) { - sp.putBoolean("showdevslope", !item.isChecked()); - } - scheduleUpdateGUI("onGraphCheckboxesCheckedChanged"); - return true; - } - }); - chartButton.setImageResource(R.drawable.ic_arrow_drop_up_white_24dp); - popup.setOnDismissListener(new PopupMenu.OnDismissListener() { - @Override - public void onDismiss(PopupMenu menu) { - chartButton.setImageResource(R.drawable.ic_arrow_drop_down_white_24dp); - } - }); - popup.show(); - }); - } - @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { + public void onCreateContextMenu(@NotNull ContextMenu menu, @NotNull View v, ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); - if (v == apsModeView) { - final PumpDescription pumpDescription = - activePlugin.getActivePump().getPumpDescription(); - if (!profileFunction.isProfileValid("ContexMenuCreation")) - return; - menu.setHeaderTitle(resourceHelper.gs(R.string.loop)); - if (loopPlugin.isEnabled(PluginType.LOOP)) { - menu.add(resourceHelper.gs(R.string.disableloop)); - if (!loopPlugin.isSuspended()) { - menu.add(resourceHelper.gs(R.string.suspendloopfor1h)); - menu.add(resourceHelper.gs(R.string.suspendloopfor2h)); - menu.add(resourceHelper.gs(R.string.suspendloopfor3h)); - menu.add(resourceHelper.gs(R.string.suspendloopfor10h)); - } else { - if (!loopPlugin.isDisconnected()) { - menu.add(resourceHelper.gs(R.string.resume)); - } - } - } - - if (!loopPlugin.isEnabled(PluginType.LOOP)) { - menu.add(resourceHelper.gs(R.string.enableloop)); - } - - if (!loopPlugin.isDisconnected()) { - showSuspendtPump(menu, pumpDescription); - } else { - menu.add(resourceHelper.gs(R.string.reconnect)); - } - - } else if (v == activeProfileView) { - menu.setHeaderTitle(resourceHelper.gs(R.string.profile)); - menu.add(resourceHelper.gs(R.string.danar_viewprofile)); - activePlugin.getActiveProfileInterface(); - if (activePlugin.getActiveProfileInterface().getProfile() != null) { - menu.add(resourceHelper.gs(R.string.careportal_profileswitch)); - } - } else if (v == tempTargetView) { - menu.setHeaderTitle(resourceHelper.gs(R.string.careportal_temporarytarget)); - menu.add(resourceHelper.gs(R.string.custom)); - menu.add(resourceHelper.gs(R.string.eatingsoon)); - menu.add(resourceHelper.gs(R.string.activity)); - menu.add(resourceHelper.gs(R.string.hypo)); - if (treatmentsPlugin.getTempTargetFromHistory() != null) { - menu.add(resourceHelper.gs(R.string.cancel)); - } - } - } - - private void showSuspendtPump(ContextMenu menu, PumpDescription pumpDescription) { - if (pumpDescription.tempDurationStep15mAllowed) - menu.add(resourceHelper.gs(R.string.disconnectpumpfor15m)); - if (pumpDescription.tempDurationStep30mAllowed) - menu.add(resourceHelper.gs(R.string.disconnectpumpfor30m)); - menu.add(resourceHelper.gs(R.string.disconnectpumpfor1h)); - menu.add(resourceHelper.gs(R.string.disconnectpumpfor2h)); - menu.add(resourceHelper.gs(R.string.disconnectpumpfor3h)); + overviewMenus.createContextMenu(menu, v); } @Override public boolean onContextItemSelected(@NonNull MenuItem item) { - final Profile profile = profileFunction.getProfile(); - if (profile == null) - return true; - if (item.getTitle().equals(resourceHelper.gs(R.string.disableloop))) { - aapsLogger.debug("USER ENTRY: LOOP DISABLED"); - loopPlugin.setPluginEnabled(PluginType.LOOP, false); - loopPlugin.setFragmentVisible(PluginType.LOOP, false); - configBuilderPlugin.storeSettings("DisablingLoop"); - updateGUI("suspendmenu"); - commandQueue.cancelTempBasal(true, new Callback() { - @Override - public void run() { - if (!result.success) { - ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.tempbasaldeliveryerror)); - } - } - }); - loopPlugin.createOfflineEvent(24 * 60); // upload 24h, we don't know real duration - return true; - } else if (item.getTitle().equals(resourceHelper.gs(R.string.enableloop))) { - aapsLogger.debug("USER ENTRY: LOOP ENABLED"); - loopPlugin.setPluginEnabled(PluginType.LOOP, true); - loopPlugin.setFragmentVisible(PluginType.LOOP, true); - configBuilderPlugin.storeSettings("EnablingLoop"); - updateGUI("suspendmenu"); - loopPlugin.createOfflineEvent(0); - return true; - } else if (item.getTitle().equals(resourceHelper.gs(R.string.resume)) || - item.getTitle().equals(resourceHelper.gs(R.string.reconnect))) { - aapsLogger.debug("USER ENTRY: RESUME"); - loopPlugin.suspendTo(0L); - updateGUI("suspendmenu"); - commandQueue.cancelTempBasal(true, new Callback() { - @Override - public void run() { - if (!result.success) { - ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.tempbasaldeliveryerror)); - } - } - }); - sp.putBoolean(R.string.key_objectiveusereconnect, true); - loopPlugin.createOfflineEvent(0); - return true; - } else if (item.getTitle().equals(resourceHelper.gs(R.string.suspendloopfor1h))) { - aapsLogger.debug("USER ENTRY: SUSPEND 1h"); - loopPlugin.suspendLoop(60); - updateGUI("suspendmenu"); - return true; - } else if (item.getTitle().equals(resourceHelper.gs(R.string.suspendloopfor2h))) { - aapsLogger.debug("USER ENTRY: SUSPEND 2h"); - loopPlugin.suspendLoop(120); - updateGUI("suspendmenu"); - return true; - } else if (item.getTitle().equals(resourceHelper.gs(R.string.suspendloopfor3h))) { - aapsLogger.debug("USER ENTRY: SUSPEND 3h"); - loopPlugin.suspendLoop(180); - updateGUI("suspendmenu"); - return true; - } else if (item.getTitle().equals(resourceHelper.gs(R.string.suspendloopfor10h))) { - aapsLogger.debug("USER ENTRY: SUSPEND 10h"); - loopPlugin.suspendLoop(600); - updateGUI("suspendmenu"); - return true; - } else if (item.getTitle().equals(resourceHelper.gs(R.string.disconnectpumpfor15m))) { - aapsLogger.debug("USER ENTRY: DISCONNECT 15m"); - loopPlugin.disconnectPump(15, profile); - updateGUI("suspendmenu"); - return true; - } else if (item.getTitle().equals(resourceHelper.gs(R.string.disconnectpumpfor30m))) { - aapsLogger.debug("USER ENTRY: DISCONNECT 30m"); - loopPlugin.disconnectPump(30, profile); - updateGUI("suspendmenu"); - return true; - } else if (item.getTitle().equals(resourceHelper.gs(R.string.disconnectpumpfor1h))) { - aapsLogger.debug("USER ENTRY: DISCONNECT 1h"); - loopPlugin.disconnectPump(60, profile); - sp.putBoolean(R.string.key_objectiveusedisconnect, true); - updateGUI("suspendmenu"); - return true; - } else if (item.getTitle().equals(resourceHelper.gs(R.string.disconnectpumpfor2h))) { - aapsLogger.debug("USER ENTRY: DISCONNECT 2h"); - loopPlugin.disconnectPump(120, profile); - updateGUI("suspendmenu"); - return true; - } else if (item.getTitle().equals(resourceHelper.gs(R.string.disconnectpumpfor3h))) { - aapsLogger.debug("USER ENTRY: DISCONNECT 3h"); - loopPlugin.disconnectPump(180, profile); - updateGUI("suspendmenu"); - return true; - } else if (item.getTitle().equals(resourceHelper.gs(R.string.careportal_profileswitch))) { - FragmentManager manager = getFragmentManager(); - if (manager != null) - new ProfileSwitchDialog().show(manager, "Overview"); - } else if (item.getTitle().equals(resourceHelper.gs(R.string.danar_viewprofile))) { - Bundle args = new Bundle(); - args.putLong("time", DateUtil.now()); - args.putInt("mode", ProfileViewerDialog.Mode.RUNNING_PROFILE.ordinal()); - ProfileViewerDialog pvd = new ProfileViewerDialog(); - pvd.setArguments(args); - FragmentManager manager = getFragmentManager(); - if (manager != null) - pvd.show(manager, "ProfileViewDialog"); - } else if (item.getTitle().equals(resourceHelper.gs(R.string.eatingsoon))) { - aapsLogger.debug("USER ENTRY: TEMP TARGET EATING SOON"); - double target = Profile.toMgdl(defaultValueHelper.determineEatingSoonTT(), profileFunction.getUnits()); - TempTarget tempTarget = new TempTarget() - .date(System.currentTimeMillis()) - .duration(defaultValueHelper.determineEatingSoonTTDuration()) - .reason(resourceHelper.gs(R.string.eatingsoon)) - .source(Source.USER) - .low(target) - .high(target); - treatmentsPlugin.addToHistoryTempTarget(tempTarget); - } else if (item.getTitle().equals(resourceHelper.gs(R.string.activity))) { - aapsLogger.debug("USER ENTRY: TEMP TARGET ACTIVITY"); - double target = Profile.toMgdl(defaultValueHelper.determineActivityTT(), profileFunction.getUnits()); - TempTarget tempTarget = new TempTarget() - .date(now()) - .duration(defaultValueHelper.determineActivityTTDuration()) - .reason(resourceHelper.gs(R.string.activity)) - .source(Source.USER) - .low(target) - .high(target); - treatmentsPlugin.addToHistoryTempTarget(tempTarget); - } else if (item.getTitle().equals(resourceHelper.gs(R.string.hypo))) { - aapsLogger.debug("USER ENTRY: TEMP TARGET HYPO"); - double target = Profile.toMgdl(defaultValueHelper.determineHypoTT(), profileFunction.getUnits()); - TempTarget tempTarget = new TempTarget() - .date(now()) - .duration(defaultValueHelper.determineHypoTTDuration()) - .reason(resourceHelper.gs(R.string.hypo)) - .source(Source.USER) - .low(target) - .high(target); - treatmentsPlugin.addToHistoryTempTarget(tempTarget); - } else if (item.getTitle().equals(resourceHelper.gs(R.string.custom))) { - FragmentManager manager = getFragmentManager(); - if (manager != null) - new TempTargetDialog().show(manager, "Overview"); - } else if (item.getTitle().equals(resourceHelper.gs(R.string.cancel))) { - aapsLogger.debug("USER ENTRY: TEMP TARGET CANCEL"); - TempTarget tempTarget = new TempTarget() - .source(Source.USER) - .date(now()) - .duration(0) - .low(0) - .high(0); - treatmentsPlugin.addToHistoryTempTarget(tempTarget); - } - - return super.onContextItemSelected(item); + FragmentManager manager = getFragmentManager(); + if (manager != null && overviewMenus.onContextItemSelected(item, manager)) return true; + else return super.onContextItemSelected(item); } @Override diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewMenus.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewMenus.kt new file mode 100644 index 0000000000..3336f62ab6 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewMenus.kt @@ -0,0 +1,433 @@ +package info.nightscout.androidaps.plugins.general.overview + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.text.SpannableString +import android.text.style.ForegroundColorSpan +import android.view.ContextMenu +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.widget.ImageButton +import androidx.appcompat.widget.PopupMenu +import androidx.fragment.app.FragmentManager +import info.nightscout.androidaps.Config +import info.nightscout.androidaps.R +import info.nightscout.androidaps.activities.ErrorHelperActivity +import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.db.Source +import info.nightscout.androidaps.db.TempTarget +import info.nightscout.androidaps.dialogs.ProfileSwitchDialog +import info.nightscout.androidaps.dialogs.ProfileViewerDialog +import info.nightscout.androidaps.dialogs.TempTargetDialog +import info.nightscout.androidaps.events.EventRefreshOverview +import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.CommandQueueProvider +import info.nightscout.androidaps.interfaces.PluginType +import info.nightscout.androidaps.interfaces.PumpDescription +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction +import info.nightscout.androidaps.plugins.general.overview.OverviewFragment.CHARTTYPE +import info.nightscout.androidaps.queue.Callback +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.DefaultValueHelper +import info.nightscout.androidaps.utils.ToastUtils +import info.nightscout.androidaps.utils.buildHelper.BuildHelper +import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.androidaps.utils.sharedPreferences.SP +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class OverviewMenus @Inject constructor( + private val aapsLogger: AAPSLogger, + private val resourceHelper: ResourceHelper, + private val sp: SP, + private val rxBus: RxBusWrapper, + private val context: Context, + private val buildHelper: BuildHelper, + private val defaultValueHelper: DefaultValueHelper, + private val activePlugin: ActivePluginProvider, + private val profileFunction: ProfileFunction, + private val commandQueue: CommandQueueProvider, + private val configBuilderPlugin: ConfigBuilderPlugin, + private val loopPlugin: LoopPlugin +) { + + fun setupChartMenu(chartButton: ImageButton) { + chartButton.setOnClickListener { v: View -> + val predictionsAvailable: Boolean = when { + Config.APS -> loopPlugin.lastRun?.request?.hasPredictions ?: false + Config.NSCLIENT -> true + else -> false + } + //var item: MenuItem + val dividerItem: MenuItem + //var title: CharSequence + var titleMaxChars = 0 + //var s: SpannableString + val popup = PopupMenu(v.context, v) + if (predictionsAvailable) { + val item = popup.menu.add(Menu.NONE, CHARTTYPE.PRE.ordinal, Menu.NONE, "Predictions") + val title = item.title + if (titleMaxChars < title.length) titleMaxChars = title.length + val s = SpannableString(title) + s.setSpan(ForegroundColorSpan(resourceHelper.gc(R.color.prediction)), 0, s.length, 0) + item.title = s + item.isCheckable = true + item.isChecked = sp.getBoolean("showprediction", true) + } + run { + val item = popup.menu.add(Menu.NONE, CHARTTYPE.BAS.ordinal, Menu.NONE, resourceHelper.gs(R.string.overview_show_basals)) + val title = item.title + if (titleMaxChars < title.length) titleMaxChars = title.length + val s = SpannableString(title) + s.setSpan(ForegroundColorSpan(resourceHelper.gc(R.color.basal)), 0, s.length, 0) + item.title = s + item.isCheckable = true + item.isChecked = sp.getBoolean("showbasals", true) + } + run { + val item = popup.menu.add(Menu.NONE, CHARTTYPE.ACTPRIM.ordinal, Menu.NONE, resourceHelper.gs(R.string.overview_show_activity)) + val title = item.title + if (titleMaxChars < title.length) titleMaxChars = title.length + val s = SpannableString(title) + s.setSpan(ForegroundColorSpan(resourceHelper.gc(R.color.activity)), 0, s.length, 0) + item.title = s + item.isCheckable = true + item.isChecked = sp.getBoolean("showactivityprimary", true) + dividerItem = popup.menu.add("") + dividerItem.isEnabled = false + } + run { + val item = popup.menu.add(Menu.NONE, CHARTTYPE.IOB.ordinal, Menu.NONE, resourceHelper.gs(R.string.overview_show_iob)) + val title = item.title + if (titleMaxChars < title.length) titleMaxChars = title.length + val s = SpannableString(title) + s.setSpan(ForegroundColorSpan(resourceHelper.gc(R.color.iob)), 0, s.length, 0) + item.title = s + item.isCheckable = true + item.isChecked = sp.getBoolean("showiob", true) + } + run { + val item = popup.menu.add(Menu.NONE, CHARTTYPE.COB.ordinal, Menu.NONE, resourceHelper.gs(R.string.overview_show_cob)) + val title = item.title + if (titleMaxChars < title.length) titleMaxChars = title.length + val s = SpannableString(title) + s.setSpan(ForegroundColorSpan(resourceHelper.gc(R.color.cob)), 0, s.length, 0) + item.title = s + item.isCheckable = true + item.isChecked = sp.getBoolean("showcob", true) + } + run { + val item = popup.menu.add(Menu.NONE, CHARTTYPE.DEV.ordinal, Menu.NONE, resourceHelper.gs(R.string.overview_show_deviations)) + val title = item.title + if (titleMaxChars < title.length) titleMaxChars = title.length + val s = SpannableString(title) + s.setSpan(ForegroundColorSpan(resourceHelper.gc(R.color.deviations)), 0, s.length, 0) + item.title = s + item.isCheckable = true + item.isChecked = sp.getBoolean("showdeviations", false) + } + run { + val item = popup.menu.add(Menu.NONE, CHARTTYPE.SEN.ordinal, Menu.NONE, resourceHelper.gs(R.string.overview_show_sensitivity)) + val title = item.title + if (titleMaxChars < title.length) titleMaxChars = title.length + val s = SpannableString(title) + s.setSpan(ForegroundColorSpan(resourceHelper.gc(R.color.ratio)), 0, s.length, 0) + item.title = s + item.isCheckable = true + item.isChecked = sp.getBoolean("showratios", false) + } + run { + val item = popup.menu.add(Menu.NONE, CHARTTYPE.ACTSEC.ordinal, Menu.NONE, resourceHelper.gs(R.string.overview_show_activity)) + val title = item.title + if (titleMaxChars < title.length) titleMaxChars = title.length + val s = SpannableString(title) + s.setSpan(ForegroundColorSpan(resourceHelper.gc(R.color.activity)), 0, s.length, 0) + item.title = s + item.isCheckable = true + item.isChecked = sp.getBoolean("showactivitysecondary", true) + } + if (buildHelper.isDev()) { + val item = popup.menu.add(Menu.NONE, CHARTTYPE.DEVSLOPE.ordinal, Menu.NONE, "Deviation slope") + val title = item.title + if (titleMaxChars < title.length) titleMaxChars = title.length + val s = SpannableString(title) + s.setSpan(ForegroundColorSpan(resourceHelper.gc(R.color.devslopepos)), 0, s.length, 0) + item.title = s + item.isCheckable = true + item.isChecked = sp.getBoolean("showdevslope", false) + } + + // Fairly good estimate for required divider text size... + dividerItem.title = String(CharArray(titleMaxChars + 10)).replace("\u0000", "_") + popup.setOnMenuItemClickListener { + when (it.itemId) { + CHARTTYPE.PRE.ordinal -> sp.putBoolean("showprediction", !it.isChecked) + CHARTTYPE.BAS.ordinal -> sp.putBoolean("showbasals", !it.isChecked) + CHARTTYPE.IOB.ordinal -> sp.putBoolean("showiob", !it.isChecked) + CHARTTYPE.COB.ordinal -> sp.putBoolean("showcob", !it.isChecked) + CHARTTYPE.DEV.ordinal -> sp.putBoolean("showdeviations", !it.isChecked) + CHARTTYPE.SEN.ordinal -> sp.putBoolean("showratios", !it.isChecked) + CHARTTYPE.ACTPRIM.ordinal -> sp.putBoolean("showactivityprimary", !it.isChecked) + CHARTTYPE.ACTSEC.ordinal -> sp.putBoolean("showactivitysecondary", !it.isChecked) + CHARTTYPE.DEVSLOPE.ordinal -> sp.putBoolean("showdevslope", !it.isChecked) + } + rxBus.send(EventRefreshOverview("OnMenuItemClickListener")) + return@setOnMenuItemClickListener true + } + chartButton.setImageResource(R.drawable.ic_arrow_drop_up_white_24dp) + popup.setOnDismissListener { chartButton.setImageResource(R.drawable.ic_arrow_drop_down_white_24dp) } + popup.show() + } + } + + fun createContextMenu(menu: ContextMenu, v: View) { + when (v.id) { + R.id.overview_apsmode -> { + val pumpDescription: PumpDescription = activePlugin.activePump.pumpDescription + if (!profileFunction.isProfileValid("ContextMenuCreation")) return + menu.setHeaderTitle(resourceHelper.gs(R.string.loop)) + if (loopPlugin.isEnabled(PluginType.LOOP)) { + menu.add(resourceHelper.gs(R.string.disableloop)) + if (!loopPlugin.isSuspended) { + menu.add(resourceHelper.gs(R.string.suspendloopfor1h)) + menu.add(resourceHelper.gs(R.string.suspendloopfor2h)) + menu.add(resourceHelper.gs(R.string.suspendloopfor3h)) + menu.add(resourceHelper.gs(R.string.suspendloopfor10h)) + } else { + if (!loopPlugin.isDisconnected) { + menu.add(resourceHelper.gs(R.string.resume)) + } + } + } + if (!loopPlugin.isEnabled(PluginType.LOOP)) { + menu.add(resourceHelper.gs(R.string.enableloop)) + } + if (!loopPlugin.isDisconnected) { + showSuspendPump(menu, pumpDescription) + } else { + menu.add(resourceHelper.gs(R.string.reconnect)) + } + } + + R.id.overview_activeprofile -> { + menu.setHeaderTitle(resourceHelper.gs(R.string.profile)) + menu.add(resourceHelper.gs(R.string.danar_viewprofile)) + if (activePlugin.activeProfileInterface.profile != null) { + menu.add(resourceHelper.gs(R.string.careportal_profileswitch)) + } + } + + R.id.overview_temptarget -> { + menu.setHeaderTitle(resourceHelper.gs(R.string.careportal_temporarytarget)) + menu.add(resourceHelper.gs(R.string.custom)) + menu.add(resourceHelper.gs(R.string.eatingsoon)) + menu.add(resourceHelper.gs(R.string.activity)) + menu.add(resourceHelper.gs(R.string.hypo)) + if (activePlugin.activeTreatments.tempTargetFromHistory != null) { + menu.add(resourceHelper.gs(R.string.cancel)) + } + } + } + } + + private fun showSuspendPump(menu: ContextMenu, pumpDescription: PumpDescription) { + if (pumpDescription.tempDurationStep15mAllowed) menu.add(resourceHelper.gs(R.string.disconnectpumpfor15m)) + if (pumpDescription.tempDurationStep30mAllowed) menu.add(resourceHelper.gs(R.string.disconnectpumpfor30m)) + menu.add(resourceHelper.gs(R.string.disconnectpumpfor1h)) + menu.add(resourceHelper.gs(R.string.disconnectpumpfor2h)) + menu.add(resourceHelper.gs(R.string.disconnectpumpfor3h)) + } + + fun onContextItemSelected(item: MenuItem, manager: FragmentManager): Boolean { + val profile = profileFunction.getProfile() ?: return true + when (item.title) { + resourceHelper.gs(R.string.disableloop) -> { + aapsLogger.debug("USER ENTRY: LOOP DISABLED") + loopPlugin.setPluginEnabled(PluginType.LOOP, false) + loopPlugin.setFragmentVisible(PluginType.LOOP, false) + configBuilderPlugin.storeSettings("DisablingLoop") + rxBus.send(EventRefreshOverview("suspendmenu")) + commandQueue.cancelTempBasal(true, object : Callback() { + override fun run() { + if (!result.success) { + ToastUtils.showToastInUiThread(context, resourceHelper.gs(R.string.tempbasaldeliveryerror)) + } + } + }) + loopPlugin.createOfflineEvent(24 * 60) // upload 24h, we don't know real duration + return true + } + + resourceHelper.gs(R.string.enableloop) -> { + aapsLogger.debug("USER ENTRY: LOOP ENABLED") + loopPlugin.setPluginEnabled(PluginType.LOOP, true) + loopPlugin.setFragmentVisible(PluginType.LOOP, true) + configBuilderPlugin.storeSettings("EnablingLoop") + rxBus.send(EventRefreshOverview("suspendmenu")) + loopPlugin.createOfflineEvent(0) + return true + } + + resourceHelper.gs(R.string.resume), resourceHelper.gs(R.string.reconnect) -> { + aapsLogger.debug("USER ENTRY: RESUME") + loopPlugin.suspendTo(0L) + rxBus.send(EventRefreshOverview("suspendmenu")) + commandQueue.cancelTempBasal(true, object : Callback() { + override fun run() { + if (!result.success) { + val i = Intent(context, ErrorHelperActivity::class.java) + i.putExtra("soundid", R.raw.boluserror) + i.putExtra("status", result.comment) + i.putExtra("title", resourceHelper.gs(R.string.tempbasaldeliveryerror)) + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + context.startActivity(i) + } + } + }) + sp.putBoolean(R.string.key_objectiveusereconnect, true) + loopPlugin.createOfflineEvent(0) + return true + } + + resourceHelper.gs(R.string.suspendloopfor1h) -> { + aapsLogger.debug("USER ENTRY: SUSPEND 1h") + loopPlugin.suspendLoop(60) + rxBus.send(EventRefreshOverview("suspendmenu")) + return true + } + + resourceHelper.gs(R.string.suspendloopfor2h) -> { + aapsLogger.debug("USER ENTRY: SUSPEND 2h") + loopPlugin.suspendLoop(120) + rxBus.send(EventRefreshOverview("suspendmenu")) + return true + } + + resourceHelper.gs(R.string.suspendloopfor3h) -> { + aapsLogger.debug("USER ENTRY: SUSPEND 3h") + loopPlugin.suspendLoop(180) + rxBus.send(EventRefreshOverview("suspendmenu")) + return true + } + + resourceHelper.gs(R.string.suspendloopfor10h) -> { + aapsLogger.debug("USER ENTRY: SUSPEND 10h") + loopPlugin.suspendLoop(600) + rxBus.send(EventRefreshOverview("suspendmenu")) + return true + } + + resourceHelper.gs(R.string.disconnectpumpfor15m) -> { + aapsLogger.debug("USER ENTRY: DISCONNECT 15m") + loopPlugin.disconnectPump(15, profile) + rxBus.send(EventRefreshOverview("suspendmenu")) + return true + } + + resourceHelper.gs(R.string.disconnectpumpfor30m) -> { + aapsLogger.debug("USER ENTRY: DISCONNECT 30m") + loopPlugin.disconnectPump(30, profile) + rxBus.send(EventRefreshOverview("suspendmenu")) + return true + } + + resourceHelper.gs(R.string.disconnectpumpfor1h) -> { + aapsLogger.debug("USER ENTRY: DISCONNECT 1h") + loopPlugin.disconnectPump(60, profile) + sp.putBoolean(R.string.key_objectiveusedisconnect, true) + rxBus.send(EventRefreshOverview("suspendmenu")) + return true + } + + resourceHelper.gs(R.string.disconnectpumpfor2h) -> { + aapsLogger.debug("USER ENTRY: DISCONNECT 2h") + loopPlugin.disconnectPump(120, profile) + rxBus.send(EventRefreshOverview("suspendmenu")) + return true + } + + resourceHelper.gs(R.string.disconnectpumpfor3h) -> { + aapsLogger.debug("USER ENTRY: DISCONNECT 3h") + loopPlugin.disconnectPump(180, profile) + rxBus.send(EventRefreshOverview("suspendmenu")) + return true + } + + resourceHelper.gs(R.string.careportal_profileswitch) -> { + ProfileSwitchDialog().show(manager, "Overview") + } + + resourceHelper.gs(R.string.danar_viewprofile) -> { + val args = Bundle() + args.putLong("time", DateUtil.now()) + args.putInt("mode", ProfileViewerDialog.Mode.RUNNING_PROFILE.ordinal) + val pvd = ProfileViewerDialog() + pvd.arguments = args + pvd.show(manager, "ProfileViewDialog") + } + + resourceHelper.gs(R.string.eatingsoon) -> { + aapsLogger.debug("USER ENTRY: TEMP TARGET EATING SOON") + val target = Profile.toMgdl(defaultValueHelper.determineEatingSoonTT(), profileFunction.getUnits()) + val tempTarget = TempTarget() + .date(System.currentTimeMillis()) + .duration(defaultValueHelper.determineEatingSoonTTDuration()) + .reason(resourceHelper.gs(R.string.eatingsoon)) + .source(Source.USER) + .low(target) + .high(target) + activePlugin.activeTreatments.addToHistoryTempTarget(tempTarget) + } + + resourceHelper.gs(R.string.activity) -> { + aapsLogger.debug("USER ENTRY: TEMP TARGET ACTIVITY") + val target = Profile.toMgdl(defaultValueHelper.determineActivityTT(), profileFunction.getUnits()) + val tempTarget = TempTarget() + .date(DateUtil.now()) + .duration(defaultValueHelper.determineActivityTTDuration()) + .reason(resourceHelper.gs(R.string.activity)) + .source(Source.USER) + .low(target) + .high(target) + activePlugin.activeTreatments.addToHistoryTempTarget(tempTarget) + } + + resourceHelper.gs(R.string.hypo) -> { + aapsLogger.debug("USER ENTRY: TEMP TARGET HYPO") + val target = Profile.toMgdl(defaultValueHelper.determineHypoTT(), profileFunction.getUnits()) + val tempTarget = TempTarget() + .date(DateUtil.now()) + .duration(defaultValueHelper.determineHypoTTDuration()) + .reason(resourceHelper.gs(R.string.hypo)) + .source(Source.USER) + .low(target) + .high(target) + activePlugin.activeTreatments.addToHistoryTempTarget(tempTarget) + } + + resourceHelper.gs(R.string.custom) -> { + TempTargetDialog().show(manager, "Overview") + } + + resourceHelper.gs(R.string.cancel) -> { + aapsLogger.debug("USER ENTRY: TEMP TARGET CANCEL") + val tempTarget = TempTarget() + .source(Source.USER) + .date(DateUtil.now()) + .duration(0) + .low(0.0) + .high(0.0) + activePlugin.activeTreatments.addToHistoryTempTarget(tempTarget) + } + } + return false + } + +} \ No newline at end of file From cf35fc52cd56e86ebde1c710e3a07795eb6bb8b1 Mon Sep 17 00:00:00 2001 From: Tim Gunn <2896311+Tornado-Tim@users.noreply.github.com> Date: Sun, 5 Apr 2020 02:46:24 +1300 Subject: [PATCH 29/36] cherry pick big fix: cb8e94990301277fb1016c778b4e9efa55a6edbc --- app/src/main/assets/OpenAPSSMB/determine-basal.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/assets/OpenAPSSMB/determine-basal.js b/app/src/main/assets/OpenAPSSMB/determine-basal.js index 363e46da73..00a9c1d0a2 100644 --- a/app/src/main/assets/OpenAPSSMB/determine-basal.js +++ b/app/src/main/assets/OpenAPSSMB/determine-basal.js @@ -142,14 +142,16 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ if (minAgo > 12 || minAgo < -5) { // Dexcom data is too old, or way in the future rT.reason = "If current system time "+systemTime+" is correct, then BG data is too old. The last BG data was read "+minAgo+"m ago at "+bgTime; // if BG is too old/noisy, or is changing less than 1 mg/dL/5m for 45m, cancel any high temps and shorten any long zero temps - } else if ( bg > 60 && glucose_status == 0 && glucose_status.short_avgdelta > -1 && glucose_status.short_avgdelta < 1 && glucose_status.long_avgdelta > -1 && glucose_status.long_avgdelta < 1 ) { + //cherry pick from oref upstream dev cb8e94990301277fb1016c778b4e9efa55a6edbc + } else if ( bg > 60 && glucose_status.delta == 0 && glucose_status.short_avgdelta > -1 && glucose_status.short_avgdelta < 1 && glucose_status.long_avgdelta > -1 && glucose_status.long_avgdelta < 1 ) { if ( glucose_status.last_cal && glucose_status.last_cal < 3 ) { rT.reason = "CGM was just calibrated"; } else { rT.reason = "Error: CGM data is unchanged for the past ~45m"; } } - if (bg <= 10 || bg === 38 || noise >= 3 || minAgo > 12 || minAgo < -5 || ( bg > 60 && glucose_status == 0 && glucose_status.short_avgdelta > -1 && glucose_status.short_avgdelta < 1 && glucose_status.long_avgdelta > -1 && glucose_status.long_avgdelta < 1 ) ) { + //cherry pick from oref upstream dev cb8e94990301277fb1016c778b4e9efa55a6edbc + if (bg <= 10 || bg === 38 || noise >= 3 || minAgo > 12 || minAgo < -5 || ( bg > 60 && glucose_status.delta == 0 && glucose_status.short_avgdelta > -1 && glucose_status.short_avgdelta < 1 && glucose_status.long_avgdelta > -1 && glucose_status.long_avgdelta < 1 ) ) { if (currenttemp.rate > basal) { // high temp is running rT.reason += ". Replacing high temp basal of "+currenttemp.rate+" with neutral temp of "+basal; rT.deliverAt = deliverAt; From 033ca414f74b0c338e454eb3f8a80ce719abfc83 Mon Sep 17 00:00:00 2001 From: Dominik Dzienia Date: Sun, 5 Apr 2020 01:37:05 +0200 Subject: [PATCH 30/36] Fixed crash on pre-26 API due tu lack of java.time.Duration in dependent OTP lib --- .../plugins/general/smsCommunicator/otp/OneTimePassword.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/otp/OneTimePassword.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/otp/OneTimePassword.kt index a3e1e9a1f1..2f49ae25ef 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/otp/OneTimePassword.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/otp/OneTimePassword.kt @@ -1,7 +1,7 @@ package info.nightscout.androidaps.plugins.general.smsCommunicator.otp import android.util.Base64 -import com.eatthepath.otp.TimeBasedOneTimePasswordGenerator +import com.eatthepath.otp.HmacOneTimePasswordGenerator import com.google.common.io.BaseEncoding import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R @@ -23,7 +23,7 @@ class OneTimePassword @Inject constructor( private var key: SecretKey? = null private var pin: String = "" - private val totp = TimeBasedOneTimePasswordGenerator() + private val totp = HmacOneTimePasswordGenerator() init { instance = this From 312d863e381891f65d9107f80b006f8b10a7704e Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Sun, 5 Apr 2020 10:04:21 +0200 Subject: [PATCH 31/36] OverviewFragment refactor --- .../androidaps/db/CareportalEvent.java | 4 - .../nightscout/androidaps/logging/LTag.kt | 1 - .../plugins/aps/loop/LoopFragment.kt | 2 +- .../plugins/aps/loop/LoopPlugin.java | 12 +- .../dataBroadcaster/DataBroadcastPlugin.kt | 7 +- .../plugins/general/nsclient/NSUpload.java | 2 +- .../general/overview/OverviewFragment.java | 1184 ----------------- .../general/overview/OverviewFragment.kt | 825 ++++++++++++ .../plugins/general/overview/OverviewMenus.kt | 41 +- .../general/overview/StatusLightHandler.kt | 18 +- .../general/overview/graphData/GraphData.kt | 2 +- .../general/wear/ActionStringHandler.kt | 18 +- .../nightscout/androidaps/utils/OKDialog.kt | 2 - .../utils/resources/ResourceHelper.kt | 1 + .../resources/ResourceHelperImplementation.kt | 2 + 15 files changed, 881 insertions(+), 1240 deletions(-) delete mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt 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 9912644797..bb412703f4 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java +++ b/app/src/main/java/info/nightscout/androidaps/db/CareportalEvent.java @@ -107,10 +107,6 @@ public class CareportalEvent implements DataPointWithLabelInterface, Interval { return diff.get(TimeUnit.DAYS) + days + diff.get(TimeUnit.HOURS) + hours; } - public String age() { - return age(OverviewFragment.shorttextmode); - } - public boolean isOlderThan(double hours) { return getHoursFromStart() > hours; } diff --git a/app/src/main/java/info/nightscout/androidaps/logging/LTag.kt b/app/src/main/java/info/nightscout/androidaps/logging/LTag.kt index fc88377d6b..6e288a2bec 100644 --- a/app/src/main/java/info/nightscout/androidaps/logging/LTag.kt +++ b/app/src/main/java/info/nightscout/androidaps/logging/LTag.kt @@ -17,7 +17,6 @@ enum class LTag(val tag: String, val defaultValue : Boolean = true, val requires LOCATION("LOCATION"), NOTIFICATION("NOTIFICATION"), NSCLIENT("NSCLIENT"), - OVERVIEW("OVERVIEW", defaultValue = false), PUMP("PUMP"), PUMPBTCOMM("PUMPBTCOMM", defaultValue = false), PUMPCOMM("PUMPCOMM"), diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopFragment.kt index 5abe8c354b..8463e8f133 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopFragment.kt @@ -81,7 +81,7 @@ class LoopFragment : DaggerFragment() { loop_request?.text = it.request?.toSpanned() ?: "" loop_constraintsprocessed?.text = it.constraintsProcessed?.toSpanned() ?: "" loop_source?.text = it.source ?: "" - loop_lastrun?.text = it.lastAPSRun?.let { lastRun -> DateUtil.dateAndTimeString(lastRun.time) } + loop_lastrun?.text = DateUtil.dateAndTimeString(it.lastAPSRun) ?: "" loop_smbrequest_time?.text = DateUtil.dateAndTimeAndSecondsString(it.lastSMBRequest) loop_smbexecution_time?.text = DateUtil.dateAndTimeAndSecondsString(it.lastSMBEnact) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java index 79c3f5cd33..30c8603b71 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java @@ -13,6 +13,7 @@ import android.os.SystemClock; import androidx.core.app.NotificationCompat; +import org.jetbrains.annotations.Nullable; import org.json.JSONException; import org.json.JSONObject; @@ -104,7 +105,7 @@ public class LoopPlugin extends PluginBase { public PumpEnactResult tbrSetByPump = null; public PumpEnactResult smbSetByPump = null; public String source = null; - public Date lastAPSRun = null; + public long lastAPSRun = DateUtil.now(); public long lastTBREnact = 0; public long lastSMBEnact = 0; public long lastTBRRequest = 0; @@ -112,7 +113,7 @@ public class LoopPlugin extends PluginBase { public long lastOpenModeAccept; } - public LastRun lastRun = null; + @Nullable public LastRun lastRun = null; @Inject public LoopPlugin( @@ -381,7 +382,6 @@ public class LoopPlugin extends PluginBase { if (lastRun == null) lastRun = new LastRun(); lastRun.request = result; lastRun.constraintsProcessed = resultAfterConstraints; - lastRun.lastAPSRun = new Date(); lastRun.source = ((PluginBase) usedAPS).getName(); lastRun.tbrSetByPump = null; lastRun.smbSetByPump = null; @@ -423,7 +423,7 @@ public class LoopPlugin extends PluginBase { public void run() { if (result.enacted || result.success) { lastRun.tbrSetByPump = result; - lastRun.lastTBRRequest = lastRun.lastAPSRun.getTime(); + lastRun.lastTBRRequest = lastRun.lastAPSRun; lastRun.lastTBREnact = DateUtil.now(); rxBus.send(new EventLoopUpdateGui()); applySMBRequest(resultAfterConstraints, new Callback() { @@ -432,7 +432,7 @@ public class LoopPlugin extends PluginBase { //Callback is only called if a bolus was acutally requested if (result.enacted || result.success) { lastRun.smbSetByPump = result; - lastRun.lastSMBRequest = lastRun.lastAPSRun.getTime(); + lastRun.lastSMBRequest = lastRun.lastAPSRun; lastRun.lastSMBEnact = DateUtil.now(); } else { new Thread(() -> { @@ -512,7 +512,7 @@ public class LoopPlugin extends PluginBase { public void run() { if (result.enacted) { lastRun.tbrSetByPump = result; - lastRun.lastTBRRequest = lastRun.lastAPSRun.getTime(); + lastRun.lastTBRRequest = lastRun.lastAPSRun; lastRun.lastTBREnact = DateUtil.now(); lastRun.lastOpenModeAccept = DateUtil.now(); NSUpload.uploadDeviceStatus(lp, iobCobCalculatorPlugin, profileFunction, activePlugin.getActivePump()); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/dataBroadcaster/DataBroadcastPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/dataBroadcaster/DataBroadcastPlugin.kt index d2c552c184..3b640a5496 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/dataBroadcaster/DataBroadcastPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/dataBroadcaster/DataBroadcastPlugin.kt @@ -125,7 +125,7 @@ class DataBroadcastPlugin @Inject constructor( private fun bgStatus(bundle: Bundle) { val lastBG: BgReading = iobCobCalculatorPlugin.lastBg() ?: return - val glucoseStatus = GlucoseStatus(injector).getGlucoseStatusData() ?: return + val glucoseStatus = GlucoseStatus(injector).glucoseStatusData ?: return bundle.putDouble("glucoseMgdl", lastBG.value) // last BG in mgdl bundle.putLong("glucoseTimeStamp", lastBG.date) // timestamp @@ -158,10 +158,9 @@ class DataBroadcastPlugin @Inject constructor( bundle.putInt("rigBattery", nsDeviceStatus.uploaderStatus.replace("%", "").trim { it <= ' ' }.toInt()) if (Config.APS && lazyLoopPlugin.get().lastRun?.lastTBREnact != 0L) { //we are AndroidAPS - bundle.putLong("suggestedTimeStamp", lazyLoopPlugin.get().lastRun?.lastAPSRun?.time - ?: -1L) + bundle.putLong("suggestedTimeStamp", lazyLoopPlugin.get().lastRun?.lastAPSRun ?: -1L) bundle.putString("suggested", lazyLoopPlugin.get().lastRun?.request?.json().toString()) - if (lazyLoopPlugin.get().lastRun.tbrSetByPump != null && lazyLoopPlugin.get().lastRun.tbrSetByPump.enacted) { + if (lazyLoopPlugin.get().lastRun?.tbrSetByPump != null && lazyLoopPlugin.get().lastRun?.tbrSetByPump?.enacted == true) { bundle.putLong("enactedTimeStamp", lazyLoopPlugin.get().lastRun?.lastTBREnact ?: -1L) bundle.putString("enacted", lazyLoopPlugin.get().lastRun?.request?.json().toString()) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSUpload.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSUpload.java index c27b3896c9..3894c68ba2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSUpload.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/nsclient/NSUpload.java @@ -173,7 +173,7 @@ public class NSUpload { DeviceStatus deviceStatus = new DeviceStatus(); try { LoopPlugin.LastRun lastRun = loopPlugin.lastRun; - if (lastRun != null && lastRun.lastAPSRun.getTime() > System.currentTimeMillis() - 300 * 1000L) { + if (lastRun != null && lastRun.lastAPSRun > System.currentTimeMillis() - 300 * 1000L) { // do not send if result is older than 1 min APSResult apsResult = lastRun.request; apsResult.json().put("timestamp", DateUtil.toISOString(lastRun.lastAPSRun)); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java deleted file mode 100644 index d7bb8e35a5..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java +++ /dev/null @@ -1,1184 +0,0 @@ -package info.nightscout.androidaps.plugins.general.overview; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.app.NotificationManager; -import android.content.ActivityNotFoundException; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.graphics.Paint; -import android.os.Bundle; -import android.os.Handler; -import android.util.DisplayMetrics; -import android.util.TypedValue; -import android.view.ContextMenu; -import android.view.LayoutInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageButton; -import android.widget.LinearLayout; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.constraintlayout.widget.ConstraintLayout; -import androidx.fragment.app.FragmentActivity; -import androidx.fragment.app.FragmentManager; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import com.jjoe64.graphview.GraphView; - -import org.jetbrains.annotations.NotNull; - -import java.util.Calendar; -import java.util.Date; -import java.util.Locale; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -import dagger.android.HasAndroidInjector; -import dagger.android.support.DaggerFragment; -import info.nightscout.androidaps.Config; -import info.nightscout.androidaps.Constants; -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.IobTotal; -import info.nightscout.androidaps.data.Profile; -import info.nightscout.androidaps.db.BgReading; -import info.nightscout.androidaps.db.ExtendedBolus; -import info.nightscout.androidaps.db.TempTarget; -import info.nightscout.androidaps.db.TemporaryBasal; -import info.nightscout.androidaps.dialogs.CalibrationDialog; -import info.nightscout.androidaps.dialogs.CarbsDialog; -import info.nightscout.androidaps.dialogs.InsulinDialog; -import info.nightscout.androidaps.dialogs.TreatmentDialog; -import info.nightscout.androidaps.dialogs.WizardDialog; -import info.nightscout.androidaps.events.EventAcceptOpenLoopChange; -import info.nightscout.androidaps.events.EventCareportalEventChange; -import info.nightscout.androidaps.events.EventExtendedBolusChange; -import info.nightscout.androidaps.events.EventInitializationChanged; -import info.nightscout.androidaps.events.EventPreferenceChange; -import info.nightscout.androidaps.events.EventProfileNeedsUpdate; -import info.nightscout.androidaps.events.EventPumpStatusChanged; -import info.nightscout.androidaps.events.EventRefreshOverview; -import info.nightscout.androidaps.events.EventTempBasalChange; -import info.nightscout.androidaps.events.EventTempTargetChange; -import info.nightscout.androidaps.events.EventTreatmentChange; -import info.nightscout.androidaps.interfaces.ActivePluginProvider; -import info.nightscout.androidaps.interfaces.Constraint; -import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.interfaces.PumpInterface; -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.aps.loop.APSResult; -import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; -import info.nightscout.androidaps.plugins.aps.loop.events.EventNewOpenLoopNotification; -import info.nightscout.androidaps.plugins.bus.RxBusWrapper; -import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker; -import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction; -import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus; -import info.nightscout.androidaps.plugins.general.overview.activities.QuickWizardListActivity; -import info.nightscout.androidaps.plugins.general.overview.graphData.GraphData; -import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationStore; -import info.nightscout.androidaps.plugins.general.wear.ActionStringHandler; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensData; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.CobInfo; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress; -import info.nightscout.androidaps.plugins.source.DexcomPlugin; -import info.nightscout.androidaps.plugins.source.XdripPlugin; -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; -import info.nightscout.androidaps.queue.CommandQueue; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.DecimalFormatter; -import info.nightscout.androidaps.utils.DefaultValueHelper; -import info.nightscout.androidaps.utils.FabricPrivacy; -import info.nightscout.androidaps.utils.OKDialog; -import info.nightscout.androidaps.utils.Profiler; -import info.nightscout.androidaps.utils.SingleClickButton; -import info.nightscout.androidaps.utils.T; -import info.nightscout.androidaps.utils.ToastUtils; -import info.nightscout.androidaps.utils.buildHelper.BuildHelper; -import info.nightscout.androidaps.utils.protection.ProtectionCheck; -import info.nightscout.androidaps.utils.resources.ResourceHelper; -import info.nightscout.androidaps.utils.sharedPreferences.SP; -import info.nightscout.androidaps.utils.wizard.BolusWizard; -import info.nightscout.androidaps.utils.wizard.QuickWizard; -import info.nightscout.androidaps.utils.wizard.QuickWizardEntry; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.schedulers.Schedulers; - -public class OverviewFragment extends DaggerFragment implements View.OnClickListener, View.OnLongClickListener { - @Inject HasAndroidInjector injector; - @Inject AAPSLogger aapsLogger; - @Inject SP sp; - @Inject RxBusWrapper rxBus; - @Inject MainApp mainApp; - @Inject ResourceHelper resourceHelper; - @Inject DefaultValueHelper defaultValueHelper; - @Inject ProfileFunction profileFunction; - @Inject ConstraintChecker constraintChecker; - @Inject StatusLightHandler statusLightHandler; - @Inject NSDeviceStatus nsDeviceStatus; - @Inject LoopPlugin loopPlugin; - @Inject ConfigBuilderPlugin configBuilderPlugin; - @Inject ActivePluginProvider activePlugin; - @Inject TreatmentsPlugin treatmentsPlugin; - @Inject IobCobCalculatorPlugin iobCobCalculatorPlugin; - @Inject DexcomPlugin dexcomPlugin; - @Inject XdripPlugin xdripPlugin; - @Inject NotificationStore notificationStore; - @Inject ActionStringHandler actionStringHandler; - @Inject QuickWizard quickWizard; - @Inject BuildHelper buildHelper; - @Inject CommandQueue commandQueue; - @Inject ProtectionCheck protectionCheck; - @Inject FabricPrivacy fabricPrivacy; - @Inject OverviewMenus overviewMenus; - - private CompositeDisposable disposable = new CompositeDisposable(); - - TextView timeView; - TextView bgView; - TextView arrowView; - TextView sensitivityView; - TextView timeAgoView; - TextView timeAgoShortView; - TextView deltaView; - TextView deltaShortView; - TextView avgdeltaView; - TextView baseBasalView; - TextView extendedBolusView; - TextView activeProfileView; - TextView iobView; - TextView cobView; - TextView apsModeView; - TextView tempTargetView; - TextView pumpStatusView; - TextView pumpDeviceStatusView; - TextView openapsDeviceStatusView; - TextView uploaderDeviceStatusView; - TextView iobCalculationProgressView; - ConstraintLayout loopStatusLayout; - LinearLayout pumpStatusLayout; - GraphView bgGraph; - GraphView iobGraph; - - TextView iage; - TextView cage; - TextView sage; - TextView pbage; - - TextView iageView; - TextView cageView; - TextView reservoirView; - TextView sageView; - TextView batteryView; - LinearLayout statuslightsLayout; - - RecyclerView notificationsView; - LinearLayoutManager llm; - - SingleClickButton acceptTempButton; - - SingleClickButton treatmentButton; - SingleClickButton wizardButton; - SingleClickButton calibrationButton; - SingleClickButton insulinButton; - SingleClickButton carbsButton; - SingleClickButton cgmButton; - SingleClickButton quickWizardButton; - - boolean smallWidth; - boolean smallHeight; - - public static boolean shorttextmode = false; - - private int rangeToDisplay = 6; // for graph - - Handler sLoopHandler = new Handler(); - Runnable sRefreshLoop = null; - - public enum CHARTTYPE {PRE, BAS, IOB, COB, DEV, SEN, ACTPRIM, ACTSEC, DEVSLOPE} - - private static final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor(); - private static ScheduledFuture scheduledUpdate = null; - - public OverviewFragment() { - super(); - } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - - //check screen width - final DisplayMetrics dm = new DisplayMetrics(); - getActivity().getWindowManager().getDefaultDisplay().getMetrics(dm); - int screen_width = dm.widthPixels; - int screen_height = dm.heightPixels; - smallWidth = screen_width <= Constants.SMALL_WIDTH; - smallHeight = screen_height <= Constants.SMALL_HEIGHT; - boolean landscape = screen_height < screen_width; - - View view; - - if (resourceHelper.gb(R.bool.isTablet) && (Config.NSCLIENT)) { - view = inflater.inflate(R.layout.overview_fragment_nsclient_tablet, container, false); - } else if (Config.NSCLIENT) { - view = inflater.inflate(R.layout.overview_fragment_nsclient, container, false); - shorttextmode = true; - } else if (smallHeight || landscape) { - view = inflater.inflate(R.layout.overview_fragment_landscape, container, false); - } else { - view = inflater.inflate(R.layout.overview_fragment, container, false); - } - - timeView = (TextView) view.findViewById(R.id.overview_time); - bgView = (TextView) view.findViewById(R.id.overview_bg); - arrowView = (TextView) view.findViewById(R.id.overview_arrow); - if (smallWidth) { - arrowView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 35); - } - sensitivityView = (TextView) view.findViewById(R.id.overview_sensitivity); - timeAgoView = (TextView) view.findViewById(R.id.overview_timeago); - timeAgoShortView = (TextView) view.findViewById(R.id.overview_timeagoshort); - deltaView = (TextView) view.findViewById(R.id.overview_delta); - deltaShortView = (TextView) view.findViewById(R.id.overview_deltashort); - avgdeltaView = (TextView) view.findViewById(R.id.overview_avgdelta); - baseBasalView = (TextView) view.findViewById(R.id.overview_basebasal); - extendedBolusView = (TextView) view.findViewById(R.id.overview_extendedbolus); - activeProfileView = (TextView) view.findViewById(R.id.overview_activeprofile); - pumpStatusView = (TextView) view.findViewById(R.id.overview_pumpstatus); - pumpDeviceStatusView = (TextView) view.findViewById(R.id.overview_pump); - openapsDeviceStatusView = (TextView) view.findViewById(R.id.overview_openaps); - uploaderDeviceStatusView = (TextView) view.findViewById(R.id.overview_uploader); - iobCalculationProgressView = (TextView) view.findViewById(R.id.overview_iobcalculationprogess); - loopStatusLayout = view.findViewById(R.id.overview_looplayout); - pumpStatusLayout = (LinearLayout) view.findViewById(R.id.overview_pumpstatuslayout); - - pumpStatusView.setBackgroundColor(resourceHelper.gc(R.color.colorInitializingBorder)); - - iobView = (TextView) view.findViewById(R.id.overview_iob); - cobView = (TextView) view.findViewById(R.id.overview_cob); - apsModeView = (TextView) view.findViewById(R.id.overview_apsmode); - tempTargetView = (TextView) view.findViewById(R.id.overview_temptarget); - - iage = view.findViewById(R.id.careportal_insulinage); - cage = view.findViewById(R.id.careportal_canulaage); - sage = view.findViewById(R.id.careportal_sensorage); - pbage = view.findViewById(R.id.careportal_pbage); - - iageView = view.findViewById(R.id.overview_insulinage); - cageView = view.findViewById(R.id.overview_canulaage); - reservoirView = view.findViewById(R.id.overview_reservoirlevel); - sageView = view.findViewById(R.id.overview_sensorage); - batteryView = view.findViewById(R.id.overview_batterylevel); - statuslightsLayout = view.findViewById(R.id.overview_statuslights); - - bgGraph = (GraphView) view.findViewById(R.id.overview_bggraph); - iobGraph = (GraphView) view.findViewById(R.id.overview_iobgraph); - - treatmentButton = (SingleClickButton) view.findViewById(R.id.overview_treatmentbutton); - treatmentButton.setOnClickListener(this); - wizardButton = (SingleClickButton) view.findViewById(R.id.overview_wizardbutton); - wizardButton.setOnClickListener(this); - insulinButton = (SingleClickButton) view.findViewById(R.id.overview_insulinbutton); - if (insulinButton != null) - insulinButton.setOnClickListener(this); - carbsButton = (SingleClickButton) view.findViewById(R.id.overview_carbsbutton); - if (carbsButton != null) - carbsButton.setOnClickListener(this); - acceptTempButton = (SingleClickButton) view.findViewById(R.id.overview_accepttempbutton); - if (acceptTempButton != null) - acceptTempButton.setOnClickListener(this); - quickWizardButton = (SingleClickButton) view.findViewById(R.id.overview_quickwizardbutton); - quickWizardButton.setOnClickListener(this); - quickWizardButton.setOnLongClickListener(this); - calibrationButton = (SingleClickButton) view.findViewById(R.id.overview_calibrationbutton); - if (calibrationButton != null) - calibrationButton.setOnClickListener(this); - cgmButton = (SingleClickButton) view.findViewById(R.id.overview_cgmbutton); - if (cgmButton != null) - cgmButton.setOnClickListener(this); - - notificationsView = (RecyclerView) view.findViewById(R.id.overview_notifications); - notificationsView.setHasFixedSize(false); - llm = new LinearLayoutManager(view.getContext()); - notificationsView.setLayoutManager(llm); - - int axisWidth = 50; - - if (dm.densityDpi <= 120) - axisWidth = 3; - else if (dm.densityDpi <= 160) - axisWidth = 10; - else if (dm.densityDpi <= 320) - axisWidth = 35; - else if (dm.densityDpi <= 420) - axisWidth = 50; - else if (dm.densityDpi <= 560) - axisWidth = 70; - else - axisWidth = 80; - - bgGraph.getGridLabelRenderer().setGridColor(resourceHelper.gc(R.color.graphgrid)); - bgGraph.getGridLabelRenderer().reloadStyles(); - iobGraph.getGridLabelRenderer().setGridColor(resourceHelper.gc(R.color.graphgrid)); - iobGraph.getGridLabelRenderer().reloadStyles(); - iobGraph.getGridLabelRenderer().setHorizontalLabelsVisible(false); - bgGraph.getGridLabelRenderer().setLabelVerticalWidth(axisWidth); - iobGraph.getGridLabelRenderer().setLabelVerticalWidth(axisWidth); - iobGraph.getGridLabelRenderer().setNumVerticalLabels(3); - - rangeToDisplay = sp.getInt(R.string.key_rangetodisplay, 6); - - bgGraph.setOnLongClickListener(v -> { - rangeToDisplay += 6; - rangeToDisplay = rangeToDisplay > 24 ? 6 : rangeToDisplay; - sp.putInt(R.string.key_rangetodisplay, rangeToDisplay); - updateGUI("rangeChange"); - sp.putBoolean(R.string.key_objectiveusescale, true); - return false; - }); - - overviewMenus.setupChartMenu(view.findViewById(R.id.overview_chartMenuButton)); - - return view; - } - - @Override - public void onPause() { - super.onPause(); - disposable.clear(); - sLoopHandler.removeCallbacksAndMessages(null); - unregisterForContextMenu(apsModeView); - unregisterForContextMenu(activeProfileView); - unregisterForContextMenu(tempTargetView); - } - - @Override - public void onResume() { - super.onResume(); - disposable.add(rxBus - .toObservable(EventRefreshOverview.class) - .observeOn(Schedulers.io()) - .subscribe(eventOpenAPSUpdateGui -> scheduleUpdateGUI(eventOpenAPSUpdateGui.getFrom()), - fabricPrivacy::logException - )); - disposable.add(rxBus - .toObservable(EventExtendedBolusChange.class) - .observeOn(Schedulers.io()) - .subscribe(event -> scheduleUpdateGUI("EventExtendedBolusChange"), - fabricPrivacy::logException - )); - disposable.add(rxBus - .toObservable(EventTempBasalChange.class) - .observeOn(Schedulers.io()) - .subscribe(event -> scheduleUpdateGUI("EventTempBasalChange"), - fabricPrivacy::logException - )); - disposable.add(rxBus - .toObservable(EventTreatmentChange.class) - .observeOn(Schedulers.io()) - .subscribe(event -> scheduleUpdateGUI("EventTreatmentChange"), - fabricPrivacy::logException - )); - disposable.add(rxBus - .toObservable(EventTempTargetChange.class) - .observeOn(Schedulers.io()) - .subscribe(event -> scheduleUpdateGUI("EventTempTargetChange"), - fabricPrivacy::logException - )); - disposable.add(rxBus - .toObservable(EventAcceptOpenLoopChange.class) - .observeOn(Schedulers.io()) - .subscribe(event -> scheduleUpdateGUI("EventAcceptOpenLoopChange"), - fabricPrivacy::logException - )); - disposable.add(rxBus - .toObservable(EventCareportalEventChange.class) - .observeOn(Schedulers.io()) - .subscribe(event -> scheduleUpdateGUI("EventCareportalEventChange"), - fabricPrivacy::logException - )); - disposable.add(rxBus - .toObservable(EventInitializationChanged.class) - .observeOn(Schedulers.io()) - .subscribe(event -> scheduleUpdateGUI("EventInitializationChanged"), - fabricPrivacy::logException - )); - disposable.add(rxBus - .toObservable(EventAutosensCalculationFinished.class) - .observeOn(Schedulers.io()) - .subscribe(event -> scheduleUpdateGUI("EventAutosensCalculationFinished"), - fabricPrivacy::logException - )); - disposable.add(rxBus - .toObservable(EventProfileNeedsUpdate.class) - .observeOn(Schedulers.io()) - .subscribe(event -> scheduleUpdateGUI("EventProfileNeedsUpdate"), - fabricPrivacy::logException - )); - disposable.add(rxBus - .toObservable(EventPreferenceChange.class) - .observeOn(Schedulers.io()) - .subscribe(event -> scheduleUpdateGUI("EventPreferenceChange"), - fabricPrivacy::logException - )); - disposable.add(rxBus - .toObservable(EventNewOpenLoopNotification.class) - .observeOn(Schedulers.io()) - .subscribe(event -> scheduleUpdateGUI("EventNewOpenLoopNotification"), - fabricPrivacy::logException - )); - disposable.add(rxBus - .toObservable(EventPumpStatusChanged.class) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(event -> updatePumpStatus(event), - fabricPrivacy::logException - )); - disposable.add(rxBus - .toObservable(EventIobCalculationProgress.class) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(event -> { - if (iobCalculationProgressView != null) - iobCalculationProgressView.setText(event.getProgress()); - }, - fabricPrivacy::logException - )); - sRefreshLoop = () -> { - scheduleUpdateGUI("refreshLoop"); - sLoopHandler.postDelayed(sRefreshLoop, 60 * 1000L); - }; - sLoopHandler.postDelayed(sRefreshLoop, 60 * 1000L); - registerForContextMenu(apsModeView); - registerForContextMenu(activeProfileView); - registerForContextMenu(tempTargetView); - updateGUI("onResume"); - } - - - @Override - public void onCreateContextMenu(@NotNull ContextMenu menu, @NotNull View v, ContextMenu.ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, v, menuInfo); - overviewMenus.createContextMenu(menu, v); - } - - @Override - public boolean onContextItemSelected(@NonNull MenuItem item) { - FragmentManager manager = getFragmentManager(); - if (manager != null && overviewMenus.onContextItemSelected(item, manager)) return true; - else return super.onContextItemSelected(item); - } - - @Override - public void onClick(View v) { - boolean xdrip = xdripPlugin.isEnabled(PluginType.BGSOURCE); - boolean dexcom = dexcomPlugin.isEnabled(PluginType.BGSOURCE); - - FragmentManager manager = getFragmentManager(); - // try to fix https://fabric.io/nightscout3/android/apps/info.nightscout.androidaps/issues/5aca7a1536c7b23527eb4be7?time=last-seven-days - // https://stackoverflow.com/questions/14860239/checking-if-state-is-saved-before-committing-a-fragmenttransaction - if (manager == null || manager.isStateSaved()) - return; - switch (v.getId()) { - case R.id.overview_accepttempbutton: - onClickAcceptTemp(); - break; - case R.id.overview_quickwizardbutton: - protectionCheck.queryProtection(getActivity(), ProtectionCheck.Protection.BOLUS, this::onClickQuickwizard); - break; - case R.id.overview_wizardbutton: - protectionCheck.queryProtection(getActivity(), ProtectionCheck.Protection.BOLUS, () -> new WizardDialog().show(manager, "WizardDialog")); - break; - case R.id.overview_calibrationbutton: - if (xdrip) { - CalibrationDialog calibrationDialog = new CalibrationDialog(); - calibrationDialog.show(manager, "CalibrationDialog"); - } else if (dexcom) { - try { - String packageName = dexcomPlugin.findDexcomPackageName(); - if (packageName != null) { - Intent i = new Intent("com.dexcom.cgm.activities.MeterEntryActivity"); - i.setPackage(packageName); - startActivity(i); - } else { - ToastUtils.showToastInUiThread(getActivity(), resourceHelper.gs(R.string.dexcom_app_not_installed)); - } - } catch (ActivityNotFoundException e) { - ToastUtils.showToastInUiThread(getActivity(), resourceHelper.gs(R.string.g5appnotdetected)); - } - } - break; - case R.id.overview_cgmbutton: - if (xdrip) - openCgmApp("com.eveningoutpost.dexdrip"); - else if (dexcom) { - String packageName = dexcomPlugin.findDexcomPackageName(); - if (packageName != null) { - openCgmApp(packageName); - } else { - ToastUtils.showToastInUiThread(getActivity(), resourceHelper.gs(R.string.dexcom_app_not_installed)); - } - } - break; - case R.id.overview_treatmentbutton: - protectionCheck.queryProtection(getActivity(), ProtectionCheck.Protection.BOLUS, () -> new TreatmentDialog().show(manager, "Overview")); - break; - case R.id.overview_insulinbutton: - protectionCheck.queryProtection(getActivity(), ProtectionCheck.Protection.BOLUS, () -> new InsulinDialog().show(manager, "Overview")); - break; - case R.id.overview_carbsbutton: - protectionCheck.queryProtection(getActivity(), ProtectionCheck.Protection.BOLUS, () -> new CarbsDialog().show(manager, "Overview")); - break; - case R.id.overview_pumpstatus: - if (activePlugin.getActivePump().isSuspended() || !activePlugin.getActivePump().isInitialized()) - commandQueue.readStatus("RefreshClicked", null); - break; - } - - } - - private void openCgmApp(String packageName) { - PackageManager packageManager = getContext().getPackageManager(); - try { - Intent intent = packageManager.getLaunchIntentForPackage(packageName); - if (intent == null) { - throw new ActivityNotFoundException(); - } - intent.addCategory(Intent.CATEGORY_LAUNCHER); - getContext().startActivity(intent); - } catch (ActivityNotFoundException e) { - OKDialog.show(getContext(), "", resourceHelper.gs(R.string.error_starting_cgm)); - } - } - - @Override - public boolean onLongClick(View v) { - switch (v.getId()) { - case R.id.overview_quickwizardbutton: - Intent i = new Intent(v.getContext(), QuickWizardListActivity.class); - startActivity(i); - return true; - } - return false; - } - - private void onClickAcceptTemp() { - Profile profile = profileFunction.getProfile(); - Context context = getContext(); - - if (context == null) return; - - if (loopPlugin.isEnabled(PluginType.LOOP) && profile != null) { - loopPlugin.invoke("Accept temp button", false); - if (loopPlugin.lastRun != null && loopPlugin.lastRun.lastAPSRun != null && loopPlugin.lastRun.constraintsProcessed.isChangeRequested()) { - OKDialog.showConfirmation(context, resourceHelper.gs(R.string.pump_tempbasal_label), loopPlugin.lastRun.constraintsProcessed.toSpanned(), () -> { - aapsLogger.debug("USER ENTRY: ACCEPT TEMP BASAL"); - hideTempRecommendation(); - clearNotification(); - loopPlugin.acceptChangeRequest(); - }); - } - } - } - - private void onClickQuickwizard() { - final BgReading actualBg = iobCobCalculatorPlugin.actualBg(); - final Profile profile = profileFunction.getProfile(); - final String profileName = profileFunction.getProfileName(); - final PumpInterface pump = activePlugin.getActivePump(); - - final QuickWizardEntry quickWizardEntry = quickWizard.getActive(); - if (quickWizardEntry != null && actualBg != null && profile != null) { - quickWizardButton.setVisibility(View.VISIBLE); - final BolusWizard wizard = quickWizardEntry.doCalc(profile, profileName, actualBg, true); - - if (wizard.getCalculatedTotalInsulin() > 0d && quickWizardEntry.carbs() > 0d) { - Integer carbsAfterConstraints = constraintChecker.applyCarbsConstraints(new Constraint<>(quickWizardEntry.carbs())).value(); - - if (Math.abs(wizard.getInsulinAfterConstraints() - wizard.getCalculatedTotalInsulin()) >= pump.getPumpDescription().pumpType.determineCorrectBolusStepSize(wizard.getInsulinAfterConstraints()) || !carbsAfterConstraints.equals(quickWizardEntry.carbs())) { - OKDialog.show(getContext(), resourceHelper.gs(R.string.treatmentdeliveryerror), resourceHelper.gs(R.string.constraints_violation) + "\n" + resourceHelper.gs(R.string.changeyourinput)); - return; - } - - wizard.confirmAndExecute(getContext()); - } - } - } - - private void hideTempRecommendation() { - Activity activity = getActivity(); - if (activity != null) - activity.runOnUiThread(() -> { - if (acceptTempButton != null) - acceptTempButton.setVisibility(View.GONE); - }); - } - - private void clearNotification() { - NotificationManager notificationManager = - (NotificationManager) mainApp.getSystemService(Context.NOTIFICATION_SERVICE); - notificationManager.cancel(Constants.notificationID); - - actionStringHandler.handleInitiate("cancelChangeRequest"); - } - - private void updatePumpStatus(EventPumpStatusChanged event) { - String status = event.getStatus(resourceHelper); - if (!status.equals("")) { - pumpStatusView.setText(status); - pumpStatusLayout.setVisibility(View.VISIBLE); - loopStatusLayout.setVisibility(View.GONE); - } else { - pumpStatusLayout.setVisibility(View.GONE); - loopStatusLayout.setVisibility(View.VISIBLE); - } - } - - private void processInsulinCarbsButtonsVisibility() { - BgReading lastBG = iobCobCalculatorPlugin.lastBg(); - - final PumpInterface pump = activePlugin.getActivePump(); - - final Profile profile = profileFunction.getProfile(); - final String profileName = profileFunction.getProfileName(); - - // QuickWizard button - QuickWizardEntry quickWizardEntry = quickWizard.getActive(); - if (quickWizardEntry != null && lastBG != null && profile != null && pump.isInitialized() && !pump.isSuspended()) { - quickWizardButton.setVisibility(View.VISIBLE); - String text = quickWizardEntry.buttonText() + "\n" + DecimalFormatter.to0Decimal(quickWizardEntry.carbs()) + "g"; - BolusWizard wizard = quickWizardEntry.doCalc(profile, profileName, lastBG, false); - text += " " + DecimalFormatter.toPumpSupportedBolus(wizard.getCalculatedTotalInsulin(), pump) + "U"; - quickWizardButton.setText(text); - if (wizard.getCalculatedTotalInsulin() <= 0) - quickWizardButton.setVisibility(View.GONE); - } else - quickWizardButton.setVisibility(View.GONE); - - // **** Various treatment buttons **** - if (carbsButton != null) { - if ((!activePlugin.getActivePump().getPumpDescription().storesCarbInfo || (pump.isInitialized() && !pump.isSuspended())) && - profile != null && - sp.getBoolean(R.string.key_show_carbs_button, true)) - carbsButton.setVisibility(View.VISIBLE); - else - carbsButton.setVisibility(View.GONE); - } - - if (treatmentButton != null) { - if (pump.isInitialized() && !pump.isSuspended() && - profile != null && - sp.getBoolean(R.string.key_show_treatment_button, false)) - treatmentButton.setVisibility(View.VISIBLE); - else - treatmentButton.setVisibility(View.GONE); - } - if (wizardButton != null) { - if (pump.isInitialized() && !pump.isSuspended() && - profile != null && - sp.getBoolean(R.string.key_show_wizard_button, true)) - wizardButton.setVisibility(View.VISIBLE); - else - wizardButton.setVisibility(View.GONE); - } - if (insulinButton != null) { - if (pump.isInitialized() && !pump.isSuspended() && - profile != null && - sp.getBoolean(R.string.key_show_insulin_button, true)) - insulinButton.setVisibility(View.VISIBLE); - else - insulinButton.setVisibility(View.GONE); - } - } - - private void scheduleUpdateGUI(final String from) { - class UpdateRunnable implements Runnable { - public void run() { - Activity activity = getActivity(); - if (activity != null) - activity.runOnUiThread(() -> { - updateGUI(from); - scheduledUpdate = null; - }); - } - } - // prepare task for execution in 400 msec - // cancel waiting task to prevent multiple updates - if (scheduledUpdate != null) - scheduledUpdate.cancel(false); - Runnable task = new UpdateRunnable(); - final int msec = 500; - scheduledUpdate = worker.schedule(task, msec, TimeUnit.MILLISECONDS); - } - - @SuppressLint("SetTextI18n") - public void updateGUI(final String from) { - aapsLogger.debug(LTag.OVERVIEW, "updateGUI entered from: " + from); - final long updateGUIStart = System.currentTimeMillis(); - - if (getActivity() == null) - return; - - if (timeView != null) { //must not exists - timeView.setText(DateUtil.timeString(new Date())); - } - - notificationStore.updateNotifications(notificationsView); - - pumpStatusLayout.setVisibility(View.GONE); - loopStatusLayout.setVisibility(View.GONE); - - if (!profileFunction.isProfileValid("Overview")) { - pumpStatusView.setText(R.string.noprofileset); - pumpStatusLayout.setVisibility(View.VISIBLE); - return; - } - loopStatusLayout.setVisibility(View.VISIBLE); - - statusLightHandler.updateAge(sage, iage, cage, pbage); - BgReading actualBG = iobCobCalculatorPlugin.actualBg(); - BgReading lastBG = iobCobCalculatorPlugin.lastBg(); - - final PumpInterface pump = activePlugin.getActivePump(); - - final Profile profile = profileFunction.getProfile(); - if (profile == null) return; - - final String units = profileFunction.getUnits(); - final double lowLine = defaultValueHelper.determineLowLine(); - final double highLine = defaultValueHelper.determineHighLine(); - - //Start with updating the BG as it is unaffected by loop. - // **** BG value **** - if (lastBG != null) { - int color = resourceHelper.gc(R.color.inrange); - if (lastBG.valueToUnits(units) < lowLine) - color = resourceHelper.gc(R.color.low); - else if (lastBG.valueToUnits(units) > highLine) - color = resourceHelper.gc(R.color.high); - bgView.setText(lastBG.valueToUnitsToString(units)); - arrowView.setText(lastBG.directionToSymbol()); - bgView.setTextColor(color); - arrowView.setTextColor(color); - GlucoseStatus glucoseStatus = new GlucoseStatus(injector).getGlucoseStatusData(); - if (glucoseStatus != null) { - if (deltaView != null) - deltaView.setText("Δ " + Profile.toUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units) + " " + units); - if (deltaShortView != null) - deltaShortView.setText(Profile.toSignedUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units)); - if (avgdeltaView != null) - avgdeltaView.setText("øΔ15m: " + Profile.toUnitsString(glucoseStatus.short_avgdelta, glucoseStatus.short_avgdelta * Constants.MGDL_TO_MMOLL, units) + "\n" + - "øΔ40m: " + Profile.toUnitsString(glucoseStatus.long_avgdelta, glucoseStatus.long_avgdelta * Constants.MGDL_TO_MMOLL, units)); - } else { - if (deltaView != null) - deltaView.setText("Δ " + resourceHelper.gs(R.string.notavailable)); - if (deltaShortView != null) - deltaShortView.setText("---"); - if (avgdeltaView != null) - avgdeltaView.setText(""); - } - } - - Constraint closedLoopEnabled = constraintChecker.isClosedLoopAllowed(); - - // open loop mode - if (Config.APS && pump.getPumpDescription().isTempBasalCapable) { - apsModeView.setVisibility(View.VISIBLE); - apsModeView.setBackgroundColor(resourceHelper.gc(R.color.ribbonDefault)); - apsModeView.setTextColor(resourceHelper.gc(R.color.ribbonTextDefault)); - if (loopPlugin.isEnabled(PluginType.LOOP) && loopPlugin.isSuperBolus()) { - apsModeView.setBackgroundColor(resourceHelper.gc(R.color.ribbonWarning)); - apsModeView.setText(String.format(resourceHelper.gs(R.string.loopsuperbolusfor), loopPlugin.minutesToEndOfSuspend())); - apsModeView.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning)); - } else if (loopPlugin.isDisconnected()) { - apsModeView.setBackgroundColor(resourceHelper.gc(R.color.ribbonCritical)); - apsModeView.setText(String.format(resourceHelper.gs(R.string.loopdisconnectedfor), loopPlugin.minutesToEndOfSuspend())); - apsModeView.setTextColor(resourceHelper.gc(R.color.ribbonTextCritical)); - } else if (loopPlugin.isEnabled(PluginType.LOOP) && loopPlugin.isSuspended()) { - apsModeView.setBackgroundColor(resourceHelper.gc(R.color.ribbonWarning)); - apsModeView.setText(String.format(resourceHelper.gs(R.string.loopsuspendedfor), loopPlugin.minutesToEndOfSuspend())); - apsModeView.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning)); - } else if (pump.isSuspended()) { - apsModeView.setBackgroundColor(resourceHelper.gc(R.color.ribbonWarning)); - apsModeView.setText(resourceHelper.gs(R.string.pumpsuspended)); - apsModeView.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning)); - } else if (loopPlugin.isEnabled(PluginType.LOOP)) { - if (closedLoopEnabled.value()) { - apsModeView.setText(resourceHelper.gs(R.string.closedloop)); - } else { - apsModeView.setText(resourceHelper.gs(R.string.openloop)); - } - } else { - apsModeView.setBackgroundColor(resourceHelper.gc(R.color.ribbonCritical)); - apsModeView.setText(resourceHelper.gs(R.string.disabledloop)); - apsModeView.setTextColor(resourceHelper.gc(R.color.ribbonTextCritical)); - } - } else { - apsModeView.setVisibility(View.GONE); - } - - // temp target - TempTarget tempTarget = treatmentsPlugin.getTempTargetFromHistory(); - if (tempTarget != null) { - tempTargetView.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning)); - tempTargetView.setBackgroundColor(resourceHelper.gc(R.color.ribbonWarning)); - tempTargetView.setText(Profile.toTargetRangeString(tempTarget.low, tempTarget.high, Constants.MGDL, units) + " " + DateUtil.untilString(tempTarget.end(), resourceHelper)); - } else { - tempTargetView.setTextColor(resourceHelper.gc(R.color.ribbonTextDefault)); - tempTargetView.setBackgroundColor(resourceHelper.gc(R.color.ribbonDefault)); - tempTargetView.setText(Profile.toTargetRangeString(profile.getTargetLowMgdl(), profile.getTargetHighMgdl(), Constants.MGDL, units)); - } - - // **** Temp button **** - if (acceptTempButton != null) { - boolean showAcceptButton = !closedLoopEnabled.value(); // Open mode needed - showAcceptButton = showAcceptButton && loopPlugin.lastRun != null && loopPlugin.lastRun.lastAPSRun != null; // aps result must exist - showAcceptButton = showAcceptButton && (loopPlugin.lastRun.lastOpenModeAccept == 0 || loopPlugin.lastRun.lastOpenModeAccept < loopPlugin.lastRun.lastAPSRun.getTime()); // never accepted or before last result - showAcceptButton = showAcceptButton && loopPlugin.lastRun.constraintsProcessed.isChangeRequested(); // change is requested - - if (showAcceptButton && pump.isInitialized() && !pump.isSuspended() && loopPlugin.isEnabled(PluginType.LOOP)) { - acceptTempButton.setVisibility(View.VISIBLE); - acceptTempButton.setText(resourceHelper.gs(R.string.setbasalquestion) + "\n" + loopPlugin.lastRun.constraintsProcessed); - } else { - acceptTempButton.setVisibility(View.GONE); - } - } - - // **** Calibration & CGM buttons **** - boolean xDripIsBgSource = xdripPlugin.isEnabled(PluginType.BGSOURCE); - boolean dexcomIsSource = dexcomPlugin.isEnabled(PluginType.BGSOURCE); - if (calibrationButton != null) { - if ((xDripIsBgSource || dexcomIsSource) && actualBG != null && sp.getBoolean(R.string.key_show_calibration_button, true)) { - calibrationButton.setVisibility(View.VISIBLE); - } else { - calibrationButton.setVisibility(View.GONE); - } - } - if (cgmButton != null) { - if (xDripIsBgSource && sp.getBoolean(R.string.key_show_cgm_button, false)) { - cgmButton.setVisibility(View.VISIBLE); - } else if (dexcomIsSource && sp.getBoolean(R.string.key_show_cgm_button, false)) { - cgmButton.setVisibility(View.VISIBLE); - } else { - cgmButton.setVisibility(View.GONE); - } - } - - final TemporaryBasal activeTemp = treatmentsPlugin.getTempBasalFromHistory(System.currentTimeMillis()); - String basalText = ""; - if (shorttextmode) { - if (activeTemp != null) { - basalText = "T: " + activeTemp.toStringVeryShort(); - } else { - basalText = resourceHelper.gs(R.string.pump_basebasalrate, profile.getBasal()); - } - } else { - if (activeTemp != null) { - basalText = activeTemp.toStringFull(); - } else { - basalText = resourceHelper.gs(R.string.pump_basebasalrate, profile.getBasal()); - } - } - baseBasalView.setText(basalText); - baseBasalView.setOnClickListener(v -> { - String fullText = resourceHelper.gs(R.string.pump_basebasalrate_label) + ": " + resourceHelper.gs(R.string.pump_basebasalrate, profile.getBasal()) + "\n"; - if (activeTemp != null) { - fullText += resourceHelper.gs(R.string.pump_tempbasal_label) + ": " + activeTemp.toStringFull(); - } - OKDialog.show(getActivity(), resourceHelper.gs(R.string.basal), fullText); - }); - - if (activeTemp != null) { - baseBasalView.setTextColor(resourceHelper.gc(R.color.basal)); - } else { - baseBasalView.setTextColor(resourceHelper.gc(R.color.defaulttextcolor)); - } - - - final ExtendedBolus extendedBolus = treatmentsPlugin.getExtendedBolusFromHistory(System.currentTimeMillis()); - String extendedBolusText = ""; - if (extendedBolusView != null) { // must not exists in all layouts - if (extendedBolus != null && !pump.isFakingTempsByExtendedBoluses()) - extendedBolusText = shorttextmode ? DecimalFormatter.to2Decimal(extendedBolus.absoluteRate()) + "U/h" : extendedBolus.toStringMedium(); - extendedBolusView.setText(extendedBolusText); - extendedBolusView.setOnClickListener(v -> { - if (extendedBolus != null) - OKDialog.show(getActivity(), resourceHelper.gs(R.string.extended_bolus), extendedBolus.toString()); - }); - } - - activeProfileView.setText(profileFunction.getProfileNameWithDuration()); - if (profile.getPercentage() != 100 || profile.getTimeshift() != 0) { - activeProfileView.setBackgroundColor(resourceHelper.gc(R.color.ribbonWarning)); - activeProfileView.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning)); - } else { - activeProfileView.setBackgroundColor(resourceHelper.gc(R.color.ribbonDefault)); - activeProfileView.setTextColor(resourceHelper.gc(R.color.ribbonTextDefault)); - } - - processInsulinCarbsButtonsVisibility(); - - // **** BG value **** - if (lastBG == null) { //left this here as it seems you want to exit at this point if it is null... - return; - } - int flag = bgView.getPaintFlags(); - if (actualBG == null) { - flag |= Paint.STRIKE_THRU_TEXT_FLAG; - } else - flag &= ~Paint.STRIKE_THRU_TEXT_FLAG; - bgView.setPaintFlags(flag); - - if (timeAgoView != null) - timeAgoView.setText(DateUtil.minAgo(resourceHelper, lastBG.date)); - if (timeAgoShortView != null) - timeAgoShortView.setText("(" + DateUtil.minAgoShort(lastBG.date) + ")"); - - // iob - treatmentsPlugin.updateTotalIOBTreatments(); - treatmentsPlugin.updateTotalIOBTempBasals(); - final IobTotal bolusIob = treatmentsPlugin.getLastCalculationTreatments().round(); - final IobTotal basalIob = treatmentsPlugin.getLastCalculationTempBasals().round(); - - if (shorttextmode) { - String iobtext = DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob) + "U"; - iobView.setText(iobtext); - iobView.setOnClickListener(v -> { - String iobtext1 = DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob) + "U\n" - + resourceHelper.gs(R.string.bolus) + ": " + DecimalFormatter.to2Decimal(bolusIob.iob) + "U\n" - + resourceHelper.gs(R.string.basal) + ": " + DecimalFormatter.to2Decimal(basalIob.basaliob) + "U\n"; - OKDialog.show(getActivity(), resourceHelper.gs(R.string.iob), iobtext1); - }); - } else if (resourceHelper.gb(R.bool.isTablet)) { - String iobtext = DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob) + "U (" - + resourceHelper.gs(R.string.bolus) + ": " + DecimalFormatter.to2Decimal(bolusIob.iob) + "U " - + resourceHelper.gs(R.string.basal) + ": " + DecimalFormatter.to2Decimal(basalIob.basaliob) + "U)"; - iobView.setText(iobtext); - } else { - String iobtext = DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob) + "U (" - + DecimalFormatter.to2Decimal(bolusIob.iob) + "/" - + DecimalFormatter.to2Decimal(basalIob.basaliob) + ")"; - iobView.setText(iobtext); - } - - // cob - if (cobView != null) { // view must not exists - String cobText = resourceHelper.gs(R.string.value_unavailable_short); - CobInfo cobInfo = iobCobCalculatorPlugin.getCobInfo(false, "Overview COB"); - if (cobInfo.displayCob != null) { - cobText = DecimalFormatter.to0Decimal(cobInfo.displayCob); - if (cobInfo.futureCarbs > 0) - cobText += "(" + DecimalFormatter.to0Decimal(cobInfo.futureCarbs) + ")"; - } - cobView.setText(cobText); - } - - if (statuslightsLayout != null) - if (sp.getBoolean(R.string.key_show_statuslights, false)) { - if (sp.getBoolean(R.string.key_show_statuslights_extended, false)) { - statusLightHandler.extendedStatusLight(cageView, iageView, reservoirView, sageView, batteryView); - statuslightsLayout.setVisibility(View.VISIBLE); - } else { - statusLightHandler.statusLight(cageView, iageView, reservoirView, sageView, batteryView); - statuslightsLayout.setVisibility(View.VISIBLE); - } - } else { - statuslightsLayout.setVisibility(View.GONE); - } - - boolean predictionsAvailable; - if (Config.APS) - predictionsAvailable = loopPlugin.lastRun != null && loopPlugin.lastRun.request.hasPredictions; - else if (Config.NSCLIENT) - predictionsAvailable = true; - else - predictionsAvailable = false; - final boolean finalPredictionsAvailable = predictionsAvailable; - - // pump status from ns - if (pumpDeviceStatusView != null) { - pumpDeviceStatusView.setText(nsDeviceStatus.getPumpStatus()); - pumpDeviceStatusView.setOnClickListener(v -> OKDialog.show(getActivity(), resourceHelper.gs(R.string.pump), nsDeviceStatus.getExtendedPumpStatus())); - } - - // OpenAPS status from ns - if (openapsDeviceStatusView != null) { - openapsDeviceStatusView.setText(nsDeviceStatus.getOpenApsStatus()); - openapsDeviceStatusView.setOnClickListener(v -> OKDialog.show(getActivity(), resourceHelper.gs(R.string.openaps), nsDeviceStatus.getExtendedOpenApsStatus())); - } - - // Uploader status from ns - if (uploaderDeviceStatusView != null) { - uploaderDeviceStatusView.setText(nsDeviceStatus.getUploaderStatusSpanned()); - uploaderDeviceStatusView.setOnClickListener(v -> OKDialog.show(getActivity(), resourceHelper.gs(R.string.uploader), nsDeviceStatus.getExtendedUploaderStatus())); - } - - // Sensitivity - if (sensitivityView != null) { - AutosensData autosensData = iobCobCalculatorPlugin.getLastAutosensData("Overview"); - if (autosensData != null) - sensitivityView.setText(String.format(Locale.ENGLISH, "%.0f%%", autosensData.autosensResult.ratio * 100)); - else - sensitivityView.setText(""); - } - - // ****** GRAPH ******* - - new Thread(() -> { - // allign to hours - Calendar calendar = Calendar.getInstance(); - calendar.setTimeInMillis(System.currentTimeMillis()); - calendar.set(Calendar.MILLISECOND, 0); - calendar.set(Calendar.SECOND, 0); - calendar.set(Calendar.MINUTE, 0); - calendar.add(Calendar.HOUR, 1); - - int hoursToFetch; - final long toTime; - final long fromTime; - final long endTime; - - APSResult apsResult = null; - - if (finalPredictionsAvailable && sp.getBoolean("showprediction", false)) { - if (Config.APS) - apsResult = loopPlugin.lastRun.constraintsProcessed; - else - apsResult = NSDeviceStatus.getAPSResult(injector); - int predHours = (int) (Math.ceil(apsResult.getLatestPredictionsTime() - System.currentTimeMillis()) / (60 * 60 * 1000)); - predHours = Math.min(2, predHours); - predHours = Math.max(0, predHours); - hoursToFetch = rangeToDisplay - predHours; - toTime = calendar.getTimeInMillis() + 100000; // little bit more to avoid wrong rounding - Graphview specific - fromTime = toTime - T.hours(hoursToFetch).msecs(); - endTime = toTime + T.hours(predHours).msecs(); - } else { - hoursToFetch = rangeToDisplay; - toTime = calendar.getTimeInMillis() + 100000; // little bit more to avoid wrong rounding - Graphview specific - fromTime = toTime - T.hours(hoursToFetch).msecs(); - endTime = toTime; - } - - - final long now = System.currentTimeMillis(); - - // ------------------ 1st graph - Profiler.log(aapsLogger, LTag.OVERVIEW, from + " - 1st graph - START", updateGUIStart); - - final GraphData graphData = new GraphData(injector, bgGraph, iobCobCalculatorPlugin); - - // **** In range Area **** - graphData.addInRangeArea(fromTime, endTime, lowLine, highLine); - - // **** BG **** - if (finalPredictionsAvailable && sp.getBoolean("showprediction", false)) - graphData.addBgReadings(fromTime, toTime, lowLine, highLine, - apsResult.getPredictions()); - else - graphData.addBgReadings(fromTime, toTime, lowLine, highLine, null); - - // set manual x bounds to have nice steps - graphData.formatAxis(fromTime, endTime); - - // Treatments - graphData.addTreatments(fromTime, endTime); - - if (sp.getBoolean("showactivityprimary", true)) { - graphData.addActivity(fromTime, endTime, false, 0.8d); - } - - // add basal data - if (pump.getPumpDescription().isTempBasalCapable && sp.getBoolean("showbasals", true)) { - graphData.addBasals(fromTime, now, lowLine / graphData.getMaxY() / 1.2d); - } - - // add target line - graphData.addTargetLine(fromTime, toTime, profile, loopPlugin.lastRun); - - // **** NOW line **** - graphData.addNowLine(now); - - // ------------------ 2nd graph - Profiler.log(aapsLogger, LTag.OVERVIEW, from + " - 2nd graph - START", updateGUIStart); - - final GraphData secondGraphData = new GraphData(injector, iobGraph, iobCobCalculatorPlugin); - - boolean useIobForScale = false; - boolean useCobForScale = false; - boolean useDevForScale = false; - boolean useRatioForScale = false; - boolean useDSForScale = false; - boolean useIAForScale = false; - - if (sp.getBoolean("showiob", true)) { - useIobForScale = true; - } else if (sp.getBoolean("showcob", true)) { - useCobForScale = true; - } else if (sp.getBoolean("showdeviations", false)) { - useDevForScale = true; - } else if (sp.getBoolean("showratios", false)) { - useRatioForScale = true; - } else if (sp.getBoolean("showactivitysecondary", false)) { - useIAForScale = true; - } else if (sp.getBoolean("showdevslope", false)) { - useDSForScale = true; - } - - if (sp.getBoolean("showiob", true)) - secondGraphData.addIob(fromTime, now, useIobForScale, 1d, sp.getBoolean("showprediction", false)); - if (sp.getBoolean("showcob", true)) - secondGraphData.addCob(fromTime, now, useCobForScale, useCobForScale ? 1d : 0.5d); - if (sp.getBoolean("showdeviations", false)) - secondGraphData.addDeviations(fromTime, now, useDevForScale, 1d); - if (sp.getBoolean("showratios", false)) - secondGraphData.addRatio(fromTime, now, useRatioForScale, 1d); - if (sp.getBoolean("showactivitysecondary", true)) - secondGraphData.addActivity(fromTime, endTime, useIAForScale, 0.8d); - if (sp.getBoolean("showdevslope", false) && buildHelper.isDev()) - secondGraphData.addDeviationSlope(fromTime, now, useDSForScale, 1d); - - // **** NOW line **** - // set manual x bounds to have nice steps - secondGraphData.formatAxis(fromTime, endTime); - secondGraphData.addNowLine(now); - - // do GUI update - FragmentActivity activity = getActivity(); - if (activity != null) { - activity.runOnUiThread(() -> { - if (sp.getBoolean("showiob", true) - || sp.getBoolean("showcob", true) - || sp.getBoolean("showdeviations", false) - || sp.getBoolean("showratios", false) - || sp.getBoolean("showactivitysecondary", false) - || sp.getBoolean("showdevslope", false)) { - iobGraph.setVisibility(View.VISIBLE); - } else { - iobGraph.setVisibility(View.GONE); - } - // finally enforce drawing of graphs - graphData.performUpdate(); - secondGraphData.performUpdate(); - Profiler.log(aapsLogger, LTag.OVERVIEW, from + " - onDataChanged", updateGUIStart); - }); - } - }).start(); - - Profiler.log(aapsLogger, LTag.OVERVIEW, from, updateGUIStart); - } - - -} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt new file mode 100644 index 0000000000..8a0a6ee04b --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt @@ -0,0 +1,825 @@ +package info.nightscout.androidaps.plugins.general.overview + +import android.annotation.SuppressLint +import android.app.NotificationManager +import android.content.ActivityNotFoundException +import android.content.Context +import android.content.Intent +import android.graphics.Paint +import android.os.Bundle +import android.os.Handler +import android.util.DisplayMetrics +import android.util.TypedValue +import android.view.ContextMenu +import android.view.ContextMenu.ContextMenuInfo +import android.view.LayoutInflater +import android.view.MenuItem +import android.view.View +import android.view.View.OnLongClickListener +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import dagger.android.HasAndroidInjector +import dagger.android.support.DaggerFragment +import info.nightscout.androidaps.Config +import info.nightscout.androidaps.Constants +import info.nightscout.androidaps.R +import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.dialogs.CalibrationDialog +import info.nightscout.androidaps.dialogs.CarbsDialog +import info.nightscout.androidaps.dialogs.InsulinDialog +import info.nightscout.androidaps.dialogs.TreatmentDialog +import info.nightscout.androidaps.dialogs.WizardDialog +import info.nightscout.androidaps.events.* +import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.Constraint +import info.nightscout.androidaps.interfaces.PluginType +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin +import info.nightscout.androidaps.plugins.aps.loop.events.EventNewOpenLoopNotification +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin +import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker +import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction +import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus +import info.nightscout.androidaps.plugins.general.overview.activities.QuickWizardListActivity +import info.nightscout.androidaps.plugins.general.overview.graphData.GraphData +import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationStore +import info.nightscout.androidaps.plugins.general.wear.ActionStringHandler +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress +import info.nightscout.androidaps.plugins.source.DexcomPlugin +import info.nightscout.androidaps.plugins.source.XdripPlugin +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin +import info.nightscout.androidaps.queue.CommandQueue +import info.nightscout.androidaps.utils.* +import info.nightscout.androidaps.utils.buildHelper.BuildHelper +import info.nightscout.androidaps.utils.protection.ProtectionCheck +import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.androidaps.utils.sharedPreferences.SP +import info.nightscout.androidaps.utils.wizard.QuickWizard +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.schedulers.Schedulers +import kotlinx.android.synthetic.main.careportal_stats_fragment.* +import kotlinx.android.synthetic.main.overview_fragment.* +import kotlinx.android.synthetic.main.overview_fragment.overview_activeprofile +import kotlinx.android.synthetic.main.overview_fragment.overview_apsmode +import kotlinx.android.synthetic.main.overview_fragment.overview_arrow +import kotlinx.android.synthetic.main.overview_fragment.overview_basebasal +import kotlinx.android.synthetic.main.overview_fragment.overview_bg +import kotlinx.android.synthetic.main.overview_fragment.overview_bggraph +import kotlinx.android.synthetic.main.overview_fragment.overview_carbsbutton +import kotlinx.android.synthetic.main.overview_fragment.overview_chartMenuButton +import kotlinx.android.synthetic.main.overview_fragment.overview_cob +import kotlinx.android.synthetic.main.overview_fragment.overview_extendedbolus +import kotlinx.android.synthetic.main.overview_fragment.overview_insulinbutton +import kotlinx.android.synthetic.main.overview_fragment.overview_iob +import kotlinx.android.synthetic.main.overview_fragment.overview_iobcalculationprogess +import kotlinx.android.synthetic.main.overview_fragment.overview_iobgraph +import kotlinx.android.synthetic.main.overview_fragment.overview_looplayout +import kotlinx.android.synthetic.main.overview_fragment.overview_notifications +import kotlinx.android.synthetic.main.overview_fragment.overview_pumpstatus +import kotlinx.android.synthetic.main.overview_fragment.overview_pumpstatuslayout +import kotlinx.android.synthetic.main.overview_fragment.overview_quickwizardbutton +import kotlinx.android.synthetic.main.overview_fragment.overview_sensitivity +import kotlinx.android.synthetic.main.overview_fragment.overview_temptarget +import kotlinx.android.synthetic.main.overview_fragment.overview_treatmentbutton +import kotlinx.android.synthetic.main.overview_fragment.overview_wizardbutton +import kotlinx.android.synthetic.main.overview_fragment_nsclient_tablet.* +import java.util.* +import java.util.concurrent.Executors +import java.util.concurrent.ScheduledFuture +import java.util.concurrent.TimeUnit +import javax.inject.Inject +import kotlin.math.abs +import kotlin.math.ceil +import kotlin.math.max +import kotlin.math.min + +class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickListener { + @Inject lateinit var injector: HasAndroidInjector + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var sp: SP + @Inject lateinit var rxBus: RxBusWrapper + @Inject lateinit var resourceHelper: ResourceHelper + @Inject lateinit var defaultValueHelper: DefaultValueHelper + @Inject lateinit var profileFunction: ProfileFunction + @Inject lateinit var constraintChecker: ConstraintChecker + @Inject lateinit var statusLightHandler: StatusLightHandler + @Inject lateinit var nsDeviceStatus: NSDeviceStatus + @Inject lateinit var loopPlugin: LoopPlugin + @Inject lateinit var configBuilderPlugin: ConfigBuilderPlugin + @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var treatmentsPlugin: TreatmentsPlugin + @Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin + @Inject lateinit var dexcomPlugin: DexcomPlugin + @Inject lateinit var xdripPlugin: XdripPlugin + @Inject lateinit var notificationStore: NotificationStore + @Inject lateinit var actionStringHandler: ActionStringHandler + @Inject lateinit var quickWizard: QuickWizard + @Inject lateinit var buildHelper: BuildHelper + @Inject lateinit var commandQueue: CommandQueue + @Inject lateinit var protectionCheck: ProtectionCheck + @Inject lateinit var fabricPrivacy: FabricPrivacy + @Inject lateinit var overviewMenus: OverviewMenus + + private val disposable = CompositeDisposable() + + private var smallWidth = false + private var smallHeight = false + private lateinit var dm: DisplayMetrics + private var rangeToDisplay = 6 // for graph + private var loopHandler = Handler() + private var refreshLoop: Runnable? = null + + private val worker = Executors.newSingleThreadScheduledExecutor() + private var scheduledUpdate: ScheduledFuture<*>? = null + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + + //check screen width + dm = DisplayMetrics() + activity?.windowManager?.defaultDisplay?.getMetrics(dm) + + val screenWidth = dm.widthPixels + val screenHeight = dm.heightPixels + smallWidth = screenWidth <= Constants.SMALL_WIDTH + smallHeight = screenHeight <= Constants.SMALL_HEIGHT + val landscape = screenHeight < screenWidth + + return when { + resourceHelper.gb(R.bool.isTablet) && Config.NSCLIENT -> + inflater.inflate(R.layout.overview_fragment_nsclient_tablet, container, false) + + Config.NSCLIENT -> + inflater.inflate(R.layout.overview_fragment_nsclient, container, false) + + smallHeight || landscape -> + inflater.inflate(R.layout.overview_fragment_landscape, container, false) + + else -> + inflater.inflate(R.layout.overview_fragment, container, false) + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + if (smallWidth) overview_arrow?.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 35f) + overview_pumpstatus?.setBackgroundColor(resourceHelper.gc(R.color.colorInitializingBorder)) + + overview_notifications?.setHasFixedSize(false) + overview_notifications?.layoutManager = LinearLayoutManager(view.context) + val axisWidth = if (dm.densityDpi <= 120) 3 else if (dm.densityDpi <= 160) 10 else if (dm.densityDpi <= 320) 35 else if (dm.densityDpi <= 420) 50 else if (dm.densityDpi <= 560) 70 else 80 + overview_bggraph?.gridLabelRenderer?.gridColor = resourceHelper.gc(R.color.graphgrid) + overview_bggraph?.gridLabelRenderer?.reloadStyles() + overview_iobgraph?.gridLabelRenderer?.gridColor = resourceHelper.gc(R.color.graphgrid) + overview_iobgraph?.gridLabelRenderer?.reloadStyles() + overview_iobgraph?.gridLabelRenderer?.isHorizontalLabelsVisible = false + overview_bggraph?.gridLabelRenderer?.labelVerticalWidth = axisWidth + overview_iobgraph?.gridLabelRenderer?.labelVerticalWidth = axisWidth + overview_iobgraph?.gridLabelRenderer?.numVerticalLabels = 3 + rangeToDisplay = sp.getInt(R.string.key_rangetodisplay, 6) + + overview_bggraph?.setOnLongClickListener { + rangeToDisplay += 6 + rangeToDisplay = if (rangeToDisplay > 24) 6 else rangeToDisplay + sp.putInt(R.string.key_rangetodisplay, rangeToDisplay) + updateGUI("rangeChange") + sp.putBoolean(R.string.key_objectiveusescale, true) + false + } + overviewMenus.setupChartMenu(overview_chartMenuButton) + + overview_accepttempbutton?.setOnClickListener(this) + overview_treatmentbutton?.setOnClickListener(this) + overview_wizardbutton?.setOnClickListener(this) + overview_calibrationbutton?.setOnClickListener(this) + overview_cgmbutton?.setOnClickListener(this) + overview_insulinbutton?.setOnClickListener(this) + overview_carbsbutton?.setOnClickListener(this) + overview_quickwizardbutton?.setOnClickListener(this) + overview_quickwizardbutton?.setOnLongClickListener(this) + } + + override fun onPause() { + super.onPause() + disposable.clear() + loopHandler.removeCallbacksAndMessages(null) + overview_apsmode?.let { unregisterForContextMenu(it) } + overview_activeprofile?.let { unregisterForContextMenu(it) } + overview_temptarget?.let { unregisterForContextMenu(it) } + } + + override fun onResume() { + super.onResume() + disposable.add(rxBus + .toObservable(EventRefreshOverview::class.java) + .observeOn(Schedulers.io()) + .subscribe({ scheduleUpdateGUI(it.from) }) { fabricPrivacy.logException(it) }) + disposable.add(rxBus + .toObservable(EventExtendedBolusChange::class.java) + .observeOn(Schedulers.io()) + .subscribe({ scheduleUpdateGUI("EventExtendedBolusChange") }) { fabricPrivacy.logException(it) }) + disposable.add(rxBus + .toObservable(EventTempBasalChange::class.java) + .observeOn(Schedulers.io()) + .subscribe({ scheduleUpdateGUI("EventTempBasalChange") }) { fabricPrivacy.logException(it) }) + disposable.add(rxBus + .toObservable(EventTreatmentChange::class.java) + .observeOn(Schedulers.io()) + .subscribe({ scheduleUpdateGUI("EventTreatmentChange") }) { fabricPrivacy.logException(it) }) + disposable.add(rxBus + .toObservable(EventTempTargetChange::class.java) + .observeOn(Schedulers.io()) + .subscribe({ scheduleUpdateGUI("EventTempTargetChange") }) { fabricPrivacy.logException(it) }) + disposable.add(rxBus + .toObservable(EventAcceptOpenLoopChange::class.java) + .observeOn(Schedulers.io()) + .subscribe({ scheduleUpdateGUI("EventAcceptOpenLoopChange") }) { fabricPrivacy.logException(it) }) + disposable.add(rxBus + .toObservable(EventCareportalEventChange::class.java) + .observeOn(Schedulers.io()) + .subscribe({ scheduleUpdateGUI("EventCareportalEventChange") }) { fabricPrivacy.logException(it) }) + disposable.add(rxBus + .toObservable(EventInitializationChanged::class.java) + .observeOn(Schedulers.io()) + .subscribe({ scheduleUpdateGUI("EventInitializationChanged") }) { fabricPrivacy.logException(it) }) + disposable.add(rxBus + .toObservable(EventAutosensCalculationFinished::class.java) + .observeOn(Schedulers.io()) + .subscribe({ scheduleUpdateGUI("EventAutosensCalculationFinished") }) { fabricPrivacy.logException(it) }) + disposable.add(rxBus + .toObservable(EventProfileNeedsUpdate::class.java) + .observeOn(Schedulers.io()) + .subscribe({ scheduleUpdateGUI("EventProfileNeedsUpdate") }) { fabricPrivacy.logException(it) }) + disposable.add(rxBus + .toObservable(EventPreferenceChange::class.java) + .observeOn(Schedulers.io()) + .subscribe({ scheduleUpdateGUI("EventPreferenceChange") }) { fabricPrivacy.logException(it) }) + disposable.add(rxBus + .toObservable(EventNewOpenLoopNotification::class.java) + .observeOn(Schedulers.io()) + .subscribe({ scheduleUpdateGUI("EventNewOpenLoopNotification") }) { fabricPrivacy.logException(it) }) + disposable.add(rxBus + .toObservable(EventPumpStatusChanged::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ updatePumpStatus(it) }) { fabricPrivacy.logException(it) }) + disposable.add(rxBus + .toObservable(EventIobCalculationProgress::class.java) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ overview_iobcalculationprogess?.text = it.progress }) { fabricPrivacy.logException(it) }) + + refreshLoop = Runnable { + scheduleUpdateGUI("refreshLoop") + loopHandler.postDelayed(refreshLoop, 60 * 1000L) + } + loopHandler.postDelayed(refreshLoop, 60 * 1000L) + + overview_apsmode?.let { registerForContextMenu(overview_apsmode) } + overview_activeprofile?.let { registerForContextMenu(it) } + overview_temptarget?.let { registerForContextMenu(it) } + updateGUI("onResume") + } + + override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenuInfo?) { + super.onCreateContextMenu(menu, v, menuInfo) + overviewMenus.createContextMenu(menu, v) + } + + override fun onContextItemSelected(item: MenuItem): Boolean { + val manager = fragmentManager + return if (manager != null && overviewMenus.onContextItemSelected(item, manager)) true else super.onContextItemSelected(item) + } + + override fun onClick(v: View) { + val manager = fragmentManager ?: return + // try to fix https://fabric.io/nightscout3/android/apps/info.nightscout.androidaps/issues/5aca7a1536c7b23527eb4be7?time=last-seven-days + // https://stackoverflow.com/questions/14860239/checking-if-state-is-saved-before-committing-a-fragmenttransaction + if (manager.isStateSaved) return + activity?.let { activity -> + when (v.id) { + R.id.overview_treatmentbutton -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, Runnable { TreatmentDialog().show(manager, "Overview") }) + R.id.overview_wizardbutton -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, Runnable { WizardDialog().show(manager, "Overview") }) + R.id.overview_insulinbutton -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, Runnable { InsulinDialog().show(manager, "Overview") }) + R.id.overview_quickwizardbutton -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, Runnable { onClickQuickWizard() }) + R.id.overview_carbsbutton -> protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, Runnable { CarbsDialog().show(manager, "Overview") }) + + R.id.overview_pumpstatus -> { + if (activePlugin.activePump.isSuspended || !activePlugin.activePump.isInitialized) commandQueue.readStatus("RefreshClicked", null) + } + + R.id.overview_cgmbutton -> { + if (xdripPlugin.isEnabled(PluginType.BGSOURCE)) + openCgmApp("com.eveningoutpost.dexdrip") + else if (dexcomPlugin.isEnabled(PluginType.BGSOURCE)) { + dexcomPlugin.findDexcomPackageName()?.let { + openCgmApp(it) + } + ?: ToastUtils.showToastInUiThread(activity, resourceHelper.gs(R.string.dexcom_app_not_installed)) + } + } + + R.id.overview_calibrationbutton -> { + if (xdripPlugin.isEnabled(PluginType.BGSOURCE)) { + CalibrationDialog().show(manager, "CalibrationDialog") + } else if (dexcomPlugin.isEnabled(PluginType.BGSOURCE)) { + try { + dexcomPlugin.findDexcomPackageName()?.let { + startActivity(Intent("com.dexcom.cgm.activities.MeterEntryActivity").setPackage(it)) + } + ?: ToastUtils.showToastInUiThread(activity, resourceHelper.gs(R.string.dexcom_app_not_installed)) + } catch (e: ActivityNotFoundException) { + ToastUtils.showToastInUiThread(activity, resourceHelper.gs(R.string.g5appnotdetected)) + } + } + } + + R.id.overview_accepttempbutton -> { + profileFunction.getProfile() ?: return + if (loopPlugin.isEnabled(PluginType.LOOP)) { + val lastRun = loopPlugin.lastRun + loopPlugin.invoke("Accept temp button", false) + if (lastRun?.lastAPSRun != null && lastRun.constraintsProcessed.isChangeRequested) { + OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.pump_tempbasal_label), lastRun.constraintsProcessed.toSpanned(), Runnable { + aapsLogger.debug("USER ENTRY: ACCEPT TEMP BASAL") + overview_accepttempbutton?.visibility = View.GONE + (context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).cancel(Constants.notificationID) + actionStringHandler.handleInitiate("cancelChangeRequest") + loopPlugin.acceptChangeRequest() + }) + } + } + } + } + } + } + + private fun openCgmApp(packageName: String) { + context?.let { + val packageManager = it.packageManager + try { + val intent = packageManager.getLaunchIntentForPackage(packageName) + ?: throw ActivityNotFoundException() + intent.addCategory(Intent.CATEGORY_LAUNCHER) + it.startActivity(intent) + } catch (e: ActivityNotFoundException) { + OKDialog.show(it, "", resourceHelper.gs(R.string.error_starting_cgm)) + } + } + } + + override fun onLongClick(v: View): Boolean { + when (v.id) { + R.id.overview_quickwizardbutton -> { + startActivity(Intent(v.context, QuickWizardListActivity::class.java)) + return true + } + } + return false + } + + private fun onClickQuickWizard() { + val actualBg = iobCobCalculatorPlugin.actualBg() + val profile = profileFunction.getProfile() + val profileName = profileFunction.getProfileName() + val pump = activePlugin.activePump + val quickWizardEntry = quickWizard.getActive() + if (quickWizardEntry != null && actualBg != null && profile != null) { + overview_quickwizardbutton?.visibility = View.VISIBLE + val wizard = quickWizardEntry.doCalc(profile, profileName, actualBg, true) + if (wizard.calculatedTotalInsulin > 0.0 && quickWizardEntry.carbs() > 0.0) { + val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(quickWizardEntry.carbs())).value() + activity?.let { + if (abs(wizard.insulinAfterConstraints - wizard.calculatedTotalInsulin) >= pump.pumpDescription.pumpType.determineCorrectBolusStepSize(wizard.insulinAfterConstraints) || carbsAfterConstraints != quickWizardEntry.carbs()) { + OKDialog.show(it, resourceHelper.gs(R.string.treatmentdeliveryerror), resourceHelper.gs(R.string.constraints_violation) + "\n" + resourceHelper.gs(R.string.changeyourinput)) + return + } + wizard.confirmAndExecute(it) + } + } + } + } + + private fun updatePumpStatus(event: EventPumpStatusChanged) { + val status = event.getStatus(resourceHelper) + if (status != "") { + overview_pumpstatus?.text = status + overview_pumpstatuslayout?.visibility = View.VISIBLE + overview_looplayout?.visibility = View.GONE + } else { + overview_pumpstatuslayout?.visibility = View.GONE + overview_looplayout?.visibility = View.VISIBLE + } + } + + @SuppressLint("SetTextI18n") + private fun processButtonsVisibility() { + val lastBG = iobCobCalculatorPlugin.lastBg() + val pump = activePlugin.activePump + val profile = profileFunction.getProfile() + val profileName = profileFunction.getProfileName() + val actualBG = iobCobCalculatorPlugin.actualBg() + + // QuickWizard button + val quickWizardEntry = quickWizard.getActive() + if (quickWizardEntry != null && lastBG != null && profile != null && pump.isInitialized && !pump.isSuspended) { + overview_quickwizardbutton?.visibility = View.VISIBLE + val wizard = quickWizardEntry.doCalc(profile, profileName, lastBG, false) + overview_quickwizardbutton?.text = quickWizardEntry.buttonText() + "\n" + resourceHelper.gs(R.string.format_carbs, quickWizardEntry.carbs()) + + " " + resourceHelper.gs(R.string.formatinsulinunits, wizard.calculatedTotalInsulin) + if (wizard.calculatedTotalInsulin <= 0) overview_quickwizardbutton?.visibility = View.GONE + } else overview_quickwizardbutton?.visibility = View.GONE + + // **** Temp button **** + val lastRun = loopPlugin.lastRun + val closedLoopEnabled = constraintChecker.isClosedLoopAllowed() + + val showAcceptButton = !closedLoopEnabled.value() && // Open mode needed + lastRun != null && + (lastRun.lastOpenModeAccept == 0L || lastRun.lastOpenModeAccept < lastRun.lastAPSRun) &&// never accepted or before last result + lastRun.constraintsProcessed.isChangeRequested // change is requested + + if (showAcceptButton && pump.isInitialized && !pump.isSuspended && loopPlugin.isEnabled(PluginType.LOOP)) { + overview_accepttempbutton?.visibility = View.VISIBLE + overview_accepttempbutton?.text = "${resourceHelper.gs(R.string.setbasalquestion)}\n${lastRun!!.constraintsProcessed}" + } else { + overview_accepttempbutton?.visibility = View.GONE + } + + // **** Various treatment buttons **** + overview_carbsbutton?.visibility = ((!activePlugin.activePump.pumpDescription.storesCarbInfo || pump.isInitialized && !pump.isSuspended) && profile != null && sp.getBoolean(R.string.key_show_carbs_button, true)).toVisibility() + overview_treatmentbutton?.visibility = (pump.isInitialized && !pump.isSuspended && profile != null && sp.getBoolean(R.string.key_show_treatment_button, false)).toVisibility() + overview_wizardbutton?.visibility = (pump.isInitialized && !pump.isSuspended && profile != null && sp.getBoolean(R.string.key_show_wizard_button, true)).toVisibility() + overview_insulinbutton?.visibility = (pump.isInitialized && !pump.isSuspended && profile != null && sp.getBoolean(R.string.key_show_insulin_button, true)).toVisibility() + + // **** Calibration & CGM buttons **** + val xDripIsBgSource = xdripPlugin.isEnabled(PluginType.BGSOURCE) + val dexcomIsSource = dexcomPlugin.isEnabled(PluginType.BGSOURCE) + overview_calibrationbutton?.visibility = ((xDripIsBgSource || dexcomIsSource) && actualBG != null && sp.getBoolean(R.string.key_show_calibration_button, true)).toVisibility() + overview_cgmbutton?.visibility = (sp.getBoolean(R.string.key_show_cgm_button, false) && (xDripIsBgSource || dexcomIsSource)).toVisibility() + + } + + private fun scheduleUpdateGUI(from: String) { + class UpdateRunnable : Runnable { + override fun run() { + activity?.runOnUiThread { + updateGUI(from) + scheduledUpdate = null + } + } + } + // prepare task for execution in 500 milliseconds + // cancel waiting task to prevent multiple updates + scheduledUpdate?.cancel(false) + val task: Runnable = UpdateRunnable() + scheduledUpdate = worker.schedule(task, 500, TimeUnit.MILLISECONDS) + } + + @SuppressLint("SetTextI18n") + fun updateGUI(from: String) { + aapsLogger.debug("UpdateGUI from $from") + + overview_time?.text = DateUtil.timeString(Date()) + + if (!profileFunction.isProfileValid("Overview")) { + overview_pumpstatus?.setText(R.string.noprofileset) + overview_pumpstatuslayout?.visibility = View.VISIBLE + overview_looplayout?.visibility = View.GONE + return + } + notificationStore.updateNotifications(overview_notifications) + overview_pumpstatuslayout?.visibility = View.GONE + overview_looplayout?.visibility = View.VISIBLE + + val profile = profileFunction.getProfile() ?: return + val actualBG = iobCobCalculatorPlugin.actualBg() + val lastBG = iobCobCalculatorPlugin.lastBg() + val pump = activePlugin.activePump + val units = profileFunction.getUnits() + val lowLine = defaultValueHelper.determineLowLine() + val highLine = defaultValueHelper.determineHighLine() + + //Start with updating the BG as it is unaffected by loop. + // **** BG value **** + if (lastBG != null) { + val color = when { + lastBG.valueToUnits(units) < lowLine -> resourceHelper.gc(R.color.low) + lastBG.valueToUnits(units) > highLine -> resourceHelper.gc(R.color.high) + else -> resourceHelper.gc(R.color.inrange) + } + + overview_bg?.text = lastBG.valueToUnitsToString(units) + overview_bg?.setTextColor(color) + overview_arrow?.text = lastBG.directionToSymbol() + overview_arrow?.setTextColor(color) + + val glucoseStatus = GlucoseStatus(injector).glucoseStatusData + if (glucoseStatus != null) { + overview_delta?.text = "Δ ${Profile.toUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units)} $units" + overview_deltashort?.text = Profile.toSignedUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units) + overview_avgdelta?.text = "øΔ15m: ${Profile.toUnitsString(glucoseStatus.short_avgdelta, glucoseStatus.short_avgdelta * Constants.MGDL_TO_MMOLL, units)}\nøΔ40m: ${Profile.toUnitsString(glucoseStatus.long_avgdelta, glucoseStatus.long_avgdelta * Constants.MGDL_TO_MMOLL, units)}" + } else { + overview_delta?.text = "Δ " + resourceHelper.gs(R.string.notavailable) + overview_deltashort?.text = "---" + overview_avgdelta?.text = "" + } + + // strike through if BG is old + overview_bg?.let { overview_bg -> + var flag = overview_bg.paintFlags + flag = if (actualBG == null) { + flag or Paint.STRIKE_THRU_TEXT_FLAG + } else flag and Paint.STRIKE_THRU_TEXT_FLAG.inv() + overview_bg.paintFlags = flag + } + overview_timeago?.text = DateUtil.minAgo(resourceHelper, lastBG.date) + overview_timeagoshort?.text = "(" + DateUtil.minAgoShort(lastBG.date) + ")" + + } + val closedLoopEnabled = constraintChecker.isClosedLoopAllowed() + + // open loop mode + if (Config.APS && pump.pumpDescription.isTempBasalCapable) { + overview_apsmode?.visibility = View.VISIBLE + when { + loopPlugin.isEnabled(PluginType.LOOP) && loopPlugin.isSuperBolus -> { + overview_apsmode?.text = String.format(resourceHelper.gs(R.string.loopsuperbolusfor), loopPlugin.minutesToEndOfSuspend()) + overview_apsmode?.setBackgroundColor(resourceHelper.gc(R.color.ribbonWarning)) + overview_apsmode?.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning)) + } + + loopPlugin.isDisconnected -> { + overview_apsmode?.text = String.format(resourceHelper.gs(R.string.loopdisconnectedfor), loopPlugin.minutesToEndOfSuspend()) + overview_apsmode?.setBackgroundColor(resourceHelper.gc(R.color.ribbonCritical)) + overview_apsmode?.setTextColor(resourceHelper.gc(R.color.ribbonTextCritical)) + } + + loopPlugin.isEnabled(PluginType.LOOP) && loopPlugin.isSuspended -> { + overview_apsmode?.text = String.format(resourceHelper.gs(R.string.loopsuspendedfor), loopPlugin.minutesToEndOfSuspend()) + overview_apsmode?.setBackgroundColor(resourceHelper.gc(R.color.ribbonWarning)) + overview_apsmode?.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning)) + } + + pump.isSuspended -> { + overview_apsmode?.text = resourceHelper.gs(R.string.pumpsuspended) + overview_apsmode?.setBackgroundColor(resourceHelper.gc(R.color.ribbonWarning)) + overview_apsmode?.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning)) + } + + loopPlugin.isEnabled(PluginType.LOOP) -> { + overview_apsmode?.text = if (closedLoopEnabled.value()) resourceHelper.gs(R.string.closedloop) else resourceHelper.gs(R.string.openloop) + overview_apsmode?.setBackgroundColor(resourceHelper.gc(R.color.ribbonDefault)) + overview_apsmode?.setTextColor(resourceHelper.gc(R.color.ribbonTextDefault)) + } + + else -> { + overview_apsmode?.text = resourceHelper.gs(R.string.disabledloop) + overview_apsmode?.setBackgroundColor(resourceHelper.gc(R.color.ribbonCritical)) + overview_apsmode?.setTextColor(resourceHelper.gc(R.color.ribbonTextCritical)) + } + } + } else { + overview_apsmode?.visibility = View.GONE + } + + // temp target + val tempTarget = treatmentsPlugin.tempTargetFromHistory + if (tempTarget != null) { + overview_temptarget?.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning)) + overview_temptarget?.setBackgroundColor(resourceHelper.gc(R.color.ribbonWarning)) + overview_temptarget?.text = Profile.toTargetRangeString(tempTarget.low, tempTarget.high, Constants.MGDL, units) + " " + DateUtil.untilString(tempTarget.end(), resourceHelper) + } else { + overview_temptarget?.setTextColor(resourceHelper.gc(R.color.ribbonTextDefault)) + overview_temptarget?.setBackgroundColor(resourceHelper.gc(R.color.ribbonDefault)) + overview_temptarget?.text = Profile.toTargetRangeString(profile.targetLowMgdl, profile.targetHighMgdl, Constants.MGDL, units) + } + + // Basal, TBR + val activeTemp = treatmentsPlugin.getTempBasalFromHistory(System.currentTimeMillis()) + overview_basebasal?.text = activeTemp?.let { if (resourceHelper.shortTextMode()) "T: " + activeTemp.toStringVeryShort() else activeTemp.toStringFull() } + ?: resourceHelper.gs(R.string.pump_basebasalrate, profile.basal) + overview_basebasal?.setOnClickListener { + var fullText = "${resourceHelper.gs(R.string.pump_basebasalrate_label)}: ${resourceHelper.gs(R.string.pump_basebasalrate, profile.basal)}" + if (activeTemp != null) + fullText += "\n" + resourceHelper.gs(R.string.pump_tempbasal_label) + ": " + activeTemp.toStringFull() + activity?.let { + OKDialog.show(it, resourceHelper.gs(R.string.basal), fullText) + } + } + overview_basebasal?.setTextColor(activeTemp?.let { resourceHelper.gc(R.color.basal) } + ?: resourceHelper.gc(R.color.defaulttextcolor)) + + // Extended bolus + val extendedBolus = treatmentsPlugin.getExtendedBolusFromHistory(System.currentTimeMillis()) + overview_extendedbolus?.text = if (extendedBolus != null && !pump.isFakingTempsByExtendedBoluses) { + if (resourceHelper.shortTextMode()) resourceHelper.gs(R.string.pump_basebasalrate, extendedBolus.absoluteRate()) + else extendedBolus.toStringMedium() + } else "" + overview_extendedbolus?.setOnClickListener { + if (extendedBolus != null) activity?.let { + OKDialog.show(it, resourceHelper.gs(R.string.extended_bolus), extendedBolus.toString()) + } + } + + overview_activeprofile?.text = profileFunction.getProfileNameWithDuration() + if (profile.percentage != 100 || profile.timeshift != 0) { + overview_activeprofile?.setBackgroundColor(resourceHelper.gc(R.color.ribbonWarning)) + overview_activeprofile?.setTextColor(resourceHelper.gc(R.color.ribbonTextWarning)) + } else { + overview_activeprofile?.setBackgroundColor(resourceHelper.gc(R.color.ribbonDefault)) + overview_activeprofile?.setTextColor(resourceHelper.gc(R.color.ribbonTextDefault)) + } + + processButtonsVisibility() + + // iob + treatmentsPlugin.updateTotalIOBTreatments() + treatmentsPlugin.updateTotalIOBTempBasals() + val bolusIob = treatmentsPlugin.lastCalculationTreatments.round() + val basalIob = treatmentsPlugin.lastCalculationTempBasals.round() + overview_iob?.text = when { + resourceHelper.shortTextMode() -> { + resourceHelper.gs(R.string.formatinsulinunits, bolusIob.iob + basalIob.basaliob) + } + + resourceHelper.gb(R.bool.isTablet) -> { + resourceHelper.gs(R.string.formatinsulinunits, bolusIob.iob + basalIob.basaliob) + " (" + + resourceHelper.gs(R.string.bolus) + ": " + resourceHelper.gs(R.string.formatinsulinunits, bolusIob.iob) + + resourceHelper.gs(R.string.basal) + ": " + resourceHelper.gs(R.string.formatinsulinunits, basalIob.basaliob) + ")" + } + + else -> { + resourceHelper.gs(R.string.formatinsulinunits, bolusIob.iob + basalIob.basaliob) + " (" + + resourceHelper.gs(R.string.formatinsulinunits, bolusIob.iob) + "/" + + resourceHelper.gs(R.string.formatinsulinunits, basalIob.basaliob) + ")" + } + } + overview_iob?.setOnClickListener { + activity?.let { + OKDialog.show(it, resourceHelper.gs(R.string.iob), + resourceHelper.gs(R.string.formatinsulinunits, bolusIob.iob + basalIob.basaliob) + "\n" + + resourceHelper.gs(R.string.bolus) + ": " + resourceHelper.gs(R.string.formatinsulinunits, bolusIob.iob) + "\n" + + resourceHelper.gs(R.string.basal) + ": " + resourceHelper.gs(R.string.formatinsulinunits, basalIob.basaliob) + ) + } + } + + // NSClient mode + statusLightHandler.updateAge(careportal_sensorage, careportal_insulinage, careportal_canulaage, careportal_pbage) + // Mode modes + if (sp.getBoolean(R.string.key_show_statuslights, false)) { + if (sp.getBoolean(R.string.key_show_statuslights_extended, false)) + statusLightHandler.extendedStatusLight(overview_canulaage, overview_insulinage, overview_reservoirlevel, overview_sensorage, overview_batterylevel) + else + statusLightHandler.statusLight(overview_canulaage, overview_insulinage, overview_reservoirlevel, overview_sensorage, overview_batterylevel) + } + + // cob + var cobText: String = resourceHelper.gs(R.string.value_unavailable_short) + val cobInfo = iobCobCalculatorPlugin.getCobInfo(false, "Overview COB") + if (cobInfo.displayCob != null) { + cobText = DecimalFormatter.to0Decimal(cobInfo.displayCob) + if (cobInfo.futureCarbs > 0) cobText += "(" + DecimalFormatter.to0Decimal(cobInfo.futureCarbs) + ")" + } + overview_cob?.text = cobText + + val lastRun = loopPlugin.lastRun + val predictionsAvailable = if (Config.APS) lastRun?.request?.hasPredictions == true else Config.NSCLIENT + + // pump status from ns + overview_pump?.text = nsDeviceStatus.pumpStatus + overview_pump?.setOnClickListener { activity?.let { OKDialog.show(it, resourceHelper.gs(R.string.pump), nsDeviceStatus.extendedPumpStatus) } } + + // OpenAPS status from ns + overview_openaps?.text = nsDeviceStatus.openApsStatus + overview_openaps?.setOnClickListener { activity?.let { OKDialog.show(it, resourceHelper.gs(R.string.openaps), nsDeviceStatus.extendedOpenApsStatus) } } + + // Uploader status from ns + overview_uploader?.text = nsDeviceStatus.uploaderStatusSpanned + overview_uploader?.setOnClickListener { activity?.let { OKDialog.show(it, resourceHelper.gs(R.string.uploader), nsDeviceStatus.extendedUploaderStatus) } } + + // Sensitivity + iobCobCalculatorPlugin.getLastAutosensData("Overview")?.let { autosensData -> + overview_sensitivity?.text = String.format(Locale.ENGLISH, "%.0f%%", autosensData.autosensResult.ratio * 100) + } + + // ****** GRAPH ******* + Thread(Runnable { + + // align to hours + val calendar = Calendar.getInstance() + calendar.timeInMillis = System.currentTimeMillis() + calendar[Calendar.MILLISECOND] = 0 + calendar[Calendar.SECOND] = 0 + calendar[Calendar.MINUTE] = 0 + calendar.add(Calendar.HOUR, 1) + val hoursToFetch: Int + val toTime: Long + val fromTime: Long + val endTime: Long + val apsResult = if (Config.APS) lastRun?.constraintsProcessed else NSDeviceStatus.getAPSResult(injector) + if (predictionsAvailable && apsResult != null && sp.getBoolean("showprediction", false)) { + var predHours = (ceil(apsResult.latestPredictionsTime - System.currentTimeMillis().toDouble()) / (60 * 60 * 1000)).toInt() + predHours = min(2, predHours) + predHours = max(0, predHours) + hoursToFetch = rangeToDisplay - predHours + toTime = calendar.timeInMillis + 100000 // little bit more to avoid wrong rounding - GraphView specific + fromTime = toTime - T.hours(hoursToFetch.toLong()).msecs() + endTime = toTime + T.hours(predHours.toLong()).msecs() + } else { + hoursToFetch = rangeToDisplay + toTime = calendar.timeInMillis + 100000 // little bit more to avoid wrong rounding - GraphView specific + fromTime = toTime - T.hours(hoursToFetch.toLong()).msecs() + endTime = toTime + } + val now = System.currentTimeMillis() + + // ------------------ 1st graph + val graphData = GraphData(injector, overview_bggraph, iobCobCalculatorPlugin) + + // **** In range Area **** + graphData.addInRangeArea(fromTime, endTime, lowLine, highLine) + + // **** BG **** + if (predictionsAvailable && sp.getBoolean("showprediction", false)) graphData.addBgReadings(fromTime, toTime, lowLine, highLine, + apsResult?.predictions) else graphData.addBgReadings(fromTime, toTime, lowLine, highLine, null) + + // set manual x bounds to have nice steps + graphData.formatAxis(fromTime, endTime) + + // Treatments + graphData.addTreatments(fromTime, endTime) + if (sp.getBoolean("showactivityprimary", true)) + graphData.addActivity(fromTime, endTime, false, 0.8) + + // add basal data + if (pump.pumpDescription.isTempBasalCapable && sp.getBoolean("showbasals", true)) + graphData.addBasals(fromTime, now, lowLine / graphData.maxY / 1.2) + + // add target line + graphData.addTargetLine(fromTime, toTime, profile, loopPlugin.lastRun) + + // **** NOW line **** + graphData.addNowLine(now) + + // ------------------ 2nd graph + val secondGraphData = GraphData(injector, overview_iobgraph, iobCobCalculatorPlugin) + var useIobForScale = false + var useCobForScale = false + var useDevForScale = false + var useRatioForScale = false + var useDSForScale = false + var useIAForScale = false + // finally enforce drawing of graphs + when { + sp.getBoolean("showiob", true) -> + useIobForScale = true + sp.getBoolean("showcob", true) -> + useCobForScale = true + sp.getBoolean("showdeviations", false) -> + useDevForScale = true + sp.getBoolean("showratios", false) -> + useRatioForScale = true + sp.getBoolean("showactivitysecondary", false) -> + useIAForScale = true + sp.getBoolean("showdevslope", false) -> + useDSForScale = true + } + + if (sp.getBoolean("showiob", true)) secondGraphData.addIob(fromTime, now, useIobForScale, 1.0, sp.getBoolean("showprediction", false)) + if (sp.getBoolean("showcob", true)) secondGraphData.addCob(fromTime, now, useCobForScale, if (useCobForScale) 1.0 else 0.5) + if (sp.getBoolean("showdeviations", false)) secondGraphData.addDeviations(fromTime, now, useDevForScale, 1.0) + if (sp.getBoolean("showratios", false)) secondGraphData.addRatio(fromTime, now, useRatioForScale, 1.0) + if (sp.getBoolean("showactivitysecondary", true)) secondGraphData.addActivity(fromTime, endTime, useIAForScale, 0.8) + if (sp.getBoolean("showdevslope", false) && buildHelper.isDev()) secondGraphData.addDeviationSlope(fromTime, now, useDSForScale, 1.0) + + // **** NOW line **** + // set manual x bounds to have nice steps + secondGraphData.formatAxis(fromTime, endTime) + secondGraphData.addNowLine(now) + + // do GUI update + val activity = activity + activity?.runOnUiThread { + if (sp.getBoolean("showiob", true) + || sp.getBoolean("showcob", true) + || sp.getBoolean("showdeviations", false) + || sp.getBoolean("showratios", false) + || sp.getBoolean("showactivitysecondary", false) + || sp.getBoolean("showdevslope", false)) { + overview_iobgraph?.visibility = View.VISIBLE + } else { + overview_iobgraph?.visibility = View.GONE + } + // finally enforce drawing of graphs + graphData.performUpdate() + secondGraphData.performUpdate() + } + }).start() + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewMenus.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewMenus.kt index 3336f62ab6..3e3714a89d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewMenus.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewMenus.kt @@ -31,7 +31,6 @@ import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction -import info.nightscout.androidaps.plugins.general.overview.OverviewFragment.CHARTTYPE import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DefaultValueHelper @@ -58,6 +57,10 @@ class OverviewMenus @Inject constructor( private val loopPlugin: LoopPlugin ) { + enum class CharType { + PRE, BAS, IOB, COB, DEV, SEN, ACTPRIM, ACTSEC, DEVSLOPE + } + fun setupChartMenu(chartButton: ImageButton) { chartButton.setOnClickListener { v: View -> val predictionsAvailable: Boolean = when { @@ -72,7 +75,7 @@ class OverviewMenus @Inject constructor( //var s: SpannableString val popup = PopupMenu(v.context, v) if (predictionsAvailable) { - val item = popup.menu.add(Menu.NONE, CHARTTYPE.PRE.ordinal, Menu.NONE, "Predictions") + val item = popup.menu.add(Menu.NONE, CharType.PRE.ordinal, Menu.NONE, "Predictions") val title = item.title if (titleMaxChars < title.length) titleMaxChars = title.length val s = SpannableString(title) @@ -82,7 +85,7 @@ class OverviewMenus @Inject constructor( item.isChecked = sp.getBoolean("showprediction", true) } run { - val item = popup.menu.add(Menu.NONE, CHARTTYPE.BAS.ordinal, Menu.NONE, resourceHelper.gs(R.string.overview_show_basals)) + val item = popup.menu.add(Menu.NONE, CharType.BAS.ordinal, Menu.NONE, resourceHelper.gs(R.string.overview_show_basals)) val title = item.title if (titleMaxChars < title.length) titleMaxChars = title.length val s = SpannableString(title) @@ -92,7 +95,7 @@ class OverviewMenus @Inject constructor( item.isChecked = sp.getBoolean("showbasals", true) } run { - val item = popup.menu.add(Menu.NONE, CHARTTYPE.ACTPRIM.ordinal, Menu.NONE, resourceHelper.gs(R.string.overview_show_activity)) + val item = popup.menu.add(Menu.NONE, CharType.ACTPRIM.ordinal, Menu.NONE, resourceHelper.gs(R.string.overview_show_activity)) val title = item.title if (titleMaxChars < title.length) titleMaxChars = title.length val s = SpannableString(title) @@ -104,7 +107,7 @@ class OverviewMenus @Inject constructor( dividerItem.isEnabled = false } run { - val item = popup.menu.add(Menu.NONE, CHARTTYPE.IOB.ordinal, Menu.NONE, resourceHelper.gs(R.string.overview_show_iob)) + val item = popup.menu.add(Menu.NONE, CharType.IOB.ordinal, Menu.NONE, resourceHelper.gs(R.string.overview_show_iob)) val title = item.title if (titleMaxChars < title.length) titleMaxChars = title.length val s = SpannableString(title) @@ -114,7 +117,7 @@ class OverviewMenus @Inject constructor( item.isChecked = sp.getBoolean("showiob", true) } run { - val item = popup.menu.add(Menu.NONE, CHARTTYPE.COB.ordinal, Menu.NONE, resourceHelper.gs(R.string.overview_show_cob)) + val item = popup.menu.add(Menu.NONE, CharType.COB.ordinal, Menu.NONE, resourceHelper.gs(R.string.overview_show_cob)) val title = item.title if (titleMaxChars < title.length) titleMaxChars = title.length val s = SpannableString(title) @@ -124,7 +127,7 @@ class OverviewMenus @Inject constructor( item.isChecked = sp.getBoolean("showcob", true) } run { - val item = popup.menu.add(Menu.NONE, CHARTTYPE.DEV.ordinal, Menu.NONE, resourceHelper.gs(R.string.overview_show_deviations)) + val item = popup.menu.add(Menu.NONE, CharType.DEV.ordinal, Menu.NONE, resourceHelper.gs(R.string.overview_show_deviations)) val title = item.title if (titleMaxChars < title.length) titleMaxChars = title.length val s = SpannableString(title) @@ -134,7 +137,7 @@ class OverviewMenus @Inject constructor( item.isChecked = sp.getBoolean("showdeviations", false) } run { - val item = popup.menu.add(Menu.NONE, CHARTTYPE.SEN.ordinal, Menu.NONE, resourceHelper.gs(R.string.overview_show_sensitivity)) + val item = popup.menu.add(Menu.NONE, CharType.SEN.ordinal, Menu.NONE, resourceHelper.gs(R.string.overview_show_sensitivity)) val title = item.title if (titleMaxChars < title.length) titleMaxChars = title.length val s = SpannableString(title) @@ -144,7 +147,7 @@ class OverviewMenus @Inject constructor( item.isChecked = sp.getBoolean("showratios", false) } run { - val item = popup.menu.add(Menu.NONE, CHARTTYPE.ACTSEC.ordinal, Menu.NONE, resourceHelper.gs(R.string.overview_show_activity)) + val item = popup.menu.add(Menu.NONE, CharType.ACTSEC.ordinal, Menu.NONE, resourceHelper.gs(R.string.overview_show_activity)) val title = item.title if (titleMaxChars < title.length) titleMaxChars = title.length val s = SpannableString(title) @@ -154,7 +157,7 @@ class OverviewMenus @Inject constructor( item.isChecked = sp.getBoolean("showactivitysecondary", true) } if (buildHelper.isDev()) { - val item = popup.menu.add(Menu.NONE, CHARTTYPE.DEVSLOPE.ordinal, Menu.NONE, "Deviation slope") + val item = popup.menu.add(Menu.NONE, CharType.DEVSLOPE.ordinal, Menu.NONE, "Deviation slope") val title = item.title if (titleMaxChars < title.length) titleMaxChars = title.length val s = SpannableString(title) @@ -168,15 +171,15 @@ class OverviewMenus @Inject constructor( dividerItem.title = String(CharArray(titleMaxChars + 10)).replace("\u0000", "_") popup.setOnMenuItemClickListener { when (it.itemId) { - CHARTTYPE.PRE.ordinal -> sp.putBoolean("showprediction", !it.isChecked) - CHARTTYPE.BAS.ordinal -> sp.putBoolean("showbasals", !it.isChecked) - CHARTTYPE.IOB.ordinal -> sp.putBoolean("showiob", !it.isChecked) - CHARTTYPE.COB.ordinal -> sp.putBoolean("showcob", !it.isChecked) - CHARTTYPE.DEV.ordinal -> sp.putBoolean("showdeviations", !it.isChecked) - CHARTTYPE.SEN.ordinal -> sp.putBoolean("showratios", !it.isChecked) - CHARTTYPE.ACTPRIM.ordinal -> sp.putBoolean("showactivityprimary", !it.isChecked) - CHARTTYPE.ACTSEC.ordinal -> sp.putBoolean("showactivitysecondary", !it.isChecked) - CHARTTYPE.DEVSLOPE.ordinal -> sp.putBoolean("showdevslope", !it.isChecked) + CharType.PRE.ordinal -> sp.putBoolean("showprediction", !it.isChecked) + CharType.BAS.ordinal -> sp.putBoolean("showbasals", !it.isChecked) + CharType.IOB.ordinal -> sp.putBoolean("showiob", !it.isChecked) + CharType.COB.ordinal -> sp.putBoolean("showcob", !it.isChecked) + CharType.DEV.ordinal -> sp.putBoolean("showdeviations", !it.isChecked) + CharType.SEN.ordinal -> sp.putBoolean("showratios", !it.isChecked) + CharType.ACTPRIM.ordinal -> sp.putBoolean("showactivityprimary", !it.isChecked) + CharType.ACTSEC.ordinal -> sp.putBoolean("showactivitysecondary", !it.isChecked) + CharType.DEVSLOPE.ordinal -> sp.putBoolean("showdevslope", !it.isChecked) } rxBus.send(EventRefreshOverview("OnMenuItemClickListener")) return@setOnMenuItemClickListener true diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/StatusLightHandler.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/StatusLightHandler.kt index 6900614a96..79b1c80ba1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/StatusLightHandler.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/StatusLightHandler.kt @@ -89,9 +89,9 @@ class StatusLightHandler @Inject constructor( /** * applies the extended statusLight subview on the overview fragment */ - fun extendedStatusLight(cageView: TextView, iAgeView: TextView, - reservoirView: TextView, sageView: TextView, - batteryView: TextView) { + fun extendedStatusLight(cageView: TextView?, iAgeView: TextView?, + reservoirView: TextView?, sageView: TextView?, + batteryView: TextView?) { val pump = activePlugin.activePump handleAge("cage", CareportalEvent.SITECHANGE, cageView, "CAN ", 48, 72) @@ -112,11 +112,11 @@ class StatusLightHandler @Inject constructor( } } - private fun handleAge(nsSettingPlugin: String, eventName: String, view: TextView, text: String, + private fun handleAge(nsSettingPlugin: String, eventName: String, view: TextView?, text: String, defaultWarnThreshold: Int, defaultUrgentThreshold: Int) { val urgent = nsSettingsStatus.getExtendedWarnValue(nsSettingPlugin, "urgent", defaultUrgentThreshold.toDouble()) val warn = nsSettingsStatus.getExtendedWarnValue(nsSettingPlugin, "warn", defaultWarnThreshold.toDouble()) - handleAge(view, text, eventName, warn, urgent, true) + handleAge(view, text, eventName, warn, urgent) } private fun handleLevel(criticalSetting: Int, criticalDefaultValue: Double, @@ -132,14 +132,14 @@ class StatusLightHandler @Inject constructor( } private fun handleAge(age: TextView?, eventType: String, warnThreshold: Double, urgentThreshold: Double) = - handleAge(age, "", eventType, warnThreshold, urgentThreshold, OverviewFragment.shorttextmode) + handleAge(age, "", eventType, warnThreshold, urgentThreshold) - fun handleAge(age: TextView?, prefix: String, eventType: String, warnThreshold: Double, urgentThreshold: Double, useShortText: Boolean) { - val notavailable = if (useShortText) "-" else resourceHelper.gs(R.string.notavailable) + fun handleAge(age: TextView?, prefix: String, eventType: String, warnThreshold: Double, urgentThreshold: Double) { + val notavailable = if (resourceHelper.shortTextMode()) "-" else resourceHelper.gs(R.string.notavailable) val careportalEvent = MainApp.getDbHelper().getLastCareportalEvent(eventType) if (careportalEvent != null) { age?.setTextColor(determineTextColor(careportalEvent, warnThreshold, urgentThreshold)) - age?.text = prefix + careportalEvent.age(useShortText) + age?.text = prefix + careportalEvent.age(resourceHelper.shortTextMode()) } else { age?.text = notavailable } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.kt index fc7d1eb2a1..4848453d91 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/graphData/GraphData.kt @@ -61,7 +61,7 @@ class GraphData(injector: HasAndroidInjector, private val graph: GraphView, priv var maxBgValue = Double.MIN_VALUE bgReadingsArray = iobCobCalculatorPlugin.bgReadings if (bgReadingsArray?.isEmpty() != false) { - aapsLogger.debug(LTag.OVERVIEW, "No BG data.") + aapsLogger.debug("No BG data.") maxY = 10.0 minY = 0.0 return diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt index 594c2f5c5b..f1c58029d1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt @@ -291,11 +291,12 @@ class ActionStringHandler @Inject constructor( } else if ("changeRequest" == act[0]) { ////////////////////////////////////////////// CHANGE REQUEST rTitle = resourceHelper.gs(R.string.openloop_newsuggestion) rAction = "changeRequest" - val finalLastRun = loopPlugin.lastRun - rMessage += finalLastRun.constraintsProcessed - wearPlugin.requestChangeConfirmation(rTitle, rMessage, rAction) - lastSentTimestamp = System.currentTimeMillis() - lastConfirmActionString = rAction + loopPlugin.lastRun?.let { + rMessage += it.constraintsProcessed + wearPlugin.requestChangeConfirmation(rTitle, rMessage, rAction) + lastSentTimestamp = System.currentTimeMillis() + lastConfirmActionString = rAction + } return } else if ("cancelChangeRequest" == act[0]) { ////////////////////////////////////////////// CANCEL CHANGE REQUEST NOTIFICATION rAction = "cancelChangeRequest" @@ -406,9 +407,10 @@ class ActionStringHandler @Inject constructor( } val aps = activePlugin.activeAPS ret += "APS: " + (aps as PluginBase).name - if (loopPlugin.lastRun != null) { - if (loopPlugin.lastRun.lastAPSRun != null) ret += "\nLast Run: " + DateUtil.timeString(loopPlugin.lastRun.lastAPSRun) - if (loopPlugin.lastRun.lastTBREnact != 0L) ret += "\nLast Enact: " + DateUtil.timeString(loopPlugin.lastRun.lastTBREnact) + val lastRun = loopPlugin.lastRun + if (lastRun != null) { + ret += "\nLast Run: " + DateUtil.timeString(lastRun.lastAPSRun) + if (lastRun.lastTBREnact != 0L) ret += "\nLast Enact: " + DateUtil.timeString(lastRun.lastTBREnact) } } else { ret += "LOOP DISABLED\n" diff --git a/app/src/main/java/info/nightscout/androidaps/utils/OKDialog.kt b/app/src/main/java/info/nightscout/androidaps/utils/OKDialog.kt index 186b81ae36..025cb041bb 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/OKDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/OKDialog.kt @@ -11,8 +11,6 @@ import info.nightscout.androidaps.utils.alertDialogs.AlertDialogHelper object OKDialog { @SuppressLint("InflateParams") - @JvmStatic - @JvmOverloads fun show(context: Context, title: String, message: String, runnable: Runnable? = null) { var notEmptytitle = title if (notEmptytitle.isEmpty()) notEmptytitle = context.getString(R.string.message) diff --git a/app/src/main/java/info/nightscout/androidaps/utils/resources/ResourceHelper.kt b/app/src/main/java/info/nightscout/androidaps/utils/resources/ResourceHelper.kt index 1045f4b5b8..9b03902883 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/resources/ResourceHelper.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/resources/ResourceHelper.kt @@ -20,4 +20,5 @@ interface ResourceHelper { fun decodeResource(id : Int) : Bitmap fun getDisplayMetrics(): DisplayMetrics fun dpToPx(dp: Int): Int + fun shortTextMode(): Boolean } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/resources/ResourceHelperImplementation.kt b/app/src/main/java/info/nightscout/androidaps/utils/resources/ResourceHelperImplementation.kt index c62130f421..f398534039 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/resources/ResourceHelperImplementation.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/resources/ResourceHelperImplementation.kt @@ -68,4 +68,6 @@ class ResourceHelperImplementation @Inject constructor(private val context: Cont val scale = context.resources.displayMetrics.density return (dp * scale + 0.5f).toInt() } + + override fun shortTextMode() : Boolean = !gb(R.bool.isTablet) && Config.NSCLIENT } \ No newline at end of file From 6d064fb56c1b38d5e8c8bfac9fb85807183ce653 Mon Sep 17 00:00:00 2001 From: Tim Gunn <2896311+Tornado-Tim@users.noreply.github.com> Date: Sun, 5 Apr 2020 23:03:19 +1200 Subject: [PATCH 32/36] fix preference screen to conform to norms --- app/src/main/res/xml/pref_openapssmb.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/res/xml/pref_openapssmb.xml b/app/src/main/res/xml/pref_openapssmb.xml index a4ca7c02d4..859a75a82b 100644 --- a/app/src/main/res/xml/pref_openapssmb.xml +++ b/app/src/main/res/xml/pref_openapssmb.xml @@ -73,7 +73,7 @@ android:summary="@string/enablesmbaftercarbs_summary" android:title="@string/enablesmbaftercarbs" /> - - - - Date: Sun, 5 Apr 2020 18:33:39 +0200 Subject: [PATCH 33/36] New translations objectives.xml (Dutch) --- app/src/main/res/values-nl-rNL/objectives.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-nl-rNL/objectives.xml b/app/src/main/res/values-nl-rNL/objectives.xml index c9e23d317a..613bcfa9e6 100644 --- a/app/src/main/res/values-nl-rNL/objectives.xml +++ b/app/src/main/res/values-nl-rNL/objectives.xml @@ -35,6 +35,7 @@ Inhoud van loop plugin weergeven Gebruik de schaalfunctie: houd de BG grafiek lang ingedrukt Enter + Als je ten minste 3 maanden closed loop ervaring hebt met een ander doe-het-zelf systeem dan kun je wellicht een code aanvragen om doelen over te slaan. Zie https://androidaps.readthedocs.io/en/latest/CROWDIN/nl/Usage/Objectives.html#doelen-overslaan voor details. Code geaccepteerd Code ongeldig Bewijs je kennis From 98af8531f0dc7883a9bab2f85061f690838b0c43 Mon Sep 17 00:00:00 2001 From: Winfried Kuiper Date: Sun, 5 Apr 2020 21:11:13 +0200 Subject: [PATCH 34/36] Typo in log message (#2547) --- .../plugins/constraints/versionChecker/VersionCheckerUtils.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/versionChecker/VersionCheckerUtils.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/versionChecker/VersionCheckerUtils.kt index ac9724608f..91f1438d4a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/versionChecker/VersionCheckerUtils.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/versionChecker/VersionCheckerUtils.kt @@ -46,7 +46,7 @@ private fun checkVersion() = if (isConnected()) { } }.start() } else - log.debug("Github master version no checked. No connectivity") + log.debug("Github master version not checked. No connectivity") fun compareWithCurrentVersion(newVersion: String?, currentVersion: String) { From f1770c093b70412425fcd6ee78e956373cfd9ebb Mon Sep 17 00:00:00 2001 From: Dominik Dzienia Date: Fri, 3 Apr 2020 00:45:37 +0200 Subject: [PATCH 35/36] Introduced Patient name, refactored SMS OTP and Prefs Export to use it --- .../plugins/general/maintenance/ImportExportPrefs.kt | 10 +++++----- .../general/smsCommunicator/otp/OneTimePassword.kt | 6 +++--- .../nightscout/androidaps/setupwizard/SWDefinition.kt | 11 +++++++++++ app/src/main/res/values/strings.xml | 10 +++++----- app/src/main/res/xml/pref_general.xml | 8 ++++++++ app/src/main/res/xml/pref_smscommunicator.xml | 8 -------- 6 files changed, 32 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.kt index 356fc918e1..785f2acd1c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/ImportExportPrefs.kt @@ -125,13 +125,13 @@ class ImportExportPrefs @Inject constructor( val n5 = Settings.Secure.getString(context.contentResolver, "lock_screen_owner_info") val n6 = Settings.Global.getString(context.contentResolver, "device_name") - // name we use for SMS OTP token in communicator - val otpName = otp.name().trim() - val defaultOtpName = resourceHelper.gs(R.string.smscommunicator_default_user_display_name) + // name provided (hopefully) by user + val patientName = sp.getString(R.string.key_patient_name, "") + val defaultPatientName = resourceHelper.gs(R.string.patient_name_default) // name we detect from OS - val systemName = n1 ?: n2 ?: n3 ?: n4 ?: n5 ?: n6 ?: defaultOtpName - val name = if (otpName.length > 0 && otpName != defaultOtpName) otpName else systemName + val systemName = n1 ?: n2 ?: n3 ?: n4 ?: n5 ?: n6 ?: defaultPatientName + val name = if (patientName.isNotEmpty() && patientName != defaultPatientName) patientName else systemName return name } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/otp/OneTimePassword.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/otp/OneTimePassword.kt index 2f49ae25ef..3610fb37b4 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/otp/OneTimePassword.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/otp/OneTimePassword.kt @@ -48,8 +48,8 @@ class OneTimePassword @Inject constructor( * Name of master device (target of OTP) */ fun name(): String { - val defaultUserName = resourceHelper.gs(R.string.smscommunicator_default_user_display_name) - var userName = sp.getString(R.string.key_smscommunicator_otp_name, defaultUserName).replace(":", "").trim() + val defaultUserName = resourceHelper.gs(R.string.patient_name_default) + var userName = sp.getString(R.string.key_patient_name, defaultUserName).replace(":", "").trim() if (userName.isEmpty()) userName = defaultUserName return userName @@ -119,6 +119,6 @@ class OneTimePassword @Inject constructor( * Return URI used to provision Authenticator apps */ fun provisioningURI(): String? = - key?.let { "otpauth://totp/AndroidAPS:" + URLEncoder.encode(name(), "utf-8") + "?secret=" + BaseEncoding.base32().encode(it.encoded).replace("=", "") + "&issuer=AndroidAPS" } + key?.let { "otpauth://totp/AndroidAPS:" + URLEncoder.encode(name(), "utf-8").replace("+", "%20") + "?secret=" + BaseEncoding.base32().encode(it.encoded).replace("=", "") + "&issuer=AndroidAPS" } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.kt b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.kt index 3912b48433..4af38741b5 100644 --- a/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.kt +++ b/app/src/main/java/info/nightscout/androidaps/setupwizard/SWDefinition.kt @@ -198,6 +198,14 @@ class SWDefinition @Inject constructor( .add(SWBreak(injector)) .validator(SWValidator { nsClientPlugin.nsClientService != null && NSClientService.isConnected && NSClientService.hasWriteAuth }) .visibility(SWValidator { !(nsClientPlugin.nsClientService != null && NSClientService.isConnected && NSClientService.hasWriteAuth) }) + private val screenPatientName = SWScreen(injector, R.string.patient_name) + .skippable(true) + .add(SWInfotext(injector) + .label(R.string.patient_name_summary)) + .add(SWEditString(injector) + .validator(SWTextValidator { text: String -> text.length > 0 }) + .preferenceId(R.string.key_patient_name) + .updateDelay(5)) private val screenAge = SWScreen(injector, R.string.patientage) .skippable(false) .add(SWBreak(injector)) @@ -389,6 +397,7 @@ class SWDefinition @Inject constructor( .add(screenUnits) .add(displaySettings) .add(screenNsClient) + .add(screenPatientName) .add(screenAge) .add(screenInsulin) .add(screenBgSource) @@ -415,6 +424,7 @@ class SWDefinition @Inject constructor( .add(screenUnits) .add(displaySettings) .add(screenNsClient) + .add(screenPatientName) .add(screenAge) .add(screenInsulin) .add(screenBgSource) @@ -437,6 +447,7 @@ class SWDefinition @Inject constructor( .add(displaySettings) .add(screenNsClient) .add(screenBgSource) + .add(screenPatientName) .add(screenAge) .add(screenInsulin) .add(screenSensitivity) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f1ebc84bc5..041004a25a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -276,7 +276,7 @@ Created at AAPS Version Build Variant - Exporting device name + Exporting device patient name Exporting device model File encryption @@ -611,6 +611,10 @@ adult resistantadult Please select patient age to setup safety limits + Patient name + Please provide patient name or nickname to differentiate among multiple setups + User + patient_name I_understand Glimp %1$s needs battery optimalization whitelisting for proper performance @@ -1755,19 +1759,15 @@ smscommunicator_otp_enabled - smscommunicator_otp_name smscommunicator_otp_password smscommunicator_otp_secret - User from Authenticator app for: %1$s Enable Authenticator Authenticate commands using One Time Passwords generated by Google Authenticator or similar 2FA apps. Additional PIN at token end Additional digits that should be memorised and glued at end of each generated One Time Password - User name for Authenticator - User name displayed along with generated OTP on Authenticator App Authenticator setup diff --git a/app/src/main/res/xml/pref_general.xml b/app/src/main/res/xml/pref_general.xml index b5f7074c7e..3ee920cc01 100644 --- a/app/src/main/res/xml/pref_general.xml +++ b/app/src/main/res/xml/pref_general.xml @@ -20,6 +20,14 @@ android:key="@string/key_language" android:title="@string/language" /> + + + - - - \ No newline at end of file From a87681a31af2e93fc61acfe098f74f20596121fa Mon Sep 17 00:00:00 2001 From: Tim Gunn <2896311+Tornado-Tim@users.noreply.github.com> Date: Mon, 6 Apr 2020 18:45:47 +1200 Subject: [PATCH 36/36] fix variable --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7cf42b2542..646f67228d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -858,7 +858,7 @@ BG upload settings Show detailed delta Show delta with one more decimal place - key_smbinterval + smbinterval How frequently SMBs will be given in min SMB max minutes Max minutes of basal to limit SMB to