Merge branch 'andyrozman:aidex_support_v2' into aidex_support_v2

This commit is contained in:
Mark Breen 2022-04-19 01:38:35 +01:00 committed by GitHub
commit 0262e7f88e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
721 changed files with 11533 additions and 7470 deletions

View file

@ -44,5 +44,5 @@ Hints
* Start small, it is easier to review smaller changes that affect fewer parts of code * Start small, it is easier to review smaller changes that affect fewer parts of code
* Take a look into Issues list (https://github.com/nightscout/AndroidAPS/issues) - maybe there is something you can fix or implement * Take a look into Issues list (https://github.com/nightscout/AndroidAPS/issues) - maybe there is something you can fix or implement
* For new features, make sure there is Issue to track progress and have on-topic discussion * For new features, make sure there is Issue to track progress and have on-topic discussion
* Reach out to community, discuss idea on Gitter (https://gitter.im/MilosKozak/AndroidAPS) * Reach out to community, discuss idea on Discord (https://discord.gg/4fQUWHZ4Mw)
* Speak with other developers to minimise merge conflicts. Find out who worked, working or plan to work on speciffic issue or part of app * Speak with other developers to minimise merge conflicts. Find out who worked, working or plan to work on speciffic issue or part of app

View file

@ -109,7 +109,7 @@ android {
defaultConfig { defaultConfig {
multiDexEnabled true multiDexEnabled true
versionCode 1500 versionCode 1500
version "3.0.0.1-dev-aidex" version "3.0.0.1-dev-i"
buildConfigField "String", "VERSION", '"' + version + '"' buildConfigField "String", "VERSION", '"' + version + '"'
buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"' buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"'
buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"' buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"'
@ -167,9 +167,6 @@ android {
allprojects { allprojects {
repositories { repositories {
flatDir {
dirs 'libs'
}
} }
} }

View file

@ -41,14 +41,35 @@
<application <application
android:name=".MainApp" android:name=".MainApp"
android:allowBackup="true" android:allowBackup="true"
android:backupAgent=".utils.SPBackupAgent"
android:fullBackupOnly="false"
android:icon="${appIcon}" android:icon="${appIcon}"
android:label="@string/app_name" android:label="@string/app_name"
android:restoreAnyVersion="true"
android:roundIcon="${appIconRound}" android:roundIcon="${appIconRound}"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme.Launcher" android:theme="@style/AppTheme.Launcher" >
android:fullBackupOnly="false"
android:backupAgent=".utils.SPBackupAgent" <activity
android:restoreAnyVersion="true"> android:name=".widget.WidgetConfigureActivity"
android:theme="@android:style/Theme.Material.Dialog.NoActionBar"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter>
</activity>
<receiver
android:name=".widget.Widget"
android:exported="true" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_info" />
</receiver>
<meta-data <meta-data
android:name="com.google.android.backup.api_key" android:name="com.google.android.backup.api_key"
@ -59,6 +80,7 @@
android:resource="@xml/automotive_app_desc" /> android:resource="@xml/automotive_app_desc" />
<activity android:name=".MainActivity" <activity android:name=".MainActivity"
android:theme="@style/AppTheme"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
@ -76,12 +98,18 @@
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".plugins.general.maintenance.activities.PrefImportListActivity" /> <activity android:name=".plugins.general.maintenance.activities.PrefImportListActivity"
<activity android:name=".activities.HistoryBrowseActivity" /> android:theme="@style/AppTheme" />
<activity android:name=".activities.TreatmentsActivity" /> <activity android:name=".activities.HistoryBrowseActivity"
<activity android:name=".activities.SurveyActivity" /> android:theme="@style/AppTheme" />
<activity android:name=".activities.ProfileHelperActivity" /> <activity android:name=".activities.TreatmentsActivity"
<activity android:name=".activities.StatsActivity" /> android:theme="@style/AppTheme" />
<activity android:name=".activities.SurveyActivity"
android:theme="@style/AppTheme" />
<activity android:name=".activities.ProfileHelperActivity"
android:theme="@style/AppTheme" />
<activity android:name=".activities.StatsActivity"
android:theme="@style/AppTheme" />
<activity <activity
android:name="com.google.firebase.auth.internal.FederatedSignInActivity" android:name="com.google.firebase.auth.internal.FederatedSignInActivity"
android:excludeFromRecents="true" android:excludeFromRecents="true"
@ -125,9 +153,6 @@
</intent-filter> </intent-filter>
</receiver> </receiver>
<!-- Receiver keep alive, scheduled every 30 min -->
<receiver android:name=".receivers.KeepAliveReceiver" />
<!-- Receive ignore 5m, 15m, 30m requests for carb notifications --> <!-- Receive ignore 5m, 15m, 30m requests for carb notifications -->
<receiver android:name=".plugins.aps.loop.CarbSuggestionReceiver" /> <receiver android:name=".plugins.aps.loop.CarbSuggestionReceiver" />
@ -235,6 +260,7 @@
<activity <activity
android:name=".setupwizard.SetupWizardActivity" android:name=".setupwizard.SetupWizardActivity"
android:configChanges="orientation|keyboardHidden|screenSize" android:configChanges="orientation|keyboardHidden|screenSize"
android:theme="@style/AppTheme"
android:label="@string/title_activity_setup_wizard" /> android:label="@string/title_activity_setup_wizard" />
<activity <activity

View file

@ -202,161 +202,194 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
halfBasalTarget = 160; // when temptarget is 160 mg/dL, run 50% basal (120 = 75%; 140 = 60%) halfBasalTarget = 160; // when temptarget is 160 mg/dL, run 50% basal (120 = 75%; 140 = 60%)
// 80 mg/dL with low_temptarget_lowers_sensitivity would give 1.5x basal, but is limited to autosens_max (1.2x by default) // 80 mg/dL with low_temptarget_lowers_sensitivity would give 1.5x basal, but is limited to autosens_max (1.2x by default)
} }
if ( high_temptarget_raises_sensitivity && profile.temptargetSet && target_bg > normalTarget
|| profile.low_temptarget_lowers_sensitivity && profile.temptargetSet && target_bg < normalTarget ) {
// w/ target 100, temp target 110 = .89, 120 = 0.8, 140 = 0.67, 160 = .57, and 200 = .44
// e.g.: Sensitivity ratio set to 0.8 based on temp target of 120; Adjusting basal from 1.65 to 1.35; ISF from 58.9 to 73.6
//sensitivityRatio = 2/(2+(target_bg-normalTarget)/40);
var c = halfBasalTarget - normalTarget;
sensitivityRatio = c/(c+target_bg-normalTarget);
// limit sensitivityRatio to profile.autosens_max (1.2x by default)
sensitivityRatio = Math.min(sensitivityRatio, profile.autosens_max);
sensitivityRatio = round(sensitivityRatio,2);
console.log("Sensitivity ratio set to "+sensitivityRatio+" based on temp target of "+target_bg+"; ");
} else if (typeof autosens_data !== 'undefined' && autosens_data) {
sensitivityRatio = autosens_data.ratio;
console.log("Autosens ratio: "+sensitivityRatio+"; ");
}
if (sensitivityRatio) {
basal = profile.current_basal * sensitivityRatio;
basal = round_basal(basal, profile);
if (basal !== profile_current_basal) {
console.log("Adjusting basal from "+profile_current_basal+" to "+basal+"; ");
} else {
console.log("Basal unchanged: "+basal+"; ");
}
}
// adjust min, max, and target BG for sensitivity, such that 50% increase in ISF raises target from 100 to 120
if (profile.temptargetSet) {
//console.log("Temp Target set, not adjusting with autosens; ");
} else if (typeof autosens_data !== 'undefined' && autosens_data) {
if ( profile.sensitivity_raises_target && autosens_data.ratio < 1 || profile.resistance_lowers_target && autosens_data.ratio > 1 ) {
// with a target of 100, default 0.7-1.2 autosens min/max range would allow a 93-117 target range
min_bg = round((min_bg - 60) / autosens_data.ratio) + 60;
max_bg = round((max_bg - 60) / autosens_data.ratio) + 60;
var new_target_bg = round((target_bg - 60) / autosens_data.ratio) + 60;
// don't allow target_bg below 80
new_target_bg = Math.max(80, new_target_bg);
if (target_bg === new_target_bg) {
console.log("target_bg unchanged: "+new_target_bg+"; ");
} else {
console.log("target_bg from "+target_bg+" to "+new_target_bg+"; ");
}
target_bg = new_target_bg;
}
}
if (typeof iob_data === 'undefined' ) {
rT.error ='Error: iob_data undefined. ';
return rT;
}
var iobArray = iob_data;
if (typeof(iob_data.length) && iob_data.length > 1) {
iob_data = iobArray[0];
//console.error(JSON.stringify(iob_data[0]));
}
if (typeof iob_data.activity === 'undefined' || typeof iob_data.iob === 'undefined' ) {
rT.error ='Error: iob_data missing some property. ';
return rT;
}
var tick;
if (glucose_status.delta > -0.5) {
tick = "+" + round(glucose_status.delta,0);
} else {
tick = round(glucose_status.delta,0);
}
//var minDelta = Math.min(glucose_status.delta, glucose_status.short_avgdelta, glucose_status.long_avgdelta);
var minDelta = Math.min(glucose_status.delta, glucose_status.short_avgdelta);
var minAvgDelta = Math.min(glucose_status.short_avgdelta, glucose_status.long_avgdelta);
var maxDelta = Math.max(glucose_status.delta, glucose_status.short_avgdelta, glucose_status.long_avgdelta);
var profile_sens = round(profile.sens,1) var profile_sens = round(profile.sens,1)
var sens = profile.sens; var sens = profile.sens;
var now = new Date().getHours(); var now = new Date().getHours();
if (now < 1){ if (now < 1){
now = 1;} now = 1;}
else { else {
console.error("Time now is "+now+"; "); console.error("Time now is "+now+"; ");
} }
console.error(" "); //*********************************************************************************
console.error("++++++++++++++++++++++++++++++++++++++++++++++++++++++"); //** Start of Dynamic ISF code for predictions **
console.error("++ Dynamic ISF Beta 1.4 - Linear Extrapolation/TDD7 ++"); //*********************************************************************************
console.error("++++++++++++++++++++++++++++++++++++++++++++++++++++++");
console.error(" ");
if (meal_data.TDDAIMI7){ console.error("---------------------------------------------------------");
var tdd7 = meal_data.TDDAIMI7; console.error( " Dynamic ISF version Beta 1.5 ");
console.error("---------------------------------------------------------");
if (meal_data.TDDAIMI7){
var tdd7 = meal_data.TDDAIMI7;
} }
else{ else{
var tdd7 = ((basal * 12)*100)/21; var tdd7 = ((basal * 12)*100)/21;
} }
console.error("7-day average TDD is: " +tdd7+ "; "); console.error("7-day average TDD is: " +tdd7+ "; ");
console.error(" ");
if (meal_data.TDDLast24){ if (meal_data.TDDLast24){
var tdd_24 = meal_data.TDDLast24; var tdd_24 = meal_data.TDDLast24;
}
else {
var tdd_24 = (( basal * 24 ) * 2.8);
}
if (meal_data.TDDPUMP){
var tdd_pump = ( (meal_data.TDDPUMP / now ) * 24);
}
else {
var tdd_pump = (( basal * 24 ) * 2.8);
}
console.log("Rolling TDD for last 24 hours is: "+tdd_24+"; ");
/*var tdd_pump_now = meal_data.TDDPUMP;
var tdd_pump = ( tdd_pump_now / (now / 24));*/
var TDD = (tdd7 * 0.4) + (tdd_pump * 0.6);
console.error("Pump extrapolated TDD = "+tdd_pump+"; ");
//if (tdd7 > 0){
if ( tdd_pump > tdd7 && now < 5 || now < 7 && TDD < ( 0.8 * tdd7 ) ){
TDD = ( 0.8 * tdd7 );
console.log("Excess or too low insulin from pump so TDD set to "+TDD+" based on 75% of TDD7; ");
rT.reason += "TDD: " +TDD+ " due to low or high tdd from pump; ";
}
else if (tdd_pump > (1.75 * tdd7)){
TDD = tdd7;
console.error("TDD set to TDD7 due to high pump usage reported. TDD = "+TDD+"; ");
rT.reason += "TDD set to TDD7 due to high pump usage reported. TDD = "+TDD+"; ";
}
else if (tdd_pump < (0.33 * tdd7)){
TDD = (tdd7 * 0.25) + (tdd_pump * 0.75);
console.error("TDD weighted to pump due to low insulin usage. TDD = "+TDD+"; ");
rT.reason += "TDD weighted to pump due to low insulin usage. TDD = "+TDD+"; ";
}
else {
console.log("TDD = " +TDD+ " based on standard pump 60/tdd7 40 split; ");
rT.reason += "TDD: " +TDD+ " based on standard pump 60/tdd7 40 split; ";
}
var dynISFadjust = profile.DynISFAdjust;
var dynISFadjust = ( dynISFadjust / 100 );
var TDD = (dynISFadjust * TDD);
var variable_sens = (277700 / ( TDD * bg));
variable_sens = round(variable_sens,1);
if (dynISFadjust > 1 ) {
console.log("TDD adjustment factor is: " +dynISFadjust+"; ");
console.log("TDD adjusted to "+TDD+" using adjustment factor of "+dynISFadjust+"; ");
console.log("Current sensitivity for predictions is " +variable_sens+" based on current bg");
} }
else { else if (dynISFadjust < 1 ){
var tdd_24 = (( basal * 24 ) * 2.8); console.log("TDD adjustment factor is: " +dynISFadjust+"; ");
console.log("TDD adjusted to "+TDD+" using adjustment factor of "+dynISFadjust+"; ");
console.log("Current sensitivity for predictions is " +variable_sens+" based on current bg");
} else {
console.log("Current sensitivity for predictions is " +variable_sens+" based on current bg");
}
sens = variable_sens;
//*********************************************************************************
//** End of Dynamic ISF code for predictions **
//*********************************************************************************
if ( high_temptarget_raises_sensitivity && profile.temptargetSet && target_bg > normalTarget || profile.low_temptarget_lowers_sensitivity && profile.temptargetSet && target_bg < normalTarget ) {
// w/ target 100, temp target 110 = .89, 120 = 0.8, 140 = 0.67, 160 = .57, and 200 = .44
// e.g.: Sensitivity ratio set to 0.8 based on temp target of 120; Adjusting basal from 1.65 to 1.35; ISF from 58.9 to 73.6
//sensitivityRatio = 2/(2+(target_bg-normalTarget)/40);
var c = halfBasalTarget - normalTarget;
sensitivityRatio = c/(c+target_bg-normalTarget);
// limit sensitivityRatio to profile.autosens_max (1.2x by default)
sensitivityRatio = Math.min(sensitivityRatio, profile.autosens_max);
sensitivityRatio = round(sensitivityRatio,2);
console.log("Sensitivity ratio set to "+sensitivityRatio+" based on temp target of "+target_bg+"; ");
sens = sens / sensitivityRatio ;
sens = round(sens, 1);
console.log("ISF from "+variable_sens+" to "+sens+ "due to temp target; ");
}
else {
sensitivityRatio = ( tdd_24 / tdd7 );
}
if (sensitivityRatio > 1) {
sensitivityRatio = Math.min(sensitivityRatio, profile.autosens_max);
sensitivityRatio = round(sensitivityRatio,2);
console.log("Sensitivity ratio: "+sensitivityRatio+"; ");
}
else if( sensitivityRatio < 1) {
sensitivityRatio = Math.max(sensitivityRatio, profile.autosens_min);
sensitivityRatio = round(sensitivityRatio,2);
console.log("Sensitivity ratio: "+sensitivityRatio+"; ");
}
else {
console.log("Sensitivity ratio: "+sensitivityRatio+"; ");
}
if (sensitivityRatio && profile.openapsama_useautosens === true) {
basal = profile.current_basal * sensitivityRatio;
basal = round_basal(basal, profile);
if (basal !== profile_current_basal) {
console.log("Adjusting basal from "+profile_current_basal+" to "+basal+"; ");
} else {
console.log("Autosens disabled. Basal unchanged: "+basal+"; ");
}
} }
if (meal_data.TDDPUMP){ // adjust min, max, and target BG for sensitivity, such that 50% increase in ISF raises target from 100 to 120
var tdd_pump = ( (meal_data.TDDPUMP / now ) * 24); if (profile.temptargetSet) {
//console.log("Temp Target set, not adjusting with autosens; ");
} else {
if ( profile.sensitivity_raises_target && sensitivityRatio < 1 && profile.openapsama_useautosens === true || profile.resistance_lowers_target && sensitivityRatio > 1 && profile.openapsama_useautosens === true) {
// with a target of 100, default 0.7-1.2 autosens min/max range would allow a 93-117 target range
min_bg = round((min_bg - 60) / sensitivityRatio) + 60;
max_bg = round((max_bg - 60) / sensitivityRatio) + 60;
var new_target_bg = round((target_bg - 60) / sensitivityRatio) + 60;
// don't allow target_bg below 80
new_target_bg = Math.max(80, new_target_bg);
if (target_bg === new_target_bg) {
console.log("target_bg unchanged: "+new_target_bg+"; ");
} else {
console.log("target_bg from "+target_bg+" to "+new_target_bg+"; ");
}
target_bg = new_target_bg;
}
} }
else {
var tdd_pump = (( basal * 24 ) * 2.8);
if (typeof iob_data === 'undefined' ) {
rT.error ='Error: iob_data undefined. ';
return rT;
} }
var TDD = (tdd7 * 0.4) + (tdd_pump * 0.6);
console.error("Pump extrapolated TDD = "+tdd_pump+"; "); var iobArray = iob_data;
//if (tdd7 > 0){ if (typeof(iob_data.length) && iob_data.length > 1) {
if ( tdd_pump > tdd7 && now < 5 || now < 7 && TDD < ( 0.8 * tdd7 ) ){ iob_data = iobArray[0];
TDD = ( 0.8 * tdd7 ); //console.error(JSON.stringify(iob_data[0]));
console.log("Excess or too low insulin from pump so TDD set to "+TDD+" based on 75% of TDD7; "); }
rT.reason += "TDD: " +TDD+ " due to low or high tdd from pump; ";
}
else if (tdd_pump > (1.75 * tdd7)){ if (typeof iob_data.activity === 'undefined' || typeof iob_data.iob === 'undefined' ) {
TDD = tdd7; rT.error ='Error: iob_data missing some property. ';
console.error("TDD set to TDD7 due to high pump usage reported. TDD = "+TDD+"; "); return rT;
rT.reason += "TDD set to TDD7 due to high pump usage reported. TDD = "+TDD+"; "; }
}
var tick;
else if (tdd_pump < (0.33 * tdd7)){ if (glucose_status.delta > -0.5) {
TDD = (tdd7 * 0.25) + (tdd_pump * 0.75); tick = "+" + round(glucose_status.delta,0);
console.error("TDD weighted to pump due to low insulin usage. TDD = "+TDD+"; "); } else {
rT.reason += "TDD weighted to pump due to low insulin usage. TDD = "+TDD+"; "; tick = round(glucose_status.delta,0);
} }
//var minDelta = Math.min(glucose_status.delta, glucose_status.short_avgdelta, glucose_status.long_avgdelta);
var minDelta = Math.min(glucose_status.delta, glucose_status.short_avgdelta);
var minAvgDelta = Math.min(glucose_status.short_avgdelta, glucose_status.long_avgdelta);
var maxDelta = Math.max(glucose_status.delta, glucose_status.short_avgdelta, glucose_status.long_avgdelta);
else {
console.log("TDD = " +TDD+ " based on standard pump 60/tdd7 40 split; ");
rT.reason += "TDD: " +TDD+ " based on standard pump 60/tdd7 40 split; ";
}
console.error(" ");
var variable_sens = (277700 / (TDD * bg));
variable_sens = round(variable_sens,1);
console.log("Current sensitivity for predictions is " +variable_sens+" based on current bg");
console.error(" ");
sens = variable_sens;
if ( high_temptarget_raises_sensitivity && profile.temptargetSet && target_bg > normalTarget || profile.low_temptarget_lowers_sensitivity && profile.temptargetSet && target_bg < normalTarget ) {
sens = sens / sensitivityRatio ;
sens = round(sens, 1);
console.log("ISF from "+variable_sens+" to "+sens+ "due to temp target; ");
} else {
sens = sens;
sens = round(sens, 1);
}
console.error("; CR:",profile.carb_ratio); console.error("; CR:",profile.carb_ratio);
@ -772,19 +805,27 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
console.error("UAM Impact:",uci,"mg/dL per 5m; UAM Duration:",UAMduration,"hours"); console.error("UAM Impact:",uci,"mg/dL per 5m; UAM Duration:",UAMduration,"hours");
console.log("EventualBG is" +eventualBG+" ;"); console.log("EventualBG is" +eventualBG+" ;");
if (bg > target_bg && glucose_status.delta < 3 && glucose_status.delta > -3 && glucose_status.short_avgdelta > -3 && glucose_status.short_avgdelta < 3 && eventualBG > target_bg && eventualBG < bg ) {
var future_sens = ( 277700 / (TDD * ((eventualBG * 0.5) + (bg * 0.5) ) ) );
console.log("Future state sensitivity is " +future_sens+" based on eventual and current bg due to flat glucose level above target");
rT.reason += "Dosing sensitivity: " +future_sens+" using eventual BG;";
}
else if( glucose_status.delta > 0 && eventualBG > target_bg ) {
var future_sens = ( 277700 / (TDD * bg) );
console.log("Future state sensitivity is " +future_sens+" using current bg due to small delta or variation");
rT.reason += "Dosing sensitivity: " +future_sens+" using current BG;";
}
else {
var future_sens = ( 277700 / (TDD * eventualBG) );
console.log("Future state sensitivity is " +future_sens+" based on eventual bg due to -ve delta");
rT.reason += "Dosing sensitivity: " +future_sens+" using eventual BG;";
}
var future_sens = round(future_sens,1);
if( glucose_status.delta >= 0 || bg > 60 && glucose_status.delta < 2 && glucose_status.delta > -2 && glucose_status.short_avgdelta > -2 && glucose_status.short_avgdelta < 2 || eventualBG > target_bg && glucose_status.delta < 0 ) {
var future_sens = ( 277700 / (TDD * bg) );
console.log("Future state sensitivity is " +future_sens+" using current bg due to no COB & small delta or variation");
rT.reason += "Dosing sensitivity: " +future_sens+" using current BG;";
}
else {
var future_sens = ( 277700 / (TDD * eventualBG));
console.log("Future state sensitivity is " +future_sens+" based on eventual bg due to -ve delta");
rT.reason += "Dosing sensitivity: " +future_sens+" using eventual BG;";
}
var future_sens = round(future_sens,1);
minIOBPredBG = Math.max(39,minIOBPredBG); minIOBPredBG = Math.max(39,minIOBPredBG);

View file

@ -7,6 +7,7 @@ import android.os.Bundle
import android.os.PersistableBundle import android.os.PersistableBundle
import android.text.SpannableString import android.text.SpannableString
import android.text.method.LinkMovementMethod import android.text.method.LinkMovementMethod
import android.text.style.ForegroundColorSpan
import android.text.util.Linkify import android.text.util.Linkify
import android.util.TypedValue import android.util.TypedValue
import android.view.Menu import android.view.Menu
@ -19,10 +20,10 @@ import android.widget.EditText
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.ActionBarDrawerToggle import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.core.view.GravityCompat import androidx.core.view.GravityCompat
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.tabs.TabLayoutMediator import com.google.android.material.tabs.TabLayoutMediator
import com.google.firebase.crashlytics.FirebaseCrashlytics import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.joanzapata.iconify.Iconify import com.joanzapata.iconify.Iconify
@ -33,6 +34,7 @@ import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.databinding.ActivityMainBinding import info.nightscout.androidaps.databinding.ActivityMainBinding
import info.nightscout.androidaps.events.EventAppExit import info.nightscout.androidaps.events.EventAppExit
import info.nightscout.androidaps.events.EventInitializationChanged
import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventRebuildTabs import info.nightscout.androidaps.events.EventRebuildTabs
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
@ -50,6 +52,7 @@ import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.extensions.isRunningRealPumpTest import info.nightscout.androidaps.utils.extensions.isRunningRealPumpTest
import info.nightscout.androidaps.utils.locale.LocaleHelper import info.nightscout.androidaps.utils.locale.LocaleHelper
import info.nightscout.androidaps.utils.protection.PasswordCheck
import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.tabs.TabPageAdapter import info.nightscout.androidaps.utils.tabs.TabPageAdapter
@ -67,7 +70,6 @@ class MainActivity : NoSplashAppCompatActivity() {
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
@Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var rxBus: RxBus
@Inject lateinit var androidPermission: AndroidPermission @Inject lateinit var androidPermission: AndroidPermission
@Inject lateinit var sp: SP @Inject lateinit var sp: SP
@Inject lateinit var versionCheckerUtils: VersionCheckerUtils @Inject lateinit var versionCheckerUtils: VersionCheckerUtils
@ -84,12 +86,13 @@ class MainActivity : NoSplashAppCompatActivity() {
@Inject lateinit var config: Config @Inject lateinit var config: Config
@Inject lateinit var uel: UserEntryLogger @Inject lateinit var uel: UserEntryLogger
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var passwordCheck: PasswordCheck
private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle
private var pluginPreferencesMenuItem: MenuItem? = null private var pluginPreferencesMenuItem: MenuItem? = null
private var menu: Menu? = null private var menu: Menu? = null
private var menuOpen = false private var menuOpen = false
private var isProtectionCheckActive = false
private lateinit var binding: ActivityMainBinding private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -115,6 +118,7 @@ class MainActivity : NoSplashAppCompatActivity() {
override fun onPageSelected(position: Int) { override fun onPageSelected(position: Int) {
setPluginPreferenceMenuName() setPluginPreferenceMenuName()
checkPluginPreferences(binding.mainPager) checkPluginPreferences(binding.mainPager)
setDisabledMenuItemColorPluginPreferences()
} }
}) })
@ -134,6 +138,12 @@ class MainActivity : NoSplashAppCompatActivity() {
.toObservable(EventPreferenceChange::class.java) .toObservable(EventPreferenceChange::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ processPreferenceChange(it) }, fabricPrivacy::logException) .subscribe({ processPreferenceChange(it) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventInitializationChanged::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({
passwordCheck.passwordResetCheck(this)
}, fabricPrivacy::logException)
if (startWizard() && !isRunningRealPumpTest()) { if (startWizard() && !isRunningRealPumpTest()) {
protectionCheck.queryProtection(this, ProtectionCheck.Protection.PREFERENCES, { protectionCheck.queryProtection(this, ProtectionCheck.Protection.PREFERENCES, {
startActivity(Intent(this, SetupWizardActivity::class.java)) startActivity(Intent(this, SetupWizardActivity::class.java))
@ -168,10 +178,13 @@ class MainActivity : NoSplashAppCompatActivity() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
protectionCheck.queryProtection(this, ProtectionCheck.Protection.APPLICATION, null, if (!isProtectionCheckActive) {
UIRunnable { OKDialog.show(this, "", rh.gs(R.string.authorizationfailed)) { finish() } }, isProtectionCheckActive = true
UIRunnable { OKDialog.show(this, "", rh.gs(R.string.authorizationfailed)) { finish() } } protectionCheck.queryProtection(this, ProtectionCheck.Protection.APPLICATION, UIRunnable { isProtectionCheckActive = false },
) UIRunnable { OKDialog.show(this, "", rh.gs(R.string.authorizationfailed)) { isProtectionCheckActive = false; finish() } },
UIRunnable { OKDialog.show(this, "", rh.gs(R.string.authorizationfailed)) { isProtectionCheckActive = false; finish() } }
)
}
} }
private fun setWakeLock() { private fun setWakeLock() {
@ -252,6 +265,14 @@ class MainActivity : NoSplashAppCompatActivity() {
return super.dispatchTouchEvent(event) return super.dispatchTouchEvent(event)
} }
private fun setDisabledMenuItemColorPluginPreferences() {
if (pluginPreferencesMenuItem?.isEnabled == false) {
val spanString = SpannableString(this.menu?.findItem(R.id.nav_plugin_preferences)?.title.toString())
spanString.setSpan(ForegroundColorSpan(rh.gac(R.attr.disabledTextColor)), 0, spanString.length, 0)
this.menu?.findItem(R.id.nav_plugin_preferences)?.title = spanString
}
}
private fun setPluginPreferenceMenuName() { private fun setPluginPreferenceMenuName() {
if (binding.mainPager.currentItem >= 0) { if (binding.mainPager.currentItem >= 0) {
val plugin = (binding.mainPager.adapter as TabPageAdapter).getPluginAt(binding.mainPager.currentItem) val plugin = (binding.mainPager.adapter as TabPageAdapter).getPluginAt(binding.mainPager.currentItem)
@ -270,7 +291,7 @@ class MainActivity : NoSplashAppCompatActivity() {
} }
override fun onPanelClosed(featureId: Int, menu: Menu) { override fun onPanelClosed(featureId: Int, menu: Menu) {
menuOpen = false; menuOpen = false
super.onPanelClosed(featureId, menu) super.onPanelClosed(featureId, menu)
} }
@ -280,6 +301,7 @@ class MainActivity : NoSplashAppCompatActivity() {
pluginPreferencesMenuItem = menu.findItem(R.id.nav_plugin_preferences) pluginPreferencesMenuItem = menu.findItem(R.id.nav_plugin_preferences)
setPluginPreferenceMenuName() setPluginPreferenceMenuName()
checkPluginPreferences(binding.mainPager) checkPluginPreferences(binding.mainPager)
setDisabledMenuItemColorPluginPreferences()
return true return true
} }
@ -320,7 +342,7 @@ class MainActivity : NoSplashAppCompatActivity() {
message += rh.gs(R.string.about_link_urls) message += rh.gs(R.string.about_link_urls)
val messageSpanned = SpannableString(message) val messageSpanned = SpannableString(message)
Linkify.addLinks(messageSpanned, Linkify.WEB_URLS) Linkify.addLinks(messageSpanned, Linkify.WEB_URLS)
AlertDialog.Builder(this, R.style.DialogTheme) MaterialAlertDialogBuilder(this, R.style.DialogTheme)
.setTitle(rh.gs(R.string.app_name) + " " + BuildConfig.VERSION) .setTitle(rh.gs(R.string.app_name) + " " + BuildConfig.VERSION)
.setIcon(iconsProvider.getIcon()) .setIcon(iconsProvider.getIcon())
.setMessage(messageSpanned) .setMessage(messageSpanned)

View file

@ -6,6 +6,13 @@ import android.content.IntentFilter
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.net.wifi.WifiManager import android.net.wifi.WifiManager
import android.os.Build import android.os.Build
import android.os.Handler
import android.os.HandlerThread
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.work.Data
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.PeriodicWorkRequest
import androidx.work.WorkManager
import com.uber.rxdogtag.RxDogTag import com.uber.rxdogtag.RxDogTag
import dagger.android.AndroidInjector import dagger.android.AndroidInjector
import dagger.android.DaggerApplication import dagger.android.DaggerApplication
@ -20,24 +27,23 @@ import info.nightscout.androidaps.di.StaticInjector
import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.ConfigBuilder import info.nightscout.androidaps.interfaces.ConfigBuilder
import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.configBuilder.PluginStore import info.nightscout.androidaps.plugins.configBuilder.PluginStore
import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerUtils import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerUtils
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationStore import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationStore
import info.nightscout.androidaps.receivers.BTReceiver import info.nightscout.androidaps.plugins.general.themes.ThemeSwitcherPlugin
import info.nightscout.androidaps.receivers.ChargingStateReceiver import info.nightscout.androidaps.receivers.*
import info.nightscout.androidaps.receivers.KeepAliveReceiver.KeepAliveManager
import info.nightscout.androidaps.receivers.NetworkChangeReceiver
import info.nightscout.androidaps.receivers.TimeDateOrTZChangeReceiver
import info.nightscout.androidaps.services.AlarmSoundServiceHelper import info.nightscout.androidaps.services.AlarmSoundServiceHelper
import info.nightscout.androidaps.utils.ActivityMonitor import info.nightscout.androidaps.utils.ActivityMonitor
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.LocalAlertUtils
import info.nightscout.androidaps.utils.ProcessLifecycleListener
import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.locale.LocaleHelper import info.nightscout.androidaps.utils.locale.LocaleHelper
import info.nightscout.androidaps.utils.protection.PasswordCheck import info.nightscout.androidaps.widget.updateWidget
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.exceptions.UndeliverableException import io.reactivex.rxjava3.exceptions.UndeliverableException
@ -46,6 +52,7 @@ import io.reactivex.rxjava3.plugins.RxJavaPlugins
import net.danlew.android.joda.JodaTimeAndroid import net.danlew.android.joda.JodaTimeAndroid
import java.io.IOException import java.io.IOException
import java.net.SocketException import java.net.SocketException
import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
class MainApp : DaggerApplication() { class MainApp : DaggerApplication() {
@ -60,16 +67,20 @@ class MainApp : DaggerApplication() {
@Inject lateinit var config: Config @Inject lateinit var config: Config
@Inject lateinit var buildHelper: BuildHelper @Inject lateinit var buildHelper: BuildHelper
@Inject lateinit var configBuilder: ConfigBuilder @Inject lateinit var configBuilder: ConfigBuilder
@Inject lateinit var keepAliveManager: KeepAliveManager
@Inject lateinit var plugins: List<@JvmSuppressWildcards PluginBase> @Inject lateinit var plugins: List<@JvmSuppressWildcards PluginBase>
@Inject lateinit var compatDBHelper: CompatDBHelper @Inject lateinit var compatDBHelper: CompatDBHelper
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
@Inject lateinit var dateUtil: DateUtil @Inject lateinit var dateUtil: DateUtil
@Inject lateinit var staticInjector: StaticInjector// TODO avoid , here fake only to initialize @Inject lateinit var staticInjector: StaticInjector// TODO avoid , here fake only to initialize
@Inject lateinit var uel: UserEntryLogger @Inject lateinit var uel: UserEntryLogger
@Inject lateinit var passwordCheck: PasswordCheck
@Inject lateinit var alarmSoundServiceHelper: AlarmSoundServiceHelper @Inject lateinit var alarmSoundServiceHelper: AlarmSoundServiceHelper
@Inject lateinit var notificationStore: NotificationStore @Inject lateinit var notificationStore: NotificationStore
@Inject lateinit var processLifecycleListener: ProcessLifecycleListener
@Inject lateinit var profileSwitchPlugin: ThemeSwitcherPlugin
@Inject lateinit var localAlertUtils: LocalAlertUtils
private var handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper)
private lateinit var refreshWidget: Runnable
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
@ -77,6 +88,7 @@ class MainApp : DaggerApplication() {
RxDogTag.install() RxDogTag.install()
setRxErrorHandler() setRxErrorHandler()
LocaleHelper.update(this) LocaleHelper.update(this)
ProcessLifecycleOwner.get().lifecycle.addObserver(processLifecycleListener)
var gitRemote: String? = BuildConfig.REMOTE var gitRemote: String? = BuildConfig.REMOTE
var commitHash: String? = BuildConfig.HEAD var commitHash: String? = BuildConfig.HEAD
@ -84,21 +96,10 @@ class MainApp : DaggerApplication() {
gitRemote = null gitRemote = null
commitHash = null commitHash = null
} }
disposable += repository.runTransaction(VersionChangeTransaction(BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE, gitRemote, commitHash)).subscribe()
if (sp.getBoolean(R.string.key_ns_logappstartedevent, config.APS))
disposable += repository
.runTransaction(
InsertIfNewByTimestampTherapyEventTransaction(
timestamp = dateUtil.now(),
type = TherapyEvent.Type.NOTE,
note = getString(info.nightscout.androidaps.core.R.string.androidaps_start) + " - " + Build.MANUFACTURER + " " + Build.MODEL,
glucoseUnit = TherapyEvent.GlucoseUnit.MGDL
)
)
.subscribe()
disposable += compatDBHelper.dbChangeDisposable() disposable += compatDBHelper.dbChangeDisposable()
registerActivityLifecycleCallbacks(activityMonitor) registerActivityLifecycleCallbacks(activityMonitor)
JodaTimeAndroid.init(this) JodaTimeAndroid.init(this)
profileSwitchPlugin.setThemeMode()
aapsLogger.debug("Version: " + BuildConfig.VERSION_NAME) aapsLogger.debug("Version: " + BuildConfig.VERSION_NAME)
aapsLogger.debug("BuildVersion: " + BuildConfig.BUILDVERSION) aapsLogger.debug("BuildVersion: " + BuildConfig.BUILDVERSION)
aapsLogger.debug("Remote: " + BuildConfig.REMOTE) aapsLogger.debug("Remote: " + BuildConfig.REMOTE)
@ -113,10 +114,38 @@ class MainApp : DaggerApplication() {
// Register all tabs in app here // Register all tabs in app here
pluginStore.plugins = plugins pluginStore.plugins = plugins
configBuilder.initialize() configBuilder.initialize()
keepAliveManager.setAlarm(this)
disposable += repository.runTransaction(VersionChangeTransaction(BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE, gitRemote, commitHash)).subscribe()
if (sp.getBoolean(R.string.key_ns_logappstartedevent, config.APS))
disposable += repository
.runTransaction(
InsertIfNewByTimestampTherapyEventTransaction(
timestamp = dateUtil.now(),
type = TherapyEvent.Type.NOTE,
note = getString(info.nightscout.androidaps.core.R.string.androidaps_start) + " - " + Build.MANUFACTURER + " " + Build.MODEL,
glucoseUnit = TherapyEvent.GlucoseUnit.MGDL
)
)
.subscribe()
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
"KeepAlive",
ExistingPeriodicWorkPolicy.REPLACE,
PeriodicWorkRequest.Builder(KeepAliveWorker::class.java, 15, TimeUnit.MINUTES)
.setInputData(Data.Builder().putString("schedule", "KeepAlive").build())
.setInitialDelay(5, TimeUnit.SECONDS)
.build()
)
localAlertUtils.shortenSnoozeInterval()
localAlertUtils.preSnoozeAlarms()
doMigrations() doMigrations()
uel.log(UserEntry.Action.START_AAPS, UserEntry.Sources.Aaps) uel.log(UserEntry.Action.START_AAPS, UserEntry.Sources.Aaps)
passwordCheck.passwordResetCheck(this)
// schedule widget update
refreshWidget = Runnable {
handler.postDelayed(refreshWidget, 60000)
updateWidget(this)
}
handler.postDelayed(refreshWidget, 60000)
} }
private fun setRxErrorHandler() { private fun setRxErrorHandler() {
@ -186,7 +215,6 @@ class MainApp : DaggerApplication() {
override fun onTerminate() { override fun onTerminate() {
aapsLogger.debug(LTag.CORE, "onTerminate") aapsLogger.debug(LTag.CORE, "onTerminate")
unregisterActivityLifecycleCallbacks(activityMonitor) unregisterActivityLifecycleCallbacks(activityMonitor)
keepAliveManager.cancelAlarm(this)
alarmSoundServiceHelper.stopService(this) alarmSoundServiceHelper.stopService(this)
super.onTerminate() super.onTerminate()
} }

View file

@ -2,7 +2,7 @@ package info.nightscout.androidaps.activities
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.DatePickerDialog import android.app.DatePickerDialog
import android.graphics.Color import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.util.DisplayMetrics import android.util.DisplayMetrics
import android.view.ViewGroup import android.view.ViewGroup
@ -18,21 +18,19 @@ import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.events.EventCustomCalculationFinished import info.nightscout.androidaps.events.EventCustomCalculationFinished
import info.nightscout.androidaps.events.EventRefreshOverview import info.nightscout.androidaps.events.EventRefreshOverview
import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.extensions.toVisibilityKeepSpace
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.Config import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.Loop import info.nightscout.androidaps.interfaces.Loop
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus
import info.nightscout.androidaps.plugins.general.overview.OverviewData import info.nightscout.androidaps.plugins.general.overview.OverviewData
import info.nightscout.androidaps.plugins.general.overview.OverviewMenus import info.nightscout.androidaps.plugins.general.overview.OverviewMenus
import info.nightscout.androidaps.plugins.general.overview.events.EventUpdateOverviewGraph
import info.nightscout.androidaps.plugins.general.overview.graphData.GraphData import info.nightscout.androidaps.plugins.general.overview.graphData.GraphData
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventBucketedDataCreated
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DefaultValueHelper import info.nightscout.androidaps.utils.DefaultValueHelper
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
@ -40,6 +38,7 @@ import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.Translator import info.nightscout.androidaps.utils.Translator
import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.workflow.CalculationWorkflow
import info.nightscout.shared.logging.LTag import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
@ -52,15 +51,11 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
@Inject lateinit var injector: HasAndroidInjector @Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var rxBus: RxBus
@Inject lateinit var sp: SP @Inject lateinit var sp: SP
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var defaultValueHelper: DefaultValueHelper @Inject lateinit var defaultValueHelper: DefaultValueHelper
@Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var buildHelper: BuildHelper @Inject lateinit var buildHelper: BuildHelper
@Inject lateinit var sensitivityOref1Plugin: SensitivityOref1Plugin
@Inject lateinit var sensitivityAAPSPlugin: SensitivityAAPSPlugin
@Inject lateinit var sensitivityWeightedAveragePlugin: SensitivityWeightedAveragePlugin
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
@Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var overviewMenus: OverviewMenus @Inject lateinit var overviewMenus: OverviewMenus
@ -69,6 +64,9 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
@Inject lateinit var loop: Loop @Inject lateinit var loop: Loop
@Inject lateinit var nsDeviceStatus: NSDeviceStatus @Inject lateinit var nsDeviceStatus: NSDeviceStatus
@Inject lateinit var translator: Translator @Inject lateinit var translator: Translator
@Inject lateinit var context: Context
@Inject lateinit var dataWorker: DataWorker
@Inject lateinit var calculationWorkflow: CalculationWorkflow
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
@ -91,6 +89,17 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
setContentView(binding.root) setContentView(binding.root)
// We don't want to use injected singletons but own instance working on top of different data // We don't want to use injected singletons but own instance working on top of different data
overviewData =
OverviewData(
aapsLogger,
rh,
dateUtil,
sp,
activePlugin,
defaultValueHelper,
profileFunction,
repository
)
iobCobCalculator = iobCobCalculator =
IobCobCalculatorPlugin( IobCobCalculatorPlugin(
injector, injector,
@ -101,30 +110,11 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
rh, rh,
profileFunction, profileFunction,
activePlugin, activePlugin,
sensitivityOref1Plugin,
sensitivityAAPSPlugin,
sensitivityWeightedAveragePlugin,
fabricPrivacy, fabricPrivacy,
dateUtil, dateUtil,
repository
)
overviewData =
OverviewData(
injector,
aapsLogger,
rh,
dateUtil,
sp,
activePlugin,
defaultValueHelper,
profileFunction,
config,
loop,
nsDeviceStatus,
repository, repository,
overviewMenus, overviewData,
iobCobCalculator, calculationWorkflow
translator
) )
binding.left.setOnClickListener { binding.left.setOnClickListener {
@ -179,7 +169,7 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
val cal = Calendar.getInstance() val cal = Calendar.getInstance()
cal.timeInMillis = overviewData.fromTime cal.timeInMillis = overviewData.fromTime
DatePickerDialog( DatePickerDialog(
this, R.style.MaterialPickerTheme, this,
dateSetListener, dateSetListener,
cal.get(Calendar.YEAR), cal.get(Calendar.YEAR),
cal.get(Calendar.MONTH), cal.get(Calendar.MONTH),
@ -188,18 +178,19 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
} }
val dm = DisplayMetrics() val dm = DisplayMetrics()
@Suppress("DEPRECATION")
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R)
display?.getRealMetrics(dm) display?.getRealMetrics(dm)
else else
@Suppress("DEPRECATION") windowManager.defaultDisplay.getMetrics(dm) windowManager.defaultDisplay.getMetrics(dm)
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 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
binding.bgGraph.gridLabelRenderer?.gridColor = rh.gc(R.color.graphgrid) binding.bgGraph.gridLabelRenderer?.gridColor = rh.gac(this, R.attr.graphgrid)
binding.bgGraph.gridLabelRenderer?.reloadStyles() binding.bgGraph.gridLabelRenderer?.reloadStyles()
binding.bgGraph.gridLabelRenderer?.labelVerticalWidth = axisWidth binding.bgGraph.gridLabelRenderer?.labelVerticalWidth = axisWidth
overviewMenus.setupChartMenu(binding.chartMenuButton) overviewMenus.setupChartMenu(context, binding.chartMenuButton)
prepareGraphsIfNeeded(overviewMenus.setting.size) prepareGraphsIfNeeded(overviewMenus.setting.size)
savedInstanceState?.let { bundle -> savedInstanceState?.let { bundle ->
rangeToDisplay = bundle.getInt("rangeToDisplay", 0) rangeToDisplay = bundle.getInt("rangeToDisplay", 0)
@ -211,7 +202,7 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
disposable.clear() disposable.clear()
iobCobCalculator.stopCalculation("onPause") calculationWorkflow.stopCalculation(CalculationWorkflow.HISTORY_CALCULATION, "onPause")
} }
@Synchronized @Synchronized
@ -233,22 +224,11 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
disposable += rxBus disposable += rxBus
.toObservable(EventIobCalculationProgress::class.java) .toObservable(EventIobCalculationProgress::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ .subscribe({ updateCalcProgress(it.pass.finalPercent(it.progressPct)) }, fabricPrivacy::logException)
if (it.cause is EventCustomCalculationFinished)
binding.overviewIobcalculationprogess.text = it.progress
}, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventRefreshOverview::class.java) .toObservable(EventUpdateOverviewGraph::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ updateGUI("EventRefreshOverview") }, fabricPrivacy::logException) .subscribe({ updateGUI("EventRefreshOverview") }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventBucketedDataCreated::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({
overviewData.prepareBucketedData("EventBucketedDataCreated")
overviewData.prepareBgData("EventBucketedDataCreated")
rxBus.send(EventRefreshOverview("EventBucketedDataCreated"))
}, fabricPrivacy::logException)
if (overviewData.fromTime == 0L) { if (overviewData.fromTime == 0L) {
// set start of current day // set start of current day
@ -279,12 +259,12 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
val graph = GraphView(this) val graph = GraphView(this)
graph.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, rh.dpToPx(100)).also { it.setMargins(0, rh.dpToPx(15), 0, rh.dpToPx(10)) } graph.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, rh.dpToPx(100)).also { it.setMargins(0, rh.dpToPx(15), 0, rh.dpToPx(10)) }
graph.gridLabelRenderer?.gridColor = rh.gc(R.color.graphgrid) graph.gridLabelRenderer?.gridColor = rh.gac(R.attr.graphgrid)
graph.gridLabelRenderer?.reloadStyles() graph.gridLabelRenderer?.reloadStyles()
graph.gridLabelRenderer?.isHorizontalLabelsVisible = false graph.gridLabelRenderer?.isHorizontalLabelsVisible = false
graph.gridLabelRenderer?.labelVerticalWidth = axisWidth graph.gridLabelRenderer?.labelVerticalWidth = axisWidth
graph.gridLabelRenderer?.numVerticalLabels = 3 graph.gridLabelRenderer?.numVerticalLabels = 3
graph.viewport.backgroundColor = Color.argb(20, 255, 255, 255) // 8% of gray graph.viewport.backgroundColor = rh.gac(this, R.attr.viewPortbackgroundColor)
relativeLayout.addView(graph) relativeLayout.addView(graph)
val label = TextView(this) val label = TextView(this)
@ -304,17 +284,7 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
@Suppress("SameParameterValue") @Suppress("SameParameterValue")
private fun loadAll(from: String) { private fun loadAll(from: String) {
updateDate() updateDate()
Thread { runCalculation(from)
overviewData.prepareBgData("$from")
overviewData.prepareTreatmentsData(from)
rxBus.send(EventRefreshOverview("loadAll_$from"))
overviewData.prepareTemporaryTargetData(from)
rxBus.send(EventRefreshOverview("loadAll_$from"))
overviewData.prepareBasalData(from)
rxBus.send(EventRefreshOverview(from))
aapsLogger.debug(LTag.UI, "loadAll $from finished")
runCalculation(from)
}.start()
} }
private fun setTime(start: Long) { private fun setTime(start: Long) {
@ -335,25 +305,32 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
} }
private fun runCalculation(from: String) { private fun runCalculation(from: String) {
Thread { calculationWorkflow.runCalculation(
iobCobCalculator.stopCalculation(from) CalculationWorkflow.HISTORY_CALCULATION,
iobCobCalculator.stopCalculationTrigger = false iobCobCalculator,
iobCobCalculator.runCalculation(from, overviewData.toTime, bgDataReload = true, limitDataToOldestAvailable = false, cause = EventCustomCalculationFinished()) overviewData,
}.start() from,
overviewData.toTime,
bgDataReload = true,
limitDataToOldestAvailable = false,
cause = EventCustomCalculationFinished(),
runLoop = false
)
} }
@Volatile @Volatile
var runningRefresh = false var runningRefresh = false
@Suppress("SameParameterValue")
private fun refreshLoop(from: String) { private fun refreshLoop(from: String) {
if (runningRefresh) return if (runningRefresh) return
runningRefresh = true runningRefresh = true
overviewData.prepareIobAutosensData(from)
rxBus.send(EventRefreshOverview(from)) rxBus.send(EventRefreshOverview(from))
aapsLogger.debug(LTag.UI, "refreshLoop finished") aapsLogger.debug(LTag.UI, "refreshLoop finished")
runningRefresh = false runningRefresh = false
} }
fun updateDate() { private fun updateDate() {
binding.date.text = dateUtil.dateAndTimeString(overviewData.fromTime) binding.date.text = dateUtil.dateAndTimeString(overviewData.fromTime)
binding.zoom.text = rangeToDisplay.toString() binding.zoom.text = rangeToDisplay.toString()
} }
@ -372,6 +349,8 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
graphData.addBgReadings(menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal]) graphData.addBgReadings(menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal])
if (buildHelper.isDev()) graphData.addBucketedData() if (buildHelper.isDev()) graphData.addBucketedData()
graphData.addTreatments() graphData.addTreatments()
if (menuChartSettings[0][OverviewMenus.CharType.TREAT.ordinal])
graphData.addTherapyEvents()
if (menuChartSettings[0][OverviewMenus.CharType.ACT.ordinal]) if (menuChartSettings[0][OverviewMenus.CharType.ACT.ordinal])
graphData.addActivity(0.8) graphData.addActivity(0.8)
if (pump.pumpDescription.isTempBasalCapable && menuChartSettings[0][OverviewMenus.CharType.BAS.ordinal]) if (pump.pumpDescription.isTempBasalCapable && menuChartSettings[0][OverviewMenus.CharType.BAS.ordinal])
@ -400,12 +379,12 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
var useDSForScale = false var useDSForScale = false
var useBGIForScale = false var useBGIForScale = false
when { when {
menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] -> useABSForScale = true menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] -> useABSForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal] -> useIobForScale = true menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal] -> useIobForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal] -> useCobForScale = true menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal] -> useCobForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] -> useDevForScale = true menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] -> useDevForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal] -> useBGIForScale = true menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal] -> useBGIForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal] -> useRatioForScale = true menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal] -> useRatioForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] -> useDSForScale = true menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] -> useDSForScale = true
} }
val alignDevBgiScale = menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] && menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal] val alignDevBgiScale = menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] && menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]
@ -437,4 +416,9 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
secondaryGraphsData[g].performUpdate() secondaryGraphsData[g].performUpdate()
} }
} }
private fun updateCalcProgress(percent: Int) {
binding.progressBar.progress = percent
binding.progressBar.visibility = (percent != 100).toVisibilityKeepSpace()
}
} }

View file

@ -8,17 +8,17 @@ import android.os.Bundle
import androidx.annotation.XmlRes import androidx.annotation.XmlRes
import androidx.preference.* import androidx.preference.*
import dagger.android.support.AndroidSupportInjection import dagger.android.support.AndroidSupportInjection
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.danaRKorean.DanaRKoreanPlugin import info.nightscout.androidaps.danaRKorean.DanaRKoreanPlugin
import info.nightscout.androidaps.danaRv2.DanaRv2Plugin import info.nightscout.androidaps.danaRv2.DanaRv2Plugin
import info.nightscout.androidaps.danar.DanaRPlugin import info.nightscout.androidaps.danar.DanaRPlugin
import info.nightscout.androidaps.danars.DanaRSPlugin import info.nightscout.androidaps.danars.DanaRSPlugin
import info.nightscout.androidaps.diaconn.DiaconnG8Plugin import info.nightscout.androidaps.diaconn.DiaconnG8Plugin
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventRebuildTabs import info.nightscout.androidaps.events.EventRebuildTabs
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugin.general.openhumans.OpenHumansUploader import info.nightscout.androidaps.plugin.general.openhumans.OpenHumansUploader
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
@ -44,11 +44,11 @@ import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin
import info.nightscout.androidaps.plugins.source.* import info.nightscout.androidaps.plugins.source.*
import info.nightscout.shared.SafeParse
import info.nightscout.androidaps.utils.alertDialogs.OKDialog.show import info.nightscout.androidaps.utils.alertDialogs.OKDialog.show
import info.nightscout.androidaps.utils.protection.PasswordCheck import info.nightscout.androidaps.utils.protection.PasswordCheck
import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.protection.ProtectionCheck.ProtectionType.*
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.shared.SafeParse
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import javax.inject.Inject import javax.inject.Inject
@ -236,7 +236,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
rh.gs(R.string.key_application_protection) == key || rh.gs(R.string.key_application_protection) == key ||
rh.gs(R.string.key_bolus_protection) == key) && rh.gs(R.string.key_bolus_protection) == key) &&
sp.getString(R.string.key_master_password, "") == "" && sp.getString(R.string.key_master_password, "") == "" &&
sp.getInt(key, ProtectionCheck.ProtectionType.NONE.ordinal) == ProtectionCheck.ProtectionType.BIOMETRIC.ordinal sp.getInt(key, NONE.ordinal) == BIOMETRIC.ordinal
) { ) {
activity?.let { activity?.let {
val title = rh.gs(R.string.unsecure_fallback_biometric) val title = rh.gs(R.string.unsecure_fallback_biometric)
@ -246,9 +246,9 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
} }
// Master password erased with activated Biometric protection // Master password erased with activated Biometric protection
val isBiometricActivated = sp.getInt(R.string.key_settings_protection, ProtectionCheck.ProtectionType.NONE.ordinal) == ProtectionCheck.ProtectionType.BIOMETRIC.ordinal || val isBiometricActivated = sp.getInt(R.string.key_settings_protection, NONE.ordinal) == BIOMETRIC.ordinal ||
sp.getInt(R.string.key_application_protection, ProtectionCheck.ProtectionType.NONE.ordinal) == ProtectionCheck.ProtectionType.BIOMETRIC.ordinal || sp.getInt(R.string.key_application_protection, NONE.ordinal) == BIOMETRIC.ordinal ||
sp.getInt(R.string.key_bolus_protection, ProtectionCheck.ProtectionType.NONE.ordinal) == ProtectionCheck.ProtectionType.BIOMETRIC.ordinal sp.getInt(R.string.key_bolus_protection, NONE.ordinal) == BIOMETRIC.ordinal
if (rh.gs(R.string.key_master_password) == key && sp.getString(key, "") == "" && isBiometricActivated) { if (rh.gs(R.string.key_master_password) == key && sp.getString(key, "") == "" && isBiometricActivated) {
activity?.let { activity?.let {
val title = rh.gs(R.string.unsecure_fallback_biometric) val title = rh.gs(R.string.unsecure_fallback_biometric)
@ -319,26 +319,35 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
if (pref is ListPreference) { if (pref is ListPreference) {
pref.setSummary(pref.entry) pref.setSummary(pref.entry)
// Preferences // Preferences
// Preferences
if (pref.getKey() == rh.gs(R.string.key_settings_protection)) { if (pref.getKey() == rh.gs(R.string.key_settings_protection)) {
val pass: Preference? = findPreference(rh.gs(R.string.key_settings_password)) val pass: Preference? = findPreference(rh.gs(R.string.key_settings_password))
if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.CUSTOM_PASSWORD.ordinal.toString() val usePassword = pref.value == CUSTOM_PASSWORD.ordinal.toString()
pass?.let { it.isVisible = usePassword }
val pin: Preference? = findPreference(rh.gs(R.string.key_settings_pin))
val usePin = pref.value == CUSTOM_PIN.ordinal.toString()
pin?.let { it.isVisible = usePin }
} }
// Application // Application
// Application
if (pref.getKey() == rh.gs(R.string.key_application_protection)) { if (pref.getKey() == rh.gs(R.string.key_application_protection)) {
val pass: Preference? = findPreference(rh.gs(R.string.key_application_password)) val pass: Preference? = findPreference(rh.gs(R.string.key_application_password))
if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.CUSTOM_PASSWORD.ordinal.toString() val usePassword = pref.value == CUSTOM_PASSWORD.ordinal.toString()
pass?.let { it.isVisible = usePassword }
val pin: Preference? = findPreference(rh.gs(R.string.key_application_pin))
val usePin = pref.value == CUSTOM_PIN.ordinal.toString()
pin?.let { it.isVisible = usePin }
} }
// Bolus // Bolus
// Bolus
if (pref.getKey() == rh.gs(R.string.key_bolus_protection)) { if (pref.getKey() == rh.gs(R.string.key_bolus_protection)) {
val pass: Preference? = findPreference(rh.gs(R.string.key_bolus_password)) val pass: Preference? = findPreference(rh.gs(R.string.key_bolus_password))
if (pass != null) pass.isEnabled = pref.value == ProtectionCheck.ProtectionType.CUSTOM_PASSWORD.ordinal.toString() val usePassword = pref.value == CUSTOM_PASSWORD.ordinal.toString()
pass?.let { it.isVisible = usePassword }
val pin: Preference? = findPreference(rh.gs(R.string.key_bolus_pin))
val usePin = pref.value == CUSTOM_PIN.ordinal.toString()
pin?.let { it.isVisible = usePin }
} }
} }
if (pref is EditTextPreference) { if (pref is EditTextPreference) {
if (pref.getKey().contains("password") || pref.getKey().contains("secret")) { if (pref.getKey().contains("password") || pref.getKey().contains("pin") || pref.getKey().contains("secret")) {
pref.setSummary("******") pref.setSummary("******")
} else if (pref.text != null) { } else if (pref.text != null) {
pref.dialogMessage = pref.dialogMessage pref.dialogMessage = pref.dialogMessage
@ -354,7 +363,10 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
rh.gs(R.string.key_bolus_password), rh.gs(R.string.key_bolus_password),
rh.gs(R.string.key_master_password), rh.gs(R.string.key_master_password),
rh.gs(R.string.key_application_password), rh.gs(R.string.key_application_password),
rh.gs(R.string.key_settings_password) rh.gs(R.string.key_settings_password),
rh.gs(R.string.key_bolus_pin),
rh.gs(R.string.key_application_pin),
rh.gs(R.string.key_settings_pin)
) )
if (pref is Preference) { if (pref is Preference) {
@ -362,7 +374,11 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
if (sp.getString(pref.key, "").startsWith("hmac:")) { if (sp.getString(pref.key, "").startsWith("hmac:")) {
pref.summary = "******" pref.summary = "******"
} else { } else {
pref.summary = rh.gs(R.string.password_not_set) if (pref.key.contains("pin")) {
pref.summary = rh.gs(R.string.pin_not_set)
}else {
pref.summary = rh.gs(R.string.password_not_set)
}
} }
} }
} }
@ -408,6 +424,18 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
passwordCheck.setPassword(context, R.string.application_password, R.string.key_application_password) passwordCheck.setPassword(context, R.string.application_password, R.string.key_application_password)
return true return true
} }
if (preference.key == rh.gs(R.string.key_settings_pin)) {
passwordCheck.setPassword(context, R.string.settings_pin, R.string.key_settings_pin, pinInput = true)
return true
}
if (preference.key == rh.gs(R.string.key_bolus_pin)) {
passwordCheck.setPassword(context, R.string.bolus_pin, R.string.key_bolus_pin, pinInput = true)
return true
}
if (preference.key == rh.gs(R.string.key_application_pin)) {
passwordCheck.setPassword(context, R.string.application_pin, R.string.key_application_pin, pinInput = true)
return true
}
// NSClient copy settings // NSClient copy settings
if (preference.key == rh.gs(R.string.key_statuslights_copy_ns)) { if (preference.key == rh.gs(R.string.key_statuslights_copy_ns)) {
nsSettingStatus.copyStatusLightsNsSettings(context) nsSettingStatus.copyStatusLightsNsSettings(context)
@ -421,4 +449,4 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
this.filter = filter this.filter = filter
preferenceManager?.preferenceScreen?.let { updateFilterVisibility(filter, it) } preferenceManager?.preferenceScreen?.let { updateFilterVisibility(filter, it) }
} }
} }

View file

@ -4,6 +4,7 @@ import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
import android.view.MenuItem
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
@ -19,6 +20,7 @@ class PreferencesActivity : NoSplashAppCompatActivity(), PreferenceFragmentCompa
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setTheme(R.style.AppTheme)
binding = ActivityPreferencesBinding.inflate(layoutInflater) binding = ActivityPreferencesBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
@ -61,4 +63,14 @@ class PreferencesActivity : NoSplashAppCompatActivity(), PreferenceFragmentCompa
private fun filterPreferences() { private fun filterPreferences() {
myPreferenceFragment?.setFilter(binding.prefFilter.text.toString()) myPreferenceFragment?.setFilter(binding.prefFilter.text.toString())
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
android.R.id.home -> {
onBackPressed()
return true
}
}
return super.onOptionsItemSelected(item)
}
} }

View file

@ -7,6 +7,7 @@ import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
import android.view.Menu import android.view.Menu
import android.widget.PopupMenu import android.widget.PopupMenu
import android.widget.TextView
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.ProfileSealed import info.nightscout.androidaps.data.ProfileSealed
import info.nightscout.androidaps.data.PureProfile import info.nightscout.androidaps.data.PureProfile
@ -37,7 +38,6 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
@Inject lateinit var defaultProfile: DefaultProfile @Inject lateinit var defaultProfile: DefaultProfile
@Inject lateinit var defaultProfileDPV: DefaultProfileDPV @Inject lateinit var defaultProfileDPV: DefaultProfileDPV
@Inject lateinit var localProfilePlugin: LocalProfilePlugin @Inject lateinit var localProfilePlugin: LocalProfilePlugin
@Inject lateinit var rxBus: RxBus
@Inject lateinit var dateUtil: DateUtil @Inject lateinit var dateUtil: DateUtil
@Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
@ -66,9 +66,9 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
private lateinit var binding: ActivityProfilehelperBinding private lateinit var binding: ActivityProfilehelperBinding
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityProfilehelperBinding.inflate(layoutInflater) binding = ActivityProfilehelperBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
@ -79,11 +79,11 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
switchTab(1, typeSelected[1]) switchTab(1, typeSelected[1])
} }
binding.profiletype.setOnClickListener { binding.profileType.setOnClickListener {
PopupMenu(this, binding.profiletype).apply { PopupMenu(this, binding.profileType).apply {
menuInflater.inflate(R.menu.menu_profilehelper, menu) menuInflater.inflate(R.menu.menu_profilehelper, menu)
setOnMenuItemClickListener { item -> setOnMenuItemClickListener { item ->
binding.profiletype.setText(item.title) binding.profileType.setText(item.title)
when (item.itemId) { when (item.itemId) {
R.id.menu_default -> switchTab(tabSelected, ProfileType.MOTOL_DEFAULT) R.id.menu_default -> switchTab(tabSelected, ProfileType.MOTOL_DEFAULT)
R.id.menu_default_dpv -> switchTab(tabSelected, ProfileType.DPV_DEFAULT) R.id.menu_default_dpv -> switchTab(tabSelected, ProfileType.DPV_DEFAULT)
@ -130,7 +130,7 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
} }
// Default profile // Default profile
binding.copytolocalprofile.setOnClickListener { binding.copyToLocalProfile.setOnClickListener {
storeValues() storeValues()
val age = ageUsed[tabSelected] val age = ageUsed[tabSelected]
val weight = weightUsed[tabSelected] val weight = weightUsed[tabSelected]
@ -168,20 +168,22 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
} }
}) })
binding.basalpctfromtdd.setParams(32.0, 32.0, 37.0, 1.0, DecimalFormat("0"), false, null) binding.basalPctFromTdd.setParams(32.0, 32.0, 37.0, 1.0, DecimalFormat("0"), false, null)
@SuppressLint("SetTextI18n") binding.tdds.addView(TextView(this).apply { text = rh.gs(R.string.tdd) + ": " + rh.gs(R.string.calculation_in_progress) })
binding.tdds.text = getString(R.string.tdd) + ": " + rh.gs(R.string.calculation_in_progress)
Thread { Thread {
val tdds = tddCalculator.stats() val tdds = tddCalculator.stats(this)
runOnUiThread { binding.tdds.text = tdds } runOnUiThread {
binding.tdds.removeAllViews()
binding.tdds.addView(tdds)
}
}.start() }.start()
// Current profile // Current profile
binding.currentProfileText.text = profileFunction.getProfileName() binding.currentProfileText.text = profileFunction.getProfileName()
// General // General
binding.compareprofile.setOnClickListener { binding.compareProfiles.setOnClickListener {
storeValues() storeValues()
for (i in 0..1) { for (i in 0..1) {
if (typeSelected[i] == ProfileType.MOTOL_DEFAULT) { if (typeSelected[i] == ProfileType.MOTOL_DEFAULT) {
@ -239,10 +241,10 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
} }
ToastUtils.showToastInUiThread(this, R.string.invalidinput) ToastUtils.showToastInUiThread(this, R.string.invalidinput)
} }
binding.age.editText?.id?.let { binding.ageLabel.labelFor = it } binding.ageLabel.labelFor = binding.age.editTextId
binding.tdd.editText?.id?.let { binding.tddLabel.labelFor = it } binding.tddLabel.labelFor = binding.tdd.editTextId
binding.weight.editText?.id?.let { binding.weightLabel.labelFor = it } binding.weightLabel.labelFor = binding.weight.editTextId
binding.basalpctfromtdd.editText?.id?.let { binding.basalpctfromtddLabel.labelFor = it } binding.basalPctFromTddLabel.labelFor = binding.basalPctFromTdd.editTextId
switchTab(0, typeSelected[0], false) switchTab(0, typeSelected[0], false)
} }
@ -273,7 +275,7 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
ageUsed[tabSelected] = binding.age.value ageUsed[tabSelected] = binding.age.value
weightUsed[tabSelected] = binding.weight.value weightUsed[tabSelected] = binding.weight.value
tddUsed[tabSelected] = binding.tdd.value tddUsed[tabSelected] = binding.tdd.value
pctUsed[tabSelected] = binding.basalpctfromtdd.value pctUsed[tabSelected] = binding.basalPctFromTdd.value
} }
private fun switchTab(tab: Int, newContent: ProfileType, storeOld: Boolean = true) { private fun switchTab(tab: Int, newContent: ProfileType, storeOld: Boolean = true) {
@ -283,10 +285,10 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
tabSelected = tab tabSelected = tab
typeSelected[tabSelected] = newContent typeSelected[tabSelected] = newContent
binding.profiletypeTitle.defaultHintTextColor = ColorStateList.valueOf(rh.gc(if (tab == 0) R.color.helperProfile else R.color.examinedProfile)) binding.profileTypeTitle.defaultHintTextColor = ColorStateList.valueOf(rh.gac( this, if (tab == 0) R.attr.helperProfileColor else R.attr.examinedProfileColor))
// show new content // show new content
binding.profiletype.setText( binding.profileType.setText(
when (typeSelected[tabSelected]) { when (typeSelected[tabSelected]) {
ProfileType.MOTOL_DEFAULT -> rh.gs(R.string.motoldefaultprofile) ProfileType.MOTOL_DEFAULT -> rh.gs(R.string.motoldefaultprofile)
ProfileType.DPV_DEFAULT -> rh.gs(R.string.dpvdefaultprofile) ProfileType.DPV_DEFAULT -> rh.gs(R.string.dpvdefaultprofile)
@ -304,9 +306,9 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
binding.age.value = ageUsed[tabSelected] binding.age.value = ageUsed[tabSelected]
binding.weight.value = weightUsed[tabSelected] binding.weight.value = weightUsed[tabSelected]
binding.tdd.value = tddUsed[tabSelected] binding.tdd.value = tddUsed[tabSelected]
binding.basalpctfromtdd.value = pctUsed[tabSelected] binding.basalPctFromTdd.value = pctUsed[tabSelected]
binding.basalpctfromtddRow.visibility = (newContent == ProfileType.DPV_DEFAULT).toVisibility() binding.basalPctFromTddRow.visibility = (newContent == ProfileType.DPV_DEFAULT).toVisibility()
if (profileList.isNotEmpty()) if (profileList.isNotEmpty())
binding.availableProfileList.setText(profileList[profileUsed[tabSelected]].toString()) binding.availableProfileList.setText(profileList[profileUsed[tabSelected]].toString())
if (profileSwitch.isNotEmpty()) if (profileSwitch.isNotEmpty())
@ -314,7 +316,7 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
} }
private fun setBackgroundColorOnSelected(tab: Int) { private fun setBackgroundColorOnSelected(tab: Int) {
binding.menu1.setBackgroundColor(rh.gc(if (tab == 1) R.color.defaultbackground else R.color.helperProfile)) binding.menu1.setBackgroundColor(rh.gac(this, if (tab == 1) R.attr.defaultbackground else R.attr.helperProfileColor))
binding.menu2.setBackgroundColor(rh.gc(if (tab == 0) R.color.defaultbackground else R.color.examinedProfile)) binding.menu2.setBackgroundColor(rh.gac(this, if (tab == 0) R.attr.defaultbackground else R.attr.examinedProfileColor))
} }
} }

View file

@ -2,6 +2,7 @@ package info.nightscout.androidaps.activities
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import android.widget.TextView
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.UserEntry.Sources
@ -9,7 +10,6 @@ import info.nightscout.androidaps.databinding.ActivityStatsBinding
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.utils.ActivityMonitor import info.nightscout.androidaps.utils.ActivityMonitor
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.stats.TddCalculator import info.nightscout.androidaps.utils.stats.TddCalculator
import info.nightscout.androidaps.utils.stats.TirCalculator import info.nightscout.androidaps.utils.stats.TirCalculator
import javax.inject.Inject import javax.inject.Inject
@ -29,21 +29,30 @@ class StatsActivity : NoSplashAppCompatActivity() {
binding = ActivityStatsBinding.inflate(layoutInflater) binding = ActivityStatsBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
binding.tdds.text = getString(R.string.tdd) + ": " + rh.gs(R.string.calculation_in_progress) binding.tdds.addView(TextView(this).apply { text = getString(R.string.tdd) + ": " + rh.gs(R.string.calculation_in_progress) })
binding.tir.text = getString(R.string.tir) + ": " + rh.gs(R.string.calculation_in_progress) binding.tir.addView(TextView(this).apply { text = getString(R.string.tir) + ": " + rh.gs(R.string.calculation_in_progress) })
binding.activity.text = rh.gs(R.string.activitymonitor) + ": " + rh.gs(R.string.calculation_in_progress) binding.activity.addView(TextView(this).apply { text = getString(R.string.activitymonitor) + ": " + rh.gs(R.string.calculation_in_progress) })
Thread { Thread {
val tdds = tddCalculator.stats() val tdds = tddCalculator.stats(this)
runOnUiThread { binding.tdds.text = tdds } runOnUiThread {
binding.tdds.removeAllViews()
binding.tdds.addView(tdds)
}
}.start() }.start()
Thread { Thread {
val tir = tirCalculator.stats() val tir = tirCalculator.stats(this)
runOnUiThread { binding.tir.text = tir } runOnUiThread {
binding.tir.removeAllViews()
binding.tir.addView(tir)
}
}.start() }.start()
Thread { Thread {
val activity = activityMonitor.stats() val activity = activityMonitor.stats(this)
runOnUiThread { binding.activity.text = activity } runOnUiThread {
binding.activity.removeAllViews()
binding.activity.addView(activity)
}
}.start() }.start()
binding.ok.setOnClickListener { finish() } binding.ok.setOnClickListener { finish() }

View file

@ -43,9 +43,9 @@ class SurveyActivity : NoSplashAppCompatActivity() {
val profileList = profileStore?.getProfileList() ?: return val profileList = profileStore?.getProfileList() ?: return
binding.spinner.adapter = ArrayAdapter(this, R.layout.spinner_centered, profileList) binding.spinner.adapter = ArrayAdapter(this, R.layout.spinner_centered, profileList)
binding.tdds.text = tddCalculator.stats() //binding.tdds.text = tddCalculator.stats()
binding.tir.text = tirCalculator.stats() //binding.tir.text = tirCalculator.stats()
binding.activity.text = activityMonitor.stats() //binding.activity.text = activityMonitor.stats()
binding.profile.setOnClickListener { binding.profile.setOnClickListener {
val age = SafeParse.stringToDouble(binding.age.text.toString()) val age = SafeParse.stringToDouble(binding.age.text.toString())
@ -104,7 +104,7 @@ class SurveyActivity : NoSplashAppCompatActivity() {
.addOnCompleteListener(this) { task -> .addOnCompleteListener(this) { task ->
if (task.isSuccessful) { if (task.isSuccessful) {
aapsLogger.debug(LTag.CORE, "signInAnonymously:success") aapsLogger.debug(LTag.CORE, "signInAnonymously:success")
//val user = auth.currentUser // TODO: do we need this, seems unused? //val user = auth.currentUser // do we need this, seems unused?
val database = FirebaseDatabase.getInstance().reference val database = FirebaseDatabase.getInstance().reference
database.child("survey").child(r.id).setValue(r) database.child("survey").child(r.id).setValue(r)

View file

@ -2,12 +2,14 @@ package info.nightscout.androidaps.activities
import android.os.Bundle import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import android.view.View
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction import androidx.fragment.app.FragmentTransaction
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.fragments.* import info.nightscout.androidaps.activities.fragments.*
import info.nightscout.androidaps.databinding.TreatmentsFragmentBinding import info.nightscout.androidaps.databinding.TreatmentsFragmentBinding
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import javax.inject.Inject import javax.inject.Inject
@ -23,49 +25,34 @@ class TreatmentsActivity : NoSplashAppCompatActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = TreatmentsFragmentBinding.inflate(layoutInflater) binding = TreatmentsFragmentBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
//binding.tempBasals.visibility = buildHelper.isEngineeringMode().toVisibility()
//binding.extendedBoluses.visibility = (buildHelper.isEngineeringMode() && !activePlugin.activePump.isFakingTempsByExtendedBoluses).toVisibility() // Use index, TabItems crashes with an id
val useFakeTempBasal = activePlugin.activePump.isFakingTempsByExtendedBoluses
binding.treatmentsTabs.getTabAt(1)?.view?.visibility = useFakeTempBasal.toVisibility()
binding.treatments.setOnClickListener {
setFragment(TreatmentsBolusCarbsFragment())
setBackgroundColorOnSelected(it)
supportActionBar?.title = rh.gs(R.string.carbs_and_bolus)
}
binding.extendedBoluses.setOnClickListener {
setFragment(TreatmentsExtendedBolusesFragment())
setBackgroundColorOnSelected(it)
supportActionBar?.title = rh.gs(R.string.extended_bolus)
}
binding.tempBasals.setOnClickListener {
setFragment(TreatmentsTemporaryBasalsFragment())
setBackgroundColorOnSelected(it)
supportActionBar?.title = rh.gs(R.string.tempbasal_label)
}
binding.tempTargets.setOnClickListener {
setFragment(TreatmentsTempTargetFragment())
setBackgroundColorOnSelected(it)
supportActionBar?.title = rh.gs(R.string.tempt_targets)
}
binding.profileSwitches.setOnClickListener {
setFragment(TreatmentsProfileSwitchFragment())
setBackgroundColorOnSelected(it)
supportActionBar?.title = rh.gs(R.string.profile_changes)
}
binding.careportal.setOnClickListener {
setFragment(TreatmentsCareportalFragment())
setBackgroundColorOnSelected(it)
supportActionBar?.title = rh.gs(R.string.careportal)
}
binding.userentry.setOnClickListener {
setFragment(TreatmentsUserEntryFragment())
setBackgroundColorOnSelected(it)
supportActionBar?.title = rh.gs(R.string.user_action)
}
setFragment(TreatmentsBolusCarbsFragment()) setFragment(TreatmentsBolusCarbsFragment())
setBackgroundColorOnSelected(binding.treatments)
setSupportActionBar(binding.toolbar) setSupportActionBar(binding.toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.title = rh.gs(R.string.carbs_and_bolus) supportActionBar?.title = rh.gs(R.string.carbs_and_bolus)
binding.treatmentsTabs.addOnTabSelectedListener(object : OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab) {
val fragment = when (tab.position) {
0 -> TreatmentsBolusCarbsFragment::class.java
1 -> TreatmentsExtendedBolusesFragment::class.java
2 -> TreatmentsTemporaryBasalsFragment::class.java
3 -> TreatmentsTempTargetFragment::class.java
4 -> TreatmentsProfileSwitchFragment::class.java
5 -> TreatmentsCareportalFragment::class.java
else -> TreatmentsUserEntryFragment::class.java
}
setFragment(fragment.newInstance())
supportActionBar?.title = tab.contentDescription
}
override fun onTabUnselected(tab: TabLayout.Tab) {}
override fun onTabReselected(tab: TabLayout.Tab) {}
})
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
@ -86,15 +73,4 @@ class TreatmentsActivity : NoSplashAppCompatActivity() {
.commit() .commit()
} }
private fun setBackgroundColorOnSelected(selected: View) {
binding.treatments.setBackgroundColor(rh.gc(R.color.defaultbackground))
binding.extendedBoluses.setBackgroundColor(rh.gc(R.color.defaultbackground))
binding.tempBasals.setBackgroundColor(rh.gc(R.color.defaultbackground))
binding.tempTargets.setBackgroundColor(rh.gc(R.color.defaultbackground))
binding.profileSwitches.setBackgroundColor(rh.gc(R.color.defaultbackground))
binding.careportal.setBackgroundColor(rh.gc(R.color.defaultbackground))
binding.userentry.setBackgroundColor(rh.gc(R.color.defaultbackground))
selected.setBackgroundColor(rh.gc(R.color.tabBgColorSelected))
}
} }

View file

@ -3,12 +3,8 @@ package info.nightscout.androidaps.activities.fragments
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.graphics.Paint import android.graphics.Paint
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.util.SparseArray import android.util.SparseArray
import android.view.* import android.view.*
import android.widget.CompoundButton
import android.view.ActionMode
import androidx.appcompat.widget.Toolbar
import androidx.core.util.forEach import androidx.core.util.forEach
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -18,6 +14,7 @@ import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.Bolus import info.nightscout.androidaps.database.entities.Bolus
import info.nightscout.androidaps.database.entities.BolusCalculatorResult import info.nightscout.androidaps.database.entities.BolusCalculatorResult
import info.nightscout.androidaps.database.entities.Carbs import info.nightscout.androidaps.database.entities.Carbs
import info.nightscout.androidaps.database.entities.UserEntry
import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.ValueWithUnit
@ -28,9 +25,7 @@ import info.nightscout.androidaps.database.transactions.InvalidateCarbsTransacti
import info.nightscout.androidaps.databinding.TreatmentsBolusCarbsFragmentBinding import info.nightscout.androidaps.databinding.TreatmentsBolusCarbsFragmentBinding
import info.nightscout.androidaps.databinding.TreatmentsBolusCarbsItemBinding import info.nightscout.androidaps.databinding.TreatmentsBolusCarbsItemBinding
import info.nightscout.androidaps.dialogs.WizardInfoDialog import info.nightscout.androidaps.dialogs.WizardInfoDialog
import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.events.EventTreatmentChange import info.nightscout.androidaps.events.EventTreatmentChange
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.extensions.iobCalc import info.nightscout.androidaps.extensions.iobCalc
import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ActivePlugin
@ -39,9 +34,11 @@ import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData
import info.nightscout.androidaps.utils.ActionModeHelper
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
@ -72,8 +69,10 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
@Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var activePlugin: ActivePlugin
private var _binding: TreatmentsBolusCarbsFragmentBinding? = null private var _binding: TreatmentsBolusCarbsFragmentBinding? = null
// This property is only valid between onCreateView and onDestroyView. // This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!! private val binding get() = _binding!!
private var menu: Menu? = null
class MealLink( class MealLink(
val bolus: Bolus? = null, val bolus: Bolus? = null,
@ -82,23 +81,24 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
) )
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
private lateinit var actionHelper: ActionModeHelper<MealLink>
private val millsToThePast = T.days(30).msecs() private val millsToThePast = T.days(30).msecs()
private var selectedItems: SparseArray<MealLink> = SparseArray()
private var showInvalidated = false private var showInvalidated = false
private var removeActionMode: ActionMode? = null
private var toolbar: Toolbar? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
TreatmentsBolusCarbsFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root TreatmentsBolusCarbsFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root
@SuppressLint("CheckResult") @SuppressLint("NotifyDataSetChanged")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
actionHelper = ActionModeHelper(rh, activity)
actionHelper.setUpdateListHandler { binding.recyclerview.adapter?.notifyDataSetChanged() }
actionHelper.setOnRemoveHandler { removeSelected(it) }
setHasOptionsMenu(true) setHasOptionsMenu(true)
toolbar = activity?.findViewById(R.id.toolbar)
binding.recyclerview.setHasFixedSize(true) binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context) binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
binding.recyclerview.emptyView = binding.noRecordsText
binding.recyclerview.loadingView = binding.progressBar
} }
private fun bolusMealLinksWithInvalid(now: Long) = repository private fun bolusMealLinksWithInvalid(now: Long) = repository
@ -127,8 +127,8 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
fun swapAdapter() { fun swapAdapter() {
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
binding.recyclerview.isLoading = true
disposable += disposable +=
if (showInvalidated) if (showInvalidated)
carbsMealLinksWithInvalid(now) carbsMealLinksWithInvalid(now)
.zipWith(bolusMealLinksWithInvalid(now)) { first, second -> first + second } .zipWith(bolusMealLinksWithInvalid(now)) { first, second -> first + second }
@ -169,28 +169,18 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.debounce(1L, TimeUnit.SECONDS) .debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException) .subscribe({ swapAdapter() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTreatmentUpdateGui::class.java) // TODO join with above
.observeOn(aapsSchedulers.io)
.debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(aapsSchedulers.main)
.debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
} }
@Synchronized @Synchronized
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
actionHelper.finish()
disposable.clear() disposable.clear()
} }
@Synchronized @Synchronized
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
removeActionMode?.let { it.finish() }
binding.recyclerview.adapter = null // avoid leaks binding.recyclerview.adapter = null // avoid leaks
_binding = null _binding = null
} }
@ -206,9 +196,9 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
val profile = profileFunction.getProfile() ?: return val profile = profileFunction.getProfile() ?: return
val ml = mealLinks[position] val ml = mealLinks[position]
val sameDayPrevious = position > 0 && dateUtil.isSameDay(timestamp(ml), timestamp(mealLinks[position - 1])) val newDay = position == 0 || !dateUtil.isSameDayGroup(timestamp(ml), timestamp(mealLinks[position - 1]))
holder.binding.date.visibility = sameDayPrevious.not().toVisibility() holder.binding.date.visibility = newDay.toVisibility()
holder.binding.date.text = dateUtil.dateString(timestamp(ml)) holder.binding.date.text = if (newDay) dateUtil.dateStringRelative(timestamp(ml), rh) else ""
// Metadata // Metadata
holder.binding.metadataLayout.visibility = (ml.bolusCalculatorResult != null && (ml.bolusCalculatorResult.isValid || showInvalidated)).toVisibility() holder.binding.metadataLayout.visibility = (ml.bolusCalculatorResult != null && (ml.bolusCalculatorResult.isValid || showInvalidated)).toVisibility()
@ -226,7 +216,7 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
holder.binding.bolusInvalid.visibility = bolus.isValid.not().toVisibility() holder.binding.bolusInvalid.visibility = bolus.isValid.not().toVisibility()
val iob = bolus.iobCalc(activePlugin, System.currentTimeMillis(), profile.dia) val iob = bolus.iobCalc(activePlugin, System.currentTimeMillis(), profile.dia)
if (iob.iobContrib > 0.01) { if (iob.iobContrib > 0.01) {
holder.binding.iob.setTextColor(rh.gc(R.color.colorActive)) holder.binding.iob.setTextColor(rh.gac(context , R.attr.activeColor))
holder.binding.iob.text = rh.gs(R.string.formatinsulinunits, iob.iobContrib) holder.binding.iob.text = rh.gs(R.string.formatinsulinunits, iob.iobContrib)
holder.binding.iobLabel.visibility = View.VISIBLE holder.binding.iobLabel.visibility = View.VISIBLE
holder.binding.iob.visibility = View.VISIBLE holder.binding.iob.visibility = View.VISIBLE
@ -236,13 +226,25 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
holder.binding.iobLabel.visibility = View.GONE holder.binding.iobLabel.visibility = View.GONE
holder.binding.iob.visibility = View.GONE holder.binding.iob.visibility = View.GONE
} }
if (bolus.timestamp > dateUtil.now()) holder.binding.date.setTextColor(rh.gc(R.color.colorScheduled)) else holder.binding.date.setTextColor(holder.binding.carbs.currentTextColor) if (bolus.timestamp > dateUtil.now()) holder.binding.date.setTextColor(rh.gac(context, R.attr.scheduledColor)) else holder.binding.date.setTextColor(holder.binding.carbs
.currentTextColor)
holder.binding.mealOrCorrection.text = holder.binding.mealOrCorrection.text =
when (ml.bolus.type) { when (ml.bolus.type) {
Bolus.Type.SMB -> "SMB" Bolus.Type.SMB -> "SMB"
Bolus.Type.NORMAL -> rh.gs(R.string.mealbolus) Bolus.Type.NORMAL -> rh.gs(R.string.mealbolus)
Bolus.Type.PRIMING -> rh.gs(R.string.prime) Bolus.Type.PRIMING -> rh.gs(R.string.prime)
} }
holder.binding.cbBolusRemove.visibility = (ml.bolus.isValid && actionHelper.isRemoving).toVisibility()
if (actionHelper.isRemoving) {
holder.binding.cbBolusRemove.setOnCheckedChangeListener { _, value ->
actionHelper.updateSelection(position, ml, value)
}
holder.binding.root.setOnClickListener {
holder.binding.cbBolusRemove.toggle()
actionHelper.updateSelection(position, ml, holder.binding.cbBolusRemove.isChecked)
}
holder.binding.cbBolusRemove.isChecked = actionHelper.isSelected(position)
}
} }
// Carbs // Carbs
holder.binding.carbsLayout.visibility = (ml.carbs != null && (ml.carbs.isValid || showInvalidated)).toVisibility() holder.binding.carbsLayout.visibility = (ml.carbs != null && (ml.carbs.isValid || showInvalidated)).toVisibility()
@ -253,26 +255,22 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
holder.binding.carbsNs.visibility = (carbs.interfaceIDs.nightscoutId != null).toVisibility() holder.binding.carbsNs.visibility = (carbs.interfaceIDs.nightscoutId != null).toVisibility()
holder.binding.carbsPump.visibility = (carbs.interfaceIDs.pumpId != null).toVisibility() holder.binding.carbsPump.visibility = (carbs.interfaceIDs.pumpId != null).toVisibility()
holder.binding.carbsInvalid.visibility = carbs.isValid.not().toVisibility() holder.binding.carbsInvalid.visibility = carbs.isValid.not().toVisibility()
} holder.binding.cbCarbsRemove.visibility = (ml.carbs.isValid && actionHelper.isRemoving).toVisibility()
holder.binding.cbBolusRemove.visibility = (ml.bolus?.isValid == true && removeActionMode != null).toVisibility() if (actionHelper.isRemoving) {
holder.binding.cbCarbsRemove.visibility = (ml.carbs?.isValid == true && removeActionMode != null).toVisibility() holder.binding.cbCarbsRemove.setOnCheckedChangeListener { _, value ->
if (removeActionMode != null) { actionHelper.updateSelection(position, ml, value)
val onChange = CompoundButton.OnCheckedChangeListener { _, value ->
if (value) {
selectedItems.put(position, ml)
} else {
selectedItems.remove(position)
} }
removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size()) holder.binding.root.setOnClickListener {
holder.binding.cbBolusRemove.toggle()
actionHelper.updateSelection(position, ml, holder.binding.cbBolusRemove.isChecked)
}
holder.binding.cbCarbsRemove.isChecked = actionHelper.isSelected(position)
} }
holder.binding.cbBolusRemove.setOnCheckedChangeListener(onChange)
holder.binding.cbBolusRemove.isChecked = selectedItems.get(position) != null
holder.binding.cbCarbsRemove.setOnCheckedChangeListener(onChange)
holder.binding.cbCarbsRemove.isChecked = selectedItems.get(position) != null
} }
holder.binding.calculation.tag = ml holder.binding.calculation.tag = ml
val nextTimestamp = if (mealLinks.size != position + 1) timestamp(mealLinks[position + 1]) else 0L val nextTimestamp = if (mealLinks.size != position + 1) timestamp(mealLinks[position + 1]) else 0L
holder.binding.delimiter.visibility = dateUtil.isSameDay(timestamp(ml), nextTimestamp).toVisibility() holder.binding.delimiter.visibility = dateUtil.isSameDayGroup(timestamp(ml), nextTimestamp).toVisibility()
} }
override fun getItemCount() = mealLinks.size override fun getItemCount() = mealLinks.size
@ -297,13 +295,18 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
} }
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
this.menu = menu
inflater.inflate(R.menu.menu_treatments_carbs_bolus, menu) inflater.inflate(R.menu.menu_treatments_carbs_bolus, menu)
super.onCreateOptionsMenu(menu, inflater) super.onCreateOptionsMenu(menu, inflater)
} }
private fun updateMenuVisibility() {
menu?.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated
menu?.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated
}
override fun onPrepareOptionsMenu(menu: Menu) { override fun onPrepareOptionsMenu(menu: Menu) {
menu.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated updateMenuVisibility()
menu.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated
val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_insulin, false) || !sp.getBoolean(R.string.key_ns_receive_carbs, false) || !buildHelper.isEngineeringMode() val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_insulin, false) || !sp.getBoolean(R.string.key_ns_receive_carbs, false) || !buildHelper.isEngineeringMode()
menu.findItem(R.id.nav_refresh_ns)?.isVisible = !nsUploadOnly menu.findItem(R.id.nav_refresh_ns)?.isVisible = !nsUploadOnly
val hasItems = (binding.recyclerview.adapter?.itemCount ?: 0) > 0 val hasItems = (binding.recyclerview.adapter?.itemCount ?: 0) > 0
@ -314,20 +317,21 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
override fun onOptionsItemSelected(item: MenuItem): Boolean = override fun onOptionsItemSelected(item: MenuItem): Boolean =
when (item.itemId) { when (item.itemId) {
R.id.nav_remove_items -> { R.id.nav_remove_items -> actionHelper.startRemove()
removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback())
true
}
R.id.nav_show_invalidated -> { R.id.nav_show_invalidated -> {
showInvalidated = true showInvalidated = true
rxBus.send(EventTreatmentUpdateGui()) updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records))
swapAdapter()
true true
} }
R.id.nav_hide_invalidated -> { R.id.nav_hide_invalidated -> {
showInvalidated = false showInvalidated = false
rxBus.send(EventTreatmentUpdateGui()) updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.hide_invalidated_records))
swapAdapter()
true true
} }
@ -423,36 +427,7 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
} }
} }
inner class RemoveActionModeCallback : ActionMode.Callback { private fun getConfirmationText(selectedItems: SparseArray<MealLink>): String {
override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean {
mode.menuInflater.inflate(R.menu.menu_delete_selection, menu)
selectedItems.clear()
mode.title = rh.gs(R.string.count_selected, selectedItems.size())
binding.recyclerview.adapter?.notifyDataSetChanged()
return true
}
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?) = false
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
return when (item.itemId) {
R.id.remove_selected -> {
removeSelected()
true
}
else -> false
}
}
override fun onDestroyActionMode(mode: ActionMode?) {
removeActionMode = null
binding.recyclerview.adapter?.notifyDataSetChanged()
}
}
private fun getConfirmationText(): String {
if (selectedItems.size() == 1) { if (selectedItems.size() == 1) {
val mealLink = selectedItems.valueAt(0) val mealLink = selectedItems.valueAt(0)
val bolus = mealLink.bolus val bolus = mealLink.bolus
@ -467,40 +442,38 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size()) return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size())
} }
fun removeSelected() { private fun removeSelected(selectedItems: SparseArray<MealLink>) {
if (selectedItems.size() > 0) activity?.let { activity ->
activity?.let { activity -> OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(selectedItems), Runnable {
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable { selectedItems.forEach { _, ml ->
selectedItems.forEach { _, ml -> ml.bolus?.let { bolus ->
ml.bolus?.let { bolus -> uel.log(
uel.log( Action.BOLUS_REMOVED, Sources.Treatments,
Action.BOLUS_REMOVED, Sources.Treatments, ValueWithUnit.Timestamp(bolus.timestamp),
ValueWithUnit.Timestamp(bolus.timestamp), ValueWithUnit.Insulin(bolus.amount)
ValueWithUnit.Insulin(bolus.amount) )
disposable += repository.runTransactionForResult(InvalidateBolusTransaction(bolus.id))
.subscribe(
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated bolus $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating bolus", it) }
) )
disposable += repository.runTransactionForResult(InvalidateBolusTransaction(bolus.id))
.subscribe(
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated bolus $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating bolus", it) }
)
}
ml.carbs?.let { carb ->
uel.log(
Action.CARBS_REMOVED, Sources.Treatments,
ValueWithUnit.Timestamp(carb.timestamp),
ValueWithUnit.Gram(carb.amount.toInt())
)
disposable += repository.runTransactionForResult(InvalidateCarbsTransaction(carb.id))
.subscribe(
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated carbs $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating carbs", it) }
)
}
} }
removeActionMode?.finish() ml.carbs?.let { carb ->
}) uel.log(
} Action.CARBS_REMOVED, Sources.Treatments,
else ValueWithUnit.Timestamp(carb.timestamp),
removeActionMode?.finish() ValueWithUnit.Gram(carb.amount.toInt())
)
disposable += repository.runTransactionForResult(InvalidateCarbsTransaction(carb.id))
.subscribe(
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated carbs $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating carbs", it) }
)
}
}
actionHelper.finish()
})
}
} }
} }

View file

@ -3,39 +3,33 @@ package info.nightscout.androidaps.activities.fragments
import android.os.Bundle import android.os.Bundle
import android.util.SparseArray import android.util.SparseArray
import android.view.* import android.view.*
import android.view.ActionMode
import androidx.appcompat.widget.Toolbar
import androidx.core.util.forEach import androidx.core.util.forEach
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import dagger.android.support.DaggerFragment import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.fragments.TreatmentsCareportalFragment.RecyclerViewAdapter.TherapyEventsViewHolder
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.entities.TherapyEvent import info.nightscout.androidaps.database.entities.TherapyEvent
import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.transactions.InvalidateAAPSStartedTherapyEventTransaction import info.nightscout.androidaps.database.transactions.InvalidateAAPSStartedTherapyEventTransaction
import info.nightscout.androidaps.database.transactions.InvalidateTherapyEventTransaction import info.nightscout.androidaps.database.transactions.InvalidateTherapyEventTransaction
import info.nightscout.androidaps.databinding.TreatmentsCareportalFragmentBinding import info.nightscout.androidaps.databinding.TreatmentsCareportalFragmentBinding
import info.nightscout.androidaps.databinding.TreatmentsCareportalItemBinding import info.nightscout.androidaps.databinding.TreatmentsCareportalItemBinding
import info.nightscout.androidaps.events.EventTherapyEventChange import info.nightscout.androidaps.events.EventTherapyEventChange
import info.nightscout.shared.logging.AAPSLogger import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
import info.nightscout.androidaps.events.EventTreatmentUpdateGui import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.activities.fragments.TreatmentsCareportalFragment.RecyclerViewAdapter.TherapyEventsViewHolder
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.Translator
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.core.Completable import io.reactivex.rxjava3.core.Completable
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
@ -59,26 +53,28 @@ class TreatmentsCareportalFragment : DaggerFragment() {
@Inject lateinit var uel: UserEntryLogger @Inject lateinit var uel: UserEntryLogger
private var _binding: TreatmentsCareportalFragmentBinding? = null private var _binding: TreatmentsCareportalFragmentBinding? = null
// This property is only valid between onCreateView and onDestroyView. // This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!! private val binding get() = _binding!!
private var menu: Menu? = null
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
private val millsToThePast = T.days(30).msecs() private val millsToThePast = T.days(30).msecs()
private var selectedItems: SparseArray<TherapyEvent> = SparseArray() private lateinit var actionHelper: ActionModeHelper<TherapyEvent>
private var showInvalidated = false private var showInvalidated = false
private var toolbar: Toolbar? = null
private var removeActionMode: ActionMode? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
TreatmentsCareportalFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root TreatmentsCareportalFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
toolbar = activity?.findViewById(R.id.toolbar) actionHelper = ActionModeHelper(rh, activity)
actionHelper.setUpdateListHandler { binding.recyclerview.adapter?.notifyDataSetChanged() }
actionHelper.setOnRemoveHandler { removeSelected(it) }
setHasOptionsMenu(true) setHasOptionsMenu(true)
binding.recyclerview.setHasFixedSize(true) binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context) binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
binding.recyclerview.emptyView = binding.noRecordsText
binding.recyclerview.loadingView = binding.progressBar
} }
private fun refreshFromNightscout() { private fun refreshFromNightscout() {
@ -111,6 +107,7 @@ class TreatmentsCareportalFragment : DaggerFragment() {
fun swapAdapter() { fun swapAdapter() {
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
binding.recyclerview.isLoading = true
disposable += disposable +=
if (showInvalidated) if (showInvalidated)
repository repository
@ -133,23 +130,18 @@ class TreatmentsCareportalFragment : DaggerFragment() {
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.debounce(1L, TimeUnit.SECONDS) .debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException) .subscribe({ swapAdapter() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTreatmentUpdateGui::class.java) // TODO join with above
.observeOn(aapsSchedulers.io)
.debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
} }
@Synchronized @Synchronized
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
actionHelper.finish()
disposable.clear() disposable.clear()
} }
@Synchronized @Synchronized
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
removeActionMode?.let { it.finish() }
binding.recyclerview.adapter = null // avoid leaks binding.recyclerview.adapter = null // avoid leaks
_binding = null _binding = null
} }
@ -165,27 +157,24 @@ class TreatmentsCareportalFragment : DaggerFragment() {
val therapyEvent = therapyList[position] val therapyEvent = therapyList[position]
holder.binding.ns.visibility = (therapyEvent.interfaceIDs.nightscoutId != null).toVisibility() holder.binding.ns.visibility = (therapyEvent.interfaceIDs.nightscoutId != null).toVisibility()
holder.binding.invalid.visibility = therapyEvent.isValid.not().toVisibility() holder.binding.invalid.visibility = therapyEvent.isValid.not().toVisibility()
val sameDayPrevious = position > 0 && dateUtil.isSameDay(therapyEvent.timestamp, therapyList[position - 1].timestamp) val newDay = position == 0 || !dateUtil.isSameDayGroup(therapyEvent.timestamp, therapyList[position - 1].timestamp)
holder.binding.date.visibility = sameDayPrevious.not().toVisibility() holder.binding.date.visibility = newDay.toVisibility()
holder.binding.date.text = dateUtil.dateString(therapyEvent.timestamp) holder.binding.date.text = if (newDay) dateUtil.dateStringRelative(therapyEvent.timestamp, rh) else ""
holder.binding.time.text = dateUtil.timeString(therapyEvent.timestamp) holder.binding.time.text = dateUtil.timeString(therapyEvent.timestamp)
holder.binding.duration.text = if (therapyEvent.duration == 0L) "" else dateUtil.niceTimeScalar(therapyEvent.duration, rh) holder.binding.duration.text = if (therapyEvent.duration == 0L) "" else dateUtil.niceTimeScalar(therapyEvent.duration, rh)
holder.binding.note.text = therapyEvent.note holder.binding.note.text = therapyEvent.note
holder.binding.type.text = translator.translate(therapyEvent.type) holder.binding.type.text = translator.translate(therapyEvent.type)
holder.binding.cbRemove.visibility = (therapyEvent.isValid && removeActionMode != null).toVisibility() holder.binding.cbRemove.visibility = (therapyEvent.isValid && actionHelper.isRemoving).toVisibility()
if (removeActionMode != null) { holder.binding.cbRemove.setOnCheckedChangeListener { _, value ->
holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> actionHelper.updateSelection(position, therapyEvent, value)
if (value) {
selectedItems.put(position, therapyEvent)
} else {
selectedItems.remove(position)
}
removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size())
}
holder.binding.cbRemove.isChecked = selectedItems.get(position) != null
} }
holder.binding.root.setOnClickListener {
holder.binding.cbRemove.toggle()
actionHelper.updateSelection(position, therapyEvent, holder.binding.cbRemove.isChecked)
}
holder.binding.cbRemove.isChecked = actionHelper.isSelected(position)
val nextTimestamp = if (therapyList.size != position + 1) therapyList[position + 1].timestamp else 0L val nextTimestamp = if (therapyList.size != position + 1) therapyList[position + 1].timestamp else 0L
holder.binding.delimiter.visibility = dateUtil.isSameDay(therapyEvent.timestamp, nextTimestamp).toVisibility() holder.binding.delimiter.visibility = dateUtil.isSameDayGroup(therapyEvent.timestamp, nextTimestamp).toVisibility()
} }
override fun getItemCount() = therapyList.size override fun getItemCount() = therapyList.size
@ -198,35 +187,41 @@ class TreatmentsCareportalFragment : DaggerFragment() {
} }
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
this.menu = menu
inflater.inflate(R.menu.menu_treatments_careportal, menu) inflater.inflate(R.menu.menu_treatments_careportal, menu)
super.onCreateOptionsMenu(menu, inflater) super.onCreateOptionsMenu(menu, inflater)
} }
override fun onPrepareOptionsMenu(menu: Menu) { override fun onPrepareOptionsMenu(menu: Menu) {
menu.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated updateMenuVisibility()
menu.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated
val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || !buildHelper.isEngineeringMode() val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || !buildHelper.isEngineeringMode()
menu.findItem(R.id.nav_refresh_ns)?.isVisible = !nsUploadOnly menu.findItem(R.id.nav_refresh_ns)?.isVisible = !nsUploadOnly
return super.onPrepareOptionsMenu(menu) return super.onPrepareOptionsMenu(menu)
} }
private fun updateMenuVisibility() {
menu?.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated
menu?.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated
}
override fun onOptionsItemSelected(item: MenuItem): Boolean = override fun onOptionsItemSelected(item: MenuItem): Boolean =
when (item.itemId) { when (item.itemId) {
R.id.nav_remove_items -> { R.id.nav_remove_items -> actionHelper.startRemove()
removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback())
true
}
R.id.nav_show_invalidated -> { R.id.nav_show_invalidated -> {
showInvalidated = true showInvalidated = true
rxBus.send(EventTreatmentUpdateGui()) updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records))
swapAdapter()
true true
} }
R.id.nav_hide_invalidated -> { R.id.nav_hide_invalidated -> {
showInvalidated = false showInvalidated = false
rxBus.send(EventTreatmentUpdateGui()) updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.hide_invalidated_records))
swapAdapter()
true true
} }
@ -243,36 +238,7 @@ class TreatmentsCareportalFragment : DaggerFragment() {
else -> false else -> false
} }
inner class RemoveActionModeCallback : ActionMode.Callback { private fun getConfirmationText(selectedItems: SparseArray<TherapyEvent>): String {
override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean {
mode.menuInflater.inflate(R.menu.menu_delete_selection, menu)
selectedItems.clear()
mode.title = rh.gs(R.string.count_selected, selectedItems.size())
binding.recyclerview.adapter?.notifyDataSetChanged()
return true
}
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?) = false
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
return when (item.itemId) {
R.id.remove_selected -> {
removeSelected()
true
}
else -> false
}
}
override fun onDestroyActionMode(mode: ActionMode?) {
removeActionMode = null
binding.recyclerview.adapter?.notifyDataSetChanged()
}
}
private fun getConfirmationText(): String {
if (selectedItems.size() == 1) { if (selectedItems.size() == 1) {
val therapyEvent = selectedItems.valueAt(0) val therapyEvent = selectedItems.valueAt(0)
return rh.gs(R.string.eventtype) + ": " + translator.translate(therapyEvent.type) + "\n" + return rh.gs(R.string.eventtype) + ": " + translator.translate(therapyEvent.type) + "\n" +
@ -282,27 +248,24 @@ class TreatmentsCareportalFragment : DaggerFragment() {
return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size()) return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size())
} }
private fun removeSelected() { private fun removeSelected(selectedItems: SparseArray<TherapyEvent>) {
if (selectedItems.size() > 0) activity?.let { activity ->
activity?.let { activity -> OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(selectedItems), Runnable {
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable { selectedItems.forEach { _, therapyEvent ->
selectedItems.forEach { _, therapyEvent -> uel.log(
uel.log( Action.CAREPORTAL_REMOVED, Sources.Treatments, therapyEvent.note,
Action.CAREPORTAL_REMOVED, Sources.Treatments, therapyEvent.note, ValueWithUnit.Timestamp(therapyEvent.timestamp),
ValueWithUnit.Timestamp(therapyEvent.timestamp), ValueWithUnit.TherapyEventType(therapyEvent.type)
ValueWithUnit.TherapyEventType(therapyEvent.type) )
disposable += repository.runTransactionForResult(InvalidateTherapyEventTransaction(therapyEvent.id))
.subscribe(
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated therapy event $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating therapy event", it) }
) )
disposable += repository.runTransactionForResult(InvalidateTherapyEventTransaction(therapyEvent.id)) }
.subscribe( actionHelper.finish()
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated therapy event $it") } }, })
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating therapy event", it) } }
)
}
removeActionMode?.finish()
})
}
else
removeActionMode?.finish()
} }
} }

View file

@ -4,13 +4,12 @@ import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import android.util.SparseArray import android.util.SparseArray
import android.view.* import android.view.*
import android.view.ActionMode
import androidx.appcompat.widget.Toolbar
import androidx.core.util.forEach import androidx.core.util.forEach
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import dagger.android.support.DaggerFragment import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.fragments.TreatmentsExtendedBolusesFragment.RecyclerViewAdapter.ExtendedBolusesViewHolder
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.ExtendedBolus import info.nightscout.androidaps.database.entities.ExtendedBolus
import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Action
@ -26,20 +25,20 @@ import info.nightscout.androidaps.extensions.isInProgress
import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.activities.fragments.TreatmentsExtendedBolusesFragment.RecyclerViewAdapter.ExtendedBolusesViewHolder import info.nightscout.androidaps.utils.ActionModeHelper
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
import info.nightscout.shared.logging.LTag
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
@ -61,26 +60,32 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() {
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
private var _binding: TreatmentsExtendedbolusFragmentBinding? = null private var _binding: TreatmentsExtendedbolusFragmentBinding? = null
// This property is only valid between onCreateView and onDestroyView. // This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!! private val binding get() = _binding!!
private var menu: Menu? = null
private var selectedItems: SparseArray<ExtendedBolus> = SparseArray() private lateinit var actionHelper: ActionModeHelper<ExtendedBolus>
private var showInvalidated = false private var showInvalidated = false
private var removeActionMode: ActionMode? = null
private var toolbar: Toolbar? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
TreatmentsExtendedbolusFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root TreatmentsExtendedbolusFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root
@SuppressLint("NotifyDataSetChanged")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
actionHelper = ActionModeHelper(rh, activity)
actionHelper.setUpdateListHandler { binding.recyclerview.adapter?.notifyDataSetChanged() }
actionHelper.setOnRemoveHandler { removeSelected(it) }
setHasOptionsMenu(true) setHasOptionsMenu(true)
toolbar = activity?.findViewById(R.id.toolbar)
binding.recyclerview.setHasFixedSize(true) binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context) binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
binding.recyclerview.emptyView = binding.noRecordsText
binding.recyclerview.loadingView = binding.progressBar
} }
fun swapAdapter() { fun swapAdapter() {
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
binding.recyclerview.isLoading = true
disposable += if (showInvalidated) disposable += if (showInvalidated)
repository repository
.getExtendedBolusDataIncludingInvalidFromTime(now - millsToThePast, false) .getExtendedBolusDataIncludingInvalidFromTime(now - millsToThePast, false)
@ -97,7 +102,6 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
swapAdapter() swapAdapter()
disposable += rxBus disposable += rxBus
.toObservable(EventExtendedBolusChange::class.java) .toObservable(EventExtendedBolusChange::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
@ -108,13 +112,13 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() {
@Synchronized @Synchronized
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
actionHelper.finish()
disposable.clear() disposable.clear()
} }
@Synchronized @Synchronized
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
removeActionMode?.let { it.finish() }
binding.recyclerview.adapter = null // avoid leaks binding.recyclerview.adapter = null // avoid leaks
_binding = null _binding = null
} }
@ -131,13 +135,12 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() {
holder.binding.ns.visibility = (extendedBolus.interfaceIDs.nightscoutId != null).toVisibility() holder.binding.ns.visibility = (extendedBolus.interfaceIDs.nightscoutId != null).toVisibility()
holder.binding.ph.visibility = (extendedBolus.interfaceIDs.pumpId != null).toVisibility() holder.binding.ph.visibility = (extendedBolus.interfaceIDs.pumpId != null).toVisibility()
holder.binding.invalid.visibility = extendedBolus.isValid.not().toVisibility() holder.binding.invalid.visibility = extendedBolus.isValid.not().toVisibility()
val sameDayPrevious = position > 0 && dateUtil.isSameDay(extendedBolus.timestamp, extendedBolusList[position - 1].timestamp) val newDay = position == 0 || !dateUtil.isSameDayGroup(extendedBolus.timestamp, extendedBolusList[position - 1].timestamp)
holder.binding.date.visibility = sameDayPrevious.not().toVisibility() holder.binding.date.visibility = newDay.toVisibility()
holder.binding.date.text = dateUtil.dateString(extendedBolus.timestamp) holder.binding.date.text = if (newDay) dateUtil.dateStringRelative(extendedBolus.timestamp, rh) else ""
@SuppressLint("SetTextI18n")
if (extendedBolus.isInProgress(dateUtil)) { if (extendedBolus.isInProgress(dateUtil)) {
holder.binding.time.text = dateUtil.timeString(extendedBolus.timestamp) holder.binding.time.text = dateUtil.timeString(extendedBolus.timestamp)
holder.binding.time.setTextColor(rh.gc(R.color.colorActive)) holder.binding.time.setTextColor(rh.gac(context , R.attr.activeColor))
} else { } else {
holder.binding.time.text = dateUtil.timeRangeString(extendedBolus.timestamp, extendedBolus.end) holder.binding.time.text = dateUtil.timeRangeString(extendedBolus.timestamp, extendedBolus.end)
holder.binding.time.setTextColor(holder.binding.insulin.currentTextColor) holder.binding.time.setTextColor(holder.binding.insulin.currentTextColor)
@ -148,21 +151,20 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() {
val iob = extendedBolus.iobCalc(System.currentTimeMillis(), profile, activePlugin.activeInsulin) val iob = extendedBolus.iobCalc(System.currentTimeMillis(), profile, activePlugin.activeInsulin)
holder.binding.iob.text = rh.gs(R.string.formatinsulinunits, iob.iob) holder.binding.iob.text = rh.gs(R.string.formatinsulinunits, iob.iob)
holder.binding.ratio.text = rh.gs(R.string.pump_basebasalrate, extendedBolus.rate) holder.binding.ratio.text = rh.gs(R.string.pump_basebasalrate, extendedBolus.rate)
if (iob.iob != 0.0) holder.binding.iob.setTextColor(rh.gc(R.color.colorActive)) else holder.binding.iob.setTextColor(holder.binding.insulin.currentTextColor) if (iob.iob != 0.0) holder.binding.iob.setTextColor(rh.gac(context , R.attr.activeColor)) else holder.binding.iob.setTextColor(holder.binding.insulin.currentTextColor)
holder.binding.cbRemove.visibility = (extendedBolus.isValid && removeActionMode != null).toVisibility() holder.binding.cbRemove.visibility = (extendedBolus.isValid && actionHelper.isRemoving).toVisibility()
if (removeActionMode != null) { if (actionHelper.isRemoving) {
holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> holder.binding.cbRemove.setOnCheckedChangeListener { _, value ->
if (value) { actionHelper.updateSelection(position, extendedBolus, value)
selectedItems.put(position, extendedBolus)
} else {
selectedItems.remove(position)
}
removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size())
} }
holder.binding.cbRemove.isChecked = selectedItems.get(position) != null holder.binding.root.setOnClickListener {
holder.binding.cbRemove.toggle()
actionHelper.updateSelection(position, extendedBolus, holder.binding.cbRemove.isChecked)
}
holder.binding.cbRemove.isChecked = actionHelper.isSelected(position)
} }
val nextTimestamp = if (extendedBolusList.size != position + 1) extendedBolusList[position + 1].timestamp else 0L val nextTimestamp = if (extendedBolusList.size != position + 1) extendedBolusList[position + 1].timestamp else 0L
holder.binding.delimiter.visibility = dateUtil.isSameDay(extendedBolus.timestamp, nextTimestamp).toVisibility() holder.binding.delimiter.visibility = dateUtil.isSameDayGroup(extendedBolus.timestamp, nextTimestamp).toVisibility()
} }
override fun getItemCount() = extendedBolusList.size override fun getItemCount() = extendedBolusList.size
@ -175,33 +177,38 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() {
} }
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
this.menu = menu
inflater.inflate(R.menu.menu_treatments_extended_bolus, menu) inflater.inflate(R.menu.menu_treatments_extended_bolus, menu)
super.onCreateOptionsMenu(menu, inflater) super.onCreateOptionsMenu(menu, inflater)
} }
override fun onPrepareOptionsMenu(menu: Menu) { private fun updateMenuVisibility() {
menu.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated menu?.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated
menu.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated menu?.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated
}
override fun onPrepareOptionsMenu(menu: Menu) {
updateMenuVisibility()
return super.onPrepareOptionsMenu(menu) return super.onPrepareOptionsMenu(menu)
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) { return when (item.itemId) {
R.id.nav_remove_items -> { R.id.nav_remove_items -> actionHelper.startRemove()
removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback())
true
}
R.id.nav_show_invalidated -> { R.id.nav_show_invalidated -> {
showInvalidated = true showInvalidated = true
rxBus.send(EventTreatmentUpdateGui()) updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records))
swapAdapter()
true true
} }
R.id.nav_hide_invalidated -> { R.id.nav_hide_invalidated -> {
showInvalidated = false showInvalidated = false
rxBus.send(EventTreatmentUpdateGui()) updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.hide_invalidated_records))
swapAdapter()
true true
} }
@ -209,36 +216,7 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() {
} }
} }
inner class RemoveActionModeCallback : ActionMode.Callback { private fun getConfirmationText(selectedItems: SparseArray<ExtendedBolus>): String {
override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean {
mode.menuInflater.inflate(R.menu.menu_delete_selection, menu)
selectedItems.clear()
mode.title = rh.gs(R.string.count_selected, selectedItems.size())
binding.recyclerview.adapter?.notifyDataSetChanged()
return true
}
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?) = false
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
return when (item.itemId) {
R.id.remove_selected -> {
removeSelected()
true
}
else -> false
}
}
override fun onDestroyActionMode(mode: ActionMode?) {
removeActionMode = null
binding.recyclerview.adapter?.notifyDataSetChanged()
}
}
private fun getConfirmationText(): String {
if (selectedItems.size() == 1) { if (selectedItems.size() == 1) {
val bolus = selectedItems.valueAt(0) val bolus = selectedItems.valueAt(0)
return rh.gs(R.string.extended_bolus) + "\n" + return rh.gs(R.string.extended_bolus) + "\n" +
@ -247,27 +225,25 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() {
return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size()) return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size())
} }
private fun removeSelected() { private fun removeSelected(selectedItems: SparseArray<ExtendedBolus>) {
if (selectedItems.size() > 0) activity?.let { activity ->
activity?.let { activity -> OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(selectedItems), Runnable {
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable { selectedItems.forEach { _, extendedBolus ->
selectedItems.forEach { _, extendedBolus -> uel.log(
uel.log( Action.EXTENDED_BOLUS_REMOVED, Sources.Treatments,
Action.EXTENDED_BOLUS_REMOVED, Sources.Treatments, ValueWithUnit.Timestamp(extendedBolus.timestamp),
ValueWithUnit.Timestamp(extendedBolus.timestamp), ValueWithUnit.Insulin(extendedBolus.amount),
ValueWithUnit.Insulin(extendedBolus.amount), ValueWithUnit.UnitPerHour(extendedBolus.rate),
ValueWithUnit.UnitPerHour(extendedBolus.rate), ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(extendedBolus.duration).toInt())
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(extendedBolus.duration).toInt()) )
) disposable += repository.runTransactionForResult(InvalidateExtendedBolusTransaction(extendedBolus.id))
disposable += repository.runTransactionForResult(InvalidateExtendedBolusTransaction(extendedBolus.id)) .subscribe(
.subscribe( { aapsLogger.debug(LTag.DATABASE, "Removed extended bolus $extendedBolus") },
{ aapsLogger.debug(LTag.DATABASE, "Removed extended bolus $extendedBolus") }, { aapsLogger.error(LTag.DATABASE, "Error while invalidating extended bolus", it) })
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating extended bolus", it) }) }
} actionHelper.finish()
removeActionMode?.finish() })
}) }
}
else
removeActionMode?.finish()
} }
} }

View file

@ -1,11 +1,10 @@
package info.nightscout.androidaps.activities.fragments package info.nightscout.androidaps.activities.fragments
import android.annotation.SuppressLint
import android.graphics.Paint import android.graphics.Paint
import android.os.Bundle import android.os.Bundle
import android.util.SparseArray import android.util.SparseArray
import android.view.* import android.view.*
import android.view.ActionMode
import androidx.appcompat.widget.Toolbar
import androidx.core.util.forEach import androidx.core.util.forEach
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -23,7 +22,6 @@ import info.nightscout.androidaps.databinding.TreatmentsProfileswitchItemBinding
import info.nightscout.androidaps.dialogs.ProfileViewerDialog import info.nightscout.androidaps.dialogs.ProfileViewerDialog
import info.nightscout.androidaps.events.EventEffectiveProfileSwitchChanged import info.nightscout.androidaps.events.EventEffectiveProfileSwitchChanged
import info.nightscout.androidaps.events.EventProfileSwitchChanged import info.nightscout.androidaps.events.EventProfileSwitchChanged
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.extensions.getCustomizedName import info.nightscout.androidaps.extensions.getCustomizedName
import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
@ -32,9 +30,11 @@ import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientR
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged
import info.nightscout.androidaps.utils.ActionModeHelper
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
@ -63,26 +63,29 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() {
@Inject lateinit var uel: UserEntryLogger @Inject lateinit var uel: UserEntryLogger
private var _binding: TreatmentsProfileswitchFragmentBinding? = null private var _binding: TreatmentsProfileswitchFragmentBinding? = null
// This property is only valid between onCreateView and onDestroyView. // This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!! private val binding get() = _binding!!
private var menu: Menu? = null
private lateinit var actionHelper: ActionModeHelper<ProfileSealed>
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
private val millsToThePast = T.days(30).msecs() private val millsToThePast = T.days(30).msecs()
private var selectedItems: SparseArray<ProfileSealed> = SparseArray()
private var showInvalidated = false private var showInvalidated = false
private var removeActionMode: ActionMode? = null
private var toolbar: Toolbar? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
TreatmentsProfileswitchFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root TreatmentsProfileswitchFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root
@SuppressLint("NotifyDataSetChanged")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
actionHelper = ActionModeHelper(rh, activity)
actionHelper.setUpdateListHandler { binding.recyclerview.adapter?.notifyDataSetChanged() }
actionHelper.setOnRemoveHandler { removeSelected(it) }
setHasOptionsMenu(true) setHasOptionsMenu(true)
toolbar = activity?.findViewById(R.id.toolbar)
binding.recyclerview.setHasFixedSize(true) binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context) binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
binding.recyclerview.emptyView = binding.noRecordsText
binding.recyclerview.loadingView = binding.progressBar
} }
private fun refreshFromNightscout() { private fun refreshFromNightscout() {
@ -127,7 +130,7 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() {
fun swapAdapter() { fun swapAdapter() {
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
binding.recyclerview.isLoading = true
disposable += disposable +=
if (showInvalidated) if (showInvalidated)
profileSwitchWithInvalid(now) profileSwitchWithInvalid(now)
@ -164,13 +167,13 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() {
@Synchronized @Synchronized
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
actionHelper.finish()
disposable.clear() disposable.clear()
} }
@Synchronized @Synchronized
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
removeActionMode?.finish()
binding.recyclerview.adapter = null // avoid leaks binding.recyclerview.adapter = null // avoid leaks
_binding = null _binding = null
} }
@ -184,36 +187,35 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() {
val profileSwitch = profileSwitchList[position] val profileSwitch = profileSwitchList[position]
holder.binding.ph.visibility = (profileSwitch is ProfileSealed.EPS).toVisibility() holder.binding.ph.visibility = (profileSwitch is ProfileSealed.EPS).toVisibility()
holder.binding.ns.visibility = (profileSwitch.interfaceIDs_backing?.nightscoutId != null).toVisibility() holder.binding.ns.visibility = (profileSwitch.interfaceIDs_backing?.nightscoutId != null).toVisibility()
val sameDayPrevious = position > 0 && dateUtil.isSameDay(profileSwitch.timestamp, profileSwitchList[position - 1].timestamp) val newDay = position == 0 || !dateUtil.isSameDayGroup(profileSwitch.timestamp, profileSwitchList[position - 1].timestamp)
holder.binding.date.visibility = sameDayPrevious.not().toVisibility() holder.binding.date.visibility = newDay.toVisibility()
holder.binding.date.text = dateUtil.dateString(profileSwitch.timestamp) holder.binding.date.text = if (newDay) dateUtil.dateStringRelative(profileSwitch.timestamp, rh) else ""
holder.binding.time.text = dateUtil.timeString(profileSwitch.timestamp) holder.binding.time.text = dateUtil.timeString(profileSwitch.timestamp)
holder.binding.duration.text = rh.gs(R.string.format_mins, T.msecs(profileSwitch.duration ?: 0L).mins()) holder.binding.duration.text = rh.gs(R.string.format_mins, T.msecs(profileSwitch.duration ?: 0L).mins())
holder.binding.name.text = holder.binding.name.text =
if (profileSwitch is ProfileSealed.PS) profileSwitch.value.getCustomizedName() else if (profileSwitch is ProfileSealed.EPS) profileSwitch.value.originalCustomizedName else "" if (profileSwitch is ProfileSealed.PS) profileSwitch.value.getCustomizedName() else if (profileSwitch is ProfileSealed.EPS) profileSwitch.value.originalCustomizedName else ""
if (profileSwitch.isInProgress(dateUtil)) holder.binding.date.setTextColor(rh.gc(R.color.colorActive)) if (profileSwitch.isInProgress(dateUtil)) holder.binding.date.setTextColor(rh.gac(context , R.attr.activeColor))
else holder.binding.date.setTextColor(holder.binding.duration.currentTextColor) else holder.binding.date.setTextColor(holder.binding.duration.currentTextColor)
holder.binding.clone.tag = profileSwitch holder.binding.clone.tag = profileSwitch
holder.binding.name.tag = profileSwitch holder.binding.name.tag = profileSwitch
holder.binding.date.tag = profileSwitch holder.binding.date.tag = profileSwitch
holder.binding.invalid.visibility = profileSwitch.isValid.not().toVisibility() holder.binding.invalid.visibility = profileSwitch.isValid.not().toVisibility()
holder.binding.duration.visibility = (profileSwitch.duration != 0L && profileSwitch.duration != null).toVisibility() holder.binding.duration.visibility = (profileSwitch.duration != 0L && profileSwitch.duration != null).toVisibility()
holder.binding.cbRemove.visibility = (removeActionMode != null && profileSwitch is ProfileSealed.PS).toVisibility() holder.binding.cbRemove.visibility = (actionHelper.isRemoving && profileSwitch is ProfileSealed.PS).toVisibility()
if (removeActionMode != null) { if (actionHelper.isRemoving) {
holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> holder.binding.cbRemove.setOnCheckedChangeListener { _, value ->
if (value) { actionHelper.updateSelection(position, profileSwitch, value)
selectedItems.put(position, profileSwitch)
} else {
selectedItems.remove(position)
}
removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size())
} }
holder.binding.cbRemove.isChecked = selectedItems.get(position) != null holder.binding.root.setOnClickListener {
holder.binding.cbRemove.toggle()
actionHelper.updateSelection(position, profileSwitch, holder.binding.cbRemove.isChecked)
}
holder.binding.cbRemove.isChecked = actionHelper.isSelected(position)
} }
holder.binding.clone.visibility = (profileSwitch is ProfileSealed.PS).toVisibility() holder.binding.clone.visibility = (profileSwitch is ProfileSealed.PS).toVisibility()
holder.binding.spacer.visibility = (profileSwitch is ProfileSealed.PS).toVisibility() holder.binding.spacer.visibility = (profileSwitch is ProfileSealed.PS).toVisibility()
val nextTimestamp = if (profileSwitchList.size != position + 1) profileSwitchList[position + 1].timestamp else 0L val nextTimestamp = if (profileSwitchList.size != position + 1) profileSwitchList[position + 1].timestamp else 0L
holder.binding.delimiter.visibility = dateUtil.isSameDay(profileSwitch.timestamp, nextTimestamp).toVisibility() holder.binding.delimiter.visibility = dateUtil.isSameDayGroup(profileSwitch.timestamp, nextTimestamp).toVisibility()
} }
override fun getItemCount() = profileSwitchList.size override fun getItemCount() = profileSwitchList.size
@ -273,13 +275,18 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() {
} }
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
this.menu = menu
inflater.inflate(R.menu.menu_treatments_profile_switch, menu) inflater.inflate(R.menu.menu_treatments_profile_switch, menu)
super.onCreateOptionsMenu(menu, inflater) super.onCreateOptionsMenu(menu, inflater)
} }
private fun updateMenuVisibility() {
menu?.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated
menu?.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated
}
override fun onPrepareOptionsMenu(menu: Menu) { override fun onPrepareOptionsMenu(menu: Menu) {
menu.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated updateMenuVisibility()
menu.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated
val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_profile_switch, false) || !buildHelper.isEngineeringMode() val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_profile_switch, false) || !buildHelper.isEngineeringMode()
menu.findItem(R.id.nav_refresh_ns)?.isVisible = !nsUploadOnly menu.findItem(R.id.nav_refresh_ns)?.isVisible = !nsUploadOnly
@ -288,20 +295,21 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() {
override fun onOptionsItemSelected(item: MenuItem): Boolean = override fun onOptionsItemSelected(item: MenuItem): Boolean =
when (item.itemId) { when (item.itemId) {
R.id.nav_remove_items -> { R.id.nav_remove_items -> actionHelper.startRemove()
removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback())
true
}
R.id.nav_show_invalidated -> { R.id.nav_show_invalidated -> {
showInvalidated = true showInvalidated = true
rxBus.send(EventTreatmentUpdateGui()) updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records))
swapAdapter()
true true
} }
R.id.nav_hide_invalidated -> { R.id.nav_hide_invalidated -> {
showInvalidated = false showInvalidated = false
rxBus.send(EventTreatmentUpdateGui()) updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.hide_invalidated_records))
swapAdapter()
true true
} }
@ -313,36 +321,7 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() {
else -> false else -> false
} }
inner class RemoveActionModeCallback : ActionMode.Callback { private fun getConfirmationText(selectedItems: SparseArray<ProfileSealed>): String {
override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean {
mode.menuInflater.inflate(R.menu.menu_delete_selection, menu)
selectedItems.clear()
mode.title = rh.gs(R.string.count_selected, selectedItems.size())
binding.recyclerview.adapter?.notifyDataSetChanged()
return true
}
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?) = false
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
return when (item.itemId) {
R.id.remove_selected -> {
removeSelected()
true
}
else -> false
}
}
override fun onDestroyActionMode(mode: ActionMode?) {
removeActionMode = null
binding.recyclerview.adapter?.notifyDataSetChanged()
}
}
private fun getConfirmationText(): String {
if (selectedItems.size() == 1) { if (selectedItems.size() == 1) {
val profileSwitch = selectedItems.valueAt(0) val profileSwitch = selectedItems.valueAt(0)
return rh.gs(R.string.careportal_profileswitch) + ": " + profileSwitch.profileName + "\n" + rh.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(profileSwitch.timestamp) return rh.gs(R.string.careportal_profileswitch) + ": " + profileSwitch.profileName + "\n" + rh.gs(R.string.date) + ": " + dateUtil.dateAndTimeString(profileSwitch.timestamp)
@ -350,25 +329,22 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() {
return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size()) return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size())
} }
private fun removeSelected() { private fun removeSelected(selectedItems: SparseArray<ProfileSealed>) {
if (selectedItems.size() > 0) activity?.let { activity ->
activity?.let { activity -> OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(selectedItems), Runnable {
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable { selectedItems.forEach { _, profileSwitch ->
selectedItems.forEach { _, profileSwitch -> uel.log(
uel.log( Action.PROFILE_SWITCH_REMOVED, Sources.Treatments, profileSwitch.profileName,
Action.PROFILE_SWITCH_REMOVED, Sources.Treatments, profileSwitch.profileName, ValueWithUnit.Timestamp(profileSwitch.timestamp)
ValueWithUnit.Timestamp(profileSwitch.timestamp) )
disposable += repository.runTransactionForResult(InvalidateProfileSwitchTransaction(profileSwitch.id))
.subscribe(
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated ProfileSwitch $it") } },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating ProfileSwitch", it) }
) )
disposable += repository.runTransactionForResult(InvalidateProfileSwitchTransaction(profileSwitch.id)) }
.subscribe( actionHelper.finish()
{ result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated ProfileSwitch $it") } }, })
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating ProfileSwitch", it) } }
)
}
removeActionMode?.finish()
})
}
else
removeActionMode?.finish()
} }
} }

View file

@ -4,46 +4,41 @@ import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import android.util.SparseArray import android.util.SparseArray
import android.view.* import android.view.*
import androidx.appcompat.widget.Toolbar
import androidx.core.util.forEach import androidx.core.util.forEach
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import dagger.android.support.DaggerFragment import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.fragments.TreatmentsTempTargetFragment.RecyclerViewAdapter.TempTargetsViewHolder
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.ValueWrapper import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.entities.TemporaryTarget import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.interfaces.end import info.nightscout.androidaps.database.interfaces.end
import info.nightscout.androidaps.database.transactions.InvalidateTemporaryTargetTransaction import info.nightscout.androidaps.database.transactions.InvalidateTemporaryTargetTransaction
import info.nightscout.androidaps.databinding.TreatmentsTemptargetFragmentBinding import info.nightscout.androidaps.databinding.TreatmentsTemptargetFragmentBinding
import info.nightscout.androidaps.databinding.TreatmentsTemptargetItemBinding import info.nightscout.androidaps.databinding.TreatmentsTemptargetItemBinding
import info.nightscout.androidaps.events.EventTempTargetChange
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.activities.fragments.TreatmentsTempTargetFragment.RecyclerViewAdapter.TempTargetsViewHolder
import info.nightscout.androidaps.events.EventEffectiveProfileSwitchChanged import info.nightscout.androidaps.events.EventEffectiveProfileSwitchChanged
import info.nightscout.androidaps.events.EventProfileSwitchChanged import info.nightscout.androidaps.events.EventProfileSwitchChanged
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.events.EventTempTargetChange
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.Translator
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.extensions.friendlyDescription import info.nightscout.androidaps.extensions.friendlyDescription
import info.nightscout.androidaps.extensions.highValueToUnitsToString import info.nightscout.androidaps.extensions.highValueToUnitsToString
import info.nightscout.androidaps.extensions.lowValueToUnitsToString import info.nightscout.androidaps.extensions.lowValueToUnitsToString
import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData
import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.core.Completable import io.reactivex.rxjava3.core.Completable
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
@ -68,24 +63,28 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
private var _binding: TreatmentsTemptargetFragmentBinding? = null private var _binding: TreatmentsTemptargetFragmentBinding? = null
// This property is only valid between onCreateView and onDestroyView. // This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!! private val binding get() = _binding!!
private var menu: Menu? = null
private lateinit var actionHelper: ActionModeHelper<TemporaryTarget>
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
private val millsToThePast = T.days(30).msecs() private val millsToThePast = T.days(30).msecs()
private var selectedItems: SparseArray<TemporaryTarget> = SparseArray()
private var showInvalidated = false private var showInvalidated = false
private var toolbar: Toolbar? = null
private var removeActionMode: ActionMode? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
TreatmentsTemptargetFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root TreatmentsTemptargetFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root
@SuppressLint("NotifyDataSetChanged")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding.recyclerview.setHasFixedSize(true) binding.recyclerview.setHasFixedSize(true)
toolbar = activity?.findViewById(R.id.toolbar) actionHelper = ActionModeHelper(rh, activity)
actionHelper.setUpdateListHandler { binding.recyclerview.adapter?.notifyDataSetChanged() }
actionHelper.setOnRemoveHandler { removeSelected(it) }
setHasOptionsMenu(true) setHasOptionsMenu(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context) binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
binding.recyclerview.emptyView = binding.noRecordsText
binding.recyclerview.loadingView = binding.progressBar
} }
private fun refreshFromNightscout() { private fun refreshFromNightscout() {
@ -114,6 +113,7 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
fun swapAdapter() { fun swapAdapter() {
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
binding.recyclerview.isLoading = true
disposable += disposable +=
if (showInvalidated) if (showInvalidated)
repository repository
@ -131,30 +131,23 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
swapAdapter() swapAdapter()
disposable += rxBus disposable += rxBus
.toObservable(EventTempTargetChange::class.java) .toObservable(EventTempTargetChange::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.debounce(1L, TimeUnit.SECONDS) .debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException) .subscribe({ swapAdapter() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTreatmentUpdateGui::class.java) // TODO join with above
.observeOn(aapsSchedulers.io)
.debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
} }
@Synchronized @Synchronized
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
actionHelper.finish()
disposable.clear() disposable.clear()
} }
@Synchronized @Synchronized
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
removeActionMode?.let { it.finish() }
binding.recyclerview.adapter = null // avoid leaks binding.recyclerview.adapter = null // avoid leaks
_binding = null _binding = null
} }
@ -173,21 +166,20 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
val tempTarget = tempTargetList[position] val tempTarget = tempTargetList[position]
holder.binding.ns.visibility = (tempTarget.interfaceIDs.nightscoutId != null).toVisibility() holder.binding.ns.visibility = (tempTarget.interfaceIDs.nightscoutId != null).toVisibility()
holder.binding.invalid.visibility = tempTarget.isValid.not().toVisibility() holder.binding.invalid.visibility = tempTarget.isValid.not().toVisibility()
holder.binding.cbRemove.visibility = (tempTarget.isValid && removeActionMode != null).toVisibility() holder.binding.cbRemove.visibility = (tempTarget.isValid && actionHelper.isRemoving).toVisibility()
if (removeActionMode != null) { if (actionHelper.isRemoving) {
holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> holder.binding.cbRemove.setOnCheckedChangeListener { _, value ->
if (value) { actionHelper.updateSelection(position, tempTarget, value)
selectedItems.put(position, tempTarget)
} else {
selectedItems.remove(position)
}
removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size())
} }
holder.binding.cbRemove.isChecked = selectedItems.get(position) != null holder.binding.root.setOnClickListener {
holder.binding.cbRemove.toggle()
actionHelper.updateSelection(position, tempTarget, holder.binding.cbRemove.isChecked)
}
holder.binding.cbRemove.isChecked = actionHelper.isSelected(position)
} }
val sameDayPrevious = position > 0 && dateUtil.isSameDay(tempTarget.timestamp, tempTargetList[position - 1].timestamp) val newDay = position == 0 || !dateUtil.isSameDayGroup(tempTarget.timestamp, tempTargetList[position - 1].timestamp)
holder.binding.date.visibility = sameDayPrevious.not().toVisibility() holder.binding.date.visibility = newDay.toVisibility()
holder.binding.date.text = dateUtil.dateString(tempTarget.timestamp) holder.binding.date.text = if (newDay) dateUtil.dateStringRelative(tempTarget.timestamp, rh) else ""
holder.binding.time.text = dateUtil.timeRangeString(tempTarget.timestamp, tempTarget.end) holder.binding.time.text = dateUtil.timeRangeString(tempTarget.timestamp, tempTarget.end)
holder.binding.duration.text = rh.gs(R.string.format_mins, T.msecs(tempTarget.duration).mins()) holder.binding.duration.text = rh.gs(R.string.format_mins, T.msecs(tempTarget.duration).mins())
holder.binding.low.text = tempTarget.lowValueToUnitsToString(units) holder.binding.low.text = tempTarget.lowValueToUnitsToString(units)
@ -195,13 +187,13 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
holder.binding.reason.text = translator.translate(tempTarget.reason) holder.binding.reason.text = translator.translate(tempTarget.reason)
holder.binding.time.setTextColor( holder.binding.time.setTextColor(
when { when {
tempTarget.id == currentlyActiveTarget?.id -> rh.gc(R.color.colorActive) tempTarget.id == currentlyActiveTarget?.id -> rh.gac(context , R.attr.activeColor)
tempTarget.timestamp > dateUtil.now() -> rh.gc(R.color.colorScheduled) tempTarget.timestamp > dateUtil.now() -> rh.gac(context , R.attr.scheduledColor)
else -> holder.binding.reasonColon.currentTextColor else -> holder.binding.reasonColon.currentTextColor
} }
) )
val nextTimestamp = if (tempTargetList.size != position + 1) tempTargetList[position + 1].timestamp else 0L val nextTimestamp = if (tempTargetList.size != position + 1) tempTargetList[position + 1].timestamp else 0L
holder.binding.delimiter.visibility = dateUtil.isSameDay(tempTarget.timestamp, nextTimestamp).toVisibility() holder.binding.delimiter.visibility = dateUtil.isSameDayGroup(tempTarget.timestamp, nextTimestamp).toVisibility()
} }
override fun getItemCount() = tempTargetList.size override fun getItemCount() = tempTargetList.size
@ -213,39 +205,19 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
} }
} }
private fun removeSelected() {
if (selectedItems.size() > 0)
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable {
selectedItems.forEach { _, tempTarget ->
uel.log(
Action.TT_REMOVED, Sources.Treatments,
ValueWithUnit.Timestamp(tempTarget.timestamp),
ValueWithUnit.TherapyEventTTReason(tempTarget.reason),
ValueWithUnit.Mgdl(tempTarget.lowTarget),
ValueWithUnit.Mgdl(tempTarget.highTarget).takeIf { tempTarget.lowTarget != tempTarget.highTarget },
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(tempTarget.duration).toInt())
)
disposable += repository.runTransactionForResult(InvalidateTemporaryTargetTransaction(tempTarget.id))
.subscribe(
{ aapsLogger.debug(LTag.DATABASE, "Removed temp target $tempTarget") },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating temporary target", it) })
}
removeActionMode?.finish()
})
}
else
removeActionMode?.finish()
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
this.menu = menu
inflater.inflate(R.menu.menu_treatments_temp_target, menu) inflater.inflate(R.menu.menu_treatments_temp_target, menu)
super.onCreateOptionsMenu(menu, inflater) super.onCreateOptionsMenu(menu, inflater)
} }
private fun updateMenuVisibility() {
menu?.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated
menu?.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated
}
override fun onPrepareOptionsMenu(menu: Menu) { override fun onPrepareOptionsMenu(menu: Menu) {
menu.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated updateMenuVisibility()
menu.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated
val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_temp_target, false) || !buildHelper.isEngineeringMode() val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_temp_target, false) || !buildHelper.isEngineeringMode()
menu.findItem(R.id.nav_refresh_ns)?.isVisible = !nsUploadOnly menu.findItem(R.id.nav_refresh_ns)?.isVisible = !nsUploadOnly
@ -254,20 +226,21 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
override fun onOptionsItemSelected(item: MenuItem): Boolean = override fun onOptionsItemSelected(item: MenuItem): Boolean =
when (item.itemId) { when (item.itemId) {
R.id.nav_remove_items -> { R.id.nav_remove_items -> actionHelper.startRemove()
removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback())
true
}
R.id.nav_show_invalidated -> { R.id.nav_show_invalidated -> {
showInvalidated = true showInvalidated = true
rxBus.send(EventTreatmentUpdateGui()) updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records))
swapAdapter()
true true
} }
R.id.nav_hide_invalidated -> { R.id.nav_hide_invalidated -> {
showInvalidated = false showInvalidated = false
rxBus.send(EventTreatmentUpdateGui()) updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records))
swapAdapter()
true true
} }
@ -279,36 +252,7 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
else -> false else -> false
} }
inner class RemoveActionModeCallback : ActionMode.Callback { private fun getConfirmationText(selectedItems: SparseArray<TemporaryTarget>): String {
override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean {
mode.menuInflater.inflate(R.menu.menu_delete_selection, menu)
selectedItems.clear()
mode.title = rh.gs(R.string.count_selected, selectedItems.size())
binding.recyclerview.adapter?.notifyDataSetChanged()
return true
}
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?) = false
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
return when (item.itemId) {
R.id.remove_selected -> {
removeSelected()
true
}
else -> false
}
}
override fun onDestroyActionMode(mode: ActionMode?) {
removeActionMode = null
binding.recyclerview.adapter?.notifyDataSetChanged()
}
}
private fun getConfirmationText(): String {
if (selectedItems.size() == 1) { if (selectedItems.size() == 1) {
val tempTarget = selectedItems.valueAt(0) val tempTarget = selectedItems.valueAt(0)
return "${rh.gs(R.string.careportal_temporarytarget)}: ${tempTarget.friendlyDescription(profileFunction.getUnits(), rh)}\n" + return "${rh.gs(R.string.careportal_temporarytarget)}: ${tempTarget.friendlyDescription(profileFunction.getUnits(), rh)}\n" +
@ -317,4 +261,26 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size()) return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size())
} }
private fun removeSelected(selectedItems: SparseArray<TemporaryTarget>) {
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(selectedItems), Runnable {
selectedItems.forEach { _, tempTarget ->
uel.log(
Action.TT_REMOVED, Sources.Treatments,
ValueWithUnit.Timestamp(tempTarget.timestamp),
ValueWithUnit.TherapyEventTTReason(tempTarget.reason),
ValueWithUnit.Mgdl(tempTarget.lowTarget),
ValueWithUnit.Mgdl(tempTarget.highTarget).takeIf { tempTarget.lowTarget != tempTarget.highTarget },
ValueWithUnit.Minute(TimeUnit.MILLISECONDS.toMinutes(tempTarget.duration).toInt())
)
disposable += repository.runTransactionForResult(InvalidateTemporaryTargetTransaction(tempTarget.id))
.subscribe(
{ aapsLogger.debug(LTag.DATABASE, "Removed temp target $tempTarget") },
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating temporary target", it) })
}
actionHelper.finish()
})
}
}
} }

View file

@ -1,15 +1,15 @@
package info.nightscout.androidaps.activities.fragments package info.nightscout.androidaps.activities.fragments
import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.util.SparseArray import android.util.SparseArray
import android.view.* import android.view.*
import androidx.appcompat.widget.Toolbar
import androidx.core.util.forEach import androidx.core.util.forEach
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import dagger.android.support.DaggerFragment import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.fragments.TreatmentsTemporaryBasalsFragment.RecyclerViewAdapter.TempBasalsViewHolder
import info.nightscout.androidaps.data.IobTotal import info.nightscout.androidaps.data.IobTotal
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.ValueWrapper import info.nightscout.androidaps.database.ValueWrapper
@ -23,7 +23,6 @@ import info.nightscout.androidaps.database.transactions.InvalidateExtendedBolusT
import info.nightscout.androidaps.database.transactions.InvalidateTemporaryBasalTransaction import info.nightscout.androidaps.database.transactions.InvalidateTemporaryBasalTransaction
import info.nightscout.androidaps.databinding.TreatmentsTempbasalsFragmentBinding import info.nightscout.androidaps.databinding.TreatmentsTempbasalsFragmentBinding
import info.nightscout.androidaps.databinding.TreatmentsTempbasalsItemBinding import info.nightscout.androidaps.databinding.TreatmentsTempbasalsItemBinding
import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.events.EventTempBasalChange import info.nightscout.androidaps.events.EventTempBasalChange
import info.nightscout.androidaps.extensions.iobCalc import info.nightscout.androidaps.extensions.iobCalc
import info.nightscout.androidaps.extensions.toStringFull import info.nightscout.androidaps.extensions.toStringFull
@ -31,18 +30,18 @@ import info.nightscout.androidaps.extensions.toTemporaryBasal
import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.activities.fragments.TreatmentsTemporaryBasalsFragment.RecyclerViewAdapter.TempBasalsViewHolder import info.nightscout.androidaps.utils.ActionModeHelper
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -65,24 +64,28 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
private var _binding: TreatmentsTempbasalsFragmentBinding? = null private var _binding: TreatmentsTempbasalsFragmentBinding? = null
// This property is only valid between onCreateView and onDestroyView. // This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!! private val binding get() = _binding!!
private var menu: Menu? = null
private lateinit var actionHelper: ActionModeHelper<TemporaryBasal>
private val millsToThePast = T.days(30).msecs() private val millsToThePast = T.days(30).msecs()
private var selectedItems: SparseArray<TemporaryBasal> = SparseArray()
private var showInvalidated = false private var showInvalidated = false
private var toolbar: Toolbar? = null
private var removeActionMode: ActionMode? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
TreatmentsTempbasalsFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root TreatmentsTempbasalsFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root
@SuppressLint("NotifyDataSetChanged")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
toolbar = activity?.findViewById(R.id.toolbar) actionHelper = ActionModeHelper(rh, activity)
actionHelper.setUpdateListHandler { binding.recyclerview.adapter?.notifyDataSetChanged() }
actionHelper.setOnRemoveHandler { removeSelected(it) }
setHasOptionsMenu(true) setHasOptionsMenu(true)
binding.recyclerview.setHasFixedSize(true) binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context) binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
binding.recyclerview.emptyView = binding.noRecordsText
binding.recyclerview.loadingView = binding.progressBar
} }
private fun tempBasalsWithInvalid(now: Long) = repository private fun tempBasalsWithInvalid(now: Long) = repository
@ -101,6 +104,7 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
fun swapAdapter() { fun swapAdapter() {
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
binding.recyclerview.isLoading = true
disposable += disposable +=
if (activePlugin.activePump.isFakingTempsByExtendedBoluses) { if (activePlugin.activePump.isFakingTempsByExtendedBoluses) {
if (showInvalidated) if (showInvalidated)
@ -134,28 +138,22 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
swapAdapter() swapAdapter()
disposable += rxBus disposable += rxBus
.toObservable(EventTempBasalChange::class.java) .toObservable(EventTempBasalChange::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ swapAdapter() }, fabricPrivacy::logException) .subscribe({ swapAdapter() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
} }
@Synchronized @Synchronized
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
actionHelper.finish()
disposable.clear() disposable.clear()
} }
@Synchronized @Synchronized
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
removeActionMode?.let { it.finish() }
binding.recyclerview.adapter = null // avoid leaks binding.recyclerview.adapter = null // avoid leaks
_binding = null _binding = null
} }
@ -172,10 +170,12 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
holder.binding.ph.visibility = (tempBasal.interfaceIDs.pumpId != null).toVisibility() holder.binding.ph.visibility = (tempBasal.interfaceIDs.pumpId != null).toVisibility()
val sameDayPrevious = position > 0 && dateUtil.isSameDay(tempBasal.timestamp, tempBasalList[position - 1].timestamp) val sameDayPrevious = position > 0 && dateUtil.isSameDay(tempBasal.timestamp, tempBasalList[position - 1].timestamp)
holder.binding.date.visibility = sameDayPrevious.not().toVisibility() holder.binding.date.visibility = sameDayPrevious.not().toVisibility()
holder.binding.date.text = dateUtil.dateString(tempBasal.timestamp) val newDay = position == 0 || !dateUtil.isSameDayGroup(tempBasal.timestamp, tempBasalList[position - 1].timestamp)
holder.binding.date.visibility = newDay.toVisibility()
holder.binding.date.text = if (newDay) dateUtil.dateStringRelative(tempBasal.timestamp, rh) else ""
if (tempBasal.isInProgress) { if (tempBasal.isInProgress) {
holder.binding.time.text = dateUtil.timeString(tempBasal.timestamp) holder.binding.time.text = dateUtil.timeString(tempBasal.timestamp)
holder.binding.time.setTextColor(rh.gc(R.color.colorActive)) holder.binding.time.setTextColor(rh.gac(context, R.attr.activeColor))
} else { } else {
holder.binding.time.text = dateUtil.timeRangeString(tempBasal.timestamp, tempBasal.end) holder.binding.time.text = dateUtil.timeRangeString(tempBasal.timestamp, tempBasal.end)
holder.binding.time.setTextColor(holder.binding.duration.currentTextColor) holder.binding.time.setTextColor(holder.binding.duration.currentTextColor)
@ -192,21 +192,20 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
holder.binding.suspendFlag.visibility = (tempBasal.type == TemporaryBasal.Type.PUMP_SUSPEND).toVisibility() holder.binding.suspendFlag.visibility = (tempBasal.type == TemporaryBasal.Type.PUMP_SUSPEND).toVisibility()
holder.binding.emulatedSuspendFlag.visibility = (tempBasal.type == TemporaryBasal.Type.EMULATED_PUMP_SUSPEND).toVisibility() holder.binding.emulatedSuspendFlag.visibility = (tempBasal.type == TemporaryBasal.Type.EMULATED_PUMP_SUSPEND).toVisibility()
holder.binding.superBolusFlag.visibility = (tempBasal.type == TemporaryBasal.Type.SUPERBOLUS).toVisibility() holder.binding.superBolusFlag.visibility = (tempBasal.type == TemporaryBasal.Type.SUPERBOLUS).toVisibility()
if (abs(iob.basaliob) > 0.01) holder.binding.iob.setTextColor(rh.gc(R.color.colorActive)) else holder.binding.iob.setTextColor(holder.binding.duration.currentTextColor) if (abs(iob.basaliob) > 0.01) holder.binding.iob.setTextColor(rh.gac(context, R.attr.activeColor)) else holder.binding.iob.setTextColor(holder.binding.duration.currentTextColor)
holder.binding.cbRemove.visibility = (tempBasal.isValid && removeActionMode != null).toVisibility() holder.binding.cbRemove.visibility = (tempBasal.isValid && actionHelper.isRemoving).toVisibility()
if (removeActionMode != null) { if (actionHelper.isRemoving) {
holder.binding.cbRemove.setOnCheckedChangeListener { _, value -> holder.binding.cbRemove.setOnCheckedChangeListener { _, value ->
if (value) { actionHelper.updateSelection(position, tempBasal, value)
selectedItems.put(position, tempBasal)
} else {
selectedItems.remove(position)
}
removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size())
} }
holder.binding.cbRemove.isChecked = selectedItems.get(position) != null holder.binding.root.setOnClickListener {
holder.binding.cbRemove.toggle()
actionHelper.updateSelection(position, tempBasal, holder.binding.cbRemove.isChecked)
}
holder.binding.cbRemove.isChecked = actionHelper.isSelected(position)
} }
val nextTimestamp = if (tempBasalList.size != position + 1) tempBasalList[position + 1].timestamp else 0L val nextTimestamp = if (tempBasalList.size != position + 1) tempBasalList[position + 1].timestamp else 0L
holder.binding.delimiter.visibility = dateUtil.isSameDay(tempBasal.timestamp, nextTimestamp).toVisibility() holder.binding.delimiter.visibility = dateUtil.isSameDayGroup(tempBasal.timestamp, nextTimestamp).toVisibility()
} }
override fun getItemCount() = tempBasalList.size override fun getItemCount() = tempBasalList.size
@ -220,69 +219,46 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
} }
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
this.menu = menu
inflater.inflate(R.menu.menu_treatments_temp_basal, menu) inflater.inflate(R.menu.menu_treatments_temp_basal, menu)
super.onCreateOptionsMenu(menu, inflater) super.onCreateOptionsMenu(menu, inflater)
} }
private fun updateMenuVisibility() {
menu?.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated
menu?.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated
}
override fun onPrepareOptionsMenu(menu: Menu) { override fun onPrepareOptionsMenu(menu: Menu) {
menu.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated updateMenuVisibility()
menu.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated
return super.onPrepareOptionsMenu(menu) return super.onPrepareOptionsMenu(menu)
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean = override fun onOptionsItemSelected(item: MenuItem): Boolean =
when (item.itemId) { when (item.itemId) {
R.id.nav_remove_items -> { R.id.nav_remove_items -> actionHelper.startRemove()
removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback())
true
}
R.id.nav_show_invalidated -> { R.id.nav_show_invalidated -> {
showInvalidated = true showInvalidated = true
rxBus.send(EventTreatmentUpdateGui()) updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records))
swapAdapter()
true true
} }
R.id.nav_hide_invalidated -> { R.id.nav_hide_invalidated -> {
showInvalidated = false showInvalidated = false
rxBus.send(EventTreatmentUpdateGui()) updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.hide_invalidated_records))
swapAdapter()
true true
} }
else -> false else -> false
} }
inner class RemoveActionModeCallback : ActionMode.Callback { private fun getConfirmationText(selectedItems: SparseArray<TemporaryBasal>): String {
override fun onCreateActionMode(mode: ActionMode, menu: Menu?): Boolean {
mode.menuInflater.inflate(R.menu.menu_delete_selection, menu)
selectedItems.clear()
mode.title = rh.gs(R.string.count_selected, selectedItems.size())
binding.recyclerview.adapter?.notifyDataSetChanged()
return true
}
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?) = false
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
return when (item.itemId) {
R.id.remove_selected -> {
removeSelected()
true
}
else -> false
}
}
override fun onDestroyActionMode(mode: ActionMode?) {
removeActionMode = null
binding.recyclerview.adapter?.notifyDataSetChanged()
}
}
private fun getConfirmationText(): String {
if (selectedItems.size() == 1) { if (selectedItems.size() == 1) {
val tempBasal = selectedItems.valueAt(0) val tempBasal = selectedItems.valueAt(0)
val isFakeExtended = tempBasal.type == TemporaryBasal.Type.FAKE_EXTENDED val isFakeExtended = tempBasal.type == TemporaryBasal.Type.FAKE_EXTENDED
@ -294,11 +270,11 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size()) return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size())
} }
private fun removeSelected() { private fun removeSelected(selectedItems: SparseArray<TemporaryBasal>) {
if (selectedItems.size() > 0) if (selectedItems.size() > 0)
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable { OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(selectedItems), Runnable {
selectedItems.forEach {_, tempBasal -> selectedItems.forEach { _, tempBasal ->
var extendedBolus: ExtendedBolus? = null var extendedBolus: ExtendedBolus? = null
val isFakeExtended = tempBasal.type == TemporaryBasal.Type.FAKE_EXTENDED val isFakeExtended = tempBasal.type == TemporaryBasal.Type.FAKE_EXTENDED
if (isFakeExtended) { if (isFakeExtended) {
@ -330,10 +306,8 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
{ aapsLogger.error(LTag.DATABASE, "Error while invalidating temporary basal", it) }) { aapsLogger.error(LTag.DATABASE, "Error while invalidating temporary basal", it) })
} }
} }
removeActionMode?.finish() actionHelper.finish()
}) })
} }
else
removeActionMode?.finish()
} }
} }

View file

@ -13,23 +13,22 @@ import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.databinding.TreatmentsUserEntryFragmentBinding import info.nightscout.androidaps.databinding.TreatmentsUserEntryFragmentBinding
import info.nightscout.androidaps.databinding.TreatmentsUserEntryItemBinding import info.nightscout.androidaps.databinding.TreatmentsUserEntryItemBinding
import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.ImportExportPrefs import info.nightscout.androidaps.interfaces.ImportExportPrefs
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.Translator import info.nightscout.androidaps.utils.Translator
import info.nightscout.androidaps.utils.userEntry.UserEntryPresentationHelper
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.userEntry.UserEntryPresentationHelper
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
class TreatmentsUserEntryFragment : DaggerFragment() { class TreatmentsUserEntryFragment : DaggerFragment() {
@ -47,9 +46,9 @@ class TreatmentsUserEntryFragment : DaggerFragment() {
@Inject lateinit var userEntryPresentationHelper: UserEntryPresentationHelper @Inject lateinit var userEntryPresentationHelper: UserEntryPresentationHelper
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
private val millsToThePastFiltered = T.days(30).msecs() private val millsToThePastFiltered = T.days(30).msecs()
private val millsToThePastUnFiltered = T.days(3).msecs() private val millsToThePastUnFiltered = T.days(3).msecs()
private var menu: Menu? = null
private var showLoop = false private var showLoop = false
private var _binding: TreatmentsUserEntryFragmentBinding? = null private var _binding: TreatmentsUserEntryFragmentBinding? = null
@ -64,9 +63,11 @@ class TreatmentsUserEntryFragment : DaggerFragment() {
setHasOptionsMenu(true) setHasOptionsMenu(true)
binding.recyclerview.setHasFixedSize(true) binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context) binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
binding.recyclerview.emptyView = binding.noRecordsText
binding.recyclerview.loadingView = binding.progressBar
} }
fun exportUserEnteries() { private fun exportUserEntries() {
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.ue_export_to_csv) + "?") { OKDialog.showConfirmation(activity, rh.gs(R.string.ue_export_to_csv) + "?") {
uel.log(Action.EXPORT_CSV, Sources.Treatments) uel.log(Action.EXPORT_CSV, Sources.Treatments)
@ -77,6 +78,7 @@ class TreatmentsUserEntryFragment : DaggerFragment() {
fun swapAdapter() { fun swapAdapter() {
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
binding.recyclerview.isLoading = true
disposable += disposable +=
if (showLoop) if (showLoop)
repository repository
@ -94,16 +96,10 @@ class TreatmentsUserEntryFragment : DaggerFragment() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
swapAdapter() swapAdapter()
disposable += rxBus disposable += rxBus
.toObservable(EventPreferenceChange::class.java) .toObservable(EventPreferenceChange::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ swapAdapter() }, fabricPrivacy::logException) .subscribe({ swapAdapter() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTreatmentUpdateGui::class.java)
.observeOn(aapsSchedulers.io)
.debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
} }
@Synchronized @Synchronized
@ -128,19 +124,18 @@ class TreatmentsUserEntryFragment : DaggerFragment() {
override fun onBindViewHolder(holder: UserEntryViewHolder, position: Int) { override fun onBindViewHolder(holder: UserEntryViewHolder, position: Int) {
val current = entries[position] val current = entries[position]
val sameDayPrevious = position > 0 && dateUtil.isSameDay(current.timestamp, entries[position - 1].timestamp) val newDay = position == 0 || !dateUtil.isSameDayGroup(current.timestamp, entries[position - 1].timestamp)
holder.binding.date.visibility = sameDayPrevious.not().toVisibility() holder.binding.date.visibility = newDay.toVisibility()
holder.binding.date.text = dateUtil.dateString(current.timestamp) holder.binding.date.text = if (newDay) dateUtil.dateStringRelative(current.timestamp, rh) else ""
holder.binding.time.text = dateUtil.timeStringWithSeconds(current.timestamp) holder.binding.time.text = dateUtil.timeStringWithSeconds(current.timestamp)
holder.binding.action.text = userEntryPresentationHelper.actionToColoredString(current.action) holder.binding.action.text = userEntryPresentationHelper.actionToColoredString(current.action)
holder.binding.notes.text = current.note holder.binding.notes.text = current.note
holder.binding.notes.visibility = if (current.note != "") View.VISIBLE else View.GONE holder.binding.notes.visibility = (current.note != "").toVisibility()
holder.binding.iconSource.setImageResource(userEntryPresentationHelper.iconId(current.source)) holder.binding.iconSource.setImageResource(userEntryPresentationHelper.iconId(current.source))
holder.binding.iconSource.visibility = View.VISIBLE
holder.binding.values.text = userEntryPresentationHelper.listToPresentationString(current.values) holder.binding.values.text = userEntryPresentationHelper.listToPresentationString(current.values)
holder.binding.values.visibility = if (holder.binding.values.text != "") View.VISIBLE else View.GONE holder.binding.values.visibility = (holder.binding.values.text != "").toVisibility()
val nextTimestamp = if (entries.size != position + 1) entries[position + 1].timestamp else 0L val nextTimestamp = if (entries.size != position + 1) entries[position + 1].timestamp else 0L
holder.binding.delimiter.visibility = dateUtil.isSameDay(current.timestamp, nextTimestamp).toVisibility() holder.binding.delimiter.visibility = dateUtil.isSameDayGroup(current.timestamp, nextTimestamp).toVisibility()
} }
inner class UserEntryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { inner class UserEntryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
@ -152,14 +147,18 @@ class TreatmentsUserEntryFragment : DaggerFragment() {
} }
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
this.menu = menu
inflater.inflate(R.menu.menu_treatments_user_entry, menu) inflater.inflate(R.menu.menu_treatments_user_entry, menu)
super.onCreateOptionsMenu(menu, inflater) super.onCreateOptionsMenu(menu, inflater)
} }
override fun onPrepareOptionsMenu(menu: Menu) { private fun updateMenuVisibility() {
menu.findItem(R.id.nav_hide_loop)?.isVisible = showLoop menu?.findItem(R.id.nav_hide_loop)?.isVisible = showLoop
menu.findItem(R.id.nav_show_loop)?.isVisible = !showLoop menu?.findItem(R.id.nav_show_loop)?.isVisible = !showLoop
}
override fun onPrepareOptionsMenu(menu: Menu) {
updateMenuVisibility()
return super.onPrepareOptionsMenu(menu) return super.onPrepareOptionsMenu(menu)
} }
@ -167,21 +166,26 @@ class TreatmentsUserEntryFragment : DaggerFragment() {
when (item.itemId) { when (item.itemId) {
R.id.nav_show_loop -> { R.id.nav_show_loop -> {
showLoop = true showLoop = true
rxBus.send(EventTreatmentUpdateGui()) updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_loop_records))
swapAdapter()
true true
} }
R.id.nav_hide_loop -> { R.id.nav_hide_loop -> {
showLoop = false showLoop = false
rxBus.send(EventTreatmentUpdateGui()) updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_hide_records))
swapAdapter()
true true
} }
R.id.nav_export -> { R.id.nav_export -> {
exportUserEnteries() exportUserEntries()
true true
} }
else -> false else -> false
} }
} }

View file

@ -60,6 +60,11 @@ class CompatDBHelper @Inject constructor(
rxBus.send(EventExtendedBolusChange()) rxBus.send(EventExtendedBolusChange())
rxBus.send(EventNewHistoryData(timestamp, false)) rxBus.send(EventNewHistoryData(timestamp, false))
} }
it.filterIsInstance<EffectiveProfileSwitch>().firstOrNull()?.let { eps ->
aapsLogger.debug(LTag.DATABASE, "Firing EventEffectiveProfileSwitchChanged $eps")
rxBus.send(EventEffectiveProfileSwitchChanged(eps))
rxBus.send(EventNewHistoryData(eps.timestamp, false))
}
it.filterIsInstance<TemporaryTarget>().firstOrNull()?.let { tt -> it.filterIsInstance<TemporaryTarget>().firstOrNull()?.let { tt ->
aapsLogger.debug(LTag.DATABASE, "Firing EventTempTargetChange $tt") aapsLogger.debug(LTag.DATABASE, "Firing EventTempTargetChange $tt")
rxBus.send(EventTempTargetChange()) rxBus.send(EventTempTargetChange())
@ -76,10 +81,6 @@ class CompatDBHelper @Inject constructor(
aapsLogger.debug(LTag.DATABASE, "Firing EventProfileSwitchChanged $ps") aapsLogger.debug(LTag.DATABASE, "Firing EventProfileSwitchChanged $ps")
rxBus.send(EventProfileSwitchChanged()) rxBus.send(EventProfileSwitchChanged())
} }
it.filterIsInstance<EffectiveProfileSwitch>().firstOrNull()?.let { eps ->
aapsLogger.debug(LTag.DATABASE, "Firing EventEffectiveProfileSwitchChanged $eps")
rxBus.send(EventEffectiveProfileSwitchChanged(eps))
}
it.filterIsInstance<OfflineEvent>().firstOrNull()?.let { oe -> it.filterIsInstance<OfflineEvent>().firstOrNull()?.let { oe ->
aapsLogger.debug(LTag.DATABASE, "Firing EventOfflineChange $oe") aapsLogger.debug(LTag.DATABASE, "Firing EventOfflineChange $oe")
rxBus.send(EventOfflineChange()) rxBus.send(EventOfflineChange())

View file

@ -8,8 +8,6 @@ import info.nightscout.androidaps.plugins.aps.openAPSAMA.DetermineBasalResultAMA
import info.nightscout.androidaps.plugins.aps.openAPSSMB.DetermineBasalAdapterSMBJS import info.nightscout.androidaps.plugins.aps.openAPSSMB.DetermineBasalAdapterSMBJS
import info.nightscout.androidaps.plugins.aps.openAPSSMB.DetermineBasalResultSMB import info.nightscout.androidaps.plugins.aps.openAPSSMB.DetermineBasalResultSMB
import info.nightscout.androidaps.plugins.aps.openAPSSMBDynamicISF.DetermineBasalAdapterSMBDynamicISFJS import info.nightscout.androidaps.plugins.aps.openAPSSMBDynamicISF.DetermineBasalAdapterSMBDynamicISFJS
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobOref1Thread
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobThread
@Module @Module
@Suppress("unused") @Suppress("unused")
@ -21,6 +19,4 @@ abstract class APSModule {
@ContributesAndroidInjector abstract fun determineBasalAdapterAMAJSInjector(): DetermineBasalAdapterAMAJS @ContributesAndroidInjector abstract fun determineBasalAdapterAMAJSInjector(): DetermineBasalAdapterAMAJS
@ContributesAndroidInjector abstract fun determineBasalAdapterSMBJSInjector(): DetermineBasalAdapterSMBJS @ContributesAndroidInjector abstract fun determineBasalAdapterSMBJSInjector(): DetermineBasalAdapterSMBJS
@ContributesAndroidInjector abstract fun determineBasalAdapterSMBAutoISFJSInjector(): DetermineBasalAdapterSMBDynamicISFJS @ContributesAndroidInjector abstract fun determineBasalAdapterSMBAutoISFJSInjector(): DetermineBasalAdapterSMBDynamicISFJS
@ContributesAndroidInjector abstract fun iobCobThreadInjector(): IobCobThread
@ContributesAndroidInjector abstract fun iobCobOref1ThreadInjector(): IobCobOref1Thread
} }

View file

@ -46,6 +46,7 @@ import javax.inject.Singleton
OmnipodDashModule::class, OmnipodDashModule::class,
OmnipodErosModule::class, OmnipodErosModule::class,
APSModule::class, APSModule::class,
WorkflowModule::class,
PreferencesModule::class, PreferencesModule::class,
OverviewModule::class, OverviewModule::class,
DataClassesModule::class, DataClassesModule::class,

View file

@ -32,6 +32,7 @@ import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin
import info.nightscout.androidaps.plugins.general.overview.OverviewPlugin import info.nightscout.androidaps.plugins.general.overview.OverviewPlugin
import info.nightscout.androidaps.plugins.general.persistentNotification.PersistentNotificationPlugin import info.nightscout.androidaps.plugins.general.persistentNotification.PersistentNotificationPlugin
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin
import info.nightscout.androidaps.plugins.general.themes.ThemeSwitcherPlugin
import info.nightscout.androidaps.plugins.general.wear.WearPlugin import info.nightscout.androidaps.plugins.general.wear.WearPlugin
import info.nightscout.androidaps.plugins.general.xdripStatusline.StatusLinePlugin import info.nightscout.androidaps.plugins.general.xdripStatusline.StatusLinePlugin
import info.nightscout.androidaps.plugins.insulin.InsulinLyumjevPlugin import info.nightscout.androidaps.plugins.insulin.InsulinLyumjevPlugin
@ -393,6 +394,12 @@ abstract class PluginsModule {
@IntKey(490) @IntKey(490)
abstract fun bindConfigBuilderPlugin(plugin: ConfigBuilderPlugin): PluginBase abstract fun bindConfigBuilderPlugin(plugin: ConfigBuilderPlugin): PluginBase
@Binds
@AllConfigs
@IntoMap
@IntKey(500)
abstract fun bindThemeSwitcherPlugin(plugin: ThemeSwitcherPlugin): PluginBase
@Qualifier @Qualifier
annotation class AllConfigs annotation class AllConfigs

View file

@ -2,12 +2,11 @@ package info.nightscout.androidaps.di
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.plugins.aps.loop.CarbSuggestionReceiver
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkBluetoothStateReceiver import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkBluetoothStateReceiver
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkBroadcastReceiver import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkBroadcastReceiver
import info.nightscout.androidaps.plugins.aps.loop.CarbSuggestionReceiver
import info.nightscout.androidaps.receivers.* import info.nightscout.androidaps.receivers.*
@Module @Module
@Suppress("unused") @Suppress("unused")
abstract class ReceiversModule { abstract class ReceiversModule {
@ -16,8 +15,7 @@ abstract class ReceiversModule {
@ContributesAndroidInjector abstract fun contributesBTReceiver(): BTReceiver @ContributesAndroidInjector abstract fun contributesBTReceiver(): BTReceiver
@ContributesAndroidInjector abstract fun contributesChargingStateReceiver(): ChargingStateReceiver @ContributesAndroidInjector abstract fun contributesChargingStateReceiver(): ChargingStateReceiver
@ContributesAndroidInjector abstract fun contributesDataReceiver(): DataReceiver @ContributesAndroidInjector abstract fun contributesDataReceiver(): DataReceiver
@ContributesAndroidInjector abstract fun contributesKeepAliveReceiver(): KeepAliveReceiver @ContributesAndroidInjector abstract fun contributesKeepAliveWorker(): KeepAliveWorker
@ContributesAndroidInjector abstract fun contributesKeepAliveWorker(): KeepAliveReceiver.KeepAliveWorker
@ContributesAndroidInjector abstract fun contributesRileyLinkBluetoothStateReceiver(): RileyLinkBluetoothStateReceiver @ContributesAndroidInjector abstract fun contributesRileyLinkBluetoothStateReceiver(): RileyLinkBluetoothStateReceiver
@ContributesAndroidInjector abstract fun contributesSmsReceiver(): SmsReceiver @ContributesAndroidInjector abstract fun contributesSmsReceiver(): SmsReceiver
@ContributesAndroidInjector abstract fun contributesTimeDateOrTZChangeReceiver(): TimeDateOrTZChangeReceiver @ContributesAndroidInjector abstract fun contributesTimeDateOrTZChangeReceiver(): TimeDateOrTZChangeReceiver

View file

@ -2,10 +2,16 @@ package info.nightscout.androidaps.di
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.widget.WidgetConfigureActivity
import info.nightscout.androidaps.skins.SkinListPreference import info.nightscout.androidaps.skins.SkinListPreference
import info.nightscout.androidaps.widget.Widget
@Module @Module
@Suppress("unused") @Suppress("unused")
abstract class UIModule { abstract class UIModule {
@ContributesAndroidInjector abstract fun skinListPreferenceInjector(): SkinListPreference @ContributesAndroidInjector abstract fun skinListPreferenceInjector(): SkinListPreference
@ContributesAndroidInjector abstract fun aapsWidgetInjector(): Widget
@ContributesAndroidInjector abstract fun contributesWidgetConfigureActivity(): WidgetConfigureActivity
} }

View file

@ -0,0 +1,25 @@
package info.nightscout.androidaps.di
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.*
import info.nightscout.androidaps.workflow.*
@Module
@Suppress("unused")
abstract class WorkflowModule {
@ContributesAndroidInjector abstract fun iobCobWorkerInjector(): IobCobOrefWorker
@ContributesAndroidInjector abstract fun iobCobOref1WorkerInjector(): IobCobOref1Worker
@ContributesAndroidInjector abstract fun prepareIobAutosensDataWorkerInjector(): PrepareIobAutosensGraphDataWorker
@ContributesAndroidInjector abstract fun prepareBasalDataWorkerInjector(): PrepareBasalDataWorker
@ContributesAndroidInjector abstract fun prepareTemporaryTargetDataWorkerInjector(): PrepareTemporaryTargetDataWorker
@ContributesAndroidInjector abstract fun prepareTreatmentsDataWorkerInjector(): PrepareTreatmentsDataWorker
@ContributesAndroidInjector abstract fun loadIobCobResultsWorkerInjector(): UpdateIobCobSensWorker
@ContributesAndroidInjector abstract fun preparePredictionsWorkerInjector(): PreparePredictionsWorker
@ContributesAndroidInjector abstract fun updateGraphAndIobWorkerInjector(): UpdateGraphWorker
@ContributesAndroidInjector abstract fun prepareBgDataWorkerInjector(): PrepareBgDataWorker
@ContributesAndroidInjector abstract fun prepareBucketedDataWorkerInjector(): PrepareBucketedDataWorker
@ContributesAndroidInjector abstract fun loadBgDataWorkerInjector(): LoadBgDataWorker
@ContributesAndroidInjector abstract fun invokeLoopWorkerInjector(): InvokeLoopWorker
}

View file

@ -64,7 +64,7 @@ class CalibrationDialog : DialogFragmentWithDate() {
binding.bg.setParams(savedInstanceState?.getDouble("bg") binding.bg.setParams(savedInstanceState?.getDouble("bg")
?: bg, 36.0, 500.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok) ?: bg, 36.0, 500.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok)
binding.units.text = if (units == GlucoseUnit.MMOL) rh.gs(R.string.mmol) else rh.gs(R.string.mgdl) binding.units.text = if (units == GlucoseUnit.MMOL) rh.gs(R.string.mmol) else rh.gs(R.string.mgdl)
binding.bg.editText?.id?.let { binding.bgLabel.labelFor = it } binding.bgLabel.labelFor = binding.bg.editTextId
} }
override fun onDestroyView() { override fun onDestroyView() {

View file

@ -27,6 +27,8 @@ import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProv
import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.* import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.protection.ProtectionCheck.Protection.BOLUS
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
@ -50,6 +52,7 @@ class CarbsDialog : DialogFragmentWithDate() {
@Inject lateinit var bolusTimer: BolusTimer @Inject lateinit var bolusTimer: BolusTimer
@Inject lateinit var commandQueue: CommandQueue @Inject lateinit var commandQueue: CommandQueue
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
@Inject lateinit var protectionCheck: ProtectionCheck
companion object { companion object {
@ -58,6 +61,7 @@ class CarbsDialog : DialogFragmentWithDate() {
private const val FAV3_DEFAULT = 20 private const val FAV3_DEFAULT = 20
} }
private var queryingProtection = false
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
private val textWatcher: TextWatcher = object : TextWatcher { private val textWatcher: TextWatcher = object : TextWatcher {
@ -195,9 +199,9 @@ class CarbsDialog : DialogFragmentWithDate() {
binding.hypoTt.isChecked = false binding.hypoTt.isChecked = false
binding.activityTt.isChecked = false binding.activityTt.isChecked = false
} }
binding.duration.editText?.id?.let { binding.durationLabel.labelFor = it } binding.durationLabel.labelFor = binding.duration.editTextId
binding.time.editText?.id?.let { binding.timeLabel.labelFor = it } binding.timeLabel.labelFor = binding.time.editTextId
binding.carbs.editText?.id?.let { binding.carbsLabel.labelFor = it } binding.carbsLabel.labelFor = binding.carbs.editTextId
} }
override fun onDestroyView() { override fun onDestroyView() {
@ -230,8 +234,9 @@ class CarbsDialog : DialogFragmentWithDate() {
if (activitySelected) if (activitySelected)
actions.add( actions.add(
rh.gs(R.string.temptargetshort) + ": " + (DecimalFormatter.to1Decimal(activityTT) + " " + unitLabel + " (" + rh.gs(R.string.format_mins, activityTTDuration) + ")").formatColor( rh.gs(R.string.temptargetshort) + ": " + (DecimalFormatter.to1Decimal(activityTT) + " " + unitLabel + " (" + rh.gs(R.string.format_mins, activityTTDuration) + ")").formatColor(
context,
rh, rh,
R.color.tempTargetConfirmation R.attr.tempTargetConfirmation
) )
) )
val eatingSoonSelected = binding.eatingSoonTt.isChecked val eatingSoonSelected = binding.eatingSoonTt.isChecked
@ -240,27 +245,27 @@ class CarbsDialog : DialogFragmentWithDate() {
rh.gs(R.string.temptargetshort) + ": " + (DecimalFormatter.to1Decimal(eatingSoonTT) + " " + unitLabel + " (" + rh.gs( rh.gs(R.string.temptargetshort) + ": " + (DecimalFormatter.to1Decimal(eatingSoonTT) + " " + unitLabel + " (" + rh.gs(
R.string.format_mins, R.string.format_mins,
eatingSoonTTDuration eatingSoonTTDuration
) + ")").formatColor(rh, R.color.tempTargetConfirmation) ) + ")").formatColor(context, rh, R.attr.tempTargetConfirmation)
) )
val hypoSelected = binding.hypoTt.isChecked val hypoSelected = binding.hypoTt.isChecked
if (hypoSelected) if (hypoSelected)
actions.add( actions.add(
rh.gs(R.string.temptargetshort) + ": " + (DecimalFormatter.to1Decimal(hypoTT) + " " + unitLabel + " (" + rh.gs(R.string.format_mins, hypoTTDuration) + ")").formatColor( rh.gs(R.string.temptargetshort) + ": " + (DecimalFormatter.to1Decimal(hypoTT) + " " + unitLabel + " (" + rh.gs(R.string.format_mins, hypoTTDuration) + ")").formatColor( context,
rh, rh,
R.color.tempTargetConfirmation R.attr.tempTargetConfirmation
) )
) )
val timeOffset = binding.time.value.toInt() val timeOffset = binding.time.value.toInt()
if (useAlarm && carbs > 0 && timeOffset > 0) if (useAlarm && carbs > 0 && timeOffset > 0)
actions.add(rh.gs(R.string.alarminxmin, timeOffset).formatColor(rh, R.color.info)) actions.add(rh.gs(R.string.alarminxmin, timeOffset).formatColor(context , rh, R.attr.infoColor))
val duration = binding.duration.value.toInt() val duration = binding.duration.value.toInt()
if (duration > 0) if (duration > 0)
actions.add(rh.gs(R.string.duration) + ": " + duration + rh.gs(R.string.shorthour)) actions.add(rh.gs(R.string.duration) + ": " + duration + rh.gs(R.string.shorthour))
if (carbsAfterConstraints > 0) { if (carbsAfterConstraints > 0) {
actions.add(rh.gs(R.string.carbs) + ": " + "<font color='" + rh.gc(R.color.carbs) + "'>" + rh.gs(R.string.format_carbs, carbsAfterConstraints) + "</font>") actions.add(rh.gs(R.string.carbs) + ": " + "<font color='" + rh.gac(context, R.attr.carbsColor) + "'>" + rh.gs(R.string.format_carbs, carbsAfterConstraints) + "</font>")
if (carbsAfterConstraints != carbs) if (carbsAfterConstraints != carbs)
actions.add("<font color='" + rh.gc(R.color.warning) + "'>" + rh.gs(R.string.carbsconstraintapplied) + "</font>") actions.add("<font color='" + rh.gac(context, R.attr.warningColor) + "'>" + rh.gs(R.string.carbsconstraintapplied) + "</font>")
} }
val notes = binding.notesLayout.notes.text.toString() val notes = binding.notesLayout.notes.text.toString()
if (notes.isNotEmpty()) if (notes.isNotEmpty())
@ -377,4 +382,20 @@ class CarbsDialog : DialogFragmentWithDate() {
} }
return true return true
} }
}
override fun onResume() {
super.onResume()
if(!queryingProtection) {
queryingProtection = true
activity?.let { activity ->
val cancelFail = {
queryingProtection = false
aapsLogger.debug(LTag.APS, "Dialog canceled on resume protection: ${this.javaClass.name}")
ToastUtils.showToastInUiThread(ctx, R.string.dialog_canceled)
dismiss()
}
protectionCheck.queryProtection(activity, BOLUS, { queryingProtection = false }, cancelFail, cancelFail)
}
}
}
}

View file

@ -165,8 +165,8 @@ class CareDialog : DialogFragmentWithDate() {
?: 0.0, 0.0, Constants.MAX_PROFILE_SWITCH_DURATION, 10.0, DecimalFormat("0"), false, binding.okcancel.ok) ?: 0.0, 0.0, Constants.MAX_PROFILE_SWITCH_DURATION, 10.0, DecimalFormat("0"), false, binding.okcancel.ok)
if (options == EventType.NOTE || options == EventType.QUESTION || options == EventType.ANNOUNCEMENT || options == EventType.EXERCISE) if (options == EventType.NOTE || options == EventType.QUESTION || options == EventType.ANNOUNCEMENT || options == EventType.EXERCISE)
binding.notesLayout.root.visibility = View.VISIBLE // independent to preferences binding.notesLayout.root.visibility = View.VISIBLE // independent to preferences
binding.bg.editText?.id?.let { binding.bgLabel.labelFor = it } binding.bgLabel.labelFor = binding.bg.editTextId
binding.duration.editText?.id?.let { binding.durationLabel.labelFor = it } binding.durationLabel.labelFor = binding.duration.editTextId
} }
override fun onDestroyView() { override fun onDestroyView() {

View file

@ -22,7 +22,11 @@ import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.shared.SafeParse import info.nightscout.shared.SafeParse
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.extensions.formatColor import info.nightscout.androidaps.extensions.formatColor
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.protection.ProtectionCheck.Protection.BOLUS
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.shared.logging.LTag
import java.text.DecimalFormat import java.text.DecimalFormat
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
@ -36,11 +40,12 @@ class ExtendedBolusDialog : DialogFragmentWithDate() {
@Inject lateinit var commandQueue: CommandQueue @Inject lateinit var commandQueue: CommandQueue
@Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var uel: UserEntryLogger @Inject lateinit var uel: UserEntryLogger
@Inject lateinit var protectionCheck: ProtectionCheck
private var queryingProtection = false
private var _binding: DialogExtendedbolusBinding? = null private var _binding: DialogExtendedbolusBinding? = null
// This property is only valid between onCreateView and // This property is only valid between onCreateView and onDestroyView.
// onDestroyView.
private val binding get() = _binding!! private val binding get() = _binding!!
override fun onSaveInstanceState(savedInstanceState: Bundle) { override fun onSaveInstanceState(savedInstanceState: Bundle) {
@ -70,8 +75,8 @@ class ExtendedBolusDialog : DialogFragmentWithDate() {
val extendedMaxDuration = pumpDescription.extendedBolusMaxDuration val extendedMaxDuration = pumpDescription.extendedBolusMaxDuration
binding.duration.setParams(savedInstanceState?.getDouble("duration") binding.duration.setParams(savedInstanceState?.getDouble("duration")
?: extendedDurationStep, extendedDurationStep, extendedMaxDuration, extendedDurationStep, DecimalFormat("0"), false, binding.okcancel.ok) ?: extendedDurationStep, extendedDurationStep, extendedMaxDuration, extendedDurationStep, DecimalFormat("0"), false, binding.okcancel.ok)
binding.insulin.editText?.id?.let { binding.insulinLabel.labelFor = it } binding.insulinLabel.labelFor = binding.insulin.editTextId
binding.duration.editText?.id?.let { binding.durationLabel.labelFor = it } binding.durationLabel.labelFor = binding.duration.editTextId
} }
override fun onDestroyView() { override fun onDestroyView() {
@ -88,7 +93,7 @@ class ExtendedBolusDialog : DialogFragmentWithDate() {
actions.add(rh.gs(R.string.formatinsulinunits, insulinAfterConstraint)) actions.add(rh.gs(R.string.formatinsulinunits, insulinAfterConstraint))
actions.add(rh.gs(R.string.duration) + ": " + rh.gs(R.string.format_mins, durationInMinutes)) actions.add(rh.gs(R.string.duration) + ": " + rh.gs(R.string.format_mins, durationInMinutes))
if (abs(insulinAfterConstraint - insulin) > 0.01) if (abs(insulinAfterConstraint - insulin) > 0.01)
actions.add(rh.gs(R.string.constraintapllied).formatColor(rh, R.color.warning)) actions.add(rh.gs(R.string.constraintapllied).formatColor(context, rh, R.attr.warningColor))
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.extended_bolus), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), { OKDialog.showConfirmation(activity, rh.gs(R.string.extended_bolus), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
@ -106,4 +111,20 @@ class ExtendedBolusDialog : DialogFragmentWithDate() {
} }
return true return true
} }
}
override fun onResume() {
super.onResume()
if(!queryingProtection) {
queryingProtection = true
activity?.let { activity ->
val cancelFail = {
queryingProtection = false
aapsLogger.debug(LTag.APS, "Dialog canceled on resume protection: ${this.javaClass.name}")
ToastUtils.showToastInUiThread(ctx, R.string.dialog_canceled)
dismiss()
}
protectionCheck.queryProtection(activity, BOLUS, { queryingProtection = false }, cancelFail, cancelFail)
}
}
}
}

View file

@ -28,6 +28,9 @@ import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.shared.SafeParse import info.nightscout.shared.SafeParse
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.extensions.formatColor import info.nightscout.androidaps.extensions.formatColor
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.protection.ProtectionCheck.Protection.BOLUS
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
@ -44,13 +47,13 @@ class FillDialog : DialogFragmentWithDate() {
@Inject lateinit var activePlugin: ActivePlugin @Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var uel: UserEntryLogger @Inject lateinit var uel: UserEntryLogger
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
@Inject lateinit var protectionCheck: ProtectionCheck
private var queryingProtection = false
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
private var _binding: DialogFillBinding? = null private var _binding: DialogFillBinding? = null
// This property is only valid between onCreateView and // This property is only valid between onCreateView and onDestroyView.
// onDestroyView.
private val binding get() = _binding!! private val binding get() = _binding!!
override fun onSaveInstanceState(savedInstanceState: Bundle) { override fun onSaveInstanceState(savedInstanceState: Bundle) {
@ -96,7 +99,7 @@ class FillDialog : DialogFragmentWithDate() {
} else { } else {
binding.fillPresetButton3.visibility = View.GONE binding.fillPresetButton3.visibility = View.GONE
} }
binding.fillInsulinamount.editText?.id?.let { binding.fillLabel.labelFor = it } binding.fillLabel.labelFor = binding.fillInsulinamount.editTextId
} }
override fun onDestroyView() { override fun onDestroyView() {
@ -113,16 +116,16 @@ class FillDialog : DialogFragmentWithDate() {
if (insulinAfterConstraints > 0) { if (insulinAfterConstraints > 0) {
actions.add(rh.gs(R.string.fillwarning)) actions.add(rh.gs(R.string.fillwarning))
actions.add("") actions.add("")
actions.add(rh.gs(R.string.bolus) + ": " + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints, activePlugin.activePump, rh).formatColor(rh, R.color.colorInsulinButton)) actions.add(rh.gs(R.string.bolus) + ": " + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints, activePlugin.activePump, rh).formatColor(context, rh, R.attr.insulinButtonColor))
if (abs(insulinAfterConstraints - insulin) > 0.01) if (abs(insulinAfterConstraints - insulin) > 0.01)
actions.add(rh.gs(R.string.bolusconstraintappliedwarn, insulin, insulinAfterConstraints).formatColor(rh, R.color.warning)) actions.add(rh.gs(R.string.bolusconstraintappliedwarn, insulin, insulinAfterConstraints).formatColor(context, rh, R.attr.warningColor))
} }
val siteChange = binding.fillCatheterChange.isChecked val siteChange = binding.fillCatheterChange.isChecked
if (siteChange) if (siteChange)
actions.add(rh.gs(R.string.record_pump_site_change).formatColor(rh, R.color.actionsConfirm)) actions.add(rh.gs(R.string.record_pump_site_change).formatColor(context, rh, R.attr.actionsConfirmColor))
val insulinChange = binding.fillCartridgeChange.isChecked val insulinChange = binding.fillCartridgeChange.isChecked
if (insulinChange) if (insulinChange)
actions.add(rh.gs(R.string.record_insulin_cartridge_change).formatColor(rh, R.color.actionsConfirm)) actions.add(rh.gs(R.string.record_insulin_cartridge_change).formatColor(context, rh, R.attr.actionsConfirmColor))
val notes: String = binding.notesLayout.notes.text.toString() val notes: String = binding.notesLayout.notes.text.toString()
if (notes.isNotEmpty()) if (notes.isNotEmpty())
actions.add(rh.gs(R.string.notes_label) + ": " + notes) actions.add(rh.gs(R.string.notes_label) + ": " + notes)
@ -196,4 +199,20 @@ class FillDialog : DialogFragmentWithDate() {
} }
}) })
} }
override fun onResume() {
super.onResume()
if(!queryingProtection) {
queryingProtection = true
activity?.let { activity ->
val cancelFail = {
queryingProtection = false
aapsLogger.debug(LTag.APS, "Dialog canceled on resume protection: ${this.javaClass.name}")
ToastUtils.showToastInUiThread(ctx, R.string.dialog_canceled)
dismiss()
}
protectionCheck.queryProtection(activity, BOLUS, { queryingProtection = false }, cancelFail, cancelFail)
}
}
}
} }

View file

@ -28,6 +28,8 @@ import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.* import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.toSignedString import info.nightscout.androidaps.utils.extensions.toSignedString
import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.protection.ProtectionCheck.Protection.BOLUS
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.shared.SafeParse import info.nightscout.shared.SafeParse
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
@ -52,6 +54,7 @@ class InsulinDialog : DialogFragmentWithDate() {
@Inject lateinit var config: Config @Inject lateinit var config: Config
@Inject lateinit var bolusTimer: BolusTimer @Inject lateinit var bolusTimer: BolusTimer
@Inject lateinit var uel: UserEntryLogger @Inject lateinit var uel: UserEntryLogger
@Inject lateinit var protectionCheck: ProtectionCheck
companion object { companion object {
@ -60,7 +63,12 @@ class InsulinDialog : DialogFragmentWithDate() {
private const val PLUS3_DEFAULT = 2.0 private const val PLUS3_DEFAULT = 2.0
} }
private var queryingProtection = false
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
private var _binding: DialogInsulinBinding? = null
// This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!!
private val textWatcher: TextWatcher = object : TextWatcher { private val textWatcher: TextWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) { override fun afterTextChanged(s: Editable) {
@ -71,12 +79,6 @@ class InsulinDialog : DialogFragmentWithDate() {
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {} override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
} }
private var _binding: DialogInsulinBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
private fun validateInputs() { private fun validateInputs() {
val maxInsulin = constraintChecker.getMaxBolusAllowed().value() val maxInsulin = constraintChecker.getMaxBolusAllowed().value()
if (abs(binding.time.value.toInt()) > 12 * 60) { if (abs(binding.time.value.toInt()) > 12 * 60) {
@ -148,8 +150,8 @@ class InsulinDialog : DialogFragmentWithDate() {
binding.recordOnly.setOnCheckedChangeListener { _, isChecked: Boolean -> binding.recordOnly.setOnCheckedChangeListener { _, isChecked: Boolean ->
binding.timeLayout.visibility = isChecked.toVisibility() binding.timeLayout.visibility = isChecked.toVisibility()
} }
binding.amount.editText?.id?.let { binding.insulinLabel.labelFor = it } binding.insulinLabel.labelFor = binding.amount.editTextId
binding.time.editText?.id?.let { binding.timeLabel.labelFor = it } binding.timeLabel.labelFor = binding.time.editTextId
} }
override fun onDestroyView() { override fun onDestroyView() {
@ -170,16 +172,17 @@ class InsulinDialog : DialogFragmentWithDate() {
val eatingSoonChecked = binding.startEatingSoonTt.isChecked val eatingSoonChecked = binding.startEatingSoonTt.isChecked
if (insulinAfterConstraints > 0) { if (insulinAfterConstraints > 0) {
actions.add(rh.gs(R.string.bolus) + ": " + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints, activePlugin.activePump, rh).formatColor(rh, R.color.bolus)) actions.add(rh.gs(R.string.bolus) + ": " + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints, activePlugin.activePump, rh).formatColor(context, rh, R.attr.bolusColor))
if (recordOnlyChecked) if (recordOnlyChecked)
actions.add(rh.gs(R.string.bolusrecordedonly).formatColor(rh, R.color.warning)) actions.add(rh.gs(R.string.bolusrecordedonly).formatColor(context, rh, R.attr.warningColor))
if (abs(insulinAfterConstraints - insulin) > pumpDescription.pumpType.determineCorrectBolusStepSize(insulinAfterConstraints)) if (abs(insulinAfterConstraints - insulin) > pumpDescription.pumpType.determineCorrectBolusStepSize(insulinAfterConstraints))
actions.add(rh.gs(R.string.bolusconstraintappliedwarn, insulin, insulinAfterConstraints).formatColor(rh, R.color.warning)) actions.add(rh.gs(R.string.bolusconstraintappliedwarn, insulin, insulinAfterConstraints).formatColor(context, rh, R.attr.warningColor))
} }
val eatingSoonTTDuration = defaultValueHelper.determineEatingSoonTTDuration() val eatingSoonTTDuration = defaultValueHelper.determineEatingSoonTTDuration()
val eatingSoonTT = defaultValueHelper.determineEatingSoonTT() val eatingSoonTT = defaultValueHelper.determineEatingSoonTT()
if (eatingSoonChecked) if (eatingSoonChecked)
actions.add(rh.gs(R.string.temptargetshort) + ": " + (DecimalFormatter.to1Decimal(eatingSoonTT) + " " + unitLabel + " (" + rh.gs(R.string.format_mins, eatingSoonTTDuration) + ")").formatColor(rh, R.color.tempTargetConfirmation)) actions.add(rh.gs(R.string.temptargetshort) + ": " + (DecimalFormatter.to1Decimal(eatingSoonTT) + " " + unitLabel + " (" + rh.gs(R.string.format_mins, eatingSoonTTDuration) + ")")
.formatColor(context, rh, R.attr.tempTargetConfirmation))
val timeOffset = binding.time.value.toInt() val timeOffset = binding.time.value.toInt()
val time = dateUtil.now() + T.mins(timeOffset.toLong()).msecs() val time = dateUtil.now() + T.mins(timeOffset.toLong()).msecs()
@ -255,4 +258,20 @@ class InsulinDialog : DialogFragmentWithDate() {
} }
return true return true
} }
}
override fun onResume() {
super.onResume()
if(!queryingProtection) {
queryingProtection = true
activity?.let { activity ->
val cancelFail = {
queryingProtection = false
aapsLogger.debug(LTag.APS, "Dialog canceled on resume protection: ${this.javaClass.name}")
ToastUtils.showToastInUiThread(ctx, R.string.dialog_canceled)
dismiss()
}
protectionCheck.queryProtection(activity, BOLUS, { queryingProtection = false }, cancelFail, cancelFail)
}
}
}
}

View file

@ -38,6 +38,8 @@ import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.protection.ProtectionCheck.Protection.BOLUS
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
@ -62,14 +64,15 @@ class LoopDialog : DaggerDialogFragment() {
@Inject lateinit var dateUtil: DateUtil @Inject lateinit var dateUtil: DateUtil
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
@Inject lateinit var objectivePlugin: ObjectivesPlugin @Inject lateinit var objectivePlugin: ObjectivesPlugin
@Inject lateinit var protectionCheck: ProtectionCheck
private var queryingProtection = false
private var showOkCancel: Boolean = true private var showOkCancel: Boolean = true
private var _binding: DialogLoopBinding? = null private var _binding: DialogLoopBinding? = null
private var handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper) private var handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper)
private lateinit var refreshDialog: Runnable private lateinit var refreshDialog: Runnable
// This property is only valid between onCreateView and // This property is only valid between onCreateView and onDestroyView.
// onDestroyView.
private val binding get() = _binding!! private val binding get() = _binding!!
val disposable = CompositeDisposable() val disposable = CompositeDisposable()
@ -437,4 +440,20 @@ class LoopDialog : DaggerDialogFragment() {
aapsLogger.debug(e.localizedMessage ?: e.toString()) aapsLogger.debug(e.localizedMessage ?: e.toString())
} }
} }
override fun onResume() {
super.onResume()
if(!queryingProtection) {
queryingProtection = true
activity?.let { activity ->
val cancelFail = {
queryingProtection = false
aapsLogger.debug(LTag.APS, "Dialog canceled on resume protection: ${this.javaClass.name}")
ToastUtils.showToastInUiThread(ctx, R.string.dialog_canceled)
dismiss()
}
protectionCheck.queryProtection(activity, BOLUS, { queryingProtection = false }, cancelFail, cancelFail)
}
}
}
} }

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.dialogs package info.nightscout.androidaps.dialogs
import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
@ -30,7 +31,10 @@ import info.nightscout.androidaps.utils.DefaultValueHelper
import info.nightscout.androidaps.utils.HardLimits import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.protection.ProtectionCheck.Protection.BOLUS
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
@ -51,15 +55,15 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
@Inject lateinit var hardLimits: HardLimits @Inject lateinit var hardLimits: HardLimits
@Inject lateinit var rxBus: RxBus @Inject lateinit var rxBus: RxBus
@Inject lateinit var defaultValueHelper: DefaultValueHelper @Inject lateinit var defaultValueHelper: DefaultValueHelper
@Inject lateinit var ctx: Context
@Inject lateinit var protectionCheck: ProtectionCheck
private var profileIndex: Int? = null private var queryingProtection = false
private var profileName: String? = null
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
private var _binding: DialogProfileswitchBinding? = null private var _binding: DialogProfileswitchBinding? = null
// This property is only valid between onCreateView and // This property is only valid between onCreateView and onDestroyView.
// onDestroyView.
private val binding get() = _binding!! private val binding get() = _binding!!
private val textWatcher: TextWatcher = object : TextWatcher { private val textWatcher: TextWatcher = object : TextWatcher {
@ -86,7 +90,7 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
): View { ): View {
onCreateViewGeneral() onCreateViewGeneral()
arguments?.let { bundle -> arguments?.let { bundle ->
profileIndex = bundle.getInt("profileIndex", 0) profileName = bundle.getString("profileName", null)
} }
_binding = DialogProfileswitchBinding.inflate(inflater, container, false) _binding = DialogProfileswitchBinding.inflate(inflater, container, false)
return binding.root return binding.root
@ -112,8 +116,7 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
// profile // profile
context?.let { context -> context?.let { context ->
val profileStore = activePlugin.activeProfileSource.profile val profileStore = activePlugin.activeProfileSource.profile ?: return
?: return
val profileListToCheck = profileStore.getProfileList() val profileListToCheck = profileStore.getProfileList()
val profileList = ArrayList<CharSequence>() val profileList = ArrayList<CharSequence>()
for (profileName in profileListToCheck) { for (profileName in profileListToCheck) {
@ -125,15 +128,16 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
dismiss() dismiss()
return return
} }
val adapter = ArrayAdapter(context, R.layout.spinner_centered, profileList) binding.profileList.setAdapter(ArrayAdapter(context, R.layout.spinner_centered, profileList))
binding.profile.adapter = adapter
// set selected to actual profile // set selected to actual profile
if (profileIndex != null) if (profileName != null)
binding.profile.setSelection(profileIndex as Int) binding.profileList.setText(profileName, false)
else else {
binding.profileList.setText(profileList[0], false)
for (p in profileList.indices) for (p in profileList.indices)
if (profileList[p] == profileFunction.getOriginalProfileName()) if (profileList[p] == profileFunction.getOriginalProfileName())
binding.profile.setSelection(p) binding.profileList.setText(profileList[p], false)
}
} }
profileFunction.getProfile()?.let { profile -> profileFunction.getProfile()?.let { profile ->
@ -148,9 +152,9 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
} }
} }
binding.ttLayout.visibility = View.GONE binding.ttLayout.visibility = View.GONE
binding.duration.editText?.id?.let { binding.durationLabel.labelFor = it } binding.durationLabel.labelFor = binding.duration.editTextId
binding.percentage.editText?.id?.let { binding.percentageLabel.labelFor = it } binding.percentageLabel.labelFor = binding.percentage.editTextId
binding.timeshift.editText?.id?.let { binding.timeshiftLabel.labelFor = it } binding.timeshiftLabel.labelFor = binding.timeshift.editTextId
} }
override fun onDestroyView() { override fun onDestroyView() {
@ -168,7 +172,7 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
val duration = binding.duration.value.toInt() val duration = binding.duration.value.toInt()
if (duration > 0L) if (duration > 0L)
actions.add(rh.gs(R.string.duration) + ": " + rh.gs(R.string.format_mins, duration)) actions.add(rh.gs(R.string.duration) + ": " + rh.gs(R.string.format_mins, duration))
val profileName = binding.profile.selectedItem.toString() val profileName = binding.profileList.text.toString()
actions.add(rh.gs(R.string.profile) + ": " + profileName) actions.add(rh.gs(R.string.profile) + ": " + profileName)
val percent = binding.percentage.value.toInt() val percent = binding.percentage.value.toInt()
if (percent != 100) if (percent != 100)
@ -245,4 +249,20 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
} }
return true return true
} }
override fun onResume() {
super.onResume()
if(!queryingProtection) {
queryingProtection = true
activity?.let { activity ->
val cancelFail = {
queryingProtection = false
aapsLogger.debug(LTag.APS, "Dialog canceled on resume protection: ${this.javaClass.name}")
ToastUtils.showToastInUiThread(ctx, R.string.dialog_canceled)
dismiss()
}
protectionCheck.queryProtection(activity, BOLUS, { queryingProtection = false }, cancelFail, cancelFail)
}
}
}
} }

View file

@ -20,7 +20,11 @@ import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.shared.SafeParse import info.nightscout.shared.SafeParse
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.extensions.formatColor import info.nightscout.androidaps.extensions.formatColor
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.protection.ProtectionCheck.Protection.BOLUS
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.shared.logging.LTag
import java.text.DecimalFormat import java.text.DecimalFormat
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
@ -35,13 +39,13 @@ class TempBasalDialog : DialogFragmentWithDate() {
@Inject lateinit var commandQueue: CommandQueue @Inject lateinit var commandQueue: CommandQueue
@Inject lateinit var ctx: Context @Inject lateinit var ctx: Context
@Inject lateinit var uel: UserEntryLogger @Inject lateinit var uel: UserEntryLogger
@Inject lateinit var protectionCheck: ProtectionCheck
private var queryingProtection = false
private var isPercentPump = true private var isPercentPump = true
private var _binding: DialogTempbasalBinding? = null private var _binding: DialogTempbasalBinding? = null
// This property is only valid between onCreateView and // This property is only valid between onCreateView and onDestroyView.
// onDestroyView.
private val binding get() = _binding!! private val binding get() = _binding!!
override fun onSaveInstanceState(savedInstanceState: Bundle) { override fun onSaveInstanceState(savedInstanceState: Bundle) {
@ -51,8 +55,7 @@ class TempBasalDialog : DialogFragmentWithDate() {
savedInstanceState.putDouble("basalAbsoluteInput", binding.basalAbsoluteInput.value) savedInstanceState.putDouble("basalAbsoluteInput", binding.basalAbsoluteInput.value)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
savedInstanceState: Bundle?): View {
onCreateViewGeneral() onCreateViewGeneral()
_binding = DialogTempbasalBinding.inflate(inflater, container, false) _binding = DialogTempbasalBinding.inflate(inflater, container, false)
return binding.root return binding.root
@ -86,9 +89,9 @@ class TempBasalDialog : DialogFragmentWithDate() {
binding.percentLayout.visibility = View.GONE binding.percentLayout.visibility = View.GONE
binding.absoluteLayout.visibility = View.VISIBLE binding.absoluteLayout.visibility = View.VISIBLE
} }
binding.basalPercentInput.editText?.id?.let { binding.basalPercentLabel.labelFor = it } binding.basalPercentLabel.labelFor = binding.basalPercentInput.editTextId
binding.basalAbsoluteInput.editText?.id?.let { binding.basalAbsoluteLabel.labelFor = it } binding.basalAbsoluteLabel.labelFor = binding.basalAbsoluteInput.editTextId
binding.duration.editText?.id?.let { binding.durationLabel.labelFor = it } binding.durationLabel.labelFor = binding.duration.editTextId
} }
override fun onDestroyView() { override fun onDestroyView() {
@ -115,7 +118,7 @@ class TempBasalDialog : DialogFragmentWithDate() {
actions.add(rh.gs(R.string.tempbasal_label) + ": " + rh.gs(R.string.pump_basebasalrate, absolute)) actions.add(rh.gs(R.string.tempbasal_label) + ": " + rh.gs(R.string.pump_basebasalrate, absolute))
actions.add(rh.gs(R.string.duration) + ": " + rh.gs(R.string.format_mins, durationInMinutes)) actions.add(rh.gs(R.string.duration) + ": " + rh.gs(R.string.format_mins, durationInMinutes))
if (abs(absolute - basalAbsoluteInput) > 0.01) if (abs(absolute - basalAbsoluteInput) > 0.01)
actions.add(rh.gs(R.string.constraintapllied).formatColor(rh, R.color.warning)) actions.add(rh.gs(R.string.constraintapllied).formatColor(context, rh, R.attr.warningColor))
} }
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.tempbasal_label), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), { OKDialog.showConfirmation(activity, rh.gs(R.string.tempbasal_label), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
@ -141,4 +144,20 @@ class TempBasalDialog : DialogFragmentWithDate() {
} }
return true return true
} }
}
override fun onResume() {
super.onResume()
if(!queryingProtection) {
queryingProtection = true
activity?.let { activity ->
val cancelFail = {
queryingProtection = false
aapsLogger.debug(LTag.APS, "Dialog canceled on resume protection: ${this.javaClass.name}")
ToastUtils.showToastInUiThread(ctx, R.string.dialog_canceled)
dismiss()
}
protectionCheck.queryProtection(activity, BOLUS, { queryingProtection = false }, cancelFail, cancelFail)
}
}
}
}

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.dialogs package info.nightscout.androidaps.dialogs
import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@ -26,7 +27,10 @@ import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.utils.DefaultValueHelper import info.nightscout.androidaps.utils.DefaultValueHelper
import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.protection.ProtectionCheck.Protection.BOLUS
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
@ -43,15 +47,16 @@ class TempTargetDialog : DialogFragmentWithDate() {
@Inject lateinit var defaultValueHelper: DefaultValueHelper @Inject lateinit var defaultValueHelper: DefaultValueHelper
@Inject lateinit var uel: UserEntryLogger @Inject lateinit var uel: UserEntryLogger
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
@Inject lateinit var ctx: Context
@Inject lateinit var protectionCheck: ProtectionCheck
private lateinit var reasonList: List<String> private lateinit var reasonList: List<String>
private var queryingProtection = false
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
private var _binding: DialogTemptargetBinding? = null private var _binding: DialogTemptargetBinding? = null
// This property is only valid between onCreateView and // This property is only valid between onCreateView and onDestroyView.
// onDestroyView.
private val binding get() = _binding!! private val binding get() = _binding!!
override fun onSaveInstanceState(savedInstanceState: Bundle) { override fun onSaveInstanceState(savedInstanceState: Bundle) {
@ -60,8 +65,7 @@ class TempTargetDialog : DialogFragmentWithDate() {
savedInstanceState.putDouble("tempTarget", binding.temptarget.value) savedInstanceState.putDouble("tempTarget", binding.temptarget.value)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
savedInstanceState: Bundle?): View {
onCreateViewGeneral() onCreateViewGeneral()
_binding = DialogTemptargetBinding.inflate(inflater, container, false) _binding = DialogTemptargetBinding.inflate(inflater, container, false)
return binding.root return binding.root
@ -100,10 +104,9 @@ class TempTargetDialog : DialogFragmentWithDate() {
rh.gs(R.string.activity), rh.gs(R.string.activity),
rh.gs(R.string.hypo) rh.gs(R.string.hypo)
) )
val adapterReason = ArrayAdapter(context, R.layout.spinner_centered, reasonList) binding.reasonList.setAdapter(ArrayAdapter(context, R.layout.spinner_centered, reasonList))
binding.reason.adapter = adapterReason
binding.targetCancel.setOnClickListener { shortClick(it) } binding.targetCancel.setOnClickListener { binding.duration.value = 0.0; shortClick(it) }
binding.eatingSoon.setOnClickListener { shortClick(it) } binding.eatingSoon.setOnClickListener { shortClick(it) }
binding.activity.setOnClickListener { shortClick(it) } binding.activity.setOnClickListener { shortClick(it) }
binding.hypo.setOnClickListener { shortClick(it) } binding.hypo.setOnClickListener { shortClick(it) }
@ -120,8 +123,8 @@ class TempTargetDialog : DialogFragmentWithDate() {
longClick(it) longClick(it)
return@setOnLongClickListener true return@setOnLongClickListener true
} }
binding.duration.editText?.id?.let { binding.durationLabel.labelFor = it } binding.durationLabel.labelFor = binding.duration.editTextId
binding.temptarget.editText?.id?.let { binding.temptargetLabel.labelFor = it } binding.temptargetLabel.labelFor = binding.temptarget.editTextId
} }
} }
@ -135,23 +138,19 @@ class TempTargetDialog : DialogFragmentWithDate() {
R.id.eating_soon -> { R.id.eating_soon -> {
binding.temptarget.value = defaultValueHelper.determineEatingSoonTT() binding.temptarget.value = defaultValueHelper.determineEatingSoonTT()
binding.duration.value = defaultValueHelper.determineEatingSoonTTDuration().toDouble() binding.duration.value = defaultValueHelper.determineEatingSoonTTDuration().toDouble()
binding.reason.setSelection(reasonList.indexOf(rh.gs(R.string.eatingsoon))) binding.reasonList.setText(rh.gs(R.string.eatingsoon), false)
} }
R.id.activity -> { R.id.activity -> {
binding.temptarget.value = defaultValueHelper.determineActivityTT() binding.temptarget.value = defaultValueHelper.determineActivityTT()
binding.duration.value = defaultValueHelper.determineActivityTTDuration().toDouble() binding.duration.value = defaultValueHelper.determineActivityTTDuration().toDouble()
binding.reason.setSelection(reasonList.indexOf(rh.gs(R.string.activity))) binding.reasonList.setText(rh.gs(R.string.activity), false)
} }
R.id.hypo -> { R.id.hypo -> {
binding.temptarget.value = defaultValueHelper.determineHypoTT() binding.temptarget.value = defaultValueHelper.determineHypoTT()
binding.duration.value = defaultValueHelper.determineHypoTTDuration().toDouble() binding.duration.value = defaultValueHelper.determineHypoTTDuration().toDouble()
binding.reason.setSelection(reasonList.indexOf(rh.gs(R.string.hypo))) binding.reasonList.setText(rh.gs(R.string.hypo), false)
}
R.id.cancel -> {
binding.duration.value = 0.0
} }
} }
} }
@ -165,7 +164,7 @@ class TempTargetDialog : DialogFragmentWithDate() {
override fun submit(): Boolean { override fun submit(): Boolean {
if (_binding == null) return false if (_binding == null) return false
val actions: LinkedList<String> = LinkedList() val actions: LinkedList<String> = LinkedList()
var reason = binding.reason.selectedItem?.toString() ?: return false var reason = binding.reasonList.text.toString()
val unitResId = if (profileFunction.getUnits() == GlucoseUnit.MGDL) R.string.mgdl else R.string.mmol val unitResId = if (profileFunction.getUnits() == GlucoseUnit.MGDL) R.string.mgdl else R.string.mmol
val target = binding.temptarget.value val target = binding.temptarget.value
val duration = binding.duration.value.toInt() val duration = binding.duration.value.toInt()
@ -222,4 +221,20 @@ class TempTargetDialog : DialogFragmentWithDate() {
} }
return true return true
} }
override fun onResume() {
super.onResume()
if(!queryingProtection) {
queryingProtection = true
activity?.let { activity ->
val cancelFail = {
queryingProtection = false
aapsLogger.debug(LTag.APS, "Dialog canceled on resume protection: ${this.javaClass.name}")
ToastUtils.showToastInUiThread(ctx, R.string.dialog_canceled)
dismiss()
}
protectionCheck.queryProtection(activity, BOLUS, { queryingProtection = false }, cancelFail, cancelFail)
}
}
}
} }

View file

@ -30,6 +30,8 @@ import info.nightscout.shared.SafeParse
import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.extensions.formatColor import info.nightscout.androidaps.extensions.formatColor
import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.protection.ProtectionCheck.Protection.BOLUS
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
@ -48,8 +50,14 @@ class TreatmentDialog : DialogFragmentWithDate() {
@Inject lateinit var config: Config @Inject lateinit var config: Config
@Inject lateinit var uel: UserEntryLogger @Inject lateinit var uel: UserEntryLogger
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
@Inject lateinit var protectionCheck: ProtectionCheck
private var queryingProtection = false
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
private var _binding: DialogTreatmentBinding? = null
// This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!!
private val textWatcher: TextWatcher = object : TextWatcher { private val textWatcher: TextWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {} override fun afterTextChanged(s: Editable) {}
@ -72,12 +80,6 @@ class TreatmentDialog : DialogFragmentWithDate() {
} }
} }
private var _binding: DialogTreatmentBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onSaveInstanceState(savedInstanceState: Bundle) { override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState) super.onSaveInstanceState(savedInstanceState)
savedInstanceState.putDouble("carbs", binding.carbs.value) savedInstanceState.putDouble("carbs", binding.carbs.value)
@ -106,8 +108,8 @@ class TreatmentDialog : DialogFragmentWithDate() {
binding.insulin.setParams(savedInstanceState?.getDouble("insulin") binding.insulin.setParams(savedInstanceState?.getDouble("insulin")
?: 0.0, 0.0, maxInsulin, pumpDescription.bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, binding.okcancel.ok, textWatcher) ?: 0.0, 0.0, maxInsulin, pumpDescription.bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, binding.okcancel.ok, textWatcher)
binding.recordOnlyLayout.visibility = View.GONE binding.recordOnlyLayout.visibility = View.GONE
binding.insulin.editText?.id?.let { binding.insulinLabel.labelFor = it } binding.insulinLabel.labelFor = binding.insulin.editTextId
binding.carbs.editText?.id?.let { binding.carbsLabel.labelFor = it } binding.carbsLabel.labelFor = binding.carbs.editTextId
} }
override fun onDestroyView() { override fun onDestroyView() {
@ -126,16 +128,16 @@ class TreatmentDialog : DialogFragmentWithDate() {
val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value() val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value()
if (insulinAfterConstraints > 0) { if (insulinAfterConstraints > 0) {
actions.add(rh.gs(R.string.bolus) + ": " + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints, activePlugin.activePump, rh).formatColor(rh, R.color.bolus)) actions.add(rh.gs(R.string.bolus) + ": " + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints, activePlugin.activePump, rh).formatColor(context, rh, R.attr.bolusColor))
if (recordOnlyChecked) if (recordOnlyChecked)
actions.add(rh.gs(R.string.bolusrecordedonly).formatColor(rh, R.color.warning)) actions.add(rh.gs(R.string.bolusrecordedonly).formatColor(context, rh, R.attr.warningColor))
if (abs(insulinAfterConstraints - insulin) > pumpDescription.pumpType.determineCorrectBolusStepSize(insulinAfterConstraints)) if (abs(insulinAfterConstraints - insulin) > pumpDescription.pumpType.determineCorrectBolusStepSize(insulinAfterConstraints))
actions.add(rh.gs(R.string.bolusconstraintappliedwarn, insulin, insulinAfterConstraints).formatColor(rh, R.color.warning)) actions.add(rh.gs(R.string.bolusconstraintappliedwarn, insulin, insulinAfterConstraints).formatColor(context, rh, R.attr.warningColor))
} }
if (carbsAfterConstraints > 0) { if (carbsAfterConstraints > 0) {
actions.add(rh.gs(R.string.carbs) + ": " + rh.gs(R.string.format_carbs, carbsAfterConstraints).formatColor(rh, R.color.carbs)) actions.add(rh.gs(R.string.carbs) + ": " + rh.gs(R.string.format_carbs, carbsAfterConstraints).formatColor(context, rh, R.attr.carbsColor))
if (carbsAfterConstraints != carbs) if (carbsAfterConstraints != carbs)
actions.add(rh.gs(R.string.carbsconstraintapplied).formatColor(rh, R.color.warning)) actions.add(rh.gs(R.string.carbsconstraintapplied).formatColor(context, rh, R.attr.warningColor))
} }
if (insulinAfterConstraints > 0 || carbsAfterConstraints > 0) { if (insulinAfterConstraints > 0 || carbsAfterConstraints > 0) {
activity?.let { activity -> activity?.let { activity ->
@ -201,4 +203,20 @@ class TreatmentDialog : DialogFragmentWithDate() {
} }
return true return true
} }
}
override fun onResume() {
super.onResume()
if(!queryingProtection) {
queryingProtection = true
activity?.let { activity ->
val cancelFail = {
queryingProtection = false
aapsLogger.debug(LTag.APS, "Dialog canceled on resume protection: ${this.javaClass.name}")
ToastUtils.showToastInUiThread(ctx, R.string.dialog_canceled)
dismiss()
}
protectionCheck.queryProtection(activity, BOLUS, { queryingProtection = false }, cancelFail, cancelFail)
}
}
}
}

View file

@ -1,12 +1,16 @@
package info.nightscout.androidaps.dialogs package info.nightscout.androidaps.dialogs
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
import android.view.* import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.view.WindowManager
import android.widget.AdapterView import android.widget.AdapterView
import android.widget.AdapterView.OnItemSelectedListener
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.CompoundButton import android.widget.CompoundButton
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
@ -20,22 +24,24 @@ import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.databinding.DialogWizardBinding import info.nightscout.androidaps.databinding.DialogWizardBinding
import info.nightscout.androidaps.events.EventAutosensCalculationFinished import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.extensions.formatColor import info.nightscout.androidaps.extensions.formatColor
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.shared.SafeParse
import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.extensions.valueToUnits import info.nightscout.androidaps.extensions.valueToUnits
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.utils.* import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.protection.ProtectionCheck.Protection.BOLUS
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.androidaps.utils.wizard.BolusWizard import info.nightscout.androidaps.utils.wizard.BolusWizard
import info.nightscout.shared.SafeParse
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
import java.text.DecimalFormat import java.text.DecimalFormat
import java.util.*
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.abs import kotlin.math.abs
@ -55,16 +61,22 @@ class WizardDialog : DaggerDialogFragment() {
@Inject lateinit var iobCobCalculator: IobCobCalculator @Inject lateinit var iobCobCalculator: IobCobCalculator
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
@Inject lateinit var dateUtil: DateUtil @Inject lateinit var dateUtil: DateUtil
@Inject lateinit var protectionCheck: ProtectionCheck
private var queryingProtection = false
private var wizard: BolusWizard? = null private var wizard: BolusWizard? = null
private var calculatedPercentage = 100.0 private var calculatedPercentage = 100.0
private var calculatedCorrection = 0.0 private var calculatedCorrection = 0.0
private var correctionPercent = false private var correctionPercent = false
private var carbsPassedIntoWizard = 0.0 private var carbsPassedIntoWizard = 0.0
private var notesPassedIntoWizard = "" private var notesPassedIntoWizard = ""
private var okClicked: Boolean = false // one shot guards
private var disposable: CompositeDisposable = CompositeDisposable()
private var bolusStep = 0.0
private var _binding: DialogWizardBinding? = null
//one shot guards // This property is only valid between onCreateView and onDestroyView.
private var okClicked: Boolean = false private val binding get() = _binding!!
private val textWatcher = object : TextWatcher { private val textWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {} override fun afterTextChanged(s: Editable) {}
@ -83,15 +95,6 @@ class WizardDialog : DaggerDialogFragment() {
} }
} }
private var disposable: CompositeDisposable = CompositeDisposable()
private var bolusStep = 0.0
private var _binding: DialogWizardBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
@ -106,10 +109,9 @@ class WizardDialog : DaggerDialogFragment() {
savedInstanceState.putDouble("carb_time_input", binding.carbTimeInput.value) savedInstanceState.putDouble("carb_time_input", binding.carbTimeInput.value)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
savedInstanceState: Bundle?): View {
this.arguments?.let { bundle -> this.arguments?.let { bundle ->
carbsPassedIntoWizard = bundle.getInt("carbs_input").toDouble() carbsPassedIntoWizard = bundle.getDouble("carbs_input")
notesPassedIntoWizard = bundle.getString("notes_input").toString() notesPassedIntoWizard = bundle.getString("notes_input").toString()
} }
@ -128,7 +130,7 @@ class WizardDialog : DaggerDialogFragment() {
val useSuperBolus = sp.getBoolean(R.string.key_usesuperbolus, false) val useSuperBolus = sp.getBoolean(R.string.key_usesuperbolus, false)
binding.sbCheckbox.visibility = useSuperBolus.toVisibility() binding.sbCheckbox.visibility = useSuperBolus.toVisibility()
binding.superBolusRow.visibility = useSuperBolus.toVisibility() binding.superBolusRow.visibility = useSuperBolus.toVisibility()
binding.notesLayout.visibility = sp.getBoolean(R.string.key_show_notes_entry_dialogs, false).toVisibility() binding.notesLayout.root.visibility = sp.getBoolean(R.string.key_show_notes_entry_dialogs, false).toVisibility()
val maxCarbs = constraintChecker.getMaxCarbsAllowed().value() val maxCarbs = constraintChecker.getMaxCarbsAllowed().value()
val maxCorrection = constraintChecker.getMaxBolusAllowed().value() val maxCorrection = constraintChecker.getMaxBolusAllowed().value()
@ -137,14 +139,18 @@ class WizardDialog : DaggerDialogFragment() {
if (profileFunction.getUnits() == GlucoseUnit.MGDL) { if (profileFunction.getUnits() == GlucoseUnit.MGDL) {
binding.bgInput.setParams( binding.bgInput.setParams(
savedInstanceState?.getDouble("bg_input") savedInstanceState?.getDouble("bg_input")
?: 0.0, 0.0, 500.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, timeTextWatcher) ?: 0.0, 0.0, 500.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, timeTextWatcher
)
} else { } else {
binding.bgInput.setParams( binding.bgInput.setParams(
savedInstanceState?.getDouble("bg_input") savedInstanceState?.getDouble("bg_input")
?: 0.0, 0.0, 30.0, 0.1, DecimalFormat("0.0"), false, binding.okcancel.ok, textWatcher) ?: 0.0, 0.0, 30.0, 0.1, DecimalFormat("0.0"), false, binding.okcancel.ok, textWatcher
)
} }
binding.carbsInput.setParams(savedInstanceState?.getDouble("carbs_input") binding.carbsInput.setParams(
?: 0.0, 0.0, maxCarbs.toDouble(), 1.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher) savedInstanceState?.getDouble("carbs_input")
?: 0.0, 0.0, maxCarbs.toDouble(), 1.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher
)
if (correctionPercent) { if (correctionPercent) {
calculatedPercentage = sp.getInt(R.string.key_boluswizard_percentage, 100).toDouble() calculatedPercentage = sp.getInt(R.string.key_boluswizard_percentage, 100).toDouble()
@ -154,11 +160,14 @@ class WizardDialog : DaggerDialogFragment() {
} else { } else {
binding.correctionInput.setParams( binding.correctionInput.setParams(
savedInstanceState?.getDouble("correction_input") savedInstanceState?.getDouble("correction_input")
?: 0.0, -maxCorrection, maxCorrection, bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, binding.okcancel.ok, textWatcher) ?: 0.0, -maxCorrection, maxCorrection, bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, binding.okcancel.ok, textWatcher
)
binding.correctionUnit.text = rh.gs(R.string.insulin_unit_shortname) binding.correctionUnit.text = rh.gs(R.string.insulin_unit_shortname)
} }
binding.carbTimeInput.setParams(savedInstanceState?.getDouble("carb_time_input") binding.carbTimeInput.setParams(
?: 0.0, -60.0, 60.0, 5.0, DecimalFormat("0"), false, binding.okcancel.ok, timeTextWatcher) savedInstanceState?.getDouble("carb_time_input")
?: 0.0, -60.0, 60.0, 5.0, DecimalFormat("0"), false, binding.okcancel.ok, timeTextWatcher
)
initDialog() initDialog()
calculatedPercentage = sp.getInt(R.string.key_boluswizard_percentage, 100).toDouble() calculatedPercentage = sp.getInt(R.string.key_boluswizard_percentage, 100).toDouble()
binding.percentUsed.text = rh.gs(R.string.format_percent, sp.getInt(R.string.key_boluswizard_percentage, 100)) binding.percentUsed.text = rh.gs(R.string.format_percent, sp.getInt(R.string.key_boluswizard_percentage, 100))
@ -209,7 +218,7 @@ class WizardDialog : DaggerDialogFragment() {
processEnabledIcons() processEnabledIcons()
binding.correctionPercent.setOnCheckedChangeListener {_, isChecked -> binding.correctionPercent.setOnCheckedChangeListener { _, isChecked ->
run { run {
sp.putBoolean(rh.gs(R.string.key_wizard_correction_percent), isChecked) sp.putBoolean(rh.gs(R.string.key_wizard_correction_percent), isChecked)
binding.correctionUnit.text = if (isChecked) "%" else rh.gs(R.string.insulin_unit_shortname) binding.correctionUnit.text = if (isChecked) "%" else rh.gs(R.string.insulin_unit_shortname)
@ -228,35 +237,21 @@ class WizardDialog : DaggerDialogFragment() {
binding.correctionInput.value = if (correctionPercent) calculatedPercentage else Round.roundTo(calculatedCorrection, bolusStep) binding.correctionInput.value = if (correctionPercent) calculatedPercentage else Round.roundTo(calculatedCorrection, bolusStep)
} }
} }
// profile spinner // profile
binding.profile.onItemSelectedListener = object : OnItemSelectedListener { binding.profileList.onItemClickListener = AdapterView.OnItemClickListener { _, _, _, _ -> calculateInsulin() }
override fun onNothingSelected(parent: AdapterView<*>?) {
ToastUtils.showToastInUiThread(ctx, rh.gs(R.string.noprofileset))
binding.okcancel.ok.visibility = View.GONE
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
calculateInsulin()
binding.okcancel.ok.visibility = View.VISIBLE
}
}
// bus // bus
disposable.add(rxBus disposable += rxBus
.toObservable(EventAutosensCalculationFinished::class.java) .toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ .subscribe({ calculateInsulin() }, fabricPrivacy::logException)
activity?.runOnUiThread { calculateInsulin() }
}, fabricPrivacy::logException)
)
setA11yLabels() setA11yLabels()
} }
private fun setA11yLabels() { private fun setA11yLabels() {
binding.bgInput.editText?.id?.let { binding.bgInputLabel.labelFor = it } binding.bgInputLabel.labelFor = binding.bgInput.editTextId
binding.carbsInput.editText?.id?.let { binding.carbsInputLabel.labelFor = it } binding.carbsInputLabel.labelFor = binding.carbsInput.editTextId
binding.correctionInput.editText?.id?.let { binding.correctionInputLabel.labelFor = it } binding.correctionInputLabel.labelFor = binding.correctionInput.editTextId
binding.carbTimeInput.editText?.id?.let { binding.carbTimeInputLabel.labelFor = it } binding.carbTimeInputLabel.labelFor = binding.carbTimeInput.editTextId
} }
override fun onDestroyView() { override fun onDestroyView() {
@ -307,6 +302,7 @@ class WizardDialog : DaggerDialogFragment() {
binding.trendCheckboxIcon.visibility = binding.calculationCheckbox.isChecked.not().toVisibility() binding.trendCheckboxIcon.visibility = binding.calculationCheckbox.isChecked.not().toVisibility()
binding.iobCheckboxIcon.visibility = binding.calculationCheckbox.isChecked.not().toVisibility() binding.iobCheckboxIcon.visibility = binding.calculationCheckbox.isChecked.not().toVisibility()
binding.cobCheckboxIcon.visibility = binding.calculationCheckbox.isChecked.not().toVisibility() binding.cobCheckboxIcon.visibility = binding.calculationCheckbox.isChecked.not().toVisibility()
binding.checkboxRow.visibility = binding.calculationCheckbox.isChecked.not().toVisibility()
} }
private fun saveCheckedStates() { private fun saveCheckedStates() {
@ -318,7 +314,7 @@ class WizardDialog : DaggerDialogFragment() {
private fun loadCheckedStates() { private fun loadCheckedStates() {
binding.bgTrendCheckbox.isChecked = sp.getBoolean(R.string.key_wizard_include_trend_bg, false) binding.bgTrendCheckbox.isChecked = sp.getBoolean(R.string.key_wizard_include_trend_bg, false)
binding.cobCheckbox.isChecked = sp.getBoolean(R.string.key_wizard_include_cob, false) binding.cobCheckbox.isChecked = sp.getBoolean(R.string.key_wizard_include_cob, false)
correctionPercent = sp.getBoolean(R.string.key_wizard_correction_percent,false) correctionPercent = sp.getBoolean(R.string.key_wizard_correction_percent, false)
binding.correctionPercent.isChecked = correctionPercent binding.correctionPercent.isChecked = correctionPercent
} }
@ -327,11 +323,11 @@ class WizardDialog : DaggerDialogFragment() {
else DecimalFormatter.to1Decimal(value * Constants.MGDL_TO_MMOLL) else DecimalFormatter.to1Decimal(value * Constants.MGDL_TO_MMOLL)
private fun initDialog() { private fun initDialog() {
if(carbsPassedIntoWizard != 0.0) { if (carbsPassedIntoWizard != 0.0) {
binding.carbsInput.value = carbsPassedIntoWizard binding.carbsInput.value = carbsPassedIntoWizard
} }
if(notesPassedIntoWizard.isNotBlank()) { if (notesPassedIntoWizard.isNotBlank()) {
binding.notes.setText(notesPassedIntoWizard) binding.notesLayout.notes.setText(notesPassedIntoWizard)
} }
val profile = profileFunction.getProfile() val profile = profileFunction.getProfile()
val profileStore = activePlugin.activeProfileSource.profile val profileStore = activePlugin.activeProfileSource.profile
@ -345,9 +341,9 @@ class WizardDialog : DaggerDialogFragment() {
val profileList: ArrayList<CharSequence> = profileStore.getProfileList() val profileList: ArrayList<CharSequence> = profileStore.getProfileList()
profileList.add(0, rh.gs(R.string.active)) profileList.add(0, rh.gs(R.string.active))
context?.let { context -> context?.let { context ->
val adapter = ArrayAdapter(context, R.layout.spinner_centered, profileList) binding.profileList.setAdapter(ArrayAdapter(context, R.layout.spinner_centered, profileList))
binding.profile.adapter = adapter binding.profileList.setText(profileList[0], false)
} ?: return }
val units = profileFunction.getUnits() val units = profileFunction.getUnits()
binding.bgUnits.text = units.asText binding.bgUnits.text = units.asText
@ -370,11 +366,10 @@ class WizardDialog : DaggerDialogFragment() {
binding.percentUsed.visibility = (sp.getInt(R.string.key_boluswizard_percentage, 100) != 100 || correctionPercent).toVisibility() binding.percentUsed.visibility = (sp.getInt(R.string.key_boluswizard_percentage, 100) != 100 || correctionPercent).toVisibility()
} }
@SuppressLint("SetTextI18n")
private fun calculateInsulin() { private fun calculateInsulin() {
val profileStore = activePlugin.activeProfileSource.profile val profileStore = activePlugin.activeProfileSource.profile ?: return // not initialized yet
if (binding.profile.selectedItem == null || profileStore == null) var profileName = binding.profileList.text.toString()
return // not initialized yet
var profileName = binding.profile.selectedItem.toString()
val specificProfile: Profile? val specificProfile: Profile?
if (profileName == rh.gs(R.string.active)) { if (profileName == rh.gs(R.string.active)) {
specificProfile = profileFunction.getProfile() specificProfile = profileFunction.getProfile()
@ -396,7 +391,7 @@ class WizardDialog : DaggerDialogFragment() {
} else } else
0.0 0.0
val percentageCorrection = if (usePercentage) { val percentageCorrection = if (usePercentage) {
if (Round.roundTo(calculatedPercentage,1.0) == SafeParse.stringToDouble(binding.correctionInput.text)) if (Round.roundTo(calculatedPercentage, 1.0) == SafeParse.stringToDouble(binding.correctionInput.text))
calculatedPercentage calculatedPercentage
else else
SafeParse.stringToDouble(binding.correctionInput.text) SafeParse.stringToDouble(binding.correctionInput.text)
@ -422,7 +417,8 @@ class WizardDialog : DaggerDialogFragment() {
val carbTime = SafeParse.stringToInt(binding.carbTimeInput.text) val carbTime = SafeParse.stringToInt(binding.carbTimeInput.text)
wizard = BolusWizard(injector).doCalc(specificProfile, profileName, tempTarget, carbsAfterConstraint, cob, bg, correction, sp.getInt(R.string.key_boluswizard_percentage, 100), wizard = BolusWizard(injector).doCalc(
specificProfile, profileName, tempTarget, carbsAfterConstraint, cob, bg, correction, sp.getInt(R.string.key_boluswizard_percentage, 100),
binding.bgCheckbox.isChecked, binding.bgCheckbox.isChecked,
binding.cobCheckbox.isChecked, binding.cobCheckbox.isChecked,
binding.iobCheckbox.isChecked, binding.iobCheckbox.isChecked,
@ -431,17 +427,17 @@ class WizardDialog : DaggerDialogFragment() {
binding.ttCheckbox.isChecked, binding.ttCheckbox.isChecked,
binding.bgTrendCheckbox.isChecked, binding.bgTrendCheckbox.isChecked,
binding.alarm.isChecked, binding.alarm.isChecked,
binding.notes.text.toString(), binding.notesLayout.notes.text.toString(),
carbTime, carbTime,
usePercentage = usePercentage, usePercentage = usePercentage,
totalPercentage = percentageCorrection totalPercentage = percentageCorrection
) )
wizard?.let { wizard -> wizard?.let { wizard ->
binding.bg.text = String.format(rh.gs(R.string.format_bg_isf), valueToUnitsToString(Profile.toMgdl(bg, profileFunction.getUnits()), profileFunction.getUnits().asText), wizard.sens) binding.bg.text = rh.gs(R.string.format_bg_isf, valueToUnitsToString(Profile.toMgdl(bg, profileFunction.getUnits()), profileFunction.getUnits().asText), wizard.sens)
binding.bgInsulin.text = rh.gs(R.string.formatinsulinunits, wizard.insulinFromBG) binding.bgInsulin.text = rh.gs(R.string.formatinsulinunits, wizard.insulinFromBG)
binding.carbs.text = String.format(rh.gs(R.string.format_carbs_ic), carbs.toDouble(), wizard.ic) binding.carbs.text = rh.gs(R.string.format_carbs_ic, carbs.toDouble(), wizard.ic)
binding.carbsInsulin.text = rh.gs(R.string.formatinsulinunits, wizard.insulinFromCarbs) binding.carbsInsulin.text = rh.gs(R.string.formatinsulinunits, wizard.insulinFromCarbs)
binding.iobInsulin.text = rh.gs(R.string.formatinsulinunits, wizard.insulinFromBolusIOB + wizard.insulinFromBasalIOB) binding.iobInsulin.text = rh.gs(R.string.formatinsulinunits, wizard.insulinFromBolusIOB + wizard.insulinFromBasalIOB)
@ -464,7 +460,7 @@ class WizardDialog : DaggerDialogFragment() {
// COB // COB
if (binding.cobCheckbox.isChecked) { if (binding.cobCheckbox.isChecked) {
binding.cob.text = String.format(rh.gs(R.string.format_cob_ic), cob, wizard.ic) binding.cob.text = rh.gs(R.string.format_cob_ic, cob, wizard.ic)
binding.cobInsulin.text = rh.gs(R.string.formatinsulinunits, wizard.insulinFromCOB) binding.cobInsulin.text = rh.gs(R.string.formatinsulinunits, wizard.insulinFromCOB)
} else { } else {
binding.cob.text = "" binding.cob.text = ""
@ -472,12 +468,12 @@ class WizardDialog : DaggerDialogFragment() {
} }
if (wizard.calculatedTotalInsulin > 0.0 || carbsAfterConstraint > 0.0) { if (wizard.calculatedTotalInsulin > 0.0 || carbsAfterConstraint > 0.0) {
val insulinText = if (wizard.calculatedTotalInsulin > 0.0) rh.gs(R.string.formatinsulinunits, wizard.calculatedTotalInsulin).formatColor(rh, R.color.bolus) else "" val insulinText = if (wizard.calculatedTotalInsulin > 0.0) rh.gs(R.string.formatinsulinunits, wizard.calculatedTotalInsulin).formatColor(context, rh, R.attr.bolusColor) else ""
val carbsText = if (carbsAfterConstraint > 0.0) rh.gs(R.string.format_carbs, carbsAfterConstraint).formatColor(rh, R.color.carbs) else "" val carbsText = if (carbsAfterConstraint > 0.0) rh.gs(R.string.format_carbs, carbsAfterConstraint).formatColor(context, rh, R.attr.carbsColor) else ""
binding.total.text = HtmlHelper.fromHtml(rh.gs(R.string.result_insulin_carbs, insulinText, carbsText)) binding.total.text = HtmlHelper.fromHtml(rh.gs(R.string.result_insulin_carbs, insulinText, carbsText))
binding.okcancel.ok.visibility = View.VISIBLE binding.okcancel.ok.visibility = View.VISIBLE
} else { } else {
binding.total.text = HtmlHelper.fromHtml(rh.gs(R.string.missing_carbs, wizard.carbsEquivalent.toInt()).formatColor(rh, R.color.carbs)) binding.total.text = HtmlHelper.fromHtml(rh.gs(R.string.missing_carbs, wizard.carbsEquivalent.toInt()).formatColor(context, rh, R.attr.carbsColor))
binding.okcancel.ok.visibility = View.INVISIBLE binding.okcancel.ok.visibility = View.INVISIBLE
} }
binding.percentUsed.text = rh.gs(R.string.format_percent, wizard.percentageCorrection) binding.percentUsed.text = rh.gs(R.string.format_percent, wizard.percentageCorrection)
@ -497,4 +493,20 @@ class WizardDialog : DaggerDialogFragment() {
aapsLogger.debug(e.localizedMessage ?: "") aapsLogger.debug(e.localizedMessage ?: "")
} }
} }
override fun onResume() {
super.onResume()
if(!queryingProtection) {
queryingProtection = true
activity?.let { activity ->
val cancelFail = {
queryingProtection = false
aapsLogger.debug(LTag.APS, "Dialog canceled on resume protection: ${this.javaClass.name}")
ToastUtils.showToastInUiThread(ctx, R.string.dialog_canceled)
dismiss()
}
protectionCheck.queryProtection(activity, BOLUS, { queryingProtection = false }, cancelFail, cancelFail)
}
}
}
} }

View file

@ -1,3 +0,0 @@
package info.nightscout.androidaps.events
class EventReloadProfileSwitchData : Event()

View file

@ -1,3 +0,0 @@
package info.nightscout.androidaps.events
class EventTreatmentUpdateGui : EventUpdateGui()

View file

@ -1,5 +0,0 @@
package info.nightscout.androidaps.plugins.aps.events
import info.nightscout.androidaps.events.Event
class EventLoopInvoked : Event()

View file

@ -10,11 +10,13 @@ import android.content.Intent
import android.os.SystemClock import android.os.SystemClock
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.* import info.nightscout.androidaps.BuildConfig
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.MainActivity
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.ErrorHelperActivity import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.annotations.OpenForTesting import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.androidaps.data.DetailedBolusInfo import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.data.PumpEnactResult import info.nightscout.androidaps.data.PumpEnactResult
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.ValueWrapper import info.nightscout.androidaps.database.ValueWrapper
@ -25,13 +27,13 @@ import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentOfflineEventTransaction import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentOfflineEventTransaction
import info.nightscout.androidaps.database.transactions.InsertTherapyEventAnnouncementTransaction import info.nightscout.androidaps.database.transactions.InsertTherapyEventAnnouncementTransaction
import info.nightscout.androidaps.events.EventAcceptOpenLoopChange import info.nightscout.androidaps.events.EventAcceptOpenLoopChange
import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.events.EventNewBG
import info.nightscout.androidaps.events.EventTempTargetChange import info.nightscout.androidaps.events.EventTempTargetChange
import info.nightscout.androidaps.extensions.buildDeviceStatus
import info.nightscout.androidaps.extensions.convertedToAbsolute
import info.nightscout.androidaps.extensions.convertedToPercent
import info.nightscout.androidaps.extensions.plannedRemainingMinutes
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.interfaces.Loop.LastRun import info.nightscout.androidaps.interfaces.Loop.LastRun
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopSetLastRunGui import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopSetLastRunGui
import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopUpdateGui import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopUpdateGui
@ -51,13 +53,10 @@ import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.HardLimits import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.extensions.buildDeviceStatus
import info.nightscout.androidaps.extensions.convertedToAbsolute
import info.nightscout.androidaps.extensions.convertedToPercent
import info.nightscout.androidaps.extensions.plannedRemainingMinutes
import info.nightscout.androidaps.plugins.aps.events.EventLoopInvoked
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
@ -88,20 +87,21 @@ class LoopPlugin @Inject constructor(
private val uel: UserEntryLogger, private val uel: UserEntryLogger,
private val repository: AppRepository, private val repository: AppRepository,
private val runningConfiguration: RunningConfiguration private val runningConfiguration: RunningConfiguration
) : PluginBase(PluginDescription() ) : PluginBase(
.mainType(PluginType.LOOP) PluginDescription()
.fragmentClass(LoopFragment::class.java.name) .mainType(PluginType.LOOP)
.pluginIcon(R.drawable.ic_loop_closed_white) .fragmentClass(LoopFragment::class.java.name)
.pluginName(R.string.loop) .pluginIcon(R.drawable.ic_loop_closed_white)
.shortName(R.string.loop_shortname) .pluginName(R.string.loop)
.preferencesId(R.xml.pref_loop) .shortName(R.string.loop_shortname)
.enableByDefault(config.APS) .preferencesId(R.xml.pref_loop)
.description(R.string.description_loop), .enableByDefault(config.APS)
.description(R.string.description_loop),
aapsLogger, rh, injector aapsLogger, rh, injector
), Loop { ), Loop {
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
private var lastBgTriggeredRun: Long = 0 override var lastBgTriggeredRun: Long = 0
private var carbsSuggestionsSuspendedUntil: Long = 0 private var carbsSuggestionsSuspendedUntil: Long = 0
private var prevCarbsreq = 0 private var prevCarbsreq = 0
override var lastRun: LastRun? = null override var lastRun: LastRun? = null
@ -109,39 +109,19 @@ class LoopPlugin @Inject constructor(
override fun onStart() { override fun onStart() {
createNotificationChannel() createNotificationChannel()
super.onStart() super.onStart()
disposable.add(rxBus disposable += rxBus
.toObservable(EventTempTargetChange::class.java) .toObservable(EventTempTargetChange::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ invoke("EventTempTargetChange", true) }, fabricPrivacy::logException) .subscribe({ invoke("EventTempTargetChange", true) }, fabricPrivacy::logException)
)
/*
This method is triggered once autosens calculation has completed, so the LoopPlugin
has current data to work with. However, autosens calculation can be triggered by multiple
sources and currently only a new BG should trigger a loop run. Hence we return early if
the event causing the calculation is not EventNewBg.
<p>
*/
disposable.add(rxBus
.toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ event: EventAutosensCalculationFinished ->
// Autosens calculation not triggered by a new BG
if (event.cause !is EventNewBG) return@subscribe
val glucoseValue = iobCobCalculator.ads.actualBg() ?: return@subscribe
// BG outdated
// already looped with that value
if (glucoseValue.timestamp <= lastBgTriggeredRun) return@subscribe
lastBgTriggeredRun = glucoseValue.timestamp
invoke("AutosenseCalculation for $glucoseValue", true)
}, fabricPrivacy::logException)
)
} }
private fun createNotificationChannel() { private fun createNotificationChannel() {
val mNotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val mNotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
@SuppressLint("WrongConstant") val channel = NotificationChannel(CHANNEL_ID, @SuppressLint("WrongConstant") val channel = NotificationChannel(
CHANNEL_ID, CHANNEL_ID,
NotificationManager.IMPORTANCE_HIGH) CHANNEL_ID,
NotificationManager.IMPORTANCE_HIGH
)
mNotificationManager.createNotificationChannel(channel) mNotificationManager.createNotificationChannel(channel)
} }
@ -255,7 +235,7 @@ class LoopPlugin @Inject constructor(
if (apsResult == null) { if (apsResult == null) {
rxBus.send(EventLoopSetLastRunGui(rh.gs(R.string.noapsselected))) rxBus.send(EventLoopSetLastRunGui(rh.gs(R.string.noapsselected)))
return return
} else rxBus.send(EventLoopInvoked()) }
if (!isEmptyQueue()) { if (!isEmptyQueue()) {
aapsLogger.debug(LTag.APS, rh.gs(R.string.pumpbusy)) aapsLogger.debug(LTag.APS, rh.gs(R.string.pumpbusy))
@ -296,9 +276,11 @@ class LoopPlugin @Inject constructor(
lastRun.lastTBRRequest = 0 lastRun.lastTBRRequest = 0
lastRun.lastSMBEnact = 0 lastRun.lastSMBEnact = 0
lastRun.lastSMBRequest = 0 lastRun.lastSMBRequest = 0
buildDeviceStatus(dateUtil, this, iobCobCalculator, profileFunction, buildDeviceStatus(
dateUtil, this, iobCobCalculator, profileFunction,
activePlugin.activePump, receiverStatusStore, runningConfiguration, activePlugin.activePump, receiverStatusStore, runningConfiguration,
BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)?.also { BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION
)?.also {
repository.insert(it) repository.insert(it)
} }
@ -316,7 +298,11 @@ class LoopPlugin @Inject constructor(
if (closedLoopEnabled.value()) { if (closedLoopEnabled.value()) {
if (allowNotification) { if (allowNotification) {
if (resultAfterConstraints.isCarbsRequired if (resultAfterConstraints.isCarbsRequired
&& resultAfterConstraints.carbsReq >= sp.getInt(R.string.key_smb_enable_carbs_suggestions_threshold, 0) && carbsSuggestionsSuspendedUntil < System.currentTimeMillis() && !treatmentTimeThreshold(-15)) { && resultAfterConstraints.carbsReq >= sp.getInt(
R.string.key_smb_enable_carbs_suggestions_threshold,
0
) && carbsSuggestionsSuspendedUntil < System.currentTimeMillis() && !treatmentTimeThreshold(-15)
) {
if (sp.getBoolean(R.string.key_enable_carbs_required_alert_local, true) && !sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, true)) { if (sp.getBoolean(R.string.key_enable_carbs_required_alert_local, true) && !sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, true)) {
val carbReqLocal = Notification(Notification.CARBS_REQUIRED, resultAfterConstraints.carbsRequiredText, Notification.NORMAL) val carbReqLocal = Notification(Notification.CARBS_REQUIRED, resultAfterConstraints.carbsRequiredText, Notification.NORMAL)
rxBus.send(EventNewNotification(carbReqLocal)) rxBus.send(EventNewNotification(carbReqLocal))
@ -353,9 +339,11 @@ class LoopPlugin @Inject constructor(
// mId allows you to update the notification later on. // mId allows you to update the notification later on.
mNotificationManager.notify(Constants.notificationID, builder.build()) mNotificationManager.notify(Constants.notificationID, builder.build())
uel.log(Action.CAREPORTAL, Sources.Loop, rh.gs(R.string.carbsreq, resultAfterConstraints.carbsReq, resultAfterConstraints.carbsReqWithin), uel.log(
ValueWithUnit.Gram(resultAfterConstraints.carbsReq), Action.CAREPORTAL, Sources.Loop, rh.gs(R.string.carbsreq, resultAfterConstraints.carbsReq, resultAfterConstraints.carbsReqWithin),
ValueWithUnit.Minute(resultAfterConstraints.carbsReqWithin)) ValueWithUnit.Gram(resultAfterConstraints.carbsReq),
ValueWithUnit.Minute(resultAfterConstraints.carbsReqWithin)
)
rxBus.send(EventNewOpenLoopNotification()) rxBus.send(EventNewOpenLoopNotification())
//only send to wear if Native notifications are turned off //only send to wear if Native notifications are turned off
@ -373,7 +361,8 @@ class LoopPlugin @Inject constructor(
} }
} }
if (resultAfterConstraints.isChangeRequested if (resultAfterConstraints.isChangeRequested
&& !commandQueue.bolusInQueue()) { && !commandQueue.bolusInQueue()
) {
val waiting = PumpEnactResult(injector) val waiting = PumpEnactResult(injector)
waiting.queued = true waiting.queued = true
if (resultAfterConstraints.tempBasalRequested) lastRun.tbrSetByPump = waiting if (resultAfterConstraints.tempBasalRequested) lastRun.tbrSetByPump = waiting
@ -486,9 +475,11 @@ class LoopPlugin @Inject constructor(
lastRun.lastTBRRequest = lastRun.lastAPSRun lastRun.lastTBRRequest = lastRun.lastAPSRun
lastRun.lastTBREnact = dateUtil.now() lastRun.lastTBREnact = dateUtil.now()
lastRun.lastOpenModeAccept = dateUtil.now() lastRun.lastOpenModeAccept = dateUtil.now()
buildDeviceStatus(dateUtil, this@LoopPlugin, iobCobCalculator, profileFunction, buildDeviceStatus(
dateUtil, this@LoopPlugin, iobCobCalculator, profileFunction,
activePlugin.activePump, receiverStatusStore, runningConfiguration, activePlugin.activePump, receiverStatusStore, runningConfiguration,
BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)?.also { BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION
)?.also {
repository.insert(it) repository.insert(it)
} }
sp.incInt(R.string.key_ObjectivesmanualEnacts) sp.incInt(R.string.key_ObjectivesmanualEnacts)
@ -531,8 +522,10 @@ class LoopPlugin @Inject constructor(
commandQueue.cancelTempBasal(false, callback) commandQueue.cancelTempBasal(false, callback)
} else { } else {
aapsLogger.debug(LTag.APS, "applyAPSRequest: Basal set correctly") aapsLogger.debug(LTag.APS, "applyAPSRequest: Basal set correctly")
callback?.result(PumpEnactResult(injector).absolute(request.rate).duration(0) callback?.result(
.enacted(false).success(true).comment(R.string.basal_set_correctly))?.run() PumpEnactResult(injector).absolute(request.rate).duration(0)
.enacted(false).success(true).comment(R.string.basal_set_correctly)
)?.run()
} }
} else if (request.usePercent && allowPercentage()) { } else if (request.usePercent && allowPercentage()) {
if (request.percent == 100 && request.duration == 0) { if (request.percent == 100 && request.duration == 0) {
@ -542,32 +535,52 @@ class LoopPlugin @Inject constructor(
commandQueue.cancelTempBasal(false, callback) commandQueue.cancelTempBasal(false, callback)
} else { } else {
aapsLogger.debug(LTag.APS, "applyAPSRequest: Basal set correctly") aapsLogger.debug(LTag.APS, "applyAPSRequest: Basal set correctly")
callback?.result(PumpEnactResult(injector).percent(request.percent).duration(0) callback?.result(
.enacted(false).success(true).comment(R.string.basal_set_correctly))?.run() PumpEnactResult(injector).percent(request.percent).duration(0)
.enacted(false).success(true).comment(R.string.basal_set_correctly)
)?.run()
} }
} else if (activeTemp != null && activeTemp.plannedRemainingMinutes > 5 && request.duration - activeTemp.plannedRemainingMinutes < 30 && request.percent == activeTemp.convertedToPercent(now, profile)) { } else if (activeTemp != null && activeTemp.plannedRemainingMinutes > 5 && request.duration - activeTemp.plannedRemainingMinutes < 30 && request.percent == activeTemp.convertedToPercent(
now,
profile
)
) {
aapsLogger.debug(LTag.APS, "applyAPSRequest: Temp basal set correctly") aapsLogger.debug(LTag.APS, "applyAPSRequest: Temp basal set correctly")
callback?.result(PumpEnactResult(injector).percent(request.percent) callback?.result(
.enacted(false).success(true).duration(activeTemp.plannedRemainingMinutes) PumpEnactResult(injector).percent(request.percent)
.comment(R.string.let_temp_basal_run))?.run() .enacted(false).success(true).duration(activeTemp.plannedRemainingMinutes)
.comment(R.string.let_temp_basal_run)
)?.run()
} else { } else {
aapsLogger.debug(LTag.APS, "applyAPSRequest: tempBasalPercent()") aapsLogger.debug(LTag.APS, "applyAPSRequest: tempBasalPercent()")
uel.log(Action.TEMP_BASAL, Sources.Loop, uel.log(
Action.TEMP_BASAL, Sources.Loop,
ValueWithUnit.Percent(request.percent), ValueWithUnit.Percent(request.percent),
ValueWithUnit.Minute(request.duration)) ValueWithUnit.Minute(request.duration)
)
commandQueue.tempBasalPercent(request.percent, request.duration, false, profile, PumpSync.TemporaryBasalType.NORMAL, callback) commandQueue.tempBasalPercent(request.percent, request.duration, false, profile, PumpSync.TemporaryBasalType.NORMAL, callback)
} }
} else { } else {
if (activeTemp != null && activeTemp.plannedRemainingMinutes > 5 && request.duration - activeTemp.plannedRemainingMinutes < 30 && abs(request.rate - activeTemp.convertedToAbsolute(now, profile)) < pump.pumpDescription.basalStep) { if (activeTemp != null && activeTemp.plannedRemainingMinutes > 5 && request.duration - activeTemp.plannedRemainingMinutes < 30 && abs(
request.rate - activeTemp.convertedToAbsolute(
now,
profile
)
) < pump.pumpDescription.basalStep
) {
aapsLogger.debug(LTag.APS, "applyAPSRequest: Temp basal set correctly") aapsLogger.debug(LTag.APS, "applyAPSRequest: Temp basal set correctly")
callback?.result(PumpEnactResult(injector).absolute(activeTemp.convertedToAbsolute(now, profile)) callback?.result(
.enacted(false).success(true).duration(activeTemp.plannedRemainingMinutes) PumpEnactResult(injector).absolute(activeTemp.convertedToAbsolute(now, profile))
.comment(R.string.let_temp_basal_run))?.run() .enacted(false).success(true).duration(activeTemp.plannedRemainingMinutes)
.comment(R.string.let_temp_basal_run)
)?.run()
} else { } else {
aapsLogger.debug(LTag.APS, "applyAPSRequest: setTempBasalAbsolute()") aapsLogger.debug(LTag.APS, "applyAPSRequest: setTempBasalAbsolute()")
uel.log(Action.TEMP_BASAL, Sources.Loop, uel.log(
Action.TEMP_BASAL, Sources.Loop,
ValueWithUnit.UnitPerHour(request.rate), ValueWithUnit.UnitPerHour(request.rate),
ValueWithUnit.Minute(request.duration)) ValueWithUnit.Minute(request.duration)
)
commandQueue.tempBasalAbsolute(request.rate, request.duration, false, profile, PumpSync.TemporaryBasalType.NORMAL, callback) commandQueue.tempBasalAbsolute(request.rate, request.duration, false, profile, PumpSync.TemporaryBasalType.NORMAL, callback)
} }
} }
@ -581,9 +594,11 @@ class LoopPlugin @Inject constructor(
val lastBolusTime = repository.getLastBolusRecord()?.timestamp ?: 0L val lastBolusTime = repository.getLastBolusRecord()?.timestamp ?: 0L
if (lastBolusTime != 0L && lastBolusTime + 3 * 60 * 1000 > System.currentTimeMillis()) { if (lastBolusTime != 0L && lastBolusTime + 3 * 60 * 1000 > System.currentTimeMillis()) {
aapsLogger.debug(LTag.APS, "SMB requested but still in 3 min interval") aapsLogger.debug(LTag.APS, "SMB requested but still in 3 min interval")
callback?.result(PumpEnactResult(injector) callback?.result(
.comment(R.string.smb_frequency_exceeded) PumpEnactResult(injector)
.enacted(false).success(false))?.run() .comment(R.string.smb_frequency_exceeded)
.enacted(false).success(false)
)?.run()
return return
} }
if (!pump.isInitialized()) { if (!pump.isInitialized()) {
@ -619,11 +634,11 @@ class LoopPlugin @Inject constructor(
val pump = activePlugin.activePump val pump = activePlugin.activePump
disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(durationInMinutes.toLong()).msecs(), reason)) disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(durationInMinutes.toLong()).msecs(), reason))
.subscribe({ result -> .subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") } result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") } result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
}, { }, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it) aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
}) })
if (pump.pumpDescription.tempBasalStyle == PumpDescription.ABSOLUTE) { if (pump.pumpDescription.tempBasalStyle == PumpDescription.ABSOLUTE) {
commandQueue.tempBasalAbsolute(0.0, durationInMinutes, true, profile, PumpSync.TemporaryBasalType.EMULATED_PUMP_SUSPEND, object : Callback() { commandQueue.tempBasalAbsolute(0.0, durationInMinutes, true, profile, PumpSync.TemporaryBasalType.EMULATED_PUMP_SUSPEND, object : Callback() {
override fun run() { override fun run() {
@ -655,11 +670,11 @@ class LoopPlugin @Inject constructor(
override fun suspendLoop(durationInMinutes: Int) { override fun suspendLoop(durationInMinutes: Int) {
disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(durationInMinutes.toLong()).msecs(), OfflineEvent.Reason.SUSPEND)) disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(durationInMinutes.toLong()).msecs(), OfflineEvent.Reason.SUSPEND))
.subscribe({ result -> .subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") } result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") } result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
}, { }, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it) aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
}) })
commandQueue.cancelTempBasal(true, object : Callback() { commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() { override fun run() {
if (!result.success) { if (!result.success) {

View file

@ -51,7 +51,7 @@ class DetermineBasalResultSMB private constructor(injector: HasAndroidInjector)
aapsLogger.error(LTag.APS, "Error parsing 'deliverAt' date: $date", e) aapsLogger.error(LTag.APS, "Error parsing 'deliverAt' date: $date", e)
} }
} }
if (result.has("variable_sens")) variableSens = result.getDouble("variable_sens"); if (result.has("variable_sens")) variableSens = result.getDouble("variable_sens")
} catch (e: JSONException) { } catch (e: JSONException) {
aapsLogger.error(LTag.APS, "Error parsing determine-basal result JSON", e) aapsLogger.error(LTag.APS, "Error parsing determine-basal result JSON", e)
} }

View file

@ -194,9 +194,9 @@ class DetermineBasalAdapterSMBDynamicISFJS internal constructor(private val scri
this.profile.put("current_basal_safety_multiplier", sp.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4.0)) this.profile.put("current_basal_safety_multiplier", sp.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4.0))
//mProfile.put("high_temptarget_raises_sensitivity", SP.getBoolean(R.string.key_high_temptarget_raises_sensitivity, SMBDefaults.high_temptarget_raises_sensitivity)); //mProfile.put("high_temptarget_raises_sensitivity", SP.getBoolean(R.string.key_high_temptarget_raises_sensitivity, SMBDefaults.high_temptarget_raises_sensitivity));
this.profile.put("high_temptarget_raises_sensitivity", false) this.profile.put("high_temptarget_raises_sensitivity", sp.getBoolean(R.string.key_high_temptarget_raises_sensitivity, SMBDefaults.high_temptarget_raises_sensitivity))
//mProfile.put("low_temptarget_lowers_sensitivity", SP.getBoolean(R.string.key_low_temptarget_lowers_sensitivity, SMBDefaults.low_temptarget_lowers_sensitivity)); //mProfile.put("low_temptarget_lowers_sensitivity", SP.getBoolean(R.string.key_low_temptarget_lowers_sensitivity, SMBDefaults.low_temptarget_lowers_sensitivity));
this.profile.put("low_temptarget_lowers_sensitivity", false) this.profile.put("low_temptarget_lowers_sensitivity", sp.getBoolean(R.string.key_low_temptarget_lowers_sensitivity, SMBDefaults.low_temptarget_lowers_sensitivity))
this.profile.put("sensitivity_raises_target", sp.getBoolean(R.string.key_sensitivity_raises_target, SMBDefaults.sensitivity_raises_target)) this.profile.put("sensitivity_raises_target", sp.getBoolean(R.string.key_sensitivity_raises_target, SMBDefaults.sensitivity_raises_target))
this.profile.put("resistance_lowers_target", sp.getBoolean(R.string.key_resistance_lowers_target, SMBDefaults.resistance_lowers_target)) this.profile.put("resistance_lowers_target", sp.getBoolean(R.string.key_resistance_lowers_target, SMBDefaults.resistance_lowers_target))
this.profile.put("adv_target_adjustments", SMBDefaults.adv_target_adjustments) this.profile.put("adv_target_adjustments", SMBDefaults.adv_target_adjustments)
@ -222,12 +222,17 @@ class DetermineBasalAdapterSMBDynamicISFJS internal constructor(private val scri
this.profile.put("enableSMB_after_carbs", smbEnabled && sp.getBoolean(R.string.key_enableSMB_after_carbs, false) && advancedFiltering) this.profile.put("enableSMB_after_carbs", smbEnabled && sp.getBoolean(R.string.key_enableSMB_after_carbs, false) && advancedFiltering)
this.profile.put("maxSMBBasalMinutes", sp.getInt(R.string.key_smbmaxminutes, SMBDefaults.maxSMBBasalMinutes)) this.profile.put("maxSMBBasalMinutes", sp.getInt(R.string.key_smbmaxminutes, SMBDefaults.maxSMBBasalMinutes))
this.profile.put("maxUAMSMBBasalMinutes", sp.getInt(R.string.key_uamsmbmaxminutes, SMBDefaults.maxUAMSMBBasalMinutes)) this.profile.put("maxUAMSMBBasalMinutes", sp.getInt(R.string.key_uamsmbmaxminutes, SMBDefaults.maxUAMSMBBasalMinutes))
this.profile.put("DynISFAdjust", SafeParse.stringToDouble(sp.getString(R.string.key_DynISFAdjust,"100")))
this.profile.put("maxUAMSMBBasalMinutes", sp.getInt(R.string.key_uamsmbmaxminutes, SMBDefaults.maxUAMSMBBasalMinutes))
//set the min SMB amount to be the amount set by the pump. //set the min SMB amount to be the amount set by the pump.
this.profile.put("bolus_increment", pumpBolusStep) this.profile.put("bolus_increment", pumpBolusStep)
this.profile.put("carbsReqThreshold", sp.getInt(R.string.key_carbsReqThreshold, SMBDefaults.carbsReqThreshold)) this.profile.put("carbsReqThreshold", sp.getInt(R.string.key_carbsReqThreshold, SMBDefaults.carbsReqThreshold))
this.profile.put("current_basal", basalRate) this.profile.put("current_basal", basalRate)
this.profile.put("temptargetSet", tempTargetSet) this.profile.put("temptargetSet", tempTargetSet)
this.profile.put("autosens_max", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_autosens_max, "1.2"))) this.profile.put("autosens_max", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_autosens_max, "1.2")))
this.profile.put("autosens_min", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_autosens_min, "0.8")))
this.profile.put("openapsama_useautosens", sp.getBoolean(R.string.key_openapsama_useautosens, false))
//set the min SMB amount to be the amount set by the pump.
if (profileFunction.getUnits() == GlucoseUnit.MMOL) { if (profileFunction.getUnits() == GlucoseUnit.MMOL) {
this.profile.put("out_units", "mmol/L") this.profile.put("out_units", "mmol/L")
} }

View file

@ -66,6 +66,7 @@ class OpenAPSSMBDynamicISFPlugin @Inject constructor(
.pluginName(R.string.openaps_smb_dynamic_isf) .pluginName(R.string.openaps_smb_dynamic_isf)
.description(R.string.description_smb_dynamic_isf) .description(R.string.description_smb_dynamic_isf)
.shortName(R.string.dynisf_shortname) .shortName(R.string.dynisf_shortname)
.preferencesId(R.xml.pref_openapssmbdynamicisf)
.setDefault(false) .setDefault(false)
} }

View file

@ -10,23 +10,26 @@ import android.widget.*
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import dagger.android.support.DaggerFragment import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.PreferencesActivity import info.nightscout.androidaps.activities.PreferencesActivity
import info.nightscout.androidaps.activities.SingleFragmentActivity
import info.nightscout.androidaps.databinding.ConfigbuilderFragmentBinding import info.nightscout.androidaps.databinding.ConfigbuilderFragmentBinding
import info.nightscout.androidaps.events.EventRebuildTabs import info.nightscout.androidaps.events.EventRebuildTabs
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.configBuilder.events.EventConfigBuilderUpdateGui import info.nightscout.androidaps.plugins.configBuilder.events.EventConfigBuilderUpdateGui
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import io.reactivex.rxjava3.kotlin.plusAssign
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.protection.ProtectionCheck.Protection.PREFERENCES
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import java.util.* import io.reactivex.rxjava3.kotlin.plusAssign
import javax.inject.Inject import javax.inject.Inject
class ConfigBuilderFragment : DaggerFragment() { class ConfigBuilderFragment : DaggerFragment() {
@ -44,48 +47,36 @@ class ConfigBuilderFragment : DaggerFragment() {
private var disposable: CompositeDisposable = CompositeDisposable() private var disposable: CompositeDisposable = CompositeDisposable()
private val pluginViewHolders = ArrayList<PluginViewHolder>() private val pluginViewHolders = ArrayList<PluginViewHolder>()
private var inMenu = false
private var queryingProtection = false
private var _binding: ConfigbuilderFragmentBinding? = null private var _binding: ConfigbuilderFragmentBinding? = null
// This property is only valid between onCreateView and // This property is only valid between onCreateView and onDestroyView.
// onDestroyView.
private val binding get() = _binding!! private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
savedInstanceState: Bundle?): View {
_binding = ConfigbuilderFragmentBinding.inflate(inflater, container, false) _binding = ConfigbuilderFragmentBinding.inflate(inflater, container, false)
return binding.root return binding.root
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
val parentClass = this.activity?.let { it::class.java }
if (protectionCheck.isLocked(ProtectionCheck.Protection.PREFERENCES)) inMenu = parentClass == SingleFragmentActivity::class.java
binding.mainLayout.visibility = View.GONE updateProtectedUi()
else binding.unlock.setOnClickListener { queryProtection() }
binding.unlock.visibility = View.GONE
binding.unlock.setOnClickListener {
activity?.let { activity ->
protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, {
activity.runOnUiThread {
binding.mainLayout.visibility = View.VISIBLE
binding.unlock.visibility = View.GONE
}
})
}
}
} }
@Synchronized @Synchronized
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
if (inMenu) queryProtection() else updateProtectedUi()
disposable += rxBus disposable += rxBus
.toObservable(EventConfigBuilderUpdateGui::class.java) .toObservable(EventConfigBuilderUpdateGui::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ .subscribe({
for (pluginViewHolder in pluginViewHolders) pluginViewHolder.update() for (pluginViewHolder in pluginViewHolders) pluginViewHolder.update()
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
updateGUI() updateGUI()
} }
@ -215,4 +206,21 @@ class ConfigBuilderFragment : DaggerFragment() {
return type == PluginType.GENERAL || type == PluginType.CONSTRAINTS || type == PluginType.LOOP return type == PluginType.GENERAL || type == PluginType.CONSTRAINTS || type == PluginType.LOOP
} }
} }
private fun updateProtectedUi() {
val isLocked = protectionCheck.isLocked(PREFERENCES)
binding.mainLayout.visibility = isLocked.not().toVisibility()
binding.unlock.visibility = isLocked.toVisibility()
}
private fun queryProtection() {
val isLocked = protectionCheck.isLocked(PREFERENCES)
if (isLocked && !queryingProtection) {
activity?.let { activity ->
queryingProtection = true
val doUpdate = { activity.runOnUiThread { queryingProtection = false; updateProtectedUi() } }
protectionCheck.queryProtection(activity, PREFERENCES, doUpdate, doUpdate, doUpdate)
}
}
}
} }

View file

@ -169,6 +169,9 @@ class PluginStore @Inject constructor(
override val activeSafety: Safety override val activeSafety: Safety
get() = getSpecificPluginsListByInterface(Safety::class.java).first() as Safety get() = getSpecificPluginsListByInterface(Safety::class.java).first() as Safety
override val activeIobCobCalculator: IobCobCalculator
get() = getSpecificPluginsListByInterface(IobCobCalculator::class.java).first() as IobCobCalculator
override fun getPluginsList(): ArrayList<PluginBase> = ArrayList(plugins) override fun getPluginsList(): ArrayList<PluginBase> = ArrayList(plugins)
} }

View file

@ -233,7 +233,7 @@ class ObjectivesFragment : DaggerFragment() {
} }
} }
holder.binding.accomplished.text = rh.gs(R.string.accomplished, dateUtil.dateAndTimeString(objective.accomplishedOn)) holder.binding.accomplished.text = rh.gs(R.string.accomplished, dateUtil.dateAndTimeString(objective.accomplishedOn))
holder.binding.accomplished.setTextColor(-0x3e3e3f) holder.binding.accomplished.setTextColor(rh.gac(context,R.attr.defaultTextColor))
holder.binding.verify.setOnClickListener { holder.binding.verify.setOnClickListener {
receiverStatusStore.updateNetworkStatus() receiverStatusStore.updateNetworkStatus()
if (binding.fake.isChecked) { if (binding.fake.isChecked) {

View file

@ -109,29 +109,29 @@ class SafetyPlugin @Inject constructor(
} }
override fun applyBasalConstraints(absoluteRate: Constraint<Double>, profile: Profile): Constraint<Double> { override fun applyBasalConstraints(absoluteRate: Constraint<Double>, profile: Profile): Constraint<Double> {
absoluteRate.setIfGreater(aapsLogger, 0.0, String.format(rh.gs(R.string.limitingbasalratio), 0.0, rh.gs(R.string.itmustbepositivevalue)), this) absoluteRate.setIfGreater(aapsLogger, 0.0, rh.gs(R.string.limitingbasalratio, 0.0, rh.gs(R.string.itmustbepositivevalue)), this)
if (config.APS) { if (config.APS) {
var maxBasal = sp.getDouble(R.string.key_openapsma_max_basal, 1.0) var maxBasal = sp.getDouble(R.string.key_openapsma_max_basal, 1.0)
if (maxBasal < profile.getMaxDailyBasal()) { if (maxBasal < profile.getMaxDailyBasal()) {
maxBasal = profile.getMaxDailyBasal() maxBasal = profile.getMaxDailyBasal()
absoluteRate.addReason(rh.gs(R.string.increasingmaxbasal), this) absoluteRate.addReason(rh.gs(R.string.increasingmaxbasal), this)
} }
absoluteRate.setIfSmaller(aapsLogger, maxBasal, String.format(rh.gs(R.string.limitingbasalratio), maxBasal, rh.gs(R.string.maxvalueinpreferences)), this) absoluteRate.setIfSmaller(aapsLogger, maxBasal,rh.gs(R.string.limitingbasalratio, maxBasal, rh.gs(R.string.maxvalueinpreferences)), this)
// Check percentRate but absolute rate too, because we know real current basal in pump // Check percentRate but absolute rate too, because we know real current basal in pump
val maxBasalMultiplier = sp.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4.0) val maxBasalMultiplier = sp.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4.0)
val maxFromBasalMultiplier = floor(maxBasalMultiplier * profile.getBasal() * 100) / 100 val maxFromBasalMultiplier = floor(maxBasalMultiplier * profile.getBasal() * 100) / 100
absoluteRate.setIfSmaller(aapsLogger, maxFromBasalMultiplier, String.format(rh.gs(R.string.limitingbasalratio), maxFromBasalMultiplier, rh.gs(R.string.maxbasalmultiplier)), this) absoluteRate.setIfSmaller(aapsLogger, maxFromBasalMultiplier, rh.gs(R.string.limitingbasalratio, maxFromBasalMultiplier, rh.gs(R.string.maxbasalmultiplier)), this)
val maxBasalFromDaily = sp.getDouble(R.string.key_openapsama_max_daily_safety_multiplier, 3.0) val maxBasalFromDaily = sp.getDouble(R.string.key_openapsama_max_daily_safety_multiplier, 3.0)
val maxFromDaily = floor(profile.getMaxDailyBasal() * maxBasalFromDaily * 100) / 100 val maxFromDaily = floor(profile.getMaxDailyBasal() * maxBasalFromDaily * 100) / 100
absoluteRate.setIfSmaller(aapsLogger, maxFromDaily, String.format(rh.gs(R.string.limitingbasalratio), maxFromDaily, rh.gs(R.string.maxdailybasalmultiplier)), this) absoluteRate.setIfSmaller(aapsLogger, maxFromDaily,rh.gs(R.string.limitingbasalratio, maxFromDaily, rh.gs(R.string.maxdailybasalmultiplier)), this)
} }
absoluteRate.setIfSmaller(aapsLogger, hardLimits.maxBasal(), String.format(rh.gs(R.string.limitingbasalratio), hardLimits.maxBasal(), rh.gs(R.string.hardlimit)), this) absoluteRate.setIfSmaller(aapsLogger, hardLimits.maxBasal(),rh.gs(R.string.limitingbasalratio, hardLimits.maxBasal(), rh.gs(R.string.hardlimit)), this)
val pump = activePlugin.activePump val pump = activePlugin.activePump
// check for pump max // check for pump max
if (pump.pumpDescription.tempBasalStyle == PumpDescription.ABSOLUTE) { if (pump.pumpDescription.tempBasalStyle == PumpDescription.ABSOLUTE) {
val pumpLimit = pump.pumpDescription.pumpType.tbrSettings?.maxDose ?: 0.0 val pumpLimit = pump.pumpDescription.pumpType.tbrSettings?.maxDose ?: 0.0
absoluteRate.setIfSmaller(aapsLogger, pumpLimit, String.format(rh.gs(R.string.limitingbasalratio), pumpLimit, rh.gs(R.string.pumplimit)), this) absoluteRate.setIfSmaller(aapsLogger, pumpLimit, rh.gs(R.string.limitingbasalratio, pumpLimit, rh.gs(R.string.pumplimit)), this)
} }
// do rounding // do rounding
@ -151,19 +151,19 @@ class SafetyPlugin @Inject constructor(
val pump = activePlugin.activePump val pump = activePlugin.activePump
var percentRateAfterConst = java.lang.Double.valueOf(absoluteConstraint.value() / currentBasal * 100).toInt() var percentRateAfterConst = java.lang.Double.valueOf(absoluteConstraint.value() / currentBasal * 100).toInt()
percentRateAfterConst = if (percentRateAfterConst < 100) Round.ceilTo(percentRateAfterConst.toDouble(), pump.pumpDescription.tempPercentStep.toDouble()).toInt() else Round.floorTo(percentRateAfterConst.toDouble(), pump.pumpDescription.tempPercentStep.toDouble()).toInt() percentRateAfterConst = if (percentRateAfterConst < 100) Round.ceilTo(percentRateAfterConst.toDouble(), pump.pumpDescription.tempPercentStep.toDouble()).toInt() else Round.floorTo(percentRateAfterConst.toDouble(), pump.pumpDescription.tempPercentStep.toDouble()).toInt()
percentRate.set(aapsLogger, percentRateAfterConst, String.format(rh.gs(R.string.limitingpercentrate), percentRateAfterConst, rh.gs(R.string.pumplimit)), this) percentRate.set(aapsLogger, percentRateAfterConst, rh.gs(R.string.limitingpercentrate, percentRateAfterConst, rh.gs(R.string.pumplimit)), this)
if (pump.pumpDescription.tempBasalStyle == PumpDescription.PERCENT) { if (pump.pumpDescription.tempBasalStyle == PumpDescription.PERCENT) {
val pumpLimit = pump.pumpDescription.pumpType.tbrSettings?.maxDose ?: 0.0 val pumpLimit = pump.pumpDescription.pumpType.tbrSettings?.maxDose ?: 0.0
percentRate.setIfSmaller(aapsLogger, pumpLimit.toInt(), String.format(rh.gs(R.string.limitingbasalratio), pumpLimit, rh.gs(R.string.pumplimit)), this) percentRate.setIfSmaller(aapsLogger, pumpLimit.toInt(), rh.gs(R.string.limitingbasalratio, pumpLimit, rh.gs(R.string.pumplimit)), this)
} }
return percentRate return percentRate
} }
override fun applyBolusConstraints(insulin: Constraint<Double>): Constraint<Double> { override fun applyBolusConstraints(insulin: Constraint<Double>): Constraint<Double> {
insulin.setIfGreater(aapsLogger, 0.0, String.format(rh.gs(R.string.limitingbolus), 0.0, rh.gs(R.string.itmustbepositivevalue)), this) insulin.setIfGreater(aapsLogger, 0.0, rh.gs(R.string.limitingbolus, 0.0, rh.gs(R.string.itmustbepositivevalue)), this)
val maxBolus = sp.getDouble(R.string.key_treatmentssafety_maxbolus, 3.0) val maxBolus = sp.getDouble(R.string.key_treatmentssafety_maxbolus, 3.0)
insulin.setIfSmaller(aapsLogger, maxBolus, String.format(rh.gs(R.string.limitingbolus), maxBolus, rh.gs(R.string.maxvalueinpreferences)), this) insulin.setIfSmaller(aapsLogger, maxBolus, rh.gs(R.string.limitingbolus, maxBolus, rh.gs(R.string.maxvalueinpreferences)), this)
insulin.setIfSmaller(aapsLogger, hardLimits.maxBolus(), String.format(rh.gs(R.string.limitingbolus), hardLimits.maxBolus(), rh.gs(R.string.hardlimit)), this) insulin.setIfSmaller(aapsLogger, hardLimits.maxBolus(), rh.gs(R.string.limitingbolus, hardLimits.maxBolus(), rh.gs(R.string.hardlimit)), this)
val pump = activePlugin.activePump val pump = activePlugin.activePump
val rounded = pump.pumpDescription.pumpType.determineCorrectBolusSize(insulin.value()) val rounded = pump.pumpDescription.pumpType.determineCorrectBolusSize(insulin.value())
insulin.setIfDifferent(aapsLogger, rounded, rh.gs(R.string.pumplimit), this) insulin.setIfDifferent(aapsLogger, rounded, rh.gs(R.string.pumplimit), this)
@ -171,10 +171,10 @@ class SafetyPlugin @Inject constructor(
} }
override fun applyExtendedBolusConstraints(insulin: Constraint<Double>): Constraint<Double> { override fun applyExtendedBolusConstraints(insulin: Constraint<Double>): Constraint<Double> {
insulin.setIfGreater(aapsLogger, 0.0, String.format(rh.gs(R.string.limitingextendedbolus), 0.0, rh.gs(R.string.itmustbepositivevalue)), this) insulin.setIfGreater(aapsLogger, 0.0, rh.gs(R.string.limitingextendedbolus, 0.0, rh.gs(R.string.itmustbepositivevalue)), this)
val maxBolus = sp.getDouble(R.string.key_treatmentssafety_maxbolus, 3.0) val maxBolus = sp.getDouble(R.string.key_treatmentssafety_maxbolus, 3.0)
insulin.setIfSmaller(aapsLogger, maxBolus, String.format(rh.gs(R.string.limitingextendedbolus), maxBolus, rh.gs(R.string.maxvalueinpreferences)), this) insulin.setIfSmaller(aapsLogger, maxBolus, rh.gs(R.string.limitingextendedbolus, maxBolus, rh.gs(R.string.maxvalueinpreferences)), this)
insulin.setIfSmaller(aapsLogger, hardLimits.maxBolus(), String.format(rh.gs(R.string.limitingextendedbolus), hardLimits.maxBolus(), rh.gs(R.string.hardlimit)), this) insulin.setIfSmaller(aapsLogger, hardLimits.maxBolus(), rh.gs(R.string.limitingextendedbolus, hardLimits.maxBolus(), rh.gs(R.string.hardlimit)), this)
val pump = activePlugin.activePump val pump = activePlugin.activePump
val rounded = pump.pumpDescription.pumpType.determineCorrectExtendedBolusSize(insulin.value()) val rounded = pump.pumpDescription.pumpType.determineCorrectExtendedBolusSize(insulin.value())
insulin.setIfDifferent(aapsLogger, rounded, rh.gs(R.string.pumplimit), this) insulin.setIfDifferent(aapsLogger, rounded, rh.gs(R.string.pumplimit), this)
@ -182,9 +182,9 @@ class SafetyPlugin @Inject constructor(
} }
override fun applyCarbsConstraints(carbs: Constraint<Int>): Constraint<Int> { override fun applyCarbsConstraints(carbs: Constraint<Int>): Constraint<Int> {
carbs.setIfGreater(aapsLogger, 0, String.format(rh.gs(R.string.limitingcarbs), 0, rh.gs(R.string.itmustbepositivevalue)), this) carbs.setIfGreater(aapsLogger, 0, rh.gs(R.string.limitingcarbs, 0, rh.gs(R.string.itmustbepositivevalue)), this)
val maxCarbs = sp.getInt(R.string.key_treatmentssafety_maxcarbs, 48) val maxCarbs = sp.getInt(R.string.key_treatmentssafety_maxcarbs, 48)
carbs.setIfSmaller(aapsLogger, maxCarbs, String.format(rh.gs(R.string.limitingcarbs), maxCarbs, rh.gs(R.string.maxvalueinpreferences)), this) carbs.setIfSmaller(aapsLogger, maxCarbs, rh.gs(R.string.limitingcarbs, maxCarbs, rh.gs(R.string.maxvalueinpreferences)), this)
return carbs return carbs
} }
@ -192,11 +192,11 @@ class SafetyPlugin @Inject constructor(
val apsMode = sp.getString(R.string.key_aps_mode, "open") val apsMode = sp.getString(R.string.key_aps_mode, "open")
val maxIobPref: Double = if (openAPSSMBPlugin.isEnabled() || OpenAPSSMBDynamicISFPlugin.isEnabled()) sp.getDouble(R.string.key_openapssmb_max_iob, 3.0) else sp.getDouble(R.string val maxIobPref: Double = if (openAPSSMBPlugin.isEnabled() || OpenAPSSMBDynamicISFPlugin.isEnabled()) sp.getDouble(R.string.key_openapssmb_max_iob, 3.0) else sp.getDouble(R.string
.key_openapsma_max_iob, 1.5) .key_openapsma_max_iob, 1.5)
maxIob.setIfSmaller(aapsLogger, maxIobPref, String.format(rh.gs(R.string.limitingiob), maxIobPref, rh.gs(R.string.maxvalueinpreferences)), this) maxIob.setIfSmaller(aapsLogger, maxIobPref, rh.gs(R.string.limitingiob, maxIobPref, rh.gs(R.string.maxvalueinpreferences)), this)
if (openAPSAMAPlugin.isEnabled()) maxIob.setIfSmaller(aapsLogger, hardLimits.maxIobAMA(), String.format(rh.gs(R.string.limitingiob), hardLimits.maxIobAMA(), rh.gs(R.string.hardlimit)), this) if (openAPSAMAPlugin.isEnabled()) maxIob.setIfSmaller(aapsLogger, hardLimits.maxIobAMA(), rh.gs(R.string.limitingiob, hardLimits.maxIobAMA(), rh.gs(R.string.hardlimit)), this)
if (openAPSSMBPlugin.isEnabled()) maxIob.setIfSmaller(aapsLogger, hardLimits.maxIobSMB(), String.format(rh.gs(R.string.limitingiob), hardLimits.maxIobSMB(), rh.gs(R.string.hardlimit)), this) if (openAPSSMBPlugin.isEnabled()) maxIob.setIfSmaller(aapsLogger, hardLimits.maxIobSMB(), rh.gs(R.string.limitingiob, hardLimits.maxIobSMB(), rh.gs(R.string.hardlimit)), this)
if (OpenAPSSMBDynamicISFPlugin.isEnabled()) maxIob.setIfSmaller(aapsLogger, hardLimits.maxIobSMB(), String.format(rh.gs(R.string.limitingiob), hardLimits.maxIobSMB(), rh.gs(R.string.hardlimit)), this) if (OpenAPSSMBDynamicISFPlugin.isEnabled()) maxIob.setIfSmaller(aapsLogger, hardLimits.maxIobSMB(), rh.gs(R.string.limitingiob, hardLimits.maxIobSMB(), rh.gs(R.string.hardlimit)), this)
if (apsMode == "lgs") maxIob.setIfSmaller(aapsLogger, HardLimits.MAX_IOB_LGS, String.format(rh.gs(R.string.limitingiob), HardLimits.MAX_IOB_LGS, rh.gs(R.string.lowglucosesuspend)), this) if (apsMode == "lgs") maxIob.setIfSmaller(aapsLogger, HardLimits.MAX_IOB_LGS, rh.gs(R.string.limitingiob, HardLimits.MAX_IOB_LGS, rh.gs(R.string.lowglucosesuspend)), this)
return maxIob return maxIob
} }

View file

@ -8,10 +8,8 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import dagger.android.support.DaggerFragment import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.ErrorHelperActivity import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.activities.HistoryBrowseActivity import info.nightscout.androidaps.activities.HistoryBrowseActivity
@ -20,7 +18,7 @@ import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.ValueWrapper import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.diaconn.DiaconnG8Plugin import info.nightscout.androidaps.databinding.ActionsFragmentBinding
import info.nightscout.androidaps.dialogs.* import info.nightscout.androidaps.dialogs.*
import info.nightscout.androidaps.events.EventCustomActionsChanged import info.nightscout.androidaps.events.EventCustomActionsChanged
import info.nightscout.androidaps.events.EventExtendedBolusChange import info.nightscout.androidaps.events.EventExtendedBolusChange
@ -36,7 +34,6 @@ import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction import info.nightscout.androidaps.plugins.general.actions.defs.CustomAction
import info.nightscout.androidaps.plugins.general.overview.StatusLightHandler import info.nightscout.androidaps.plugins.general.overview.StatusLightHandler
import info.nightscout.androidaps.plugins.pump.omnipod.eros.OmnipodErosPumpPlugin
import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.skins.SkinProvider import info.nightscout.androidaps.skins.SkinProvider
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
@ -81,88 +78,47 @@ class ActionsFragment : DaggerFragment() {
private val pumpCustomActions = HashMap<String, CustomAction>() private val pumpCustomActions = HashMap<String, CustomAction>()
private val pumpCustomButtons = ArrayList<SingleClickButton>() private val pumpCustomButtons = ArrayList<SingleClickButton>()
private var smallWidth = false
private var smallHeight = false
private lateinit var dm: DisplayMetrics private lateinit var dm: DisplayMetrics
private var buttonsLayout: LinearLayout? = null private var _binding: ActionsFragmentBinding? = null
private var profileSwitch: SingleClickButton? = null // This property is only valid between onCreateView and onDestroyView.
private var tempTarget: SingleClickButton? = null private val binding get() = _binding!!
private var extendedBolus: SingleClickButton? = null
private var extendedBolusCancel: SingleClickButton? = null
private var setTempBasal: SingleClickButton? = null
private var cancelTempBasal: SingleClickButton? = null
private var fill: SingleClickButton? = null
private var historyBrowser: SingleClickButton? = null
private var tddStats: SingleClickButton? = null
private var pumpBatteryChange: SingleClickButton? = null
private var cannulaAge: TextView? = null override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
private var insulinAge: TextView? = null
private var reservoirLevel: TextView? = null
private var sensorAge: TextView? = null
private var sensorLevel: TextView? = null
private var pbAge: TextView? = null
private var batteryLevel: TextView? = null
private var sensorLevelLabel: TextView? = null
private var insulinLevelLabel: TextView? = null
private var pbLevelLabel: TextView? = null
private var cannulaOrPatch: TextView? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
//check screen width //check screen width
dm = DisplayMetrics() dm = DisplayMetrics()
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) {
@Suppress("DEPRECATION")
activity?.display?.getRealMetrics(dm) activity?.display?.getRealMetrics(dm)
else } else {
@Suppress("DEPRECATION") activity?.windowManager?.defaultDisplay?.getMetrics(dm) @Suppress("DEPRECATION") activity?.windowManager?.defaultDisplay?.getMetrics(dm)
}
val screenWidth = dm.widthPixels _binding = ActionsFragmentBinding.inflate(inflater, container, false)
val screenHeight = dm.heightPixels return binding.root
smallWidth = screenWidth <= Constants.SMALL_WIDTH
smallHeight = screenHeight <= Constants.SMALL_HEIGHT
val landscape = screenHeight < screenWidth
return inflater.inflate(skinProvider.activeSkin().actionsLayout(landscape, smallWidth), container, false)
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
buttonsLayout = view.findViewById(R.id.action_buttons_layout) skinProvider.activeSkin().preProcessLandscapeActionsLayout(dm, binding)
profileSwitch = view.findViewById(R.id.actions_profileswitch)
tempTarget = view.findViewById(R.id.actions_temptarget)
extendedBolus = view.findViewById(R.id.actions_extendedbolus)
extendedBolusCancel = view.findViewById(R.id.actions_extendedbolus_cancel)
setTempBasal = view.findViewById(R.id.actions_settempbasal)
cancelTempBasal = view.findViewById(R.id.actions_canceltempbasal)
fill = view.findViewById(R.id.actions_fill)
historyBrowser = view.findViewById(R.id.actions_historybrowser)
tddStats = view.findViewById(R.id.actions_tddstats)
pumpBatteryChange = view.findViewById(R.id.actions_pumpbatterychange)
cannulaAge = view.findViewById(R.id.cannula_age) binding.profileSwitch.setOnClickListener {
insulinAge = view.findViewById(R.id.insulin_age) activity?.let { activity ->
reservoirLevel = view.findViewById(R.id.reservoir_level) protectionCheck.queryProtection(
sensorAge = view.findViewById(R.id.sensor_age) activity,
sensorLevel = view.findViewById(R.id.sensor_level) ProtectionCheck.Protection.BOLUS,
pbAge = view.findViewById(R.id.pb_age) UIRunnable { ProfileSwitchDialog().show(childFragmentManager, "ProfileSwitchDialog")})
batteryLevel = view.findViewById(R.id.battery_level) }
sensorLevelLabel = view.findViewById(R.id.sensor_level_label)
insulinLevelLabel = view.findViewById(R.id.insulin_level_label)
pbLevelLabel = view.findViewById(R.id.pb_level_label)
cannulaOrPatch = view.findViewById(R.id.cannula_or_patch)
profileSwitch?.setOnClickListener {
ProfileSwitchDialog().show(childFragmentManager, "ProfileSwitchDialog")
} }
tempTarget?.setOnClickListener { binding.tempTarget.setOnClickListener {
TempTargetDialog().show(childFragmentManager, "Actions") activity?.let { activity ->
protectionCheck.queryProtection(
activity,
ProtectionCheck.Protection.BOLUS,
UIRunnable { TempTargetDialog().show(childFragmentManager, "Actions") })
}
} }
extendedBolus?.setOnClickListener { binding.extendedBolus.setOnClickListener {
activity?.let { activity -> activity?.let { activity ->
protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable { protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable {
OKDialog.showConfirmation( OKDialog.showConfirmation(
@ -174,7 +130,7 @@ class ActionsFragment : DaggerFragment() {
}) })
} }
} }
extendedBolusCancel?.setOnClickListener { binding.extendedBolusCancel.setOnClickListener {
if (iobCobCalculator.getExtendedBolus(dateUtil.now()) != null) { if (iobCobCalculator.getExtendedBolus(dateUtil.now()) != null) {
uel.log(Action.CANCEL_EXTENDED_BOLUS, Sources.Actions) uel.log(Action.CANCEL_EXTENDED_BOLUS, Sources.Actions)
commandQueue.cancelExtended(object : Callback() { commandQueue.cancelExtended(object : Callback() {
@ -186,10 +142,15 @@ class ActionsFragment : DaggerFragment() {
}) })
} }
} }
setTempBasal?.setOnClickListener { binding.setTempBasal.setOnClickListener {
TempBasalDialog().show(childFragmentManager, "Actions") activity?.let { activity ->
protectionCheck.queryProtection(
activity,
ProtectionCheck.Protection.BOLUS,
UIRunnable { TempBasalDialog().show(childFragmentManager, "Actions") })
}
} }
cancelTempBasal?.setOnClickListener { binding.cancelTempBasal.setOnClickListener {
if (iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now()) != null) { if (iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now()) != null) {
uel.log(Action.CANCEL_TEMP_BASAL, Sources.Actions) uel.log(Action.CANCEL_TEMP_BASAL, Sources.Actions)
commandQueue.cancelTempBasal(true, object : Callback() { commandQueue.cancelTempBasal(true, object : Callback() {
@ -201,32 +162,32 @@ class ActionsFragment : DaggerFragment() {
}) })
} }
} }
fill?.setOnClickListener { binding.fill.setOnClickListener {
activity?.let { activity -> activity?.let { activity ->
protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable { FillDialog().show(childFragmentManager, "FillDialog") }) protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable { FillDialog().show(childFragmentManager, "FillDialog") })
} }
} }
historyBrowser?.setOnClickListener { startActivity(Intent(context, HistoryBrowseActivity::class.java)) } binding.historyBrowser.setOnClickListener { startActivity(Intent(context, HistoryBrowseActivity::class.java)) }
tddStats?.setOnClickListener { startActivity(Intent(context, TDDStatsActivity::class.java)) } binding.tddStats.setOnClickListener { startActivity(Intent(context, TDDStatsActivity::class.java)) }
view.findViewById<SingleClickButton>(R.id.actions_bgcheck).setOnClickListener { binding.bgCheck.setOnClickListener {
CareDialog().setOptions(CareDialog.EventType.BGCHECK, R.string.careportal_bgcheck).show(childFragmentManager, "Actions") CareDialog().setOptions(CareDialog.EventType.BGCHECK, R.string.careportal_bgcheck).show(childFragmentManager, "Actions")
} }
view.findViewById<SingleClickButton>(R.id.actions_cgmsensorinsert).setOnClickListener { binding.cgmSensorInsert.setOnClickListener {
CareDialog().setOptions(CareDialog.EventType.SENSOR_INSERT, R.string.careportal_cgmsensorinsert).show(childFragmentManager, "Actions") CareDialog().setOptions(CareDialog.EventType.SENSOR_INSERT, R.string.careportal_cgmsensorinsert).show(childFragmentManager, "Actions")
} }
pumpBatteryChange?.setOnClickListener { binding.pumpBatteryChange.setOnClickListener {
CareDialog().setOptions(CareDialog.EventType.BATTERY_CHANGE, R.string.careportal_pumpbatterychange).show(childFragmentManager, "Actions") CareDialog().setOptions(CareDialog.EventType.BATTERY_CHANGE, R.string.careportal_pumpbatterychange).show(childFragmentManager, "Actions")
} }
view.findViewById<SingleClickButton>(R.id.actions_note).setOnClickListener { binding.note.setOnClickListener {
CareDialog().setOptions(CareDialog.EventType.NOTE, R.string.careportal_note).show(childFragmentManager, "Actions") CareDialog().setOptions(CareDialog.EventType.NOTE, R.string.careportal_note).show(childFragmentManager, "Actions")
} }
view.findViewById<SingleClickButton>(R.id.actions_exercise).setOnClickListener { binding.exercise.setOnClickListener {
CareDialog().setOptions(CareDialog.EventType.EXERCISE, R.string.careportal_exercise).show(childFragmentManager, "Actions") CareDialog().setOptions(CareDialog.EventType.EXERCISE, R.string.careportal_exercise).show(childFragmentManager, "Actions")
} }
view.findViewById<SingleClickButton>(R.id.actions_question).setOnClickListener { binding.question.setOnClickListener {
CareDialog().setOptions(CareDialog.EventType.QUESTION, R.string.careportal_question).show(childFragmentManager, "Actions") CareDialog().setOptions(CareDialog.EventType.QUESTION, R.string.careportal_question).show(childFragmentManager, "Actions")
} }
view.findViewById<SingleClickButton>(R.id.actions_announcement).setOnClickListener { binding.announcement.setOnClickListener {
CareDialog().setOptions(CareDialog.EventType.ANNOUNCEMENT, R.string.careportal_announcement).show(childFragmentManager, "Actions") CareDialog().setOptions(CareDialog.EventType.ANNOUNCEMENT, R.string.careportal_announcement).show(childFragmentManager, "Actions")
} }
@ -265,13 +226,18 @@ class ActionsFragment : DaggerFragment() {
disposable.clear() disposable.clear()
} }
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
@Synchronized @Synchronized
fun updateGui() { fun updateGui() {
val profile = profileFunction.getProfile() val profile = profileFunction.getProfile()
val pump = activePlugin.activePump val pump = activePlugin.activePump
profileSwitch?.visibility = ( binding.profileSwitch.visibility = (
activePlugin.activeProfileSource.profile != null && activePlugin.activeProfileSource.profile != null &&
pump.pumpDescription.isSetBasalProfileCapable && pump.pumpDescription.isSetBasalProfileCapable &&
pump.isInitialized() && pump.isInitialized() &&
@ -279,60 +245,58 @@ class ActionsFragment : DaggerFragment() {
!loop.isDisconnected).toVisibility() !loop.isDisconnected).toVisibility()
if (!pump.pumpDescription.isExtendedBolusCapable || !pump.isInitialized() || pump.isSuspended() || loop.isDisconnected || pump.isFakingTempsByExtendedBoluses || config.NSCLIENT) { if (!pump.pumpDescription.isExtendedBolusCapable || !pump.isInitialized() || pump.isSuspended() || loop.isDisconnected || pump.isFakingTempsByExtendedBoluses || config.NSCLIENT) {
extendedBolus?.visibility = View.GONE binding.extendedBolus.visibility = View.GONE
extendedBolusCancel?.visibility = View.GONE binding.extendedBolusCancel.visibility = View.GONE
} else { } else {
val activeExtendedBolus = repository.getExtendedBolusActiveAt(dateUtil.now()).blockingGet() val activeExtendedBolus = repository.getExtendedBolusActiveAt(dateUtil.now()).blockingGet()
if (activeExtendedBolus is ValueWrapper.Existing) { if (activeExtendedBolus is ValueWrapper.Existing) {
extendedBolus?.visibility = View.GONE binding.extendedBolus.visibility = View.GONE
extendedBolusCancel?.visibility = View.VISIBLE binding.extendedBolusCancel.visibility = View.VISIBLE
@Suppress("SetTextI18n") @Suppress("SetTextI18n")
extendedBolusCancel?.text = rh.gs(R.string.cancel) + " " + activeExtendedBolus.value.toStringMedium(dateUtil) binding.extendedBolusCancel.text = rh.gs(R.string.cancel) + " " + activeExtendedBolus.value.toStringMedium(dateUtil)
} else { } else {
extendedBolus?.visibility = View.VISIBLE binding.extendedBolus.visibility = View.VISIBLE
extendedBolusCancel?.visibility = View.GONE binding.extendedBolusCancel.visibility = View.GONE
} }
} }
if (!pump.pumpDescription.isTempBasalCapable || !pump.isInitialized() || pump.isSuspended() || loop.isDisconnected || config.NSCLIENT) { if (!pump.pumpDescription.isTempBasalCapable || !pump.isInitialized() || pump.isSuspended() || loop.isDisconnected || config.NSCLIENT) {
setTempBasal?.visibility = View.GONE binding.setTempBasal.visibility = View.GONE
cancelTempBasal?.visibility = View.GONE binding.cancelTempBasal.visibility = View.GONE
} else { } else {
val activeTemp = iobCobCalculator.getTempBasalIncludingConvertedExtended(System.currentTimeMillis()) val activeTemp = iobCobCalculator.getTempBasalIncludingConvertedExtended(System.currentTimeMillis())
if (activeTemp != null) { if (activeTemp != null) {
setTempBasal?.visibility = View.GONE binding.setTempBasal.visibility = View.GONE
cancelTempBasal?.visibility = View.VISIBLE binding.cancelTempBasal.visibility = View.VISIBLE
@Suppress("SetTextI18n") @Suppress("SetTextI18n")
cancelTempBasal?.text = rh.gs(R.string.cancel) + " " + activeTemp.toStringShort() binding.cancelTempBasal.text = rh.gs(R.string.cancel) + " " + activeTemp.toStringShort()
} else { } else {
setTempBasal?.visibility = View.VISIBLE binding.setTempBasal.visibility = View.VISIBLE
cancelTempBasal?.visibility = View.GONE binding.cancelTempBasal.visibility = View.GONE
} }
} }
val activeBgSource = activePlugin.activeBgSource val activeBgSource = activePlugin.activeBgSource
historyBrowser?.visibility = (profile != null).toVisibility() binding.historyBrowser.visibility = (profile != null).toVisibility()
fill?.visibility = (pump.pumpDescription.isRefillingCapable && pump.isInitialized() && !pump.isSuspended()).toVisibility() binding.fill.visibility = (pump.pumpDescription.isRefillingCapable && pump.isInitialized() && !pump.isSuspended()).toVisibility()
if (pump is DiaconnG8Plugin) { binding.pumpBatteryChange.visibility = (pump.pumpDescription.isBatteryReplaceable || pump.isBatteryChangeLoggingEnabled()).toVisibility()
pumpBatteryChange?.visibility = (pump.pumpDescription.isBatteryReplaceable && !pump.isBatteryChangeLoggingEnabled()).toVisibility() binding.tempTarget.visibility = (profile != null && !loop.isDisconnected).toVisibility()
} else { binding.tddStats.visibility = pump.pumpDescription.supportsTDDs.toVisibility()
pumpBatteryChange?.visibility = val isPatchPump = pump.pumpDescription.isPatchPump
(pump.pumpDescription.isBatteryReplaceable || (pump is OmnipodErosPumpPlugin && pump.isUseRileyLinkBatteryLevel && pump.isBatteryChangeLoggingEnabled)).toVisibility() binding.status.apply {
} cannulaOrPatch.text = if (isPatchPump) rh.gs(R.string.patch_pump) else rh.gs(R.string.cannula)
tempTarget?.visibility = (profile != null && !loop.isDisconnected).toVisibility() val imageResource = if (isPatchPump) R.drawable.ic_patch_pump_outline else R.drawable.ic_cp_age_cannula
tddStats?.visibility = pump.pumpDescription.supportsTDDs.toVisibility() cannulaOrPatch.setCompoundDrawablesWithIntrinsicBounds(imageResource, 0, 0, 0)
batteryLayout.visibility = (!isPatchPump || pump.pumpDescription.useHardwareLink).toVisibility()
cannulaOrPatch?.text = if (pump.pumpDescription.isPatchPump) rh.gs(R.string.patch_pump) else rh.gs(R.string.cannula) if (!config.NSCLIENT) {
val imageResource = if (pump.pumpDescription.isPatchPump) R.drawable.ic_patch_pump_outline else R.drawable.ic_cp_age_cannula statusLightHandler.updateStatusLights(cannulaAge, insulinAge, reservoirLevel, sensorAge, sensorLevel, pbAge, batteryLevel)
cannulaOrPatch?.setCompoundDrawablesWithIntrinsicBounds(imageResource, 0, 0, 0) sensorLevelLabel.text = if (activeBgSource.sensorBatteryLevel == -1) "" else rh.gs(R.string.careportal_level_label)
} else {
if (!config.NSCLIENT) { statusLightHandler.updateStatusLights(cannulaAge, insulinAge, null, sensorAge, null, pbAge, null)
statusLightHandler.updateStatusLights(cannulaAge, insulinAge, reservoirLevel, sensorAge, sensorLevel, pbAge, batteryLevel) sensorLevelLabel.text = ""
sensorLevelLabel?.text = if (activeBgSource.sensorBatteryLevel == -1) "" else rh.gs(R.string.careportal_level_label) insulinLevelLabel.text = ""
} else { pbLevelLabel.text = ""
statusLightHandler.updateStatusLights(cannulaAge, insulinAge, null, sensorAge, null, pbAge, null) }
sensorLevelLabel?.text = ""
insulinLevelLabel?.text = ""
pbLevelLabel?.text = ""
} }
checkPumpCustomActions() checkPumpCustomActions()
@ -365,7 +329,7 @@ class ActionsFragment : DaggerFragment() {
val top = activity?.let { ContextCompat.getDrawable(it, customAction.iconResourceId) } val top = activity?.let { ContextCompat.getDrawable(it, customAction.iconResourceId) }
btn.setCompoundDrawablesWithIntrinsicBounds(null, top, null, null) btn.setCompoundDrawablesWithIntrinsicBounds(null, top, null, null)
buttonsLayout?.addView(btn) binding.buttonsLayout.addView(btn)
this.pumpCustomActions[rh.gs(customAction.name)] = customAction this.pumpCustomActions[rh.gs(customAction.name)] = customAction
this.pumpCustomButtons.add(btn) this.pumpCustomButtons.add(btn)
@ -373,7 +337,7 @@ class ActionsFragment : DaggerFragment() {
} }
private fun removePumpCustomActions() { private fun removePumpCustomActions() {
for (customButton in pumpCustomButtons) buttonsLayout?.removeView(customButton) for (customButton in pumpCustomButtons) binding.buttonsLayout.removeView(customButton)
pumpCustomButtons.clear() pumpCustomButtons.clear()
} }
} }

View file

@ -6,12 +6,11 @@ import android.content.pm.ResolveInfo
import android.os.Bundle import android.os.Bundle
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.events.* import info.nightscout.androidaps.events.Event
import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.extensions.durationInMinutes import info.nightscout.androidaps.extensions.durationInMinutes
import info.nightscout.androidaps.extensions.toStringFull import info.nightscout.androidaps.extensions.toStringFull
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui
import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.nsclient.data.DeviceStatusData import info.nightscout.androidaps.plugins.general.nsclient.data.DeviceStatusData
@ -25,6 +24,8 @@ import info.nightscout.androidaps.utils.DefaultValueHelper
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -68,26 +69,6 @@ class DataBroadcastPlugin @Inject constructor(
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ sendData(it) }, fabricPrivacy::logException) .subscribe({ sendData(it) }, fabricPrivacy::logException)
) )
disposable.add(rxBus
.toObservable(EventExtendedBolusChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ sendData(it) }, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(EventTempBasalChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ sendData(it) }, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(EventTreatmentChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ sendData(it) }, fabricPrivacy::logException)
)
disposable.add(rxBus
.toObservable(EventEffectiveProfileSwitchChanged::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ sendData(it) }, fabricPrivacy::logException)
)
disposable.add(rxBus disposable.add(rxBus
.toObservable(EventAutosensCalculationFinished::class.java) .toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.plugins.general.food package info.nightscout.androidaps.plugins.general.food
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.graphics.Paint
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
@ -24,18 +23,18 @@ import info.nightscout.androidaps.databinding.FoodFragmentBinding
import info.nightscout.androidaps.databinding.FoodItemBinding import info.nightscout.androidaps.databinding.FoodItemBinding
import info.nightscout.androidaps.dialogs.WizardDialog import info.nightscout.androidaps.dialogs.WizardDialog
import info.nightscout.androidaps.events.EventFoodDatabaseChanged import info.nightscout.androidaps.events.EventFoodDatabaseChanged
import info.nightscout.shared.logging.AAPSLogger import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientRestart
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.ui.UIRunnable import info.nightscout.androidaps.utils.ui.UIRunnable
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import io.reactivex.rxjava3.core.Completable import io.reactivex.rxjava3.core.Completable
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
@ -43,7 +42,6 @@ import io.reactivex.rxjava3.kotlin.subscribeBy
import java.util.* import java.util.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
import kotlin.collections.ArrayList
class FoodFragment : DaggerFragment() { class FoodFragment : DaggerFragment() {
@ -66,10 +64,8 @@ class FoodFragment : DaggerFragment() {
// onDestroyView. // onDestroyView.
private val binding get() = _binding!! private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
_binding = FoodFragmentBinding.inflate(inflater, container, false) FoodFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -80,8 +76,10 @@ class FoodFragment : DaggerFragment() {
binding.refreshFromNightscout.setOnClickListener { binding.refreshFromNightscout.setOnClickListener {
context?.let { context -> context?.let { context ->
OKDialog.showConfirmation(context, rh.gs(R.string.refresheventsfromnightscout) + " ?", { OKDialog.showConfirmation(context, rh.gs(R.string.refresheventsfromnightscout) + " ?", {
uel.log(Action.FOOD, Sources.Food, rh.gs(R.string.refresheventsfromnightscout), uel.log(
ValueWithUnit.SimpleString(rh.gsNotLocalised(R.string.refresheventsfromnightscout))) Action.FOOD, Sources.Food, rh.gs(R.string.refresheventsfromnightscout),
ValueWithUnit.SimpleString(rh.gsNotLocalised(R.string.refresheventsfromnightscout))
)
disposable += Completable.fromAction { repository.deleteAllFoods() } disposable += Completable.fromAction { repository.deleteAllFoods() }
.subscribeOn(aapsSchedulers.io) .subscribeOn(aapsSchedulers.io)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
@ -95,32 +93,14 @@ class FoodFragment : DaggerFragment() {
} }
} }
binding.clearfilter.setOnClickListener { binding.filterinputLayout.setEndIconOnClickListener {
binding.filter.setText("") binding.filter.setText("")
binding.category.setSelection(0) binding.categoryList.setText(rh.gs(R.string.none), false)
binding.subcategory.setSelection(0) binding.subcategoryList.setText(rh.gs(R.string.none), false)
filterData() filterData()
} }
binding.category.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { binding.categoryList.onItemClickListener = AdapterView.OnItemClickListener { _, _, _, _ -> fillSubcategories(); filterData() }
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { binding.subcategoryList.onItemClickListener = AdapterView.OnItemClickListener { _, _, _, _ -> filterData() }
fillSubcategories()
filterData()
}
override fun onNothingSelected(parent: AdapterView<*>?) {
fillSubcategories()
filterData()
}
}
binding.subcategory.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
filterData()
}
override fun onNothingSelected(parent: AdapterView<*>?) {
filterData()
}
}
binding.filter.addTextChangedListener(object : TextWatcher { binding.filter.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
@ -134,12 +114,11 @@ class FoodFragment : DaggerFragment() {
@Synchronized @Synchronized
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
disposable.add(rxBus disposable += rxBus
.toObservable(EventFoodDatabaseChanged::class.java) .toObservable(EventFoodDatabaseChanged::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.debounce(1L, TimeUnit.SECONDS) .debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException) .subscribe({ swapAdapter() }, fabricPrivacy::logException)
)
swapAdapter() swapAdapter()
} }
@ -178,13 +157,13 @@ class FoodFragment : DaggerFragment() {
val categories = ArrayList(catSet) val categories = ArrayList(catSet)
categories.add(0, rh.gs(R.string.none)) categories.add(0, rh.gs(R.string.none))
context?.let { context -> context?.let { context ->
val adapterCategories = ArrayAdapter(context, R.layout.spinner_centered, categories) binding.categoryList.setAdapter(ArrayAdapter(context, R.layout.spinner_centered, categories))
binding.category.adapter = adapterCategories binding.categoryList.setText(rh.gs(R.string.none), false)
} }
} }
private fun fillSubcategories() { private fun fillSubcategories() {
val categoryFilter = binding.category.selectedItem.toString() val categoryFilter = binding.categoryList.text.toString()
val subCatSet: MutableSet<CharSequence> = HashSet() val subCatSet: MutableSet<CharSequence> = HashSet()
if (categoryFilter != rh.gs(R.string.none)) { if (categoryFilter != rh.gs(R.string.none)) {
for (f in unfiltered) { for (f in unfiltered) {
@ -198,17 +177,15 @@ class FoodFragment : DaggerFragment() {
val subcategories = ArrayList(subCatSet) val subcategories = ArrayList(subCatSet)
subcategories.add(0, rh.gs(R.string.none)) subcategories.add(0, rh.gs(R.string.none))
context?.let { context -> context?.let { context ->
val adapterSubcategories = ArrayAdapter(context, R.layout.spinner_centered, subcategories) binding.subcategoryList.setAdapter(ArrayAdapter(context, R.layout.spinner_centered, subcategories))
binding.subcategory.adapter = adapterSubcategories binding.subcategoryList.setText(rh.gs(R.string.none), false)
} }
} }
private fun filterData() { private fun filterData() {
val textFilter = binding.filter.text.toString() val textFilter = binding.filter.text.toString()
val categoryFilter = binding.category.selectedItem?.toString() val categoryFilter = binding.categoryList.text.toString()
?: rh.gs(R.string.none) val subcategoryFilter = binding.subcategoryList.text.toString()
val subcategoryFilter = binding.subcategory.selectedItem?.toString()
?: rh.gs(R.string.none)
val newFiltered = ArrayList<Food>() val newFiltered = ArrayList<Food>()
for (f in unfiltered) { for (f in unfiltered) {
if (f.category == null || f.subCategory == null) continue if (f.category == null || f.subCategory == null) continue
@ -267,18 +244,17 @@ class FoodFragment : DaggerFragment() {
}, null) }, null)
} }
} }
binding.icCalculator.setOnClickListener { v:View -> binding.icCalculator.setOnClickListener { v: View ->
val food = v.tag as Food val food = v.tag as Food
activity?.let { activity -> activity?.let { activity ->
protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable { protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable {
if (isAdded) { if (isAdded)
val wizardDialog = WizardDialog() WizardDialog().also { dialog ->
val bundle = Bundle() dialog.arguments = Bundle().also { bundle ->
bundle.putInt("carbs_input", food.carbs) bundle.putDouble("carbs_input", food.carbs.toDouble())
bundle.putString("notes_input", " ${food.name} - ${food.carbs}g") bundle.putString("notes_input", " ${food.name} - ${food.carbs}g")
wizardDialog.setArguments(bundle) }
wizardDialog.show(childFragmentManager, "Food Item") }.show(childFragmentManager, "Food Item")
}
}) })
} }
} }

View file

@ -7,6 +7,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import dagger.android.support.DaggerFragment import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.SingleFragmentActivity
import info.nightscout.androidaps.dana.database.DanaHistoryDatabase import info.nightscout.androidaps.dana.database.DanaHistoryDatabase
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Action
@ -14,6 +15,7 @@ import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.databinding.MaintenanceFragmentBinding import info.nightscout.androidaps.databinding.MaintenanceFragmentBinding
import info.nightscout.androidaps.diaconn.database.DiaconnHistoryDatabase import info.nightscout.androidaps.diaconn.database.DiaconnHistoryDatabase
import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.insight.database.InsightDatabase import info.nightscout.androidaps.insight.database.InsightDatabase
import info.nightscout.androidaps.interfaces.DataSyncSelector import info.nightscout.androidaps.interfaces.DataSyncSelector
import info.nightscout.androidaps.interfaces.ImportExportPrefs import info.nightscout.androidaps.interfaces.ImportExportPrefs
@ -27,6 +29,8 @@ import info.nightscout.androidaps.plugins.general.overview.OverviewData
import info.nightscout.androidaps.plugins.pump.omnipod.dash.history.database.DashHistoryDatabase import info.nightscout.androidaps.plugins.pump.omnipod.dash.history.database.DashHistoryDatabase
import info.nightscout.androidaps.plugins.pump.omnipod.eros.history.database.ErosHistoryDatabase import info.nightscout.androidaps.plugins.pump.omnipod.eros.history.database.ErosHistoryDatabase
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.protection.ProtectionCheck.Protection.PREFERENCES
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.rxjava3.core.Completable.fromAction import io.reactivex.rxjava3.core.Completable.fromAction
@ -48,6 +52,7 @@ class MaintenanceFragment : DaggerFragment() {
@Inject lateinit var diaconnDatabase: DiaconnHistoryDatabase @Inject lateinit var diaconnDatabase: DiaconnHistoryDatabase
@Inject lateinit var erosDatabase: ErosHistoryDatabase @Inject lateinit var erosDatabase: ErosHistoryDatabase
@Inject lateinit var dashDatabase: DashHistoryDatabase @Inject lateinit var dashDatabase: DashHistoryDatabase
@Inject lateinit var protectionCheck: ProtectionCheck
@Inject lateinit var uel: UserEntryLogger @Inject lateinit var uel: UserEntryLogger
@Inject lateinit var dataSyncSelector: DataSyncSelector @Inject lateinit var dataSyncSelector: DataSyncSelector
@Inject lateinit var pumpSync: PumpSync @Inject lateinit var pumpSync: PumpSync
@ -55,11 +60,11 @@ class MaintenanceFragment : DaggerFragment() {
@Inject lateinit var overviewData: OverviewData @Inject lateinit var overviewData: OverviewData
private val compositeDisposable = CompositeDisposable() private val compositeDisposable = CompositeDisposable()
private var inMenu = false
private var queryingProtection = false
private var _binding: MaintenanceFragmentBinding? = null private var _binding: MaintenanceFragmentBinding? = null
// This property is only valid between onCreateView and // This property is only valid between onCreateView and onDestroyView.
// onDestroyView.
private val binding get() = _binding!! private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
@ -69,6 +74,9 @@ class MaintenanceFragment : DaggerFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
val parentClass = this.activity?.let { it::class.java }
inMenu = parentClass == SingleFragmentActivity::class.java
updateProtectedUi()
binding.logSend.setOnClickListener { maintenancePlugin.sendLogs() } binding.logSend.setOnClickListener { maintenancePlugin.sendLogs() }
binding.logDelete.setOnClickListener { binding.logDelete.setOnClickListener {
uel.log(Action.DELETE_LOGS, Sources.Maintenance) uel.log(Action.DELETE_LOGS, Sources.Maintenance)
@ -128,6 +136,13 @@ class MaintenanceFragment : DaggerFragment() {
} }
} }
} }
binding.unlock.setOnClickListener { queryProtection() }
}
override fun onResume() {
super.onResume()
if (inMenu) queryProtection() else updateProtectedUi()
} }
@Synchronized @Synchronized
@ -136,4 +151,21 @@ class MaintenanceFragment : DaggerFragment() {
compositeDisposable.clear() compositeDisposable.clear()
_binding = null _binding = null
} }
}
private fun updateProtectedUi() {
val isLocked = protectionCheck.isLocked(PREFERENCES)
binding.mainLayout.visibility = isLocked.not().toVisibility()
binding.unlock.visibility = isLocked.toVisibility()
}
private fun queryProtection() {
val isLocked = protectionCheck.isLocked(PREFERENCES)
if (isLocked && !queryingProtection) {
activity?.let { activity ->
queryingProtection = true
val doUpdate = { activity.runOnUiThread { queryingProtection = false; updateProtectedUi() } }
protectionCheck.queryProtection(activity, PREFERENCES, doUpdate, doUpdate, doUpdate)
}
}
}
}

View file

@ -64,8 +64,6 @@ class MaintenancePlugin @Inject constructor(
context.startActivity(emailIntent) context.startActivity(emailIntent)
} }
//todo replace this with a call on startup of the application, specifically to remove
// unnecessary garbage from the log exports
fun deleteLogs(keep: Int) { fun deleteLogs(keep: Int) {
val logDir = File(loggerUtils.logDirectory) val logDir = File(loggerUtils.logDirectory)
val files = logDir.listFiles { _: File?, name: String -> val files = logDir.listFiles { _: File?, name: String ->

View file

@ -170,8 +170,7 @@ class NSClientPlugin @Inject constructor(
override fun onServiceConnected(name: ComponentName, service: IBinder) { override fun onServiceConnected(name: ComponentName, service: IBinder) {
aapsLogger.debug(LTag.NSCLIENT, "Service is connected") aapsLogger.debug(LTag.NSCLIENT, "Service is connected")
val mLocalBinder = service as NSClientService.LocalBinder val mLocalBinder = service as NSClientService.LocalBinder?
@Suppress("UNNECESSARY_SAFE_CALL")
nsClientService = mLocalBinder?.serviceInstance // is null when running in roboelectric nsClientService = mLocalBinder?.serviceInstance // is null when running in roboelectric
} }
} }

View file

@ -190,7 +190,7 @@ class NSClientService : DaggerService() {
lastAckTime = dateUtil.now() lastAckTime = dateUtil.now()
dataWorker.enqueue( dataWorker.enqueue(
OneTimeWorkRequest.Builder(NSClientAddAckWorker::class.java) OneTimeWorkRequest.Builder(NSClientAddAckWorker::class.java)
.setInputData(dataWorker.storeInputData(ack, null)) .setInputData(dataWorker.storeInputData(ack))
.build() .build()
) )
} }
@ -199,7 +199,7 @@ class NSClientService : DaggerService() {
lastAckTime = dateUtil.now() lastAckTime = dateUtil.now()
dataWorker.enqueue( dataWorker.enqueue(
OneTimeWorkRequest.Builder(NSClientUpdateRemoveAckWorker::class.java) OneTimeWorkRequest.Builder(NSClientUpdateRemoveAckWorker::class.java)
.setInputData(dataWorker.storeInputData(ack, null)) .setInputData(dataWorker.storeInputData(ack))
.build() .build()
) )
} }
@ -483,7 +483,7 @@ class NSClientService : DaggerService() {
rxBus.send(EventNSClientNewLog("PROFILE", "profile received")) rxBus.send(EventNSClientNewLog("PROFILE", "profile received"))
dataWorker.enqueue( dataWorker.enqueue(
OneTimeWorkRequest.Builder(LocalProfilePlugin.NSProfileWorker::class.java) OneTimeWorkRequest.Builder(LocalProfilePlugin.NSProfileWorker::class.java)
.setInputData(dataWorker.storeInputData(profileStoreJson, null)) .setInputData(dataWorker.storeInputData(profileStoreJson))
.build() .build()
) )
xDripBroadcast.sendProfile(profileStoreJson) xDripBroadcast.sendProfile(profileStoreJson)
@ -502,7 +502,7 @@ class NSClientService : DaggerService() {
if (addedOrUpdatedTreatments.length() > 0) { if (addedOrUpdatedTreatments.length() > 0) {
dataWorker.enqueue( dataWorker.enqueue(
OneTimeWorkRequest.Builder(NSClientAddUpdateWorker::class.java) OneTimeWorkRequest.Builder(NSClientAddUpdateWorker::class.java)
.setInputData(dataWorker.storeInputData(addedOrUpdatedTreatments, null)) .setInputData(dataWorker.storeInputData(addedOrUpdatedTreatments))
.build() .build()
) )
xDripBroadcast.sendTreatments(addedOrUpdatedTreatments) xDripBroadcast.sendTreatments(addedOrUpdatedTreatments)
@ -520,7 +520,7 @@ class NSClientService : DaggerService() {
if (foods.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + foods.length() + " foods")) if (foods.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + foods.length() + " foods"))
dataWorker.enqueue( dataWorker.enqueue(
OneTimeWorkRequest.Builder(FoodWorker::class.java) OneTimeWorkRequest.Builder(FoodWorker::class.java)
.setInputData(dataWorker.storeInputData(foods, null)) .setInputData(dataWorker.storeInputData(foods))
.build() .build()
) )
} }
@ -529,7 +529,7 @@ class NSClientService : DaggerService() {
if (mbgArray.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + mbgArray.length() + " mbgs")) if (mbgArray.length() > 0) rxBus.send(EventNSClientNewLog("DATA", "received " + mbgArray.length() + " mbgs"))
dataWorker.enqueue( dataWorker.enqueue(
OneTimeWorkRequest.Builder(NSClientMbgWorker::class.java) OneTimeWorkRequest.Builder(NSClientMbgWorker::class.java)
.setInputData(dataWorker.storeInputData(mbgArray, null)) .setInputData(dataWorker.storeInputData(mbgArray))
.build() .build()
) )
} }
@ -546,7 +546,7 @@ class NSClientService : DaggerService() {
sp.putBoolean(R.string.key_ObjectivesbgIsAvailableInNS, true) sp.putBoolean(R.string.key_ObjectivesbgIsAvailableInNS, true)
dataWorker.enqueue( dataWorker.enqueue(
OneTimeWorkRequest.Builder(NSClientSourceWorker::class.java) OneTimeWorkRequest.Builder(NSClientSourceWorker::class.java)
.setInputData(dataWorker.storeInputData(sgvs, null)) .setInputData(dataWorker.storeInputData(sgvs))
.build() .build()
) )
xDripBroadcast.sendSgvs(sgvs) xDripBroadcast.sendSgvs(sgvs)

View file

@ -1,44 +1,42 @@
package info.nightscout.androidaps.plugins.general.overview package info.nightscout.androidaps.plugins.general.overview
import android.graphics.DashPathEffect import android.content.Context
import android.graphics.Paint import androidx.annotation.ColorInt
import com.jjoe64.graphview.series.BarGraphSeries import com.jjoe64.graphview.series.BarGraphSeries
import com.jjoe64.graphview.series.DataPoint import com.jjoe64.graphview.series.DataPoint
import com.jjoe64.graphview.series.LineGraphSeries import com.jjoe64.graphview.series.LineGraphSeries
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.IobTotal import info.nightscout.androidaps.data.IobTotal
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.ValueWrapper import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.database.entities.Bolus
import info.nightscout.androidaps.database.entities.GlucoseValue import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.database.entities.TemporaryTarget import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.database.entities.TherapyEvent import info.nightscout.androidaps.extensions.convertedToPercent
import info.nightscout.androidaps.extensions.* import info.nightscout.androidaps.extensions.isInProgress
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.extensions.toStringFull
import info.nightscout.shared.logging.AAPSLogger import info.nightscout.androidaps.extensions.toStringShort
import info.nightscout.shared.logging.LTag import info.nightscout.androidaps.extensions.valueToUnits
import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus import info.nightscout.androidaps.interfaces.IobCobCalculator
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.* import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DataPointWithLabelInterface
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.FixedLineGraphSeries
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.PointsWithLabelGraphSeries
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.Scale
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.ScaledDataPoint
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.CobInfo import info.nightscout.androidaps.plugins.iob.iobCobCalculator.CobInfo
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.* import info.nightscout.androidaps.utils.DefaultValueHelper
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
import kotlin.collections.ArrayList
import kotlin.math.abs
import kotlin.math.ceil
import kotlin.math.max
import kotlin.math.min
@Singleton @Singleton
class OverviewData @Inject constructor( class OverviewData @Inject constructor(
private val injector: HasAndroidInjector,
private val aapsLogger: AAPSLogger, private val aapsLogger: AAPSLogger,
private val rh: ResourceHelper, private val rh: ResourceHelper,
private val dateUtil: DateUtil, private val dateUtil: DateUtil,
@ -46,13 +44,7 @@ class OverviewData @Inject constructor(
private val activePlugin: ActivePlugin, private val activePlugin: ActivePlugin,
private val defaultValueHelper: DefaultValueHelper, private val defaultValueHelper: DefaultValueHelper,
private val profileFunction: ProfileFunction, private val profileFunction: ProfileFunction,
private val config: Config, private val repository: AppRepository
private val loop: Loop,
private val nsDeviceStatus: NSDeviceStatus,
private val repository: AppRepository,
private val overviewMenus: OverviewMenus,
private val iobCobCalculator: IobCobCalculator,
private val translator: Translator
) { ) {
var rangeToDisplay = 6 // for graph var rangeToDisplay = 6 // for graph
@ -62,14 +54,7 @@ class OverviewData @Inject constructor(
fun reset() { fun reset() {
pumpStatus = "" pumpStatus = ""
calcProgress = "" calcProgressPct = 100
lastBg = null
bolusIob = null
basalIob = null
cobInfo = null
lastCarbsTime = 0L
temporaryTarget = null
lastAutosensData = null
bgReadingsArray = ArrayList() bgReadingsArray = ArrayList()
bucketedGraphSeries = PointsWithLabelGraphSeries() bucketedGraphSeries = PointsWithLabelGraphSeries()
bgReadingGraphSeries = PointsWithLabelGraphSeries() bgReadingGraphSeries = PointsWithLabelGraphSeries()
@ -120,13 +105,18 @@ class OverviewData @Inject constructor(
* CALC PROGRESS * CALC PROGRESS
*/ */
var calcProgress: String = "" var calcProgressPct: Int = 100
/* /*
* BG * BG
*/ */
var lastBg: GlucoseValue? = null val lastBg: GlucoseValue?
get() =
repository.getLastGlucoseValueWrapped().blockingGet().let { gvWrapped ->
if (gvWrapped is ValueWrapper.Existing) gvWrapped.value
else null
}
val isLow: Boolean val isLow: Boolean
get() = lastBg?.let { lastBg -> get() = lastBg?.let { lastBg ->
@ -138,11 +128,12 @@ class OverviewData @Inject constructor(
lastBg.valueToUnits(profileFunction.getUnits()) > defaultValueHelper.determineHighLine() lastBg.valueToUnits(profileFunction.getUnits()) > defaultValueHelper.determineHighLine()
} ?: false } ?: false
val lastBgColor: Int @ColorInt
get() = when { fun lastBgColor(context: Context?): Int =
isLow -> rh.gc(R.color.low) when {
isHigh -> rh.gc(R.color.high) isLow -> rh.gac(context, R.attr.bgLow)
else -> rh.gc(R.color.inrange) isHigh -> rh.gac(context, R.attr.highColor)
else -> rh.gac(context, R.attr.bgInRange)
} }
val lastBgDescription: String val lastBgDescription: String
@ -162,17 +153,16 @@ class OverviewData @Inject constructor(
* TEMPORARY BASAL * TEMPORARY BASAL
*/ */
val temporaryBasalText: String fun temporaryBasalText(iobCobCalculator: IobCobCalculator): String =
get() = profileFunction.getProfile()?.let { profile ->
profileFunction.getProfile()?.let { profile -> var temporaryBasal = iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())
var temporaryBasal = iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now()) if (temporaryBasal?.isInProgress == false) temporaryBasal = null
if (temporaryBasal?.isInProgress == false) temporaryBasal = null temporaryBasal?.let { "T:" + it.toStringShort() }
temporaryBasal?.let { "T:" + it.toStringShort() } ?: rh.gs(R.string.pump_basebasalrate, profile.getBasal())
?: rh.gs(R.string.pump_basebasalrate, profile.getBasal()) } ?: rh.gs(R.string.notavailable)
} ?: rh.gs(R.string.notavailable)
val temporaryBasalDialogText: String fun temporaryBasalDialogText(iobCobCalculator: IobCobCalculator): String =
get() = profileFunction.getProfile()?.let { profile -> profileFunction.getProfile()?.let { profile ->
iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())?.let { temporaryBasal -> iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())?.let { temporaryBasal ->
"${rh.gs(R.string.basebasalrate_label)}: ${rh.gs(R.string.pump_basebasalrate, profile.getBasal())}" + "${rh.gs(R.string.basebasalrate_label)}: ${rh.gs(R.string.pump_basebasalrate, profile.getBasal())}" +
"\n" + rh.gs(R.string.tempbasal_label) + ": " + temporaryBasal.toStringFull(profile, dateUtil) "\n" + rh.gs(R.string.tempbasal_label) + ": " + temporaryBasal.toStringFull(profile, dateUtil)
@ -180,76 +170,73 @@ class OverviewData @Inject constructor(
?: "${rh.gs(R.string.basebasalrate_label)}: ${rh.gs(R.string.pump_basebasalrate, profile.getBasal())}" ?: "${rh.gs(R.string.basebasalrate_label)}: ${rh.gs(R.string.pump_basebasalrate, profile.getBasal())}"
} ?: rh.gs(R.string.notavailable) } ?: rh.gs(R.string.notavailable)
val temporaryBasalIcon: Int fun temporaryBasalIcon(iobCobCalculator: IobCobCalculator): Int =
get() = profileFunction.getProfile()?.let { profile ->
profileFunction.getProfile()?.let { profile -> iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())?.let { temporaryBasal ->
iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())?.let { temporaryBasal -> val percentRate = temporaryBasal.convertedToPercent(dateUtil.now(), profile)
val percentRate = temporaryBasal.convertedToPercent(dateUtil.now(), profile) when {
when { percentRate > 100 -> R.drawable.ic_cp_basal_tbr_high
percentRate > 100 -> R.drawable.ic_cp_basal_tbr_high percentRate < 100 -> R.drawable.ic_cp_basal_tbr_low
percentRate < 100 -> R.drawable.ic_cp_basal_tbr_low else -> R.drawable.ic_cp_basal_no_tbr
else -> R.drawable.ic_cp_basal_no_tbr
}
} }
} ?: R.drawable.ic_cp_basal_no_tbr }
} ?: R.drawable.ic_cp_basal_no_tbr
val temporaryBasalColor: Int fun temporaryBasalColor(context: Context?, iobCobCalculator: IobCobCalculator): Int = iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())?.let { rh.gac(context , R.attr.basal) }
get() = iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())?.let { rh.gc(R.color.basal) } ?: rh.gac(context, R.attr.defaultTextColor)
?: rh.gc(R.color.defaulttextcolor)
/* /*
* EXTENDED BOLUS * EXTENDED BOLUS
*/ */
val extendedBolusText: String fun extendedBolusText(iobCobCalculator: IobCobCalculator): String =
get() = iobCobCalculator.getExtendedBolus(dateUtil.now())?.let { extendedBolus ->
iobCobCalculator.getExtendedBolus(dateUtil.now())?.let { extendedBolus -> if (!extendedBolus.isInProgress(dateUtil)) ""
if (!extendedBolus.isInProgress(dateUtil)) "" else if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) rh.gs(R.string.pump_basebasalrate, extendedBolus.rate)
else if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) rh.gs(R.string.pump_basebasalrate, extendedBolus.rate) else ""
else "" } ?: ""
} ?: ""
val extendedBolusDialogText: String fun extendedBolusDialogText(iobCobCalculator: IobCobCalculator): String =
get() = iobCobCalculator.getExtendedBolus(dateUtil.now())?.toStringFull(dateUtil) ?: "" iobCobCalculator.getExtendedBolus(dateUtil.now())?.toStringFull(dateUtil) ?: ""
/* /*
* IOB, COB * IOB, COB
*/ */
var bolusIob: IobTotal? = null fun bolusIob(iobCobCalculator: IobCobCalculator): IobTotal = iobCobCalculator.calculateIobFromBolus().round()
var basalIob: IobTotal? = null fun basalIob(iobCobCalculator: IobCobCalculator): IobTotal = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round()
var cobInfo: CobInfo? = null fun cobInfo(iobCobCalculator: IobCobCalculator): CobInfo = iobCobCalculator.getCobInfo(true, "Overview COB")
var lastCarbsTime: Long = 0L
val iobText: String val lastCarbsTime: Long
get() = get() = repository.getLastCarbsRecordWrapped().blockingGet().let { lastCarbs ->
bolusIob?.let { bolusIob -> if (lastCarbs is ValueWrapper.Existing) lastCarbs.value.timestamp else 0L
basalIob?.let { basalIob -> }
rh.gs(R.string.formatinsulinunits, bolusIob.iob + basalIob.basaliob)
} ?: rh.gs(R.string.value_unavailable_short)
} ?: rh.gs(R.string.value_unavailable_short)
val iobDialogText: String fun iobText(iobCobCalculator: IobCobCalculator): String =
get() = rh.gs(R.string.formatinsulinunits, bolusIob(iobCobCalculator).iob + basalIob(iobCobCalculator).basaliob)
bolusIob?.let { bolusIob ->
basalIob?.let { basalIob -> fun iobDialogText(iobCobCalculator: IobCobCalculator): String =
rh.gs(R.string.formatinsulinunits, bolusIob.iob + basalIob.basaliob) + "\n" + rh.gs(R.string.formatinsulinunits, bolusIob(iobCobCalculator).iob + basalIob(iobCobCalculator).basaliob) + "\n" +
rh.gs(R.string.bolus) + ": " + rh.gs(R.string.formatinsulinunits, bolusIob.iob) + "\n" + rh.gs(R.string.bolus) + ": " + rh.gs(R.string.formatinsulinunits, bolusIob(iobCobCalculator).iob) + "\n" +
rh.gs(R.string.basal) + ": " + rh.gs(R.string.formatinsulinunits, basalIob.basaliob) rh.gs(R.string.basal) + ": " + rh.gs(R.string.formatinsulinunits, basalIob(iobCobCalculator).basaliob)
} ?: rh.gs(R.string.value_unavailable_short)
} ?: rh.gs(R.string.value_unavailable_short)
/* /*
* TEMP TARGET * TEMP TARGET
*/ */
var temporaryTarget: TemporaryTarget? = null val temporaryTarget: TemporaryTarget?
get() =
repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet().let { tempTarget ->
if (tempTarget is ValueWrapper.Existing) tempTarget.value
else null
}
/* /*
* SENSITIVITY * SENSITIVITY
*/ */
var lastAutosensData: AutosensData? = null fun lastAutosensData(iobCobCalculator: IobCobCalculator) = iobCobCalculator.ads.getLastAutosensData("Overview", aapsLogger, dateUtil)
/* /*
* Graphs * Graphs
*/ */
@ -276,6 +263,8 @@ class OverviewData @Inject constructor(
var maxTreatmentsValue = 0.0 var maxTreatmentsValue = 0.0
var treatmentsSeries: PointsWithLabelGraphSeries<DataPointWithLabelInterface> = PointsWithLabelGraphSeries() var treatmentsSeries: PointsWithLabelGraphSeries<DataPointWithLabelInterface> = PointsWithLabelGraphSeries()
var maxTherapyEventValue = 0.0
var therapyEventSeries: PointsWithLabelGraphSeries<DataPointWithLabelInterface> = PointsWithLabelGraphSeries()
var maxIobValueFound = Double.MIN_VALUE var maxIobValueFound = Double.MIN_VALUE
val iobScale = Scale() val iobScale = Scale()
@ -309,532 +298,4 @@ class OverviewData @Inject constructor(
val dsMinScale = Scale() val dsMinScale = Scale()
var dsMaxSeries: LineGraphSeries<ScaledDataPoint> = LineGraphSeries() var dsMaxSeries: LineGraphSeries<ScaledDataPoint> = LineGraphSeries()
var dsMinSeries: LineGraphSeries<ScaledDataPoint> = LineGraphSeries() var dsMinSeries: LineGraphSeries<ScaledDataPoint> = LineGraphSeries()
@Synchronized
@Suppress("SameParameterValue", "UNUSED_PARAMETER")
fun prepareBgData(from: String) {
// val start = dateUtil.now()
maxBgValue = Double.MIN_VALUE
bgReadingsArray = repository.compatGetBgReadingsDataFromTime(fromTime, toTime, false).blockingGet()
val bgListArray: MutableList<DataPointWithLabelInterface> = java.util.ArrayList()
for (bg in bgReadingsArray) {
if (bg.timestamp < fromTime || bg.timestamp > toTime) continue
if (bg.value > maxBgValue) maxBgValue = bg.value
bgListArray.add(GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, rh))
}
bgListArray.sortWith { o1: DataPointWithLabelInterface, o2: DataPointWithLabelInterface -> o1.x.compareTo(o2.x) }
bgReadingGraphSeries = PointsWithLabelGraphSeries(Array(bgListArray.size) { i -> bgListArray[i] })
maxBgValue = Profile.fromMgdlToUnits(maxBgValue, profileFunction.getUnits())
if (defaultValueHelper.determineHighLine() > maxBgValue) maxBgValue = defaultValueHelper.determineHighLine()
maxBgValue = addUpperChartMargin(maxBgValue)
// profiler.log(LTag.UI, "prepareBgData() $from", start)
}
@Suppress("UNUSED_PARAMETER")
@Synchronized
fun preparePredictions(from: String) {
// val start = dateUtil.now()
val apsResult = if (config.APS) loop.lastRun?.constraintsProcessed else nsDeviceStatus.getAPSResult(injector)
val predictionsAvailable = if (config.APS) loop.lastRun?.request?.hasPredictions == true else config.NSCLIENT
val menuChartSettings = overviewMenus.setting
// align to hours
val calendar = Calendar.getInstance().also {
it.timeInMillis = System.currentTimeMillis()
it[Calendar.MILLISECOND] = 0
it[Calendar.SECOND] = 0
it[Calendar.MINUTE] = 0
it.add(Calendar.HOUR, 1)
}
if (predictionsAvailable && apsResult != null && menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal]) {
var predictionHours = (ceil(apsResult.latestPredictionsTime - System.currentTimeMillis().toDouble()) / (60 * 60 * 1000)).toInt()
predictionHours = min(2, predictionHours)
predictionHours = max(0, predictionHours)
val hoursToFetch = rangeToDisplay - predictionHours
toTime = calendar.timeInMillis + 100000 // little bit more to avoid wrong rounding - GraphView specific
fromTime = toTime - T.hours(hoursToFetch.toLong()).msecs()
endTime = toTime + T.hours(predictionHours.toLong()).msecs()
} else {
toTime = calendar.timeInMillis + 100000 // little bit more to avoid wrong rounding - GraphView specific
fromTime = toTime - T.hours(rangeToDisplay.toLong()).msecs()
endTime = toTime
}
val bgListArray: MutableList<DataPointWithLabelInterface> = java.util.ArrayList()
val predictions: MutableList<GlucoseValueDataPoint>? = apsResult?.predictions
?.map { bg -> GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, rh) }
?.toMutableList()
if (predictions != null) {
predictions.sortWith { o1: GlucoseValueDataPoint, o2: GlucoseValueDataPoint -> o1.x.compareTo(o2.x) }
for (prediction in predictions) if (prediction.data.value >= 40) bgListArray.add(prediction)
}
predictionsGraphSeries = PointsWithLabelGraphSeries(Array(bgListArray.size) { i -> bgListArray[i] })
// profiler.log(LTag.UI, "preparePredictions() $from", start)
}
@Synchronized
@Suppress("SameParameterValue", "UNUSED_PARAMETER")
fun prepareBucketedData(from: String) {
// val start = dateUtil.now()
val bucketedData = iobCobCalculator.ads.getBucketedDataTableCopy() ?: return
if (bucketedData.isEmpty()) {
aapsLogger.debug("No bucketed data.")
return
}
val bucketedListArray: MutableList<DataPointWithLabelInterface> = java.util.ArrayList()
for (inMemoryGlucoseValue in bucketedData) {
if (inMemoryGlucoseValue.timestamp < fromTime || inMemoryGlucoseValue.timestamp > toTime) continue
bucketedListArray.add(InMemoryGlucoseValueDataPoint(inMemoryGlucoseValue, profileFunction, rh))
}
bucketedListArray.sortWith { o1: DataPointWithLabelInterface, o2: DataPointWithLabelInterface -> o1.x.compareTo(o2.x) }
bucketedGraphSeries = PointsWithLabelGraphSeries(Array(bucketedListArray.size) { i -> bucketedListArray[i] })
// profiler.log(LTag.UI, "prepareBucketedData() $from", start)
}
@Suppress("UNUSED_PARAMETER")
@Synchronized
fun prepareBasalData(from: String) {
// val start = dateUtil.now()
maxBasalValueFound = 0.0
val baseBasalArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
val tempBasalArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
val basalLineArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
val absoluteBasalLineArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
var lastLineBasal = 0.0
var lastAbsoluteLineBasal = -1.0
var lastBaseBasal = 0.0
var lastTempBasal = 0.0
var time = fromTime
while (time < toTime) {
val profile = profileFunction.getProfile(time)
if (profile == null) {
time += 60 * 1000L
continue
}
val basalData = iobCobCalculator.getBasalData(profile, time)
val baseBasalValue = basalData.basal
var absoluteLineValue = baseBasalValue
var tempBasalValue = 0.0
var basal = 0.0
if (basalData.isTempBasalRunning) {
tempBasalValue = basalData.tempBasalAbsolute
absoluteLineValue = tempBasalValue
if (tempBasalValue != lastTempBasal) {
tempBasalArray.add(ScaledDataPoint(time, lastTempBasal, basalScale))
tempBasalArray.add(ScaledDataPoint(time, tempBasalValue.also { basal = it }, basalScale))
}
if (lastBaseBasal != 0.0) {
baseBasalArray.add(ScaledDataPoint(time, lastBaseBasal, basalScale))
baseBasalArray.add(ScaledDataPoint(time, 0.0, basalScale))
lastBaseBasal = 0.0
}
} else {
if (baseBasalValue != lastBaseBasal) {
baseBasalArray.add(ScaledDataPoint(time, lastBaseBasal, basalScale))
baseBasalArray.add(ScaledDataPoint(time, baseBasalValue.also { basal = it }, basalScale))
lastBaseBasal = baseBasalValue
}
if (lastTempBasal != 0.0) {
tempBasalArray.add(ScaledDataPoint(time, lastTempBasal, basalScale))
tempBasalArray.add(ScaledDataPoint(time, 0.0, basalScale))
}
}
if (baseBasalValue != lastLineBasal) {
basalLineArray.add(ScaledDataPoint(time, lastLineBasal, basalScale))
basalLineArray.add(ScaledDataPoint(time, baseBasalValue, basalScale))
}
if (absoluteLineValue != lastAbsoluteLineBasal) {
absoluteBasalLineArray.add(ScaledDataPoint(time, lastAbsoluteLineBasal, basalScale))
absoluteBasalLineArray.add(ScaledDataPoint(time, basal, basalScale))
}
lastAbsoluteLineBasal = absoluteLineValue
lastLineBasal = baseBasalValue
lastTempBasal = tempBasalValue
maxBasalValueFound = max(maxBasalValueFound, max(tempBasalValue, baseBasalValue))
time += 60 * 1000L
}
// final points
basalLineArray.add(ScaledDataPoint(toTime, lastLineBasal, basalScale))
baseBasalArray.add(ScaledDataPoint(toTime, lastBaseBasal, basalScale))
tempBasalArray.add(ScaledDataPoint(toTime, lastTempBasal, basalScale))
absoluteBasalLineArray.add(ScaledDataPoint(toTime, lastAbsoluteLineBasal, basalScale))
// create series
baseBasalGraphSeries = LineGraphSeries(Array(baseBasalArray.size) { i -> baseBasalArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = rh.gc(R.color.basebasal)
it.thickness = 0
}
tempBasalGraphSeries = LineGraphSeries(Array(tempBasalArray.size) { i -> tempBasalArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = rh.gc(R.color.tempbasal)
it.thickness = 0
}
basalLineGraphSeries = LineGraphSeries(Array(basalLineArray.size) { i -> basalLineArray[i] }).also {
it.setCustomPaint(Paint().also { paint ->
paint.style = Paint.Style.STROKE
paint.strokeWidth = rh.getDisplayMetrics().scaledDensity * 2
paint.pathEffect = DashPathEffect(floatArrayOf(2f, 4f), 0f)
paint.color = rh.gc(R.color.basal)
})
}
absoluteBasalGraphSeries = LineGraphSeries(Array(absoluteBasalLineArray.size) { i -> absoluteBasalLineArray[i] }).also {
it.setCustomPaint(Paint().also { absolutePaint ->
absolutePaint.style = Paint.Style.STROKE
absolutePaint.strokeWidth = rh.getDisplayMetrics().scaledDensity * 2
absolutePaint.color = rh.gc(R.color.basal)
})
}
// profiler.log(LTag.UI, "prepareBasalData() $from", start)
}
@Suppress("UNUSED_PARAMETER")
@Synchronized
fun prepareTemporaryTargetData(from: String) {
// val start = dateUtil.now()
val profile = profileFunction.getProfile() ?: return
val units = profileFunction.getUnits()
var toTime = toTime
val targetsSeriesArray: MutableList<DataPoint> = java.util.ArrayList()
var lastTarget = -1.0
loop.lastRun?.constraintsProcessed?.let { toTime = max(it.latestPredictionsTime, toTime) }
var time = fromTime
while (time < toTime) {
val tt = repository.getTemporaryTargetActiveAt(time).blockingGet()
val value: Double = if (tt is ValueWrapper.Existing) {
Profile.fromMgdlToUnits(tt.value.target(), units)
} else {
Profile.fromMgdlToUnits((profile.getTargetLowMgdl(time) + profile.getTargetHighMgdl(time)) / 2, units)
}
if (lastTarget != value) {
if (lastTarget != -1.0) targetsSeriesArray.add(DataPoint(time.toDouble(), lastTarget))
targetsSeriesArray.add(DataPoint(time.toDouble(), value))
}
lastTarget = value
time += 5 * 60 * 1000L
}
// final point
targetsSeriesArray.add(DataPoint(toTime.toDouble(), lastTarget))
// create series
temporaryTargetSeries = LineGraphSeries(Array(targetsSeriesArray.size) { i -> targetsSeriesArray[i] }).also {
it.isDrawBackground = false
it.color = rh.gc(R.color.tempTargetBackground)
it.thickness = 2
}
// profiler.log(LTag.UI, "prepareTemporaryTargetData() $from", start)
}
@Suppress("UNUSED_PARAMETER")
@Synchronized
fun prepareTreatmentsData(from: String) {
// val start = dateUtil.now()
maxTreatmentsValue = 0.0
val filteredTreatments: MutableList<DataPointWithLabelInterface> = java.util.ArrayList()
repository.getBolusesDataFromTimeToTime(fromTime, endTime, true).blockingGet()
.map { BolusDataPoint(it, rh, activePlugin, defaultValueHelper) }
.filter { it.data.type == Bolus.Type.NORMAL || it.data.type == Bolus.Type.SMB }
.forEach {
it.y = getNearestBg(it.x.toLong())
filteredTreatments.add(it)
}
repository.getCarbsDataFromTimeToTimeExpanded(fromTime, endTime, true).blockingGet()
.map { CarbsDataPoint(it, rh) }
.forEach {
it.y = getNearestBg(it.x.toLong())
filteredTreatments.add(it)
}
// ProfileSwitch
repository.getEffectiveProfileSwitchDataFromTimeToTime(fromTime, endTime, true).blockingGet()
.map { EffectiveProfileSwitchDataPoint(it) }
.forEach(filteredTreatments::add)
// OfflineEvent
repository.getOfflineEventDataFromTimeToTime(fromTime, endTime, true).blockingGet()
.map {
TherapyEventDataPoint(
TherapyEvent(timestamp = it.timestamp, duration = it.duration, type = TherapyEvent.Type.APS_OFFLINE, glucoseUnit = TherapyEvent.GlucoseUnit.MMOL),
rh,
profileFunction,
translator
)
}
.forEach(filteredTreatments::add)
// Extended bolus
if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) {
repository.getExtendedBolusDataFromTimeToTime(fromTime, endTime, true).blockingGet()
.map { ExtendedBolusDataPoint(it) }
.filter { it.duration != 0L }
.forEach {
it.y = getNearestBg(it.x.toLong())
filteredTreatments.add(it)
}
}
// Careportal
repository.compatGetTherapyEventDataFromToTime(fromTime - T.hours(6).msecs(), endTime).blockingGet()
.map { TherapyEventDataPoint(it, rh, profileFunction, translator) }
.filterTimeframe(fromTime, endTime)
.forEach {
if (it.y == 0.0) it.y = getNearestBg(it.x.toLong())
filteredTreatments.add(it)
}
// increase maxY if a treatment forces it's own height that's higher than a BG value
filteredTreatments.map { it.y }
.maxOrNull()
?.let(::addUpperChartMargin)
?.let { maxTreatmentsValue = maxOf(maxTreatmentsValue, it) }
treatmentsSeries = PointsWithLabelGraphSeries(filteredTreatments.toTypedArray())
// profiler.log(LTag.UI, "prepareTreatmentsData() $from", start)
}
@Suppress("UNUSED_PARAMETER")
@Synchronized
fun prepareIobAutosensData(from: String) {
// val start = dateUtil.now()
val iobArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
val absIobArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
maxIobValueFound = Double.MIN_VALUE
var lastIob = 0.0
var absLastIob = 0.0
var time = fromTime
val minFailOverActiveList: MutableList<DataPointWithLabelInterface> = java.util.ArrayList()
val cobArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
maxCobValueFound = Double.MIN_VALUE
var lastCob = 0
val actArrayHist: MutableList<ScaledDataPoint> = java.util.ArrayList()
val actArrayPrediction: MutableList<ScaledDataPoint> = java.util.ArrayList()
val now = dateUtil.now().toDouble()
maxIAValue = 0.0
val bgiArrayHist: MutableList<ScaledDataPoint> = java.util.ArrayList()
val bgiArrayPrediction: MutableList<ScaledDataPoint> = java.util.ArrayList()
maxBGIValue = Double.MIN_VALUE
val devArray: MutableList<OverviewPlugin.DeviationDataPoint> = java.util.ArrayList()
maxDevValueFound = Double.MIN_VALUE
val ratioArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
maxRatioValueFound = 5.0 //even if sens data equals 0 for all the period, minimum scale is between 95% and 105%
minRatioValueFound = -5.0
val dsMaxArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
val dsMinArray: MutableList<ScaledDataPoint> = java.util.ArrayList()
maxFromMaxValueFound = Double.MIN_VALUE
maxFromMinValueFound = Double.MIN_VALUE
val adsData = iobCobCalculator.ads.clone()
while (time <= toTime) {
val profile = profileFunction.getProfile(time)
if (profile == null) {
time += 5 * 60 * 1000L
continue
}
// IOB
val iob = iobCobCalculator.calculateFromTreatmentsAndTemps(time, profile)
val baseBasalIob = iobCobCalculator.calculateAbsoluteIobFromBaseBasals(time)
val absIob = IobTotal.combine(iob, baseBasalIob)
val autosensData = adsData.getAutosensDataAtTime(time)
if (abs(lastIob - iob.iob) > 0.02) {
if (abs(lastIob - iob.iob) > 0.2) iobArray.add(ScaledDataPoint(time, lastIob, iobScale))
iobArray.add(ScaledDataPoint(time, iob.iob, iobScale))
maxIobValueFound = maxOf(maxIobValueFound, abs(iob.iob))
lastIob = iob.iob
}
if (abs(absLastIob - absIob.iob) > 0.02) {
if (abs(absLastIob - absIob.iob) > 0.2) absIobArray.add(ScaledDataPoint(time, absLastIob, iobScale))
absIobArray.add(ScaledDataPoint(time, absIob.iob, iobScale))
maxIobValueFound = maxOf(maxIobValueFound, abs(absIob.iob))
absLastIob = absIob.iob
}
// COB
if (autosensData != null) {
val cob = autosensData.cob.toInt()
if (cob != lastCob) {
if (autosensData.carbsFromBolus > 0) cobArray.add(ScaledDataPoint(time, lastCob.toDouble(), cobScale))
cobArray.add(ScaledDataPoint(time, cob.toDouble(), cobScale))
maxCobValueFound = max(maxCobValueFound, cob.toDouble())
lastCob = cob
}
if (autosensData.failOverToMinAbsorptionRate) {
autosensData.scale = cobScale
autosensData.chartTime = time
minFailOverActiveList.add(autosensData)
}
}
// ACTIVITY
if (time <= now) actArrayHist.add(ScaledDataPoint(time, iob.activity, actScale))
else actArrayPrediction.add(ScaledDataPoint(time, iob.activity, actScale))
maxIAValue = max(maxIAValue, abs(iob.activity))
// BGI
val devBgiScale = overviewMenus.isEnabledIn(OverviewMenus.CharType.DEV) == overviewMenus.isEnabledIn(OverviewMenus.CharType.BGI)
val deviation = if (devBgiScale) autosensData?.deviation ?: 0.0 else 0.0
val bgi: Double = iob.activity * profile.getIsfMgdl(time) * 5.0
if (time <= now) bgiArrayHist.add(ScaledDataPoint(time, bgi, bgiScale))
else bgiArrayPrediction.add(ScaledDataPoint(time, bgi, bgiScale))
maxBGIValue = max(maxBGIValue, max(abs(bgi), deviation))
// DEVIATIONS
if (autosensData != null) {
var color = rh.gc(R.color.deviationblack) // "="
if (autosensData.type == "" || autosensData.type == "non-meal") {
if (autosensData.pastSensitivity == "C") color = rh.gc(R.color.deviationgrey)
if (autosensData.pastSensitivity == "+") color = rh.gc(R.color.deviationgreen)
if (autosensData.pastSensitivity == "-") color = rh.gc(R.color.deviationred)
} else if (autosensData.type == "uam") {
color = rh.gc(R.color.uam)
} else if (autosensData.type == "csf") {
color = rh.gc(R.color.deviationgrey)
}
devArray.add(OverviewPlugin.DeviationDataPoint(time.toDouble(), autosensData.deviation, color, devScale))
maxDevValueFound = maxOf(maxDevValueFound, abs(autosensData.deviation), abs(bgi))
}
// RATIO
if (autosensData != null) {
ratioArray.add(ScaledDataPoint(time, 100.0 * (autosensData.autosensResult.ratio - 1), ratioScale))
maxRatioValueFound = max(maxRatioValueFound, 100.0 * (autosensData.autosensResult.ratio - 1))
minRatioValueFound = min(minRatioValueFound, 100.0 * (autosensData.autosensResult.ratio - 1))
}
// DEV SLOPE
if (autosensData != null) {
dsMaxArray.add(ScaledDataPoint(time, autosensData.slopeFromMaxDeviation, dsMaxScale))
dsMinArray.add(ScaledDataPoint(time, autosensData.slopeFromMinDeviation, dsMinScale))
maxFromMaxValueFound = max(maxFromMaxValueFound, abs(autosensData.slopeFromMaxDeviation))
maxFromMinValueFound = max(maxFromMinValueFound, abs(autosensData.slopeFromMinDeviation))
}
time += 5 * 60 * 1000L
}
// IOB
iobSeries = FixedLineGraphSeries(Array(iobArray.size) { i -> iobArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = -0x7f000001 and rh.gc(R.color.iob) //50%
it.color = rh.gc(R.color.iob)
it.thickness = 3
}
absIobSeries = FixedLineGraphSeries(Array(absIobArray.size) { i -> absIobArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = -0x7f000001 and rh.gc(R.color.iob) //50%
it.color = rh.gc(R.color.iob)
it.thickness = 3
}
if (overviewMenus.setting[0][OverviewMenus.CharType.PRE.ordinal]) {
val autosensData = adsData.getLastAutosensData("GraphData", aapsLogger, dateUtil)
val lastAutosensResult = autosensData?.autosensResult ?: AutosensResult()
val isTempTarget = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() is ValueWrapper.Existing
val iobPrediction: MutableList<DataPointWithLabelInterface> = java.util.ArrayList()
val iobPredictionArray = iobCobCalculator.calculateIobArrayForSMB(lastAutosensResult, SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget)
for (i in iobPredictionArray) {
iobPrediction.add(i.setColor(rh.gc(R.color.iobPredAS)))
maxIobValueFound = max(maxIobValueFound, abs(i.iob))
}
iobPredictions1Series = PointsWithLabelGraphSeries(Array(iobPrediction.size) { i -> iobPrediction[i] })
aapsLogger.debug(LTag.AUTOSENS, "IOB prediction for AS=" + DecimalFormatter.to2Decimal(lastAutosensResult.ratio) + ": " + iobCobCalculator.iobArrayToString(iobPredictionArray))
/*
val iobPrediction2: MutableList<DataPointWithLabelInterface> = java.util.ArrayList()
val iobPredictionArray2 = iobCobCalculator.calculateIobArrayForSMB(AutosensResult(), SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget)
for (i in iobPredictionArray2) {
iobPrediction2.add(i.setColor(rh.gc(R.color.iobPred)))
maxIobValueFound = max(maxIobValueFound, abs(i.iob))
}
iobPredictions2Series = PointsWithLabelGraphSeries(Array(iobPrediction2.size) { i -> iobPrediction2[i] })
aapsLogger.debug(LTag.AUTOSENS, "IOB prediction for AS=" + DecimalFormatter.to2Decimal(1.0) + ": " + iobCobCalculator.iobArrayToString(iobPredictionArray2))
*/
} else {
iobPredictions1Series = PointsWithLabelGraphSeries()
//iobPredictions2Series = PointsWithLabelGraphSeries()
}
// COB
cobSeries = FixedLineGraphSeries(Array(cobArray.size) { i -> cobArray[i] }).also {
it.isDrawBackground = true
it.backgroundColor = -0x7f000001 and rh.gc(R.color.cob) //50%
it.color = rh.gc(R.color.cob)
it.thickness = 3
}
cobMinFailOverSeries = PointsWithLabelGraphSeries(Array(minFailOverActiveList.size) { i -> minFailOverActiveList[i] })
// ACTIVITY
activitySeries = FixedLineGraphSeries(Array(actArrayHist.size) { i -> actArrayHist[i] }).also {
it.isDrawBackground = false
it.color = rh.gc(R.color.activity)
it.thickness = 3
}
activityPredictionSeries = FixedLineGraphSeries(Array(actArrayPrediction.size) { i -> actArrayPrediction[i] }).also {
it.setCustomPaint(Paint().also { paint ->
paint.style = Paint.Style.STROKE
paint.strokeWidth = 3f
paint.pathEffect = DashPathEffect(floatArrayOf(4f, 4f), 0f)
paint.color = rh.gc(R.color.activity)
})
}
// BGI
minusBgiSeries = FixedLineGraphSeries(Array(bgiArrayHist.size) { i -> bgiArrayHist[i] }).also {
it.isDrawBackground = false
it.color = rh.gc(R.color.bgi)
it.thickness = 3
}
minusBgiHistSeries = FixedLineGraphSeries(Array(bgiArrayPrediction.size) { i -> bgiArrayPrediction[i] }).also {
it.setCustomPaint(Paint().also { paint ->
paint.style = Paint.Style.STROKE
paint.strokeWidth = 3f
paint.pathEffect = DashPathEffect(floatArrayOf(4f, 4f), 0f)
paint.color = rh.gc(R.color.bgi)
})
}
// DEVIATIONS
deviationsSeries = BarGraphSeries(Array(devArray.size) { i -> devArray[i] }).also {
it.setValueDependentColor { data: OverviewPlugin.DeviationDataPoint -> data.color }
}
// RATIO
ratioSeries = LineGraphSeries(Array(ratioArray.size) { i -> ratioArray[i] }).also {
it.color = rh.gc(R.color.ratio)
it.thickness = 3
}
// DEV SLOPE
dsMaxSeries = LineGraphSeries(Array(dsMaxArray.size) { i -> dsMaxArray[i] }).also {
it.color = rh.gc(R.color.devslopepos)
it.thickness = 3
}
dsMinSeries = LineGraphSeries(Array(dsMinArray.size) { i -> dsMinArray[i] }).also {
it.color = rh.gc(R.color.devslopeneg)
it.thickness = 3
}
// profiler.log(LTag.UI, "prepareIobAutosensData() $from", start)
}
private fun addUpperChartMargin(maxBgValue: Double) =
if (profileFunction.getUnits() == GlucoseUnit.MGDL) Round.roundTo(maxBgValue, 40.0) + 80 else Round.roundTo(maxBgValue, 2.0) + 4
private fun getNearestBg(date: Long): Double {
bgReadingsArray.let { bgReadingsArray ->
for (reading in bgReadingsArray) {
if (reading.timestamp > date) continue
return Profile.fromMgdlToUnits(reading.value, profileFunction.getUnits())
}
return if (bgReadingsArray.isNotEmpty()) Profile.fromMgdlToUnits(bgReadingsArray[0].value, profileFunction.getUnits())
else Profile.fromMgdlToUnits(100.0, profileFunction.getUnits())
}
}
private fun <E : DataPointWithLabelInterface> List<E>.filterTimeframe(fromTime: Long, endTime: Long): List<E> =
filter { it.x + it.duration >= fromTime && it.x <= endTime }
} }

View file

@ -5,8 +5,9 @@ import android.app.NotificationManager
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.Color
import android.graphics.Paint import android.graphics.Paint
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.graphics.drawable.AnimationDrawable import android.graphics.drawable.AnimationDrawable
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
@ -35,17 +36,12 @@ import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.interfaces.end import info.nightscout.androidaps.database.interfaces.end
import info.nightscout.androidaps.databinding.OverviewFragmentBinding import info.nightscout.androidaps.databinding.OverviewFragmentBinding
import info.nightscout.androidaps.dialogs.* import info.nightscout.androidaps.dialogs.*
import info.nightscout.androidaps.events.EventAcceptOpenLoopChange import info.nightscout.androidaps.events.*
import info.nightscout.androidaps.events.EventInitializationChanged
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventPumpStatusChanged
import info.nightscout.androidaps.events.EventRefreshOverview
import info.nightscout.androidaps.extensions.directionToIcon import info.nightscout.androidaps.extensions.directionToIcon
import info.nightscout.androidaps.extensions.isInProgress import info.nightscout.androidaps.extensions.runOnUiThread
import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.extensions.valueToUnitsString import info.nightscout.androidaps.extensions.valueToUnitsString
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.aps.loop.events.EventNewOpenLoopNotification import info.nightscout.androidaps.plugins.aps.loop.events.EventNewOpenLoopNotification
import info.nightscout.androidaps.plugins.aps.openAPSSMB.DetermineBasalResultSMB import info.nightscout.androidaps.plugins.aps.openAPSSMB.DetermineBasalResultSMB
@ -61,6 +57,7 @@ import info.nightscout.androidaps.plugins.general.overview.notifications.Notific
import info.nightscout.androidaps.plugins.general.wear.events.EventWearInitiateAction import info.nightscout.androidaps.plugins.general.wear.events.EventWearInitiateAction
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType import info.nightscout.androidaps.plugins.pump.common.defs.PumpType
import info.nightscout.androidaps.plugins.pump.omnipod.eros.OmnipodErosPumpPlugin
import info.nightscout.androidaps.plugins.source.DexcomPlugin import info.nightscout.androidaps.plugins.source.DexcomPlugin
import info.nightscout.androidaps.plugins.source.XdripPlugin import info.nightscout.androidaps.plugins.source.XdripPlugin
import info.nightscout.androidaps.skins.SkinProvider import info.nightscout.androidaps.skins.SkinProvider
@ -74,16 +71,16 @@ import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.sharedPreferences.SP
import info.nightscout.androidaps.utils.ui.SingleClickButton import info.nightscout.androidaps.utils.ui.SingleClickButton
import info.nightscout.androidaps.utils.ui.UIRunnable import info.nightscout.androidaps.utils.ui.UIRunnable
import info.nightscout.androidaps.utils.wizard.QuickWizard import info.nightscout.androidaps.utils.wizard.QuickWizard
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
import java.util.* import java.util.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
import kotlin.collections.ArrayList
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.min import kotlin.math.min
@ -150,10 +147,11 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
_binding = it _binding = it
//check screen width //check screen width
dm = DisplayMetrics() dm = DisplayMetrics()
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) @Suppress("DEPRECATION")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
activity?.display?.getRealMetrics(dm) activity?.display?.getRealMetrics(dm)
else else
@Suppress("DEPRECATION") activity?.windowManager?.defaultDisplay?.getMetrics(dm) activity?.windowManager?.defaultDisplay?.getMetrics(dm)
}.root }.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -166,13 +164,13 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
smallHeight = screenHeight <= Constants.SMALL_HEIGHT smallHeight = screenHeight <= Constants.SMALL_HEIGHT
val landscape = screenHeight < screenWidth val landscape = screenHeight < screenWidth
skinProvider.activeSkin().preProcessLandscapeOverviewLayout(dm, view, landscape, rh.gb(R.bool.isTablet), smallHeight) skinProvider.activeSkin().preProcessLandscapeOverviewLayout(dm, binding, landscape, rh.gb(R.bool.isTablet), smallHeight)
binding.nsclientLayout.visibility = config.NSCLIENT.toVisibility() binding.nsclientLayout.visibility = config.NSCLIENT.toVisibility()
binding.notifications.setHasFixedSize(false) binding.notifications.setHasFixedSize(false)
binding.notifications.layoutManager = LinearLayoutManager(view.context) binding.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 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
binding.graphsLayout.bgGraph.gridLabelRenderer?.gridColor = rh.gc(R.color.graphgrid) binding.graphsLayout.bgGraph.gridLabelRenderer?.gridColor = rh.gac(context, R.attr.graphgrid)
binding.graphsLayout.bgGraph.gridLabelRenderer?.reloadStyles() binding.graphsLayout.bgGraph.gridLabelRenderer?.reloadStyles()
binding.graphsLayout.bgGraph.gridLabelRenderer?.labelVerticalWidth = axisWidth binding.graphsLayout.bgGraph.gridLabelRenderer?.labelVerticalWidth = axisWidth
binding.graphsLayout.bgGraph.layoutParams?.height = rh.dpToPx(skinProvider.activeSkin().mainGraphHeight) binding.graphsLayout.bgGraph.layoutParams?.height = rh.dpToPx(skinProvider.activeSkin().mainGraphHeight)
@ -186,16 +184,12 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
overviewData.rangeToDisplay += 6 overviewData.rangeToDisplay += 6
overviewData.rangeToDisplay = if (overviewData.rangeToDisplay > 24) 6 else overviewData.rangeToDisplay overviewData.rangeToDisplay = if (overviewData.rangeToDisplay > 24) 6 else overviewData.rangeToDisplay
sp.putInt(R.string.key_rangetodisplay, overviewData.rangeToDisplay) sp.putInt(R.string.key_rangetodisplay, overviewData.rangeToDisplay)
overviewData.initRange()
overviewData.prepareBucketedData("EventBucketedDataCreated")
overviewData.prepareBgData("EventBucketedDataCreated")
updateGraph("rangeChange")
rxBus.send(EventPreferenceChange(rh, R.string.key_rangetodisplay)) rxBus.send(EventPreferenceChange(rh, R.string.key_rangetodisplay))
sp.putBoolean(R.string.key_objectiveusescale, true) sp.putBoolean(R.string.key_objectiveusescale, true)
false false
} }
prepareGraphsIfNeeded(overviewMenus.setting.size) prepareGraphsIfNeeded(overviewMenus.setting.size)
overviewMenus.setupChartMenu(binding.graphsLayout.chartMenuButton) context?.let { overviewMenus.setupChartMenu(it, binding.graphsLayout.chartMenuButton) }
binding.activeProfile.setOnClickListener(this) binding.activeProfile.setOnClickListener(this)
binding.activeProfile.setOnLongClickListener(this) binding.activeProfile.setOnLongClickListener(this)
@ -225,113 +219,107 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
@Synchronized @Synchronized
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewTime::class.java)
.debounce(1L, TimeUnit.SECONDS)
.observeOn(aapsSchedulers.main)
.subscribe({ updateTime(it.from) }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewCalcProgress::class.java) .toObservable(EventUpdateOverviewCalcProgress::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ updateCalcProgress(it.from) }, fabricPrivacy::logException) .subscribe({ updateCalcProgress() }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewProfile::class.java)
.debounce(1L, TimeUnit.SECONDS)
.observeOn(aapsSchedulers.main)
.subscribe({ updateProfile(it.from) }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewTemporaryBasal::class.java)
.debounce(1L, TimeUnit.SECONDS)
.observeOn(aapsSchedulers.main)
.subscribe({ updateTemporaryBasal(it.from) }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewExtendedBolus::class.java)
.debounce(1L, TimeUnit.SECONDS)
.observeOn(aapsSchedulers.main)
.subscribe({ updateExtendedBolus(it.from) }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewTemporaryTarget::class.java)
.debounce(1L, TimeUnit.SECONDS)
.observeOn(aapsSchedulers.main)
.subscribe({ updateTemporaryTarget(it.from) }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewBg::class.java)
.debounce(1L, TimeUnit.SECONDS)
.observeOn(aapsSchedulers.main)
.subscribe({ updateBg(it.from) }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewIobCob::class.java) .toObservable(EventUpdateOverviewIobCob::class.java)
.debounce(1L, TimeUnit.SECONDS) .debounce(1L, TimeUnit.SECONDS)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ updateIobCob(it.from) }, fabricPrivacy::logException) .subscribe({ updateIobCob() }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewSensitivity::class.java) .toObservable(EventUpdateOverviewSensitivity::class.java)
.debounce(1L, TimeUnit.SECONDS) .debounce(1L, TimeUnit.SECONDS)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ updateSensitivity(it.from) }, fabricPrivacy::logException) .subscribe({ updateSensitivity() }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewGraph::class.java) .toObservable(EventUpdateOverviewGraph::class.java)
.debounce(1L, TimeUnit.SECONDS) .debounce(1L, TimeUnit.SECONDS)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ updateGraph(it.from) }, fabricPrivacy::logException) .subscribe({ updateGraph() }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewPumpStatus::class.java) .toObservable(EventUpdateOverviewPumpStatus::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ updatePumpStatus(it.from) }, fabricPrivacy::logException) .subscribe({ updatePumpStatus() }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewNotification::class.java) .toObservable(EventUpdateOverviewNotification::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ updateNotification(it.from) }, fabricPrivacy::logException) .subscribe({ updateNotification() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventNewBG::class.java)
.debounce(1L, TimeUnit.SECONDS)
.observeOn(aapsSchedulers.main)
.subscribe({ updateBg() }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventRefreshOverview::class.java) .toObservable(EventRefreshOverview::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ .subscribe({
if (it.now) overviewPlugin.refreshLoop(it.from) if (it.now) refreshAll()
else scheduleUpdateGUI(it.from) else scheduleUpdateGUI()
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventAcceptOpenLoopChange::class.java) .toObservable(EventAcceptOpenLoopChange::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ scheduleUpdateGUI("EventAcceptOpenLoopChange") }, fabricPrivacy::logException) .subscribe({ scheduleUpdateGUI() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventInitializationChanged::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ updateTime("EventInitializationChanged") }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventPreferenceChange::class.java) .toObservable(EventPreferenceChange::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ scheduleUpdateGUI("EventPreferenceChange") }, fabricPrivacy::logException) .subscribe({ scheduleUpdateGUI() }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventNewOpenLoopNotification::class.java) .toObservable(EventNewOpenLoopNotification::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ scheduleUpdateGUI("EventNewOpenLoopNotification") }, fabricPrivacy::logException) .subscribe({ scheduleUpdateGUI() }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventPumpStatusChanged::class.java) .toObservable(EventPumpStatusChanged::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.delay(30, TimeUnit.MILLISECONDS, aapsSchedulers.main) .delay(30, TimeUnit.MILLISECONDS, aapsSchedulers.main)
.subscribe({ .subscribe({
overviewData.pumpStatus = it.getStatus(rh) overviewData.pumpStatus = it.getStatus(rh)
updatePumpStatus("EventPumpStatusChanged") updatePumpStatus()
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventEffectiveProfileSwitchChanged::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ updateProfile() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTempTargetChange::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ updateTemporaryTarget() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventExtendedBolusChange::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ updateExtendedBolus() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTempBasalChange::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ updateTemporaryBasal() }, fabricPrivacy::logException)
refreshLoop = Runnable { refreshLoop = Runnable {
overviewPlugin.refreshLoop("refreshLoop") refreshAll()
handler.postDelayed(refreshLoop, 60 * 1000L) handler.postDelayed(refreshLoop, 60 * 1000L)
} }
handler.postDelayed(refreshLoop, 60 * 1000L) handler.postDelayed(refreshLoop, 60 * 1000L)
updateTime("onResume") refreshAll()
updateCalcProgress("onResume") updatePumpStatus()
updateProfile("onResume") updateCalcProgress()
updateTemporaryBasal("onResume") }
updateExtendedBolus("onResume")
updateTemporaryTarget("onResume") fun refreshAll() {
updateBg("onResume") runOnUiThread {
updateIobCob("onResume") _binding ?: return@runOnUiThread
updateSensitivity("onResume") updateBg()
updateGraph("onResume") updateTime()
updatePumpStatus("onResume") updateProfile()
updateNotification("onResume") updateTemporaryBasal()
updateExtendedBolus()
updateTemporaryTarget()
updateIobCob()
updateSensitivity()
updateGraph()
updateNotification()
}
} }
@Synchronized @Synchronized
@ -415,14 +403,15 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
loop.invoke("Accept temp button", false) loop.invoke("Accept temp button", false)
if (lastRun?.lastAPSRun != null && lastRun.constraintsProcessed?.isChangeRequested == true) { if (lastRun?.lastAPSRun != null && lastRun.constraintsProcessed?.isChangeRequested == true) {
protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable { protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable {
OKDialog.showConfirmation(activity, rh.gs(R.string.tempbasal_label), lastRun.constraintsProcessed?.toSpanned() if (isAdded)
?: "".toSpanned(), { OKDialog.showConfirmation(activity, rh.gs(R.string.tempbasal_label), lastRun.constraintsProcessed?.toSpanned()
uel.log(Action.ACCEPTS_TEMP_BASAL, Sources.Overview) ?: "".toSpanned(), {
(context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager?)?.cancel(Constants.notificationID) uel.log(Action.ACCEPTS_TEMP_BASAL, Sources.Overview)
rxBus.send(EventWearInitiateAction("cancelChangeRequest")) (context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager?)?.cancel(Constants.notificationID)
Thread { loop.acceptChangeRequest() }.run() rxBus.send(EventWearInitiateAction("cancelChangeRequest"))
binding.buttonsLayout.acceptTempButton.visibility = View.GONE Thread { loop.acceptChangeRequest() }.run()
}) binding.buttonsLayout.acceptTempButton.visibility = View.GONE
})
}) })
} }
} }
@ -558,10 +547,18 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
binding.buttonsLayout.calibrationButton.visibility = (xDripIsBgSource && actualBG != null && sp.getBoolean(R.string.key_show_calibration_button, true)).toVisibility() binding.buttonsLayout.calibrationButton.visibility = (xDripIsBgSource && actualBG != null && sp.getBoolean(R.string.key_show_calibration_button, true)).toVisibility()
if (dexcomIsSource) { if (dexcomIsSource) {
binding.buttonsLayout.cgmButton.setCompoundDrawablesWithIntrinsicBounds(null, rh.gd(R.drawable.ic_byoda), null, null) binding.buttonsLayout.cgmButton.setCompoundDrawablesWithIntrinsicBounds(null, rh.gd(R.drawable.ic_byoda), null, null)
binding.buttonsLayout.cgmButton.setTextColor(rh.gc(R.color.colorLightGray)) for (drawable in binding.buttonsLayout.cgmButton.compoundDrawables) {
drawable?.mutate()
drawable?.colorFilter = PorterDuffColorFilter(rh.gac(context, R.attr.cgmdexColor), PorterDuff.Mode.SRC_IN)
}
binding.buttonsLayout.cgmButton.setTextColor(rh.gac(context, R.attr.cgmdexColor))
} else if (xDripIsBgSource) { } else if (xDripIsBgSource) {
binding.buttonsLayout.cgmButton.setCompoundDrawablesWithIntrinsicBounds(null, rh.gd(R.drawable.ic_xdrip), null, null) binding.buttonsLayout.cgmButton.setCompoundDrawablesWithIntrinsicBounds(null, rh.gd(R.drawable.ic_xdrip), null, null)
binding.buttonsLayout.cgmButton.setTextColor(rh.gc(R.color.colorCalibrationButton)) for (drawable in binding.buttonsLayout.cgmButton.compoundDrawables) {
drawable?.mutate()
drawable?.colorFilter = PorterDuffColorFilter(rh.gac(context, R.attr.cgmxdripColor), PorterDuff.Mode.SRC_IN)
}
binding.buttonsLayout.cgmButton.setTextColor(rh.gac(context, R.attr.cgmxdripColor))
} }
binding.buttonsLayout.cgmButton.visibility = (sp.getBoolean(R.string.key_show_cgm_button, false) && (xDripIsBgSource || dexcomIsSource)).toVisibility() binding.buttonsLayout.cgmButton.visibility = (sp.getBoolean(R.string.key_show_cgm_button, false) && (xDripIsBgSource || dexcomIsSource)).toVisibility()
@ -573,7 +570,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
if (event.isEnabled && event.trigger.shouldRun()) if (event.isEnabled && event.trigger.shouldRun())
context?.let { context -> context?.let { context ->
SingleClickButton(context).also { SingleClickButton(context).also {
it.setTextColor(rh.gc(R.color.colorTreatmentButton)) it.setTextColor(rh.gac(context, R.attr.treatmentButton))
it.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10f) it.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10f)
it.layoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 0.5f).also { l -> it.layoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 0.5f).also { l ->
l.setMargins(0, 0, rh.dpToPx(-4), 0) l.setMargins(0, 0, rh.dpToPx(-4), 0)
@ -582,11 +579,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
it.text = event.title it.text = event.title
it.setOnClickListener { it.setOnClickListener {
OKDialog.showConfirmation( OKDialog.showConfirmation(context, rh.gs(R.string.run_question, event.title), { handler.post { automationPlugin.processEvent(event) } })
context,
rh.gs(R.string.run_question, event.title),
{ handler.post { automationPlugin.processEvent(event) } }
)
} }
binding.buttonsLayout.userButtonsLayout.addView(it) binding.buttonsLayout.userButtonsLayout.addView(it)
} }
@ -721,12 +714,12 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
val graph = GraphView(context) val graph = GraphView(context)
graph.layoutParams = graph.layoutParams =
LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, rh.dpToPx(skinProvider.activeSkin().secondaryGraphHeight)).also { it.setMargins(0, rh.dpToPx(15), 0, rh.dpToPx(10)) } LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, rh.dpToPx(skinProvider.activeSkin().secondaryGraphHeight)).also { it.setMargins(0, rh.dpToPx(15), 0, rh.dpToPx(10)) }
graph.gridLabelRenderer?.gridColor = rh.gc(R.color.graphgrid) graph.gridLabelRenderer?.gridColor = rh.gac(context, R.attr.graphgrid)
graph.gridLabelRenderer?.reloadStyles() graph.gridLabelRenderer?.reloadStyles()
graph.gridLabelRenderer?.isHorizontalLabelsVisible = false graph.gridLabelRenderer?.isHorizontalLabelsVisible = false
graph.gridLabelRenderer?.labelVerticalWidth = axisWidth graph.gridLabelRenderer?.labelVerticalWidth = axisWidth
graph.gridLabelRenderer?.numVerticalLabels = 3 graph.gridLabelRenderer?.numVerticalLabels = 3
graph.viewport.backgroundColor = Color.argb(20, 255, 255, 255) // 8% of gray graph.viewport.backgroundColor = rh.gac(context, R.attr.viewPortbackgroundColor)
relativeLayout.addView(graph) relativeLayout.addView(graph)
val label = TextView(context) val label = TextView(context)
@ -745,11 +738,11 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
var task: Runnable? = null var task: Runnable? = null
private fun scheduleUpdateGUI(from: String) { private fun scheduleUpdateGUI() {
class UpdateRunnable : Runnable { class UpdateRunnable : Runnable {
override fun run() { override fun run() {
overviewPlugin.refreshLoop(from) refreshAll()
task = null task = null
} }
} }
@ -759,20 +752,20 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
} }
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
@Suppress("UNUSED_PARAMETER") fun updateBg() {
fun updateBg(from: String) { _binding ?: return
val units = profileFunction.getUnits() val units = profileFunction.getUnits()
binding.infoLayout.bg.text = overviewData.lastBg?.valueToUnitsString(units) binding.infoLayout.bg.text = overviewData.lastBg?.valueToUnitsString(units)
?: rh.gs(R.string.notavailable) ?: rh.gs(R.string.notavailable)
binding.infoLayout.bg.setTextColor(overviewData.lastBgColor) binding.infoLayout.bg.setTextColor(overviewData.lastBgColor(context))
binding.infoLayout.arrow.setImageResource(trendCalculator.getTrendArrow(overviewData.lastBg).directionToIcon()) binding.infoLayout.arrow.setImageResource(trendCalculator.getTrendArrow(overviewData.lastBg).directionToIcon())
binding.infoLayout.arrow.setColorFilter(overviewData.lastBgColor) binding.infoLayout.arrow.setColorFilter(overviewData.lastBgColor(context))
binding.infoLayout.arrow.contentDescription = overviewData.lastBgDescription + " " + rh.gs(R.string.and) + " " + trendCalculator.getTrendDescription(overviewData.lastBg) binding.infoLayout.arrow.contentDescription = overviewData.lastBgDescription + " " + rh.gs(R.string.and) + " " + trendCalculator.getTrendDescription(overviewData.lastBg)
val glucoseStatus = glucoseStatusProvider.glucoseStatusData val glucoseStatus = glucoseStatusProvider.glucoseStatusData
if (glucoseStatus != null) { if (glucoseStatus != null) {
binding.infoLayout.deltaLarge.text = Profile.toSignedUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units) binding.infoLayout.deltaLarge.text = Profile.toSignedUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units)
binding.infoLayout.deltaLarge.setTextColor(overviewData.lastBgColor) binding.infoLayout.deltaLarge.setTextColor(overviewData.lastBgColor(context))
binding.infoLayout.delta.text = Profile.toSignedUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units) binding.infoLayout.delta.text = Profile.toSignedUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units)
binding.infoLayout.avgDelta.text = Profile.toSignedUnitsString(glucoseStatus.shortAvgDelta, glucoseStatus.shortAvgDelta * Constants.MGDL_TO_MMOLL, units) binding.infoLayout.avgDelta.text = Profile.toSignedUnitsString(glucoseStatus.shortAvgDelta, glucoseStatus.shortAvgDelta * Constants.MGDL_TO_MMOLL, units)
binding.infoLayout.longAvgDelta.text = Profile.toSignedUnitsString(glucoseStatus.longAvgDelta, glucoseStatus.longAvgDelta * Constants.MGDL_TO_MMOLL, units) binding.infoLayout.longAvgDelta.text = Profile.toSignedUnitsString(glucoseStatus.longAvgDelta, glucoseStatus.longAvgDelta * Constants.MGDL_TO_MMOLL, units)
@ -809,70 +802,76 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
} }
} }
@Suppress("UNUSED_PARAMETER") fun updateProfile() {
fun updateProfile(from: String) { _binding ?: return
val profileBackgroundColor = val profileBackgroundColor =
profileFunction.getProfile()?.let { profileFunction.getProfile()?.let {
if (it is ProfileSealed.EPS) { if (it is ProfileSealed.EPS) {
if (it.value.originalPercentage != 100 || it.value.originalTimeshift != 0L || it.value.originalDuration != 0L) if (it.value.originalPercentage != 100 || it.value.originalTimeshift != 0L || it.value.originalDuration != 0L)
rh.gc(R.color.ribbonWarning) rh.gac(context, R.attr.ribbonWarningColor)
else rh.gc(R.color.ribbonDefault) else rh.gac(context, R.attr.ribbonDefaultColor)
} else if (it is ProfileSealed.PS) { } else if (it is ProfileSealed.PS) {
rh.gc(R.color.ribbonDefault) rh.gac(context, R.attr.ribbonDefaultColor)
} else { } else {
rh.gc(R.color.ribbonDefault) rh.gac(context, R.attr.ribbonDefaultColor)
} }
} ?: rh.gc(R.color.ribbonCritical) } ?: rh.gac(context, R.attr.ribbonCriticalColor)
val profileTextColor = val profileTextColor =
profileFunction.getProfile()?.let { profileFunction.getProfile()?.let {
if (it is ProfileSealed.EPS) { if (it is ProfileSealed.EPS) {
if (it.value.originalPercentage != 100 || it.value.originalTimeshift != 0L || it.value.originalDuration != 0L) if (it.value.originalPercentage != 100 || it.value.originalTimeshift != 0L || it.value.originalDuration != 0L)
rh.gc(R.color.ribbonTextWarning) rh.gac(context, R.attr.ribbonTextWarningColor)
else rh.gc(R.color.ribbonTextDefault) else rh.gac(context, R.attr.ribbonTextDefaultColor)
} else if (it is ProfileSealed.PS) { } else if (it is ProfileSealed.PS) {
rh.gc(R.color.ribbonTextDefault) rh.gac(context, R.attr.ribbonTextDefaultColor)
} else { } else {
rh.gc(R.color.ribbonTextDefault) rh.gac(context, R.attr.ribbonTextDefaultColor)
} }
} ?: rh.gc(R.color.ribbonTextDefault) } ?: rh.gac(context, R.attr.ribbonTextDefaultColor)
binding.activeProfile.text = profileFunction.getProfileNameWithRemainingTime() binding.activeProfile.text = profileFunction.getProfileNameWithRemainingTime()
binding.activeProfile.setBackgroundColor(profileBackgroundColor) binding.activeProfile.setBackgroundColor(profileBackgroundColor)
binding.activeProfile.setTextColor(profileTextColor) binding.activeProfile.setTextColor(profileTextColor)
} }
@Suppress("UNUSED_PARAMETER") private fun updateTemporaryBasal() {
fun updateTemporaryBasal(from: String) { _binding ?: return
binding.infoLayout.baseBasal.text = overviewData.temporaryBasalText binding.infoLayout.baseBasal.text = overviewData.temporaryBasalText(iobCobCalculator)
binding.infoLayout.baseBasal.setTextColor(overviewData.temporaryBasalColor) binding.infoLayout.baseBasal.setTextColor(overviewData.temporaryBasalColor(context, iobCobCalculator))
binding.infoLayout.baseBasalIcon.setImageResource(overviewData.temporaryBasalIcon) binding.infoLayout.baseBasalIcon.setImageResource(overviewData.temporaryBasalIcon(iobCobCalculator))
binding.infoLayout.basalLayout.setOnClickListener { binding.infoLayout.basalLayout.setOnClickListener {
activity?.let { OKDialog.show(it, rh.gs(R.string.basal), overviewData.temporaryBasalDialogText) } activity?.let { OKDialog.show(it, rh.gs(R.string.basal), overviewData.temporaryBasalDialogText(iobCobCalculator)) }
} }
} }
@Suppress("UNUSED_PARAMETER") private fun updateExtendedBolus() {
fun updateExtendedBolus(from: String) { _binding ?: return
val pump = activePlugin.activePump val pump = activePlugin.activePump
binding.infoLayout.extendedBolus.text = overviewData.extendedBolusText binding.infoLayout.extendedBolus.text = overviewData.extendedBolusText(iobCobCalculator)
binding.infoLayout.extendedLayout.setOnClickListener { binding.infoLayout.extendedLayout.setOnClickListener {
activity?.let { OKDialog.show(it, rh.gs(R.string.extended_bolus), overviewData.extendedBolusDialogText) } activity?.let { OKDialog.show(it, rh.gs(R.string.extended_bolus), overviewData.extendedBolusDialogText(iobCobCalculator)) }
} }
binding.infoLayout.extendedLayout.visibility = (iobCobCalculator.getExtendedBolus(dateUtil.now()) != null && !pump.isFakingTempsByExtendedBoluses).toVisibility() binding.infoLayout.extendedLayout.visibility = (iobCobCalculator.getExtendedBolus(dateUtil.now()) != null && !pump.isFakingTempsByExtendedBoluses).toVisibility()
} }
@Suppress("UNUSED_PARAMETER") fun updateTime() {
fun updateTime(from: String) { _binding ?: return
binding.infoLayout.time.text = dateUtil.timeString(dateUtil.now()) binding.infoLayout.time.text = dateUtil.timeString(dateUtil.now())
// Status lights // Status lights
val isPatchPump = activePlugin.activePump.pumpDescription.isPatchPump val pump = activePlugin.activePump
val isPatchPump = pump.pumpDescription.isPatchPump
binding.statusLightsLayout.apply { binding.statusLightsLayout.apply {
cannulaOrPatch.setImageResource(if (isPatchPump) R.drawable.ic_patch_pump_outline else R.drawable.ic_cp_age_cannula) cannulaOrPatch.setImageResource(if (isPatchPump) R.drawable.ic_patch_pump_outline else R.drawable.ic_cp_age_cannula)
cannulaOrPatch.contentDescription = rh.gs(if (isPatchPump) R.string.statuslights_patch_pump_age else R.string.statuslights_cannula_age) cannulaOrPatch.contentDescription = rh.gs(if (isPatchPump) R.string.statuslights_patch_pump_age else R.string.statuslights_cannula_age)
cannulaOrPatch.scaleX = if (isPatchPump) 1.4f else 2f cannulaOrPatch.scaleX = if (isPatchPump) 1.4f else 2f
cannulaOrPatch.scaleY = cannulaOrPatch.scaleX cannulaOrPatch.scaleY = cannulaOrPatch.scaleX
insulinAge.visibility = isPatchPump.not().toVisibility() insulinAge.visibility = isPatchPump.not().toVisibility()
batteryLayout.visibility = (!isPatchPump || pump.pumpDescription.useHardwareLink).toVisibility()
pbAge.visibility = (pump.pumpDescription.isBatteryReplaceable || pump.isBatteryChangeLoggingEnabled()).toVisibility()
val useBatteryLevel = (pump.model() == PumpType.OMNIPOD_EROS && pump is OmnipodErosPumpPlugin)
|| (pump.model() != PumpType.ACCU_CHEK_COMBO && pump.model() != PumpType.OMNIPOD_DASH)
batteryLevel.visibility = useBatteryLevel.toVisibility()
statusLights.visibility = (sp.getBoolean(R.string.key_show_statuslights, true) || config.NSCLIENT).toVisibility() statusLights.visibility = (sp.getBoolean(R.string.key_show_statuslights, true) || config.NSCLIENT).toVisibility()
} }
statusLightHandler.updateStatusLights( statusLightHandler.updateStatusLights(
@ -888,14 +887,14 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
processAps() processAps()
} }
@Suppress("UNUSED_PARAMETER") fun updateIobCob() {
fun updateIobCob(from: String) { _binding ?: return
binding.infoLayout.iob.text = overviewData.iobText binding.infoLayout.iob.text = overviewData.iobText(iobCobCalculator)
binding.infoLayout.iobLayout.setOnClickListener { binding.infoLayout.iobLayout.setOnClickListener {
activity?.let { OKDialog.show(it, rh.gs(R.string.iob), overviewData.iobDialogText) } activity?.let { OKDialog.show(it, rh.gs(R.string.iob), overviewData.iobDialogText(iobCobCalculator)) }
} }
// cob // cob
var cobText = overviewData.cobInfo?.displayText(rh, dateUtil, buildHelper.isEngineeringMode()) ?: rh.gs(R.string.value_unavailable_short) var cobText = overviewData.cobInfo(iobCobCalculator).displayText(rh, dateUtil, buildHelper.isEngineeringMode()) ?: rh.gs(R.string.value_unavailable_short)
val constraintsProcessed = loop.lastRun?.constraintsProcessed val constraintsProcessed = loop.lastRun?.constraintsProcessed
val lastRun = loop.lastRun val lastRun = loop.lastRun
@ -916,14 +915,13 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
} }
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
@Suppress("UNUSED_PARAMETER") fun updateTemporaryTarget() {
fun updateTemporaryTarget(from: String) { _binding ?: return
val units = profileFunction.getUnits() val units = profileFunction.getUnits()
if (overviewData.temporaryTarget?.isInProgress(dateUtil) == false) overviewData.temporaryTarget = null
val tempTarget = overviewData.temporaryTarget val tempTarget = overviewData.temporaryTarget
if (tempTarget != null) { if (tempTarget != null) {
binding.tempTarget.setTextColor(rh.gc(R.color.ribbonTextWarning)) binding.tempTarget.setTextColor(rh.gac(context, R.attr.ribbonTextWarningColor))
binding.tempTarget.setBackgroundColor(rh.gc(R.color.ribbonWarning)) binding.tempTarget.setBackgroundColor(rh.gac(context, R.attr.ribbonWarningColor))
binding.tempTarget.text = Profile.toTargetRangeString(tempTarget.lowTarget, tempTarget.highTarget, GlucoseUnit.MGDL, units) + " " + dateUtil.untilString(tempTarget.end, rh) binding.tempTarget.text = Profile.toTargetRangeString(tempTarget.lowTarget, tempTarget.highTarget, GlucoseUnit.MGDL, units) + " " + dateUtil.untilString(tempTarget.end, rh)
} else { } else {
// If the target is not the same as set in the profile then oref has overridden it // If the target is not the same as set in the profile then oref has overridden it
@ -933,19 +931,19 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
if (targetUsed != 0.0 && abs(profile.getTargetMgdl() - targetUsed) > 0.01) { if (targetUsed != 0.0 && abs(profile.getTargetMgdl() - targetUsed) > 0.01) {
aapsLogger.debug("Adjusted target. Profile: ${profile.getTargetMgdl()} APS: $targetUsed") aapsLogger.debug("Adjusted target. Profile: ${profile.getTargetMgdl()} APS: $targetUsed")
binding.tempTarget.text = Profile.toTargetRangeString(targetUsed, targetUsed, GlucoseUnit.MGDL, units) binding.tempTarget.text = Profile.toTargetRangeString(targetUsed, targetUsed, GlucoseUnit.MGDL, units)
binding.tempTarget.setTextColor(rh.gc(R.color.ribbonTextWarning)) binding.tempTarget.setTextColor(rh.gac(context, R.attr.ribbonTextWarningColor))
binding.tempTarget.setBackgroundColor(rh.gc(R.color.tempTargetBackground)) binding.tempTarget.setBackgroundColor(rh.gac(context, R.attr.tempTargetBackgroundColor))
} else { } else {
binding.tempTarget.setTextColor(rh.gc(R.color.ribbonTextDefault)) binding.tempTarget.setTextColor(rh.gac(context, R.attr.ribbonTextDefaultColor))
binding.tempTarget.setBackgroundColor(rh.gc(R.color.ribbonDefault)) binding.tempTarget.setBackgroundColor(rh.gac(context, R.attr.ribbonDefaultColor))
binding.tempTarget.text = Profile.toTargetRangeString(profile.getTargetLowMgdl(), profile.getTargetHighMgdl(), GlucoseUnit.MGDL, units) binding.tempTarget.text = Profile.toTargetRangeString(profile.getTargetLowMgdl(), profile.getTargetHighMgdl(), GlucoseUnit.MGDL, units)
} }
} }
} }
} }
@Suppress("UNUSED_PARAMETER") private fun updateGraph() {
fun updateGraph(from: String) { _binding ?: return
val pump = activePlugin.activePump val pump = activePlugin.activePump
val graphData = GraphData(injector, binding.graphsLayout.bgGraph, overviewData) val graphData = GraphData(injector, binding.graphsLayout.bgGraph, overviewData)
val menuChartSettings = overviewMenus.setting val menuChartSettings = overviewMenus.setting
@ -953,6 +951,8 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
graphData.addBgReadings(menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal]) graphData.addBgReadings(menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal])
if (buildHelper.isDev()) graphData.addBucketedData() if (buildHelper.isDev()) graphData.addBucketedData()
graphData.addTreatments() graphData.addTreatments()
if (menuChartSettings[0][OverviewMenus.CharType.TREAT.ordinal])
graphData.addTherapyEvents()
if (menuChartSettings[0][OverviewMenus.CharType.ACT.ordinal]) if (menuChartSettings[0][OverviewMenus.CharType.ACT.ordinal])
graphData.addActivity(0.8) graphData.addActivity(0.8)
if ((pump.pumpDescription.isTempBasalCapable || config.NSCLIENT) && menuChartSettings[0][OverviewMenus.CharType.BAS.ordinal]) if ((pump.pumpDescription.isTempBasalCapable || config.NSCLIENT) && menuChartSettings[0][OverviewMenus.CharType.BAS.ordinal])
@ -1023,13 +1023,14 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
} }
} }
@Suppress("UNUSED_PARAMETER") private fun updateCalcProgress() {
fun updateCalcProgress(from: String) { _binding ?: return
binding.graphsLayout.iobCalculationProgress.text = overviewData.calcProgress binding.progressBar.progress = overviewData.calcProgressPct
binding.progressBar.visibility = (overviewData.calcProgressPct != 100).toVisibility()
} }
@Suppress("UNUSED_PARAMETER") private fun updateSensitivity() {
fun updateSensitivity(from: String) { _binding ?: return
if (sp.getBoolean(R.string.key_openapsama_useautosens, false) && constraintChecker.isAutosensModeEnabled().value()) { if (sp.getBoolean(R.string.key_openapsama_useautosens, false) && constraintChecker.isAutosensModeEnabled().value()) {
binding.infoLayout.sensitivityIcon.setImageResource(R.drawable.ic_swap_vert_black_48dp_green) binding.infoLayout.sensitivityIcon.setImageResource(R.drawable.ic_swap_vert_black_48dp_green)
} else { } else {
@ -1037,20 +1038,20 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
} }
binding.infoLayout.sensitivity.text = binding.infoLayout.sensitivity.text =
overviewData.lastAutosensData?.let { autosensData -> overviewData.lastAutosensData(iobCobCalculator)?.let { autosensData ->
String.format(Locale.ENGLISH, "%.0f%%", autosensData.autosensResult.ratio * 100) String.format(Locale.ENGLISH, "%.0f%%", autosensData.autosensResult.ratio * 100)
} ?: "" } ?: ""
} }
@Suppress("UNUSED_PARAMETER") private fun updatePumpStatus() {
fun updatePumpStatus(from: String) { _binding ?: return
val status = overviewData.pumpStatus val status = overviewData.pumpStatus
binding.pumpStatus.text = status binding.pumpStatus.text = status
binding.pumpStatusLayout.visibility = (status != "").toVisibility() binding.pumpStatusLayout.visibility = (status != "").toVisibility()
} }
@Suppress("UNUSED_PARAMETER") private fun updateNotification() {
fun updateNotification(from: String) { _binding ?: return
binding.notifications.let { notificationStore.updateNotifications(it) } binding.notifications.let { notificationStore.updateNotifications(it) }
} }
} }

View file

@ -1,10 +1,13 @@
package info.nightscout.androidaps.plugins.general.overview package info.nightscout.androidaps.plugins.general.overview
import android.content.Context
import android.text.SpannableString import android.text.SpannableString
import android.text.style.BackgroundColorSpan
import android.text.style.ForegroundColorSpan import android.text.style.ForegroundColorSpan
import android.view.Menu import android.view.Menu
import android.view.View import android.view.View
import android.widget.ImageButton import android.widget.ImageButton
import androidx.annotation.AttrRes
import androidx.annotation.ColorRes import androidx.annotation.ColorRes
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
@ -31,18 +34,18 @@ class OverviewMenus @Inject constructor(
private val loop: Loop, private val loop: Loop,
private val config: Config private val config: Config
) { ) {
enum class CharType(@StringRes val nameId: Int, @AttrRes val attrId: Int, @AttrRes val attrTextId: Int, val primary: Boolean, val secondary: Boolean, @StringRes val shortnameId: Int) {
enum class CharType(@StringRes val nameId: Int, @ColorRes val colorId: Int, val primary: Boolean, val secondary: Boolean, @StringRes val shortnameId: Int) { PRE(R.string.overview_show_predictions, R.attr.predictionColor, R.attr.menuTextColor, primary = true, secondary = false, shortnameId = R.string.prediction_shortname),
PRE(R.string.overview_show_predictions, R.color.prediction, primary = true, secondary = false, shortnameId = R.string.prediction_shortname), TREAT(R.string.overview_show_treatments, R.attr.predictionColor, R.attr.menuTextColor, primary = true, secondary = false, shortnameId = R.string.treatments_shortname),
BAS(R.string.overview_show_basals, R.color.basal, primary = true, secondary = false, shortnameId = R.string.basal_shortname), BAS(R.string.overview_show_basals, R.attr.basal, R.attr.menuTextColor, primary = true, secondary = false,shortnameId = R.string.basal_shortname),
ABS(R.string.overview_show_absinsulin, R.color.iob, primary = false, secondary = true, shortnameId = R.string.abs_insulin_shortname), ABS(R.string.overview_show_absinsulin, R.attr.iobColor, R.attr.menuTextColor, primary = false, secondary = true,shortnameId = R.string.abs_insulin_shortname),
IOB(R.string.overview_show_iob, R.color.iob, primary = false, secondary = true, shortnameId = R.string.iob), IOB(R.string.overview_show_iob, R.attr.iobColor, R.attr.menuTextColor, primary = false, secondary = true,shortnameId = R.string.iob),
COB(R.string.overview_show_cob, R.color.cob, primary = false, secondary = true, shortnameId = R.string.cob), COB(R.string.overview_show_cob, R.attr.cobColor, R.attr.menuTextColor, primary = false, secondary = true,shortnameId = R.string.cob),
DEV(R.string.overview_show_deviations, R.color.bgi, primary = false, secondary = true, shortnameId = R.string.deviation_shortname), DEV(R.string.overview_show_deviations, R.attr.bgiColor, R.attr.menuTextColor, primary = false, secondary = true,shortnameId = R.string.deviation_shortname),
BGI(R.string.overview_show_bgi, R.color.bgi, primary = false, secondary = true, shortnameId = R.string.bgi_shortname), BGI(R.string.overview_show_bgi, R.attr.bgiColor, R.attr.menuTextColor, primary = false, secondary = true,shortnameId = R.string.bgi_shortname),
SEN(R.string.overview_show_sensitivity, R.color.ratio, primary = false, secondary = true, shortnameId = R.string.sensitivity_shortname), SEN(R.string.overview_show_sensitivity, R.attr.ratioColor, R.attr.menuTextColorInverse, primary = false, secondary = true,shortnameId = R.string.sensitivity_shortname),
ACT(R.string.overview_show_activity, R.color.activity, primary = true, secondary = false, shortnameId = R.string.activity_shortname), ACT(R.string.overview_show_activity, R.attr.activityColor, R.attr.menuTextColor, primary = true, secondary = false,shortnameId = R.string.activity_shortname),
DEVSLOPE(R.string.overview_show_deviationslope, R.color.devslopepos, primary = false, secondary = true, shortnameId = R.string.devslope_shortname) DEVSLOPE(R.string.overview_show_deviationslope, R.attr.devslopeposColor, R.attr.menuTextColor, primary = false, secondary = true,shortnameId = R.string.devslope_shortname)
} }
companion object { companion object {
@ -86,7 +89,7 @@ class OverviewMenus @Inject constructor(
} }
} }
fun setupChartMenu(chartButton: ImageButton) { fun setupChartMenu(context: Context, chartButton: ImageButton) {
val settingsCopy = setting val settingsCopy = setting
val numOfGraphs = settingsCopy.size // 1 main + x secondary val numOfGraphs = settingsCopy.size // 1 main + x secondary
@ -119,8 +122,9 @@ class OverviewMenus @Inject constructor(
if (insert) { if (insert) {
val item = popup.menu.add(Menu.NONE, m.ordinal + 100 * (g + 1), Menu.NONE, rh.gs(m.nameId)) val item = popup.menu.add(Menu.NONE, m.ordinal + 100 * (g + 1), Menu.NONE, rh.gs(m.nameId))
val title = item.title val title = item.title
val s = SpannableString(title) val s = SpannableString(" " + title + " ")
s.setSpan(ForegroundColorSpan(rh.gc(m.colorId)), 0, s.length, 0) s.setSpan(ForegroundColorSpan(rh.gac(context, m.attrTextId)), 0, s.length, 0)
s.setSpan(BackgroundColorSpan(rh.gac(context, m.attrId)), 0, s.length, 0)
item.title = s item.title = s
item.isCheckable = true item.isCheckable = true
item.isChecked = settingsCopy[g][m.ordinal] item.isChecked = settingsCopy[g][m.ordinal]
@ -152,7 +156,7 @@ class OverviewMenus @Inject constructor(
} }
} }
storeGraphConfig() storeGraphConfig()
setupChartMenu(chartButton) setupChartMenu(context, chartButton)
rxBus.send(EventRefreshOverview("OnMenuItemClickListener", now = true)) rxBus.send(EventRefreshOverview("OnMenuItemClickListener", now = true))
return@setOnMenuItemClickListener true return@setOnMenuItemClickListener true
} }

View file

@ -4,25 +4,26 @@ import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference import androidx.preference.SwitchPreference
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.events.EventPumpStatusChanged
import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.events.*
import info.nightscout.androidaps.extensions.* import info.nightscout.androidaps.extensions.*
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.Config
import info.nightscout.shared.logging.AAPSLogger import info.nightscout.androidaps.interfaces.Overview
import info.nightscout.shared.logging.LTag import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.plugins.aps.events.EventLoopInvoked import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.overview.events.* import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
import info.nightscout.androidaps.plugins.general.overview.events.EventUpdateOverviewCalcProgress
import info.nightscout.androidaps.plugins.general.overview.events.EventUpdateOverviewNotification
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.Scale import info.nightscout.androidaps.plugins.general.overview.graphExtensions.Scale
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.ScaledDataPoint import info.nightscout.androidaps.plugins.general.overview.graphExtensions.ScaledDataPoint
import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationStore import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationStore
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventBucketedDataCreated
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
@ -41,9 +42,6 @@ class OverviewPlugin @Inject constructor(
private val aapsSchedulers: AapsSchedulers, private val aapsSchedulers: AapsSchedulers,
rh: ResourceHelper, rh: ResourceHelper,
private val config: Config, private val config: Config,
private val dateUtil: DateUtil,
private val iobCobCalculator: IobCobCalculator,
private val repository: AppRepository,
private val overviewData: OverviewData, private val overviewData: OverviewData,
private val overviewMenus: OverviewMenus private val overviewMenus: OverviewMenus
) : PluginBase( ) : PluginBase(
@ -89,62 +87,9 @@ class OverviewPlugin @Inject constructor(
disposable += rxBus disposable += rxBus
.toObservable(EventIobCalculationProgress::class.java) .toObservable(EventIobCalculationProgress::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ overviewData.calcProgress = it.progress; overviewBus.send(EventUpdateOverviewCalcProgress("EventIobCalculationProgress")) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTempBasalChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ overviewBus.send(EventUpdateOverviewTemporaryBasal("EventTempBasalChange")) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventExtendedBolusChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ overviewBus.send(EventUpdateOverviewExtendedBolus("EventExtendedBolusChange")) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventNewBG::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ loadBg("EventNewBG") }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTempTargetChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ loadTemporaryTarget("EventTempTargetChange") }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTreatmentChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ .subscribe({
loadIobCobResults("EventTreatmentChange") overviewData.calcProgressPct = it.pass.finalPercent(it.progressPct)
overviewData.prepareTreatmentsData("EventTreatmentChange") overviewBus.send(EventUpdateOverviewCalcProgress("EventIobCalculationProgress"))
overviewBus.send(EventUpdateOverviewGraph("EventTreatmentChange"))
}, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTherapyEventChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({
overviewData.prepareTreatmentsData("EventTherapyEventChange")
overviewBus.send(EventUpdateOverviewGraph("EventTherapyEventChange"))
}, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventBucketedDataCreated::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({
overviewData.prepareBucketedData("EventBucketedDataCreated")
overviewData.prepareBgData("EventBucketedDataCreated")
overviewBus.send(EventUpdateOverviewGraph("EventBucketedDataCreated"))
}, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventLoopInvoked::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ overviewData.preparePredictions("EventLoopInvoked") }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventEffectiveProfileSwitchChanged::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({
loadProfile("EventEffectiveProfileSwitchChanged")
overviewData.prepareBasalData("EventEffectiveProfileSwitchChanged")
}, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({
if (it.cause !is EventCustomCalculationFinished) refreshLoop("EventAutosensCalculationFinished")
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventPumpStatusChanged::class.java) .toObservable(EventPumpStatusChanged::class.java)
@ -152,20 +97,7 @@ class OverviewPlugin @Inject constructor(
.subscribe({ .subscribe({
overviewData.pumpStatus = it.getStatus(rh) overviewData.pumpStatus = it.getStatus(rh)
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventPreferenceChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ event ->
if (event.isChanged(rh, R.string.key_units)) {
overviewData.reset()
overviewData.prepareBucketedData("EventBucketedDataCreated")
overviewData.prepareBgData("EventBucketedDataCreated")
overviewBus.send(EventUpdateOverviewGraph("EventBucketedDataCreated"))
loadAll("EventPreferenceChange")
}
}, fabricPrivacy::logException)
Thread { loadAll("onResume") }.start()
} }
override fun onStop() { override fun onStop() {
@ -243,7 +175,7 @@ class OverviewPlugin @Inject constructor(
.storeDouble(R.string.key_statuslights_bat_critical, sp, rh) .storeDouble(R.string.key_statuslights_bat_critical, sp, rh)
.storeInt(R.string.key_boluswizard_percentage, sp, rh) .storeInt(R.string.key_boluswizard_percentage, sp, rh)
} }
/*
@Volatile @Volatile
var runningRefresh = false var runningRefresh = false
override fun refreshLoop(from: String) { override fun refreshLoop(from: String) {
@ -284,38 +216,5 @@ class OverviewPlugin @Inject constructor(
overviewBus.send(EventUpdateOverviewGraph(from)) overviewBus.send(EventUpdateOverviewGraph(from))
aapsLogger.debug(LTag.UI, "loadAll finished") aapsLogger.debug(LTag.UI, "loadAll finished")
} }
*/
private fun loadProfile(from: String) {
overviewBus.send(EventUpdateOverviewProfile(from))
}
private fun loadTemporaryTarget(from: String) {
val tempTarget = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet()
if (tempTarget is ValueWrapper.Existing) overviewData.temporaryTarget = tempTarget.value
else overviewData.temporaryTarget = null
overviewBus.send(EventUpdateOverviewTemporaryTarget(from))
}
private fun loadAsData(from: String) {
overviewData.lastAutosensData = iobCobCalculator.ads.getLastAutosensData("Overview", aapsLogger, dateUtil)
overviewBus.send(EventUpdateOverviewSensitivity(from))
}
private fun loadBg(from: String) {
val gvWrapped = repository.getLastGlucoseValueWrapped().blockingGet()
if (gvWrapped is ValueWrapper.Existing) overviewData.lastBg = gvWrapped.value
else overviewData.lastBg = null
overviewBus.send(EventUpdateOverviewBg(from))
}
private fun loadIobCobResults(from: String) {
overviewData.bolusIob = iobCobCalculator.calculateIobFromBolus().round()
overviewData.basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round()
overviewData.cobInfo = iobCobCalculator.getCobInfo(true, "Overview COB")
val lastCarbs = repository.getLastCarbsRecordWrapped().blockingGet()
overviewData.lastCarbsTime = if (lastCarbs is ValueWrapper.Existing) lastCarbs.value.timestamp else 0L
overviewBus.send(EventUpdateOverviewIobCob(from))
}
} }

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.plugins.general.overview package info.nightscout.androidaps.plugins.general.overview
import android.graphics.Color import android.graphics.Color
import android.view.View
import android.widget.TextView import android.widget.TextView
import androidx.annotation.StringRes import androidx.annotation.StringRes
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
@ -42,7 +41,7 @@ class StatusLightHandler @Inject constructor(
handleAge(careportal_cannula_age, TherapyEvent.Type.CANNULA_CHANGE, R.string.key_statuslights_cage_warning, 48.0, R.string.key_statuslights_cage_critical, 72.0) handleAge(careportal_cannula_age, TherapyEvent.Type.CANNULA_CHANGE, R.string.key_statuslights_cage_warning, 48.0, R.string.key_statuslights_cage_critical, 72.0)
handleAge(careportal_insulin_age, TherapyEvent.Type.INSULIN_CHANGE, R.string.key_statuslights_iage_warning, 72.0, R.string.key_statuslights_iage_critical, 144.0) handleAge(careportal_insulin_age, TherapyEvent.Type.INSULIN_CHANGE, R.string.key_statuslights_iage_warning, 72.0, R.string.key_statuslights_iage_critical, 144.0)
handleAge(careportal_sensor_age, TherapyEvent.Type.SENSOR_CHANGE, R.string.key_statuslights_sage_warning, 216.0, R.string.key_statuslights_sage_critical, 240.0) handleAge(careportal_sensor_age, TherapyEvent.Type.SENSOR_CHANGE, R.string.key_statuslights_sage_warning, 216.0, R.string.key_statuslights_sage_critical, 240.0)
if (pump.pumpDescription.isBatteryReplaceable || (pump is OmnipodErosPumpPlugin && pump.isUseRileyLinkBatteryLevel && pump.isBatteryChangeLoggingEnabled)) { if (pump.pumpDescription.isBatteryReplaceable || pump.isBatteryChangeLoggingEnabled()) {
handleAge(careportal_pb_age, TherapyEvent.Type.PUMP_BATTERY_CHANGE, R.string.key_statuslights_bage_warning, 216.0, R.string.key_statuslights_bage_critical, 240.0) handleAge(careportal_pb_age, TherapyEvent.Type.PUMP_BATTERY_CHANGE, R.string.key_statuslights_bage_warning, 216.0, R.string.key_statuslights_bage_critical, 240.0)
} }
if (!config.NSCLIENT) { if (!config.NSCLIENT) {
@ -58,16 +57,16 @@ class StatusLightHandler @Inject constructor(
} }
if (!config.NSCLIENT) { if (!config.NSCLIENT) {
if (pump.model() == PumpType.OMNIPOD_DASH) { // The Omnipod Eros does not report its battery level. However, some RileyLink alternatives do.
// Omnipod Dash does not report its battery level // Depending on the user's configuration, we will either show the battery level reported by the RileyLink or "n/a"
careportal_battery_level?.text = rh.gs(R.string.notavailable) // Pump instance check is needed because at startup, the pump can still be VirtualPumpPlugin and that will cause a crash
careportal_battery_level?.setTextColor(Color.WHITE) val erosBatteryLinkAvailable = pump.model() == PumpType.OMNIPOD_EROS && pump is OmnipodErosPumpPlugin && pump.isUseRileyLinkBatteryLevel
} else if (pump.model() == PumpType.OMNIPOD_EROS && pump is OmnipodErosPumpPlugin) { // instance of check is needed because at startup, pump can still be VirtualPumpPlugin and that will cause a crash because of the class cast below
// The Omnipod Eros does not report its battery level. However, some RileyLink alternatives do. if (pump.model().supportBatteryLevel || erosBatteryLinkAvailable) {
// Depending on the user's configuration, we will either show the battery level reported by the RileyLink or "n/a"
handleOmnipodErosBatteryLevel(careportal_battery_level, R.string.key_statuslights_bat_critical, 26.0, R.string.key_statuslights_bat_warning, 51.0, pump.batteryLevel.toDouble(), "%", pump.isUseRileyLinkBatteryLevel)
} else if (pump.model() != PumpType.ACCU_CHEK_COMBO) {
handleLevel(careportal_battery_level, R.string.key_statuslights_bat_critical, 26.0, R.string.key_statuslights_bat_warning, 51.0, pump.batteryLevel.toDouble(), "%") handleLevel(careportal_battery_level, R.string.key_statuslights_bat_critical, 26.0, R.string.key_statuslights_bat_warning, 51.0, pump.batteryLevel.toDouble(), "%")
} else {
careportal_battery_level?.text = rh.gs(R.string.notavailable)
careportal_battery_level?.setTextColor(rh.gac(careportal_battery_level.context, R.attr.defaultTextColor))
} }
} }
} }
@ -104,13 +103,4 @@ class StatusLightHandler @Inject constructor(
} }
} }
@Suppress("SameParameterValue") }
private fun handleOmnipodErosBatteryLevel(view: TextView?, criticalSetting: Int, criticalDefaultValue: Double, warnSetting: Int, warnDefaultValue: Double, level: Double, units: String, useRileyLinkBatteryLevel: Boolean) {
if (useRileyLinkBatteryLevel) {
handleLevel(view, criticalSetting, criticalDefaultValue, warnSetting, warnDefaultValue, level, units)
} else {
view?.text = rh.gs(R.string.notavailable)
view?.setTextColor(Color.WHITE)
}
}
}

View file

@ -2,32 +2,28 @@ package info.nightscout.androidaps.plugins.general.overview.activities
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.SparseArray
import android.view.LayoutInflater import android.view.*
import android.view.MenuItem import androidx.core.util.forEach
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.ItemTouchHelper.ACTION_STATE_DRAG
import androidx.recyclerview.widget.ItemTouchHelper.DOWN
import androidx.recyclerview.widget.ItemTouchHelper.END
import androidx.recyclerview.widget.ItemTouchHelper.START
import androidx.recyclerview.widget.ItemTouchHelper.UP
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.DaggerAppCompatActivityWithResult import info.nightscout.androidaps.activities.DaggerAppCompatActivityWithResult
import info.nightscout.androidaps.databinding.OverviewQuickwizardlistActivityBinding import info.nightscout.androidaps.databinding.OverviewQuickwizardlistActivityBinding
import info.nightscout.androidaps.databinding.OverviewQuickwizardlistItemBinding
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.utils.dragHelpers.ItemTouchHelperAdapter
import info.nightscout.androidaps.utils.dragHelpers.OnStartDragListener
import info.nightscout.androidaps.utils.dragHelpers.SimpleItemTouchHelperCallback
import info.nightscout.androidaps.plugins.general.overview.dialogs.EditQuickWizardDialog import info.nightscout.androidaps.plugins.general.overview.dialogs.EditQuickWizardDialog
import info.nightscout.androidaps.plugins.general.overview.events.EventQuickWizardChange import info.nightscout.androidaps.plugins.general.overview.events.EventQuickWizardChange
import info.nightscout.androidaps.utils.ActionModeHelper
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.wizard.QuickWizard import info.nightscout.androidaps.utils.wizard.QuickWizard
import info.nightscout.androidaps.utils.wizard.QuickWizardEntry import info.nightscout.androidaps.utils.wizard.QuickWizardEntry
@ -36,7 +32,7 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
import javax.inject.Inject import javax.inject.Inject
class QuickWizardListActivity : DaggerAppCompatActivityWithResult() { class QuickWizardListActivity : DaggerAppCompatActivityWithResult(), OnStartDragListener {
@Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var rxBus: RxBus @Inject lateinit var rxBus: RxBus
@ -46,134 +42,78 @@ class QuickWizardListActivity : DaggerAppCompatActivityWithResult() {
@Inject lateinit var sp: SP @Inject lateinit var sp: SP
private var disposable: CompositeDisposable = CompositeDisposable() private var disposable: CompositeDisposable = CompositeDisposable()
private lateinit var actionHelper: ActionModeHelper<QuickWizardEntry>
private val itemTouchHelper = ItemTouchHelper(SimpleItemTouchHelperCallback())
private lateinit var binding: OverviewQuickwizardlistActivityBinding private lateinit var binding: OverviewQuickwizardlistActivityBinding
private val itemTouchHelper by lazy { override fun onStartDrag(viewHolder: RecyclerView.ViewHolder) {
val simpleItemTouchCallback = object : ItemTouchHelper.SimpleCallback(UP or DOWN or START or END, 0) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
val adapter = recyclerView.adapter as RecyclerViewAdapter
val from = viewHolder.layoutPosition
val to = target.layoutPosition
adapter.moveItem(from, to)
adapter.notifyItemMoved(from, to)
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
}
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
super.onSelectedChanged(viewHolder, actionState)
if (actionState == ACTION_STATE_DRAG) {
viewHolder?.itemView?.alpha = 0.5f
}
}
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
super.clearView(recyclerView, viewHolder)
viewHolder.itemView.alpha = 1.0f
val adapter = recyclerView.adapter as RecyclerViewAdapter
adapter.onDrop()
}
}
ItemTouchHelper(simpleItemTouchCallback)
}
fun startDragging(viewHolder: RecyclerView.ViewHolder) {
itemTouchHelper.startDrag(viewHolder) itemTouchHelper.startDrag(viewHolder)
} }
private inner class RecyclerViewAdapter(var fragmentManager: FragmentManager) : RecyclerView.Adapter<RecyclerViewAdapter.QuickWizardEntryViewHolder>() { private inner class RecyclerViewAdapter(var fragmentManager: FragmentManager) : RecyclerView.Adapter<RecyclerViewAdapter.QuickWizardEntryViewHolder>(), ItemTouchHelperAdapter {
private inner class QuickWizardEntryViewHolder(val binding: OverviewQuickwizardlistItemBinding, val fragmentManager: FragmentManager) : RecyclerView.ViewHolder(binding.root)
@SuppressLint("ClickableViewAccessibility")
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QuickWizardEntryViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QuickWizardEntryViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.overview_quickwizardlist_item, parent, false) val binding = OverviewQuickwizardlistItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
val viewHolder = QuickWizardEntryViewHolder(itemView, fragmentManager) return QuickWizardEntryViewHolder(binding, fragmentManager)
viewHolder.handleView.setOnTouchListener { _, event ->
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
startDragging(viewHolder)
}
return@setOnTouchListener true
}
return viewHolder
} }
@SuppressLint("ClickableViewAccessibility")
override fun onBindViewHolder(holder: QuickWizardEntryViewHolder, position: Int) { override fun onBindViewHolder(holder: QuickWizardEntryViewHolder, position: Int) {
holder.from.text = dateUtil.timeString(quickWizard[position].validFromDate()) val entry = quickWizard[position]
holder.to.text = dateUtil.timeString(quickWizard[position].validToDate()) holder.binding.from.text = dateUtil.timeString(entry.validFromDate())
val wearControl = sp.getBoolean(R.string.key_wear_control, false) holder.binding.to.text = dateUtil.timeString(entry.validToDate())
holder.binding.buttonText.text = entry.buttonText()
if (wearControl) { holder.binding.carbs.text = rh.gs(R.string.format_carbs, entry.carbs())
holder.handleView.visibility = View.VISIBLE if (entry.device() == QuickWizardEntry.DEVICE_ALL) {
holder.binding.device.visibility = View.GONE
} else { } else {
holder.handleView.visibility = View.GONE holder.binding.device.visibility = View.VISIBLE
} holder.binding.device.setImageResource(
if (quickWizard[position].device() == QuickWizardEntry.DEVICE_ALL) {
holder.device.visibility = View.GONE
} else {
holder.device.visibility = View.VISIBLE
holder.device.setImageResource(
when (quickWizard[position].device()) { when (quickWizard[position].device()) {
QuickWizardEntry.DEVICE_WATCH -> R.drawable.ic_watch QuickWizardEntry.DEVICE_WATCH -> R.drawable.ic_watch
else -> R.drawable.ic_smartphone else -> R.drawable.ic_smartphone
} }
) )
} }
holder.buttonText.text = quickWizard[position].buttonText() holder.binding.root.setOnClickListener {
holder.carbs.text = rh.gs(R.string.format_carbs, quickWizard[position].carbs()) if (actionHelper.isNoAction) {
}
override fun getItemCount(): Int = quickWizard.size()
private inner class QuickWizardEntryViewHolder(itemView: View, var fragmentManager: FragmentManager) : RecyclerView.ViewHolder(itemView) {
val buttonText: TextView = itemView.findViewById(R.id.overview_quickwizard_item_buttonText)
val carbs: TextView = itemView.findViewById(R.id.overview_quickwizard_item_carbs)
val from: TextView = itemView.findViewById(R.id.overview_quickwizard_item_from)
val handleView: ImageView = itemView.findViewById(R.id.handleView)
val device: ImageView = itemView.findViewById(R.id.overview_quickwizard_item_device)
val to: TextView = itemView.findViewById(R.id.overview_quickwizard_item_to)
private val editButton: Button = itemView.findViewById(R.id.overview_quickwizard_item_edit_button)
private val removeButton: Button = itemView.findViewById(R.id.overview_quickwizard_item_remove_button)
init {
editButton.setOnClickListener {
val manager = fragmentManager val manager = fragmentManager
val editQuickWizardDialog = EditQuickWizardDialog() val editQuickWizardDialog = EditQuickWizardDialog()
val bundle = Bundle() val bundle = Bundle()
bundle.putInt("position", bindingAdapterPosition) bundle.putInt("position", position)
editQuickWizardDialog.arguments = bundle editQuickWizardDialog.arguments = bundle
editQuickWizardDialog.show(manager, "EditQuickWizardDialog") editQuickWizardDialog.show(manager, "EditQuickWizardDialog")
} } else if (actionHelper.isRemoving) {
removeButton.setOnClickListener { holder.binding.cbRemove.toggle()
quickWizard.remove(bindingAdapterPosition) actionHelper.updateSelection(position, entry, holder.binding.cbRemove.isChecked)
rxBus.send(EventQuickWizardChange())
} }
} }
holder.binding.sortHandle.setOnTouchListener { _, event ->
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
onStartDrag(holder)
return@setOnTouchListener true
}
return@setOnTouchListener false
}
holder.binding.cbRemove.isChecked = actionHelper.isSelected(position)
holder.binding.cbRemove.setOnCheckedChangeListener { _, value ->
actionHelper.updateSelection(position, entry, value)
}
holder.binding.sortHandle.visibility = actionHelper.isSorting.toVisibility()
holder.binding.cbRemove.visibility = actionHelper.isRemoving.toVisibility()
} }
fun moveItem(from: Int, to: Int) { override fun getItemCount() = quickWizard.size()
Log.i("QuickWizard", "moveItem")
quickWizard.move(from, to) override fun onItemMove(fromPosition: Int, toPosition: Int): Boolean {
binding.recyclerview.adapter?.notifyItemMoved(fromPosition, toPosition)
quickWizard.move(fromPosition, toPosition)
return true
} }
fun onDrop() { override fun onDrop() = rxBus.send(EventQuickWizardChange())
Log.i("QuickWizard", "onDrop")
rxBus.send(EventQuickWizardChange())
}
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -181,6 +121,11 @@ class QuickWizardListActivity : DaggerAppCompatActivityWithResult() {
binding = OverviewQuickwizardlistActivityBinding.inflate(layoutInflater) binding = OverviewQuickwizardlistActivityBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
actionHelper = ActionModeHelper(rh, this)
actionHelper.setUpdateListHandler { binding.recyclerview.adapter?.notifyDataSetChanged() }
actionHelper.setOnRemoveHandler { removeSelected(it) }
actionHelper.enableSort = true
title = rh.gs(R.string.quickwizard) title = rh.gs(R.string.quickwizard)
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true) supportActionBar?.setDisplayShowHomeEnabled(true)
@ -191,6 +136,7 @@ class QuickWizardListActivity : DaggerAppCompatActivityWithResult() {
itemTouchHelper.attachToRecyclerView(binding.recyclerview) itemTouchHelper.attachToRecyclerView(binding.recyclerview)
binding.addButton.setOnClickListener { binding.addButton.setOnClickListener {
actionHelper.finish()
val manager = supportFragmentManager val manager = supportFragmentManager
val editQuickWizardDialog = EditQuickWizardDialog() val editQuickWizardDialog = EditQuickWizardDialog()
editQuickWizardDialog.show(manager, "EditQuickWizardDialog") editQuickWizardDialog.show(manager, "EditQuickWizardDialog")
@ -210,9 +156,25 @@ class QuickWizardListActivity : DaggerAppCompatActivityWithResult() {
override fun onPause() { override fun onPause() {
disposable.clear() disposable.clear()
actionHelper.finish()
super.onPause() super.onPause()
} }
private fun removeSelected(selectedItems: SparseArray<QuickWizardEntry>) {
OKDialog.showConfirmation(this, rh.gs(R.string.removerecord), getConfirmationText(selectedItems), Runnable {
selectedItems.forEach { _, item ->
quickWizard.remove(item.position)
rxBus.send(EventQuickWizardChange())
}
actionHelper.finish()
})
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.menu_actions, menu)
return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean = override fun onOptionsItemSelected(item: MenuItem): Boolean =
when (item.itemId) { when (item.itemId) {
android.R.id.home -> { android.R.id.home -> {
@ -220,6 +182,17 @@ class QuickWizardListActivity : DaggerAppCompatActivityWithResult() {
true true
} }
else -> false else -> actionHelper.onOptionsItemSelected(item)
} }
private fun getConfirmationText(selectedItems: SparseArray<QuickWizardEntry>): String {
if (selectedItems.size() == 1) {
val entry = selectedItems.valueAt(0)
return "${rh.gs(R.string.remove_button)} ${entry.buttonText()} ${rh.gs(R.string.format_carbs, entry.carbs())}\n" +
"${dateUtil.timeString(entry.validFromDate())} - ${dateUtil.timeString(entry.validToDate())}"
}
return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size())
}
} }

View file

@ -105,7 +105,7 @@ class EditQuickWizardDialog : DaggerDialogFragment(), View.OnClickListener {
binding.from.setOnClickListener { binding.from.setOnClickListener {
context?.let { context?.let {
TimePickerDialog( TimePickerDialog(
it, R.style.MaterialPickerTheme, it,
fromTimeSetListener, fromTimeSetListener,
T.secs(fromSeconds.toLong()).hours().toInt(), T.secs(fromSeconds.toLong()).hours().toInt(),
T.secs((fromSeconds % 3600).toLong()).mins().toInt(), T.secs((fromSeconds % 3600).toLong()).mins().toInt(),
@ -124,7 +124,7 @@ class EditQuickWizardDialog : DaggerDialogFragment(), View.OnClickListener {
binding.to.setOnClickListener { binding.to.setOnClickListener {
context?.let { context?.let {
TimePickerDialog( TimePickerDialog(
it, R.style.MaterialPickerTheme, it,
toTimeSetListener, toTimeSetListener,
T.secs(toSeconds.toLong()).hours().toInt(), T.secs(toSeconds.toLong()).hours().toInt(),
T.secs((toSeconds % 3600).toLong()).mins().toInt(), T.secs((toSeconds % 3600).toLong()).mins().toInt(),

View file

@ -1,5 +0,0 @@
package info.nightscout.androidaps.plugins.general.overview.events
import info.nightscout.androidaps.events.Event
class EventUpdateOverviewBg(val from: String) : Event()

View file

@ -1,5 +0,0 @@
package info.nightscout.androidaps.plugins.general.overview.events
import info.nightscout.androidaps.events.Event
class EventUpdateOverviewExtendedBolus(val from: String) : Event()

View file

@ -1,5 +0,0 @@
package info.nightscout.androidaps.plugins.general.overview.events
import info.nightscout.androidaps.events.Event
class EventUpdateOverviewProfile(val from: String) : Event()

View file

@ -1,5 +0,0 @@
package info.nightscout.androidaps.plugins.general.overview.events
import info.nightscout.androidaps.events.Event
class EventUpdateOverviewTemporaryBasal(val from: String) : Event()

View file

@ -1,5 +0,0 @@
package info.nightscout.androidaps.plugins.general.overview.events
import info.nightscout.androidaps.events.Event
class EventUpdateOverviewTemporaryTarget(val from: String) : Event()

View file

@ -1,5 +0,0 @@
package info.nightscout.androidaps.plugins.general.overview.events
import info.nightscout.androidaps.events.Event
class EventUpdateOverviewTime(val from: String) : Event()

View file

@ -66,7 +66,7 @@ class GraphData(
addSeries(AreaGraphSeries(inRangeAreaDataPoints).also { addSeries(AreaGraphSeries(inRangeAreaDataPoints).also {
it.color = 0 it.color = 0
it.isDrawBackground = true it.isDrawBackground = true
it.backgroundColor = rh.gc(R.color.inrangebackground) it.backgroundColor = rh.gac(graph.context,R.attr.inrangeBackground)
}) })
} }
@ -88,6 +88,11 @@ class GraphData(
addSeries(overviewData.treatmentsSeries) addSeries(overviewData.treatmentsSeries)
} }
fun addTherapyEvents() {
maxY = maxOf(maxY, overviewData.maxTherapyEventValue)
addSeries(overviewData.therapyEventSeries)
}
fun addActivity(scale: Double) { fun addActivity(scale: Double) {
addSeries(overviewData.activitySeries) addSeries(overviewData.activitySeries)
addSeries(overviewData.activityPredictionSeries) addSeries(overviewData.activityPredictionSeries)

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.plugins.general.overview.graphExtensions package info.nightscout.androidaps.plugins.general.overview.graphExtensions
import android.content.Context
import android.graphics.Color import android.graphics.Color
import info.nightscout.androidaps.core.R import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.database.entities.Bolus import info.nightscout.androidaps.database.entities.Bolus
@ -28,13 +29,11 @@ class BolusDataPoint @Inject constructor(
override val shape override val shape
get() = if (data.type == Bolus.Type.SMB) PointsWithLabelGraphSeries.Shape.SMB else PointsWithLabelGraphSeries.Shape.BOLUS get() = if (data.type == Bolus.Type.SMB) PointsWithLabelGraphSeries.Shape.SMB else PointsWithLabelGraphSeries.Shape.BOLUS
override val color override fun color(context: Context?): Int =
get() = if (data.type == Bolus.Type.SMB) rh.gac(context, R.attr.smbColor)
when { else if (data.isValid) rh.gac(context, R.attr.bolusDataPointColor)
data.type == Bolus.Type.SMB -> rh.gc(R.color.tempbasal) else rh.gac(context, R.attr.alarmColor)
data.isValid -> Color.CYAN
else -> rh.gc(android.R.color.holo_red_light)
}
override fun setY(y: Double) { override fun setY(y: Double) {
yValue = y yValue = y

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.plugins.general.overview.graphExtensions package info.nightscout.androidaps.plugins.general.overview.graphExtensions
import android.content.Context
import info.nightscout.androidaps.core.R import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.database.entities.Carbs import info.nightscout.androidaps.database.entities.Carbs
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
@ -18,7 +19,10 @@ class CarbsDataPoint @Inject constructor(
override val duration = 0L override val duration = 0L
override val size = 2f override val size = 2f
override val shape = PointsWithLabelGraphSeries.Shape.CARBS override val shape = PointsWithLabelGraphSeries.Shape.CARBS
override val color get() = if (data.isValid) rh.gc(R.color.carbs) else rh.gc(android.R.color.holo_red_light)
override fun color(context: Context?): Int {
return if (data.isValid) rh.gac(context, R.attr.cobColor) else rh.gac(context, R.attr.alarmColor)
}
override fun setY(y: Double) { override fun setY(y: Double) {
yValue = y yValue = y

View file

@ -1,11 +1,15 @@
package info.nightscout.androidaps.plugins.general.overview.graphExtensions package info.nightscout.androidaps.plugins.general.overview.graphExtensions
import android.content.Context
import android.graphics.Color import android.graphics.Color
import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.database.entities.EffectiveProfileSwitch import info.nightscout.androidaps.database.entities.EffectiveProfileSwitch
import info.nightscout.androidaps.utils.resources.ResourceHelper
import javax.inject.Inject import javax.inject.Inject
class EffectiveProfileSwitchDataPoint @Inject constructor( class EffectiveProfileSwitchDataPoint @Inject constructor(
val data: EffectiveProfileSwitch val data: EffectiveProfileSwitch,
private val rh: ResourceHelper
) : DataPointWithLabelInterface { ) : DataPointWithLabelInterface {
private var yValue = 0.0 private var yValue = 0.0
@ -21,5 +25,7 @@ class EffectiveProfileSwitchDataPoint @Inject constructor(
override val duration = 0L override val duration = 0L
override val shape = PointsWithLabelGraphSeries.Shape.PROFILE override val shape = PointsWithLabelGraphSeries.Shape.PROFILE
override val size = 10f override val size = 10f
override val color = Color.CYAN override fun color(context: Context?): Int {
return rh.gac(context, R.attr.profileSwitchColor)
}
} }

View file

@ -1,12 +1,16 @@
package info.nightscout.androidaps.plugins.general.overview.graphExtensions package info.nightscout.androidaps.plugins.general.overview.graphExtensions
import android.content.Context
import android.graphics.Color import android.graphics.Color
import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.database.entities.ExtendedBolus import info.nightscout.androidaps.database.entities.ExtendedBolus
import info.nightscout.androidaps.extensions.toStringTotal import info.nightscout.androidaps.extensions.toStringTotal
import info.nightscout.androidaps.utils.resources.ResourceHelper
import javax.inject.Inject import javax.inject.Inject
class ExtendedBolusDataPoint @Inject constructor( class ExtendedBolusDataPoint @Inject constructor(
val data: ExtendedBolus val data: ExtendedBolus,
private val rh: ResourceHelper
) : DataPointWithLabelInterface { ) : DataPointWithLabelInterface {
private var yValue = 0.0 private var yValue = 0.0
@ -17,7 +21,9 @@ class ExtendedBolusDataPoint @Inject constructor(
override val duration get() = data.duration override val duration get() = data.duration
override val size = 10f override val size = 10f
override val shape = PointsWithLabelGraphSeries.Shape.EXTENDEDBOLUS override val shape = PointsWithLabelGraphSeries.Shape.EXTENDEDBOLUS
override val color = Color.CYAN override fun color(context: Context?): Int {
return rh.gac(context, R.attr.extBolusColor)
}
override fun setY(y: Double) { override fun setY(y: Double) {
yValue = y yValue = y

View file

@ -261,7 +261,7 @@ public class FixedLineGraphSeries<E extends DataPointInterface> extends BaseSeri
//fix: last value not drawn as datapoint. Draw first point here, and then on every step the end values (above) //fix: last value not drawn as datapoint. Draw first point here, and then on every step the end values (above)
// float first_X = (float) x + (graphLeft + 1); // float first_X = (float) x + (graphLeft + 1);
// float first_Y = (float) (graphTop - y) + graphHeight; // float first_Y = (float) (graphTop - y) + graphHeight;
//TODO canvas.drawCircle(first_X, first_Y, dataPointsRadius, mPaint); // canvas.drawCircle(first_X, first_Y, dataPointsRadius, mPaint);
} }
lastEndY = orgY; lastEndY = orgY;
lastEndX = orgX; lastEndX = orgX;

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.plugins.general.overview.graphExtensions package info.nightscout.androidaps.plugins.general.overview.graphExtensions
import android.content.Context
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.core.R import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.database.entities.GlucoseValue import info.nightscout.androidaps.database.entities.GlucoseValue
@ -27,30 +28,28 @@ class GlucoseValueDataPoint @Inject constructor(
override val duration = 0L override val duration = 0L
override val shape get() = if (isPrediction) PointsWithLabelGraphSeries.Shape.PREDICTION else PointsWithLabelGraphSeries.Shape.BG override val shape get() = if (isPrediction) PointsWithLabelGraphSeries.Shape.PREDICTION else PointsWithLabelGraphSeries.Shape.BG
override val size = 1f override val size = 1f
override val color: Int override fun color(context: Context?): Int {
get() { val units = profileFunction.getUnits()
val units = profileFunction.getUnits() val lowLine = defaultValueHelper.determineLowLine()
val lowLine = defaultValueHelper.determineLowLine() val highLine = defaultValueHelper.determineHighLine()
val highLine = defaultValueHelper.determineHighLine() return when {
return when { isPrediction -> predictionColor(context)
isPrediction -> predictionColor valueToUnits(units) < lowLine -> rh.gac(context, R.attr.bgLow)
valueToUnits(units) < lowLine -> rh.gc(R.color.low) valueToUnits(units) > highLine -> rh.gac(context, R.attr.highColor)
valueToUnits(units) > highLine -> rh.gc(R.color.high) else -> rh.gac(context, R.attr.bgInRange)
else -> rh.gc(R.color.inrange)
}
} }
}
val predictionColor: Int private fun predictionColor (context: Context?): Int {
get() {
return when (data.sourceSensor) { return when (data.sourceSensor) {
GlucoseValue.SourceSensor.IOB_PREDICTION -> rh.gc(R.color.iob) GlucoseValue.SourceSensor.IOB_PREDICTION -> rh.gac(context, R.attr.iobColor)
GlucoseValue.SourceSensor.COB_PREDICTION -> rh.gc(R.color.cob) GlucoseValue.SourceSensor.COB_PREDICTION -> rh.gac(context, R.attr.cobColor)
GlucoseValue.SourceSensor.A_COB_PREDICTION -> -0x7f000001 and rh.gc(R.color.cob) GlucoseValue.SourceSensor.A_COB_PREDICTION -> -0x7f000001 and rh.gac(context, R.attr.cobColor)
GlucoseValue.SourceSensor.UAM_PREDICTION -> rh.gc(R.color.uam) GlucoseValue.SourceSensor.UAM_PREDICTION -> rh.gac(context, R.attr.uamColor)
GlucoseValue.SourceSensor.ZT_PREDICTION -> rh.gc(R.color.zt) GlucoseValue.SourceSensor.ZT_PREDICTION -> rh.gac(context, R.attr.ztColor)
else -> R.color.white else -> rh.gac( context,R.attr.defaultTextColor)
} }
} }
private val isPrediction: Boolean private val isPrediction: Boolean
get() = data.sourceSensor == GlucoseValue.SourceSensor.IOB_PREDICTION || get() = data.sourceSensor == GlucoseValue.SourceSensor.IOB_PREDICTION ||

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.plugins.general.overview.graphExtensions package info.nightscout.androidaps.plugins.general.overview.graphExtensions
import android.content.Context
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.core.R import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.data.InMemoryGlucoseValue import info.nightscout.androidaps.data.InMemoryGlucoseValue
@ -24,5 +25,7 @@ class InMemoryGlucoseValueDataPoint @Inject constructor(
override val duration = 0L override val duration = 0L
override val shape = PointsWithLabelGraphSeries.Shape.BUCKETED_BG override val shape = PointsWithLabelGraphSeries.Shape.BUCKETED_BG
override val size = 0.3f override val size = 0.3f
override val color get() = rh.gc(R.color.white) override fun color(context: Context?): Int {
return rh.gac(context, R.attr.inMemoryColor)
}
} }

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.plugins.general.overview.graphExtensions package info.nightscout.androidaps.plugins.general.overview.graphExtensions
import android.content.Context
import android.graphics.Color import android.graphics.Color
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.core.R import info.nightscout.androidaps.core.R
@ -59,14 +60,14 @@ class TherapyEventDataPoint @Inject constructor(
} }
override val size get() = if (rh.gb(R.bool.isTablet)) 12.0f else 10.0f override val size get() = if (rh.gb(R.bool.isTablet)) 12.0f else 10.0f
override val color override fun color(context: Context?): Int {
get() = return when (data.type) {
when (data.type) { TherapyEvent.Type.ANNOUNCEMENT -> rh.gac(context, R.attr.notificationAnnouncement)
TherapyEvent.Type.ANNOUNCEMENT -> rh.gc(R.color.notificationAnnouncement) TherapyEvent.Type.NS_MBG -> rh.gac(context, R.attr.therapyEvent_NS_MBG)
TherapyEvent.Type.NS_MBG -> Color.RED TherapyEvent.Type.FINGER_STICK_BG_VALUE -> rh.gac(context, R.attr.therapyEvent_FINGER_STICK_BG_VALUE)
TherapyEvent.Type.FINGER_STICK_BG_VALUE -> Color.RED TherapyEvent.Type.EXERCISE -> rh.gac(context, R.attr.therapyEvent_EXERCISE)
TherapyEvent.Type.EXERCISE -> Color.BLUE TherapyEvent.Type.APS_OFFLINE -> rh.gac(context, R.attr.therapyEvent_APS_OFFLINE) and -0x7f000001
TherapyEvent.Type.APS_OFFLINE -> Color.GRAY and -0x7f000001 else -> rh.gac(context, R.attr.therapyEvent_Default)
else -> Color.GRAY }
} }
} }

View file

@ -164,11 +164,11 @@ class NotificationStore @Inject constructor(
@Suppress("SetTextI18n") @Suppress("SetTextI18n")
holder.binding.text.text = dateUtil.timeString(notification.date) + " " + notification.text holder.binding.text.text = dateUtil.timeString(notification.date) + " " + notification.text
when (notification.level) { when (notification.level) {
Notification.URGENT -> holder.binding.cv.setBackgroundColor(rh.gc(R.color.notificationUrgent)) Notification.URGENT -> holder.binding.cv.setBackgroundColor(rh.gac(R.attr.notificationUrgent))
Notification.NORMAL -> holder.binding.cv.setBackgroundColor(rh.gc(R.color.notificationNormal)) Notification.NORMAL -> holder.binding.cv.setBackgroundColor(rh.gac(R.attr.notificationNormal))
Notification.LOW -> holder.binding.cv.setBackgroundColor(rh.gc(R.color.notificationLow)) Notification.LOW -> holder.binding.cv.setBackgroundColor(rh.gac(R.attr.notificationLow))
Notification.INFO -> holder.binding.cv.setBackgroundColor(rh.gc(R.color.notificationInfo)) Notification.INFO -> holder.binding.cv.setBackgroundColor(rh.gac(R.attr.notificationInfo))
Notification.ANNOUNCEMENT -> holder.binding.cv.setBackgroundColor(rh.gc(R.color.notificationAnnouncement)) Notification.ANNOUNCEMENT -> holder.binding.cv.setBackgroundColor(rh.gac(R.attr.notificationAnnouncement))
} }
} }

View file

@ -39,8 +39,6 @@ class DummyService : DaggerService() {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
// TODO: I guess this was moved here in order to adhere to the 5 seconds rule to call "startForeground" after a Service was called as Foreground service?
// As onCreate() is not called every time a service is started, copied to onStartCommand().
try { try {
aapsLogger.debug("Starting DummyService with ID ${notificationHolder.notificationID} notification ${notificationHolder.notification}") aapsLogger.debug("Starting DummyService with ID ${notificationHolder.notificationID} notification ${notificationHolder.notification}")
startForeground(notificationHolder.notificationID, notificationHolder.notification) startForeground(notificationHolder.notificationID, notificationHolder.notification)

View file

@ -9,17 +9,20 @@ import androidx.core.app.RemoteInput
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.events.* import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.events.EventInitializationChanged
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventRefreshOverview
import info.nightscout.androidaps.extensions.toStringShort import info.nightscout.androidaps.extensions.toStringShort
import info.nightscout.androidaps.extensions.valueToUnitsString import info.nightscout.androidaps.extensions.valueToUnitsString
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider
import info.nightscout.androidaps.utils.DecimalFormatter import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.logging.AAPSLogger
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
import javax.inject.Inject import javax.inject.Inject
@ -72,26 +75,10 @@ class PersistentNotificationPlugin @Inject constructor(
.toObservable(EventRefreshOverview::class.java) .toObservable(EventRefreshOverview::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ triggerNotificationUpdate() }, fabricPrivacy::logException) .subscribe({ triggerNotificationUpdate() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventExtendedBolusChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ triggerNotificationUpdate() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTempBasalChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ triggerNotificationUpdate() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTreatmentChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ triggerNotificationUpdate() }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventInitializationChanged::class.java) .toObservable(EventInitializationChanged::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ triggerNotificationUpdate() }, fabricPrivacy::logException) .subscribe({ triggerNotificationUpdate() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventEffectiveProfileSwitchChanged::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ triggerNotificationUpdate() }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventAutosensCalculationFinished::class.java) .toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)

View file

@ -328,7 +328,7 @@ class SmsCommunicatorPlugin @Inject constructor(
} else if (lastBG != null) { } else if (lastBG != null) {
val agoMilliseconds = dateUtil.now() - lastBG.timestamp val agoMilliseconds = dateUtil.now() - lastBG.timestamp
val agoMin = (agoMilliseconds / 60.0 / 1000.0).toInt() val agoMin = (agoMilliseconds / 60.0 / 1000.0).toInt()
reply = rh.gs(R.string.sms_lastbg) + " " + lastBG.valueToUnitsString(units) + " " + String.format(rh.gs(R.string.sms_minago), agoMin) + ", " reply = rh.gs(R.string.sms_lastbg) + " " + lastBG.valueToUnitsString(units) + " " + rh.gs(R.string.sms_minago, agoMin) + ", "
} }
val glucoseStatus = glucoseStatusProvider.glucoseStatusData val glucoseStatus = glucoseStatusProvider.glucoseStatusData
if (glucoseStatus != null) reply += rh.gs(R.string.sms_delta) + " " + Profile.toUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units) + " " + units + ", " if (glucoseStatus != null) reply += rh.gs(R.string.sms_delta) + " " + Profile.toUnitsString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units) + " " + units + ", "
@ -348,7 +348,7 @@ class SmsCommunicatorPlugin @Inject constructor(
"DISABLE", "STOP" -> { "DISABLE", "STOP" -> {
if (loop.enabled) { if (loop.enabled) {
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(rh.gs(R.string.smscommunicator_loopdisablereplywithcode), passCode) val reply = rh.gs(R.string.smscommunicator_loopdisablereplywithcode, passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false) { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false) {
override fun run() { override fun run() {
@ -372,7 +372,7 @@ class SmsCommunicatorPlugin @Inject constructor(
"ENABLE", "START" -> { "ENABLE", "START" -> {
if (!loop.enabled) { if (!loop.enabled) {
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(rh.gs(R.string.smscommunicator_loopenablereplywithcode), passCode) val reply = rh.gs(R.string.smscommunicator_loopenablereplywithcode, passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false) { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false) {
override fun run() { override fun run() {
@ -389,7 +389,7 @@ class SmsCommunicatorPlugin @Inject constructor(
"STATUS" -> { "STATUS" -> {
val reply = if (loop.enabled) { val reply = if (loop.enabled) {
if (loop.isSuspended) String.format(rh.gs(R.string.loopsuspendedfor), loop.minutesToEndOfSuspend()) if (loop.isSuspended) rh.gs(R.string.loopsuspendedfor, loop.minutesToEndOfSuspend())
else rh.gs(R.string.smscommunicator_loopisenabled) else rh.gs(R.string.smscommunicator_loopisenabled)
} else } else
rh.gs(R.string.loopisdisabled) rh.gs(R.string.loopisdisabled)
@ -399,7 +399,7 @@ class SmsCommunicatorPlugin @Inject constructor(
"RESUME" -> { "RESUME" -> {
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(rh.gs(R.string.smscommunicator_loopresumereplywithcode), passCode) val reply = rh.gs(R.string.smscommunicator_loopresumereplywithcode, passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true) { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true) {
override fun run() { override fun run() {
@ -436,7 +436,7 @@ class SmsCommunicatorPlugin @Inject constructor(
return return
} else { } else {
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(rh.gs(R.string.smscommunicator_suspendreplywithcode), duration, passCode) val reply = rh.gs(R.string.smscommunicator_suspendreplywithcode, duration, passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, duration) { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, duration) {
override fun run() { override fun run() {
@ -515,7 +515,7 @@ class SmsCommunicatorPlugin @Inject constructor(
receivedSms.processed = true receivedSms.processed = true
} else if ((divided.size == 2) && (divided[1].equals("CONNECT", ignoreCase = true))) { } else if ((divided.size == 2) && (divided[1].equals("CONNECT", ignoreCase = true))) {
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(rh.gs(R.string.smscommunicator_pumpconnectwithcode), passCode) val reply = rh.gs(R.string.smscommunicator_pumpconnectwithcode, passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true) { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true) {
override fun run() { override fun run() {
@ -548,7 +548,7 @@ class SmsCommunicatorPlugin @Inject constructor(
return return
} else { } else {
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(rh.gs(R.string.smscommunicator_pumpdisconnectwithcode), duration, passCode) val reply = rh.gs(R.string.smscommunicator_pumpdisconnectwithcode, duration, passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true) { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true) {
override fun run() { override fun run() {
@ -601,7 +601,7 @@ class SmsCommunicatorPlugin @Inject constructor(
if (profile == null) sendSMS(Sms(receivedSms.phoneNumber, rh.gs(R.string.noprofile))) if (profile == null) sendSMS(Sms(receivedSms.phoneNumber, rh.gs(R.string.noprofile)))
else { else {
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(rh.gs(R.string.smscommunicator_profilereplywithcode), list[pIndex - 1], percentage, passCode) val reply = rh.gs(R.string.smscommunicator_profilereplywithcode, list[pIndex - 1], percentage, passCode)
receivedSms.processed = true receivedSms.processed = true
val finalPercentage = percentage val finalPercentage = percentage
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, list[pIndex - 1] as String, finalPercentage) { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, list[pIndex - 1] as String, finalPercentage) {
@ -627,7 +627,7 @@ class SmsCommunicatorPlugin @Inject constructor(
private fun processBASAL(divided: Array<String>, receivedSms: Sms) { private fun processBASAL(divided: Array<String>, receivedSms: Sms) {
if (divided[1].uppercase(Locale.getDefault()) == "CANCEL" || divided[1].uppercase(Locale.getDefault()) == "STOP") { if (divided[1].uppercase(Locale.getDefault()) == "CANCEL" || divided[1].uppercase(Locale.getDefault()) == "STOP") {
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(rh.gs(R.string.smscommunicator_basalstopreplywithcode), passCode) val reply = rh.gs(R.string.smscommunicator_basalstopreplywithcode, passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true) { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true) {
override fun run() { override fun run() {
@ -662,14 +662,14 @@ class SmsCommunicatorPlugin @Inject constructor(
else { else {
tempBasalPct = constraintChecker.applyBasalPercentConstraints(Constraint(tempBasalPct), profile).value() tempBasalPct = constraintChecker.applyBasalPercentConstraints(Constraint(tempBasalPct), profile).value()
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(rh.gs(R.string.smscommunicator_basalpctreplywithcode), tempBasalPct, duration, passCode) val reply = rh.gs(R.string.smscommunicator_basalpctreplywithcode, tempBasalPct, duration, passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, tempBasalPct, duration) { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, tempBasalPct, duration) {
override fun run() { override fun run() {
commandQueue.tempBasalPercent(anInteger(), secondInteger(), true, profile, PumpSync.TemporaryBasalType.NORMAL, object : Callback() { commandQueue.tempBasalPercent(anInteger(), secondInteger(), true, profile, PumpSync.TemporaryBasalType.NORMAL, object : Callback() {
override fun run() { override fun run() {
if (result.success) { if (result.success) {
var replyText = if (result.isPercent) String.format(rh.gs(R.string.smscommunicator_tempbasalset_percent), result.percent, result.duration) else String.format(rh.gs(R.string.smscommunicator_tempbasalset), result.absolute, result.duration) var replyText = if (result.isPercent) rh.gs(R.string.smscommunicator_tempbasalset_percent, result.percent, result.duration) else rh.gs(R.string.smscommunicator_tempbasalset, result.absolute, result.duration)
replyText += "\n" + activePlugin.activePump.shortStatus(true) replyText += "\n" + activePlugin.activePump.shortStatus(true)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
if (result.isPercent) if (result.isPercent)
@ -706,15 +706,15 @@ class SmsCommunicatorPlugin @Inject constructor(
else { else {
tempBasal = constraintChecker.applyBasalConstraints(Constraint(tempBasal), profile).value() tempBasal = constraintChecker.applyBasalConstraints(Constraint(tempBasal), profile).value()
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(rh.gs(R.string.smscommunicator_basalreplywithcode), tempBasal, duration, passCode) val reply = rh.gs(R.string.smscommunicator_basalreplywithcode, tempBasal, duration, passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, tempBasal, duration) { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, tempBasal, duration) {
override fun run() { override fun run() {
commandQueue.tempBasalAbsolute(aDouble(), secondInteger(), true, profile, PumpSync.TemporaryBasalType.NORMAL, object : Callback() { commandQueue.tempBasalAbsolute(aDouble(), secondInteger(), true, profile, PumpSync.TemporaryBasalType.NORMAL, object : Callback() {
override fun run() { override fun run() {
if (result.success) { if (result.success) {
var replyText = if (result.isPercent) String.format(rh.gs(R.string.smscommunicator_tempbasalset_percent), result.percent, result.duration) var replyText = if (result.isPercent) rh.gs(R.string.smscommunicator_tempbasalset_percent, result.percent, result.duration)
else String.format(rh.gs(R.string.smscommunicator_tempbasalset), result.absolute, result.duration) else rh.gs(R.string.smscommunicator_tempbasalset, result.absolute, result.duration)
replyText += "\n" + activePlugin.activePump.shortStatus(true) replyText += "\n" + activePlugin.activePump.shortStatus(true)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
if (result.isPercent) if (result.isPercent)
@ -743,7 +743,7 @@ class SmsCommunicatorPlugin @Inject constructor(
private fun processEXTENDED(divided: Array<String>, receivedSms: Sms) { private fun processEXTENDED(divided: Array<String>, receivedSms: Sms) {
if (divided[1].uppercase(Locale.getDefault()) == "CANCEL" || divided[1].uppercase(Locale.getDefault()) == "STOP") { if (divided[1].uppercase(Locale.getDefault()) == "CANCEL" || divided[1].uppercase(Locale.getDefault()) == "STOP") {
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(rh.gs(R.string.smscommunicator_extendedstopreplywithcode), passCode) val reply = rh.gs(R.string.smscommunicator_extendedstopreplywithcode, passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true) { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true) {
override fun run() { override fun run() {
@ -773,14 +773,14 @@ class SmsCommunicatorPlugin @Inject constructor(
if (extended == 0.0 || duration == 0) sendSMS(Sms(receivedSms.phoneNumber, rh.gs(R.string.wrongformat))) if (extended == 0.0 || duration == 0) sendSMS(Sms(receivedSms.phoneNumber, rh.gs(R.string.wrongformat)))
else { else {
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(rh.gs(R.string.smscommunicator_extendedreplywithcode), extended, duration, passCode) val reply = rh.gs(R.string.smscommunicator_extendedreplywithcode, extended, duration, passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, extended, duration) { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, extended, duration) {
override fun run() { override fun run() {
commandQueue.extendedBolus(aDouble(), secondInteger(), object : Callback() { commandQueue.extendedBolus(aDouble(), secondInteger(), object : Callback() {
override fun run() { override fun run() {
if (result.success) { if (result.success) {
var replyText = String.format(rh.gs(R.string.smscommunicator_extendedset), aDouble, duration) var replyText = rh.gs(R.string.smscommunicator_extendedset, aDouble, duration)
if (config.APS) replyText += "\n" + rh.gs(R.string.loopsuspended) if (config.APS) replyText += "\n" + rh.gs(R.string.loopsuspended)
replyText += "\n" + activePlugin.activePump.shortStatus(true) replyText += "\n" + activePlugin.activePump.shortStatus(true)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
@ -817,9 +817,9 @@ class SmsCommunicatorPlugin @Inject constructor(
} else if (bolus > 0.0) { } else if (bolus > 0.0) {
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = if (isMeal) val reply = if (isMeal)
String.format(rh.gs(R.string.smscommunicator_mealbolusreplywithcode), bolus, passCode) rh.gs(R.string.smscommunicator_mealbolusreplywithcode, bolus, passCode)
else else
String.format(rh.gs(R.string.smscommunicator_bolusreplywithcode), bolus, passCode) rh.gs(R.string.smscommunicator_bolusreplywithcode, bolus, passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, bolus) { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, bolus) {
override fun run() { override fun run() {
@ -833,9 +833,9 @@ class SmsCommunicatorPlugin @Inject constructor(
override fun run() { override fun run() {
if (resultSuccess) { if (resultSuccess) {
var replyText = if (isMeal) var replyText = if (isMeal)
String.format(rh.gs(R.string.smscommunicator_mealbolusdelivered), resultBolusDelivered) rh.gs(R.string.smscommunicator_mealbolusdelivered, resultBolusDelivered)
else else
String.format(rh.gs(R.string.smscommunicator_bolusdelivered), resultBolusDelivered) rh.gs(R.string.smscommunicator_bolusdelivered, resultBolusDelivered)
replyText += "\n" + activePlugin.activePump.shortStatus(true) replyText += "\n" + activePlugin.activePump.shortStatus(true)
lastRemoteBolusTime = dateUtil.now() lastRemoteBolusTime = dateUtil.now()
if (isMeal) { if (isMeal) {
@ -866,7 +866,7 @@ class SmsCommunicatorPlugin @Inject constructor(
val tt = if (currentProfile.units == GlucoseUnit.MMOL) { val tt = if (currentProfile.units == GlucoseUnit.MMOL) {
DecimalFormatter.to1Decimal(eatingSoonTT) DecimalFormatter.to1Decimal(eatingSoonTT)
} else DecimalFormatter.to0Decimal(eatingSoonTT) } else DecimalFormatter.to0Decimal(eatingSoonTT)
replyText += "\n" + String.format(rh.gs(R.string.smscommunicator_mealbolusdelivered_tt), tt, eatingSoonTTDuration) replyText += "\n" + rh.gs(R.string.smscommunicator_mealbolusdelivered_tt, tt, eatingSoonTTDuration)
} }
} }
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
@ -920,7 +920,7 @@ class SmsCommunicatorPlugin @Inject constructor(
if (grams == 0) sendSMS(Sms(receivedSms.phoneNumber, rh.gs(R.string.wrongformat))) if (grams == 0) sendSMS(Sms(receivedSms.phoneNumber, rh.gs(R.string.wrongformat)))
else { else {
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(rh.gs(R.string.smscommunicator_carbsreplywithcode), grams, dateUtil.timeString(time), passCode) val reply = rh.gs(R.string.smscommunicator_carbsreplywithcode, grams, dateUtil.timeString(time), passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, grams, time) { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, grams, time) {
override fun run() { override fun run() {
@ -930,7 +930,7 @@ class SmsCommunicatorPlugin @Inject constructor(
commandQueue.bolus(detailedBolusInfo, object : Callback() { commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() { override fun run() {
if (result.success) { if (result.success) {
var replyText = String.format(rh.gs(R.string.smscommunicator_carbsset), anInteger) var replyText = rh.gs(R.string.smscommunicator_carbsset, anInteger)
replyText += "\n" + activePlugin.activePump.shortStatus(true) replyText += "\n" + activePlugin.activePump.shortStatus(true)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
uel.log(Action.CARBS, Sources.SMS, activePlugin.activePump.shortStatus(true) + ": " + rh.gs(R.string.smscommunicator_carbsset, anInteger), uel.log(Action.CARBS, Sources.SMS, activePlugin.activePump.shortStatus(true) + ": " + rh.gs(R.string.smscommunicator_carbsset, anInteger),
@ -956,7 +956,7 @@ class SmsCommunicatorPlugin @Inject constructor(
val isStop = divided[1].equals("STOP", ignoreCase = true) || divided[1].equals("CANCEL", ignoreCase = true) val isStop = divided[1].equals("STOP", ignoreCase = true) || divided[1].equals("CANCEL", ignoreCase = true)
if (isMeal || isActivity || isHypo) { if (isMeal || isActivity || isHypo) {
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(rh.gs(R.string.smscommunicator_temptargetwithcode), divided[1].uppercase(Locale.getDefault()), passCode) val reply = rh.gs(R.string.smscommunicator_temptargetwithcode, divided[1].uppercase(Locale.getDefault()), passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false) { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false) {
override fun run() { override fun run() {
@ -1009,7 +1009,7 @@ class SmsCommunicatorPlugin @Inject constructor(
aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it)
}) })
val ttString = if (units == GlucoseUnit.MMOL) DecimalFormatter.to1Decimal(tt) else DecimalFormatter.to0Decimal(tt) val ttString = if (units == GlucoseUnit.MMOL) DecimalFormatter.to1Decimal(tt) else DecimalFormatter.to0Decimal(tt)
val replyText = String.format(rh.gs(R.string.smscommunicator_tt_set), ttString, ttDuration) val replyText = rh.gs(R.string.smscommunicator_tt_set, ttString, ttDuration)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
uel.log(Action.TT, Sources.SMS, uel.log(Action.TT, Sources.SMS,
ValueWithUnit.fromGlucoseUnit(tt, units.asText), ValueWithUnit.fromGlucoseUnit(tt, units.asText),
@ -1018,7 +1018,7 @@ class SmsCommunicatorPlugin @Inject constructor(
}) })
} else if (isStop) { } else if (isStop) {
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(rh.gs(R.string.smscommunicator_temptargetcancel), passCode) val reply = rh.gs(R.string.smscommunicator_temptargetcancel, passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false) { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false) {
override fun run() { override fun run() {
@ -1028,7 +1028,7 @@ class SmsCommunicatorPlugin @Inject constructor(
}, { }, {
aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it)
}) })
val replyText = String.format(rh.gs(R.string.smscommunicator_tt_canceled)) val replyText = rh.gs(R.string.smscommunicator_tt_canceled)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
uel.log(Action.CANCEL_TT, Sources.SMS, rh.gs(R.string.smscommunicator_tt_canceled), uel.log(Action.CANCEL_TT, Sources.SMS, rh.gs(R.string.smscommunicator_tt_canceled),
ValueWithUnit.SimpleString(rh.gsNotLocalised(R.string.smscommunicator_tt_canceled))) ValueWithUnit.SimpleString(rh.gsNotLocalised(R.string.smscommunicator_tt_canceled)))
@ -1043,12 +1043,12 @@ class SmsCommunicatorPlugin @Inject constructor(
|| divided[1].equals("DISABLE", ignoreCase = true)) || divided[1].equals("DISABLE", ignoreCase = true))
if (isStop) { if (isStop) {
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(rh.gs(R.string.smscommunicator_stopsmswithcode), passCode) val reply = rh.gs(R.string.smscommunicator_stopsmswithcode, passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false) { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false) {
override fun run() { override fun run() {
sp.putBoolean(R.string.key_smscommunicator_remotecommandsallowed, false) sp.putBoolean(R.string.key_smscommunicator_remotecommandsallowed, false)
val replyText = String.format(rh.gs(R.string.smscommunicator_stoppedsms)) val replyText = rh.gs(R.string.smscommunicator_stoppedsms)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText)) sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
uel.log(Action.STOP_SMS, Sources.SMS, rh.gs(R.string.smscommunicator_stoppedsms), uel.log(Action.STOP_SMS, Sources.SMS, rh.gs(R.string.smscommunicator_stoppedsms),
ValueWithUnit.SimpleString(rh.gsNotLocalised(R.string.smscommunicator_stoppedsms))) ValueWithUnit.SimpleString(rh.gsNotLocalised(R.string.smscommunicator_stoppedsms)))
@ -1061,7 +1061,7 @@ class SmsCommunicatorPlugin @Inject constructor(
val cal = SafeParse.stringToDouble(divided[1]) val cal = SafeParse.stringToDouble(divided[1])
if (cal > 0.0) { if (cal > 0.0) {
val passCode = generatePassCode() val passCode = generatePassCode()
val reply = String.format(rh.gs(R.string.smscommunicator_calibrationreplywithcode), cal, passCode) val reply = rh.gs(R.string.smscommunicator_calibrationreplywithcode, cal, passCode)
receivedSms.processed = true receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false, cal) { messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false, cal) {
override fun run() { override fun run() {
@ -1127,7 +1127,7 @@ class SmsCommunicatorPlugin @Inject constructor(
} }
private fun generatePassCode(): String = private fun generatePassCode(): String =
String.format(rh.gs(R.string.smscommunicator_code_from_authenticator_for), otp.name()) rh.gs(R.string.smscommunicator_code_from_authenticator_for, otp.name())
private fun stripAccents(str: String): String { private fun stripAccents(str: String): String {
var s = str var s = str

View file

@ -31,7 +31,6 @@ import javax.inject.Inject
class SmsCommunicatorOtpActivity : NoSplashAppCompatActivity() { class SmsCommunicatorOtpActivity : NoSplashAppCompatActivity() {
@Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var rxBus: RxBus
@Inject lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin @Inject lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin
@Inject lateinit var otp: OneTimePassword @Inject lateinit var otp: OneTimePassword
@Inject lateinit var uel: UserEntryLogger @Inject lateinit var uel: UserEntryLogger

View file

@ -0,0 +1,61 @@
package info.nightscout.androidaps.plugins.general.themes
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventThemeSwitch
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.disposables.CompositeDisposable
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class ThemeSwitcherPlugin @Inject constructor(
injector: HasAndroidInjector,
aapsLogger: AAPSLogger,
rh: ResourceHelper,
private val sp: SP,
private val rxBus: RxBus,
) : PluginBase(PluginDescription()
.mainType(PluginType.GENERAL)
.neverVisible(true)
.alwaysEnabled(true)
.showInList(false)
.pluginName(R.string.dst_plugin_name),
aapsLogger, rh, injector
) {
private val compositeDisposable = CompositeDisposable()
override fun onStart() {
compositeDisposable.add(rxBus.toObservable(EventPreferenceChange::class.java).subscribe {
if (it.isChanged(rh, id = R.string.key_use_dark_mode)) {
setThemeMode()
rxBus.send(EventThemeSwitch())
}
})
}
fun setThemeMode() {
val mode = when (sp.getString(R.string.key_use_dark_mode, "dark")) {
sp.getString(R.string.value_dark_theme, "dark") -> MODE_NIGHT_YES
sp.getString(R.string.value_light_theme, "light") -> MODE_NIGHT_NO
else -> MODE_NIGHT_FOLLOW_SYSTEM
}
AppCompatDelegate.setDefaultNightMode(mode)
}
override fun onStop() {
compositeDisposable.dispose()
}
}

View file

@ -111,7 +111,7 @@ class ActionStringHandler @Inject constructor(
@Synchronized @Synchronized
private fun handleInitiate(actionString: String) { private fun handleInitiate(actionString: String) {
//TODO: i18n //TODO: i18n
Log.i("ActionStringHandler", "handleInitiate actionString=" + actionString) Log.i("ActionStringHandler", "handleInitiate actionString=$actionString")
if (!sp.getBoolean(R.string.key_wear_control, false)) return if (!sp.getBoolean(R.string.key_wear_control, false)) return
lastBolusWizard = null lastBolusWizard = null
var rTitle = rh.gs(R.string.confirm).uppercase() var rTitle = rh.gs(R.string.confirm).uppercase()
@ -640,10 +640,10 @@ class ActionStringHandler @Inject constructor(
var msg = "" var msg = ""
//check for validity //check for validity
if (percentage < Constants.CPP_MIN_PERCENTAGE || percentage > Constants.CPP_MAX_PERCENTAGE) { if (percentage < Constants.CPP_MIN_PERCENTAGE || percentage > Constants.CPP_MAX_PERCENTAGE) {
msg += String.format(rh.gs(R.string.valueoutofrange), "Profile-Percentage") + "\n" msg += rh.gs(R.string.valueoutofrange, "Profile-Percentage") + "\n"
} }
if (timeshift < 0 || timeshift > 23) { if (timeshift < 0 || timeshift > 23) {
msg += String.format(rh.gs(R.string.valueoutofrange), "Profile-Timeshift") + "\n" msg += rh.gs(R.string.valueoutofrange, "Profile-Timeshift") + "\n"
} }
val profile = profileFunction.getProfile() val profile = profileFunction.getProfile()
if (profile == null) { if (profile == null) {

View file

@ -95,7 +95,7 @@ class WearPlugin @Inject constructor(
.toObservable(EventBolusRequested::class.java) .toObservable(EventBolusRequested::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ event: EventBolusRequested -> .subscribe({ event: EventBolusRequested ->
val status = String.format(rh.gs(R.string.bolusrequested), event.amount) val status = rh.gs(R.string.bolusrequested, event.amount)
val intent = Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_BOLUSPROGRESS) val intent = Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_BOLUSPROGRESS)
intent.putExtra("progresspercent", 0) intent.putExtra("progresspercent", 0)
intent.putExtra("progressstatus", status) intent.putExtra("progressstatus", status)

View file

@ -120,7 +120,7 @@ class SendToDataLayerThread extends AsyncTask<DataMap,Void,Void> {
} }
state = 0; state = 0;
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, logPrefix + "Got exception in sendToWear: " + e.toString()); Log.e(TAG, logPrefix + "Got exception in sendToWear: " + e);
} finally { } finally {
lastlock = 0; lastlock = 0;
lock.unlock(); lock.unlock();

View file

@ -46,9 +46,6 @@ import info.nightscout.androidaps.interfaces.Loop;
import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginBase;
import info.nightscout.androidaps.interfaces.Profile; import info.nightscout.androidaps.interfaces.Profile;
import info.nightscout.androidaps.interfaces.ProfileFunction; import info.nightscout.androidaps.interfaces.ProfileFunction;
import info.nightscout.androidaps.utils.wizard.QuickWizardEntry;
import info.nightscout.shared.logging.AAPSLogger;
import info.nightscout.shared.logging.LTag;
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin;
import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.androidaps.plugins.bus.RxBus;
import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus; import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus;
@ -64,7 +61,11 @@ import info.nightscout.androidaps.utils.DefaultValueHelper;
import info.nightscout.androidaps.utils.TrendCalculator; import info.nightscout.androidaps.utils.TrendCalculator;
import info.nightscout.androidaps.utils.resources.ResourceHelper; import info.nightscout.androidaps.utils.resources.ResourceHelper;
import info.nightscout.androidaps.utils.wizard.QuickWizard; import info.nightscout.androidaps.utils.wizard.QuickWizard;
import info.nightscout.androidaps.utils.wizard.QuickWizardEntry;
import info.nightscout.shared.logging.AAPSLogger;
import info.nightscout.shared.logging.LTag;
import info.nightscout.shared.sharedPreferences.SP; import info.nightscout.shared.sharedPreferences.SP;
import info.nightscout.shared.weardata.WearUris;
public class WatchUpdaterService extends WearableListenerService implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { public class WatchUpdaterService extends WearableListenerService implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
@Inject public GlucoseStatusProvider glucoseStatusProvider; @Inject public GlucoseStatusProvider glucoseStatusProvider;
@ -95,21 +96,6 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
public static final String ACTION_CANCEL_NOTIFICATION = WatchUpdaterService.class.getName().concat(".CancelNotification"); public static final String ACTION_CANCEL_NOTIFICATION = WatchUpdaterService.class.getName().concat(".CancelNotification");
private GoogleApiClient googleApiClient; private GoogleApiClient googleApiClient;
public static final String WEARABLE_DATA_PATH = "/nightscout_watch_data";
public static final String WEARABLE_RESEND_PATH = "/nightscout_watch_data_resend";
private static final String WEARABLE_CANCELBOLUS_PATH = "/nightscout_watch_cancel_bolus";
public static final String WEARABLE_CONFIRM_ACTIONSTRING_PATH = "/nightscout_watch_confirmactionstring";
public static final String WEARABLE_INITIATE_ACTIONSTRING_PATH = "/nightscout_watch_initiateactionstring";
private static final String OPEN_SETTINGS_PATH = "/openwearsettings";
private static final String NEW_STATUS_PATH = "/sendstatustowear";
private static final String NEW_PREFERENCES_PATH = "/sendpreferencestowear";
private static final String QUICK_WIZARD_PATH = "/send_quick_wizard";
public static final String BASAL_DATA_PATH = "/nightscout_watch_basal";
public static final String BOLUS_PROGRESS_PATH = "/nightscout_watch_bolusprogress";
public static final String ACTION_CONFIRMATION_REQUEST_PATH = "/nightscout_watch_actionconfirmationrequest";
public static final String ACTION_CHANGECONFIRMATION_REQUEST_PATH = "/nightscout_watch_changeconfirmationrequest";
public static final String ACTION_CANCELNOTIFICATION_REQUEST_PATH = "/nightscout_watch_cancelnotificationrequest";
String TAG = "WatchUpdateService"; String TAG = "WatchUpdateService";
@ -259,21 +245,21 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
// Log.d(TAG, "onMessageRecieved: " + event); // Log.d(TAG, "onMessageRecieved: " + event);
if (wearIntegration()) { if (wearIntegration()) {
if (event != null && event.getPath().equals(WEARABLE_RESEND_PATH)) { if (event != null && event.getPath().equals(WearUris.WEARABLE_RESEND_PATH)) {
resendData(); resendData();
} }
if (event != null && event.getPath().equals(WEARABLE_CANCELBOLUS_PATH)) { if (event != null && event.getPath().equals(WearUris.WEARABLE_CANCELBOLUS_PATH)) {
cancelBolus(); cancelBolus();
} }
if (event != null && event.getPath().equals(WEARABLE_INITIATE_ACTIONSTRING_PATH)) { if (event != null && event.getPath().equals(WearUris.WEARABLE_INITIATE_ACTIONSTRING_PATH)) {
String actionstring = new String(event.getData()); String actionstring = new String(event.getData());
aapsLogger.debug(LTag.WEAR, "Wear: " + actionstring); aapsLogger.debug(LTag.WEAR, "Wear: " + actionstring);
rxBus.send(new EventWearInitiateAction(actionstring)); rxBus.send(new EventWearInitiateAction(actionstring));
} }
if (event != null && event.getPath().equals(WEARABLE_CONFIRM_ACTIONSTRING_PATH)) { if (event != null && event.getPath().equals(WearUris.WEARABLE_CONFIRM_ACTIONSTRING_PATH)) {
String actionstring = new String(event.getData()); String actionstring = new String(event.getData());
aapsLogger.debug(LTag.WEAR, "Wear Confirm: " + actionstring); aapsLogger.debug(LTag.WEAR, "Wear Confirm: " + actionstring);
rxBus.send(new EventWearConfirmAction(actionstring)); rxBus.send(new EventWearConfirmAction(actionstring));
@ -299,7 +285,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
final DataMap dataMap = dataMapSingleBG(lastBG, glucoseStatus); final DataMap dataMap = dataMapSingleBG(lastBG, glucoseStatus);
(new SendToDataLayerThread(WEARABLE_DATA_PATH, googleApiClient)).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, dataMap); (new SendToDataLayerThread(WearUris.WEARABLE_DATA_PATH, googleApiClient)).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, dataMap);
} }
} }
} }
@ -389,7 +375,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
dataMaps.add(dataMap); dataMaps.add(dataMap);
} }
entries.putDataMapArrayList("entries", dataMaps); entries.putDataMapArrayList("entries", dataMaps);
(new SendToDataLayerThread(WEARABLE_DATA_PATH, googleApiClient)).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, entries); (new SendToDataLayerThread(WearUris.WEARABLE_DATA_PATH, googleApiClient)).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, entries);
} }
sendBasals(); sendBasals();
sendStatus(); sendStatus();
@ -423,13 +409,12 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
double endBasalValue = beginBasalValue; double endBasalValue = beginBasalValue;
TemporaryBasal tb1 = iobCobCalculator.getTempBasalIncludingConvertedExtended(runningTime); TemporaryBasal tb1 = iobCobCalculator.getTempBasalIncludingConvertedExtended(runningTime);
TemporaryBasal tb2 = iobCobCalculator.getTempBasalIncludingConvertedExtended(runningTime); //TODO for Adrian ... what's the meaning? TemporaryBasal tb2;
double tb_before = beginBasalValue; double tb_before = beginBasalValue;
double tb_amount = beginBasalValue; double tb_amount = beginBasalValue;
long tb_start = runningTime; long tb_start = runningTime;
if (tb1 != null) { if (tb1 != null) {
tb_before = beginBasalValue;
Profile profileTB = profileFunction.getProfile(runningTime); Profile profileTB = profileFunction.getProfile(runningTime);
if (profileTB != null) { if (profileTB != null) {
tb_amount = TemporaryBasalExtensionKt.convertedToAbsolute(tb1, runningTime, profileTB); tb_amount = TemporaryBasalExtensionKt.convertedToAbsolute(tb1, runningTime, profileTB);
@ -457,7 +442,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
tb2 = iobCobCalculator.getTempBasalIncludingConvertedExtended(runningTime); tb2 = iobCobCalculator.getTempBasalIncludingConvertedExtended(runningTime);
if (tb1 == null && tb2 == null) { if (tb1 == null && tb2 == null) {
//no temp stays no temp ; //no temp stays no temp
} else if (tb1 != null && tb2 == null) { } else if (tb1 != null && tb2 == null) {
//temp is over -> push it //temp is over -> push it
@ -529,7 +514,8 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
if (!predArray.isEmpty()) { if (!predArray.isEmpty()) {
for (GlucoseValueDataPoint bg : predArray) { for (GlucoseValueDataPoint bg : predArray) {
if (bg.getData().getValue() < 40) continue; if (bg.getData().getValue() < 40) continue;
predictions.add(predictionMap(bg.getData().getTimestamp(), bg.getData().getValue(), bg.getPredictionColor())); predictions.add(predictionMap(bg.getData().getTimestamp(),
bg.getData().getValue(), bg.color(null)));
} }
} }
} }
@ -540,7 +526,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
dm.putDataMapArrayList("temps", temps); dm.putDataMapArrayList("temps", temps);
dm.putDataMapArrayList("boluses", boluses); dm.putDataMapArrayList("boluses", boluses);
dm.putDataMapArrayList("predictions", predictions); dm.putDataMapArrayList("predictions", predictions);
(new SendToDataLayerThread(BASAL_DATA_PATH, googleApiClient)).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, dm); (new SendToDataLayerThread(WearUris.BASAL_DATA_PATH, googleApiClient)).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, dm);
} }
private DataMap tempDatamap(long startTime, double startBasal, long to, double toBasal, double amount) { private DataMap tempDatamap(long startTime, double startBasal, long to, double toBasal, double amount) {
@ -582,7 +568,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
private void sendNotification() { private void sendNotification() {
if (googleApiClient != null && googleApiClient.isConnected()) { if (googleApiClient != null && googleApiClient.isConnected()) {
PutDataMapRequest dataMapRequest = PutDataMapRequest.create(OPEN_SETTINGS_PATH); PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.OPEN_SETTINGS_PATH);
//unique content //unique content
dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis()); dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis());
dataMapRequest.getDataMap().putString("openSettings", "openSettings"); dataMapRequest.getDataMap().putString("openSettings", "openSettings");
@ -595,7 +581,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
private void sendBolusProgress(int progresspercent, String status) { private void sendBolusProgress(int progresspercent, String status) {
if (googleApiClient != null && googleApiClient.isConnected()) { if (googleApiClient != null && googleApiClient.isConnected()) {
PutDataMapRequest dataMapRequest = PutDataMapRequest.create(BOLUS_PROGRESS_PATH); PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.BOLUS_PROGRESS_PATH);
//unique content //unique content
dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis()); dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis());
dataMapRequest.getDataMap().putString("bolusProgress", "bolusProgress"); dataMapRequest.getDataMap().putString("bolusProgress", "bolusProgress");
@ -610,7 +596,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
private void sendActionConfirmationRequest(String title, String message, String actionstring) { private void sendActionConfirmationRequest(String title, String message, String actionstring) {
if (googleApiClient != null && googleApiClient.isConnected()) { if (googleApiClient != null && googleApiClient.isConnected()) {
PutDataMapRequest dataMapRequest = PutDataMapRequest.create(ACTION_CONFIRMATION_REQUEST_PATH); PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.ACTION_CONFIRMATION_REQUEST_PATH);
//unique content //unique content
dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis()); dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis());
dataMapRequest.getDataMap().putString("actionConfirmationRequest", "actionConfirmationRequest"); dataMapRequest.getDataMap().putString("actionConfirmationRequest", "actionConfirmationRequest");
@ -629,7 +615,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
private void sendChangeConfirmationRequest(String title, String message, String actionstring) { private void sendChangeConfirmationRequest(String title, String message, String actionstring) {
if (googleApiClient != null && googleApiClient.isConnected()) { if (googleApiClient != null && googleApiClient.isConnected()) {
PutDataMapRequest dataMapRequest = PutDataMapRequest.create(ACTION_CHANGECONFIRMATION_REQUEST_PATH); PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.ACTION_CHANGECONFIRMATION_REQUEST_PATH);
//unique content //unique content
dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis()); dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis());
dataMapRequest.getDataMap().putString("changeConfirmationRequest", "changeConfirmationRequest"); dataMapRequest.getDataMap().putString("changeConfirmationRequest", "changeConfirmationRequest");
@ -648,7 +634,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
private void sendCancelNotificationRequest(String actionstring) { private void sendCancelNotificationRequest(String actionstring) {
if (googleApiClient != null && googleApiClient.isConnected()) { if (googleApiClient != null && googleApiClient.isConnected()) {
PutDataMapRequest dataMapRequest = PutDataMapRequest.create(ACTION_CANCELNOTIFICATION_REQUEST_PATH); PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.ACTION_CANCELNOTIFICATION_REQUEST_PATH);
//unique content //unique content
dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis()); dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis());
dataMapRequest.getDataMap().putString("cancelNotificationRequest", "cancelNotificationRequest"); dataMapRequest.getDataMap().putString("cancelNotificationRequest", "cancelNotificationRequest");
@ -704,7 +690,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
openApsStatus = nsDeviceStatus.getOpenApsTimestamp(); openApsStatus = nsDeviceStatus.getOpenApsTimestamp();
} }
PutDataMapRequest dataMapRequest = PutDataMapRequest.create(NEW_STATUS_PATH); PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.NEW_STATUS_PATH);
//unique content //unique content
dataMapRequest.getDataMap().putString("externalStatusString", status); dataMapRequest.getDataMap().putString("externalStatusString", status);
dataMapRequest.getDataMap().putString("iobSum", iobSum); dataMapRequest.getDataMap().putString("iobSum", iobSum);
@ -734,14 +720,14 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
int percentage = sp.getInt(R.string.key_boluswizard_percentage, 100); int percentage = sp.getInt(R.string.key_boluswizard_percentage, 100);
int maxCarbs = sp.getInt(R.string.key_treatmentssafety_maxcarbs, 48); int maxCarbs = sp.getInt(R.string.key_treatmentssafety_maxcarbs, 48);
double maxBolus = sp.getDouble(R.string.key_treatmentssafety_maxbolus, 3.0); double maxBolus = sp.getDouble(R.string.key_treatmentssafety_maxbolus, 3.0);
PutDataMapRequest dataMapRequest = PutDataMapRequest.create(NEW_PREFERENCES_PATH); PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.NEW_PREFERENCES_PATH);
//unique content //unique content
dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis()); dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis());
dataMapRequest.getDataMap().putBoolean(rh.gs(R.string.key_wear_control), wearcontrol); dataMapRequest.getDataMap().putBoolean(rh.gs(R.string.key_wear_control), wearcontrol);
dataMapRequest.getDataMap().putBoolean(rh.gs(R.string.key_units_mgdl), mgdl); dataMapRequest.getDataMap().putBoolean(rh.gs(R.string.key_units_mgdl), mgdl);
dataMapRequest.getDataMap().putInt(rh.gs(R.string.key_boluswizard_percentage), percentage); dataMapRequest.getDataMap().putInt(rh.gs(R.string.key_boluswizard_percentage), percentage);
dataMapRequest.getDataMap().putInt(rh.gs(R.string.key_treatmentssafety_maxcarbs), maxCarbs); dataMapRequest.getDataMap().putInt(rh.gs(R.string.key_treatmentssafety_maxcarbs), maxCarbs);
dataMapRequest.getDataMap().putDouble(rh.gs(R.string.key_treatmentssafety_maxbolus),maxBolus); dataMapRequest.getDataMap().putDouble(rh.gs(R.string.key_treatmentssafety_maxbolus), maxBolus);
PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest(); PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest();
Wearable.DataApi.putDataItem(googleApiClient, putDataRequest); Wearable.DataApi.putDataItem(googleApiClient, putDataRequest);
} else { } else {
@ -753,14 +739,14 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
if (googleApiClient != null && googleApiClient.isConnected()) { if (googleApiClient != null && googleApiClient.isConnected()) {
int size = quickWizard.size(); int size = quickWizard.size();
ArrayList<DataMap> entities = new ArrayList<>(); ArrayList<DataMap> entities = new ArrayList<>();
for(int i=0; i < size; i++) { for (int i = 0; i < size; i++) {
QuickWizardEntry q = quickWizard.get(i); QuickWizardEntry q = quickWizard.get(i);
if (q.forDevice(QuickWizardEntry.DEVICE_WATCH)) { if (q.forDevice(QuickWizardEntry.DEVICE_WATCH)) {
entities.add(quickMap(q)); entities.add(quickMap(q));
} }
} }
PutDataMapRequest dataMapRequest = PutDataMapRequest.create(QUICK_WIZARD_PATH); PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.QUICK_WIZARD_PATH);
DataMap dm = dataMapRequest.getDataMap(); DataMap dm = dataMapRequest.getDataMap();
dm.putLong("timestamp", System.currentTimeMillis()); dm.putLong("timestamp", System.currentTimeMillis());
@ -794,7 +780,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
return status; return status;
} }
if (!((PluginBase)loop).isEnabled()) { if (!((PluginBase) loop).isEnabled()) {
status += rh.gs(R.string.disabledloop) + "\n"; status += rh.gs(R.string.disabledloop) + "\n";
lastLoopStatus = false; lastLoopStatus = false;
} else { } else {

View file

@ -44,6 +44,9 @@ class ActivityGraph : GraphView {
viewport.isXAxisBoundsManual = true viewport.isXAxisBoundsManual = true
viewport.setMinX(0.0) viewport.setMinX(0.0)
viewport.setMaxX((hours * 60).toDouble()) viewport.setMaxX((hours * 60).toDouble())
viewport.isYAxisBoundsManual = true
viewport.setMinY(0.0)
viewport.setMaxY(0.01)
gridLabelRenderer.numHorizontalLabels = (hours + 1).toInt() gridLabelRenderer.numHorizontalLabels = (hours + 1).toInt()
gridLabelRenderer.horizontalAxisTitle = "[min]" gridLabelRenderer.horizontalAxisTitle = "[min]"
secondScale.addSeries(LineGraphSeries(Array(iobArray.size) { i -> iobArray[i] }).also { secondScale.addSeries(LineGraphSeries(Array(iobArray.size) { i -> iobArray[i] }).also {

View file

@ -1,6 +1,5 @@
package info.nightscout.androidaps.plugins.iob.iobCobCalculator package info.nightscout.androidaps.plugins.iob.iobCobCalculator
import android.os.SystemClock
import androidx.collection.LongSparseArray import androidx.collection.LongSparseArray
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
@ -19,20 +18,19 @@ import info.nightscout.androidaps.extensions.convertedToAbsolute
import info.nightscout.androidaps.extensions.iobCalc import info.nightscout.androidaps.extensions.iobCalc
import info.nightscout.androidaps.extensions.toTemporaryBasal import info.nightscout.androidaps.extensions.toTemporaryBasal
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.overview.OverviewData
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DecimalFormatter import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.workflow.CalculationWorkflow
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.plusAssign
@ -57,12 +55,11 @@ class IobCobCalculatorPlugin @Inject constructor(
rh: ResourceHelper, rh: ResourceHelper,
private val profileFunction: ProfileFunction, private val profileFunction: ProfileFunction,
private val activePlugin: ActivePlugin, private val activePlugin: ActivePlugin,
private val sensitivityOref1Plugin: SensitivityOref1Plugin,
private val sensitivityAAPSPlugin: SensitivityAAPSPlugin,
private val sensitivityWeightedAveragePlugin: SensitivityWeightedAveragePlugin,
private val fabricPrivacy: FabricPrivacy, private val fabricPrivacy: FabricPrivacy,
private val dateUtil: DateUtil, private val dateUtil: DateUtil,
private val repository: AppRepository private val repository: AppRepository,
val overviewData: OverviewData,
private val calculationWorkflow: CalculationWorkflow
) : PluginBase( ) : PluginBase(
PluginDescription() PluginDescription()
.mainType(PluginType.GENERAL) .mainType(PluginType.GENERAL)
@ -81,7 +78,6 @@ class IobCobCalculatorPlugin @Inject constructor(
override var ads: AutosensDataStore = AutosensDataStore() override var ads: AutosensDataStore = AutosensDataStore()
private val dataLock = Any() private val dataLock = Any()
var stopCalculationTrigger = false
private var thread: Thread? = null private var thread: Thread? = null
override fun onStart() { override fun onStart() {
@ -117,14 +113,6 @@ class IobCobCalculatorPlugin @Inject constructor(
resetDataAndRunCalculation("onEventPreferenceChange", event) resetDataAndRunCalculation("onEventPreferenceChange", event)
} }
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
// EventAppInitialized
disposable += rxBus
.toObservable(EventAppInitialized::class.java)
.observeOn(aapsSchedulers.io)
.subscribe(
{ event -> runCalculation("onEventAppInitialized", System.currentTimeMillis(), bgDataReload = true, limitDataToOldestAvailable = true, cause = event) },
fabricPrivacy::logException
)
// EventNewHistoryData // EventNewHistoryData
disposable += rxBus disposable += rxBus
.toObservable(EventNewHistoryData::class.java) .toObservable(EventNewHistoryData::class.java)
@ -138,10 +126,10 @@ class IobCobCalculatorPlugin @Inject constructor(
} }
private fun resetDataAndRunCalculation(reason: String, event: Event?) { private fun resetDataAndRunCalculation(reason: String, event: Event?) {
stopCalculation(reason) calculationWorkflow.stopCalculation(CalculationWorkflow.MAIN_CALCULATION,reason)
clearCache() clearCache()
ads.reset() ads.reset()
runCalculation(reason, System.currentTimeMillis(), bgDataReload = false, limitDataToOldestAvailable = true, cause = event) calculationWorkflow.runCalculation(CalculationWorkflow.MAIN_CALCULATION,this, overviewData, reason, System.currentTimeMillis(), bgDataReload = false, limitDataToOldestAvailable = true, cause = event, runLoop = true)
} }
override fun clearCache() { override fun clearCache() {
@ -168,10 +156,9 @@ class IobCobCalculatorPlugin @Inject constructor(
return oldestTime return oldestTime
} }
fun calculateDetectionStart(from: Long, limitDataToOldestAvailable: Boolean): Long { override fun calculateDetectionStart(from: Long, limitDataToOldestAvailable: Boolean): Long {
val profile = profileFunction.getProfile(from) val profile = profileFunction.getProfile(from)
var dia = Constants.defaultDIA val dia = profile?.dia ?: Constants.defaultDIA
if (profile != null) dia = profile.dia
val oldestDataAvailable = oldestDataAvailable() val oldestDataAvailable = oldestDataAvailable()
val getBGDataFrom: Long val getBGDataFrom: Long
if (limitDataToOldestAvailable) { if (limitDataToOldestAvailable) {
@ -302,11 +289,7 @@ class IobCobCalculatorPlugin @Inject constructor(
override fun getMealDataWithWaitingForCalculationFinish(): MealData { override fun getMealDataWithWaitingForCalculationFinish(): MealData {
val result = MealData() val result = MealData()
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
val maxAbsorptionHours: Double = if (sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled()) { val maxAbsorptionHours: Double = activePlugin.activeSensitivity.maxAbsorptionHours()
sp.getDouble(R.string.key_absorption_maxtime, Constants.DEFAULT_MAX_ABSORPTION_TIME)
} else {
sp.getDouble(R.string.key_absorption_cutoff, Constants.DEFAULT_MAX_ABSORPTION_TIME)
}
val absorptionTimeAgo = now - (maxAbsorptionHours * T.hours(1).msecs()).toLong() val absorptionTimeAgo = now - (maxAbsorptionHours * T.hours(1).msecs()).toLong()
repository.getCarbsDataFromTimeToTimeExpanded(absorptionTimeAgo + 1, now, true) repository.getCarbsDataFromTimeToTimeExpanded(absorptionTimeAgo + 1, now, true)
.blockingGet() .blockingGet()
@ -366,27 +349,6 @@ class IobCobCalculatorPlugin @Inject constructor(
return sb.toString() return sb.toString()
} }
fun stopCalculation(from: String) {
if (thread?.state != Thread.State.TERMINATED) {
stopCalculationTrigger = true
aapsLogger.debug(LTag.AUTOSENS, "Stopping calculation thread: $from")
while (thread != null && thread?.state != Thread.State.TERMINATED) {
SystemClock.sleep(100)
}
aapsLogger.debug(LTag.AUTOSENS, "Calculation thread stopped: $from")
}
}
fun runCalculation(from: String, end: Long, bgDataReload: Boolean, limitDataToOldestAvailable: Boolean, cause: Event?) {
aapsLogger.debug(LTag.AUTOSENS, "Starting calculation thread: " + from + " to " + dateUtil.dateAndTimeAndSecondsString(end))
if (thread == null || thread?.state == Thread.State.TERMINATED) {
thread =
if (sensitivityOref1Plugin.isEnabled()) IobCobOref1Thread(injector, this, from, end, bgDataReload, limitDataToOldestAvailable, cause)
else IobCobThread(injector, this, from, end, bgDataReload, limitDataToOldestAvailable, cause)
thread?.start()
}
}
// Limit rate of EventNewHistoryData // Limit rate of EventNewHistoryData
private val historyWorker = Executors.newSingleThreadScheduledExecutor() private val historyWorker = Executors.newSingleThreadScheduledExecutor()
private var scheduledHistoryPost: ScheduledFuture<*>? = null private var scheduledHistoryPost: ScheduledFuture<*>? = null
@ -428,7 +390,7 @@ class IobCobCalculatorPlugin @Inject constructor(
// When historical data is changed (coming from NS etc) finished calculations after this date must be invalidated // When historical data is changed (coming from NS etc) finished calculations after this date must be invalidated
private fun newHistoryData(oldDataTimestamp: Long, bgDataReload: Boolean, event: Event) { private fun newHistoryData(oldDataTimestamp: Long, bgDataReload: Boolean, event: Event) {
//log.debug("Locking onNewHistoryData"); //log.debug("Locking onNewHistoryData");
stopCalculation("onEventNewHistoryData") calculationWorkflow.stopCalculation(CalculationWorkflow.MAIN_CALCULATION,"onEventNewHistoryData")
synchronized(dataLock) { synchronized(dataLock) {
// clear up 5 min back for proper COB calculation // clear up 5 min back for proper COB calculation
@ -452,7 +414,7 @@ class IobCobCalculatorPlugin @Inject constructor(
} }
ads.newHistoryData(time, aapsLogger, dateUtil) ads.newHistoryData(time, aapsLogger, dateUtil)
} }
runCalculation(event.javaClass.simpleName, System.currentTimeMillis(), bgDataReload, true, event) calculationWorkflow.runCalculation(CalculationWorkflow.MAIN_CALCULATION,this, overviewData, event.javaClass.simpleName, System.currentTimeMillis(), bgDataReload, true, event, runLoop = true)
//log.debug("Releasing onNewHistoryData"); //log.debug("Releasing onNewHistoryData");
} }
@ -574,17 +536,17 @@ class IobCobCalculatorPlugin @Inject constructor(
override fun getTempBasalIncludingConvertedExtended(timestamp: Long): TemporaryBasal? { override fun getTempBasalIncludingConvertedExtended(timestamp: Long): TemporaryBasal? {
val tb = repository.getTemporaryBasalActiveAt(timestamp).blockingGet() val tb = repository.getTemporaryBasalActiveAt(timestamp).blockingGet()
if (tb is ValueWrapper.Existing) return tb.value if (tb is ValueWrapper.Existing) return tb.value
return getConvertedExtended(timestamp); return getConvertedExtended(timestamp)
} }
override fun getTempBasalIncludingConvertedExtendedForRange(startTime: Long, endTime: Long, calculationStep: Long): Map<Long, TemporaryBasal?> { override fun getTempBasalIncludingConvertedExtendedForRange(startTime: Long, endTime: Long, calculationStep: Long): Map<Long, TemporaryBasal?> {
val tempBasals = HashMap<Long, TemporaryBasal?>(); val tempBasals = HashMap<Long, TemporaryBasal?>()
val tbs = repository.getTemporaryBasalsDataActiveBetweenTimeAndTime(startTime, endTime).blockingGet() val tbs = repository.getTemporaryBasalsDataActiveBetweenTimeAndTime(startTime, endTime).blockingGet()
for (t in startTime until endTime step calculationStep) { for (t in startTime until endTime step calculationStep) {
val tb = tbs.firstOrNull { basal -> basal.timestamp <= t && (basal.timestamp + basal.duration) > t } val tb = tbs.firstOrNull { basal -> basal.timestamp <= t && (basal.timestamp + basal.duration) > t }
tempBasals[t] = tb ?: getConvertedExtended(t) tempBasals[t] = tb ?: getConvertedExtended(t)
} }
return tempBasals; return tempBasals
} }
override fun calculateAbsoluteIobFromBaseBasals(toTime: Long): IobTotal { override fun calculateAbsoluteIobFromBaseBasals(toTime: Long): IobTotal {

View file

@ -1,8 +1,10 @@
package info.nightscout.androidaps.plugins.iob.iobCobCalculator package info.nightscout.androidaps.plugins.iob.iobCobCalculator
import android.content.Context import android.content.Context
import android.os.PowerManager
import android.os.SystemClock import android.os.SystemClock
import androidx.work.Worker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
@ -12,9 +14,8 @@ import info.nightscout.androidaps.events.Event
import info.nightscout.androidaps.events.EventAutosensCalculationFinished import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.extensions.target import info.nightscout.androidaps.extensions.target
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.IobCobCalculator
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults
import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
@ -23,6 +24,7 @@ import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin
import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DecimalFormatter import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
@ -30,6 +32,9 @@ import info.nightscout.androidaps.utils.Profiler
import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.workflow.CalculationWorkflow
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
@ -38,15 +43,10 @@ import kotlin.math.max
import kotlin.math.min import kotlin.math.min
import kotlin.math.roundToLong import kotlin.math.roundToLong
class IobCobOref1Thread internal constructor( class IobCobOref1Worker(
private val injector: HasAndroidInjector, context: Context,
private val iobCobCalculatorPlugin: IobCobCalculatorPlugin, // cannot be injected : HistoryBrowser uses different instance params: WorkerParameters
private val from: String, ) : Worker(context, params) {
private val end: Long,
private val bgDataReload: Boolean,
private val limitDataToOldestAvailable: Boolean,
private val cause: Event?
) : Thread() {
@Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var sp: SP @Inject lateinit var sp: SP
@ -62,48 +62,53 @@ class IobCobOref1Thread internal constructor(
@Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var dateUtil: DateUtil @Inject lateinit var dateUtil: DateUtil
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
@Inject lateinit var dataWorker: DataWorker
private var mWakeLock: PowerManager.WakeLock? = null @Inject lateinit var calculationWorkflow: CalculationWorkflow
init { init {
injector.androidInjector().inject(this) (context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
mWakeLock = (context.applicationContext.getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, rh.gs(R.string.app_name) + ":iobCobThread")
} }
override fun run() { class IobCobOref1WorkerData(
val injector: HasAndroidInjector,
val iobCobCalculator: IobCobCalculator, // cannot be injected : HistoryBrowser uses different instance
val from: String,
val end: Long,
val limitDataToOldestAvailable: Boolean,
val cause: Event?
)
override fun doWork(): Result {
val data = dataWorker.pickupObject(inputData.getLong(DataWorker.STORE_KEY, -1)) as IobCobOref1WorkerData?
?: return Result.failure(workDataOf("Error" to "missing input data"))
val start = dateUtil.now() val start = dateUtil.now()
mWakeLock?.acquire(T.mins(10).msecs())
try { try {
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread started: $from") aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread started: ${data.from}")
if (!profileFunction.isProfileValid("IobCobThread")) { if (!profileFunction.isProfileValid("IobCobThread")) {
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No profile): $from") aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No profile): ${data.from}")
return // app still initializing return Result.failure(workDataOf("Error" to "app still initializing"))
} }
//log.debug("Locking calculateSensitivityData"); //log.debug("Locking calculateSensitivityData");
val oldestTimeWithData = iobCobCalculatorPlugin.calculateDetectionStart(end, limitDataToOldestAvailable) val oldestTimeWithData = data.iobCobCalculator.calculateDetectionStart(data.end, data.limitDataToOldestAvailable)
if (bgDataReload) {
iobCobCalculatorPlugin.ads.loadBgData(end, repository, aapsLogger, dateUtil, rxBus)
iobCobCalculatorPlugin.clearCache()
}
// work on local copy and set back when finished // work on local copy and set back when finished
val ads = iobCobCalculatorPlugin.ads.clone() val ads = data.iobCobCalculator.ads.clone()
val bucketedData = ads.bucketedData val bucketedData = ads.bucketedData
val autosensDataTable = ads.autosensDataTable val autosensDataTable = ads.autosensDataTable
if (bucketedData == null || bucketedData.size < 3) { if (bucketedData == null || bucketedData.size < 3) {
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No bucketed data available): $from") aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No bucketed data available): ${data.from}")
return return Result.failure(workDataOf("Error" to "Aborting calculation thread (No bucketed data available): ${data.from}"))
} }
val prevDataTime = ads.roundUpTime(bucketedData[bucketedData.size - 3].timestamp) val prevDataTime = ads.roundUpTime(bucketedData[bucketedData.size - 3].timestamp)
aapsLogger.debug(LTag.AUTOSENS, "Prev data time: " + dateUtil.dateAndTimeString(prevDataTime)) aapsLogger.debug(LTag.AUTOSENS, "Prev data time: " + dateUtil.dateAndTimeString(prevDataTime))
var previous = autosensDataTable[prevDataTime] var previous = autosensDataTable[prevDataTime]
// start from oldest to be able sub cob // start from oldest to be able sub cob
for (i in bucketedData.size - 4 downTo 0) { for (i in bucketedData.size - 4 downTo 0) {
val progress = i.toString() + if (buildHelper.isDev()) " ($from)" else "" rxBus.send(EventIobCalculationProgress(CalculationWorkflow.ProgressData.IOB_COB_OREF, 100 - (100.0 * i / bucketedData.size).toInt(), data.cause))
rxBus.send(EventIobCalculationProgress(progress, cause)) if (isStopped) {
if (iobCobCalculatorPlugin.stopCalculationTrigger) { aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (trigger): ${data.from}")
iobCobCalculatorPlugin.stopCalculationTrigger = false return Result.failure(workDataOf("Error" to "Aborting calculation thread (trigger): ${data.from}"))
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (trigger): $from")
return
} }
// check if data already exists // check if data already exists
var bgTime = bucketedData[i].timestamp var bgTime = bucketedData[i].timestamp
@ -116,12 +121,12 @@ class IobCobOref1Thread internal constructor(
} }
val profile = profileFunction.getProfile(bgTime) val profile = profileFunction.getProfile(bgTime)
if (profile == null) { if (profile == null) {
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (no profile): $from") aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (no profile): ${data.from}")
continue // profile not set yet continue // profile not set yet
} }
aapsLogger.debug(LTag.AUTOSENS, "Processing calculation thread: " + from + " (" + i + "/" + bucketedData.size + ")") aapsLogger.debug(LTag.AUTOSENS, "Processing calculation thread: ${data.from} ($i/${bucketedData.size})")
val sens = profile.getIsfMgdl(bgTime) val sens = profile.getIsfMgdl(bgTime)
val autosensData = AutosensData(injector) val autosensData = AutosensData(data.injector)
autosensData.time = bgTime autosensData.time = bgTime
if (previous != null) autosensData.activeCarbsList = previous.cloneCarbsList() else autosensData.activeCarbsList = ArrayList() if (previous != null) autosensData.activeCarbsList = previous.cloneCarbsList() else autosensData.activeCarbsList = ArrayList()
@ -136,7 +141,7 @@ class IobCobOref1Thread internal constructor(
autosensData.bg = bg autosensData.bg = bg
delta = bg - bucketedData[i + 1].value delta = bg - bucketedData[i + 1].value
avgDelta = (bg - bucketedData[i + 3].value) / 3 avgDelta = (bg - bucketedData[i + 3].value) / 3
val iob = iobCobCalculatorPlugin.calculateFromTreatmentsAndTemps(bgTime, profile) val iob = data.iobCobCalculator.calculateFromTreatmentsAndTemps(bgTime, profile)
val bgi = -iob.activity * sens * 5 val bgi = -iob.activity * sens * 5
val deviation = delta - bgi val deviation = delta - bgi
val avgDeviation = ((avgDelta - bgi) * 1000).roundToLong() / 1000.0 val avgDeviation = ((avgDelta - bgi) * 1000).roundToLong() / 1000.0
@ -312,22 +317,27 @@ class IobCobOref1Thread internal constructor(
if (min in 0..4 && hours % 2 == 0) autosensData.extraDeviation.add(0.0) if (min in 0..4 && hours % 2 == 0) autosensData.extraDeviation.add(0.0)
previous = autosensData previous = autosensData
if (bgTime < dateUtil.now()) autosensDataTable.put(bgTime, autosensData) if (bgTime < dateUtil.now()) autosensDataTable.put(bgTime, autosensData)
aapsLogger.debug(LTag.AUTOSENS, "Running detectSensitivity from: " + dateUtil.dateAndTimeString(oldestTimeWithData) + " to: " + dateUtil.dateAndTimeString(bgTime) + " lastDataTime:" + ads.lastDataTime(dateUtil)) aapsLogger.debug(
LTag.AUTOSENS,
"Running detectSensitivity from: " + dateUtil.dateAndTimeString(oldestTimeWithData) + " to: " + dateUtil.dateAndTimeString(bgTime) + " lastDataTime:" + ads.lastDataTime(
dateUtil
)
)
val sensitivity = activePlugin.activeSensitivity.detectSensitivity(ads, oldestTimeWithData, bgTime) val sensitivity = activePlugin.activeSensitivity.detectSensitivity(ads, oldestTimeWithData, bgTime)
aapsLogger.debug(LTag.AUTOSENS, "Sensitivity result: $sensitivity") aapsLogger.debug(LTag.AUTOSENS, "Sensitivity result: $sensitivity")
autosensData.autosensResult = sensitivity autosensData.autosensResult = sensitivity
aapsLogger.debug(LTag.AUTOSENS, autosensData.toString()) aapsLogger.debug(LTag.AUTOSENS, autosensData.toString())
} }
iobCobCalculatorPlugin.ads = ads data.iobCobCalculator.ads = ads
Thread { Thread {
SystemClock.sleep(1000) SystemClock.sleep(1000)
rxBus.send(EventAutosensCalculationFinished(cause)) rxBus.send(EventAutosensCalculationFinished(data.cause))
}.start() }.start()
} finally { } finally {
mWakeLock?.release() rxBus.send(EventIobCalculationProgress(CalculationWorkflow.ProgressData.IOB_COB_OREF, 100, data.cause))
rxBus.send(EventIobCalculationProgress("", cause)) aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread ended: ${data.from}")
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread ended: $from")
profiler.log(LTag.AUTOSENS, "IobCobOref1Thread", start) profiler.log(LTag.AUTOSENS, "IobCobOref1Thread", start)
} }
return Result.success()
} }
} }

View file

@ -1,8 +1,10 @@
package info.nightscout.androidaps.plugins.iob.iobCobCalculator package info.nightscout.androidaps.plugins.iob.iobCobCalculator
import android.content.Context import android.content.Context
import android.os.PowerManager
import android.os.SystemClock import android.os.SystemClock
import androidx.work.Worker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
@ -10,9 +12,8 @@ import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.events.Event import info.nightscout.androidaps.events.Event
import info.nightscout.androidaps.events.EventAutosensCalculationFinished import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.IobCobCalculator
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults
import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
@ -21,6 +22,7 @@ import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin
import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DecimalFormatter import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
@ -28,23 +30,20 @@ import info.nightscout.androidaps.utils.Profiler
import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.workflow.CalculationWorkflow
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP import info.nightscout.shared.sharedPreferences.SP
import java.util.*
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
import kotlin.math.roundToLong import kotlin.math.roundToLong
class IobCobThread @Inject internal constructor( class IobCobOrefWorker @Inject internal constructor(
private val injector: HasAndroidInjector, context: Context,
private val iobCobCalculatorPlugin: IobCobCalculatorPlugin, // cannot be injected : HistoryBrowser uses different instance params: WorkerParameters
private val from: String, ) : Worker(context, params) {
private val end: Long,
private val bgDataReload: Boolean,
private val limitDataToOldestAvailable: Boolean,
private val cause: Event?
) : Thread() {
@Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var sp: SP @Inject lateinit var sp: SP
@ -60,48 +59,51 @@ class IobCobThread @Inject internal constructor(
@Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var dateUtil: DateUtil @Inject lateinit var dateUtil: DateUtil
@Inject lateinit var repository: AppRepository @Inject lateinit var repository: AppRepository
@Inject lateinit var dataWorker: DataWorker
private var mWakeLock: PowerManager.WakeLock? = null
init { init {
injector.androidInjector().inject(this) (context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
mWakeLock = (context.applicationContext.getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, rh.gs(R.string.app_name) + ":iobCobThread")
} }
override fun run() { class IobCobOrefWorkerData(
val injector: HasAndroidInjector,
val iobCobCalculatorPlugin: IobCobCalculator, // cannot be injected : HistoryBrowser uses different instance
val from: String,
val end: Long,
val limitDataToOldestAvailable: Boolean,
val cause: Event?
)
override fun doWork(): Result {
val data = dataWorker.pickupObject(inputData.getLong(DataWorker.STORE_KEY, -1)) as IobCobOrefWorkerData?
?: return Result.failure(workDataOf("Error" to "missing input data"))
val start = dateUtil.now() val start = dateUtil.now()
mWakeLock?.acquire(T.mins(10).msecs())
try { try {
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread started: $from") aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread started: ${data.from}")
if (!profileFunction.isProfileValid("IobCobThread")) { if (!profileFunction.isProfileValid("IobCobThread")) {
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No profile): $from") aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No profile): ${data.from}")
return // app still initializing return Result.failure(workDataOf("Error" to "app still initializing"))
} }
//log.debug("Locking calculateSensitivityData"); //log.debug("Locking calculateSensitivityData");
val oldestTimeWithData = iobCobCalculatorPlugin.calculateDetectionStart(end, limitDataToOldestAvailable) val oldestTimeWithData = data.iobCobCalculatorPlugin.calculateDetectionStart(data.end, data.limitDataToOldestAvailable)
if (bgDataReload) {
iobCobCalculatorPlugin.ads.loadBgData(end, repository, aapsLogger, dateUtil, rxBus)
iobCobCalculatorPlugin.clearCache()
}
// work on local copy and set back when finished // work on local copy and set back when finished
val ads = iobCobCalculatorPlugin.ads.clone() val ads = data.iobCobCalculatorPlugin.ads.clone()
val bucketedData = ads.bucketedData val bucketedData = ads.bucketedData
val autosensDataTable = ads.autosensDataTable val autosensDataTable = ads.autosensDataTable
if (bucketedData == null || bucketedData.size < 3) { if (bucketedData == null || bucketedData.size < 3) {
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No bucketed data available): $from") aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No bucketed data available): ${data.from}")
return return Result.failure(workDataOf("Error" to "Aborting calculation thread (No bucketed data available): ${data.from}"))
} }
val prevDataTime = ads.roundUpTime(bucketedData[bucketedData.size - 3].timestamp) val prevDataTime = ads.roundUpTime(bucketedData[bucketedData.size - 3].timestamp)
aapsLogger.debug(LTag.AUTOSENS, "Prev data time: " + dateUtil.dateAndTimeString(prevDataTime)) aapsLogger.debug(LTag.AUTOSENS, "Prev data time: " + dateUtil.dateAndTimeString(prevDataTime))
var previous = autosensDataTable[prevDataTime] var previous = autosensDataTable[prevDataTime]
// start from oldest to be able sub cob // start from oldest to be able sub cob
for (i in bucketedData.size - 4 downTo 0) { for (i in bucketedData.size - 4 downTo 0) {
val progress = i.toString() + if (buildHelper.isDev()) " ($from)" else "" rxBus.send(EventIobCalculationProgress(CalculationWorkflow.ProgressData.IOB_COB_OREF, 100 - (100.0 * i / bucketedData.size).toInt(), data.cause))
rxBus.send(EventIobCalculationProgress(progress, cause)) if (isStopped) {
if (iobCobCalculatorPlugin.stopCalculationTrigger) { aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (trigger): ${data.from}")
iobCobCalculatorPlugin.stopCalculationTrigger = false return Result.failure(workDataOf("Error" to "Aborting calculation thread (trigger): ${data.from}"))
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (trigger): $from")
return
} }
// check if data already exists // check if data already exists
var bgTime = bucketedData[i].timestamp var bgTime = bucketedData[i].timestamp
@ -114,12 +116,12 @@ class IobCobThread @Inject internal constructor(
} }
val profile = profileFunction.getProfile(bgTime) val profile = profileFunction.getProfile(bgTime)
if (profile == null) { if (profile == null) {
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (no profile): $from") aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (no profile): ${data.from}")
continue // profile not set yet continue // profile not set yet
} }
aapsLogger.debug(LTag.AUTOSENS, "Processing calculation thread: " + from + " (" + i + "/" + bucketedData.size + ")") aapsLogger.debug(LTag.AUTOSENS, "Processing calculation thread: ${data.from} ($i/${bucketedData.size})")
val sens = profile.getIsfMgdl(bgTime) val sens = profile.getIsfMgdl(bgTime)
val autosensData = AutosensData(injector) val autosensData = AutosensData(data.injector)
autosensData.time = bgTime autosensData.time = bgTime
if (previous != null) autosensData.activeCarbsList = previous.cloneCarbsList() else autosensData.activeCarbsList = ArrayList() if (previous != null) autosensData.activeCarbsList = previous.cloneCarbsList() else autosensData.activeCarbsList = ArrayList()
@ -134,7 +136,7 @@ class IobCobThread @Inject internal constructor(
autosensData.bg = bg autosensData.bg = bg
delta = bg - bucketedData[i + 1].value delta = bg - bucketedData[i + 1].value
avgDelta = (bg - bucketedData[i + 3].value) / 3 avgDelta = (bg - bucketedData[i + 3].value) / 3
val iob = iobCobCalculatorPlugin.calculateFromTreatmentsAndTemps(bgTime, profile) val iob = data.iobCobCalculatorPlugin.calculateFromTreatmentsAndTemps(bgTime, profile)
val bgi = -iob.activity * sens * 5 val bgi = -iob.activity * sens * 5
val deviation = delta - bgi val deviation = delta - bgi
val avgDeviation = ((avgDelta - bgi) * 1000).roundToLong() / 1000.0 val avgDeviation = ((avgDelta - bgi) * 1000).roundToLong() / 1000.0
@ -158,7 +160,7 @@ class IobCobThread @Inject internal constructor(
if (ad == null) { if (ad == null) {
aapsLogger.debug(LTag.AUTOSENS, autosensDataTable.toString()) aapsLogger.debug(LTag.AUTOSENS, autosensDataTable.toString())
aapsLogger.debug(LTag.AUTOSENS, bucketedData.toString()) aapsLogger.debug(LTag.AUTOSENS, bucketedData.toString())
//aapsLogger.debug(LTag.AUTOSENS, iobCobCalculatorPlugin.getBgReadingsDataTable().toString()) //aapsLogger.debug(LTag.AUTOSENS, data.iobCobCalculatorPlugin.getBgReadingsDataTable().toString())
val notification = Notification(Notification.SEND_LOGFILES, rh.gs(R.string.sendlogfiles), Notification.LOW) val notification = Notification(Notification.SEND_LOGFILES, rh.gs(R.string.sendlogfiles), Notification.LOW)
rxBus.send(EventNewNotification(notification)) rxBus.send(EventNewNotification(notification))
sp.putBoolean("log_AUTOSENS", true) sp.putBoolean("log_AUTOSENS", true)
@ -181,7 +183,7 @@ class IobCobThread @Inject internal constructor(
fabricPrivacy.logException(e) fabricPrivacy.logException(e)
aapsLogger.debug(autosensDataTable.toString()) aapsLogger.debug(autosensDataTable.toString())
aapsLogger.debug(bucketedData.toString()) aapsLogger.debug(bucketedData.toString())
//aapsLogger.debug(iobCobCalculatorPlugin.getBgReadingsDataTable().toString()) //aapsLogger.debug(data.iobCobCalculatorPlugin.getBgReadingsDataTable().toString())
val notification = Notification(Notification.SEND_LOGFILES, rh.gs(R.string.sendlogfiles), Notification.LOW) val notification = Notification(Notification.SEND_LOGFILES, rh.gs(R.string.sendlogfiles), Notification.LOW)
rxBus.send(EventNewNotification(notification)) rxBus.send(EventNewNotification(notification))
sp.putBoolean("log_AUTOSENS", true) sp.putBoolean("log_AUTOSENS", true)
@ -258,22 +260,27 @@ class IobCobThread @Inject internal constructor(
} }
previous = autosensData previous = autosensData
if (bgTime < dateUtil.now()) autosensDataTable.put(bgTime, autosensData) if (bgTime < dateUtil.now()) autosensDataTable.put(bgTime, autosensData)
aapsLogger.debug(LTag.AUTOSENS, "Running detectSensitivity from: " + dateUtil.dateAndTimeString(oldestTimeWithData) + " to: " + dateUtil.dateAndTimeString(bgTime) + " lastDataTime:" + ads.lastDataTime(dateUtil)) aapsLogger.debug(
LTag.AUTOSENS,
"Running detectSensitivity from: " + dateUtil.dateAndTimeString(oldestTimeWithData) + " to: " + dateUtil.dateAndTimeString(bgTime) + " lastDataTime:" + ads.lastDataTime(
dateUtil
)
)
val sensitivity = activePlugin.activeSensitivity.detectSensitivity(ads, oldestTimeWithData, bgTime) val sensitivity = activePlugin.activeSensitivity.detectSensitivity(ads, oldestTimeWithData, bgTime)
aapsLogger.debug(LTag.AUTOSENS, "Sensitivity result: $sensitivity") aapsLogger.debug(LTag.AUTOSENS, "Sensitivity result: $sensitivity")
autosensData.autosensResult = sensitivity autosensData.autosensResult = sensitivity
aapsLogger.debug(LTag.AUTOSENS, autosensData.toString()) aapsLogger.debug(LTag.AUTOSENS, autosensData.toString())
} }
iobCobCalculatorPlugin.ads = ads data.iobCobCalculatorPlugin.ads = ads
Thread { Thread {
SystemClock.sleep(1000) SystemClock.sleep(1000)
rxBus.send(EventAutosensCalculationFinished(cause)) rxBus.send(EventAutosensCalculationFinished(data.cause))
}.start() }.start()
} finally { } finally {
mWakeLock?.release() rxBus.send(EventIobCalculationProgress(CalculationWorkflow.ProgressData.IOB_COB_OREF, 100, data.cause))
rxBus.send(EventIobCalculationProgress("", cause)) aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread ended: ${data.from}")
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread ended: $from")
profiler.log(LTag.AUTOSENS, "IobCobThread", start) profiler.log(LTag.AUTOSENS, "IobCobThread", start)
} }
return Result.success()
} }
} }

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.plugins.iob.iobCobCalculator.events package info.nightscout.androidaps.plugins.iob.iobCobCalculator.events
import info.nightscout.androidaps.events.Event import info.nightscout.androidaps.events.Event
import info.nightscout.androidaps.workflow.CalculationWorkflow
class EventIobCalculationProgress(val progress: String, val cause: Event?) : Event() class EventIobCalculationProgress(val pass: CalculationWorkflow.ProgressData, val progressPct: Int, val cause: Event?) : Event()

View file

@ -9,15 +9,18 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.AdapterView import android.widget.AdapterView
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import com.google.android.material.tabs.TabLayout
import dagger.android.support.DaggerFragment import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.SingleFragmentActivity
import info.nightscout.androidaps.data.ProfileSealed import info.nightscout.androidaps.data.ProfileSealed
import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.databinding.LocalprofileFragmentBinding import info.nightscout.androidaps.databinding.LocalprofileFragmentBinding
import info.nightscout.androidaps.dialogs.ProfileSwitchDialog import info.nightscout.androidaps.dialogs.ProfileSwitchDialog
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.interfaces.GlucoseUnit
import info.nightscout.androidaps.interfaces.Profile import info.nightscout.androidaps.interfaces.Profile
@ -29,6 +32,7 @@ import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.HardLimits import info.nightscout.androidaps.utils.HardLimits
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.ui.TimeListEdit import info.nightscout.androidaps.utils.ui.TimeListEdit
@ -49,14 +53,15 @@ class LocalProfileFragment : DaggerFragment() {
@Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var localProfilePlugin: LocalProfilePlugin @Inject lateinit var localProfilePlugin: LocalProfilePlugin
@Inject lateinit var hardLimits: HardLimits @Inject lateinit var hardLimits: HardLimits
@Inject lateinit var protectionCheck: ProtectionCheck
@Inject lateinit var dateUtil: DateUtil @Inject lateinit var dateUtil: DateUtil
@Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var uel: UserEntryLogger @Inject lateinit var uel: UserEntryLogger
private var disposable: CompositeDisposable = CompositeDisposable() private var disposable: CompositeDisposable = CompositeDisposable()
private var inMenu = false
private var queryingProtection = false
private var basalView: TimeListEdit? = null private var basalView: TimeListEdit? = null
// private var spinner: SpinnerHelper? = null
private val save = Runnable { private val save = Runnable {
doEdit() doEdit()
@ -88,8 +93,7 @@ class LocalProfileFragment : DaggerFragment() {
private var _binding: LocalprofileFragmentBinding? = null private var _binding: LocalprofileFragmentBinding? = null
// This property is only valid between onCreateView and // This property is only valid between onCreateView and onDestroyView.
// onDestroyView.
private val binding get() = _binding!! private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
@ -99,31 +103,20 @@ class LocalProfileFragment : DaggerFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
// activate DIA tab val parentClass = this.activity?.let { it::class.java }
processVisibilityOnClick(binding.diaTab) inMenu = parentClass == SingleFragmentActivity::class.java
binding.diaPlaceholder.visibility = View.VISIBLE updateProtectedUi()
// setup listeners processVisibility(0)
binding.diaTab.setOnClickListener { binding.tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
processVisibilityOnClick(it) override fun onTabSelected(tab: TabLayout.Tab) {
binding.diaPlaceholder.visibility = View.VISIBLE processVisibility(tab.position)
} }
binding.icTab.setOnClickListener {
processVisibilityOnClick(it) override fun onTabUnselected(tab: TabLayout.Tab) {}
binding.ic.visibility = View.VISIBLE override fun onTabReselected(tab: TabLayout.Tab) {}
} })
binding.isfTab.setOnClickListener { binding.diaLabel.labelFor = binding.dia.editTextId
processVisibilityOnClick(it) binding.unlock.setOnClickListener { queryProtection() }
binding.isf.visibility = View.VISIBLE
}
binding.basalTab.setOnClickListener {
processVisibilityOnClick(it)
binding.basal.visibility = View.VISIBLE
}
binding.targetTab.setOnClickListener {
processVisibilityOnClick(it)
binding.target.visibility = View.VISIBLE
}
binding.dia.editText?.id?.let { binding.diaLabel.labelFor = it }
} }
fun build() { fun build() {
@ -222,7 +215,6 @@ class LocalProfileFragment : DaggerFragment() {
) )
} }
// Spinner
context?.let { context -> context?.let { context ->
val profileList: ArrayList<CharSequence> = localProfilePlugin.profile?.getProfileList() ?: ArrayList() val profileList: ArrayList<CharSequence> = localProfilePlugin.profile?.getProfileList() ?: ArrayList()
binding.profileList.setAdapter(ArrayAdapter(context, R.layout.spinner_centered, profileList)) binding.profileList.setAdapter(ArrayAdapter(context, R.layout.spinner_centered, profileList))
@ -301,7 +293,7 @@ class LocalProfileFragment : DaggerFragment() {
binding.profileswitch.setOnClickListener { binding.profileswitch.setOnClickListener {
ProfileSwitchDialog() ProfileSwitchDialog()
.also { it.arguments = Bundle().also { bundle -> bundle.putInt("profileIndex", localProfilePlugin.currentProfileIndex) } } .also { it.arguments = Bundle().also { bundle -> bundle.putString("profileName", localProfilePlugin.currentProfile()?.name) } }
.show(childFragmentManager, "ProfileSwitchDialog") .show(childFragmentManager, "ProfileSwitchDialog")
} }
@ -329,6 +321,7 @@ class LocalProfileFragment : DaggerFragment() {
@Synchronized @Synchronized
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
if (inMenu) queryProtection() else updateProtectedUi()
disposable += rxBus disposable += rxBus
.toObservable(EventLocalProfileChanged::class.java) .toObservable(EventLocalProfileChanged::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
@ -366,7 +359,7 @@ class LocalProfileFragment : DaggerFragment() {
val isValid = localProfilePlugin.isValidEditState(activity) val isValid = localProfilePlugin.isValidEditState(activity)
val isEdited = localProfilePlugin.isEdited val isEdited = localProfilePlugin.isEdited
if (isValid) { if (isValid) {
this.view?.setBackgroundColor(rh.gc(R.color.ok_background)) this.view?.setBackgroundColor(rh.gac(context, R.attr.okBackgroundColor))
binding.profileList.isEnabled = true binding.profileList.isEnabled = true
if (isEdited) { if (isEdited) {
@ -378,7 +371,7 @@ class LocalProfileFragment : DaggerFragment() {
binding.save.visibility = View.GONE binding.save.visibility = View.GONE
} }
} else { } else {
this.view?.setBackgroundColor(rh.gc(R.color.error_background)) this.view?.setBackgroundColor(rh.gac(context, R.attr.errorBackgroundColor))
binding.profileList.isEnabled = false binding.profileList.isEnabled = false
binding.profileswitch.visibility = View.GONE binding.profileswitch.visibility = View.GONE
binding.save.visibility = View.GONE //don't save an invalid profile binding.save.visibility = View.GONE //don't save an invalid profile
@ -392,17 +385,28 @@ class LocalProfileFragment : DaggerFragment() {
} }
} }
private fun processVisibilityOnClick(selected: View) { private fun processVisibility(position: Int) {
binding.diaTab.setBackgroundColor(rh.gc(R.color.defaultbackground)) binding.diaPlaceholder.visibility = (position == 0).toVisibility()
binding.icTab.setBackgroundColor(rh.gc(R.color.defaultbackground)) binding.ic.visibility = (position == 1).toVisibility()
binding.isfTab.setBackgroundColor(rh.gc(R.color.defaultbackground)) binding.isf.visibility = (position == 2).toVisibility()
binding.basalTab.setBackgroundColor(rh.gc(R.color.defaultbackground)) binding.basal.visibility = (position == 3).toVisibility()
binding.targetTab.setBackgroundColor(rh.gc(R.color.defaultbackground)) binding.target.visibility = (position == 4).toVisibility()
selected.setBackgroundColor(rh.gc(R.color.tabBgColorSelected)) }
binding.diaPlaceholder.visibility = View.GONE
binding.ic.visibility = View.GONE private fun updateProtectedUi() {
binding.isf.visibility = View.GONE val isLocked = protectionCheck.isLocked(ProtectionCheck.Protection.PREFERENCES)
binding.basal.visibility = View.GONE binding.mainLayout.visibility = isLocked.not().toVisibility()
binding.target.visibility = View.GONE binding.unlock.visibility = isLocked.toVisibility()
}
private fun queryProtection() {
val isLocked = protectionCheck.isLocked(ProtectionCheck.Protection.PREFERENCES)
if (isLocked && !queryingProtection) {
activity?.let { activity ->
queryingProtection = true
val doUpdate = { activity.runOnUiThread { queryingProtection = false; updateProtectedUi() } }
protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, doUpdate, doUpdate, doUpdate)
}
}
} }
} }

View file

@ -52,10 +52,6 @@ class VirtualPumpFragment : DaggerFragment() {
return binding.root return binding.root
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
@Synchronized @Synchronized
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()

View file

@ -137,6 +137,8 @@ class SensitivityAAPSPlugin @Inject constructor(
return output return output
} }
override fun maxAbsorptionHours(): Double = sp.getDouble(R.string.key_absorption_maxtime, Constants.DEFAULT_MAX_ABSORPTION_TIME)
override val id: SensitivityType override val id: SensitivityType
get() = SensitivityType.SENSITIVITY_AAPS get() = SensitivityType.SENSITIVITY_AAPS

View file

@ -53,8 +53,6 @@ class SensitivityOref1Plugin @Inject constructor(
) { ) {
override fun detectSensitivity(ads: AutosensDataStore, fromTime: Long, toTime: Long): AutosensResult { override fun detectSensitivity(ads: AutosensDataStore, fromTime: Long, toTime: Long): AutosensResult {
// todo this method is called from the IobCobCalculatorPlugin, which leads to a circular
// dependency, this should be avoided
val profile = profileFunction.getProfile() val profile = profileFunction.getProfile()
if (profile == null) { if (profile == null) {
aapsLogger.error("No profile") aapsLogger.error("No profile")
@ -204,6 +202,8 @@ class SensitivityOref1Plugin @Inject constructor(
return output return output
} }
override fun maxAbsorptionHours(): Double = sp.getDouble(R.string.key_absorption_cutoff, Constants.DEFAULT_MAX_ABSORPTION_TIME)
override fun configuration(): JSONObject { override fun configuration(): JSONObject {
val c = JSONObject() val c = JSONObject()
try { try {

View file

@ -157,6 +157,8 @@ class SensitivityWeightedAveragePlugin @Inject constructor(
return output return output
} }
override fun maxAbsorptionHours(): Double = sp.getDouble(R.string.key_absorption_maxtime, Constants.DEFAULT_MAX_ABSORPTION_TIME)
override val id: SensitivityType override val id: SensitivityType
get() = SensitivityType.SENSITIVITY_WEIGHTED get() = SensitivityType.SENSITIVITY_WEIGHTED

View file

@ -96,7 +96,7 @@ class AidexPlugin @Inject constructor(
) )
repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null)) repository.runTransactionForResult(CgmSourceTransaction(glucoseValues, emptyList(), null))
.doOnError { .doOnError {
aapsLogger.error(LTag.DATABASE, "Error while saving values from Xdrip", it) aapsLogger.error(LTag.DATABASE, "Error while saving values from Aidex", it)
ret = Result.failure(workDataOf("Error" to it.toString())) ret = Result.failure(workDataOf("Error" to it.toString()))
} }
.blockingGet() .blockingGet()

Some files were not shown because too many files have changed in this diff Show more