Merge branch 'dagger3' into AS0ref

This commit is contained in:
Milos Kozak 2020-04-07 12:49:21 +02:00 committed by GitHub
commit a4e7d1ee1f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
66 changed files with 1522 additions and 2825 deletions

View file

@ -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;

View file

@ -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)

View file

@ -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;
}

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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()

View file

@ -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();
});
}
}

View file

@ -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"),

View file

@ -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

View file

@ -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

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.plugins.aps.openAPSMA;
package info.nightscout.androidaps.plugins.aps.logger;
import org.mozilla.javascript.ScriptableObject;

View file

@ -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)

View file

@ -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());

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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 = ""
}
}

View file

@ -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());
}
}

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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))

View file

@ -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) {

View file

@ -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())

View file

@ -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
}

View file

@ -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)

View file

@ -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));

View file

@ -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()
}
}

View file

@ -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
}
}

View file

@ -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
}

View file

@ -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

View file

@ -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))
}
}
})
}

View file

@ -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" }
}

View file

@ -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"

View file

@ -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

View file

@ -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())
}
}

View file

@ -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;
}
}

View file

@ -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()));
}

View file

@ -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();

View file

@ -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);

View file

@ -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)

View file

@ -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
}
}

View file

@ -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)

View file

@ -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 {

View file

@ -20,4 +20,5 @@ interface ResourceHelper {
fun decodeResource(id : Int) : Bitmap
fun getDisplayMetrics(): DisplayMetrics
fun dpToPx(dp: Int): Int
fun shortTextMode(): Boolean
}

View file

@ -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
}

View file

@ -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" />

View file

@ -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"

View file

@ -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"

View file

@ -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"

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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

View file

@ -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>

View file

@ -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()

View file

@ -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() {

View file

@ -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")
}

View file

@ -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))

View file

@ -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"))
}
}