diff --git a/app/src/main/assets/OpenAPSMA/determine-basal.js b/app/src/main/assets/OpenAPSMA/determine-basal.js deleted file mode 100644 index 4c5a00fade..0000000000 --- a/app/src/main/assets/OpenAPSMA/determine-basal.js +++ /dev/null @@ -1,316 +0,0 @@ -/* - Determine Basal - - Released under MIT license. See the accompanying LICENSE.txt file for - full terms and conditions - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ -var determine_basal = function determine_basal(glucose_status, currenttemp, iob_data, profile, offline, meal_data, setTempBasal) { - var rT = { //short for requestedTemp - }; - - if (typeof profile === 'undefined' || typeof profile.current_basal === 'undefined') { - rT.error ='Error: could not get current basal rate'; - return rT; - } - - var bg = glucose_status.glucose; - if (bg < 38) { //Dexcom is in ??? mode or calibrating, do nothing. Asked @benwest for raw data in iter_glucose - rT.error = "CGM is calibrating or in ??? state"; - return rT; - } - - var max_iob = profile.max_iob; // maximum amount of non-bolus IOB OpenAPS will ever deliver - - // if target_bg is set, great. otherwise, if min and max are set, then set target to their average - var target_bg; - if (typeof profile.target_bg !== 'undefined') { - target_bg = profile.target_bg; - } else { - if (typeof profile.min_bg !== 'undefined' && typeof profile.max_bg !== 'undefined') { - target_bg = (profile.min_bg + profile.max_bg) / 2; - } else { - rT.error ='Error: could not determine target_bg'; - return rT; - } - } - - - if (typeof iob_data === 'undefined' ) { - rT.error ='Error: iob_data undefined'; - return rT; - } - - if (typeof iob_data.activity === 'undefined' || typeof iob_data.iob === 'undefined' || typeof iob_data.activity === 'undefined') { - rT.error ='Error: iob_data missing some property'; - return rT; - } - - var tick; - - if (glucose_status.delta >= 0) { - tick = "+" + glucose_status.delta; - } else { - tick = glucose_status.delta; - } - var minDelta = Math.min(glucose_status.delta, glucose_status.avgdelta); - //var maxDelta = Math.max(glucose_status.delta, glucose_status.avgdelta); - - - //calculate BG impact: the amount BG "should" be rising or falling based on insulin activity alone - var bgi = Math.round(( -iob_data.activity * profile.sens * 5 )*100)/100; - // project positive deviations for 15 minutes - var deviation = Math.round( 15 / 5 * ( glucose_status.avgdelta - bgi ) ); - // project negative deviations for 30 minutes - if (deviation < 0) { - deviation = Math.round( 30 / 5 * ( glucose_status.avgdelta - bgi ) ); - } - //console.log("Avg.Delta: " + glucose_status.avgdelta.toFixed(1) + ", BGI: " + bgi.toFixed(1) + " 15m activity projection: " + deviation.toFixed(0)); - - // calculate the naive (bolus calculator math) eventual BG based on net IOB and sensitivity - var naive_eventualBG = Math.round( bg - (iob_data.iob * 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 * profile.sens; - // and add it back in to get snoozeBG, plus another 50% to avoid low-temping at mealtime - var naive_snoozeBG = Math.round( naive_eventualBG + 1.5 * bolusContrib ); - // adjust that for deviation like we did eventualBG - var snoozeBG = naive_snoozeBG + deviation; - - //console.log("BG: " + bg +"(" + tick + ","+glucose_status.avgdelta.toFixed(1)+")"+ " -> " + eventualBG + "-" + snoozeBG + " (Unadjusted: " + naive_eventualBG + "-" + naive_snoozeBG + "), BGI: " + bgi); - - var expectedDelta = Math.round(( bgi + ( target_bg - eventualBG ) / ( profile.dia * 60 / 5 ) )*10)/10; - //console.log("expectedDelta: " + expectedDelta); - - if (typeof eventualBG === 'undefined' || isNaN(eventualBG)) { - rT.error ='Error: could not calculate eventualBG'; - return rT; - } - - // min_bg of 90 -> threshold of 70, 110 -> 80, and 130 -> 90 - var threshold = profile.min_bg - 0.5*(profile.min_bg-50); - - rT = { - 'temp': 'absolute' - , 'bg': bg - , 'tick': tick - , 'eventualBG': eventualBG - , 'snoozeBG': snoozeBG - }; - - var basaliob; - if (iob_data.basaliob) { basaliob = iob_data.basaliob; } - else { basaliob = iob_data.iob - iob_data.bolussnooze; } - // allow meal assist to run when carbs are just barely covered - if (minDelta > Math.max(3, bgi) && ( (meal_data.carbs > 0 && (1.1 * meal_data.carbs/profile.carb_ratio > meal_data.boluses + basaliob)) || ( deviation > 25 && minDelta > 7 ) ) ) { - // ignore all covered IOB, and just set eventualBG to the current bg - eventualBG = Math.max(bg,eventualBG) + deviation; - rT.eventualBG = eventualBG; - profile.min_bg = 80; - target_bg = (profile.min_bg + profile.max_bg) / 2; - expectedDelta = Math.round(( bgi + ( target_bg - eventualBG ) / ( profile.dia * 60 / 5 ) )*10)/10; - rT.mealAssist = "On: Carbs: " + meal_data.carbs + " Boluses: " + meal_data.boluses + " Target: " + target_bg + " Deviation: " + deviation + " BGI: " + bgi; - } else { - rT.mealAssist = "Off: Carbs: " + meal_data.carbs + " Boluses: " + meal_data.boluses + " Target: " + target_bg + " Deviation: " + deviation + " BGI: " + bgi; - } - if (bg < threshold) { // low glucose suspend mode: BG is < ~80 - rT.reason = "BG " + bg + "<" + threshold; - if ((glucose_status.delta <= 0 && glucose_status.avgdelta <= 0) || (glucose_status.delta < expectedDelta && glucose_status.avgdelta < expectedDelta)) { - // BG is still falling / rising slower than predicted - return setTempBasal(0, 30, profile, rT, offline); - } - if (glucose_status.delta > glucose_status.avgdelta) { - rT.reason += ", delta " + glucose_status.delta + ">0"; - } else { - rT.reason += ", avg delta " + glucose_status.avgdelta.toFixed(2) + ">0"; - } - if (currenttemp.rate > profile.current_basal) { // if a high-temp is running - rT.reason += ", cancel high temp"; - return setTempBasal(0, 0, profile, rT, offline); // cancel high temp - } else if (currenttemp.duration && eventualBG > profile.max_bg) { // if low-temped and predicted to go high from negative IOB - rT.reason += ", cancel low temp"; - return setTempBasal(0, 0, profile, rT, offline); // cancel low temp - } - rT.reason += "; no high-temp to cancel"; - return rT; - } - if (eventualBG < profile.min_bg) { // if eventual BG is below target: - if (rT.mealAssist.indexOf("On") == 0) { - rT.reason = "Meal assist: " + meal_data.carbs + "g, " + meal_data.boluses + "U"; - } else { - rT.reason = "Eventual BG " + eventualBG + "<" + profile.min_bg; - // if 5m or 15m avg BG is rising faster than expected delta - if (minDelta > expectedDelta && minDelta > 0) { - if (glucose_status.delta > glucose_status.avgdelta) { - rT.reason += ", but Delta " + tick + " > Exp. Delta " + expectedDelta; - } else { - rT.reason += ", but Avg. Delta " + glucose_status.avgdelta.toFixed(2) + " > Exp. Delta " + expectedDelta; - } - if (currenttemp.duration > 0) { // if there is currently any temp basal running - rT.reason = rT.reason += "; cancel"; - return setTempBasal(0, 0, profile, rT, offline); // cancel temp - } else { - rT.reason = rT.reason += "; no temp to cancel"; - return rT; - } - } - } - - if (eventualBG < profile.min_bg) { - // if this is just due to boluses, we can snooze until the bolus IOB decays (at double speed) - if (snoozeBG > profile.min_bg) { // if adding back in the bolus contribution BG would be above min - // if BG is falling and high-temped, or rising and low-temped, cancel - // compare against zero here, not BGI, because BGI will be highly negative from boluses and no carbs - if (glucose_status.delta < 0 && currenttemp.duration > 0 && currenttemp.rate > profile.current_basal) { - rT.reason += tick + ", and temp " + currenttemp.rate + " > basal " + profile.current_basal; - return setTempBasal(0, 0, profile, rT, offline); // cancel temp - } else if (glucose_status.delta > 0 && currenttemp.duration > 0 && currenttemp.rate < profile.current_basal) { - rT.reason += tick + ", and temp " + currenttemp.rate + " < basal " + profile.current_basal; - return setTempBasal(0, 0, profile, rT, offline); // cancel temp - } - - rT.reason += ", bolus snooze: eventual BG range " + eventualBG + "-" + snoozeBG; - return rT; - } else { - // 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) / profile.sens); - if (minDelta < 0 && minDelta > expectedDelta) { - // if we're barely falling, newinsulinReq should be barely negative - rT.reason += ", Snooze BG " + snoozeBG; - var newinsulinReq = Math.round(( insulinReq * (minDelta / expectedDelta) ) * 100)/100; - //console.log("Increasing insulinReq from " + insulinReq + " to " + newinsulinReq); - insulinReq = newinsulinReq; - } - // rate required to deliver insulinReq less insulin over 30m: - var rate = profile.current_basal + (2 * insulinReq); - rate = Math.round( rate * 1000 ) / 1000; - // if required temp < existing temp basal - var insulinScheduled = currenttemp.duration * (currenttemp.rate - profile.current_basal) / 60; - if (insulinScheduled < insulinReq - 0.2) { // if current temp would deliver >0.2U less than the required insulin, raise the rate - rT.reason = currenttemp.duration + "m@" + (currenttemp.rate - profile.current_basal).toFixed(3) + " = " + insulinScheduled.toFixed(3) + " < req " + insulinReq + "-0.2U"; - return setTempBasal(rate, 30, profile, rT, offline); - } - if (typeof currenttemp.rate !== 'undefined' && (currenttemp.duration > 5 && rate > currenttemp.rate - 0.1)) { - rT.reason += ", temp " + currenttemp.rate + " ~< req " + rate + "U/hr"; - return rT; - } else { - rT.reason += ", setting " + rate + "U/hr"; - return setTempBasal(rate, 30, profile, rT, offline); - } - } - } - } - - // if eventual BG is above min but BG is falling faster than expected Delta - if (minDelta < expectedDelta) { - if (glucose_status.delta < glucose_status.avgdelta) { - rT.reason = "Eventual BG " + eventualBG + ">" + profile.min_bg + " but Delta " + tick + " < Exp. Delta " + expectedDelta; - } else { - rT.reason = "Eventual BG " + eventualBG + ">" + profile.min_bg + " but Avg. Delta " + glucose_status.avgdelta.toFixed(2) + " < Exp. Delta " + expectedDelta; - } - if (currenttemp.duration > 0) { // if there is currently any temp basal running - rT.reason = rT.reason += "; cancel"; - return setTempBasal(0, 0, profile, rT, offline); // cancel temp - } else { - rT.reason = rT.reason += "; no temp to cancel"; - return rT; - } - } - - if (eventualBG < profile.max_bg) { - rT.reason = eventualBG + " is in range. No temp required"; - if (currenttemp.duration > 0) { // if there is currently any temp basal running - rT.reason = rT.reason += "; cancel"; - return setTempBasal(0, 0, profile, rT, offline); // cancel temp - } - if (offline == 'Offline') { - // if no temp is running or required, set the current basal as a temp, so you can see on the pump that the loop is working - if ((!currenttemp.duration || (currenttemp.rate == profile.current_basal)) && !rT.duration) { - rT.reason = rT.reason + "; setting current basal of " + profile.current_basal + " as temp"; - return setTempBasal(profile.current_basal, 30, profile, rT, offline); - } - } - return rT; - } - - if (snoozeBG < profile.max_bg) { - rT.reason = snoozeBG + " < " + profile.max_bg; - if (currenttemp.duration > 0) { // if there is currently any temp basal running - rT.reason = rT.reason += "; cancel"; - return setTempBasal(0, 0, profile, rT, offline); // cancel temp - } else { - rT.reason = rT.reason += "; no temp to cancel"; - return rT; - } - } - - - // eventual BG is at/above target: - // if iob is over max, just cancel any temps - var basaliob; - if (iob_data.basaliob) { basaliob = iob_data.basaliob; } - else { basaliob = iob_data.iob - iob_data.bolussnooze; } - rT.reason = "Eventual BG " + eventualBG + ">=" + profile.max_bg + ", "; - if (basaliob > max_iob) { - rT.reason = "basaliob " + basaliob + " > max_iob " + max_iob; - return setTempBasal(0, 0, profile, rT, offline); - } else { // otherwise, calculate 30m high-temp required to get projected BG down to target - - // insulinReq is the additional insulin required to get down to max bg: - // if in meal assist mode, check if snoozeBG is lower, as eventualBG is not dependent on IOB - var insulinReq = (Math.min(snoozeBG,eventualBG) - target_bg) / profile.sens; - if (minDelta < 0 && minDelta > expectedDelta) { - var newinsulinReq = Math.round(( insulinReq * (1 - (minDelta / expectedDelta)) ) * 100)/100; - //console.log("Reducing insulinReq from " + insulinReq + " to " + newinsulinReq); - insulinReq = newinsulinReq; - } - // if that would put us over max_iob, then reduce accordingly - if (insulinReq > max_iob-basaliob) { - rT.reason = "max_iob " + max_iob + ", "; - insulinReq = max_iob-basaliob; - } - - // rate required to deliver insulinReq more insulin over 30m: - var rate = profile.current_basal + (2 * insulinReq); - rate = Math.round( rate * 1000 ) / 1000; - - var maxSafeBasal = Math.min(profile.max_basal, 3 * profile.max_daily_basal, 4 * profile.current_basal); - if (rate > maxSafeBasal) { - rT.reason += "adj. req. rate:"+rate.toFixed(1) +" to maxSafeBasal:"+maxSafeBasal.toFixed(1)+", "; - rate = maxSafeBasal; - } - - var insulinScheduled = currenttemp.duration * (currenttemp.rate - profile.current_basal) / 60; - if (insulinScheduled > insulinReq + 0.2) { // if current temp would deliver >0.2U more than the required insulin, lower the rate - rT.reason = currenttemp.duration + "m@" + (currenttemp.rate - profile.current_basal).toFixed(3) + " = " + insulinScheduled.toFixed(3) + " > req " + insulinReq + "+0.2U"; - return setTempBasal(rate, 30, profile, rT, offline); - } - - if (typeof currenttemp.duration == 'undefined' || currenttemp.duration == 0) { // no temp is set - rT.reason += "no temp, setting " + rate + "U/hr"; - return setTempBasal(rate, 30, profile, rT, offline); - } - - if (currenttemp.duration > 5 && rate < currenttemp.rate + 0.1) { // if required temp <~ existing temp basal - rT.reason += "temp " + currenttemp.rate + " >~ req " + rate + "U/hr"; - return rT; - } - - // required temp > existing temp basal - rT.reason += "temp " + currenttemp.rate + "<" + rate + "U/hr"; - return setTempBasal(rate, 30, profile, rT, offline); - } - -}; - -module.exports = determine_basal; \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt index fd8487f8d1..c515dc942b 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt @@ -19,7 +19,6 @@ import info.nightscout.androidaps.events.EventRebuildTabs import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin import info.nightscout.androidaps.plugins.aps.openAPSAMA.OpenAPSAMAPlugin -import info.nightscout.androidaps.plugins.aps.openAPSMA.OpenAPSMAPlugin import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBPlugin import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.configBuilder.PluginStore @@ -80,7 +79,6 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang @Inject lateinit var medtronicPumpPlugin: MedtronicPumpPlugin @Inject lateinit var nsClientPlugin: NSClientPlugin @Inject lateinit var openAPSAMAPlugin: OpenAPSAMAPlugin - @Inject lateinit var openAPSMAPlugin: OpenAPSMAPlugin @Inject lateinit var openAPSSMBPlugin: OpenAPSSMBPlugin @Inject lateinit var safetyPlugin: SafetyPlugin @Inject lateinit var sensitivityAAPSPlugin: SensitivityAAPSPlugin @@ -161,7 +159,6 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang addPreferencesFromResourceIfEnabled(glimpPlugin, rootKey) addPreferencesFromResourceIfEnabled(careportalPlugin, rootKey) addPreferencesFromResourceIfEnabled(loopPlugin, rootKey, Config.APS) - addPreferencesFromResourceIfEnabled(openAPSMAPlugin, rootKey, Config.APS) addPreferencesFromResourceIfEnabled(openAPSAMAPlugin, rootKey, Config.APS) addPreferencesFromResourceIfEnabled(openAPSSMBPlugin, rootKey, Config.APS) addPreferencesFromResourceIfEnabled(sensitivityAAPSPlugin, rootKey) 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/db/DatabaseHelper.java b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java index 0d65d80f6b..661eaea15f 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java +++ b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java @@ -234,7 +234,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { new java.util.TimerTask() { @Override public void run() { - RxBus.Companion.getINSTANCE().send(new EventRefreshOverview("resetDatabases")); + RxBus.Companion.getINSTANCE().send(new EventRefreshOverview("resetDatabases", false)); } }, 3000 diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt index 68c95efe66..392056e8c8 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt @@ -12,8 +12,7 @@ import info.nightscout.androidaps.db.BgReading import info.nightscout.androidaps.db.ProfileSwitch import info.nightscout.androidaps.plugins.aps.loop.APSResult import info.nightscout.androidaps.plugins.aps.openAPSAMA.DetermineBasalResultAMA -import info.nightscout.androidaps.plugins.aps.openAPSMA.DetermineBasalResultMA -import info.nightscout.androidaps.plugins.aps.openAPSMA.LoggerCallback +import info.nightscout.androidaps.plugins.aps.logger.LoggerCallback import info.nightscout.androidaps.plugins.aps.openAPSSMB.DetermineBasalAdapterSMBJS import info.nightscout.androidaps.plugins.aps.openAPSSMB.DetermineBasalResultSMB import info.nightscout.androidaps.plugins.constraints.objectives.objectives.* @@ -55,7 +54,6 @@ interface AppComponent : AndroidInjector { fun injectPumpEnactResult(pumpEnactResult: PumpEnactResult) fun injectAPSResult(apsResult: APSResult) fun injectDetermineBasalResultSMB(determineBasalResultSMB: DetermineBasalResultSMB) - fun injectDetermineBasalResultMA(determineBasalResultMA: DetermineBasalResultMA) fun injectDetermineBasalResultAMA(determineBasalResultAMA: DetermineBasalResultAMA) fun injectDetermineBasalAdapterSMBJS(determineBasalAdapterSMBJS: DetermineBasalAdapterSMBJS) diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt index 933873bc5e..4eb2708a3f 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt @@ -22,8 +22,7 @@ import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLoggerProduction import info.nightscout.androidaps.plugins.aps.loop.APSResult import info.nightscout.androidaps.plugins.aps.openAPSAMA.DetermineBasalResultAMA -import info.nightscout.androidaps.plugins.aps.openAPSMA.DetermineBasalResultMA -import info.nightscout.androidaps.plugins.aps.openAPSMA.LoggerCallback +import info.nightscout.androidaps.plugins.aps.logger.LoggerCallback import info.nightscout.androidaps.plugins.aps.openAPSSMB.DetermineBasalAdapterSMBJS import info.nightscout.androidaps.plugins.aps.openAPSSMB.DetermineBasalResultSMB import info.nightscout.androidaps.plugins.configBuilder.PluginStore @@ -50,6 +49,7 @@ import info.nightscout.androidaps.queue.commands.* import info.nightscout.androidaps.setupwizard.SWEventListener import info.nightscout.androidaps.setupwizard.SWScreen import info.nightscout.androidaps.setupwizard.elements.* +import info.nightscout.androidaps.utils.CryptoUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelperImplementation @@ -121,7 +121,6 @@ open class AppModule { @ContributesAndroidInjector fun apsResultInjector(): APSResult @ContributesAndroidInjector fun determineBasalResultSMBInjector(): DetermineBasalResultSMB - @ContributesAndroidInjector fun determineBasalResultMAInjector(): DetermineBasalResultMA @ContributesAndroidInjector fun determineBasalResultAMAInjector(): DetermineBasalResultAMA @ContributesAndroidInjector @@ -261,6 +260,7 @@ open class AppModule { @ContributesAndroidInjector fun graphDataInjector(): GraphData + @ContributesAndroidInjector fun cryptoUtilInjector(): CryptoUtil @ContributesAndroidInjector fun importExportPrefsInjector(): ImportExportPrefs @ContributesAndroidInjector fun encryptedPrefsFormatInjector(): EncryptedPrefsFormat @ContributesAndroidInjector fun classicPrefsFormatInjector(): ClassicPrefsFormat diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/FragmentsModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/FragmentsModule.kt index d151912151..164dd0def0 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/FragmentsModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/FragmentsModule.kt @@ -6,7 +6,6 @@ import info.nightscout.androidaps.activities.MyPreferenceFragment import info.nightscout.androidaps.dialogs.* import info.nightscout.androidaps.plugins.aps.loop.LoopFragment import info.nightscout.androidaps.plugins.aps.openAPSAMA.OpenAPSAMAFragment -import info.nightscout.androidaps.plugins.aps.openAPSMA.OpenAPSMAFragment import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBFragment import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderFragment import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesFragment @@ -63,7 +62,6 @@ abstract class FragmentsModule { @ContributesAndroidInjector abstract fun contributesLocalProfileFragment(): LocalProfileFragment @ContributesAndroidInjector abstract fun contributesObjectivesFragment(): ObjectivesFragment @ContributesAndroidInjector abstract fun contributesOpenAPSAMAFragment(): OpenAPSAMAFragment - @ContributesAndroidInjector abstract fun contributesOpenAPSMAFragment(): OpenAPSMAFragment @ContributesAndroidInjector abstract fun contributesOpenAPSSMBFragment(): OpenAPSSMBFragment @ContributesAndroidInjector abstract fun contributesOverviewFragment(): OverviewFragment @ContributesAndroidInjector abstract fun contributesLocalInsightFragment(): LocalInsightFragment 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 8e9fe08d87..6918e2d9e3 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PluginsModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PluginsModule.kt @@ -11,7 +11,6 @@ import info.nightscout.androidaps.Config import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin import info.nightscout.androidaps.plugins.aps.openAPSAMA.OpenAPSAMAPlugin -import info.nightscout.androidaps.plugins.aps.openAPSMA.OpenAPSMAPlugin import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBPlugin import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin import info.nightscout.androidaps.plugins.constraints.dstHelper.DstHelperPlugin @@ -177,12 +176,6 @@ abstract class PluginsModule { @IntKey(190) abstract fun bindLoopPlugin(plugin: LoopPlugin): PluginBase - @Binds - @APS - @IntoMap - @IntKey(200) - abstract fun bindOpenAPSMAPlugin(plugin: OpenAPSMAPlugin): PluginBase - @Binds @APS @IntoMap diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventRefreshOverview.kt b/app/src/main/java/info/nightscout/androidaps/events/EventRefreshOverview.kt index 533a25dd40..25b22d4e81 100644 --- a/app/src/main/java/info/nightscout/androidaps/events/EventRefreshOverview.kt +++ b/app/src/main/java/info/nightscout/androidaps/events/EventRefreshOverview.kt @@ -1,3 +1,3 @@ package info.nightscout.androidaps.events -class EventRefreshOverview(var from: String) : Event() +class EventRefreshOverview(var from: String, val now : Boolean = false) : Event() 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/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/openAPSMA/events/EventOpenAPSUpdateGui.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/events/EventOpenAPSUpdateGui.kt similarity index 62% rename from app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/events/EventOpenAPSUpdateGui.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/aps/events/EventOpenAPSUpdateGui.kt index 2b642c6880..004bdacf41 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/events/EventOpenAPSUpdateGui.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/events/EventOpenAPSUpdateGui.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.aps.openAPSMA.events +package info.nightscout.androidaps.plugins.aps.events import info.nightscout.androidaps.events.EventUpdateGui diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/events/EventOpenAPSUpdateResultGui.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/events/EventOpenAPSUpdateResultGui.kt similarity index 66% rename from app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/events/EventOpenAPSUpdateResultGui.kt rename to app/src/main/java/info/nightscout/androidaps/plugins/aps/events/EventOpenAPSUpdateResultGui.kt index 4ba02b8755..893309c9ba 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/events/EventOpenAPSUpdateResultGui.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/events/EventOpenAPSUpdateResultGui.kt @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.aps.openAPSMA.events +package info.nightscout.androidaps.plugins.aps.events import info.nightscout.androidaps.events.EventUpdateGui diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/LoggerCallback.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/logger/LoggerCallback.java similarity index 96% rename from app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/LoggerCallback.java rename to app/src/main/java/info/nightscout/androidaps/plugins/aps/logger/LoggerCallback.java index d7e0c44d42..815cf2fa24 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/LoggerCallback.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/logger/LoggerCallback.java @@ -1,4 +1,4 @@ -package info.nightscout.androidaps.plugins.aps.openAPSMA; +package info.nightscout.androidaps.plugins.aps.logger; import org.mozilla.javascript.ScriptableObject; 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/aps/openAPSAMA/DetermineBasalAdapterAMAJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.java index 5606014c0f..50a6c6e018 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.java @@ -29,7 +29,7 @@ import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.logging.AAPSLogger; import info.nightscout.androidaps.logging.LTag; import info.nightscout.androidaps.plugins.aps.loop.ScriptReader; -import info.nightscout.androidaps.plugins.aps.openAPSMA.LoggerCallback; +import info.nightscout.androidaps.plugins.aps.logger.LoggerCallback; import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults; import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAFragment.kt index 6bf087417b..342a1395f5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAFragment.kt @@ -9,8 +9,8 @@ import dagger.android.support.DaggerFragment import info.nightscout.androidaps.R import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag -import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateGui -import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateResultGui +import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui +import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateResultGui import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.java index bc80b755fa..cea65a14d9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.java @@ -23,8 +23,8 @@ 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.ScriptReader; -import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateGui; -import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateResultGui; +import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui; +import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateResultGui; import info.nightscout.androidaps.plugins.bus.RxBusWrapper; import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/DetermineBasalAdapterMAJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/DetermineBasalAdapterMAJS.java deleted file mode 100644 index c7733d4eb7..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/DetermineBasalAdapterMAJS.java +++ /dev/null @@ -1,232 +0,0 @@ -package info.nightscout.androidaps.plugins.aps.openAPSMA; - -import org.json.JSONException; -import org.json.JSONObject; -import org.mozilla.javascript.Context; -import org.mozilla.javascript.Function; -import org.mozilla.javascript.NativeJSON; -import org.mozilla.javascript.NativeObject; -import org.mozilla.javascript.RhinoException; -import org.mozilla.javascript.Scriptable; -import org.mozilla.javascript.ScriptableObject; - -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.nio.charset.StandardCharsets; - -import javax.annotation.Nullable; -import javax.inject.Inject; - -import dagger.android.HasAndroidInjector; -import info.nightscout.androidaps.Constants; -import info.nightscout.androidaps.R; -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.logging.AAPSLogger; -import info.nightscout.androidaps.logging.L; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.aps.loop.ScriptReader; -import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus; -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; -import info.nightscout.androidaps.utils.SP; - -public class DetermineBasalAdapterMAJS { - - private HasAndroidInjector injector; - @Inject AAPSLogger aapsLogger; - @Inject ProfileFunction profileFunction; - @Inject TreatmentsPlugin treatmentsPlugin; - - private ScriptReader mScriptReader; - private JSONObject mProfile; - private JSONObject mGlucoseStatus; - private JSONObject mIobData; - private JSONObject mMealData; - private JSONObject mCurrentTemp; - - private String storedCurrentTemp = null; - private String storedIobData = null; - private String storedGlucoseStatus = null; - private String storedProfile = null; - private String storedMeal_data = null; - - DetermineBasalAdapterMAJS(ScriptReader scriptReader, HasAndroidInjector injector) { - injector.androidInjector().inject(this); - mScriptReader = scriptReader; - this.injector = injector; - } - - @Nullable - public DetermineBasalResultMA invoke() { - DetermineBasalResultMA determineBasalResultMA = null; - - Context rhino = Context.enter(); - Scriptable scope = rhino.initStandardObjects(); - // Turn off optimization to make Rhino Android compatible - rhino.setOptimizationLevel(-1); - - try { - - //register logger callback for console.log and console.error - ScriptableObject.defineClass(scope, LoggerCallback.class); - Scriptable myLogger = rhino.newObject(scope, "LoggerCallback", null); - scope.put("console", scope, myLogger); - - //set module parent - rhino.evaluateString(scope, "var module = {\"parent\":Boolean(1)};", "JavaScript", 0, null); - - //generate functions "determine_basal" and "setTempBasal" - rhino.evaluateString(scope, readFile("OpenAPSMA/determine-basal.js"), "JavaScript", 0, null); - - String setTempBasalCode = "var setTempBasal = function (rate, duration, profile, rT, offline) {" + - "rT.duration = duration;\n" + - " rT.rate = rate;" + - "return rT;" + - "};"; - rhino.evaluateString(scope, setTempBasalCode, "setTempBasal.js", 0, null); - Object determineBasalObj = scope.get("determine_basal", scope); - Object setTempBasalObj = scope.get("setTempBasal", scope); - - //call determine-basal - if (determineBasalObj instanceof Function && setTempBasalObj instanceof Function) { - Function determineBasalJS = (Function) determineBasalObj; - Function setTempBasalJS = (Function) setTempBasalObj; - - //prepare parameters - Object[] params = new Object[]{ - makeParam(mGlucoseStatus, rhino, scope), - makeParam(mCurrentTemp, rhino, scope), - makeParam(mIobData, rhino, scope), - makeParam(mProfile, rhino, scope), - "undefined", - makeParam(mMealData, rhino, scope), - setTempBasalJS}; - - NativeObject jsResult = (NativeObject) determineBasalJS.call(rhino, scope, scope, params); - - // Parse the jsResult object to a JSON-String - String result = NativeJSON.stringify(rhino, scope, jsResult, null, null).toString(); - aapsLogger.debug(LTag.APS, "Result: " + result); - try { - determineBasalResultMA = new DetermineBasalResultMA(injector, jsResult, new JSONObject(result)); - } catch (JSONException e) { - aapsLogger.error(LTag.APS, "Unhandled exception", e); - } - } else { - aapsLogger.debug(LTag.APS, "Problem loading JS Functions"); - } - } catch (IOException e) { - aapsLogger.error(LTag.APS, "IOException"); - } catch (RhinoException e) { - aapsLogger.error(LTag.APS, "RhinoException: (" + e.lineNumber() + "," + e.columnNumber() + ") " + e.toString()); - } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) { - aapsLogger.error(LTag.APS, e.toString()); - } finally { - Context.exit(); - } - - storedGlucoseStatus = mGlucoseStatus.toString(); - storedIobData = mIobData.toString(); - storedCurrentTemp = mCurrentTemp.toString(); - storedProfile = mProfile.toString(); - storedMeal_data = mMealData.toString(); - - return determineBasalResultMA; - } - - String getGlucoseStatusParam() { - return storedGlucoseStatus; - } - - String getCurrentTempParam() { - return storedCurrentTemp; - } - - String getIobDataParam() { - return storedIobData; - } - - String getProfileParam() { - return storedProfile; - } - - String getMealDataParam() { - return storedMeal_data; - } - - public void setData(Profile profile, - double maxIob, - double maxBasal, - double minBg, - double maxBg, - double targetBg, - double basalRate, - IobTotal iobData, - GlucoseStatus glucoseStatus, - MealData mealData) throws JSONException { - - mProfile = new JSONObject(); - mProfile.put("max_iob", maxIob); - mProfile.put("dia", Math.min(profile.getDia(), 3d)); - mProfile.put("type", "current"); - mProfile.put("max_daily_basal", profile.getMaxDailyBasal()); - mProfile.put("max_basal", maxBasal); - mProfile.put("min_bg", minBg); - mProfile.put("max_bg", maxBg); - mProfile.put("target_bg", targetBg); - mProfile.put("carb_ratio", profile.getIc()); - mProfile.put("sens", profile.getIsfMgdl()); - - mProfile.put("current_basal", basalRate); - - if (profileFunction.getUnits().equals(Constants.MMOL)) { - mProfile.put("out_units", "mmol/L"); - } - - long now = System.currentTimeMillis(); - TemporaryBasal tb = treatmentsPlugin.getTempBasalFromHistory(now); - - mCurrentTemp = new JSONObject(); - mCurrentTemp.put("duration", tb != null ? tb.getPlannedRemainingMinutes() : 0); - mCurrentTemp.put("rate", tb != null ? tb.tempBasalConvertedToAbsolute(now, profile) : 0d); - - mIobData = new JSONObject(); - mIobData.put("iob", iobData.iob); //netIob - mIobData.put("activity", iobData.activity); //netActivity - mIobData.put("bolussnooze", iobData.bolussnooze); //bolusIob - mIobData.put("basaliob", iobData.basaliob); - mIobData.put("netbasalinsulin", iobData.netbasalinsulin); - mIobData.put("hightempinsulin", iobData.hightempinsulin); - - mGlucoseStatus = new JSONObject(); - mGlucoseStatus.put("glucose", glucoseStatus.glucose); - if (SP.getBoolean(R.string.key_always_use_shortavg, false)) { - mGlucoseStatus.put("delta", glucoseStatus.short_avgdelta); - } else { - mGlucoseStatus.put("delta", glucoseStatus.delta); - } - mGlucoseStatus.put("avgdelta", glucoseStatus.avgdelta); - - mMealData = new JSONObject(); - mMealData.put("carbs", mealData.carbs); - mMealData.put("boluses", mealData.boluses); - } - - private String readFile(String filename) throws IOException { - byte[] bytes = mScriptReader.readFile(filename); - String string = new String(bytes, StandardCharsets.UTF_8); - if (string.startsWith("#!/usr/bin/env node")) { - string = string.substring(20); - } - return string; - } - - private Object makeParam(JSONObject jsonObject, Context rhino, Scriptable scope) { - Object param = NativeJSON.parse(rhino, scope, jsonObject.toString(), (context, scriptable, scriptable1, objects) -> objects[1]); - return param; - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/DetermineBasalResultMA.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/DetermineBasalResultMA.java deleted file mode 100644 index 298b313540..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/DetermineBasalResultMA.java +++ /dev/null @@ -1,79 +0,0 @@ -package info.nightscout.androidaps.plugins.aps.openAPSMA; - -import org.json.JSONException; -import org.json.JSONObject; -import org.mozilla.javascript.NativeObject; - -import dagger.android.HasAndroidInjector; -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.aps.loop.APSResult; - -public class DetermineBasalResultMA extends APSResult { - private AAPSLogger aapsLogger; - - private double eventualBG; - private double snoozeBG; - private String mealAssist; - - DetermineBasalResultMA(HasAndroidInjector injector, NativeObject result, JSONObject j) { - this(injector); - json = j; - if (result.containsKey("error")) { - reason = (String) result.get("error"); - tempBasalRequested = false; - rate = -1; - duration = -1; - mealAssist = ""; - } else { - reason = result.get("reason").toString(); - eventualBG = (Double) result.get("eventualBG"); - snoozeBG = (Double) result.get("snoozeBG"); - if (result.containsKey("rate")) { - rate = (Double) result.get("rate"); - if (rate < 0d) rate = 0d; - tempBasalRequested = true; - } else { - rate = -1; - tempBasalRequested = false; - } - if (result.containsKey("duration")) { - duration = ((Double) result.get("duration")).intValue(); - //changeRequested as above - } else { - duration = -1; - tempBasalRequested = false; - } - if (result.containsKey("mealAssist")) { - mealAssist = result.get("mealAssist").toString(); - } else mealAssist = ""; - } - } - - private DetermineBasalResultMA(HasAndroidInjector injector) { - super(injector); - } - - @Override - public DetermineBasalResultMA newAndClone(HasAndroidInjector injector) { - DetermineBasalResultMA newResult = new DetermineBasalResultMA(injector); - doClone(newResult); - - newResult.eventualBG = eventualBG; - newResult.snoozeBG = snoozeBG; - newResult.mealAssist = mealAssist; - return newResult; - } - - @Override - public JSONObject json() { - try { - JSONObject ret = new JSONObject(this.json.toString()); - return ret; - } catch (JSONException e) { - aapsLogger.error(LTag.APS, "Unhandled exception", e); - } - return null; - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/OpenAPSMAFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/OpenAPSMAFragment.kt deleted file mode 100644 index 5a92764b46..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/OpenAPSMAFragment.kt +++ /dev/null @@ -1,98 +0,0 @@ -package info.nightscout.androidaps.plugins.aps.openAPSMA - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import dagger.android.support.DaggerFragment -import info.nightscout.androidaps.R -import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateGui -import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateResultGui -import info.nightscout.androidaps.plugins.bus.RxBusWrapper -import info.nightscout.androidaps.utils.DateUtil -import info.nightscout.androidaps.utils.FabricPrivacy -import info.nightscout.androidaps.utils.JSONFormatter -import info.nightscout.androidaps.utils.extensions.plusAssign -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.CompositeDisposable -import kotlinx.android.synthetic.main.openapsama_fragment.* -import javax.inject.Inject - -class OpenAPSMAFragment : DaggerFragment() { - private var disposable: CompositeDisposable = CompositeDisposable() - - @Inject lateinit var rxBus: RxBusWrapper - @Inject lateinit var fabricPrivacy: FabricPrivacy - @Inject lateinit var openAPSMAPlugin: OpenAPSMAPlugin - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.openapsma_fragment, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - openapsma_run.setOnClickListener { - openAPSMAPlugin.invoke("OpenAPSMA button", false) - } - - } - - @Synchronized - override fun onResume() { - super.onResume() - - disposable += rxBus - .toObservable(EventOpenAPSUpdateGui::class.java) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ - updateGUI() - }, { fabricPrivacy.logException(it) }) - disposable += rxBus - .toObservable(EventOpenAPSUpdateResultGui::class.java) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ - updateResultGUI(it.text) - }, { fabricPrivacy.logException(it) }) - updateGUI() - } - - @Synchronized - override fun onPause() { - super.onPause() - disposable.clear() - } - - @Synchronized - private fun updateGUI() { - if (openapsma_result == null) return - openAPSMAPlugin.lastAPSResult?.let { lastAPSResult -> - openapsma_result.text = JSONFormatter.format(lastAPSResult.json) - openapsma_request.text = lastAPSResult.toSpanned() - } - openAPSMAPlugin.lastDetermineBasalAdapterMAJS?.let { determineBasalAdapterMAJS -> - openapsma_glucosestatus.text = JSONFormatter.format(determineBasalAdapterMAJS.glucoseStatusParam) - openapsma_currenttemp.text = JSONFormatter.format(determineBasalAdapterMAJS.currentTempParam) - openapsma_iobdata.text = JSONFormatter.format(determineBasalAdapterMAJS.iobDataParam) - openapsma_profile.text = JSONFormatter.format(determineBasalAdapterMAJS.profileParam) - openapsma_mealdata.text = JSONFormatter.format(determineBasalAdapterMAJS.mealDataParam) - } - if (openAPSMAPlugin.lastAPSRun != 0L) { - openapsma_lastrun.text = DateUtil.dateAndTimeString(openAPSMAPlugin.lastAPSRun) - } - } - - @Synchronized - private fun updateResultGUI(text: String) { - if (openapsma_result == null) return - openapsma_result.text = text - openapsma_glucosestatus.text = "" - openapsma_currenttemp.text = "" - openapsma_iobdata.text = "" - openapsma_profile.text = "" - openapsma_mealdata.text = "" - openapsma_request.text = "" - openapsma_lastrun.text = "" - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/OpenAPSMAPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/OpenAPSMAPlugin.java deleted file mode 100644 index a1b8c24d02..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSMA/OpenAPSMAPlugin.java +++ /dev/null @@ -1,232 +0,0 @@ -package info.nightscout.androidaps.plugins.aps.openAPSMA; - -import android.content.Context; - -import org.json.JSONException; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import dagger.android.HasAndroidInjector; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.IobTotal; -import info.nightscout.androidaps.data.MealData; -import info.nightscout.androidaps.data.Profile; -import info.nightscout.androidaps.db.TempTarget; -import info.nightscout.androidaps.interfaces.APSInterface; -import info.nightscout.androidaps.interfaces.ActivePluginProvider; -import info.nightscout.androidaps.interfaces.PluginBase; -import info.nightscout.androidaps.interfaces.PluginDescription; -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.ScriptReader; -import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateGui; -import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateResultGui; -import info.nightscout.androidaps.plugins.bus.RxBusWrapper; -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.plugins.treatments.TreatmentsPlugin; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.FabricPrivacy; -import info.nightscout.androidaps.utils.HardLimits; -import info.nightscout.androidaps.utils.Profiler; -import info.nightscout.androidaps.utils.Round; -import info.nightscout.androidaps.utils.resources.ResourceHelper; - -@Singleton -public class OpenAPSMAPlugin extends PluginBase implements APSInterface { - private final RxBusWrapper rxBus; - private final ConstraintChecker constraintChecker; - private final ResourceHelper resourceHelper; - private final ProfileFunction profileFunction; - private final Context context; - private final ActivePluginProvider activePlugin; - private final TreatmentsPlugin treatmentsPlugin; - private final IobCobCalculatorPlugin iobCobCalculatorPlugin; - private final HardLimits hardLimits; - - // last values - DetermineBasalAdapterMAJS lastDetermineBasalAdapterMAJS = null; - long lastAPSRun = 0; - DetermineBasalResultMA lastAPSResult = null; - - @Inject - public OpenAPSMAPlugin( - HasAndroidInjector injector, - AAPSLogger aapsLogger, - RxBusWrapper rxBus, - ConstraintChecker constraintChecker, - ResourceHelper resourceHelper, - ProfileFunction profileFunction, - Context context, - ActivePluginProvider activePlugin, - TreatmentsPlugin treatmentsPlugin, - IobCobCalculatorPlugin iobCobCalculatorPlugin, - HardLimits hardLimits - ) { - super(new PluginDescription() - .mainType(PluginType.APS) - .fragmentClass(OpenAPSMAFragment.class.getName()) - .pluginName(R.string.openapsma) - .shortName(R.string.oaps_shortname) - .preferencesId(R.xml.pref_openapsma) - .description(R.string.description_ma), - aapsLogger, resourceHelper, injector - ); - - this.constraintChecker = constraintChecker; - this.resourceHelper = resourceHelper; - this.profileFunction = profileFunction; - this.context = context; - this.rxBus = rxBus; - this.activePlugin = activePlugin; - this.treatmentsPlugin = treatmentsPlugin; - this.iobCobCalculatorPlugin = iobCobCalculatorPlugin; - this.hardLimits = hardLimits; - } - - @Override - public boolean specialEnableCondition() { - try { - PumpInterface pump = activePlugin.getActivePump(); - return pump.getPumpDescription().isTempBasalCapable; - } catch (Exception ignored) { - // may fail during initialization - return true; - } - } - - @Override - public boolean specialShowInListCondition() { - PumpInterface pump = activePlugin.getActivePump(); - return pump.getPumpDescription().isTempBasalCapable; - } - - @Override - public APSResult getLastAPSResult() { - return lastAPSResult; - } - - @Override - public long getLastAPSRun() { - return lastAPSRun; - } - - @Override - public void invoke(String initiator, boolean tempBasalFallback) { - getAapsLogger().debug(LTag.APS, "invoke from " + initiator + " tempBasalFallback: " + tempBasalFallback); - lastAPSResult = null; - DetermineBasalAdapterMAJS determineBasalAdapterMAJS; - determineBasalAdapterMAJS = new DetermineBasalAdapterMAJS(new ScriptReader(context), getInjector()); - - GlucoseStatus glucoseStatus = new GlucoseStatus(getInjector()).getGlucoseStatusData(); - Profile profile = profileFunction.getProfile(); - PumpInterface pump = activePlugin.getActivePump(); - - if (profile == null) { - rxBus.send(new EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.noprofileselected))); - getAapsLogger().debug(LTag.APS, resourceHelper.gs(R.string.noprofileselected)); - return; - } - - if (!isEnabled(PluginType.APS)) { - rxBus.send(new EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openapsma_disabled))); - getAapsLogger().debug(LTag.APS, resourceHelper.gs(R.string.openapsma_disabled)); - return; - } - - if (glucoseStatus == null) { - rxBus.send(new EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openapsma_noglucosedata))); - getAapsLogger().debug(LTag.APS, resourceHelper.gs(R.string.openapsma_noglucosedata)); - return; - } - - double maxBasal = constraintChecker.getMaxBasalAllowed(profile).value(); - - double minBg = profile.getTargetLowMgdl(); - double maxBg = profile.getTargetHighMgdl(); - double targetBg = profile.getTargetMgdl(); - - minBg = Round.roundTo(minBg, 0.1d); - maxBg = Round.roundTo(maxBg, 0.1d); - - long start = System.currentTimeMillis(); - treatmentsPlugin.updateTotalIOBTreatments(); - treatmentsPlugin.updateTotalIOBTempBasals(); - IobTotal bolusIob = treatmentsPlugin.getLastCalculationTreatments(); - IobTotal basalIob = treatmentsPlugin.getLastCalculationTempBasals(); - - IobTotal iobTotal = IobTotal.combine(bolusIob, basalIob).round(); - - MealData mealData = iobCobCalculatorPlugin.getMealData(); - - double maxIob = constraintChecker.getMaxIOBAllowed().value(); - Profiler.log(getAapsLogger(), LTag.APS, "MA data gathering", start); - - minBg = hardLimits.verifyHardLimits(minBg, "minBg", hardLimits.getVERY_HARD_LIMIT_MIN_BG()[0], hardLimits.getVERY_HARD_LIMIT_MIN_BG()[1]); - maxBg = hardLimits.verifyHardLimits(maxBg, "maxBg", hardLimits.getVERY_HARD_LIMIT_MAX_BG()[0], hardLimits.getVERY_HARD_LIMIT_MAX_BG()[1]); - targetBg = hardLimits.verifyHardLimits(targetBg, "targetBg", hardLimits.getVERY_HARD_LIMIT_TARGET_BG()[0], hardLimits.getVERY_HARD_LIMIT_TARGET_BG()[1]); - - TempTarget tempTarget = treatmentsPlugin.getTempTargetFromHistory(System.currentTimeMillis()); - if (tempTarget != null) { - minBg = hardLimits.verifyHardLimits(tempTarget.low, "minBg", hardLimits.getVERY_HARD_LIMIT_TEMP_MIN_BG()[0], hardLimits.getVERY_HARD_LIMIT_TEMP_MIN_BG()[1]); - maxBg = hardLimits.verifyHardLimits(tempTarget.high, "maxBg", hardLimits.getVERY_HARD_LIMIT_TEMP_MAX_BG()[0], hardLimits.getVERY_HARD_LIMIT_TEMP_MAX_BG()[1]); - targetBg = hardLimits.verifyHardLimits(tempTarget.target(), "targetBg", hardLimits.getVERY_HARD_LIMIT_TEMP_TARGET_BG()[0], hardLimits.getVERY_HARD_LIMIT_TEMP_TARGET_BG()[1]); - } - - if (!hardLimits.checkOnlyHardLimits(profile.getDia(), "dia", hardLimits.getMINDIA(), hardLimits.getMAXDIA())) - return; - if (!hardLimits.checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), "carbratio", hardLimits.getMINIC(), hardLimits.getMAXIC())) - return; - if (!hardLimits.checkOnlyHardLimits(profile.getIsfMgdl(), "sens", hardLimits.getMINISF(), hardLimits.getMAXISF())) - return; - if (!hardLimits.checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.02, hardLimits.maxBasal())) - return; - if (!hardLimits.checkOnlyHardLimits(pump.getBaseBasalRate(), "current_basal", 0.01, hardLimits.maxBasal())) - return; - - start = System.currentTimeMillis(); - try { - determineBasalAdapterMAJS.setData(profile, maxIob, maxBasal, minBg, maxBg, targetBg, activePlugin.getActivePump().getBaseBasalRate(), iobTotal, glucoseStatus, mealData); - } catch (JSONException e) { - FabricPrivacy.getInstance().logException(e); - return; - } - Profiler.log(getAapsLogger(), LTag.APS, "MA calculation", start); - - - long now = System.currentTimeMillis(); - - DetermineBasalResultMA determineBasalResultMA = determineBasalAdapterMAJS.invoke(); - if (determineBasalResultMA == null) { - getAapsLogger().error(LTag.APS, "MA calculation returned null"); - lastDetermineBasalAdapterMAJS = null; - lastAPSResult = null; - lastAPSRun = 0; - } else { - // Fix bug determine basal - if (determineBasalResultMA.rate == 0d && determineBasalResultMA.duration == 0 && !treatmentsPlugin.isTempBasalInProgress()) - determineBasalResultMA.tempBasalRequested = false; - - determineBasalResultMA.iob = iobTotal; - - try { - determineBasalResultMA.json.put("timestamp", DateUtil.toISOString(now)); - } catch (JSONException e) { - getAapsLogger().error(LTag.APS, "Unhandled exception", e); - } - - lastDetermineBasalAdapterMAJS = determineBasalAdapterMAJS; - lastAPSResult = determineBasalResultMA; - lastAPSRun = now; - } - rxBus.send(new EventOpenAPSUpdateGui()); - } - - -} 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 af56162640..4d3a0db704 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 @@ -29,7 +29,7 @@ import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.logging.AAPSLogger; import info.nightscout.androidaps.logging.LTag; import info.nightscout.androidaps.plugins.aps.loop.ScriptReader; -import info.nightscout.androidaps.plugins.aps.openAPSMA.LoggerCallback; +import info.nightscout.androidaps.plugins.aps.logger.LoggerCallback; import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction; import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBFragment.kt index ec60365f2e..95b0f7f7b8 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBFragment.kt @@ -10,8 +10,8 @@ import dagger.android.support.DaggerFragment import info.nightscout.androidaps.R import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag -import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateGui -import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateResultGui +import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui +import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateResultGui import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.java index 6081849d59..51650891ee 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.java @@ -26,8 +26,8 @@ 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.ScriptReader; -import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateGui; -import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateResultGui; +import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui; +import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateResultGui; import info.nightscout.androidaps.plugins.bus.RxBusWrapper; import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker; import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/safety/SafetyPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/safety/SafetyPlugin.java index a344d8b56a..a32b7233e1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/safety/SafetyPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/safety/SafetyPlugin.java @@ -20,7 +20,6 @@ import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.logging.AAPSLogger; import info.nightscout.androidaps.plugins.aps.openAPSAMA.OpenAPSAMAPlugin; -import info.nightscout.androidaps.plugins.aps.openAPSMA.OpenAPSMAPlugin; import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBPlugin; import info.nightscout.androidaps.plugins.bus.RxBusWrapper; import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker; @@ -42,7 +41,6 @@ public class SafetyPlugin extends PluginBase implements ConstraintsInterface { private RxBusWrapper rxBus; private ConstraintChecker constraintChecker; private OpenAPSAMAPlugin openAPSAMAPlugin; - private OpenAPSMAPlugin openAPSMAPlugin; private OpenAPSSMBPlugin openAPSSMBPlugin; private SensitivityOref1Plugin sensitivityOref1Plugin; private ActivePluginProvider activePlugin; @@ -59,7 +57,6 @@ public class SafetyPlugin extends PluginBase implements ConstraintsInterface { RxBusWrapper rxBus, ConstraintChecker constraintChecker, OpenAPSAMAPlugin openAPSAMAPlugin, - OpenAPSMAPlugin openAPSMAPlugin, OpenAPSSMBPlugin openAPSSMBPlugin, SensitivityOref1Plugin sensitivityOref1Plugin, ActivePluginProvider activePlugin, @@ -80,7 +77,6 @@ public class SafetyPlugin extends PluginBase implements ConstraintsInterface { this.rxBus = rxBus; this.constraintChecker = constraintChecker; this.openAPSAMAPlugin = openAPSAMAPlugin; - this.openAPSMAPlugin = openAPSMAPlugin; this.openAPSSMBPlugin = openAPSSMBPlugin; this.sensitivityOref1Plugin = sensitivityOref1Plugin; this.activePlugin = activePlugin; @@ -276,8 +272,6 @@ public class SafetyPlugin extends PluginBase implements ConstraintsInterface { maxIobPref = sp.getDouble(R.string.key_openapsma_max_iob, 1.5d); maxIob.setIfSmaller(getAapsLogger(), maxIobPref, String.format(getResourceHelper().gs(R.string.limitingiob), maxIobPref, getResourceHelper().gs(R.string.maxvalueinpreferences)), this); - if (openAPSMAPlugin.isEnabled(PluginType.APS)) - maxIob.setIfSmaller(getAapsLogger(), hardLimits.maxIobAMA(), String.format(getResourceHelper().gs(R.string.limitingiob), hardLimits.maxIobAMA(), getResourceHelper().gs(R.string.hardlimit)), this); if (openAPSAMAPlugin.isEnabled(PluginType.APS)) maxIob.setIfSmaller(getAapsLogger(), hardLimits.maxIobAMA(), String.format(getResourceHelper().gs(R.string.limitingiob), hardLimits.maxIobAMA(), getResourceHelper().gs(R.string.hardlimit)), this); if (openAPSSMBPlugin.isEnabled(PluginType.APS)) 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 3e822177eb..d1ce90fc72 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 @@ -56,7 +56,7 @@ class VersionCheckerUtils @Inject constructor( } }.start() } else - aapsLogger.debug(LTag.CORE, "Github master version no checked. No connectivity") + aapsLogger.debug(LTag.CORE, "Github master version not checked. No connectivity") @Suppress("SameParameterValue") fun compareWithCurrentVersion(newVersion: String?, currentVersion: String) { 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..6380bd5fcf 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 @@ -22,7 +22,7 @@ import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin -import info.nightscout.androidaps.plugins.aps.openAPSMA.events.EventOpenAPSUpdateGui +import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus @@ -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/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/maintenance/formats/EncryptedPrefsFormat.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/formats/EncryptedPrefsFormat.kt index cec2ae9b5f..463b5a31cb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/formats/EncryptedPrefsFormat.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/maintenance/formats/EncryptedPrefsFormat.kt @@ -18,6 +18,7 @@ import javax.inject.Singleton @Singleton class EncryptedPrefsFormat @Inject constructor( private var resourceHelper: ResourceHelper, + private var cryptoUtil: CryptoUtil, private var storage: Storage ) : PrefsFormat { @@ -58,14 +59,14 @@ class EncryptedPrefsFormat @Inject constructor( var encodedContent = "" if (encrypted) { - val salt = CryptoUtil.mineSalt() + val salt = cryptoUtil.mineSalt() val rawContent = content.toString() - val contentAttempt = CryptoUtil.encrypt(masterPassword!!, salt, rawContent) + val contentAttempt = cryptoUtil.encrypt(masterPassword!!, salt, rawContent) if (contentAttempt != null) { encodedContent = contentAttempt security.put("algorithm", "v1") security.put("salt", salt.toHex()) - security.put("content_hash", CryptoUtil.sha256(rawContent)) + security.put("content_hash", cryptoUtil.sha256(rawContent)) } else { // fallback when encryption does not work encrypted = false @@ -80,7 +81,7 @@ class EncryptedPrefsFormat @Inject constructor( container.put("content", if (encrypted) encodedContent else content) var fileContents = container.toString(2) - val fileHash = CryptoUtil.hmac256(fileContents, KEY_CONSCIENCE) + val fileHash = cryptoUtil.hmac256(fileContents, KEY_CONSCIENCE) fileContents = fileContents.replace(Regex("(\\\"file_hash\\\"\\s*\\:\\s*\\\")(--to-be-calculated--)(\\\")"), "$1" + fileHash + "$3") @@ -102,7 +103,7 @@ class EncryptedPrefsFormat @Inject constructor( val jsonBody = storage.getFileContents(file) val fileContents = jsonBody.replace(Regex("(?is)(\\\"file_hash\\\"\\s*\\:\\s*\\\")([^\"]*)(\\\")"), "$1--to-be-calculated--$3") - val calculatedFileHash = CryptoUtil.hmac256(fileContents, KEY_CONSCIENCE) + val calculatedFileHash = cryptoUtil.hmac256(fileContents, KEY_CONSCIENCE) val container = JSONObject(jsonBody) if (container.has(PrefsMetadataKey.FILE_FORMAT.key) && container.has("security") && container.has("content") && container.has("metadata")) { @@ -144,11 +145,11 @@ class EncryptedPrefsFormat @Inject constructor( if (security.has("salt") && security.has("content_hash")) { val salt = security.getString("salt").hexStringToByteArray() - val decrypted = CryptoUtil.decrypt(masterPassword!!, salt, container.getString("content")) + val decrypted = cryptoUtil.decrypt(masterPassword!!, salt, container.getString("content")) if (decrypted != null) { try { - val contentHash = CryptoUtil.sha256(decrypted) + val contentHash = cryptoUtil.sha256(decrypted) if (contentHash == security.getString("content_hash")) { contentJsonObj = JSONObject(decrypted) 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 a3d86716bb..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.java +++ /dev/null @@ -1,1545 +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.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; -import android.widget.ImageButton; -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; -import androidx.recyclerview.widget.RecyclerView; - -import com.jjoe64.graphview.GraphView; - -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.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; -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.PumpDescription; -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.Callback; -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; - -import static info.nightscout.androidaps.utils.DateUtil.now; - -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; - - 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; - ImageButton chartButton; - - 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 boolean accepted; - - 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; - }); - - setupChartMenu(view); - - 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()), - exception -> FabricPrivacy.getInstance().logException(exception) - )); - disposable.add(rxBus - .toObservable(EventExtendedBolusChange.class) - .observeOn(Schedulers.io()) - .subscribe(event -> scheduleUpdateGUI("EventExtendedBolusChange"), - exception -> FabricPrivacy.getInstance().logException(exception) - )); - disposable.add(rxBus - .toObservable(EventTempBasalChange.class) - .observeOn(Schedulers.io()) - .subscribe(event -> scheduleUpdateGUI("EventTempBasalChange"), - exception -> FabricPrivacy.getInstance().logException(exception) - )); - disposable.add(rxBus - .toObservable(EventTreatmentChange.class) - .observeOn(Schedulers.io()) - .subscribe(event -> scheduleUpdateGUI("EventTreatmentChange"), - exception -> FabricPrivacy.getInstance().logException(exception) - )); - disposable.add(rxBus - .toObservable(EventTempTargetChange.class) - .observeOn(Schedulers.io()) - .subscribe(event -> scheduleUpdateGUI("EventTempTargetChange"), - exception -> FabricPrivacy.getInstance().logException(exception) - )); - disposable.add(rxBus - .toObservable(EventAcceptOpenLoopChange.class) - .observeOn(Schedulers.io()) - .subscribe(event -> scheduleUpdateGUI("EventAcceptOpenLoopChange"), - exception -> FabricPrivacy.getInstance().logException(exception) - )); - disposable.add(rxBus - .toObservable(EventCareportalEventChange.class) - .observeOn(Schedulers.io()) - .subscribe(event -> scheduleUpdateGUI("EventCareportalEventChange"), - exception -> FabricPrivacy.getInstance().logException(exception) - )); - disposable.add(rxBus - .toObservable(EventInitializationChanged.class) - .observeOn(Schedulers.io()) - .subscribe(event -> scheduleUpdateGUI("EventInitializationChanged"), - exception -> FabricPrivacy.getInstance().logException(exception) - )); - disposable.add(rxBus - .toObservable(EventAutosensCalculationFinished.class) - .observeOn(Schedulers.io()) - .subscribe(event -> scheduleUpdateGUI("EventAutosensCalculationFinished"), - exception -> FabricPrivacy.getInstance().logException(exception) - )); - disposable.add(rxBus - .toObservable(EventProfileNeedsUpdate.class) - .observeOn(Schedulers.io()) - .subscribe(event -> scheduleUpdateGUI("EventProfileNeedsUpdate"), - exception -> FabricPrivacy.getInstance().logException(exception) - )); - disposable.add(rxBus - .toObservable(EventPreferenceChange.class) - .observeOn(Schedulers.io()) - .subscribe(event -> scheduleUpdateGUI("EventPreferenceChange"), - exception -> FabricPrivacy.getInstance().logException(exception) - )); - disposable.add(rxBus - .toObservable(EventNewOpenLoopNotification.class) - .observeOn(Schedulers.io()) - .subscribe(event -> scheduleUpdateGUI("EventNewOpenLoopNotification"), - exception -> FabricPrivacy.getInstance().logException(exception) - )); - disposable.add(rxBus - .toObservable(EventPumpStatusChanged.class) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(event -> updatePumpStatus(event), - exception -> FabricPrivacy.getInstance().logException(exception) - )); - disposable.add(rxBus - .toObservable(EventIobCalculationProgress.class) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(event -> { - if (iobCalculationProgressView != null) - iobCalculationProgressView.setText(event.getProgress()); - }, - exception -> FabricPrivacy.getInstance().logException(exception) - )); - sRefreshLoop = () -> { - scheduleUpdateGUI("refreshLoop"); - sLoopHandler.postDelayed(sRefreshLoop, 60 * 1000L); - }; - sLoopHandler.postDelayed(sRefreshLoop, 60 * 1000L); - registerForContextMenu(apsModeView); - registerForContextMenu(activeProfileView); - registerForContextMenu(tempTargetView); - 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) { - 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)); - } - - @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); - } - - @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..28ab89ecba --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt @@ -0,0 +1,854 @@ +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.Color +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 android.widget.LinearLayout +import androidx.recyclerview.widget.LinearLayoutManager +import com.jjoe64.graphview.GraphView +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.collections.ArrayList +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 axisWidth: Int = 0 + 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 + + private val secondaryGraphs = ArrayList() + + 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) + 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_bggraph?.gridLabelRenderer?.labelVerticalWidth = axisWidth + + 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) + prepareGraphs() + + 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(AndroidSchedulers.mainThread()) + .subscribe({ + prepareGraphs() + if (it.now) updateGUI(it.from) + else 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 prepareGraphs() { + val numOfGraphs = overviewMenus.setting.size + + if (numOfGraphs != secondaryGraphs.size - 1) { + //aapsLogger.debug("New secondary graph count ${numOfGraphs-1}") + // rebuild needed + secondaryGraphs.clear() + overview_iobgraph.removeAllViews() + for (i in 1 until numOfGraphs) { + val graph = GraphView(context) + graph.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, resourceHelper.dpToPx(100)).also { it.setMargins(0, 0, 0, resourceHelper.dpToPx(10)) } + graph.gridLabelRenderer?.gridColor = resourceHelper.gc(R.color.graphgrid) + graph.gridLabelRenderer?.reloadStyles() + graph.gridLabelRenderer?.isHorizontalLabelsVisible = false + graph.gridLabelRenderer?.labelVerticalWidth = axisWidth + graph.gridLabelRenderer?.numVerticalLabels = 3 + graph.viewport.backgroundColor = Color.argb(20, 255, 255, 255) // 8% of gray + overview_iobgraph.addView(graph) + secondaryGraphs.add(graph) + } + } + + } + + 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 && overviewMenus.setting[0][OverviewMenus.CharType.PRE.ordinal]) + 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 (overviewMenus.setting[0][OverviewMenus.CharType.ACT.ordinal]) + graphData.addActivity(fromTime, endTime, false, 0.8) + + // add basal data + if (pump.pumpDescription.isTempBasalCapable && overviewMenus.setting[0][OverviewMenus.CharType.BAS.ordinal]) + 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 secondaryGraphsData: ArrayList = ArrayList() + for (g in 0 until secondaryGraphs.size) { + val secondGraphData = GraphData(injector, secondaryGraphs[g], iobCobCalculatorPlugin) + var useIobForScale = false + var useCobForScale = false + var useDevForScale = false + var useRatioForScale = false + var useDSForScale = false + var useIAForScale = false + when { + overviewMenus.setting[g + 1][OverviewMenus.CharType.IOB.ordinal] -> useIobForScale = true + overviewMenus.setting[g + 1][OverviewMenus.CharType.COB.ordinal] -> useCobForScale = true + overviewMenus.setting[g + 1][OverviewMenus.CharType.DEV.ordinal] -> useDevForScale = true + overviewMenus.setting[g + 1][OverviewMenus.CharType.SEN.ordinal] -> useRatioForScale = true + overviewMenus.setting[g + 1][OverviewMenus.CharType.ACT.ordinal] -> useIAForScale = true + overviewMenus.setting[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] -> useDSForScale = true + } + + if (overviewMenus.setting[g + 1][OverviewMenus.CharType.IOB.ordinal]) secondGraphData.addIob(fromTime, now, useIobForScale, 1.0, overviewMenus.setting[g + 1][OverviewMenus.CharType.PRE.ordinal]) + if (overviewMenus.setting[g + 1][OverviewMenus.CharType.COB.ordinal]) secondGraphData.addCob(fromTime, now, useCobForScale, if (useCobForScale) 1.0 else 0.5) + if (overviewMenus.setting[g + 1][OverviewMenus.CharType.DEV.ordinal]) secondGraphData.addDeviations(fromTime, now, useDevForScale, 1.0) + if (overviewMenus.setting[g + 1][OverviewMenus.CharType.SEN.ordinal]) secondGraphData.addRatio(fromTime, now, useRatioForScale, 1.0) + if (overviewMenus.setting[g + 1][OverviewMenus.CharType.ACT.ordinal]) secondGraphData.addActivity(fromTime, endTime, useIAForScale, 0.8) + if (overviewMenus.setting[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] && buildHelper.isDev()) secondGraphData.addDeviationSlope(fromTime, now, useDSForScale, 1.0) + + // set manual x bounds to have nice steps + secondGraphData.formatAxis(fromTime, endTime) + secondGraphData.addNowLine(now) + secondaryGraphsData.add(secondGraphData) + } + + // do GUI update + val activity = activity + activity?.runOnUiThread { + // finally enforce drawing of graphs + graphData.performUpdate() + for (g in 0 until secondaryGraphs.size) { + secondaryGraphs[g].visibility = ( + overviewMenus.setting[g + 1][OverviewMenus.CharType.IOB.ordinal] || + overviewMenus.setting[g + 1][OverviewMenus.CharType.COB.ordinal] || + overviewMenus.setting[g + 1][OverviewMenus.CharType.DEV.ordinal] || + overviewMenus.setting[g + 1][OverviewMenus.CharType.SEN.ordinal] || + overviewMenus.setting[g + 1][OverviewMenus.CharType.ACT.ordinal] || + overviewMenus.setting[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] + ).toVisibility() + secondaryGraphsData[g].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 new file mode 100644 index 0000000000..0368974841 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewMenus.kt @@ -0,0 +1,405 @@ +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.annotation.ColorRes +import androidx.annotation.StringRes +import androidx.appcompat.widget.PopupMenu +import androidx.fragment.app.FragmentManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +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.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 java.lang.reflect.Type +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 +) { + + enum class CharType(@StringRes val nameId: Int, @ColorRes val colorId: Int, val primary: Boolean, val secondary: Boolean) { + PRE(R.string.overview_show_predictions, R.color.prediction, primary = true, secondary = false), + BAS(R.string.overview_show_basals, R.color.basal, primary = true, secondary = false), + IOB(R.string.overview_show_iob, R.color.iob, primary = false, secondary = true), + COB(R.string.overview_show_cob, R.color.cob, primary = false, secondary = true), + DEV(R.string.overview_show_deviations, R.color.deviations, primary = false, secondary = true), + SEN(R.string.overview_show_sensitivity, R.color.ratio, primary = false, secondary = true), + ACT(R.string.overview_show_activity, R.color.activity, primary = true, secondary = true), + DEVSLOPE(R.string.overview_show_deviationslope, R.color.devslopepos, primary = false, secondary = true) + } + + companion object { + const val MAX_GRAPHS = 5 // including main + } + var setting: MutableList> = ArrayList() + + private fun storeGraphConfig() { + val sts = Gson().toJson(setting) + sp.putString(R.string.key_graphconfig, sts) + aapsLogger.debug(sts) + } + + private fun loadGraphConfig() { + val sts = sp.getString(R.string.key_graphconfig, "") + if (sts.isNotEmpty()) + setting = Gson().fromJson(sts, Array>::class.java).toMutableList() + else { + setting = ArrayList() + setting.add(Array(CharType.values().size) { true }) + } + } + + fun setupChartMenu(chartButton: ImageButton) { + loadGraphConfig() + val numOfGraphs = setting.size // 1 main + x secondary + + chartButton.setOnClickListener { v: View -> + val predictionsAvailable: Boolean = when { + Config.APS -> loopPlugin.lastRun?.request?.hasPredictions ?: false + Config.NSCLIENT -> true + else -> false + } + val popup = PopupMenu(v.context, v) + + for (g in 0 until numOfGraphs) { + if (g != 0 && g < numOfGraphs) { + val dividerItem = popup.menu.add(Menu.NONE, g, Menu.NONE, "------- " + "Graph" + " " + g + " -------") + dividerItem.isCheckable = true + dividerItem.isChecked = true + } + CharType.values().forEach { m -> + if (g == 0 && !m.primary) return@forEach + if (g > 0 && !m.secondary) return@forEach + var insert = true + if (m == CharType.PRE) insert = predictionsAvailable + if (m == CharType.DEVSLOPE) insert = buildHelper.isDev() + if (insert) { + val item = popup.menu.add(Menu.NONE, m.ordinal + 100 * (g + 1), Menu.NONE, resourceHelper.gs(m.nameId)) + val title = item.title + val s = SpannableString(title) + s.setSpan(ForegroundColorSpan(resourceHelper.gc(m.colorId)), 0, s.length, 0) + item.title = s + item.isCheckable = true + item.isChecked = setting[g][m.ordinal] + } + } + } + if (numOfGraphs < MAX_GRAPHS) { + val dividerItem = popup.menu.add(Menu.NONE, numOfGraphs, Menu.NONE, "------- " + "Graph" + " " + numOfGraphs + " -------") + dividerItem.isCheckable = true + dividerItem.isChecked = false + } + + popup.setOnMenuItemClickListener { + // id < 100 graph header - divider 1, 2, 3 ..... + if (it.itemId == numOfGraphs) { + // add new empty + setting.add(Array(CharType.values().size) { false }) + } else if (it.itemId < 100) { + // remove graph + setting.removeAt(it.itemId) + } else { + val graphNumber = it.itemId / 100 - 1 + val item = it.itemId % 100 + setting[graphNumber][item] = !it.isChecked + } + storeGraphConfig() + setupChartMenu(chartButton) + rxBus.send(EventRefreshOverview("OnMenuItemClickListener", now = true)) + 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 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/smsCommunicator/SmsCommunicatorPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPlugin.kt index 350e5f08db..c8a6bc8601 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 @@ -719,20 +719,28 @@ class SmsCommunicatorPlugin @Inject constructor( override fun run() { val detailedBolusInfo = DetailedBolusInfo() detailedBolusInfo.carbs = anInteger().toDouble() + detailedBolusInfo.source = Source.USER detailedBolusInfo.date = secondLong() - commandQueue.bolus(detailedBolusInfo, object : Callback() { - override fun run() { - if (result.success) { - var replyText = String.format(resourceHelper.gs(R.string.smscommunicator_carbsset), anInteger) - replyText += "\n" + activePlugin.activePump.shortStatus(true) - sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) - } else { - var replyText = resourceHelper.gs(R.string.smscommunicator_carbsfailed) - replyText += "\n" + activePlugin.activePump.shortStatus(true) - sendSMS(Sms(receivedSms.phoneNumber, replyText)) + if (activePlugin.activePump.pumpDescription.storesCarbInfo) { + commandQueue.bolus(detailedBolusInfo, object : Callback() { + override fun run() { + if (result.success) { + var replyText = String.format(resourceHelper.gs(R.string.smscommunicator_carbsset), anInteger) + replyText += "\n" + activePlugin.activePump.shortStatus(true) + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) + } else { + var replyText = resourceHelper.gs(R.string.smscommunicator_carbsfailed) + replyText += "\n" + activePlugin.activePump.shortStatus(true) + sendSMS(Sms(receivedSms.phoneNumber, replyText)) + } } - } - }) + }) + } else { + activePlugin.activeTreatments.addToHistoryTreatment(detailedBolusInfo, true) + var replyText = String.format(resourceHelper.gs(R.string.smscommunicator_carbsset), anInteger) + replyText += "\n" + activePlugin.activePump.shortStatus(true) + sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) + } } }) } 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..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 @@ -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 @@ -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/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/plugins/general/wear/WearPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearPlugin.kt index 62c964238c..abff22eae3 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearPlugin.kt @@ -11,7 +11,7 @@ import info.nightscout.androidaps.interfaces.PluginDescription 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.openAPSMA.events.EventOpenAPSUpdateGui +import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.general.overview.events.EventDismissBolusProgressIfRunning import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinFragment.kt index 21737cac6d..c2e61f19b9 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/insulin/InsulinFragment.kt @@ -25,6 +25,6 @@ class InsulinFragment : DaggerFragment() { insulin_name?.setText(activePlugin.getActiveInsulin().getFriendlyName()) insulin_comment?.setText(activePlugin.getActiveInsulin().getComment()) insulin_dia?.text = resourceHelper.gs(R.string.dia) + ": " + activePlugin.getActiveInsulin().getDia() + "h" - insuling_graph?.show(activePlugin.getActiveInsulin()) + insulin_graph?.show(activePlugin.getActiveInsulin()) } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/combo/ComboPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/combo/ComboPlugin.java index 5700a235a3..5521396b6b 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/combo/ComboPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/combo/ComboPlugin.java @@ -644,7 +644,7 @@ public class ComboPlugin extends PumpPluginBase implements PumpInterface, Constr } finally { pump.activity = null; rxBus.send(new EventComboPumpUpdateGUI()); - rxBus.send(new EventRefreshOverview("Bolus")); + rxBus.send(new EventRefreshOverview("Bolus", false)); cancelBolus = false; } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightPlugin.java index a75398554e..bab8408935 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/insight/LocalInsightPlugin.java @@ -464,7 +464,7 @@ public class LocalInsightPlugin extends PumpPluginBase implements PumpInterface, lastUpdated = System.currentTimeMillis(); new Handler(Looper.getMainLooper()).post(() -> { rxBus.send(new EventLocalInsightUpdateGUI()); - rxBus.send(new EventRefreshOverview("LocalInsightPlugin::fetchStatus")); + rxBus.send(new EventRefreshOverview("LocalInsightPlugin::fetchStatus", false)); }); } @@ -1215,7 +1215,7 @@ public class LocalInsightPlugin extends PumpPluginBase implements PumpInterface, } catch (Exception e) { aapsLogger.error("Exception while reading history", e); } - new Handler(Looper.getMainLooper()).post(() -> rxBus.send(new EventRefreshOverview("LocalInsightPlugin::readHistory"))); + new Handler(Looper.getMainLooper()).post(() -> rxBus.send(new EventRefreshOverview("LocalInsightPlugin::readHistory", false))); } private void processHistoryEvents(String serial, List historyEvents) { @@ -1662,7 +1662,7 @@ public class LocalInsightPlugin extends PumpPluginBase implements PumpInterface, activeTBR = null; activeBoluses = null; tbrOverNotificationBlock = null; - new Handler(Looper.getMainLooper()).post(() -> rxBus.send(new EventRefreshOverview("LocalInsightPlugin::onStateChanged"))); + new Handler(Looper.getMainLooper()).post(() -> rxBus.send(new EventRefreshOverview("LocalInsightPlugin::onStateChanged", false))); } new Handler(Looper.getMainLooper()).post(() -> rxBus.send(new EventLocalInsightUpdateGUI())); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java index 1c23665406..2f6c190179 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/medtronic/MedtronicPumpPlugin.java @@ -1073,7 +1073,7 @@ public class MedtronicPumpPlugin extends PumpPluginAbstract implements PumpInter private void finishAction(String overviewKey) { if (overviewKey != null) - rxBus.send(new EventRefreshOverview(overviewKey)); + rxBus.send(new EventRefreshOverview(overviewKey, false)); triggerUIChange(); 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 46c16e9772..a6207e595b 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 @@ -281,6 +281,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); 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/java/info/nightscout/androidaps/utils/CryptoUtil.kt b/app/src/main/java/info/nightscout/androidaps/utils/CryptoUtil.kt index 745f4449ee..01cc925942 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/CryptoUtil.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/CryptoUtil.kt @@ -1,5 +1,6 @@ package info.nightscout.androidaps.utils +import info.nightscout.androidaps.logging.AAPSLogger import org.spongycastle.util.encoders.Base64 import java.nio.ByteBuffer import java.security.MessageDigest @@ -12,6 +13,8 @@ import javax.crypto.SecretKeyFactory import javax.crypto.spec.GCMParameterSpec import javax.crypto.spec.PBEKeySpec import javax.crypto.spec.SecretKeySpec +import javax.inject.Inject +import javax.inject.Singleton private val HEX_CHARS = "0123456789abcdef" private val HEX_CHARS_ARRAY = "0123456789abcdef".toCharArray() @@ -45,15 +48,21 @@ fun ByteArray.toHex() : String{ return result.toString() } -object CryptoUtil { +@Singleton +class CryptoUtil @Inject constructor( + val aapsLogger: AAPSLogger +) { - private const val IV_LENGTH_BYTE = 12 - private const val TAG_LENGTH_BIT = 128 - private const val AES_KEY_SIZE_BIT = 256 - private const val PBKDF2_ITERATIONS = 50000 // check delays it cause on real device - private const val SALT_SIZE_BYTE = 32 + companion object { + private const val IV_LENGTH_BYTE = 12 + private const val TAG_LENGTH_BIT = 128 + private const val AES_KEY_SIZE_BIT = 256 + private const val PBKDF2_ITERATIONS = 50000 // check delays it cause on real device + private const val SALT_SIZE_BYTE = 32 + } private val secureRandom: SecureRandom = SecureRandom() + var lastException: Exception? = null fun sha256(source: String): String { val digest = MessageDigest.getInstance("SHA-256") @@ -85,6 +94,7 @@ object CryptoUtil { val iv: ByteArray? val encrypted: ByteArray? return try { + lastException = null iv = ByteArray(IV_LENGTH_BYTE) secureRandom.nextBytes(iv) val cipherEnc: Cipher = Cipher.getInstance("AES/GCM/NoPadding") @@ -96,7 +106,9 @@ object CryptoUtil { byteBuffer.put(encrypted) String(Base64.encode(byteBuffer.array())) } catch (e: Exception) { - null + lastException = e + aapsLogger.error("Encryption failed due to technical exception: ${e}") + null } } @@ -104,6 +116,7 @@ object CryptoUtil { val iv: ByteArray? val encrypted: ByteArray? return try { + lastException = null val byteBuffer = ByteBuffer.wrap(Base64.decode(encryptedData)) val ivLength = byteBuffer.get().toInt() iv = ByteArray(ivLength) @@ -115,6 +128,8 @@ object CryptoUtil { val dec = cipherDec.doFinal(encrypted) String(dec) } catch (e: Exception) { + lastException = e + aapsLogger.error("Decryption failed due to technical exception: ${e}") null } } 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/protection/PasswordCheck.kt b/app/src/main/java/info/nightscout/androidaps/utils/protection/PasswordCheck.kt index 1907ac01b5..2df55c9f2f 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/protection/PasswordCheck.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/protection/PasswordCheck.kt @@ -19,7 +19,10 @@ import javax.inject.Singleton val AUTOFILL_HINT_NEW_PASSWORD = "newPassword" @Singleton -class PasswordCheck @Inject constructor(val sp: SP) { +class PasswordCheck @Inject constructor( + val sp: SP, + val cryptoUtil: CryptoUtil +) { @SuppressLint("InflateParams") fun queryPassword(context: Context, @StringRes labelId: Int, @StringRes preference: Int, ok: ( (String) -> Unit)?, cancel: (()->Unit)? = null, fail: (()->Unit)? = null) { @@ -45,7 +48,7 @@ class PasswordCheck @Inject constructor(val sp: SP) { .setCustomTitle(AlertDialogHelper.buildCustomTitle(context, context.getString(labelId), R.drawable.ic_header_key)) .setPositiveButton(context.getString(R.string.ok)) { _, _ -> val enteredPassword = userInput.text.toString() - if (CryptoUtil.checkPassword(enteredPassword, password)) ok?.invoke(enteredPassword) + if (cryptoUtil.checkPassword(enteredPassword, password)) ok?.invoke(enteredPassword) else { ToastUtils.errorToast(context, context.getString(R.string.wrongpassword)) fail?.invoke() @@ -80,7 +83,7 @@ class PasswordCheck @Inject constructor(val sp: SP) { .setPositiveButton(context.getString(R.string.ok)) { _, _ -> val enteredPassword = userInput.text.toString() if (enteredPassword.isNotEmpty()) { - sp.putString(preference, CryptoUtil.hashPassword(enteredPassword)) + sp.putString(preference, cryptoUtil.hashPassword(enteredPassword)) ToastUtils.okToast(context, context.getString(R.string.password_set)) ok?.invoke(enteredPassword) } else { 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 diff --git a/app/src/main/res/layout/insulin_fragment.xml b/app/src/main/res/layout/insulin_fragment.xml index 5f2a75850a..d1789ea80e 100644 --- a/app/src/main/res/layout/insulin_fragment.xml +++ b/app/src/main/res/layout/insulin_fragment.xml @@ -35,7 +35,7 @@ android:textAppearance="?android:attr/textAppearanceMedium" /> diff --git a/app/src/main/res/layout/overview_fragment.xml b/app/src/main/res/layout/overview_fragment.xml index 35ddd5eed4..64e32cf747 100644 --- a/app/src/main/res/layout/overview_fragment.xml +++ b/app/src/main/res/layout/overview_fragment.xml @@ -452,10 +452,11 @@ app:layout_constraintTop_toTopOf="@+id/overview_bggraph" /> - - - @@ -208,8 +208,8 @@ android:layout_height="wrap_content" android:layout_weight="0" android:gravity="center_horizontal" - android:paddingEnd="2dp" android:paddingStart="2dp" + android:paddingEnd="2dp" android:text=":" android:textSize="14sp" /> @@ -230,10 +230,10 @@ @@ -256,8 +256,8 @@ android:layout_height="wrap_content" android:layout_weight="0" android:gravity="center_horizontal" - android:paddingEnd="2dp" android:paddingStart="2dp" + android:paddingEnd="2dp" android:text=":" android:textSize="14sp" /> @@ -278,10 +278,10 @@ @@ -304,8 +304,8 @@ android:layout_height="wrap_content" android:layout_weight="0" android:gravity="center_horizontal" - android:paddingEnd="2dp" android:paddingStart="2dp" + android:paddingEnd="2dp" android:text=":" android:textSize="14sp" /> @@ -326,10 +326,10 @@ @@ -373,10 +373,10 @@ @@ -425,10 +425,10 @@ @@ -454,10 +454,10 @@ @@ -512,8 +512,8 @@ android:id="@+id/overview_chartMenuButton" android:layout_width="30dp" android:layout_height="wrap_content" - android:layout_alignParentEnd="true" android:layout_alignParentTop="true" + android:layout_alignParentEnd="true" android:paddingTop="5dp" app:srcCompat="@drawable/ic_arrow_drop_down_white_24dp" /> @@ -526,10 +526,11 @@ - + android:layout_height="wrap_content" + android:orientation="vertical" /> 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 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bb1b596cf8..148b1e538f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -275,7 +275,7 @@ Created at AAPS Version Build Variant - Exporting device name + Exporting device patient name Exporting device model File encryption @@ -610,6 +610,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 @@ -1768,19 +1772,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 @@ -1800,4 +1800,7 @@ By reseting authenticator you make all already provisioned authenticators invalid. You will need to set up them again! On connect On disconnect + Predictions + Deviation slope + graphconfig 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 diff --git a/app/src/test/java/info/nightscout/androidaps/interfaces/ConstraintsCheckerTest.kt b/app/src/test/java/info/nightscout/androidaps/interfaces/ConstraintsCheckerTest.kt index 763cd748cc..a532ced011 100644 --- a/app/src/test/java/info/nightscout/androidaps/interfaces/ConstraintsCheckerTest.kt +++ b/app/src/test/java/info/nightscout/androidaps/interfaces/ConstraintsCheckerTest.kt @@ -7,7 +7,6 @@ import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.R import info.nightscout.androidaps.TestBaseWithProfile import info.nightscout.androidaps.plugins.aps.openAPSAMA.OpenAPSAMAPlugin -import info.nightscout.androidaps.plugins.aps.openAPSMA.OpenAPSMAPlugin import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBPlugin import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker @@ -44,7 +43,7 @@ import java.util.* * Created by mike on 18.03.2018. */ @RunWith(PowerMockRunner::class) -@PrepareForTest(MainApp::class, ConfigBuilderPlugin::class, ConstraintChecker::class, SP::class, Context::class, OpenAPSMAPlugin::class, OpenAPSAMAPlugin::class, OpenAPSSMBPlugin::class, TreatmentsPlugin::class, TreatmentService::class, VirtualPumpPlugin::class, DetailedBolusInfoStorage::class, GlimpPlugin::class) +@PrepareForTest(MainApp::class, ConfigBuilderPlugin::class, ConstraintChecker::class, SP::class, Context::class, OpenAPSAMAPlugin::class, OpenAPSSMBPlugin::class, TreatmentsPlugin::class, TreatmentService::class, VirtualPumpPlugin::class, DetailedBolusInfoStorage::class, GlimpPlugin::class) class ConstraintsCheckerTest : TestBaseWithProfile() { @Mock lateinit var activePlugin: ActivePluginProvider @@ -70,7 +69,6 @@ class ConstraintsCheckerTest : TestBaseWithProfile() { private lateinit var insightPlugin: LocalInsightPlugin private lateinit var openAPSSMBPlugin: OpenAPSSMBPlugin private lateinit var openAPSAMAPlugin: OpenAPSAMAPlugin - private lateinit var openAPSMAPlugin: OpenAPSMAPlugin private lateinit var hardLimits: HardLimits val injector = HasAndroidInjector { @@ -119,8 +117,7 @@ class ConstraintsCheckerTest : TestBaseWithProfile() { insightPlugin = LocalInsightPlugin(injector, aapsLogger, rxBus, resourceHelper, treatmentsPlugin, sp, commandQueue, profileFunction, context) openAPSSMBPlugin = OpenAPSSMBPlugin(injector, aapsLogger, rxBus, constraintChecker, resourceHelper, profileFunction, context, activePlugin, treatmentsPlugin, iobCobCalculatorPlugin, hardLimits) openAPSAMAPlugin = OpenAPSAMAPlugin(injector, aapsLogger, rxBus, constraintChecker, resourceHelper, profileFunction, context, activePlugin, treatmentsPlugin, iobCobCalculatorPlugin, hardLimits) - openAPSMAPlugin = OpenAPSMAPlugin(injector, aapsLogger, rxBus, constraintChecker, resourceHelper, profileFunction, context, activePlugin, treatmentsPlugin, iobCobCalculatorPlugin, hardLimits) - safetyPlugin = SafetyPlugin(injector, aapsLogger, resourceHelper, sp, rxBus, constraintChecker, openAPSAMAPlugin, openAPSMAPlugin, openAPSSMBPlugin, sensitivityOref1Plugin, activePlugin, hardLimits, buildHelper, treatmentsPlugin) + safetyPlugin = SafetyPlugin(injector, aapsLogger, resourceHelper, sp, rxBus, constraintChecker, openAPSAMAPlugin, openAPSSMBPlugin, sensitivityOref1Plugin, activePlugin, hardLimits, buildHelper, treatmentsPlugin) val constraintsPluginsList = ArrayList() constraintsPluginsList.add(safetyPlugin) constraintsPluginsList.add(objectivesPlugin) @@ -316,7 +313,6 @@ class ConstraintsCheckerTest : TestBaseWithProfile() { `when`(sp.getDouble(R.string.key_openapsma_max_iob, 1.5)).thenReturn(1.5) `when`(sp.getString(R.string.key_age, "")).thenReturn("teenage") openAPSAMAPlugin.setPluginEnabled(PluginType.APS, true) - openAPSMAPlugin.setPluginEnabled(PluginType.APS, false) openAPSSMBPlugin.setPluginEnabled(PluginType.APS, false) // Apply all limits @@ -333,7 +329,6 @@ class ConstraintsCheckerTest : TestBaseWithProfile() { `when`(sp.getString(R.string.key_age, "")).thenReturn("teenage") openAPSSMBPlugin.setPluginEnabled(PluginType.APS, true) openAPSAMAPlugin.setPluginEnabled(PluginType.APS, false) - openAPSMAPlugin.setPluginEnabled(PluginType.APS, false) // Apply all limits val d = constraintChecker.getMaxIOBAllowed() diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/constraints/safety/SafetyPluginTest.kt b/app/src/test/java/info/nightscout/androidaps/plugins/constraints/safety/SafetyPluginTest.kt index c3e4b8a01f..9da162a301 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/constraints/safety/SafetyPluginTest.kt +++ b/app/src/test/java/info/nightscout/androidaps/plugins/constraints/safety/SafetyPluginTest.kt @@ -10,7 +10,6 @@ import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.interfaces.PumpDescription import info.nightscout.androidaps.plugins.aps.openAPSAMA.OpenAPSAMAPlugin -import info.nightscout.androidaps.plugins.aps.openAPSMA.OpenAPSMAPlugin import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBPlugin import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin @@ -35,7 +34,6 @@ class SafetyPluginTest : TestBaseWithProfile() { @Mock lateinit var sp: SP @Mock lateinit var constraintChecker: ConstraintChecker @Mock lateinit var openAPSAMAPlugin: OpenAPSAMAPlugin - @Mock lateinit var openAPSMAPlugin: OpenAPSMAPlugin @Mock lateinit var openAPSSMBPlugin: OpenAPSSMBPlugin @Mock lateinit var sensitivityOref1Plugin: SensitivityOref1Plugin @Mock lateinit var activePlugin: ActivePluginProvider @@ -75,7 +73,7 @@ class SafetyPluginTest : TestBaseWithProfile() { `when`(activePlugin.activePump).thenReturn(virtualPumpPlugin) `when`(virtualPumpPlugin.pumpDescription).thenReturn(pumpDescription) hardLimits = HardLimits(aapsLogger, rxBus, sp, resourceHelper, context) - safetyPlugin = SafetyPlugin(injector, aapsLogger, resourceHelper, sp, rxBus, constraintChecker, openAPSAMAPlugin, openAPSMAPlugin, openAPSSMBPlugin, sensitivityOref1Plugin, activePlugin, hardLimits, buildHelper, treatmentsPlugin) + safetyPlugin = SafetyPlugin(injector, aapsLogger, resourceHelper, sp, rxBus, constraintChecker, openAPSAMAPlugin, openAPSSMBPlugin, sensitivityOref1Plugin, activePlugin, hardLimits, buildHelper, treatmentsPlugin) } @Test fun pumpDescriptionShouldLimitLoopInvocation() { diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/maintenance/EncryptedPrefsFormatTest.kt b/app/src/test/java/info/nightscout/androidaps/plugins/general/maintenance/EncryptedPrefsFormatTest.kt index 16c2f4b0ef..4537783ce4 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/maintenance/EncryptedPrefsFormatTest.kt +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/maintenance/EncryptedPrefsFormatTest.kt @@ -5,10 +5,11 @@ import info.nightscout.androidaps.TestBase import info.nightscout.androidaps.plugins.general.maintenance.formats.* import info.nightscout.androidaps.testing.mockers.AAPSMocker import info.nightscout.androidaps.testing.utils.SingleStringStorage +import info.nightscout.androidaps.utils.CryptoUtil +import info.nightscout.androidaps.utils.assumeAES256isSupported import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP import org.hamcrest.CoreMatchers -import org.json.JSONException import org.junit.Assert import org.junit.Before import org.junit.Test @@ -30,6 +31,8 @@ class EncryptedPrefsFormatTest : TestBase() { @Mock lateinit var resourceHelper: ResourceHelper @Mock lateinit var sp: SP + var cryptoUtil: CryptoUtil = CryptoUtil(aapsLogger) + @Before fun mock() { AAPSMocker.prepareMock() @@ -52,9 +55,11 @@ class EncryptedPrefsFormatTest : TestBase() { "}" val storage = SingleStringStorage(frozenPrefs) - val encryptedFormat = EncryptedPrefsFormat(resourceHelper, storage) + val encryptedFormat = EncryptedPrefsFormat(resourceHelper, cryptoUtil, storage) val prefs = encryptedFormat.loadPreferences(AAPSMocker.getMockedFile(), "sikret") + assumeAES256isSupported(cryptoUtil) + Assert.assertThat(prefs.values.size, CoreMatchers.`is`(2)) Assert.assertThat(prefs.values["key1"], CoreMatchers.`is`("A")) Assert.assertThat(prefs.values["keyB"], CoreMatchers.`is`("2")) @@ -67,7 +72,7 @@ class EncryptedPrefsFormatTest : TestBase() { @Test fun preferenceSavingTest() { val storage = SingleStringStorage("") - val encryptedFormat = EncryptedPrefsFormat(resourceHelper, storage) + val encryptedFormat = EncryptedPrefsFormat(resourceHelper, cryptoUtil, storage) val prefs = Prefs( mapOf( "key1" to "A", @@ -84,7 +89,7 @@ class EncryptedPrefsFormatTest : TestBase() { @Test fun importExportStabilityTest() { val storage = SingleStringStorage("") - val encryptedFormat = EncryptedPrefsFormat(resourceHelper, storage) + val encryptedFormat = EncryptedPrefsFormat(resourceHelper, cryptoUtil, storage) val prefsIn = Prefs( mapOf( "testpref1" to "--1--", @@ -97,6 +102,8 @@ class EncryptedPrefsFormatTest : TestBase() { encryptedFormat.savePreferences(AAPSMocker.getMockedFile(), prefsIn, "tajemnica") val prefsOut = encryptedFormat.loadPreferences(AAPSMocker.getMockedFile(), "tajemnica") + assumeAES256isSupported(cryptoUtil) + Assert.assertThat(prefsOut.values.size, CoreMatchers.`is`(2)) Assert.assertThat(prefsOut.values["testpref1"], CoreMatchers.`is`("--1--")) Assert.assertThat(prefsOut.values["testpref2"], CoreMatchers.`is`("another")) @@ -121,7 +128,7 @@ class EncryptedPrefsFormatTest : TestBase() { "}" val storage = SingleStringStorage(frozenPrefs) - val encryptedFormat = EncryptedPrefsFormat(resourceHelper, storage) + val encryptedFormat = EncryptedPrefsFormat(resourceHelper, cryptoUtil, storage) val prefs = encryptedFormat.loadPreferences(AAPSMocker.getMockedFile(), "it-is-NOT-right-secret") Assert.assertThat(prefs.values.size, CoreMatchers.`is`(0)) @@ -148,9 +155,11 @@ class EncryptedPrefsFormatTest : TestBase() { "}" val storage = SingleStringStorage(frozenPrefs) - val encryptedFormat = EncryptedPrefsFormat(resourceHelper, storage) + val encryptedFormat = EncryptedPrefsFormat(resourceHelper, cryptoUtil, storage) val prefs = encryptedFormat.loadPreferences(AAPSMocker.getMockedFile(), "sikret") + assumeAES256isSupported(cryptoUtil) + // contents were not tampered and we can decrypt them Assert.assertThat(prefs.values.size, CoreMatchers.`is`(2)) @@ -173,7 +182,7 @@ class EncryptedPrefsFormatTest : TestBase() { "}" val storage = SingleStringStorage(frozenPrefs) - val encryptedFormat = EncryptedPrefsFormat(resourceHelper, storage) + val encryptedFormat = EncryptedPrefsFormat(resourceHelper, cryptoUtil, storage) val prefs = encryptedFormat.loadPreferences(AAPSMocker.getMockedFile(), "sikret") Assert.assertThat(prefs.values.size, CoreMatchers.`is`(0)) @@ -188,7 +197,7 @@ class EncryptedPrefsFormatTest : TestBase() { "}" val storage = SingleStringStorage(frozenPrefs) - val encryptedFormat = EncryptedPrefsFormat(resourceHelper, storage) + val encryptedFormat = EncryptedPrefsFormat(resourceHelper, cryptoUtil, storage) val prefs = encryptedFormat.loadPreferences(AAPSMocker.getMockedFile(), "sikret") Assert.assertThat(prefs.values.size, CoreMatchers.`is`(0)) @@ -200,7 +209,7 @@ class EncryptedPrefsFormatTest : TestBase() { val frozenPrefs = "whatever man, i duno care" val storage = SingleStringStorage(frozenPrefs) - val encryptedFormat = EncryptedPrefsFormat(resourceHelper, storage) + val encryptedFormat = EncryptedPrefsFormat(resourceHelper, cryptoUtil, storage) encryptedFormat.loadPreferences(AAPSMocker.getMockedFile(), "sikret") } @@ -219,7 +228,7 @@ class EncryptedPrefsFormatTest : TestBase() { "}" val storage = SingleStringStorage(frozenPrefs) - val encryptedFormat = EncryptedPrefsFormat(resourceHelper, storage) + val encryptedFormat = EncryptedPrefsFormat(resourceHelper, cryptoUtil, storage) encryptedFormat.loadPreferences(AAPSMocker.getMockedFile(), "sikret") } diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.kt b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.kt index d5b7ddd5f5..2ce83f0164 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.kt +++ b/app/src/test/java/info/nightscout/androidaps/plugins/general/smsCommunicator/SmsCommunicatorPluginTest.kt @@ -13,6 +13,7 @@ import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.interfaces.PluginType +import info.nightscout.androidaps.interfaces.PumpDescription import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction @@ -156,6 +157,7 @@ class SmsCommunicatorPluginTest : TestBaseWithProfile() { `when`(virtualPumpPlugin.shortStatus(ArgumentMatchers.anyBoolean())).thenReturn("Virtual Pump") `when`(virtualPumpPlugin.isSuspended).thenReturn(false) + `when`(virtualPumpPlugin.pumpDescription).thenReturn(PumpDescription()) `when`(treatmentsPlugin.lastCalculationTreatments).thenReturn(IobTotal(0)) `when`(treatmentsPlugin.lastCalculationTempBasals).thenReturn(IobTotal(0)) diff --git a/app/src/test/java/info/nightscout/androidaps/utils/CryptoUtilTest.kt b/app/src/test/java/info/nightscout/androidaps/utils/CryptoUtilTest.kt index c147c0810c..9d6b3ad9cd 100644 --- a/app/src/test/java/info/nightscout/androidaps/utils/CryptoUtilTest.kt +++ b/app/src/test/java/info/nightscout/androidaps/utils/CryptoUtilTest.kt @@ -1,16 +1,31 @@ package info.nightscout.androidaps.utils import info.nightscout.androidaps.TestBase +import org.hamcrest.CoreMatchers.containsString +import org.hamcrest.CoreMatchers.not import org.junit.Assert +import org.junit.Assume.assumeThat import org.junit.Test import org.junit.runner.RunWith import org.powermock.core.classloader.annotations.PowerMockIgnore import org.powermock.modules.junit4.PowerMockRunner +// https://stackoverflow.com/questions/52344522/joseexception-couldnt-create-aes-gcm-nopadding-cipher-illegal-key-size +// https://stackoverflow.com/questions/47708951/can-aes-256-work-on-android-devices-with-api-level-26 +// Java prior to Oracle Java 8u161 does not have policy for 256 bit AES - but Android support it +// when test is run in Vanilla JVM without policy - Invalid key size exception is thrown +fun assumeAES256isSupported(cryptoUtil: CryptoUtil) { + cryptoUtil.lastException?.message?.let { exceptionMessage -> + assumeThat("Upgrade your testing environment Java (OpenJDK or Java 8u161) and JAVA_HOME - AES 256 is supported by Android so this exception should not happen!", exceptionMessage, not(containsString("key size"))) + } +} + @PowerMockIgnore("javax.crypto.*") @RunWith(PowerMockRunner::class) class CryptoUtilTest: TestBase() { + var cryptoUtil: CryptoUtil = CryptoUtil(aapsLogger) + @Test fun testFixedSaltCrypto() { val salt = byteArrayOf( @@ -19,30 +34,36 @@ class CryptoUtilTest: TestBase() { val password = "thisIsFixedPassword" val payload = "FIXED-PAYLOAD" - val encrypted = CryptoUtil.encrypt(password, salt, payload) + val encrypted = cryptoUtil.encrypt(password, salt, payload) + assumeAES256isSupported(cryptoUtil) Assert.assertNotNull(encrypted) - val decrypted = CryptoUtil.decrypt(password, salt, encrypted!!) + + val decrypted = cryptoUtil.decrypt(password, salt, encrypted!!) + assumeAES256isSupported(cryptoUtil) Assert.assertEquals(decrypted, payload) } @Test fun testStandardCrypto() { - val salt = CryptoUtil.mineSalt() + val salt = cryptoUtil.mineSalt() val password = "topSikret" val payload = "{what:payloadYouWantToProtect}" - val encrypted = CryptoUtil.encrypt(password, salt, payload) + val encrypted = cryptoUtil.encrypt(password, salt, payload) + assumeAES256isSupported(cryptoUtil) Assert.assertNotNull(encrypted) - val decrypted = CryptoUtil.decrypt(password, salt, encrypted!!) + + val decrypted = cryptoUtil.decrypt(password, salt, encrypted!!) + assumeAES256isSupported(cryptoUtil) Assert.assertEquals(decrypted, payload) } @Test fun testHashVector() { val payload = "{what:payloadYouWantToProtect}" - val hash = CryptoUtil.sha256(payload) + val hash = cryptoUtil.sha256(payload) Assert.assertEquals(hash, "a1aafe3ed6cc127e6d102ddbc40a205147230e9cfd178daf108c83543bbdcd13") } @@ -51,25 +72,25 @@ class CryptoUtilTest: TestBase() { val payload = "{what:payloadYouWantToProtect}" val password = "topSikret" val expectedHmac = "ea2213953d0f2e55047cae2d23fb4f0de1b805d55e6271efa70d6b85fb692bea" // generated using other HMAC tool - val hash = CryptoUtil.hmac256(payload, password) + val hash = cryptoUtil.hmac256(payload, password) Assert.assertEquals(hash, expectedHmac) } @Test fun testPlainPasswordCheck() { - Assert.assertTrue(CryptoUtil.checkPassword("same", "same")) - Assert.assertFalse(CryptoUtil.checkPassword("same", "other")) + Assert.assertTrue(cryptoUtil.checkPassword("same", "same")) + Assert.assertFalse(cryptoUtil.checkPassword("same", "other")) } @Test fun testHashedPasswordCheck() { - Assert.assertTrue(CryptoUtil.checkPassword("givenSecret", CryptoUtil.hashPassword("givenSecret"))) - Assert.assertFalse(CryptoUtil.checkPassword("givenSecret", CryptoUtil.hashPassword("otherSecret"))) + Assert.assertTrue(cryptoUtil.checkPassword("givenSecret", cryptoUtil.hashPassword("givenSecret"))) + Assert.assertFalse(cryptoUtil.checkPassword("givenSecret", cryptoUtil.hashPassword("otherSecret"))) - Assert.assertTrue(CryptoUtil.checkPassword("givenHashToCheck", "hmac:7fe5f9c7b4b97c5d32d5cfad9d07473543a9938dc07af48a46dbbb49f4f68c12:a0c7cee14312bbe31b51359a67f0d2dfdf46813f319180269796f1f617a64be1")) - Assert.assertFalse(CryptoUtil.checkPassword("givenMashToCheck", "hmac:7fe5f9c7b4b97c5d32d5cfad9d07473543a9938dc07af48a46dbbb49f4f68c12:a0c7cee14312bbe31b51359a67f0d2dfdf46813f319180269796f1f617a64be1")) - Assert.assertFalse(CryptoUtil.checkPassword("givenHashToCheck", "hmac:0fe5f9c7b4b97c5d32d5cfad9d07473543a9938dc07af48a46dbbb49f4f68c12:a0c7cee14312bbe31b51359a67f0d2dfdf46813f319180269796f1f617a64be1")) - Assert.assertFalse(CryptoUtil.checkPassword("givenHashToCheck", "hmac:7fe5f9c7b4b97c5d32d5cfad9d07473543a9938dc07af48a46dbbb49f4f68c12:b0c7cee14312bbe31b51359a67f0d2dfdf46813f319180269796f1f617a64be1")) + Assert.assertTrue(cryptoUtil.checkPassword("givenHashToCheck", "hmac:7fe5f9c7b4b97c5d32d5cfad9d07473543a9938dc07af48a46dbbb49f4f68c12:a0c7cee14312bbe31b51359a67f0d2dfdf46813f319180269796f1f617a64be1")) + Assert.assertFalse(cryptoUtil.checkPassword("givenMashToCheck", "hmac:7fe5f9c7b4b97c5d32d5cfad9d07473543a9938dc07af48a46dbbb49f4f68c12:a0c7cee14312bbe31b51359a67f0d2dfdf46813f319180269796f1f617a64be1")) + Assert.assertFalse(cryptoUtil.checkPassword("givenHashToCheck", "hmac:0fe5f9c7b4b97c5d32d5cfad9d07473543a9938dc07af48a46dbbb49f4f68c12:a0c7cee14312bbe31b51359a67f0d2dfdf46813f319180269796f1f617a64be1")) + Assert.assertFalse(cryptoUtil.checkPassword("givenHashToCheck", "hmac:7fe5f9c7b4b97c5d32d5cfad9d07473543a9938dc07af48a46dbbb49f4f68c12:b0c7cee14312bbe31b51359a67f0d2dfdf46813f319180269796f1f617a64be1")) } }