Merge branch 'dagger3' into AS0ref
This commit is contained in:
commit
a4e7d1ee1f
|
@ -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;
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<MainApp> {
|
|||
fun injectPumpEnactResult(pumpEnactResult: PumpEnactResult)
|
||||
fun injectAPSResult(apsResult: APSResult)
|
||||
fun injectDetermineBasalResultSMB(determineBasalResultSMB: DetermineBasalResultSMB)
|
||||
fun injectDetermineBasalResultMA(determineBasalResultMA: DetermineBasalResultMA)
|
||||
fun injectDetermineBasalResultAMA(determineBasalResultAMA: DetermineBasalResultAMA)
|
||||
fun injectDetermineBasalAdapterSMBJS(determineBasalAdapterSMBJS: DetermineBasalAdapterSMBJS)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package info.nightscout.androidaps.plugins.aps.openAPSMA;
|
||||
package info.nightscout.androidaps.plugins.aps.logger;
|
||||
|
||||
import org.mozilla.javascript.ScriptableObject;
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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 = ""
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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));
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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<GraphView>()
|
||||
|
||||
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<GraphData> = 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()
|
||||
}
|
||||
}
|
|
@ -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<Array<Boolean>> = 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<Array<Boolean>>::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
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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" }
|
||||
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<HistoryEvent> 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()));
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -281,6 +281,10 @@ public class TreatmentService extends OrmLiteBaseService<DatabaseHelper> {
|
|||
|
||||
// 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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -20,4 +20,5 @@ interface ResourceHelper {
|
|||
fun decodeResource(id : Int) : Bitmap
|
||||
fun getDisplayMetrics(): DisplayMetrics
|
||||
fun dpToPx(dp: Int): Int
|
||||
fun shortTextMode(): Boolean
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -35,7 +35,7 @@
|
|||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<info.nightscout.androidaps.plugins.insulin.ActivityGraph
|
||||
android:id="@+id/insuling_graph"
|
||||
android:id="@+id/insulin_graph"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_margin="20dp"
|
||||
android:layout_height="200dip" />
|
||||
|
|
|
@ -452,10 +452,11 @@
|
|||
app:layout_constraintTop_toTopOf="@+id/overview_bggraph" />
|
||||
|
||||
|
||||
<com.jjoe64.graphview.GraphView
|
||||
<LinearLayout
|
||||
android:id="@+id/overview_iobgraph"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="100dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
|
|
@ -452,10 +452,11 @@
|
|||
app:layout_constraintTop_toTopOf="@+id/overview_bggraph" />
|
||||
|
||||
|
||||
<com.jjoe64.graphview.GraphView
|
||||
<LinearLayout
|
||||
android:id="@+id/overview_iobgraph"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="100dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toTopOf="@+id/overview_accepttempbutton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
|
|
@ -534,10 +534,11 @@
|
|||
app:layout_constraintTop_toTopOf="@+id/overview_bggraph" />
|
||||
|
||||
|
||||
<com.jjoe64.graphview.GraphView
|
||||
<LinearLayout
|
||||
android:id="@+id/overview_iobgraph"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="100dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
|
|
@ -98,8 +98,8 @@
|
|||
android:layout_marginLeft="5dp"
|
||||
android:layout_marginRight="5dp"
|
||||
android:gravity="center_vertical|center_horizontal"
|
||||
android:paddingBottom="3dp"
|
||||
android:paddingTop="3dp"
|
||||
android:paddingBottom="3dp"
|
||||
android:text="@string/initializing"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</LinearLayout>
|
||||
|
@ -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 @@
|
|||
<View
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="2dip"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:background="@color/listdelimiter" />
|
||||
|
||||
|
||||
|
@ -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 @@
|
|||
<View
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="2dip"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:background="@color/listdelimiter" />
|
||||
|
||||
|
||||
|
@ -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 @@
|
|||
<View
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="2dip"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:background="@color/listdelimiter" />
|
||||
|
||||
<LinearLayout
|
||||
|
@ -351,8 +351,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" />
|
||||
|
||||
|
@ -373,10 +373,10 @@
|
|||
<View
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="2dip"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:background="@color/listdelimiter" />
|
||||
|
||||
</LinearLayout>
|
||||
|
@ -425,10 +425,10 @@
|
|||
<View
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="2dip"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:background="@color/listdelimiter" />
|
||||
|
||||
|
||||
|
@ -454,10 +454,10 @@
|
|||
<View
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="2dip"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:background="@color/listdelimiter" />
|
||||
|
||||
<LinearLayout
|
||||
|
@ -482,10 +482,10 @@
|
|||
<View
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="2dip"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:background="@color/listdelimiter" />
|
||||
|
||||
</LinearLayout>
|
||||
|
@ -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 @@
|
|||
|
||||
</RelativeLayout>
|
||||
|
||||
<com.jjoe64.graphview.GraphView
|
||||
<LinearLayout
|
||||
android:id="@+id/overview_iobgraph"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="100dp" />
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
<string name="objectives_useloop">Inhoud van loop plugin weergeven</string>
|
||||
<string name="objectives_usescale">Gebruik de schaalfunctie: houd de BG grafiek lang ingedrukt</string>
|
||||
<string name="objectives_button_enter">Enter</string>
|
||||
<string name="enter_code_obtained_from_developers_to_bypass_the_rest_of_objectives">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.</string>
|
||||
<string name="codeaccepted">Code geaccepteerd</string>
|
||||
<string name="codeinvalid">Code ongeldig</string>
|
||||
<string name="objectives_exam_objective">Bewijs je kennis</string>
|
||||
|
|
|
@ -275,7 +275,7 @@
|
|||
<string name="metadata_label_created_at">Created at</string>
|
||||
<string name="metadata_label_aaps_version">AAPS Version</string>
|
||||
<string name="metadata_label_aaps_flavour">Build Variant</string>
|
||||
<string name="metadata_label_device_name">Exporting device name</string>
|
||||
<string name="metadata_label_device_name">Exporting device patient name</string>
|
||||
<string name="metadata_label_device_model">Exporting device model</string>
|
||||
<string name="metadata_label_encryption">File encryption</string>
|
||||
|
||||
|
@ -610,6 +610,10 @@
|
|||
<string name="key_adult" translatable="false">adult</string>
|
||||
<string name="key_resistantadult" translatable="false">resistantadult</string>
|
||||
<string name="patientage_summary">Please select patient age to setup safety limits</string>
|
||||
<string name="patient_name">Patient name</string>
|
||||
<string name="patient_name_summary">Please provide patient name or nickname to differentiate among multiple setups</string>
|
||||
<string name="patient_name_default" comment="This is default patient display name, when user does not provide real one">User</string>
|
||||
<string name="key_patient_name" translatable="false">patient_name</string>
|
||||
<string name="key_i_understand" translatable="false">I_understand</string>
|
||||
<string name="Glimp">Glimp</string>
|
||||
<string name="needwhitelisting">%1$s needs battery optimalization whitelisting for proper performance</string>
|
||||
|
@ -1768,19 +1772,15 @@
|
|||
<!-- SMS Communicator & OTP Authenticator -->
|
||||
|
||||
<string name="key_smscommunicator_otp_enabled" translatable="false">smscommunicator_otp_enabled</string>
|
||||
<string name="key_smscommunicator_otp_name" translatable="false">smscommunicator_otp_name</string>
|
||||
<string name="key_smscommunicator_otp_password" translatable="false">smscommunicator_otp_password</string>
|
||||
<string name="key_smscommunicator_otp_secret" translatable="false">smscommunicator_otp_secret</string>
|
||||
|
||||
<string name="smscommunicator_default_user_display_name" comment="This is default user display name, shown by Authenticators">User</string>
|
||||
<string name="smscommunicator_code_from_authenticator_for" comment="This is continuation of sentence: To [ACTION] reply with code">from Authenticator app for: %1$s</string>
|
||||
|
||||
<string name="smscommunicator_otp_enabled">Enable Authenticator</string>
|
||||
<string name="smscommunicator_otp_enabled_summary">Authenticate commands using One Time Passwords generated by Google Authenticator or similar 2FA apps.</string>
|
||||
<string name="smscommunicator_otp_pin">Additional PIN at token end</string>
|
||||
<string name="smscommunicator_otp_pin_summary">Additional digits that should be memorised and glued at end of each generated One Time Password</string>
|
||||
<string name="smscommunicator_otp_name">User name for Authenticator</string>
|
||||
<string name="smscommunicator_otp_name_summary">User name displayed along with generated OTP on Authenticator App</string>
|
||||
|
||||
<string name="smscomunicator_tab_otp_label">Authenticator setup</string>
|
||||
|
||||
|
@ -1800,4 +1800,7 @@
|
|||
<string name="smscommunicator_otp_reset_warning">By reseting authenticator you make all already provisioned authenticators invalid. You will need to set up them again!</string>
|
||||
<string name="onconnect">On connect</string>
|
||||
<string name="ondisconnect">On disconnect</string>
|
||||
<string name="overview_show_predictions">Predictions</string>
|
||||
<string name="overview_show_deviationslope">Deviation slope</string>
|
||||
<string name="key_graphconfig" translatable="false">graphconfig</string>
|
||||
</resources>
|
||||
|
|
|
@ -20,6 +20,14 @@
|
|||
android:key="@string/key_language"
|
||||
android:title="@string/language" />
|
||||
|
||||
|
||||
<EditTextPreference
|
||||
android:inputType="textPersonName"
|
||||
android:key="@string/key_patient_name"
|
||||
android:title="@string/patient_name"
|
||||
android:summary="@string/patient_name_summary"
|
||||
/>
|
||||
|
||||
<PreferenceCategory android:title="@string/protection">
|
||||
|
||||
<Preference
|
||||
|
|
|
@ -49,14 +49,6 @@
|
|||
android:title="@string/smscommunicator_otp_pin"
|
||||
validate:testType="pinStrength" />
|
||||
|
||||
<info.nightscout.androidaps.utils.textValidator.ValidatingEditTextPreference
|
||||
android:dependency="@string/key_smscommunicator_remotecommandsallowed"
|
||||
android:key="@string/key_smscommunicator_otp_name"
|
||||
android:summary="@string/smscommunicator_otp_name_summary"
|
||||
android:title="@string/smscommunicator_otp_name"
|
||||
validate:testType="personName" />
|
||||
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</androidx.preference.PreferenceScreen>
|
|
@ -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<PluginBase?>()
|
||||
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()
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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"))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue