Merge branch 'dev' into aidex_support_v2

This commit is contained in:
Andy Rozman 2022-04-12 22:47:09 +01:00
commit e82c6976fd
721 changed files with 11616 additions and 7511 deletions

View file

@ -44,5 +44,5 @@ Hints
* 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
* 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

View file

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

View file

@ -41,14 +41,35 @@
<application
android:name=".MainApp"
android:allowBackup="true"
android:backupAgent=".utils.SPBackupAgent"
android:fullBackupOnly="false"
android:icon="${appIcon}"
android:label="@string/app_name"
android:restoreAnyVersion="true"
android:roundIcon="${appIconRound}"
android:supportsRtl="true"
android:theme="@style/AppTheme.Launcher"
android:fullBackupOnly="false"
android:backupAgent=".utils.SPBackupAgent"
android:restoreAnyVersion="true">
android:theme="@style/AppTheme.Launcher" >
<activity
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
android:name="com.google.android.backup.api_key"
@ -59,6 +80,7 @@
android:resource="@xml/automotive_app_desc" />
<activity android:name=".MainActivity"
android:theme="@style/AppTheme"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@ -76,12 +98,18 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name=".plugins.general.maintenance.activities.PrefImportListActivity" />
<activity android:name=".activities.HistoryBrowseActivity" />
<activity android:name=".activities.TreatmentsActivity" />
<activity android:name=".activities.SurveyActivity" />
<activity android:name=".activities.ProfileHelperActivity" />
<activity android:name=".activities.StatsActivity" />
<activity android:name=".plugins.general.maintenance.activities.PrefImportListActivity"
android:theme="@style/AppTheme" />
<activity android:name=".activities.HistoryBrowseActivity"
android:theme="@style/AppTheme" />
<activity android:name=".activities.TreatmentsActivity"
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
android:name="com.google.firebase.auth.internal.FederatedSignInActivity"
android:excludeFromRecents="true"
@ -125,9 +153,6 @@
</intent-filter>
</receiver>
<!-- Receiver keep alive, scheduled every 30 min -->
<receiver android:name=".receivers.KeepAliveReceiver" />
<!-- Receive ignore 5m, 15m, 30m requests for carb notifications -->
<receiver android:name=".plugins.aps.loop.CarbSuggestionReceiver" />
@ -235,6 +260,7 @@
<activity
android:name=".setupwizard.SetupWizardActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:theme="@style/AppTheme"
android:label="@string/title_activity_setup_wizard" />
<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%)
// 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 sens = profile.sens;
var now = new Date().getHours();
if (now < 1){
now = 1;}
else {
console.error("Time now is "+now+"; ");
}
console.error(" ");
console.error("++++++++++++++++++++++++++++++++++++++++++++++++++++++");
console.error("++ Dynamic ISF Beta 1.4 - Linear Extrapolation/TDD7 ++");
console.error("++++++++++++++++++++++++++++++++++++++++++++++++++++++");
console.error(" ");
if (now < 1){
now = 1;}
else {
console.error("Time now is "+now+"; ");
}
//*********************************************************************************
//** Start of Dynamic ISF code for predictions **
//*********************************************************************************
if (meal_data.TDDAIMI7){
var tdd7 = meal_data.TDDAIMI7;
console.error("---------------------------------------------------------");
console.error( " Dynamic ISF version Beta 1.5 ");
console.error("---------------------------------------------------------");
if (meal_data.TDDAIMI7){
var tdd7 = meal_data.TDDAIMI7;
}
else{
var tdd7 = ((basal * 12)*100)/21;
}
console.error("7-day average TDD is: " +tdd7+ "; ");
console.error(" ");
if (meal_data.TDDLast24){
var tdd_24 = meal_data.TDDLast24;
if (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 {
var tdd_24 = (( basal * 24 ) * 2.8);
else 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 {
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){
var tdd_pump = ( (meal_data.TDDPUMP / now ) * 24);
// 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 ( 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+"; ");
//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; ";
}
var iobArray = iob_data;
if (typeof(iob_data.length) && iob_data.length > 1) {
iob_data = iobArray[0];
//console.error(JSON.stringify(iob_data[0]));
}
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+"; ";
}
if (typeof iob_data.activity === 'undefined' || typeof iob_data.iob === 'undefined' ) {
rT.error ='Error: iob_data missing some property. ';
return rT;
}
var tick;
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+"; ";
}
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);
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);
@ -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.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);

View file

@ -7,6 +7,7 @@ import android.os.Bundle
import android.os.PersistableBundle
import android.text.SpannableString
import android.text.method.LinkMovementMethod
import android.text.style.ForegroundColorSpan
import android.text.util.Linkify
import android.util.TypedValue
import android.view.Menu
@ -19,10 +20,10 @@ import android.widget.EditText
import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar
import androidx.core.view.GravityCompat
import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.tabs.TabLayoutMediator
import com.google.firebase.crashlytics.FirebaseCrashlytics
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.databinding.ActivityMainBinding
import info.nightscout.androidaps.events.EventAppExit
import info.nightscout.androidaps.events.EventInitializationChanged
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventRebuildTabs
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.extensions.isRunningRealPumpTest
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.rx.AapsSchedulers
import info.nightscout.androidaps.utils.tabs.TabPageAdapter
@ -67,7 +70,6 @@ class MainActivity : NoSplashAppCompatActivity() {
private val disposable = CompositeDisposable()
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var rxBus: RxBus
@Inject lateinit var androidPermission: AndroidPermission
@Inject lateinit var sp: SP
@Inject lateinit var versionCheckerUtils: VersionCheckerUtils
@ -84,12 +86,13 @@ class MainActivity : NoSplashAppCompatActivity() {
@Inject lateinit var config: Config
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var passwordCheck: PasswordCheck
private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle
private var pluginPreferencesMenuItem: MenuItem? = null
private var menu: Menu? = null
private var menuOpen = false
private var isProtectionCheckActive = false
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
@ -115,6 +118,7 @@ class MainActivity : NoSplashAppCompatActivity() {
override fun onPageSelected(position: Int) {
setPluginPreferenceMenuName()
checkPluginPreferences(binding.mainPager)
setDisabledMenuItemColorPluginPreferences()
}
})
@ -134,6 +138,12 @@ class MainActivity : NoSplashAppCompatActivity() {
.toObservable(EventPreferenceChange::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ processPreferenceChange(it) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventInitializationChanged::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({
passwordCheck.passwordResetCheck(this)
}, fabricPrivacy::logException)
if (startWizard() && !isRunningRealPumpTest()) {
protectionCheck.queryProtection(this, ProtectionCheck.Protection.PREFERENCES, {
startActivity(Intent(this, SetupWizardActivity::class.java))
@ -168,10 +178,13 @@ class MainActivity : NoSplashAppCompatActivity() {
override fun onResume() {
super.onResume()
protectionCheck.queryProtection(this, ProtectionCheck.Protection.APPLICATION, null,
UIRunnable { OKDialog.show(this, "", rh.gs(R.string.authorizationfailed)) { finish() } },
UIRunnable { OKDialog.show(this, "", rh.gs(R.string.authorizationfailed)) { finish() } }
)
if (!isProtectionCheckActive) {
isProtectionCheckActive = true
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() {
@ -252,6 +265,14 @@ class MainActivity : NoSplashAppCompatActivity() {
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() {
if (binding.mainPager.currentItem >= 0) {
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) {
menuOpen = false;
menuOpen = false
super.onPanelClosed(featureId, menu)
}
@ -280,6 +301,7 @@ class MainActivity : NoSplashAppCompatActivity() {
pluginPreferencesMenuItem = menu.findItem(R.id.nav_plugin_preferences)
setPluginPreferenceMenuName()
checkPluginPreferences(binding.mainPager)
setDisabledMenuItemColorPluginPreferences()
return true
}
@ -320,7 +342,7 @@ class MainActivity : NoSplashAppCompatActivity() {
message += rh.gs(R.string.about_link_urls)
val messageSpanned = SpannableString(message)
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)
.setIcon(iconsProvider.getIcon())
.setMessage(messageSpanned)

View file

@ -6,6 +6,13 @@ import android.content.IntentFilter
import android.net.ConnectivityManager
import android.net.wifi.WifiManager
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 dagger.android.AndroidInjector
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.ConfigBuilder
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.plugins.configBuilder.PluginStore
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.NotificationStore
import info.nightscout.androidaps.receivers.BTReceiver
import info.nightscout.androidaps.receivers.ChargingStateReceiver
import info.nightscout.androidaps.receivers.KeepAliveReceiver.KeepAliveManager
import info.nightscout.androidaps.receivers.NetworkChangeReceiver
import info.nightscout.androidaps.receivers.TimeDateOrTZChangeReceiver
import info.nightscout.androidaps.plugins.general.themes.ThemeSwitcherPlugin
import info.nightscout.androidaps.receivers.*
import info.nightscout.androidaps.services.AlarmSoundServiceHelper
import info.nightscout.androidaps.utils.ActivityMonitor
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.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 io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.exceptions.UndeliverableException
@ -46,6 +52,7 @@ import io.reactivex.rxjava3.plugins.RxJavaPlugins
import net.danlew.android.joda.JodaTimeAndroid
import java.io.IOException
import java.net.SocketException
import java.util.concurrent.TimeUnit
import javax.inject.Inject
class MainApp : DaggerApplication() {
@ -60,16 +67,20 @@ class MainApp : DaggerApplication() {
@Inject lateinit var config: Config
@Inject lateinit var buildHelper: BuildHelper
@Inject lateinit var configBuilder: ConfigBuilder
@Inject lateinit var keepAliveManager: KeepAliveManager
@Inject lateinit var plugins: List<@JvmSuppressWildcards PluginBase>
@Inject lateinit var compatDBHelper: CompatDBHelper
@Inject lateinit var repository: AppRepository
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var staticInjector: StaticInjector// TODO avoid , here fake only to initialize
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var passwordCheck: PasswordCheck
@Inject lateinit var alarmSoundServiceHelper: AlarmSoundServiceHelper
@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() {
super.onCreate()
@ -77,6 +88,7 @@ class MainApp : DaggerApplication() {
RxDogTag.install()
setRxErrorHandler()
LocaleHelper.update(this)
ProcessLifecycleOwner.get().lifecycle.addObserver(processLifecycleListener)
var gitRemote: String? = BuildConfig.REMOTE
var commitHash: String? = BuildConfig.HEAD
@ -84,21 +96,10 @@ class MainApp : DaggerApplication() {
gitRemote = 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()
registerActivityLifecycleCallbacks(activityMonitor)
JodaTimeAndroid.init(this)
profileSwitchPlugin.setThemeMode()
aapsLogger.debug("Version: " + BuildConfig.VERSION_NAME)
aapsLogger.debug("BuildVersion: " + BuildConfig.BUILDVERSION)
aapsLogger.debug("Remote: " + BuildConfig.REMOTE)
@ -113,10 +114,38 @@ class MainApp : DaggerApplication() {
// Register all tabs in app here
pluginStore.plugins = plugins
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()
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() {
@ -186,7 +215,6 @@ class MainApp : DaggerApplication() {
override fun onTerminate() {
aapsLogger.debug(LTag.CORE, "onTerminate")
unregisterActivityLifecycleCallbacks(activityMonitor)
keepAliveManager.cancelAlarm(this)
alarmSoundServiceHelper.stopService(this)
super.onTerminate()
}

View file

@ -2,7 +2,7 @@ package info.nightscout.androidaps.activities
import android.annotation.SuppressLint
import android.app.DatePickerDialog
import android.graphics.Color
import android.content.Context
import android.os.Bundle
import android.util.DisplayMetrics
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.EventRefreshOverview
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.extensions.toVisibilityKeepSpace
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.Loop
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.overview.OverviewData
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.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.sensitivity.SensitivityAAPSPlugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin
import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DefaultValueHelper
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.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.workflow.CalculationWorkflow
import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.disposables.CompositeDisposable
@ -52,15 +51,11 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
@Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var rxBus: RxBus
@Inject lateinit var sp: SP
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var defaultValueHelper: DefaultValueHelper
@Inject lateinit var activePlugin: ActivePlugin
@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 fabricPrivacy: FabricPrivacy
@Inject lateinit var overviewMenus: OverviewMenus
@ -69,6 +64,9 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
@Inject lateinit var loop: Loop
@Inject lateinit var nsDeviceStatus: NSDeviceStatus
@Inject lateinit var translator: Translator
@Inject lateinit var context: Context
@Inject lateinit var dataWorker: DataWorker
@Inject lateinit var calculationWorkflow: CalculationWorkflow
private val disposable = CompositeDisposable()
@ -91,6 +89,17 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
setContentView(binding.root)
// 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 =
IobCobCalculatorPlugin(
injector,
@ -101,30 +110,11 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
rh,
profileFunction,
activePlugin,
sensitivityOref1Plugin,
sensitivityAAPSPlugin,
sensitivityWeightedAveragePlugin,
fabricPrivacy,
dateUtil,
repository
)
overviewData =
OverviewData(
injector,
aapsLogger,
rh,
dateUtil,
sp,
activePlugin,
defaultValueHelper,
profileFunction,
config,
loop,
nsDeviceStatus,
repository,
overviewMenus,
iobCobCalculator,
translator
overviewData,
calculationWorkflow
)
binding.left.setOnClickListener {
@ -179,7 +169,7 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
val cal = Calendar.getInstance()
cal.timeInMillis = overviewData.fromTime
DatePickerDialog(
this, R.style.MaterialPickerTheme,
this,
dateSetListener,
cal.get(Calendar.YEAR),
cal.get(Calendar.MONTH),
@ -188,18 +178,19 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
}
val dm = DisplayMetrics()
@Suppress("DEPRECATION")
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R)
display?.getRealMetrics(dm)
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
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?.labelVerticalWidth = axisWidth
overviewMenus.setupChartMenu(binding.chartMenuButton)
overviewMenus.setupChartMenu(context, binding.chartMenuButton)
prepareGraphsIfNeeded(overviewMenus.setting.size)
savedInstanceState?.let { bundle ->
rangeToDisplay = bundle.getInt("rangeToDisplay", 0)
@ -211,7 +202,7 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
override fun onPause() {
super.onPause()
disposable.clear()
iobCobCalculator.stopCalculation("onPause")
calculationWorkflow.stopCalculation(CalculationWorkflow.HISTORY_CALCULATION, "onPause")
}
@Synchronized
@ -233,22 +224,11 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
disposable += rxBus
.toObservable(EventIobCalculationProgress::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({
if (it.cause is EventCustomCalculationFinished)
binding.overviewIobcalculationprogess.text = it.progress
}, fabricPrivacy::logException)
.subscribe({ updateCalcProgress(it.pass.finalPercent(it.progressPct)) }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventRefreshOverview::class.java)
.toObservable(EventUpdateOverviewGraph::class.java)
.observeOn(aapsSchedulers.main)
.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) {
// set start of current day
@ -279,12 +259,12 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
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.gridLabelRenderer?.gridColor = rh.gc(R.color.graphgrid)
graph.gridLabelRenderer?.gridColor = rh.gac(R.attr.graphgrid)
graph.gridLabelRenderer?.reloadStyles()
graph.gridLabelRenderer?.isHorizontalLabelsVisible = false
graph.gridLabelRenderer?.labelVerticalWidth = axisWidth
graph.gridLabelRenderer?.numVerticalLabels = 3
graph.viewport.backgroundColor = Color.argb(20, 255, 255, 255) // 8% of gray
graph.viewport.backgroundColor = rh.gac(this, R.attr.viewPortbackgroundColor)
relativeLayout.addView(graph)
val label = TextView(this)
@ -304,17 +284,7 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
@Suppress("SameParameterValue")
private fun loadAll(from: String) {
updateDate()
Thread {
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()
runCalculation(from)
}
private fun setTime(start: Long) {
@ -335,25 +305,32 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
}
private fun runCalculation(from: String) {
Thread {
iobCobCalculator.stopCalculation(from)
iobCobCalculator.stopCalculationTrigger = false
iobCobCalculator.runCalculation(from, overviewData.toTime, bgDataReload = true, limitDataToOldestAvailable = false, cause = EventCustomCalculationFinished())
}.start()
calculationWorkflow.runCalculation(
CalculationWorkflow.HISTORY_CALCULATION,
iobCobCalculator,
overviewData,
from,
overviewData.toTime,
bgDataReload = true,
limitDataToOldestAvailable = false,
cause = EventCustomCalculationFinished(),
runLoop = false
)
}
@Volatile
var runningRefresh = false
@Suppress("SameParameterValue")
private fun refreshLoop(from: String) {
if (runningRefresh) return
runningRefresh = true
overviewData.prepareIobAutosensData(from)
rxBus.send(EventRefreshOverview(from))
aapsLogger.debug(LTag.UI, "refreshLoop finished")
runningRefresh = false
}
fun updateDate() {
private fun updateDate() {
binding.date.text = dateUtil.dateAndTimeString(overviewData.fromTime)
binding.zoom.text = rangeToDisplay.toString()
}
@ -372,6 +349,8 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
graphData.addBgReadings(menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal])
if (buildHelper.isDev()) graphData.addBucketedData()
graphData.addTreatments()
if (menuChartSettings[0][OverviewMenus.CharType.TREAT.ordinal])
graphData.addTherapyEvents()
if (menuChartSettings[0][OverviewMenus.CharType.ACT.ordinal])
graphData.addActivity(0.8)
if (pump.pumpDescription.isTempBasalCapable && menuChartSettings[0][OverviewMenus.CharType.BAS.ordinal])
@ -400,12 +379,12 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
var useDSForScale = false
var useBGIForScale = false
when {
menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] -> useABSForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal] -> useIobForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal] -> useCobForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] -> useDevForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal] -> useBGIForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal] -> useRatioForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] -> useABSForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal] -> useIobForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal] -> useCobForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] -> useDevForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal] -> useBGIForScale = true
menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal] -> useRatioForScale = 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]
@ -437,4 +416,9 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
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.preference.*
import dagger.android.support.AndroidSupportInjection
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.R
import info.nightscout.androidaps.danaRKorean.DanaRKoreanPlugin
import info.nightscout.androidaps.danaRv2.DanaRv2Plugin
import info.nightscout.androidaps.danar.DanaRPlugin
import info.nightscout.androidaps.danars.DanaRSPlugin
import info.nightscout.androidaps.diaconn.DiaconnG8Plugin
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventRebuildTabs
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugin.general.openhumans.OpenHumansUploader
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.SensitivityWeightedAveragePlugin
import info.nightscout.androidaps.plugins.source.*
import info.nightscout.shared.SafeParse
import info.nightscout.androidaps.utils.alertDialogs.OKDialog.show
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.shared.SafeParse
import info.nightscout.shared.sharedPreferences.SP
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_bolus_protection) == key) &&
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 {
val title = rh.gs(R.string.unsecure_fallback_biometric)
@ -246,9 +246,9 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
}
// Master password erased with activated Biometric protection
val isBiometricActivated = sp.getInt(R.string.key_settings_protection, ProtectionCheck.ProtectionType.NONE.ordinal) == ProtectionCheck.ProtectionType.BIOMETRIC.ordinal ||
sp.getInt(R.string.key_application_protection, ProtectionCheck.ProtectionType.NONE.ordinal) == ProtectionCheck.ProtectionType.BIOMETRIC.ordinal ||
sp.getInt(R.string.key_bolus_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, NONE.ordinal) == 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) {
activity?.let {
val title = rh.gs(R.string.unsecure_fallback_biometric)
@ -319,26 +319,35 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
if (pref is ListPreference) {
pref.setSummary(pref.entry)
// Preferences
// Preferences
if (pref.getKey() == rh.gs(R.string.key_settings_protection)) {
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
if (pref.getKey() == rh.gs(R.string.key_application_protection)) {
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
if (pref.getKey() == rh.gs(R.string.key_bolus_protection)) {
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.getKey().contains("password") || pref.getKey().contains("secret")) {
if (pref.getKey().contains("password") || pref.getKey().contains("pin") || pref.getKey().contains("secret")) {
pref.setSummary("******")
} else if (pref.text != null) {
pref.dialogMessage = pref.dialogMessage
@ -354,7 +363,10 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
rh.gs(R.string.key_bolus_password),
rh.gs(R.string.key_master_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) {
@ -362,7 +374,11 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
if (sp.getString(pref.key, "").startsWith("hmac:")) {
pref.summary = "******"
} 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)
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
if (preference.key == rh.gs(R.string.key_statuslights_copy_ns)) {
nsSettingStatus.copyStatusLightsNsSettings(context)
@ -421,4 +449,4 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
this.filter = filter
preferenceManager?.preferenceScreen?.let { updateFilterVisibility(filter, it) }
}
}
}

View file

@ -4,6 +4,7 @@ import android.content.Context
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.view.MenuItem
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceScreen
import info.nightscout.androidaps.R
@ -19,6 +20,7 @@ class PreferencesActivity : NoSplashAppCompatActivity(), PreferenceFragmentCompa
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setTheme(R.style.AppTheme)
binding = ActivityPreferencesBinding.inflate(layoutInflater)
setContentView(binding.root)
@ -61,4 +63,14 @@ class PreferencesActivity : NoSplashAppCompatActivity(), PreferenceFragmentCompa
private fun filterPreferences() {
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.view.Menu
import android.widget.PopupMenu
import android.widget.TextView
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.ProfileSealed
import info.nightscout.androidaps.data.PureProfile
@ -37,7 +38,6 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
@Inject lateinit var defaultProfile: DefaultProfile
@Inject lateinit var defaultProfileDPV: DefaultProfileDPV
@Inject lateinit var localProfilePlugin: LocalProfilePlugin
@Inject lateinit var rxBus: RxBus
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var repository: AppRepository
@ -66,9 +66,9 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
private lateinit var binding: ActivityProfilehelperBinding
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityProfilehelperBinding.inflate(layoutInflater)
setContentView(binding.root)
@ -79,11 +79,11 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
switchTab(1, typeSelected[1])
}
binding.profiletype.setOnClickListener {
PopupMenu(this, binding.profiletype).apply {
binding.profileType.setOnClickListener {
PopupMenu(this, binding.profileType).apply {
menuInflater.inflate(R.menu.menu_profilehelper, menu)
setOnMenuItemClickListener { item ->
binding.profiletype.setText(item.title)
binding.profileType.setText(item.title)
when (item.itemId) {
R.id.menu_default -> switchTab(tabSelected, ProfileType.MOTOL_DEFAULT)
R.id.menu_default_dpv -> switchTab(tabSelected, ProfileType.DPV_DEFAULT)
@ -130,7 +130,7 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
}
// Default profile
binding.copytolocalprofile.setOnClickListener {
binding.copyToLocalProfile.setOnClickListener {
storeValues()
val age = ageUsed[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.text = getString(R.string.tdd) + ": " + rh.gs(R.string.calculation_in_progress)
binding.tdds.addView(TextView(this).apply { text = rh.gs(R.string.tdd) + ": " + rh.gs(R.string.calculation_in_progress) })
Thread {
val tdds = tddCalculator.stats()
runOnUiThread { binding.tdds.text = tdds }
val tdds = tddCalculator.stats(this)
runOnUiThread {
binding.tdds.removeAllViews()
binding.tdds.addView(tdds)
}
}.start()
// Current profile
binding.currentProfileText.text = profileFunction.getProfileName()
// General
binding.compareprofile.setOnClickListener {
binding.compareProfiles.setOnClickListener {
storeValues()
for (i in 0..1) {
if (typeSelected[i] == ProfileType.MOTOL_DEFAULT) {
@ -239,10 +241,10 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
}
ToastUtils.showToastInUiThread(this, R.string.invalidinput)
}
binding.age.editText?.id?.let { binding.ageLabel.labelFor = it }
binding.tdd.editText?.id?.let { binding.tddLabel.labelFor = it }
binding.weight.editText?.id?.let { binding.weightLabel.labelFor = it }
binding.basalpctfromtdd.editText?.id?.let { binding.basalpctfromtddLabel.labelFor = it }
binding.ageLabel.labelFor = binding.age.editTextId
binding.tddLabel.labelFor = binding.tdd.editTextId
binding.weightLabel.labelFor = binding.weight.editTextId
binding.basalPctFromTddLabel.labelFor = binding.basalPctFromTdd.editTextId
switchTab(0, typeSelected[0], false)
}
@ -273,7 +275,7 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
ageUsed[tabSelected] = binding.age.value
weightUsed[tabSelected] = binding.weight.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) {
@ -283,10 +285,10 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
tabSelected = tab
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
binding.profiletype.setText(
binding.profileType.setText(
when (typeSelected[tabSelected]) {
ProfileType.MOTOL_DEFAULT -> rh.gs(R.string.motoldefaultprofile)
ProfileType.DPV_DEFAULT -> rh.gs(R.string.dpvdefaultprofile)
@ -304,9 +306,9 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
binding.age.value = ageUsed[tabSelected]
binding.weight.value = weightUsed[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())
binding.availableProfileList.setText(profileList[profileUsed[tabSelected]].toString())
if (profileSwitch.isNotEmpty())
@ -314,7 +316,7 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
}
private fun setBackgroundColorOnSelected(tab: Int) {
binding.menu1.setBackgroundColor(rh.gc(if (tab == 1) R.color.defaultbackground else R.color.helperProfile))
binding.menu2.setBackgroundColor(rh.gc(if (tab == 0) R.color.defaultbackground else R.color.examinedProfile))
binding.menu1.setBackgroundColor(rh.gac(this, if (tab == 1) R.attr.defaultbackground else R.attr.helperProfileColor))
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.os.Bundle
import android.widget.TextView
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.entities.UserEntry.Action
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.utils.ActivityMonitor
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.TirCalculator
import javax.inject.Inject
@ -29,21 +29,30 @@ class StatsActivity : NoSplashAppCompatActivity() {
binding = ActivityStatsBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.tdds.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.activity.text = rh.gs(R.string.activitymonitor) + ": " + 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.addView(TextView(this).apply { text = getString(R.string.tir) + ": " + 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 {
val tdds = tddCalculator.stats()
runOnUiThread { binding.tdds.text = tdds }
val tdds = tddCalculator.stats(this)
runOnUiThread {
binding.tdds.removeAllViews()
binding.tdds.addView(tdds)
}
}.start()
Thread {
val tir = tirCalculator.stats()
runOnUiThread { binding.tir.text = tir }
val tir = tirCalculator.stats(this)
runOnUiThread {
binding.tir.removeAllViews()
binding.tir.addView(tir)
}
}.start()
Thread {
val activity = activityMonitor.stats()
runOnUiThread { binding.activity.text = activity }
val activity = activityMonitor.stats(this)
runOnUiThread {
binding.activity.removeAllViews()
binding.activity.addView(activity)
}
}.start()
binding.ok.setOnClickListener { finish() }

View file

@ -43,9 +43,9 @@ class SurveyActivity : NoSplashAppCompatActivity() {
val profileList = profileStore?.getProfileList() ?: return
binding.spinner.adapter = ArrayAdapter(this, R.layout.spinner_centered, profileList)
binding.tdds.text = tddCalculator.stats()
binding.tir.text = tirCalculator.stats()
binding.activity.text = activityMonitor.stats()
//binding.tdds.text = tddCalculator.stats()
//binding.tir.text = tirCalculator.stats()
//binding.activity.text = activityMonitor.stats()
binding.profile.setOnClickListener {
val age = SafeParse.stringToDouble(binding.age.text.toString())
@ -104,7 +104,7 @@ class SurveyActivity : NoSplashAppCompatActivity() {
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
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
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.view.MenuItem
import android.view.View
import androidx.fragment.app.Fragment
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.activities.fragments.*
import info.nightscout.androidaps.databinding.TreatmentsFragmentBinding
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import javax.inject.Inject
@ -23,49 +25,34 @@ class TreatmentsActivity : NoSplashAppCompatActivity() {
super.onCreate(savedInstanceState)
binding = TreatmentsFragmentBinding.inflate(layoutInflater)
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())
setBackgroundColorOnSelected(binding.treatments)
setSupportActionBar(binding.toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
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 {
@ -86,15 +73,4 @@ class TreatmentsActivity : NoSplashAppCompatActivity() {
.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.graphics.Paint
import android.os.Bundle
import android.util.Log
import android.util.SparseArray
import android.view.*
import android.widget.CompoundButton
import android.view.ActionMode
import androidx.appcompat.widget.Toolbar
import androidx.core.util.forEach
import androidx.recyclerview.widget.LinearLayoutManager
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.BolusCalculatorResult
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.Sources
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.TreatmentsBolusCarbsItemBinding
import info.nightscout.androidaps.dialogs.WizardInfoDialog
import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.events.EventTreatmentChange
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.extensions.iobCalc
import info.nightscout.androidaps.extensions.toVisibility
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.general.nsclient.events.EventNSClientRestart
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.FabricPrivacy
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.resources.ResourceHelper
@ -72,8 +69,10 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
@Inject lateinit var activePlugin: ActivePlugin
private var _binding: TreatmentsBolusCarbsFragmentBinding? = null
// This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!!
private var menu: Menu? = null
class MealLink(
val bolus: Bolus? = null,
@ -82,23 +81,24 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
)
private val disposable = CompositeDisposable()
private lateinit var actionHelper: ActionModeHelper<MealLink>
private val millsToThePast = T.days(30).msecs()
private var selectedItems: SparseArray<MealLink> = SparseArray()
private var showInvalidated = false
private var removeActionMode: ActionMode? = null
private var toolbar: Toolbar? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
TreatmentsBolusCarbsFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root
@SuppressLint("CheckResult")
@SuppressLint("NotifyDataSetChanged")
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)
toolbar = activity?.findViewById(R.id.toolbar)
binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
binding.recyclerview.emptyView = binding.noRecordsText
binding.recyclerview.loadingView = binding.progressBar
}
private fun bolusMealLinksWithInvalid(now: Long) = repository
@ -127,8 +127,8 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
fun swapAdapter() {
val now = System.currentTimeMillis()
disposable +=
binding.recyclerview.isLoading = true
disposable +=
if (showInvalidated)
carbsMealLinksWithInvalid(now)
.zipWith(bolusMealLinksWithInvalid(now)) { first, second -> first + second }
@ -169,28 +169,18 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
.observeOn(aapsSchedulers.main)
.debounce(1L, TimeUnit.SECONDS)
.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
override fun onPause() {
super.onPause()
actionHelper.finish()
disposable.clear()
}
@Synchronized
override fun onDestroyView() {
super.onDestroyView()
removeActionMode?.let { it.finish() }
binding.recyclerview.adapter = null // avoid leaks
_binding = null
}
@ -206,9 +196,9 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
val profile = profileFunction.getProfile() ?: return
val ml = mealLinks[position]
val sameDayPrevious = position > 0 && dateUtil.isSameDay(timestamp(ml), timestamp(mealLinks[position - 1]))
holder.binding.date.visibility = sameDayPrevious.not().toVisibility()
holder.binding.date.text = dateUtil.dateString(timestamp(ml))
val newDay = position == 0 || !dateUtil.isSameDayGroup(timestamp(ml), timestamp(mealLinks[position - 1]))
holder.binding.date.visibility = newDay.toVisibility()
holder.binding.date.text = if (newDay) dateUtil.dateStringRelative(timestamp(ml), rh) else ""
// Metadata
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()
val iob = bolus.iobCalc(activePlugin, System.currentTimeMillis(), profile.dia)
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.iobLabel.visibility = View.VISIBLE
holder.binding.iob.visibility = View.VISIBLE
@ -236,13 +226,25 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
holder.binding.iobLabel.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 =
when (ml.bolus.type) {
Bolus.Type.SMB -> "SMB"
Bolus.Type.NORMAL -> rh.gs(R.string.mealbolus)
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
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.carbsPump.visibility = (carbs.interfaceIDs.pumpId != null).toVisibility()
holder.binding.carbsInvalid.visibility = carbs.isValid.not().toVisibility()
}
holder.binding.cbBolusRemove.visibility = (ml.bolus?.isValid == true && removeActionMode != null).toVisibility()
holder.binding.cbCarbsRemove.visibility = (ml.carbs?.isValid == true && removeActionMode != null).toVisibility()
if (removeActionMode != null) {
val onChange = CompoundButton.OnCheckedChangeListener { _, value ->
if (value) {
selectedItems.put(position, ml)
} else {
selectedItems.remove(position)
holder.binding.cbCarbsRemove.visibility = (ml.carbs.isValid && actionHelper.isRemoving).toVisibility()
if (actionHelper.isRemoving) {
holder.binding.cbCarbsRemove.setOnCheckedChangeListener { _, value ->
actionHelper.updateSelection(position, ml, value)
}
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
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
@ -297,13 +295,18 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
this.menu = menu
inflater.inflate(R.menu.menu_treatments_carbs_bolus, menu)
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) {
menu.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated
menu.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated
updateMenuVisibility()
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
val hasItems = (binding.recyclerview.adapter?.itemCount ?: 0) > 0
@ -314,20 +317,21 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
override fun onOptionsItemSelected(item: MenuItem): Boolean =
when (item.itemId) {
R.id.nav_remove_items -> {
removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback())
true
}
R.id.nav_remove_items -> actionHelper.startRemove()
R.id.nav_show_invalidated -> {
showInvalidated = true
rxBus.send(EventTreatmentUpdateGui())
updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records))
swapAdapter()
true
}
R.id.nav_hide_invalidated -> {
showInvalidated = false
rxBus.send(EventTreatmentUpdateGui())
updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.hide_invalidated_records))
swapAdapter()
true
}
@ -423,36 +427,7 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
}
}
inner class RemoveActionModeCallback : ActionMode.Callback {
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 {
private fun getConfirmationText(selectedItems: SparseArray<MealLink>): String {
if (selectedItems.size() == 1) {
val mealLink = selectedItems.valueAt(0)
val bolus = mealLink.bolus
@ -467,40 +442,38 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() {
return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size())
}
fun removeSelected() {
if (selectedItems.size() > 0)
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable {
selectedItems.forEach { _, ml ->
ml.bolus?.let { bolus ->
uel.log(
Action.BOLUS_REMOVED, Sources.Treatments,
ValueWithUnit.Timestamp(bolus.timestamp),
ValueWithUnit.Insulin(bolus.amount)
private fun removeSelected(selectedItems: SparseArray<MealLink>) {
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(selectedItems), Runnable {
selectedItems.forEach { _, ml ->
ml.bolus?.let { bolus ->
uel.log(
Action.BOLUS_REMOVED, Sources.Treatments,
ValueWithUnit.Timestamp(bolus.timestamp),
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()
})
}
else
removeActionMode?.finish()
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) }
)
}
}
actionHelper.finish()
})
}
}
}

View file

@ -3,39 +3,33 @@ package info.nightscout.androidaps.activities.fragments
import android.os.Bundle
import android.util.SparseArray
import android.view.*
import android.view.ActionMode
import androidx.appcompat.widget.Toolbar
import androidx.core.util.forEach
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import dagger.android.support.DaggerFragment
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.entities.ValueWithUnit
import info.nightscout.androidaps.database.entities.TherapyEvent
import info.nightscout.androidaps.database.entities.UserEntry.Action
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.InvalidateTherapyEventTransaction
import info.nightscout.androidaps.databinding.TreatmentsCareportalFragmentBinding
import info.nightscout.androidaps.databinding.TreatmentsCareportalItemBinding
import info.nightscout.androidaps.events.EventTherapyEventChange
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.extensions.toVisibility
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.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.*
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
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.rx.AapsSchedulers
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.core.Completable
import io.reactivex.rxjava3.disposables.CompositeDisposable
@ -59,26 +53,28 @@ class TreatmentsCareportalFragment : DaggerFragment() {
@Inject lateinit var uel: UserEntryLogger
private var _binding: TreatmentsCareportalFragmentBinding? = null
// This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!!
private var menu: Menu? = null
private val disposable = CompositeDisposable()
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 toolbar: Toolbar? = null
private var removeActionMode: ActionMode? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
TreatmentsCareportalFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
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)
binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
binding.recyclerview.emptyView = binding.noRecordsText
binding.recyclerview.loadingView = binding.progressBar
}
private fun refreshFromNightscout() {
@ -111,6 +107,7 @@ class TreatmentsCareportalFragment : DaggerFragment() {
fun swapAdapter() {
val now = System.currentTimeMillis()
binding.recyclerview.isLoading = true
disposable +=
if (showInvalidated)
repository
@ -133,23 +130,18 @@ class TreatmentsCareportalFragment : DaggerFragment() {
.observeOn(aapsSchedulers.main)
.debounce(1L, TimeUnit.SECONDS)
.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
override fun onPause() {
super.onPause()
actionHelper.finish()
disposable.clear()
}
@Synchronized
override fun onDestroyView() {
super.onDestroyView()
removeActionMode?.let { it.finish() }
binding.recyclerview.adapter = null // avoid leaks
_binding = null
}
@ -165,27 +157,24 @@ class TreatmentsCareportalFragment : DaggerFragment() {
val therapyEvent = therapyList[position]
holder.binding.ns.visibility = (therapyEvent.interfaceIDs.nightscoutId != null).toVisibility()
holder.binding.invalid.visibility = therapyEvent.isValid.not().toVisibility()
val sameDayPrevious = position > 0 && dateUtil.isSameDay(therapyEvent.timestamp, therapyList[position - 1].timestamp)
holder.binding.date.visibility = sameDayPrevious.not().toVisibility()
holder.binding.date.text = dateUtil.dateString(therapyEvent.timestamp)
val newDay = position == 0 || !dateUtil.isSameDayGroup(therapyEvent.timestamp, therapyList[position - 1].timestamp)
holder.binding.date.visibility = newDay.toVisibility()
holder.binding.date.text = if (newDay) dateUtil.dateStringRelative(therapyEvent.timestamp, rh) else ""
holder.binding.time.text = dateUtil.timeString(therapyEvent.timestamp)
holder.binding.duration.text = if (therapyEvent.duration == 0L) "" else dateUtil.niceTimeScalar(therapyEvent.duration, rh)
holder.binding.note.text = therapyEvent.note
holder.binding.type.text = translator.translate(therapyEvent.type)
holder.binding.cbRemove.visibility = (therapyEvent.isValid && removeActionMode != null).toVisibility()
if (removeActionMode != null) {
holder.binding.cbRemove.setOnCheckedChangeListener { _, 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.cbRemove.visibility = (therapyEvent.isValid && actionHelper.isRemoving).toVisibility()
holder.binding.cbRemove.setOnCheckedChangeListener { _, value ->
actionHelper.updateSelection(position, therapyEvent, value)
}
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
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
@ -198,35 +187,41 @@ class TreatmentsCareportalFragment : DaggerFragment() {
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
this.menu = menu
inflater.inflate(R.menu.menu_treatments_careportal, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onPrepareOptionsMenu(menu: Menu) {
menu.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated
menu.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated
updateMenuVisibility()
val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_therapy_events, false) || !buildHelper.isEngineeringMode()
menu.findItem(R.id.nav_refresh_ns)?.isVisible = !nsUploadOnly
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 =
when (item.itemId) {
R.id.nav_remove_items -> {
removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback())
true
}
R.id.nav_remove_items -> actionHelper.startRemove()
R.id.nav_show_invalidated -> {
showInvalidated = true
rxBus.send(EventTreatmentUpdateGui())
updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records))
swapAdapter()
true
}
R.id.nav_hide_invalidated -> {
showInvalidated = false
rxBus.send(EventTreatmentUpdateGui())
updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.hide_invalidated_records))
swapAdapter()
true
}
@ -243,36 +238,7 @@ class TreatmentsCareportalFragment : DaggerFragment() {
else -> false
}
inner class RemoveActionModeCallback : ActionMode.Callback {
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 {
private fun getConfirmationText(selectedItems: SparseArray<TherapyEvent>): String {
if (selectedItems.size() == 1) {
val therapyEvent = selectedItems.valueAt(0)
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())
}
private fun removeSelected() {
if (selectedItems.size() > 0)
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable {
selectedItems.forEach { _, therapyEvent ->
uel.log(
Action.CAREPORTAL_REMOVED, Sources.Treatments, therapyEvent.note,
ValueWithUnit.Timestamp(therapyEvent.timestamp),
ValueWithUnit.TherapyEventType(therapyEvent.type)
private fun removeSelected(selectedItems: SparseArray<TherapyEvent>) {
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(selectedItems), Runnable {
selectedItems.forEach { _, therapyEvent ->
uel.log(
Action.CAREPORTAL_REMOVED, Sources.Treatments, therapyEvent.note,
ValueWithUnit.Timestamp(therapyEvent.timestamp),
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(
{ 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()
}
actionHelper.finish()
})
}
}
}

View file

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

View file

@ -1,11 +1,10 @@
package info.nightscout.androidaps.activities.fragments
import android.annotation.SuppressLint
import android.graphics.Paint
import android.os.Bundle
import android.util.SparseArray
import android.view.*
import android.view.ActionMode
import androidx.appcompat.widget.Toolbar
import androidx.core.util.forEach
import androidx.recyclerview.widget.LinearLayoutManager
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.events.EventEffectiveProfileSwitchChanged
import info.nightscout.androidaps.events.EventProfileSwitchChanged
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.extensions.getCustomizedName
import info.nightscout.androidaps.extensions.toVisibility
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.profile.local.LocalProfilePlugin
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.FabricPrivacy
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.resources.ResourceHelper
@ -63,26 +63,29 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() {
@Inject lateinit var uel: UserEntryLogger
private var _binding: TreatmentsProfileswitchFragmentBinding? = null
// This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!!
private var menu: Menu? = null
private lateinit var actionHelper: ActionModeHelper<ProfileSealed>
private val disposable = CompositeDisposable()
private val millsToThePast = T.days(30).msecs()
private var selectedItems: SparseArray<ProfileSealed> = SparseArray()
private var showInvalidated = false
private var removeActionMode: ActionMode? = null
private var toolbar: Toolbar? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
TreatmentsProfileswitchFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root
@SuppressLint("NotifyDataSetChanged")
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)
toolbar = activity?.findViewById(R.id.toolbar)
binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
binding.recyclerview.emptyView = binding.noRecordsText
binding.recyclerview.loadingView = binding.progressBar
}
private fun refreshFromNightscout() {
@ -127,7 +130,7 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() {
fun swapAdapter() {
val now = System.currentTimeMillis()
binding.recyclerview.isLoading = true
disposable +=
if (showInvalidated)
profileSwitchWithInvalid(now)
@ -164,13 +167,13 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() {
@Synchronized
override fun onPause() {
super.onPause()
actionHelper.finish()
disposable.clear()
}
@Synchronized
override fun onDestroyView() {
super.onDestroyView()
removeActionMode?.finish()
binding.recyclerview.adapter = null // avoid leaks
_binding = null
}
@ -184,36 +187,35 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() {
val profileSwitch = profileSwitchList[position]
holder.binding.ph.visibility = (profileSwitch is ProfileSealed.EPS).toVisibility()
holder.binding.ns.visibility = (profileSwitch.interfaceIDs_backing?.nightscoutId != null).toVisibility()
val sameDayPrevious = position > 0 && dateUtil.isSameDay(profileSwitch.timestamp, profileSwitchList[position - 1].timestamp)
holder.binding.date.visibility = sameDayPrevious.not().toVisibility()
holder.binding.date.text = dateUtil.dateString(profileSwitch.timestamp)
val newDay = position == 0 || !dateUtil.isSameDayGroup(profileSwitch.timestamp, profileSwitchList[position - 1].timestamp)
holder.binding.date.visibility = newDay.toVisibility()
holder.binding.date.text = if (newDay) dateUtil.dateStringRelative(profileSwitch.timestamp, rh) else ""
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.name.text =
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)
holder.binding.clone.tag = profileSwitch
holder.binding.name.tag = profileSwitch
holder.binding.date.tag = profileSwitch
holder.binding.invalid.visibility = profileSwitch.isValid.not().toVisibility()
holder.binding.duration.visibility = (profileSwitch.duration != 0L && profileSwitch.duration != null).toVisibility()
holder.binding.cbRemove.visibility = (removeActionMode != null && profileSwitch is ProfileSealed.PS).toVisibility()
if (removeActionMode != null) {
holder.binding.cbRemove.visibility = (actionHelper.isRemoving && profileSwitch is ProfileSealed.PS).toVisibility()
if (actionHelper.isRemoving) {
holder.binding.cbRemove.setOnCheckedChangeListener { _, value ->
if (value) {
selectedItems.put(position, profileSwitch)
} else {
selectedItems.remove(position)
}
removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size())
actionHelper.updateSelection(position, profileSwitch, value)
}
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.spacer.visibility = (profileSwitch is ProfileSealed.PS).toVisibility()
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
@ -273,13 +275,18 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() {
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
this.menu = menu
inflater.inflate(R.menu.menu_treatments_profile_switch, menu)
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) {
menu.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated
menu.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated
updateMenuVisibility()
val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_profile_switch, false) || !buildHelper.isEngineeringMode()
menu.findItem(R.id.nav_refresh_ns)?.isVisible = !nsUploadOnly
@ -288,20 +295,21 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() {
override fun onOptionsItemSelected(item: MenuItem): Boolean =
when (item.itemId) {
R.id.nav_remove_items -> {
removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback())
true
}
R.id.nav_remove_items -> actionHelper.startRemove()
R.id.nav_show_invalidated -> {
showInvalidated = true
rxBus.send(EventTreatmentUpdateGui())
updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records))
swapAdapter()
true
}
R.id.nav_hide_invalidated -> {
showInvalidated = false
rxBus.send(EventTreatmentUpdateGui())
updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.hide_invalidated_records))
swapAdapter()
true
}
@ -313,36 +321,7 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() {
else -> false
}
inner class RemoveActionModeCallback : ActionMode.Callback {
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 {
private fun getConfirmationText(selectedItems: SparseArray<ProfileSealed>): String {
if (selectedItems.size() == 1) {
val profileSwitch = selectedItems.valueAt(0)
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())
}
private fun removeSelected() {
if (selectedItems.size() > 0)
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable {
selectedItems.forEach { _, profileSwitch ->
uel.log(
Action.PROFILE_SWITCH_REMOVED, Sources.Treatments, profileSwitch.profileName,
ValueWithUnit.Timestamp(profileSwitch.timestamp)
private fun removeSelected(selectedItems: SparseArray<ProfileSealed>) {
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(selectedItems), Runnable {
selectedItems.forEach { _, profileSwitch ->
uel.log(
Action.PROFILE_SWITCH_REMOVED, Sources.Treatments, profileSwitch.profileName,
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(
{ 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()
}
actionHelper.finish()
})
}
}
}

View file

@ -4,46 +4,41 @@ import android.annotation.SuppressLint
import android.os.Bundle
import android.util.SparseArray
import android.view.*
import androidx.appcompat.widget.Toolbar
import androidx.core.util.forEach
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import dagger.android.support.DaggerFragment
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.ValueWrapper
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.entities.TemporaryTarget
import info.nightscout.androidaps.database.entities.UserEntry.Action
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.transactions.InvalidateTemporaryTargetTransaction
import info.nightscout.androidaps.databinding.TreatmentsTemptargetFragmentBinding
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.EventProfileSwitchChanged
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.buildHelper.BuildHelper
import info.nightscout.androidaps.events.EventTempTargetChange
import info.nightscout.androidaps.extensions.friendlyDescription
import info.nightscout.androidaps.extensions.highValueToUnitsToString
import info.nightscout.androidaps.extensions.lowValueToUnitsToString
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.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.rx.AapsSchedulers
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.core.Completable
import io.reactivex.rxjava3.disposables.CompositeDisposable
@ -68,24 +63,28 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
@Inject lateinit var repository: AppRepository
private var _binding: TreatmentsTemptargetFragmentBinding? = null
// This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!!
private var menu: Menu? = null
private lateinit var actionHelper: ActionModeHelper<TemporaryTarget>
private val disposable = CompositeDisposable()
private val millsToThePast = T.days(30).msecs()
private var selectedItems: SparseArray<TemporaryTarget> = SparseArray()
private var showInvalidated = false
private var toolbar: Toolbar? = null
private var removeActionMode: ActionMode? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
TreatmentsTemptargetFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root
@SuppressLint("NotifyDataSetChanged")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
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)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
binding.recyclerview.emptyView = binding.noRecordsText
binding.recyclerview.loadingView = binding.progressBar
}
private fun refreshFromNightscout() {
@ -114,6 +113,7 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
fun swapAdapter() {
val now = System.currentTimeMillis()
binding.recyclerview.isLoading = true
disposable +=
if (showInvalidated)
repository
@ -131,30 +131,23 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
override fun onResume() {
super.onResume()
swapAdapter()
disposable += rxBus
.toObservable(EventTempTargetChange::class.java)
.observeOn(aapsSchedulers.io)
.debounce(1L, TimeUnit.SECONDS)
.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
override fun onPause() {
super.onPause()
actionHelper.finish()
disposable.clear()
}
@Synchronized
override fun onDestroyView() {
super.onDestroyView()
removeActionMode?.let { it.finish() }
binding.recyclerview.adapter = null // avoid leaks
_binding = null
}
@ -173,21 +166,20 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
val tempTarget = tempTargetList[position]
holder.binding.ns.visibility = (tempTarget.interfaceIDs.nightscoutId != null).toVisibility()
holder.binding.invalid.visibility = tempTarget.isValid.not().toVisibility()
holder.binding.cbRemove.visibility = (tempTarget.isValid && removeActionMode != null).toVisibility()
if (removeActionMode != null) {
holder.binding.cbRemove.visibility = (tempTarget.isValid && actionHelper.isRemoving).toVisibility()
if (actionHelper.isRemoving) {
holder.binding.cbRemove.setOnCheckedChangeListener { _, value ->
if (value) {
selectedItems.put(position, tempTarget)
} else {
selectedItems.remove(position)
}
removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size())
actionHelper.updateSelection(position, tempTarget, value)
}
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)
holder.binding.date.visibility = sameDayPrevious.not().toVisibility()
holder.binding.date.text = dateUtil.dateString(tempTarget.timestamp)
val newDay = position == 0 || !dateUtil.isSameDayGroup(tempTarget.timestamp, tempTargetList[position - 1].timestamp)
holder.binding.date.visibility = newDay.toVisibility()
holder.binding.date.text = if (newDay) dateUtil.dateStringRelative(tempTarget.timestamp, rh) else ""
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.low.text = tempTarget.lowValueToUnitsToString(units)
@ -195,13 +187,13 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
holder.binding.reason.text = translator.translate(tempTarget.reason)
holder.binding.time.setTextColor(
when {
tempTarget.id == currentlyActiveTarget?.id -> rh.gc(R.color.colorActive)
tempTarget.timestamp > dateUtil.now() -> rh.gc(R.color.colorScheduled)
tempTarget.id == currentlyActiveTarget?.id -> rh.gac(context , R.attr.activeColor)
tempTarget.timestamp > dateUtil.now() -> rh.gac(context , R.attr.scheduledColor)
else -> holder.binding.reasonColon.currentTextColor
}
)
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
@ -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) {
this.menu = menu
inflater.inflate(R.menu.menu_treatments_temp_target, menu)
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) {
menu.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated
menu.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated
updateMenuVisibility()
val nsUploadOnly = !sp.getBoolean(R.string.key_ns_receive_temp_target, false) || !buildHelper.isEngineeringMode()
menu.findItem(R.id.nav_refresh_ns)?.isVisible = !nsUploadOnly
@ -254,20 +226,21 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
override fun onOptionsItemSelected(item: MenuItem): Boolean =
when (item.itemId) {
R.id.nav_remove_items -> {
removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback())
true
}
R.id.nav_remove_items -> actionHelper.startRemove()
R.id.nav_show_invalidated -> {
showInvalidated = true
rxBus.send(EventTreatmentUpdateGui())
updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records))
swapAdapter()
true
}
R.id.nav_hide_invalidated -> {
showInvalidated = false
rxBus.send(EventTreatmentUpdateGui())
updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records))
swapAdapter()
true
}
@ -279,36 +252,7 @@ class TreatmentsTempTargetFragment : DaggerFragment() {
else -> false
}
inner class RemoveActionModeCallback : ActionMode.Callback {
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 {
private fun getConfirmationText(selectedItems: SparseArray<TemporaryTarget>): String {
if (selectedItems.size() == 1) {
val tempTarget = selectedItems.valueAt(0)
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())
}
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
import android.annotation.SuppressLint
import android.os.Bundle
import android.util.Log
import android.util.SparseArray
import android.view.*
import androidx.appcompat.widget.Toolbar
import androidx.core.util.forEach
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.fragments.TreatmentsTemporaryBasalsFragment.RecyclerViewAdapter.TempBasalsViewHolder
import info.nightscout.androidaps.data.IobTotal
import info.nightscout.androidaps.database.AppRepository
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.databinding.TreatmentsTempbasalsFragmentBinding
import info.nightscout.androidaps.databinding.TreatmentsTempbasalsItemBinding
import info.nightscout.androidaps.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.events.EventTempBasalChange
import info.nightscout.androidaps.extensions.iobCalc
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.interfaces.ActivePlugin
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.activities.fragments.TreatmentsTemporaryBasalsFragment.RecyclerViewAdapter.TempBasalsViewHolder
import info.nightscout.androidaps.events.EventTreatmentUpdateGui
import info.nightscout.androidaps.utils.ActionModeHelper
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.resources.ResourceHelper
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.kotlin.plusAssign
import java.util.concurrent.TimeUnit
@ -65,24 +64,28 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
@Inject lateinit var repository: AppRepository
private var _binding: TreatmentsTempbasalsFragmentBinding? = null
// This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!!
private var menu: Menu? = null
private lateinit var actionHelper: ActionModeHelper<TemporaryBasal>
private val millsToThePast = T.days(30).msecs()
private var selectedItems: SparseArray<TemporaryBasal> = SparseArray()
private var showInvalidated = false
private var toolbar: Toolbar? = null
private var removeActionMode: ActionMode? = null
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
TreatmentsTempbasalsFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root
@SuppressLint("NotifyDataSetChanged")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
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)
binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
binding.recyclerview.emptyView = binding.noRecordsText
binding.recyclerview.loadingView = binding.progressBar
}
private fun tempBasalsWithInvalid(now: Long) = repository
@ -101,6 +104,7 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
fun swapAdapter() {
val now = System.currentTimeMillis()
binding.recyclerview.isLoading = true
disposable +=
if (activePlugin.activePump.isFakingTempsByExtendedBoluses) {
if (showInvalidated)
@ -134,28 +138,22 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
override fun onResume() {
super.onResume()
swapAdapter()
disposable += rxBus
.toObservable(EventTempBasalChange::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
}
@Synchronized
override fun onPause() {
super.onPause()
actionHelper.finish()
disposable.clear()
}
@Synchronized
override fun onDestroyView() {
super.onDestroyView()
removeActionMode?.let { it.finish() }
binding.recyclerview.adapter = null // avoid leaks
_binding = null
}
@ -172,10 +170,12 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
holder.binding.ph.visibility = (tempBasal.interfaceIDs.pumpId != null).toVisibility()
val sameDayPrevious = position > 0 && dateUtil.isSameDay(tempBasal.timestamp, tempBasalList[position - 1].timestamp)
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) {
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 {
holder.binding.time.text = dateUtil.timeRangeString(tempBasal.timestamp, tempBasal.end)
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.emulatedSuspendFlag.visibility = (tempBasal.type == TemporaryBasal.Type.EMULATED_PUMP_SUSPEND).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)
holder.binding.cbRemove.visibility = (tempBasal.isValid && removeActionMode != null).toVisibility()
if (removeActionMode != null) {
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 && actionHelper.isRemoving).toVisibility()
if (actionHelper.isRemoving) {
holder.binding.cbRemove.setOnCheckedChangeListener { _, value ->
if (value) {
selectedItems.put(position, tempBasal)
} else {
selectedItems.remove(position)
}
removeActionMode?.title = rh.gs(R.string.count_selected, selectedItems.size())
actionHelper.updateSelection(position, tempBasal, value)
}
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
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
@ -220,69 +219,46 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
this.menu = menu
inflater.inflate(R.menu.menu_treatments_temp_basal, menu)
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) {
menu.findItem(R.id.nav_hide_invalidated)?.isVisible = showInvalidated
menu.findItem(R.id.nav_show_invalidated)?.isVisible = !showInvalidated
updateMenuVisibility()
return super.onPrepareOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean =
when (item.itemId) {
R.id.nav_remove_items -> {
removeActionMode = toolbar?.startActionMode(RemoveActionModeCallback())
true
}
R.id.nav_remove_items -> actionHelper.startRemove()
R.id.nav_show_invalidated -> {
showInvalidated = true
rxBus.send(EventTreatmentUpdateGui())
updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_invalidated_records))
swapAdapter()
true
}
R.id.nav_hide_invalidated -> {
showInvalidated = false
rxBus.send(EventTreatmentUpdateGui())
updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.hide_invalidated_records))
swapAdapter()
true
}
else -> false
}
inner class RemoveActionModeCallback : ActionMode.Callback {
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 {
private fun getConfirmationText(selectedItems: SparseArray<TemporaryBasal>): String {
if (selectedItems.size() == 1) {
val tempBasal = selectedItems.valueAt(0)
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())
}
private fun removeSelected() {
private fun removeSelected(selectedItems: SparseArray<TemporaryBasal>) {
if (selectedItems.size() > 0)
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(), Runnable {
selectedItems.forEach {_, tempBasal ->
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(selectedItems), Runnable {
selectedItems.forEach { _, tempBasal ->
var extendedBolus: ExtendedBolus? = null
val isFakeExtended = tempBasal.type == TemporaryBasal.Type.FAKE_EXTENDED
if (isFakeExtended) {
@ -330,10 +306,8 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
{ 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.TreatmentsUserEntryItemBinding
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.interfaces.ImportExportPrefs
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.UserEntryLogger
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.FabricPrivacy
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.ToastUtils
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.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.userEntry.UserEntryPresentationHelper
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
import java.util.concurrent.TimeUnit
import javax.inject.Inject
class TreatmentsUserEntryFragment : DaggerFragment() {
@ -47,9 +46,9 @@ class TreatmentsUserEntryFragment : DaggerFragment() {
@Inject lateinit var userEntryPresentationHelper: UserEntryPresentationHelper
private val disposable = CompositeDisposable()
private val millsToThePastFiltered = T.days(30).msecs()
private val millsToThePastUnFiltered = T.days(3).msecs()
private var menu: Menu? = null
private var showLoop = false
private var _binding: TreatmentsUserEntryFragmentBinding? = null
@ -64,9 +63,11 @@ class TreatmentsUserEntryFragment : DaggerFragment() {
setHasOptionsMenu(true)
binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
binding.recyclerview.emptyView = binding.noRecordsText
binding.recyclerview.loadingView = binding.progressBar
}
fun exportUserEnteries() {
private fun exportUserEntries() {
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.ue_export_to_csv) + "?") {
uel.log(Action.EXPORT_CSV, Sources.Treatments)
@ -77,6 +78,7 @@ class TreatmentsUserEntryFragment : DaggerFragment() {
fun swapAdapter() {
val now = System.currentTimeMillis()
binding.recyclerview.isLoading = true
disposable +=
if (showLoop)
repository
@ -94,16 +96,10 @@ class TreatmentsUserEntryFragment : DaggerFragment() {
override fun onResume() {
super.onResume()
swapAdapter()
disposable += rxBus
.toObservable(EventPreferenceChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventTreatmentUpdateGui::class.java)
.observeOn(aapsSchedulers.io)
.debounce(1L, TimeUnit.SECONDS)
.subscribe({ swapAdapter() }, fabricPrivacy::logException)
}
@Synchronized
@ -128,19 +124,18 @@ class TreatmentsUserEntryFragment : DaggerFragment() {
override fun onBindViewHolder(holder: UserEntryViewHolder, position: Int) {
val current = entries[position]
val sameDayPrevious = position > 0 && dateUtil.isSameDay(current.timestamp, entries[position - 1].timestamp)
holder.binding.date.visibility = sameDayPrevious.not().toVisibility()
holder.binding.date.text = dateUtil.dateString(current.timestamp)
val newDay = position == 0 || !dateUtil.isSameDayGroup(current.timestamp, entries[position - 1].timestamp)
holder.binding.date.visibility = newDay.toVisibility()
holder.binding.date.text = if (newDay) dateUtil.dateStringRelative(current.timestamp, rh) else ""
holder.binding.time.text = dateUtil.timeStringWithSeconds(current.timestamp)
holder.binding.action.text = userEntryPresentationHelper.actionToColoredString(current.action)
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.visibility = View.VISIBLE
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
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) {
@ -152,14 +147,18 @@ class TreatmentsUserEntryFragment : DaggerFragment() {
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
this.menu = menu
inflater.inflate(R.menu.menu_treatments_user_entry, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onPrepareOptionsMenu(menu: Menu) {
menu.findItem(R.id.nav_hide_loop)?.isVisible = showLoop
menu.findItem(R.id.nav_show_loop)?.isVisible = !showLoop
private fun updateMenuVisibility() {
menu?.findItem(R.id.nav_hide_loop)?.isVisible = showLoop
menu?.findItem(R.id.nav_show_loop)?.isVisible = !showLoop
}
override fun onPrepareOptionsMenu(menu: Menu) {
updateMenuVisibility()
return super.onPrepareOptionsMenu(menu)
}
@ -167,21 +166,26 @@ class TreatmentsUserEntryFragment : DaggerFragment() {
when (item.itemId) {
R.id.nav_show_loop -> {
showLoop = true
rxBus.send(EventTreatmentUpdateGui())
updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_loop_records))
swapAdapter()
true
}
R.id.nav_hide_loop -> {
showLoop = false
rxBus.send(EventTreatmentUpdateGui())
updateMenuVisibility()
ToastUtils.showToastInUiThread(context, rh.gs(R.string.show_hide_records))
swapAdapter()
true
}
R.id.nav_export -> {
exportUserEnteries()
exportUserEntries()
true
}
else -> false
}
}

View file

@ -60,6 +60,11 @@ class CompatDBHelper @Inject constructor(
rxBus.send(EventExtendedBolusChange())
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 ->
aapsLogger.debug(LTag.DATABASE, "Firing EventTempTargetChange $tt")
rxBus.send(EventTempTargetChange())
@ -76,10 +81,6 @@ class CompatDBHelper @Inject constructor(
aapsLogger.debug(LTag.DATABASE, "Firing EventProfileSwitchChanged $ps")
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 ->
aapsLogger.debug(LTag.DATABASE, "Firing EventOfflineChange $oe")
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.DetermineBasalResultSMB
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
@Suppress("unused")
@ -21,6 +19,4 @@ abstract class APSModule {
@ContributesAndroidInjector abstract fun determineBasalAdapterAMAJSInjector(): DetermineBasalAdapterAMAJS
@ContributesAndroidInjector abstract fun determineBasalAdapterSMBJSInjector(): DetermineBasalAdapterSMBJS
@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,
OmnipodErosModule::class,
APSModule::class,
WorkflowModule::class,
PreferencesModule::class,
OverviewModule::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.persistentNotification.PersistentNotificationPlugin
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.xdripStatusline.StatusLinePlugin
import info.nightscout.androidaps.plugins.insulin.InsulinLyumjevPlugin
@ -393,6 +394,12 @@ abstract class PluginsModule {
@IntKey(490)
abstract fun bindConfigBuilderPlugin(plugin: ConfigBuilderPlugin): PluginBase
@Binds
@AllConfigs
@IntoMap
@IntKey(500)
abstract fun bindThemeSwitcherPlugin(plugin: ThemeSwitcherPlugin): PluginBase
@Qualifier
annotation class AllConfigs

View file

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

View file

@ -2,10 +2,16 @@ package info.nightscout.androidaps.di
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.widget.WidgetConfigureActivity
import info.nightscout.androidaps.skins.SkinListPreference
import info.nightscout.androidaps.widget.Widget
@Module
@Suppress("unused")
abstract class UIModule {
@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")
?: 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.bg.editText?.id?.let { binding.bgLabel.labelFor = it }
binding.bgLabel.labelFor = binding.bg.editTextId
}
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.utils.*
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 io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
@ -50,6 +52,7 @@ class CarbsDialog : DialogFragmentWithDate() {
@Inject lateinit var bolusTimer: BolusTimer
@Inject lateinit var commandQueue: CommandQueue
@Inject lateinit var repository: AppRepository
@Inject lateinit var protectionCheck: ProtectionCheck
companion object {
@ -58,6 +61,7 @@ class CarbsDialog : DialogFragmentWithDate() {
private const val FAV3_DEFAULT = 20
}
private var queryingProtection = false
private val disposable = CompositeDisposable()
private val textWatcher: TextWatcher = object : TextWatcher {
@ -195,9 +199,9 @@ class CarbsDialog : DialogFragmentWithDate() {
binding.hypoTt.isChecked = false
binding.activityTt.isChecked = false
}
binding.duration.editText?.id?.let { binding.durationLabel.labelFor = it }
binding.time.editText?.id?.let { binding.timeLabel.labelFor = it }
binding.carbs.editText?.id?.let { binding.carbsLabel.labelFor = it }
binding.durationLabel.labelFor = binding.duration.editTextId
binding.timeLabel.labelFor = binding.time.editTextId
binding.carbsLabel.labelFor = binding.carbs.editTextId
}
override fun onDestroyView() {
@ -230,8 +234,9 @@ class CarbsDialog : DialogFragmentWithDate() {
if (activitySelected)
actions.add(
rh.gs(R.string.temptargetshort) + ": " + (DecimalFormatter.to1Decimal(activityTT) + " " + unitLabel + " (" + rh.gs(R.string.format_mins, activityTTDuration) + ")").formatColor(
context,
rh,
R.color.tempTargetConfirmation
R.attr.tempTargetConfirmation
)
)
val eatingSoonSelected = binding.eatingSoonTt.isChecked
@ -240,27 +245,27 @@ class CarbsDialog : DialogFragmentWithDate() {
rh.gs(R.string.temptargetshort) + ": " + (DecimalFormatter.to1Decimal(eatingSoonTT) + " " + unitLabel + " (" + rh.gs(
R.string.format_mins,
eatingSoonTTDuration
) + ")").formatColor(rh, R.color.tempTargetConfirmation)
) + ")").formatColor(context, rh, R.attr.tempTargetConfirmation)
)
val hypoSelected = binding.hypoTt.isChecked
if (hypoSelected)
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,
R.color.tempTargetConfirmation
R.attr.tempTargetConfirmation
)
)
val timeOffset = binding.time.value.toInt()
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()
if (duration > 0)
actions.add(rh.gs(R.string.duration) + ": " + duration + rh.gs(R.string.shorthour))
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)
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()
if (notes.isNotEmpty())
@ -377,4 +382,20 @@ class CarbsDialog : DialogFragmentWithDate() {
}
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)
if (options == EventType.NOTE || options == EventType.QUESTION || options == EventType.ANNOUNCEMENT || options == EventType.EXERCISE)
binding.notesLayout.root.visibility = View.VISIBLE // independent to preferences
binding.bg.editText?.id?.let { binding.bgLabel.labelFor = it }
binding.duration.editText?.id?.let { binding.durationLabel.labelFor = it }
binding.bgLabel.labelFor = binding.bg.editTextId
binding.durationLabel.labelFor = binding.duration.editTextId
}
override fun onDestroyView() {

View file

@ -22,7 +22,11 @@ import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.shared.SafeParse
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
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.shared.logging.LTag
import java.text.DecimalFormat
import java.util.*
import javax.inject.Inject
@ -36,11 +40,12 @@ class ExtendedBolusDialog : DialogFragmentWithDate() {
@Inject lateinit var commandQueue: CommandQueue
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var protectionCheck: ProtectionCheck
private var queryingProtection = false
private var _binding: DialogExtendedbolusBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
// This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!!
override fun onSaveInstanceState(savedInstanceState: Bundle) {
@ -70,8 +75,8 @@ class ExtendedBolusDialog : DialogFragmentWithDate() {
val extendedMaxDuration = pumpDescription.extendedBolusMaxDuration
binding.duration.setParams(savedInstanceState?.getDouble("duration")
?: extendedDurationStep, extendedDurationStep, extendedMaxDuration, extendedDurationStep, DecimalFormat("0"), false, binding.okcancel.ok)
binding.insulin.editText?.id?.let { binding.insulinLabel.labelFor = it }
binding.duration.editText?.id?.let { binding.durationLabel.labelFor = it }
binding.insulinLabel.labelFor = binding.insulin.editTextId
binding.durationLabel.labelFor = binding.duration.editTextId
}
override fun onDestroyView() {
@ -88,7 +93,7 @@ class ExtendedBolusDialog : DialogFragmentWithDate() {
actions.add(rh.gs(R.string.formatinsulinunits, insulinAfterConstraint))
actions.add(rh.gs(R.string.duration) + ": " + rh.gs(R.string.format_mins, durationInMinutes))
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 ->
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
}
}
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.androidaps.utils.alertDialogs.OKDialog
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 io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
@ -44,13 +47,13 @@ class FillDialog : DialogFragmentWithDate() {
@Inject lateinit var activePlugin: ActivePlugin
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var repository: AppRepository
@Inject lateinit var protectionCheck: ProtectionCheck
private var queryingProtection = false
private val disposable = CompositeDisposable()
private var _binding: DialogFillBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
// This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!!
override fun onSaveInstanceState(savedInstanceState: Bundle) {
@ -96,7 +99,7 @@ class FillDialog : DialogFragmentWithDate() {
} else {
binding.fillPresetButton3.visibility = View.GONE
}
binding.fillInsulinamount.editText?.id?.let { binding.fillLabel.labelFor = it }
binding.fillLabel.labelFor = binding.fillInsulinamount.editTextId
}
override fun onDestroyView() {
@ -113,16 +116,16 @@ class FillDialog : DialogFragmentWithDate() {
if (insulinAfterConstraints > 0) {
actions.add(rh.gs(R.string.fillwarning))
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)
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
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
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()
if (notes.isNotEmpty())
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.alertDialogs.OKDialog
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.shared.SafeParse
import io.reactivex.rxjava3.disposables.CompositeDisposable
@ -52,6 +54,7 @@ class InsulinDialog : DialogFragmentWithDate() {
@Inject lateinit var config: Config
@Inject lateinit var bolusTimer: BolusTimer
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var protectionCheck: ProtectionCheck
companion object {
@ -60,7 +63,12 @@ class InsulinDialog : DialogFragmentWithDate() {
private const val PLUS3_DEFAULT = 2.0
}
private var queryingProtection = false
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 {
override fun afterTextChanged(s: Editable) {
@ -71,12 +79,6 @@ class InsulinDialog : DialogFragmentWithDate() {
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() {
val maxInsulin = constraintChecker.getMaxBolusAllowed().value()
if (abs(binding.time.value.toInt()) > 12 * 60) {
@ -148,8 +150,8 @@ class InsulinDialog : DialogFragmentWithDate() {
binding.recordOnly.setOnCheckedChangeListener { _, isChecked: Boolean ->
binding.timeLayout.visibility = isChecked.toVisibility()
}
binding.amount.editText?.id?.let { binding.insulinLabel.labelFor = it }
binding.time.editText?.id?.let { binding.timeLabel.labelFor = it }
binding.insulinLabel.labelFor = binding.amount.editTextId
binding.timeLabel.labelFor = binding.time.editTextId
}
override fun onDestroyView() {
@ -170,16 +172,17 @@ class InsulinDialog : DialogFragmentWithDate() {
val eatingSoonChecked = binding.startEatingSoonTt.isChecked
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)
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))
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 eatingSoonTT = defaultValueHelper.determineEatingSoonTT()
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 time = dateUtil.now() + T.mins(timeOffset.toLong()).msecs()
@ -255,4 +258,20 @@ class InsulinDialog : DialogFragmentWithDate() {
}
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.ToastUtils
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.shared.sharedPreferences.SP
import io.reactivex.rxjava3.disposables.CompositeDisposable
@ -62,14 +64,15 @@ class LoopDialog : DaggerDialogFragment() {
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var repository: AppRepository
@Inject lateinit var objectivePlugin: ObjectivesPlugin
@Inject lateinit var protectionCheck: ProtectionCheck
private var queryingProtection = false
private var showOkCancel: Boolean = true
private var _binding: DialogLoopBinding? = null
private var handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper)
private lateinit var refreshDialog: Runnable
// This property is only valid between onCreateView and
// onDestroyView.
// This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!!
val disposable = CompositeDisposable()
@ -437,4 +440,20 @@ class LoopDialog : DaggerDialogFragment() {
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
import android.content.Context
import android.os.Bundle
import android.text.Editable
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.HtmlHelper
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.ToastUtils
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 io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
@ -51,15 +55,15 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
@Inject lateinit var hardLimits: HardLimits
@Inject lateinit var rxBus: RxBus
@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 var _binding: DialogProfileswitchBinding? = 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 textWatcher: TextWatcher = object : TextWatcher {
@ -86,7 +90,7 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
): View {
onCreateViewGeneral()
arguments?.let { bundle ->
profileIndex = bundle.getInt("profileIndex", 0)
profileName = bundle.getString("profileName", null)
}
_binding = DialogProfileswitchBinding.inflate(inflater, container, false)
return binding.root
@ -112,8 +116,7 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
// profile
context?.let { context ->
val profileStore = activePlugin.activeProfileSource.profile
?: return
val profileStore = activePlugin.activeProfileSource.profile ?: return
val profileListToCheck = profileStore.getProfileList()
val profileList = ArrayList<CharSequence>()
for (profileName in profileListToCheck) {
@ -125,15 +128,16 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
dismiss()
return
}
val adapter = ArrayAdapter(context, R.layout.spinner_centered, profileList)
binding.profile.adapter = adapter
binding.profileList.setAdapter(ArrayAdapter(context, R.layout.spinner_centered, profileList))
// set selected to actual profile
if (profileIndex != null)
binding.profile.setSelection(profileIndex as Int)
else
if (profileName != null)
binding.profileList.setText(profileName, false)
else {
binding.profileList.setText(profileList[0], false)
for (p in profileList.indices)
if (profileList[p] == profileFunction.getOriginalProfileName())
binding.profile.setSelection(p)
binding.profileList.setText(profileList[p], false)
}
}
profileFunction.getProfile()?.let { profile ->
@ -148,9 +152,9 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
}
}
binding.ttLayout.visibility = View.GONE
binding.duration.editText?.id?.let { binding.durationLabel.labelFor = it }
binding.percentage.editText?.id?.let { binding.percentageLabel.labelFor = it }
binding.timeshift.editText?.id?.let { binding.timeshiftLabel.labelFor = it }
binding.durationLabel.labelFor = binding.duration.editTextId
binding.percentageLabel.labelFor = binding.percentage.editTextId
binding.timeshiftLabel.labelFor = binding.timeshift.editTextId
}
override fun onDestroyView() {
@ -168,7 +172,7 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
val duration = binding.duration.value.toInt()
if (duration > 0L)
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)
val percent = binding.percentage.value.toInt()
if (percent != 100)
@ -245,4 +249,20 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
}
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.androidaps.utils.alertDialogs.OKDialog
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.shared.logging.LTag
import java.text.DecimalFormat
import java.util.*
import javax.inject.Inject
@ -35,13 +39,13 @@ class TempBasalDialog : DialogFragmentWithDate() {
@Inject lateinit var commandQueue: CommandQueue
@Inject lateinit var ctx: Context
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var protectionCheck: ProtectionCheck
private var queryingProtection = false
private var isPercentPump = true
private var _binding: DialogTempbasalBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
// This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!!
override fun onSaveInstanceState(savedInstanceState: Bundle) {
@ -51,8 +55,7 @@ class TempBasalDialog : DialogFragmentWithDate() {
savedInstanceState.putDouble("basalAbsoluteInput", binding.basalAbsoluteInput.value)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
onCreateViewGeneral()
_binding = DialogTempbasalBinding.inflate(inflater, container, false)
return binding.root
@ -86,9 +89,9 @@ class TempBasalDialog : DialogFragmentWithDate() {
binding.percentLayout.visibility = View.GONE
binding.absoluteLayout.visibility = View.VISIBLE
}
binding.basalPercentInput.editText?.id?.let { binding.basalPercentLabel.labelFor = it }
binding.basalAbsoluteInput.editText?.id?.let { binding.basalAbsoluteLabel.labelFor = it }
binding.duration.editText?.id?.let { binding.durationLabel.labelFor = it }
binding.basalPercentLabel.labelFor = binding.basalPercentInput.editTextId
binding.basalAbsoluteLabel.labelFor = binding.basalAbsoluteInput.editTextId
binding.durationLabel.labelFor = binding.duration.editTextId
}
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.duration) + ": " + rh.gs(R.string.format_mins, durationInMinutes))
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 ->
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
}
}
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
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
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.utils.DefaultValueHelper
import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.ToastUtils
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 io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
@ -43,15 +47,16 @@ class TempTargetDialog : DialogFragmentWithDate() {
@Inject lateinit var defaultValueHelper: DefaultValueHelper
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var repository: AppRepository
@Inject lateinit var ctx: Context
@Inject lateinit var protectionCheck: ProtectionCheck
private lateinit var reasonList: List<String>
private var queryingProtection = false
private val disposable = CompositeDisposable()
private var _binding: DialogTemptargetBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
// This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!!
override fun onSaveInstanceState(savedInstanceState: Bundle) {
@ -60,8 +65,7 @@ class TempTargetDialog : DialogFragmentWithDate() {
savedInstanceState.putDouble("tempTarget", binding.temptarget.value)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
onCreateViewGeneral()
_binding = DialogTemptargetBinding.inflate(inflater, container, false)
return binding.root
@ -100,10 +104,9 @@ class TempTargetDialog : DialogFragmentWithDate() {
rh.gs(R.string.activity),
rh.gs(R.string.hypo)
)
val adapterReason = ArrayAdapter(context, R.layout.spinner_centered, reasonList)
binding.reason.adapter = adapterReason
binding.reasonList.setAdapter(ArrayAdapter(context, R.layout.spinner_centered, reasonList))
binding.targetCancel.setOnClickListener { shortClick(it) }
binding.targetCancel.setOnClickListener { binding.duration.value = 0.0; shortClick(it) }
binding.eatingSoon.setOnClickListener { shortClick(it) }
binding.activity.setOnClickListener { shortClick(it) }
binding.hypo.setOnClickListener { shortClick(it) }
@ -120,8 +123,8 @@ class TempTargetDialog : DialogFragmentWithDate() {
longClick(it)
return@setOnLongClickListener true
}
binding.duration.editText?.id?.let { binding.durationLabel.labelFor = it }
binding.temptarget.editText?.id?.let { binding.temptargetLabel.labelFor = it }
binding.durationLabel.labelFor = binding.duration.editTextId
binding.temptargetLabel.labelFor = binding.temptarget.editTextId
}
}
@ -135,23 +138,19 @@ class TempTargetDialog : DialogFragmentWithDate() {
R.id.eating_soon -> {
binding.temptarget.value = defaultValueHelper.determineEatingSoonTT()
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 -> {
binding.temptarget.value = defaultValueHelper.determineActivityTT()
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 -> {
binding.temptarget.value = defaultValueHelper.determineHypoTT()
binding.duration.value = defaultValueHelper.determineHypoTTDuration().toDouble()
binding.reason.setSelection(reasonList.indexOf(rh.gs(R.string.hypo)))
}
R.id.cancel -> {
binding.duration.value = 0.0
binding.reasonList.setText(rh.gs(R.string.hypo), false)
}
}
}
@ -165,7 +164,7 @@ class TempTargetDialog : DialogFragmentWithDate() {
override fun submit(): Boolean {
if (_binding == null) return false
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 target = binding.temptarget.value
val duration = binding.duration.value.toInt()
@ -222,4 +221,20 @@ class TempTargetDialog : DialogFragmentWithDate() {
}
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.alertDialogs.OKDialog
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 io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
@ -48,8 +50,14 @@ class TreatmentDialog : DialogFragmentWithDate() {
@Inject lateinit var config: Config
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var repository: AppRepository
@Inject lateinit var protectionCheck: ProtectionCheck
private var queryingProtection = false
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 {
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) {
super.onSaveInstanceState(savedInstanceState)
savedInstanceState.putDouble("carbs", binding.carbs.value)
@ -106,8 +108,8 @@ class TreatmentDialog : DialogFragmentWithDate() {
binding.insulin.setParams(savedInstanceState?.getDouble("insulin")
?: 0.0, 0.0, maxInsulin, pumpDescription.bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, binding.okcancel.ok, textWatcher)
binding.recordOnlyLayout.visibility = View.GONE
binding.insulin.editText?.id?.let { binding.insulinLabel.labelFor = it }
binding.carbs.editText?.id?.let { binding.carbsLabel.labelFor = it }
binding.insulinLabel.labelFor = binding.insulin.editTextId
binding.carbsLabel.labelFor = binding.carbs.editTextId
}
override fun onDestroyView() {
@ -126,16 +128,16 @@ class TreatmentDialog : DialogFragmentWithDate() {
val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value()
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)
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))
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) {
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)
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) {
activity?.let { activity ->
@ -201,4 +203,20 @@ class TreatmentDialog : DialogFragmentWithDate() {
}
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
import android.annotation.SuppressLint
import android.content.Context
import android.os.Bundle
import android.text.Editable
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.OnItemSelectedListener
import android.widget.ArrayAdapter
import android.widget.CompoundButton
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.events.EventAutosensCalculationFinished
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.valueToUnits
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.protection.ProtectionCheck
import info.nightscout.androidaps.utils.protection.ProtectionCheck.Protection.BOLUS
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.sharedPreferences.SP
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.kotlin.plusAssign
import java.text.DecimalFormat
import java.util.*
import javax.inject.Inject
import kotlin.math.abs
@ -55,16 +61,22 @@ class WizardDialog : DaggerDialogFragment() {
@Inject lateinit var iobCobCalculator: IobCobCalculator
@Inject lateinit var repository: AppRepository
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var protectionCheck: ProtectionCheck
private var queryingProtection = false
private var wizard: BolusWizard? = null
private var calculatedPercentage = 100.0
private var calculatedCorrection = 0.0
private var correctionPercent = false
private var carbsPassedIntoWizard = 0.0
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
private var okClicked: Boolean = false
// This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!!
private val textWatcher = object : TextWatcher {
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() {
super.onStart()
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)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
this.arguments?.let { bundle ->
carbsPassedIntoWizard = bundle.getInt("carbs_input").toDouble()
carbsPassedIntoWizard = bundle.getDouble("carbs_input")
notesPassedIntoWizard = bundle.getString("notes_input").toString()
}
@ -128,7 +130,7 @@ class WizardDialog : DaggerDialogFragment() {
val useSuperBolus = sp.getBoolean(R.string.key_usesuperbolus, false)
binding.sbCheckbox.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 maxCorrection = constraintChecker.getMaxBolusAllowed().value()
@ -137,14 +139,18 @@ class WizardDialog : DaggerDialogFragment() {
if (profileFunction.getUnits() == GlucoseUnit.MGDL) {
binding.bgInput.setParams(
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 {
binding.bgInput.setParams(
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")
?: 0.0, 0.0, maxCarbs.toDouble(), 1.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher)
binding.carbsInput.setParams(
savedInstanceState?.getDouble("carbs_input")
?: 0.0, 0.0, maxCarbs.toDouble(), 1.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher
)
if (correctionPercent) {
calculatedPercentage = sp.getInt(R.string.key_boluswizard_percentage, 100).toDouble()
@ -154,11 +160,14 @@ class WizardDialog : DaggerDialogFragment() {
} else {
binding.correctionInput.setParams(
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.carbTimeInput.setParams(savedInstanceState?.getDouble("carb_time_input")
?: 0.0, -60.0, 60.0, 5.0, DecimalFormat("0"), false, binding.okcancel.ok, timeTextWatcher)
binding.carbTimeInput.setParams(
savedInstanceState?.getDouble("carb_time_input")
?: 0.0, -60.0, 60.0, 5.0, DecimalFormat("0"), false, binding.okcancel.ok, timeTextWatcher
)
initDialog()
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))
@ -209,7 +218,7 @@ class WizardDialog : DaggerDialogFragment() {
processEnabledIcons()
binding.correctionPercent.setOnCheckedChangeListener {_, isChecked ->
binding.correctionPercent.setOnCheckedChangeListener { _, isChecked ->
run {
sp.putBoolean(rh.gs(R.string.key_wizard_correction_percent), isChecked)
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)
}
}
// profile spinner
binding.profile.onItemSelectedListener = object : OnItemSelectedListener {
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
}
}
// profile
binding.profileList.onItemClickListener = AdapterView.OnItemClickListener { _, _, _, _ -> calculateInsulin() }
// bus
disposable.add(rxBus
disposable += rxBus
.toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({
activity?.runOnUiThread { calculateInsulin() }
}, fabricPrivacy::logException)
)
.subscribe({ calculateInsulin() }, fabricPrivacy::logException)
setA11yLabels()
}
private fun setA11yLabels() {
binding.bgInput.editText?.id?.let { binding.bgInputLabel.labelFor = it }
binding.carbsInput.editText?.id?.let { binding.carbsInputLabel.labelFor = it }
binding.correctionInput.editText?.id?.let { binding.correctionInputLabel.labelFor = it }
binding.carbTimeInput.editText?.id?.let { binding.carbTimeInputLabel.labelFor = it }
binding.bgInputLabel.labelFor = binding.bgInput.editTextId
binding.carbsInputLabel.labelFor = binding.carbsInput.editTextId
binding.correctionInputLabel.labelFor = binding.correctionInput.editTextId
binding.carbTimeInputLabel.labelFor = binding.carbTimeInput.editTextId
}
override fun onDestroyView() {
@ -307,6 +302,7 @@ class WizardDialog : DaggerDialogFragment() {
binding.trendCheckboxIcon.visibility = binding.calculationCheckbox.isChecked.not().toVisibility()
binding.iobCheckboxIcon.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() {
@ -318,7 +314,7 @@ class WizardDialog : DaggerDialogFragment() {
private fun loadCheckedStates() {
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)
correctionPercent = sp.getBoolean(R.string.key_wizard_correction_percent,false)
correctionPercent = sp.getBoolean(R.string.key_wizard_correction_percent, false)
binding.correctionPercent.isChecked = correctionPercent
}
@ -327,11 +323,11 @@ class WizardDialog : DaggerDialogFragment() {
else DecimalFormatter.to1Decimal(value * Constants.MGDL_TO_MMOLL)
private fun initDialog() {
if(carbsPassedIntoWizard != 0.0) {
if (carbsPassedIntoWizard != 0.0) {
binding.carbsInput.value = carbsPassedIntoWizard
}
if(notesPassedIntoWizard.isNotBlank()) {
binding.notes.setText(notesPassedIntoWizard)
if (notesPassedIntoWizard.isNotBlank()) {
binding.notesLayout.notes.setText(notesPassedIntoWizard)
}
val profile = profileFunction.getProfile()
val profileStore = activePlugin.activeProfileSource.profile
@ -345,9 +341,9 @@ class WizardDialog : DaggerDialogFragment() {
val profileList: ArrayList<CharSequence> = profileStore.getProfileList()
profileList.add(0, rh.gs(R.string.active))
context?.let { context ->
val adapter = ArrayAdapter(context, R.layout.spinner_centered, profileList)
binding.profile.adapter = adapter
} ?: return
binding.profileList.setAdapter(ArrayAdapter(context, R.layout.spinner_centered, profileList))
binding.profileList.setText(profileList[0], false)
}
val units = profileFunction.getUnits()
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()
}
@SuppressLint("SetTextI18n")
private fun calculateInsulin() {
val profileStore = activePlugin.activeProfileSource.profile
if (binding.profile.selectedItem == null || profileStore == null)
return // not initialized yet
var profileName = binding.profile.selectedItem.toString()
val profileStore = activePlugin.activeProfileSource.profile ?: return // not initialized yet
var profileName = binding.profileList.text.toString()
val specificProfile: Profile?
if (profileName == rh.gs(R.string.active)) {
specificProfile = profileFunction.getProfile()
@ -396,7 +391,7 @@ class WizardDialog : DaggerDialogFragment() {
} else
0.0
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
else
SafeParse.stringToDouble(binding.correctionInput.text)
@ -422,7 +417,8 @@ class WizardDialog : DaggerDialogFragment() {
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.cobCheckbox.isChecked,
binding.iobCheckbox.isChecked,
@ -431,17 +427,17 @@ class WizardDialog : DaggerDialogFragment() {
binding.ttCheckbox.isChecked,
binding.bgTrendCheckbox.isChecked,
binding.alarm.isChecked,
binding.notes.text.toString(),
binding.notesLayout.notes.text.toString(),
carbTime,
usePercentage = usePercentage,
totalPercentage = percentageCorrection
)
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.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.iobInsulin.text = rh.gs(R.string.formatinsulinunits, wizard.insulinFromBolusIOB + wizard.insulinFromBasalIOB)
@ -464,7 +460,7 @@ class WizardDialog : DaggerDialogFragment() {
// COB
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)
} else {
binding.cob.text = ""
@ -472,12 +468,12 @@ class WizardDialog : DaggerDialogFragment() {
}
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 carbsText = if (carbsAfterConstraint > 0.0) rh.gs(R.string.format_carbs, carbsAfterConstraint).formatColor(rh, R.color.carbs) 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(context, rh, R.attr.carbsColor) else ""
binding.total.text = HtmlHelper.fromHtml(rh.gs(R.string.result_insulin_carbs, insulinText, carbsText))
binding.okcancel.ok.visibility = View.VISIBLE
} 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.percentUsed.text = rh.gs(R.string.format_percent, wizard.percentageCorrection)
@ -497,4 +493,20 @@ class WizardDialog : DaggerDialogFragment() {
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 androidx.core.app.NotificationCompat
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.annotations.OpenForTesting
import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.data.PumpEnactResult
import info.nightscout.androidaps.database.AppRepository
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.InsertTherapyEventAnnouncementTransaction
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.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.Loop.LastRun
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopSetLastRunGui
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.HardLimits
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.rx.AapsSchedulers
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.kotlin.plusAssign
@ -88,20 +87,21 @@ class LoopPlugin @Inject constructor(
private val uel: UserEntryLogger,
private val repository: AppRepository,
private val runningConfiguration: RunningConfiguration
) : PluginBase(PluginDescription()
.mainType(PluginType.LOOP)
.fragmentClass(LoopFragment::class.java.name)
.pluginIcon(R.drawable.ic_loop_closed_white)
.pluginName(R.string.loop)
.shortName(R.string.loop_shortname)
.preferencesId(R.xml.pref_loop)
.enableByDefault(config.APS)
.description(R.string.description_loop),
) : PluginBase(
PluginDescription()
.mainType(PluginType.LOOP)
.fragmentClass(LoopFragment::class.java.name)
.pluginIcon(R.drawable.ic_loop_closed_white)
.pluginName(R.string.loop)
.shortName(R.string.loop_shortname)
.preferencesId(R.xml.pref_loop)
.enableByDefault(config.APS)
.description(R.string.description_loop),
aapsLogger, rh, injector
), Loop {
private val disposable = CompositeDisposable()
private var lastBgTriggeredRun: Long = 0
override var lastBgTriggeredRun: Long = 0
private var carbsSuggestionsSuspendedUntil: Long = 0
private var prevCarbsreq = 0
override var lastRun: LastRun? = null
@ -109,39 +109,19 @@ class LoopPlugin @Inject constructor(
override fun onStart() {
createNotificationChannel()
super.onStart()
disposable.add(rxBus
disposable += rxBus
.toObservable(EventTempTargetChange::class.java)
.observeOn(aapsSchedulers.io)
.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() {
val mNotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
@SuppressLint("WrongConstant") val channel = NotificationChannel(CHANNEL_ID,
@SuppressLint("WrongConstant") val channel = NotificationChannel(
CHANNEL_ID,
NotificationManager.IMPORTANCE_HIGH)
CHANNEL_ID,
NotificationManager.IMPORTANCE_HIGH
)
mNotificationManager.createNotificationChannel(channel)
}
@ -255,7 +235,7 @@ class LoopPlugin @Inject constructor(
if (apsResult == null) {
rxBus.send(EventLoopSetLastRunGui(rh.gs(R.string.noapsselected)))
return
} else rxBus.send(EventLoopInvoked())
}
if (!isEmptyQueue()) {
aapsLogger.debug(LTag.APS, rh.gs(R.string.pumpbusy))
@ -296,9 +276,11 @@ class LoopPlugin @Inject constructor(
lastRun.lastTBRRequest = 0
lastRun.lastSMBEnact = 0
lastRun.lastSMBRequest = 0
buildDeviceStatus(dateUtil, this, iobCobCalculator, profileFunction,
buildDeviceStatus(
dateUtil, this, iobCobCalculator, profileFunction,
activePlugin.activePump, receiverStatusStore, runningConfiguration,
BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)?.also {
BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION
)?.also {
repository.insert(it)
}
@ -316,7 +298,11 @@ class LoopPlugin @Inject constructor(
if (closedLoopEnabled.value()) {
if (allowNotification) {
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)) {
val carbReqLocal = Notification(Notification.CARBS_REQUIRED, resultAfterConstraints.carbsRequiredText, Notification.NORMAL)
rxBus.send(EventNewNotification(carbReqLocal))
@ -353,9 +339,11 @@ class LoopPlugin @Inject constructor(
// mId allows you to update the notification later on.
mNotificationManager.notify(Constants.notificationID, builder.build())
uel.log(Action.CAREPORTAL, Sources.Loop, rh.gs(R.string.carbsreq, resultAfterConstraints.carbsReq, resultAfterConstraints.carbsReqWithin),
ValueWithUnit.Gram(resultAfterConstraints.carbsReq),
ValueWithUnit.Minute(resultAfterConstraints.carbsReqWithin))
uel.log(
Action.CAREPORTAL, Sources.Loop, rh.gs(R.string.carbsreq, resultAfterConstraints.carbsReq, resultAfterConstraints.carbsReqWithin),
ValueWithUnit.Gram(resultAfterConstraints.carbsReq),
ValueWithUnit.Minute(resultAfterConstraints.carbsReqWithin)
)
rxBus.send(EventNewOpenLoopNotification())
//only send to wear if Native notifications are turned off
@ -373,7 +361,8 @@ class LoopPlugin @Inject constructor(
}
}
if (resultAfterConstraints.isChangeRequested
&& !commandQueue.bolusInQueue()) {
&& !commandQueue.bolusInQueue()
) {
val waiting = PumpEnactResult(injector)
waiting.queued = true
if (resultAfterConstraints.tempBasalRequested) lastRun.tbrSetByPump = waiting
@ -486,9 +475,11 @@ class LoopPlugin @Inject constructor(
lastRun.lastTBRRequest = lastRun.lastAPSRun
lastRun.lastTBREnact = dateUtil.now()
lastRun.lastOpenModeAccept = dateUtil.now()
buildDeviceStatus(dateUtil, this@LoopPlugin, iobCobCalculator, profileFunction,
buildDeviceStatus(
dateUtil, this@LoopPlugin, iobCobCalculator, profileFunction,
activePlugin.activePump, receiverStatusStore, runningConfiguration,
BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION)?.also {
BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION
)?.also {
repository.insert(it)
}
sp.incInt(R.string.key_ObjectivesmanualEnacts)
@ -531,8 +522,10 @@ class LoopPlugin @Inject constructor(
commandQueue.cancelTempBasal(false, callback)
} else {
aapsLogger.debug(LTag.APS, "applyAPSRequest: Basal set correctly")
callback?.result(PumpEnactResult(injector).absolute(request.rate).duration(0)
.enacted(false).success(true).comment(R.string.basal_set_correctly))?.run()
callback?.result(
PumpEnactResult(injector).absolute(request.rate).duration(0)
.enacted(false).success(true).comment(R.string.basal_set_correctly)
)?.run()
}
} else if (request.usePercent && allowPercentage()) {
if (request.percent == 100 && request.duration == 0) {
@ -542,32 +535,52 @@ class LoopPlugin @Inject constructor(
commandQueue.cancelTempBasal(false, callback)
} else {
aapsLogger.debug(LTag.APS, "applyAPSRequest: Basal set correctly")
callback?.result(PumpEnactResult(injector).percent(request.percent).duration(0)
.enacted(false).success(true).comment(R.string.basal_set_correctly))?.run()
callback?.result(
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")
callback?.result(PumpEnactResult(injector).percent(request.percent)
.enacted(false).success(true).duration(activeTemp.plannedRemainingMinutes)
.comment(R.string.let_temp_basal_run))?.run()
callback?.result(
PumpEnactResult(injector).percent(request.percent)
.enacted(false).success(true).duration(activeTemp.plannedRemainingMinutes)
.comment(R.string.let_temp_basal_run)
)?.run()
} else {
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.Minute(request.duration))
ValueWithUnit.Minute(request.duration)
)
commandQueue.tempBasalPercent(request.percent, request.duration, false, profile, PumpSync.TemporaryBasalType.NORMAL, callback)
}
} 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")
callback?.result(PumpEnactResult(injector).absolute(activeTemp.convertedToAbsolute(now, profile))
.enacted(false).success(true).duration(activeTemp.plannedRemainingMinutes)
.comment(R.string.let_temp_basal_run))?.run()
callback?.result(
PumpEnactResult(injector).absolute(activeTemp.convertedToAbsolute(now, profile))
.enacted(false).success(true).duration(activeTemp.plannedRemainingMinutes)
.comment(R.string.let_temp_basal_run)
)?.run()
} else {
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.Minute(request.duration))
ValueWithUnit.Minute(request.duration)
)
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
if (lastBolusTime != 0L && lastBolusTime + 3 * 60 * 1000 > System.currentTimeMillis()) {
aapsLogger.debug(LTag.APS, "SMB requested but still in 3 min interval")
callback?.result(PumpEnactResult(injector)
.comment(R.string.smb_frequency_exceeded)
.enacted(false).success(false))?.run()
callback?.result(
PumpEnactResult(injector)
.comment(R.string.smb_frequency_exceeded)
.enacted(false).success(false)
)?.run()
return
}
if (!pump.isInitialized()) {
@ -619,11 +634,11 @@ class LoopPlugin @Inject constructor(
val pump = activePlugin.activePump
disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(durationInMinutes.toLong()).msecs(), reason))
.subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
if (pump.pumpDescription.tempBasalStyle == PumpDescription.ABSOLUTE) {
commandQueue.tempBasalAbsolute(0.0, durationInMinutes, true, profile, PumpSync.TemporaryBasalType.EMULATED_PUMP_SUSPEND, object : Callback() {
override fun run() {
@ -655,11 +670,11 @@ class LoopPlugin @Inject constructor(
override fun suspendLoop(durationInMinutes: Int) {
disposable += repository.runTransactionForResult(InsertAndCancelCurrentOfflineEventTransaction(dateUtil.now(), T.mins(durationInMinutes.toLong()).msecs(), OfflineEvent.Reason.SUSPEND))
.subscribe({ result ->
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated OfflineEvent $it") }
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted OfflineEvent $it") }
}, {
aapsLogger.error(LTag.DATABASE, "Error while saving OfflineEvent", it)
})
commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() {
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)
}
}
if (result.has("variable_sens")) variableSens = result.getDouble("variable_sens");
if (result.has("variable_sens")) variableSens = result.getDouble("variable_sens")
} catch (e: JSONException) {
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))
//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));
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("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)
@ -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("maxSMBBasalMinutes", sp.getInt(R.string.key_smbmaxminutes, SMBDefaults.maxSMBBasalMinutes))
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.
this.profile.put("bolus_increment", pumpBolusStep)
this.profile.put("carbsReqThreshold", sp.getInt(R.string.key_carbsReqThreshold, SMBDefaults.carbsReqThreshold))
this.profile.put("current_basal", basalRate)
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_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) {
this.profile.put("out_units", "mmol/L")
}

View file

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

View file

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

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.setTextColor(-0x3e3e3f)
holder.binding.accomplished.setTextColor(rh.gac(context,R.attr.defaultTextColor))
holder.binding.verify.setOnClickListener {
receiverStatusStore.updateNetworkStatus()
if (binding.fake.isChecked) {

View file

@ -109,29 +109,29 @@ class SafetyPlugin @Inject constructor(
}
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) {
var maxBasal = sp.getDouble(R.string.key_openapsma_max_basal, 1.0)
if (maxBasal < profile.getMaxDailyBasal()) {
maxBasal = profile.getMaxDailyBasal()
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
val maxBasalMultiplier = sp.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4.0)
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 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
// check for pump max
if (pump.pumpDescription.tempBasalStyle == PumpDescription.ABSOLUTE) {
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
@ -151,19 +151,19 @@ class SafetyPlugin @Inject constructor(
val pump = activePlugin.activePump
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()
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) {
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
}
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)
insulin.setIfSmaller(aapsLogger, maxBolus, String.format(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, maxBolus, rh.gs(R.string.limitingbolus, maxBolus, rh.gs(R.string.maxvalueinpreferences)), this)
insulin.setIfSmaller(aapsLogger, hardLimits.maxBolus(), rh.gs(R.string.limitingbolus, hardLimits.maxBolus(), rh.gs(R.string.hardlimit)), this)
val pump = activePlugin.activePump
val rounded = pump.pumpDescription.pumpType.determineCorrectBolusSize(insulin.value())
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> {
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)
insulin.setIfSmaller(aapsLogger, maxBolus, String.format(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, maxBolus, rh.gs(R.string.limitingextendedbolus, maxBolus, rh.gs(R.string.maxvalueinpreferences)), this)
insulin.setIfSmaller(aapsLogger, hardLimits.maxBolus(), rh.gs(R.string.limitingextendedbolus, hardLimits.maxBolus(), rh.gs(R.string.hardlimit)), this)
val pump = activePlugin.activePump
val rounded = pump.pumpDescription.pumpType.determineCorrectExtendedBolusSize(insulin.value())
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> {
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)
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
}
@ -192,11 +192,11 @@ class SafetyPlugin @Inject constructor(
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
.key_openapsma_max_iob, 1.5)
maxIob.setIfSmaller(aapsLogger, maxIobPref, String.format(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 (openAPSSMBPlugin.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(), String.format(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)
maxIob.setIfSmaller(aapsLogger, maxIobPref, rh.gs(R.string.limitingiob, maxIobPref, rh.gs(R.string.maxvalueinpreferences)), 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(), 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, rh.gs(R.string.limitingiob, HardLimits.MAX_IOB_LGS, rh.gs(R.string.lowglucosesuspend)), this)
return maxIob
}

View file

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

View file

@ -6,12 +6,11 @@ import android.content.pm.ResolveInfo
import android.os.Bundle
import dagger.android.HasAndroidInjector
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.toStringFull
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.bus.RxBus
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.resources.ResourceHelper
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 javax.inject.Inject
import javax.inject.Singleton
@ -68,26 +69,6 @@ class DataBroadcastPlugin @Inject constructor(
.observeOn(aapsSchedulers.io)
.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
.toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(aapsSchedulers.io)

View file

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

View file

@ -7,6 +7,7 @@ import android.view.View
import android.view.ViewGroup
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.SingleFragmentActivity
import info.nightscout.androidaps.dana.database.DanaHistoryDatabase
import info.nightscout.androidaps.database.AppRepository
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.diaconn.database.DiaconnHistoryDatabase
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.insight.database.InsightDatabase
import info.nightscout.androidaps.interfaces.DataSyncSelector
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.eros.history.database.ErosHistoryDatabase
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.rx.AapsSchedulers
import io.reactivex.rxjava3.core.Completable.fromAction
@ -48,6 +52,7 @@ class MaintenanceFragment : DaggerFragment() {
@Inject lateinit var diaconnDatabase: DiaconnHistoryDatabase
@Inject lateinit var erosDatabase: ErosHistoryDatabase
@Inject lateinit var dashDatabase: DashHistoryDatabase
@Inject lateinit var protectionCheck: ProtectionCheck
@Inject lateinit var uel: UserEntryLogger
@Inject lateinit var dataSyncSelector: DataSyncSelector
@Inject lateinit var pumpSync: PumpSync
@ -55,11 +60,11 @@ class MaintenanceFragment : DaggerFragment() {
@Inject lateinit var overviewData: OverviewData
private val compositeDisposable = CompositeDisposable()
private var inMenu = false
private var queryingProtection = false
private var _binding: MaintenanceFragmentBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
// This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
@ -69,6 +74,9 @@ class MaintenanceFragment : DaggerFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val parentClass = this.activity?.let { it::class.java }
inMenu = parentClass == SingleFragmentActivity::class.java
updateProtectedUi()
binding.logSend.setOnClickListener { maintenancePlugin.sendLogs() }
binding.logDelete.setOnClickListener {
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
@ -136,4 +151,21 @@ class MaintenanceFragment : DaggerFragment() {
compositeDisposable.clear()
_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)
}
//todo replace this with a call on startup of the application, specifically to remove
// unnecessary garbage from the log exports
fun deleteLogs(keep: Int) {
val logDir = File(loggerUtils.logDirectory)
val files = logDir.listFiles { _: File?, name: String ->

View file

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

View file

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

View file

@ -1,44 +1,42 @@
package info.nightscout.androidaps.plugins.general.overview
import android.graphics.DashPathEffect
import android.graphics.Paint
import android.content.Context
import androidx.annotation.ColorInt
import com.jjoe64.graphview.series.BarGraphSeries
import com.jjoe64.graphview.series.DataPoint
import com.jjoe64.graphview.series.LineGraphSeries
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.IobTotal
import info.nightscout.androidaps.database.AppRepository
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.TemporaryTarget
import info.nightscout.androidaps.database.entities.TherapyEvent
import info.nightscout.androidaps.extensions.*
import info.nightscout.androidaps.interfaces.*
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.general.nsclient.data.NSDeviceStatus
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.*
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult
import info.nightscout.androidaps.extensions.convertedToPercent
import info.nightscout.androidaps.extensions.isInProgress
import info.nightscout.androidaps.extensions.toStringFull
import info.nightscout.androidaps.extensions.toStringShort
import info.nightscout.androidaps.extensions.valueToUnits
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.IobCobCalculator
import info.nightscout.androidaps.interfaces.ProfileFunction
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.data.AutosensData
import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DefaultValueHelper
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.sharedPreferences.SP
import java.util.*
import javax.inject.Inject
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
class OverviewData @Inject constructor(
private val injector: HasAndroidInjector,
private val aapsLogger: AAPSLogger,
private val rh: ResourceHelper,
private val dateUtil: DateUtil,
@ -46,13 +44,7 @@ class OverviewData @Inject constructor(
private val activePlugin: ActivePlugin,
private val defaultValueHelper: DefaultValueHelper,
private val profileFunction: ProfileFunction,
private val config: Config,
private val loop: Loop,
private val nsDeviceStatus: NSDeviceStatus,
private val repository: AppRepository,
private val overviewMenus: OverviewMenus,
private val iobCobCalculator: IobCobCalculator,
private val translator: Translator
private val repository: AppRepository
) {
var rangeToDisplay = 6 // for graph
@ -62,14 +54,7 @@ class OverviewData @Inject constructor(
fun reset() {
pumpStatus = ""
calcProgress = ""
lastBg = null
bolusIob = null
basalIob = null
cobInfo = null
lastCarbsTime = 0L
temporaryTarget = null
lastAutosensData = null
calcProgressPct = 100
bgReadingsArray = ArrayList()
bucketedGraphSeries = PointsWithLabelGraphSeries()
bgReadingGraphSeries = PointsWithLabelGraphSeries()
@ -120,13 +105,18 @@ class OverviewData @Inject constructor(
* CALC PROGRESS
*/
var calcProgress: String = ""
var calcProgressPct: Int = 100
/*
* 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
get() = lastBg?.let { lastBg ->
@ -138,11 +128,12 @@ class OverviewData @Inject constructor(
lastBg.valueToUnits(profileFunction.getUnits()) > defaultValueHelper.determineHighLine()
} ?: false
val lastBgColor: Int
get() = when {
isLow -> rh.gc(R.color.low)
isHigh -> rh.gc(R.color.high)
else -> rh.gc(R.color.inrange)
@ColorInt
fun lastBgColor(context: Context?): Int =
when {
isLow -> rh.gac(context, R.attr.bgLow)
isHigh -> rh.gac(context, R.attr.highColor)
else -> rh.gac(context, R.attr.bgInRange)
}
val lastBgDescription: String
@ -162,17 +153,16 @@ class OverviewData @Inject constructor(
* TEMPORARY BASAL
*/
val temporaryBasalText: String
get() =
profileFunction.getProfile()?.let { profile ->
var temporaryBasal = iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())
if (temporaryBasal?.isInProgress == false) temporaryBasal = null
temporaryBasal?.let { "T:" + it.toStringShort() }
?: rh.gs(R.string.pump_basebasalrate, profile.getBasal())
} ?: rh.gs(R.string.notavailable)
fun temporaryBasalText(iobCobCalculator: IobCobCalculator): String =
profileFunction.getProfile()?.let { profile ->
var temporaryBasal = iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())
if (temporaryBasal?.isInProgress == false) temporaryBasal = null
temporaryBasal?.let { "T:" + it.toStringShort() }
?: rh.gs(R.string.pump_basebasalrate, profile.getBasal())
} ?: rh.gs(R.string.notavailable)
val temporaryBasalDialogText: String
get() = profileFunction.getProfile()?.let { profile ->
fun temporaryBasalDialogText(iobCobCalculator: IobCobCalculator): String =
profileFunction.getProfile()?.let { profile ->
iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())?.let { temporaryBasal ->
"${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)
@ -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.notavailable)
val temporaryBasalIcon: Int
get() =
profileFunction.getProfile()?.let { profile ->
iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())?.let { temporaryBasal ->
val percentRate = temporaryBasal.convertedToPercent(dateUtil.now(), profile)
when {
percentRate > 100 -> R.drawable.ic_cp_basal_tbr_high
percentRate < 100 -> R.drawable.ic_cp_basal_tbr_low
else -> R.drawable.ic_cp_basal_no_tbr
}
fun temporaryBasalIcon(iobCobCalculator: IobCobCalculator): Int =
profileFunction.getProfile()?.let { profile ->
iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())?.let { temporaryBasal ->
val percentRate = temporaryBasal.convertedToPercent(dateUtil.now(), profile)
when {
percentRate > 100 -> R.drawable.ic_cp_basal_tbr_high
percentRate < 100 -> R.drawable.ic_cp_basal_tbr_low
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
get() = iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())?.let { rh.gc(R.color.basal) }
?: rh.gc(R.color.defaulttextcolor)
fun temporaryBasalColor(context: Context?, iobCobCalculator: IobCobCalculator): Int = iobCobCalculator.getTempBasalIncludingConvertedExtended(dateUtil.now())?.let { rh.gac(context , R.attr.basal) }
?: rh.gac(context, R.attr.defaultTextColor)
/*
* EXTENDED BOLUS
*/
val extendedBolusText: String
get() =
iobCobCalculator.getExtendedBolus(dateUtil.now())?.let { extendedBolus ->
if (!extendedBolus.isInProgress(dateUtil)) ""
else if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) rh.gs(R.string.pump_basebasalrate, extendedBolus.rate)
else ""
} ?: ""
fun extendedBolusText(iobCobCalculator: IobCobCalculator): String =
iobCobCalculator.getExtendedBolus(dateUtil.now())?.let { extendedBolus ->
if (!extendedBolus.isInProgress(dateUtil)) ""
else if (!activePlugin.activePump.isFakingTempsByExtendedBoluses) rh.gs(R.string.pump_basebasalrate, extendedBolus.rate)
else ""
} ?: ""
val extendedBolusDialogText: String
get() = iobCobCalculator.getExtendedBolus(dateUtil.now())?.toStringFull(dateUtil) ?: ""
fun extendedBolusDialogText(iobCobCalculator: IobCobCalculator): String =
iobCobCalculator.getExtendedBolus(dateUtil.now())?.toStringFull(dateUtil) ?: ""
/*
* IOB, COB
*/
var bolusIob: IobTotal? = null
var basalIob: IobTotal? = null
var cobInfo: CobInfo? = null
var lastCarbsTime: Long = 0L
fun bolusIob(iobCobCalculator: IobCobCalculator): IobTotal = iobCobCalculator.calculateIobFromBolus().round()
fun basalIob(iobCobCalculator: IobCobCalculator): IobTotal = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round()
fun cobInfo(iobCobCalculator: IobCobCalculator): CobInfo = iobCobCalculator.getCobInfo(true, "Overview COB")
val iobText: String
get() =
bolusIob?.let { bolusIob ->
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 lastCarbsTime: Long
get() = repository.getLastCarbsRecordWrapped().blockingGet().let { lastCarbs ->
if (lastCarbs is ValueWrapper.Existing) lastCarbs.value.timestamp else 0L
}
val iobDialogText: String
get() =
bolusIob?.let { bolusIob ->
basalIob?.let { basalIob ->
rh.gs(R.string.formatinsulinunits, bolusIob.iob + basalIob.basaliob) + "\n" +
rh.gs(R.string.bolus) + ": " + rh.gs(R.string.formatinsulinunits, bolusIob.iob) + "\n" +
rh.gs(R.string.basal) + ": " + rh.gs(R.string.formatinsulinunits, basalIob.basaliob)
} ?: rh.gs(R.string.value_unavailable_short)
} ?: rh.gs(R.string.value_unavailable_short)
fun iobText(iobCobCalculator: IobCobCalculator): String =
rh.gs(R.string.formatinsulinunits, bolusIob(iobCobCalculator).iob + basalIob(iobCobCalculator).basaliob)
fun iobDialogText(iobCobCalculator: IobCobCalculator): String =
rh.gs(R.string.formatinsulinunits, bolusIob(iobCobCalculator).iob + basalIob(iobCobCalculator).basaliob) + "\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(iobCobCalculator).basaliob)
/*
* 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
*/
var lastAutosensData: AutosensData? = null
fun lastAutosensData(iobCobCalculator: IobCobCalculator) = iobCobCalculator.ads.getLastAutosensData("Overview", aapsLogger, dateUtil)
/*
* Graphs
*/
@ -276,6 +263,8 @@ class OverviewData @Inject constructor(
var maxTreatmentsValue = 0.0
var treatmentsSeries: PointsWithLabelGraphSeries<DataPointWithLabelInterface> = PointsWithLabelGraphSeries()
var maxTherapyEventValue = 0.0
var therapyEventSeries: PointsWithLabelGraphSeries<DataPointWithLabelInterface> = PointsWithLabelGraphSeries()
var maxIobValueFound = Double.MIN_VALUE
val iobScale = Scale()
@ -309,532 +298,4 @@ class OverviewData @Inject constructor(
val dsMinScale = Scale()
var dsMaxSeries: 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.Context
import android.content.Intent
import android.graphics.Color
import android.graphics.Paint
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.graphics.drawable.AnimationDrawable
import android.os.Build
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.databinding.OverviewFragmentBinding
import info.nightscout.androidaps.dialogs.*
import info.nightscout.androidaps.events.EventAcceptOpenLoopChange
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.events.*
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.valueToUnitsString
import info.nightscout.androidaps.interfaces.*
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.aps.loop.events.EventNewOpenLoopNotification
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.iob.iobCobCalculator.GlucoseStatusProvider
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.XdripPlugin
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.resources.ResourceHelper
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.UIRunnable
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.kotlin.plusAssign
import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import kotlin.collections.ArrayList
import kotlin.math.abs
import kotlin.math.min
@ -150,10 +147,11 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
_binding = it
//check screen width
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)
else
@Suppress("DEPRECATION") activity?.windowManager?.defaultDisplay?.getMetrics(dm)
activity?.windowManager?.defaultDisplay?.getMetrics(dm)
}.root
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -166,13 +164,13 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
smallHeight = screenHeight <= Constants.SMALL_HEIGHT
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.notifications.setHasFixedSize(false)
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
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?.labelVerticalWidth = axisWidth
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 = if (overviewData.rangeToDisplay > 24) 6 else 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))
sp.putBoolean(R.string.key_objectiveusescale, true)
false
}
prepareGraphsIfNeeded(overviewMenus.setting.size)
overviewMenus.setupChartMenu(binding.graphsLayout.chartMenuButton)
context?.let { overviewMenus.setupChartMenu(it, binding.graphsLayout.chartMenuButton) }
binding.activeProfile.setOnClickListener(this)
binding.activeProfile.setOnLongClickListener(this)
@ -225,113 +219,107 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
@Synchronized
override fun 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
.toObservable(EventUpdateOverviewCalcProgress::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ updateCalcProgress(it.from) }, 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)
.subscribe({ updateCalcProgress() }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewIobCob::class.java)
.debounce(1L, TimeUnit.SECONDS)
.observeOn(aapsSchedulers.main)
.subscribe({ updateIobCob(it.from) }, fabricPrivacy::logException)
.subscribe({ updateIobCob() }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewSensitivity::class.java)
.debounce(1L, TimeUnit.SECONDS)
.observeOn(aapsSchedulers.main)
.subscribe({ updateSensitivity(it.from) }, fabricPrivacy::logException)
.subscribe({ updateSensitivity() }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewGraph::class.java)
.debounce(1L, TimeUnit.SECONDS)
.observeOn(aapsSchedulers.main)
.subscribe({ updateGraph(it.from) }, fabricPrivacy::logException)
.subscribe({ updateGraph() }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewPumpStatus::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ updatePumpStatus(it.from) }, fabricPrivacy::logException)
.subscribe({ updatePumpStatus() }, fabricPrivacy::logException)
disposable += activePlugin.activeOverview.overviewBus
.toObservable(EventUpdateOverviewNotification::class.java)
.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
.toObservable(EventRefreshOverview::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({
if (it.now) overviewPlugin.refreshLoop(it.from)
else scheduleUpdateGUI(it.from)
if (it.now) refreshAll()
else scheduleUpdateGUI()
}, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventAcceptOpenLoopChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ scheduleUpdateGUI("EventAcceptOpenLoopChange") }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventInitializationChanged::class.java)
.observeOn(aapsSchedulers.main)
.subscribe({ updateTime("EventInitializationChanged") }, fabricPrivacy::logException)
.subscribe({ scheduleUpdateGUI() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventPreferenceChange::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ scheduleUpdateGUI("EventPreferenceChange") }, fabricPrivacy::logException)
.subscribe({ scheduleUpdateGUI() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventNewOpenLoopNotification::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ scheduleUpdateGUI("EventNewOpenLoopNotification") }, fabricPrivacy::logException)
.subscribe({ scheduleUpdateGUI() }, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventPumpStatusChanged::class.java)
.observeOn(aapsSchedulers.main)
.delay(30, TimeUnit.MILLISECONDS, aapsSchedulers.main)
.subscribe({
overviewData.pumpStatus = it.getStatus(rh)
updatePumpStatus("EventPumpStatusChanged")
updatePumpStatus()
}, 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 {
overviewPlugin.refreshLoop("refreshLoop")
refreshAll()
handler.postDelayed(refreshLoop, 60 * 1000L)
}
handler.postDelayed(refreshLoop, 60 * 1000L)
updateTime("onResume")
updateCalcProgress("onResume")
updateProfile("onResume")
updateTemporaryBasal("onResume")
updateExtendedBolus("onResume")
updateTemporaryTarget("onResume")
updateBg("onResume")
updateIobCob("onResume")
updateSensitivity("onResume")
updateGraph("onResume")
updatePumpStatus("onResume")
updateNotification("onResume")
refreshAll()
updatePumpStatus()
updateCalcProgress()
}
fun refreshAll() {
runOnUiThread {
_binding ?: return@runOnUiThread
updateBg()
updateTime()
updateProfile()
updateTemporaryBasal()
updateExtendedBolus()
updateTemporaryTarget()
updateIobCob()
updateSensitivity()
updateGraph()
updateNotification()
}
}
@Synchronized
@ -415,14 +403,15 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
loop.invoke("Accept temp button", false)
if (lastRun?.lastAPSRun != null && lastRun.constraintsProcessed?.isChangeRequested == true) {
protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, UIRunnable {
OKDialog.showConfirmation(activity, rh.gs(R.string.tempbasal_label), lastRun.constraintsProcessed?.toSpanned()
?: "".toSpanned(), {
uel.log(Action.ACCEPTS_TEMP_BASAL, Sources.Overview)
(context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager?)?.cancel(Constants.notificationID)
rxBus.send(EventWearInitiateAction("cancelChangeRequest"))
Thread { loop.acceptChangeRequest() }.run()
binding.buttonsLayout.acceptTempButton.visibility = View.GONE
})
if (isAdded)
OKDialog.showConfirmation(activity, rh.gs(R.string.tempbasal_label), lastRun.constraintsProcessed?.toSpanned()
?: "".toSpanned(), {
uel.log(Action.ACCEPTS_TEMP_BASAL, Sources.Overview)
(context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager?)?.cancel(Constants.notificationID)
rxBus.send(EventWearInitiateAction("cancelChangeRequest"))
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()
if (dexcomIsSource) {
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) {
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()
@ -573,7 +570,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
if (event.isEnabled && event.trigger.shouldRun())
context?.let { context ->
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.layoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 0.5f).also { l ->
l.setMargins(0, 0, rh.dpToPx(-4), 0)
@ -582,11 +579,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
it.text = event.title
it.setOnClickListener {
OKDialog.showConfirmation(
context,
rh.gs(R.string.run_question, event.title),
{ handler.post { automationPlugin.processEvent(event) } }
)
OKDialog.showConfirmation(context, rh.gs(R.string.run_question, event.title), { handler.post { automationPlugin.processEvent(event) } })
}
binding.buttonsLayout.userButtonsLayout.addView(it)
}
@ -721,12 +714,12 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
val graph = GraphView(context)
graph.layoutParams =
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?.isHorizontalLabelsVisible = false
graph.gridLabelRenderer?.labelVerticalWidth = axisWidth
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)
val label = TextView(context)
@ -745,11 +738,11 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
var task: Runnable? = null
private fun scheduleUpdateGUI(from: String) {
private fun scheduleUpdateGUI() {
class UpdateRunnable : Runnable {
override fun run() {
overviewPlugin.refreshLoop(from)
refreshAll()
task = null
}
}
@ -759,20 +752,20 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
}
@SuppressLint("SetTextI18n")
@Suppress("UNUSED_PARAMETER")
fun updateBg(from: String) {
fun updateBg() {
_binding ?: return
val units = profileFunction.getUnits()
binding.infoLayout.bg.text = overviewData.lastBg?.valueToUnitsString(units)
?: 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.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)
val glucoseStatus = glucoseStatusProvider.glucoseStatusData
if (glucoseStatus != null) {
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.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)
@ -809,70 +802,76 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
}
}
@Suppress("UNUSED_PARAMETER")
fun updateProfile(from: String) {
fun updateProfile() {
_binding ?: return
val profileBackgroundColor =
profileFunction.getProfile()?.let {
if (it is ProfileSealed.EPS) {
if (it.value.originalPercentage != 100 || it.value.originalTimeshift != 0L || it.value.originalDuration != 0L)
rh.gc(R.color.ribbonWarning)
else rh.gc(R.color.ribbonDefault)
rh.gac(context, R.attr.ribbonWarningColor)
else rh.gac(context, R.attr.ribbonDefaultColor)
} else if (it is ProfileSealed.PS) {
rh.gc(R.color.ribbonDefault)
rh.gac(context, R.attr.ribbonDefaultColor)
} 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 =
profileFunction.getProfile()?.let {
if (it is ProfileSealed.EPS) {
if (it.value.originalPercentage != 100 || it.value.originalTimeshift != 0L || it.value.originalDuration != 0L)
rh.gc(R.color.ribbonTextWarning)
else rh.gc(R.color.ribbonTextDefault)
rh.gac(context, R.attr.ribbonTextWarningColor)
else rh.gac(context, R.attr.ribbonTextDefaultColor)
} else if (it is ProfileSealed.PS) {
rh.gc(R.color.ribbonTextDefault)
rh.gac(context, R.attr.ribbonTextDefaultColor)
} 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.setBackgroundColor(profileBackgroundColor)
binding.activeProfile.setTextColor(profileTextColor)
}
@Suppress("UNUSED_PARAMETER")
fun updateTemporaryBasal(from: String) {
binding.infoLayout.baseBasal.text = overviewData.temporaryBasalText
binding.infoLayout.baseBasal.setTextColor(overviewData.temporaryBasalColor)
binding.infoLayout.baseBasalIcon.setImageResource(overviewData.temporaryBasalIcon)
private fun updateTemporaryBasal() {
_binding ?: return
binding.infoLayout.baseBasal.text = overviewData.temporaryBasalText(iobCobCalculator)
binding.infoLayout.baseBasal.setTextColor(overviewData.temporaryBasalColor(context, iobCobCalculator))
binding.infoLayout.baseBasalIcon.setImageResource(overviewData.temporaryBasalIcon(iobCobCalculator))
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")
fun updateExtendedBolus(from: String) {
private fun updateExtendedBolus() {
_binding ?: return
val pump = activePlugin.activePump
binding.infoLayout.extendedBolus.text = overviewData.extendedBolusText
binding.infoLayout.extendedBolus.text = overviewData.extendedBolusText(iobCobCalculator)
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()
}
@Suppress("UNUSED_PARAMETER")
fun updateTime(from: String) {
fun updateTime() {
_binding ?: return
binding.infoLayout.time.text = dateUtil.timeString(dateUtil.now())
// Status lights
val isPatchPump = activePlugin.activePump.pumpDescription.isPatchPump
val pump = activePlugin.activePump
val isPatchPump = pump.pumpDescription.isPatchPump
binding.statusLightsLayout.apply {
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.scaleX = if (isPatchPump) 1.4f else 2f
cannulaOrPatch.scaleY = cannulaOrPatch.scaleX
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()
}
statusLightHandler.updateStatusLights(
@ -888,14 +887,14 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
processAps()
}
@Suppress("UNUSED_PARAMETER")
fun updateIobCob(from: String) {
binding.infoLayout.iob.text = overviewData.iobText
fun updateIobCob() {
_binding ?: return
binding.infoLayout.iob.text = overviewData.iobText(iobCobCalculator)
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
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 lastRun = loop.lastRun
@ -916,14 +915,13 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
}
@SuppressLint("SetTextI18n")
@Suppress("UNUSED_PARAMETER")
fun updateTemporaryTarget(from: String) {
fun updateTemporaryTarget() {
_binding ?: return
val units = profileFunction.getUnits()
if (overviewData.temporaryTarget?.isInProgress(dateUtil) == false) overviewData.temporaryTarget = null
val tempTarget = overviewData.temporaryTarget
if (tempTarget != null) {
binding.tempTarget.setTextColor(rh.gc(R.color.ribbonTextWarning))
binding.tempTarget.setBackgroundColor(rh.gc(R.color.ribbonWarning))
binding.tempTarget.setTextColor(rh.gac(context, R.attr.ribbonTextWarningColor))
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)
} else {
// 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) {
aapsLogger.debug("Adjusted target. Profile: ${profile.getTargetMgdl()} APS: $targetUsed")
binding.tempTarget.text = Profile.toTargetRangeString(targetUsed, targetUsed, GlucoseUnit.MGDL, units)
binding.tempTarget.setTextColor(rh.gc(R.color.ribbonTextWarning))
binding.tempTarget.setBackgroundColor(rh.gc(R.color.tempTargetBackground))
binding.tempTarget.setTextColor(rh.gac(context, R.attr.ribbonTextWarningColor))
binding.tempTarget.setBackgroundColor(rh.gac(context, R.attr.tempTargetBackgroundColor))
} else {
binding.tempTarget.setTextColor(rh.gc(R.color.ribbonTextDefault))
binding.tempTarget.setBackgroundColor(rh.gc(R.color.ribbonDefault))
binding.tempTarget.setTextColor(rh.gac(context, R.attr.ribbonTextDefaultColor))
binding.tempTarget.setBackgroundColor(rh.gac(context, R.attr.ribbonDefaultColor))
binding.tempTarget.text = Profile.toTargetRangeString(profile.getTargetLowMgdl(), profile.getTargetHighMgdl(), GlucoseUnit.MGDL, units)
}
}
}
}
@Suppress("UNUSED_PARAMETER")
fun updateGraph(from: String) {
private fun updateGraph() {
_binding ?: return
val pump = activePlugin.activePump
val graphData = GraphData(injector, binding.graphsLayout.bgGraph, overviewData)
val menuChartSettings = overviewMenus.setting
@ -953,6 +951,8 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
graphData.addBgReadings(menuChartSettings[0][OverviewMenus.CharType.PRE.ordinal])
if (buildHelper.isDev()) graphData.addBucketedData()
graphData.addTreatments()
if (menuChartSettings[0][OverviewMenus.CharType.TREAT.ordinal])
graphData.addTherapyEvents()
if (menuChartSettings[0][OverviewMenus.CharType.ACT.ordinal])
graphData.addActivity(0.8)
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")
fun updateCalcProgress(from: String) {
binding.graphsLayout.iobCalculationProgress.text = overviewData.calcProgress
private fun updateCalcProgress() {
_binding ?: return
binding.progressBar.progress = overviewData.calcProgressPct
binding.progressBar.visibility = (overviewData.calcProgressPct != 100).toVisibility()
}
@Suppress("UNUSED_PARAMETER")
fun updateSensitivity(from: String) {
private fun updateSensitivity() {
_binding ?: return
if (sp.getBoolean(R.string.key_openapsama_useautosens, false) && constraintChecker.isAutosensModeEnabled().value()) {
binding.infoLayout.sensitivityIcon.setImageResource(R.drawable.ic_swap_vert_black_48dp_green)
} else {
@ -1037,20 +1038,20 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
}
binding.infoLayout.sensitivity.text =
overviewData.lastAutosensData?.let { autosensData ->
overviewData.lastAutosensData(iobCobCalculator)?.let { autosensData ->
String.format(Locale.ENGLISH, "%.0f%%", autosensData.autosensResult.ratio * 100)
} ?: ""
}
@Suppress("UNUSED_PARAMETER")
fun updatePumpStatus(from: String) {
private fun updatePumpStatus() {
_binding ?: return
val status = overviewData.pumpStatus
binding.pumpStatus.text = status
binding.pumpStatusLayout.visibility = (status != "").toVisibility()
}
@Suppress("UNUSED_PARAMETER")
fun updateNotification(from: String) {
private fun updateNotification() {
_binding ?: return
binding.notifications.let { notificationStore.updateNotifications(it) }
}
}

View file

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

View file

@ -4,25 +4,26 @@ import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.events.*
import info.nightscout.androidaps.events.EventPumpStatusChanged
import info.nightscout.androidaps.extensions.*
import info.nightscout.androidaps.interfaces.*
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.plugins.aps.events.EventLoopInvoked
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.Overview
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
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.ScaledDataPoint
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.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
@ -41,9 +42,6 @@ class OverviewPlugin @Inject constructor(
private val aapsSchedulers: AapsSchedulers,
rh: ResourceHelper,
private val config: Config,
private val dateUtil: DateUtil,
private val iobCobCalculator: IobCobCalculator,
private val repository: AppRepository,
private val overviewData: OverviewData,
private val overviewMenus: OverviewMenus
) : PluginBase(
@ -89,62 +87,9 @@ class OverviewPlugin @Inject constructor(
disposable += rxBus
.toObservable(EventIobCalculationProgress::class.java)
.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({
loadIobCobResults("EventTreatmentChange")
overviewData.prepareTreatmentsData("EventTreatmentChange")
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")
overviewData.calcProgressPct = it.pass.finalPercent(it.progressPct)
overviewBus.send(EventUpdateOverviewCalcProgress("EventIobCalculationProgress"))
}, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventPumpStatusChanged::class.java)
@ -152,20 +97,7 @@ class OverviewPlugin @Inject constructor(
.subscribe({
overviewData.pumpStatus = it.getStatus(rh)
}, 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() {
@ -243,7 +175,7 @@ class OverviewPlugin @Inject constructor(
.storeDouble(R.string.key_statuslights_bat_critical, sp, rh)
.storeInt(R.string.key_boluswizard_percentage, sp, rh)
}
/*
@Volatile
var runningRefresh = false
override fun refreshLoop(from: String) {
@ -284,38 +216,5 @@ class OverviewPlugin @Inject constructor(
overviewBus.send(EventUpdateOverviewGraph(from))
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
import android.graphics.Color
import android.view.View
import android.widget.TextView
import androidx.annotation.StringRes
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_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)
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)
}
if (!config.NSCLIENT) {
@ -58,16 +57,16 @@ class StatusLightHandler @Inject constructor(
}
if (!config.NSCLIENT) {
if (pump.model() == PumpType.OMNIPOD_DASH) {
// Omnipod Dash does not report its battery level
careportal_battery_level?.text = rh.gs(R.string.notavailable)
careportal_battery_level?.setTextColor(Color.WHITE)
} 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.
// 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) {
// The Omnipod Eros does not report its battery level. However, some RileyLink alternatives do.
// Depending on the user's configuration, we will either show the battery level reported by the RileyLink or "n/a"
// Pump instance check is needed because at startup, the pump can still be VirtualPumpPlugin and that will cause a crash
val erosBatteryLinkAvailable = pump.model() == PumpType.OMNIPOD_EROS && pump is OmnipodErosPumpPlugin && pump.isUseRileyLinkBatteryLevel
if (pump.model().supportBatteryLevel || erosBatteryLinkAvailable) {
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.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.MenuItem
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 android.util.SparseArray
import android.view.*
import androidx.core.util.forEach
import androidx.fragment.app.FragmentManager
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.RecyclerView
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.DaggerAppCompatActivityWithResult
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.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.events.EventQuickWizardChange
import info.nightscout.androidaps.utils.ActionModeHelper
import info.nightscout.androidaps.utils.DateUtil
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.wizard.QuickWizard
import info.nightscout.androidaps.utils.wizard.QuickWizardEntry
@ -36,7 +32,7 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
import javax.inject.Inject
class QuickWizardListActivity : DaggerAppCompatActivityWithResult() {
class QuickWizardListActivity : DaggerAppCompatActivityWithResult(), OnStartDragListener {
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var rxBus: RxBus
@ -46,134 +42,78 @@ class QuickWizardListActivity : DaggerAppCompatActivityWithResult() {
@Inject lateinit var sp: SP
private var disposable: CompositeDisposable = CompositeDisposable()
private lateinit var actionHelper: ActionModeHelper<QuickWizardEntry>
private val itemTouchHelper = ItemTouchHelper(SimpleItemTouchHelperCallback())
private lateinit var binding: OverviewQuickwizardlistActivityBinding
private val itemTouchHelper by lazy {
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) {
override fun onStartDrag(viewHolder: RecyclerView.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 {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.overview_quickwizardlist_item, parent, false)
val viewHolder = QuickWizardEntryViewHolder(itemView, fragmentManager)
viewHolder.handleView.setOnTouchListener { _, event ->
if (event.actionMasked == MotionEvent.ACTION_DOWN) {
startDragging(viewHolder)
}
return@setOnTouchListener true
}
return viewHolder
val binding = OverviewQuickwizardlistItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return QuickWizardEntryViewHolder(binding, fragmentManager)
}
@SuppressLint("ClickableViewAccessibility")
override fun onBindViewHolder(holder: QuickWizardEntryViewHolder, position: Int) {
holder.from.text = dateUtil.timeString(quickWizard[position].validFromDate())
holder.to.text = dateUtil.timeString(quickWizard[position].validToDate())
val wearControl = sp.getBoolean(R.string.key_wear_control, false)
if (wearControl) {
holder.handleView.visibility = View.VISIBLE
val entry = quickWizard[position]
holder.binding.from.text = dateUtil.timeString(entry.validFromDate())
holder.binding.to.text = dateUtil.timeString(entry.validToDate())
holder.binding.buttonText.text = entry.buttonText()
holder.binding.carbs.text = rh.gs(R.string.format_carbs, entry.carbs())
if (entry.device() == QuickWizardEntry.DEVICE_ALL) {
holder.binding.device.visibility = View.GONE
} else {
holder.handleView.visibility = View.GONE
}
if (quickWizard[position].device() == QuickWizardEntry.DEVICE_ALL) {
holder.device.visibility = View.GONE
} else {
holder.device.visibility = View.VISIBLE
holder.device.setImageResource(
holder.binding.device.visibility = View.VISIBLE
holder.binding.device.setImageResource(
when (quickWizard[position].device()) {
QuickWizardEntry.DEVICE_WATCH -> R.drawable.ic_watch
else -> R.drawable.ic_smartphone
}
)
}
holder.buttonText.text = quickWizard[position].buttonText()
holder.carbs.text = rh.gs(R.string.format_carbs, quickWizard[position].carbs())
}
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 {
holder.binding.root.setOnClickListener {
if (actionHelper.isNoAction) {
val manager = fragmentManager
val editQuickWizardDialog = EditQuickWizardDialog()
val bundle = Bundle()
bundle.putInt("position", bindingAdapterPosition)
bundle.putInt("position", position)
editQuickWizardDialog.arguments = bundle
editQuickWizardDialog.show(manager, "EditQuickWizardDialog")
}
removeButton.setOnClickListener {
quickWizard.remove(bindingAdapterPosition)
rxBus.send(EventQuickWizardChange())
} else if (actionHelper.isRemoving) {
holder.binding.cbRemove.toggle()
actionHelper.updateSelection(position, entry, holder.binding.cbRemove.isChecked)
}
}
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) {
Log.i("QuickWizard", "moveItem")
quickWizard.move(from, to)
override fun getItemCount() = quickWizard.size()
override fun onItemMove(fromPosition: Int, toPosition: Int): Boolean {
binding.recyclerview.adapter?.notifyItemMoved(fromPosition, toPosition)
quickWizard.move(fromPosition, toPosition)
return true
}
fun onDrop() {
Log.i("QuickWizard", "onDrop")
rxBus.send(EventQuickWizardChange())
}
override fun onDrop() = rxBus.send(EventQuickWizardChange())
}
override fun onCreate(savedInstanceState: Bundle?) {
@ -181,6 +121,11 @@ class QuickWizardListActivity : DaggerAppCompatActivityWithResult() {
binding = OverviewQuickwizardlistActivityBinding.inflate(layoutInflater)
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)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true)
@ -191,6 +136,7 @@ class QuickWizardListActivity : DaggerAppCompatActivityWithResult() {
itemTouchHelper.attachToRecyclerView(binding.recyclerview)
binding.addButton.setOnClickListener {
actionHelper.finish()
val manager = supportFragmentManager
val editQuickWizardDialog = EditQuickWizardDialog()
editQuickWizardDialog.show(manager, "EditQuickWizardDialog")
@ -210,9 +156,25 @@ class QuickWizardListActivity : DaggerAppCompatActivityWithResult() {
override fun onPause() {
disposable.clear()
actionHelper.finish()
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 =
when (item.itemId) {
android.R.id.home -> {
@ -220,6 +182,17 @@ class QuickWizardListActivity : DaggerAppCompatActivityWithResult() {
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 {
context?.let {
TimePickerDialog(
it, R.style.MaterialPickerTheme,
it,
fromTimeSetListener,
T.secs(fromSeconds.toLong()).hours().toInt(),
T.secs((fromSeconds % 3600).toLong()).mins().toInt(),
@ -124,7 +124,7 @@ class EditQuickWizardDialog : DaggerDialogFragment(), View.OnClickListener {
binding.to.setOnClickListener {
context?.let {
TimePickerDialog(
it, R.style.MaterialPickerTheme,
it,
toTimeSetListener,
T.secs(toSeconds.toLong()).hours().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 {
it.color = 0
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)
}
fun addTherapyEvents() {
maxY = maxOf(maxY, overviewData.maxTherapyEventValue)
addSeries(overviewData.therapyEventSeries)
}
fun addActivity(scale: Double) {
addSeries(overviewData.activitySeries)
addSeries(overviewData.activityPredictionSeries)

View file

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

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.plugins.general.overview.graphExtensions
import android.content.Context
import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.database.entities.Carbs
import info.nightscout.androidaps.utils.resources.ResourceHelper
@ -18,7 +19,10 @@ class CarbsDataPoint @Inject constructor(
override val duration = 0L
override val size = 2f
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) {
yValue = y

View file

@ -1,11 +1,15 @@
package info.nightscout.androidaps.plugins.general.overview.graphExtensions
import android.content.Context
import android.graphics.Color
import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.database.entities.EffectiveProfileSwitch
import info.nightscout.androidaps.utils.resources.ResourceHelper
import javax.inject.Inject
class EffectiveProfileSwitchDataPoint @Inject constructor(
val data: EffectiveProfileSwitch
val data: EffectiveProfileSwitch,
private val rh: ResourceHelper
) : DataPointWithLabelInterface {
private var yValue = 0.0
@ -21,5 +25,7 @@ class EffectiveProfileSwitchDataPoint @Inject constructor(
override val duration = 0L
override val shape = PointsWithLabelGraphSeries.Shape.PROFILE
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
import android.content.Context
import android.graphics.Color
import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.database.entities.ExtendedBolus
import info.nightscout.androidaps.extensions.toStringTotal
import info.nightscout.androidaps.utils.resources.ResourceHelper
import javax.inject.Inject
class ExtendedBolusDataPoint @Inject constructor(
val data: ExtendedBolus
val data: ExtendedBolus,
private val rh: ResourceHelper
) : DataPointWithLabelInterface {
private var yValue = 0.0
@ -17,7 +21,9 @@ class ExtendedBolusDataPoint @Inject constructor(
override val duration get() = data.duration
override val size = 10f
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) {
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)
// float first_X = (float) x + (graphLeft + 1);
// 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;
lastEndX = orgX;

View file

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

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.plugins.general.overview.graphExtensions
import android.content.Context
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.data.InMemoryGlucoseValue
@ -24,5 +25,7 @@ class InMemoryGlucoseValueDataPoint @Inject constructor(
override val duration = 0L
override val shape = PointsWithLabelGraphSeries.Shape.BUCKETED_BG
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
import android.content.Context
import android.graphics.Color
import info.nightscout.androidaps.Constants
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 color
get() =
when (data.type) {
TherapyEvent.Type.ANNOUNCEMENT -> rh.gc(R.color.notificationAnnouncement)
TherapyEvent.Type.NS_MBG -> Color.RED
TherapyEvent.Type.FINGER_STICK_BG_VALUE -> Color.RED
TherapyEvent.Type.EXERCISE -> Color.BLUE
TherapyEvent.Type.APS_OFFLINE -> Color.GRAY and -0x7f000001
else -> Color.GRAY
}
override fun color(context: Context?): Int {
return when (data.type) {
TherapyEvent.Type.ANNOUNCEMENT -> rh.gac(context, R.attr.notificationAnnouncement)
TherapyEvent.Type.NS_MBG -> rh.gac(context, R.attr.therapyEvent_NS_MBG)
TherapyEvent.Type.FINGER_STICK_BG_VALUE -> rh.gac(context, R.attr.therapyEvent_FINGER_STICK_BG_VALUE)
TherapyEvent.Type.EXERCISE -> rh.gac(context, R.attr.therapyEvent_EXERCISE)
TherapyEvent.Type.APS_OFFLINE -> rh.gac(context, R.attr.therapyEvent_APS_OFFLINE) and -0x7f000001
else -> rh.gac(context, R.attr.therapyEvent_Default)
}
}
}

View file

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

View file

@ -39,8 +39,6 @@ class DummyService : DaggerService() {
override fun 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 {
aapsLogger.debug("Starting DummyService with ID ${notificationHolder.notificationID} notification ${notificationHolder.notification}")
startForeground(notificationHolder.notificationID, notificationHolder.notification)

View file

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

View file

@ -328,7 +328,7 @@ class SmsCommunicatorPlugin @Inject constructor(
} else if (lastBG != null) {
val agoMilliseconds = dateUtil.now() - lastBG.timestamp
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
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" -> {
if (loop.enabled) {
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
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false) {
override fun run() {
@ -372,7 +372,7 @@ class SmsCommunicatorPlugin @Inject constructor(
"ENABLE", "START" -> {
if (!loop.enabled) {
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
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false) {
override fun run() {
@ -389,7 +389,7 @@ class SmsCommunicatorPlugin @Inject constructor(
"STATUS" -> {
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.loopisdisabled)
@ -399,7 +399,7 @@ class SmsCommunicatorPlugin @Inject constructor(
"RESUME" -> {
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
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true) {
override fun run() {
@ -436,7 +436,7 @@ class SmsCommunicatorPlugin @Inject constructor(
return
} else {
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
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, duration) {
override fun run() {
@ -515,7 +515,7 @@ class SmsCommunicatorPlugin @Inject constructor(
receivedSms.processed = true
} else if ((divided.size == 2) && (divided[1].equals("CONNECT", ignoreCase = true))) {
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
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true) {
override fun run() {
@ -548,7 +548,7 @@ class SmsCommunicatorPlugin @Inject constructor(
return
} else {
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
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true) {
override fun run() {
@ -601,7 +601,7 @@ class SmsCommunicatorPlugin @Inject constructor(
if (profile == null) sendSMS(Sms(receivedSms.phoneNumber, rh.gs(R.string.noprofile)))
else {
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
val finalPercentage = percentage
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) {
if (divided[1].uppercase(Locale.getDefault()) == "CANCEL" || divided[1].uppercase(Locale.getDefault()) == "STOP") {
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
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true) {
override fun run() {
@ -662,14 +662,14 @@ class SmsCommunicatorPlugin @Inject constructor(
else {
tempBasalPct = constraintChecker.applyBasalPercentConstraints(Constraint(tempBasalPct), profile).value()
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
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, tempBasalPct, duration) {
override fun run() {
commandQueue.tempBasalPercent(anInteger(), secondInteger(), true, profile, PumpSync.TemporaryBasalType.NORMAL, object : Callback() {
override fun run() {
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)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
if (result.isPercent)
@ -706,15 +706,15 @@ class SmsCommunicatorPlugin @Inject constructor(
else {
tempBasal = constraintChecker.applyBasalConstraints(Constraint(tempBasal), profile).value()
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
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, tempBasal, duration) {
override fun run() {
commandQueue.tempBasalAbsolute(aDouble(), secondInteger(), true, profile, PumpSync.TemporaryBasalType.NORMAL, object : Callback() {
override fun run() {
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)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
if (result.isPercent)
@ -743,7 +743,7 @@ class SmsCommunicatorPlugin @Inject constructor(
private fun processEXTENDED(divided: Array<String>, receivedSms: Sms) {
if (divided[1].uppercase(Locale.getDefault()) == "CANCEL" || divided[1].uppercase(Locale.getDefault()) == "STOP") {
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
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true) {
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)))
else {
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
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, extended, duration) {
override fun run() {
commandQueue.extendedBolus(aDouble(), secondInteger(), object : Callback() {
override fun run() {
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)
replyText += "\n" + activePlugin.activePump.shortStatus(true)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
@ -817,9 +817,9 @@ class SmsCommunicatorPlugin @Inject constructor(
} else if (bolus > 0.0) {
val passCode = generatePassCode()
val reply = if (isMeal)
String.format(rh.gs(R.string.smscommunicator_mealbolusreplywithcode), bolus, passCode)
rh.gs(R.string.smscommunicator_mealbolusreplywithcode, bolus, passCode)
else
String.format(rh.gs(R.string.smscommunicator_bolusreplywithcode), bolus, passCode)
rh.gs(R.string.smscommunicator_bolusreplywithcode, bolus, passCode)
receivedSms.processed = true
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, bolus) {
override fun run() {
@ -833,9 +833,9 @@ class SmsCommunicatorPlugin @Inject constructor(
override fun run() {
if (resultSuccess) {
var replyText = if (isMeal)
String.format(rh.gs(R.string.smscommunicator_mealbolusdelivered), resultBolusDelivered)
rh.gs(R.string.smscommunicator_mealbolusdelivered, resultBolusDelivered)
else
String.format(rh.gs(R.string.smscommunicator_bolusdelivered), resultBolusDelivered)
rh.gs(R.string.smscommunicator_bolusdelivered, resultBolusDelivered)
replyText += "\n" + activePlugin.activePump.shortStatus(true)
lastRemoteBolusTime = dateUtil.now()
if (isMeal) {
@ -866,7 +866,7 @@ class SmsCommunicatorPlugin @Inject constructor(
val tt = if (currentProfile.units == GlucoseUnit.MMOL) {
DecimalFormatter.to1Decimal(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))
@ -920,7 +920,7 @@ class SmsCommunicatorPlugin @Inject constructor(
if (grams == 0) sendSMS(Sms(receivedSms.phoneNumber, rh.gs(R.string.wrongformat)))
else {
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
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = true, grams, time) {
override fun run() {
@ -930,7 +930,7 @@ class SmsCommunicatorPlugin @Inject constructor(
commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() {
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)
sendSMSToAllNumbers(Sms(receivedSms.phoneNumber, replyText))
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)
if (isMeal || isActivity || isHypo) {
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
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false) {
override fun run() {
@ -1009,7 +1009,7 @@ class SmsCommunicatorPlugin @Inject constructor(
aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it)
})
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))
uel.log(Action.TT, Sources.SMS,
ValueWithUnit.fromGlucoseUnit(tt, units.asText),
@ -1018,7 +1018,7 @@ class SmsCommunicatorPlugin @Inject constructor(
})
} else if (isStop) {
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
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false) {
override fun run() {
@ -1028,7 +1028,7 @@ class SmsCommunicatorPlugin @Inject constructor(
}, {
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))
uel.log(Action.CANCEL_TT, Sources.SMS, rh.gs(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))
if (isStop) {
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
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false) {
override fun run() {
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))
uel.log(Action.STOP_SMS, Sources.SMS, rh.gs(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])
if (cal > 0.0) {
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
messageToConfirm = AuthRequest(injector, receivedSms, reply, passCode, object : SmsAction(pumpCommand = false, cal) {
override fun run() {
@ -1127,7 +1127,7 @@ class SmsCommunicatorPlugin @Inject constructor(
}
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 {
var s = str

View file

@ -31,7 +31,6 @@ import javax.inject.Inject
class SmsCommunicatorOtpActivity : NoSplashAppCompatActivity() {
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var rxBus: RxBus
@Inject lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin
@Inject lateinit var otp: OneTimePassword
@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
private fun handleInitiate(actionString: String) {
//TODO: i18n
Log.i("ActionStringHandler", "handleInitiate actionString=" + actionString)
Log.i("ActionStringHandler", "handleInitiate actionString=$actionString")
if (!sp.getBoolean(R.string.key_wear_control, false)) return
lastBolusWizard = null
var rTitle = rh.gs(R.string.confirm).uppercase()
@ -640,10 +640,10 @@ class ActionStringHandler @Inject constructor(
var msg = ""
//check for validity
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) {
msg += String.format(rh.gs(R.string.valueoutofrange), "Profile-Timeshift") + "\n"
msg += rh.gs(R.string.valueoutofrange, "Profile-Timeshift") + "\n"
}
val profile = profileFunction.getProfile()
if (profile == null) {

View file

@ -95,7 +95,7 @@ class WearPlugin @Inject constructor(
.toObservable(EventBolusRequested::class.java)
.observeOn(aapsSchedulers.io)
.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)
intent.putExtra("progresspercent", 0)
intent.putExtra("progressstatus", status)

View file

@ -120,7 +120,7 @@ class SendToDataLayerThread extends AsyncTask<DataMap,Void,Void> {
}
state = 0;
} catch (Exception e) {
Log.e(TAG, logPrefix + "Got exception in sendToWear: " + e.toString());
Log.e(TAG, logPrefix + "Got exception in sendToWear: " + e);
} finally {
lastlock = 0;
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.Profile;
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.bus.RxBus;
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.resources.ResourceHelper;
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.weardata.WearUris;
public class WatchUpdaterService extends WearableListenerService implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
@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");
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";
@ -259,21 +245,21 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
// Log.d(TAG, "onMessageRecieved: " + event);
if (wearIntegration()) {
if (event != null && event.getPath().equals(WEARABLE_RESEND_PATH)) {
if (event != null && event.getPath().equals(WearUris.WEARABLE_RESEND_PATH)) {
resendData();
}
if (event != null && event.getPath().equals(WEARABLE_CANCELBOLUS_PATH)) {
if (event != null && event.getPath().equals(WearUris.WEARABLE_CANCELBOLUS_PATH)) {
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());
aapsLogger.debug(LTag.WEAR, "Wear: " + 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());
aapsLogger.debug(LTag.WEAR, "Wear Confirm: " + actionstring);
rxBus.send(new EventWearConfirmAction(actionstring));
@ -299,7 +285,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
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);
}
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();
sendStatus();
@ -423,13 +409,12 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
double endBasalValue = beginBasalValue;
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_amount = beginBasalValue;
long tb_start = runningTime;
if (tb1 != null) {
tb_before = beginBasalValue;
Profile profileTB = profileFunction.getProfile(runningTime);
if (profileTB != null) {
tb_amount = TemporaryBasalExtensionKt.convertedToAbsolute(tb1, runningTime, profileTB);
@ -457,7 +442,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
tb2 = iobCobCalculator.getTempBasalIncludingConvertedExtended(runningTime);
if (tb1 == null && tb2 == null) {
//no temp stays no temp
; //no temp stays no temp
} else if (tb1 != null && tb2 == null) {
//temp is over -> push it
@ -529,7 +514,8 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
if (!predArray.isEmpty()) {
for (GlucoseValueDataPoint bg : predArray) {
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("boluses", boluses);
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) {
@ -582,7 +568,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
private void sendNotification() {
if (googleApiClient != null && googleApiClient.isConnected()) {
PutDataMapRequest dataMapRequest = PutDataMapRequest.create(OPEN_SETTINGS_PATH);
PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.OPEN_SETTINGS_PATH);
//unique content
dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis());
dataMapRequest.getDataMap().putString("openSettings", "openSettings");
@ -595,7 +581,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
private void sendBolusProgress(int progresspercent, String status) {
if (googleApiClient != null && googleApiClient.isConnected()) {
PutDataMapRequest dataMapRequest = PutDataMapRequest.create(BOLUS_PROGRESS_PATH);
PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.BOLUS_PROGRESS_PATH);
//unique content
dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis());
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) {
if (googleApiClient != null && googleApiClient.isConnected()) {
PutDataMapRequest dataMapRequest = PutDataMapRequest.create(ACTION_CONFIRMATION_REQUEST_PATH);
PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.ACTION_CONFIRMATION_REQUEST_PATH);
//unique content
dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis());
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) {
if (googleApiClient != null && googleApiClient.isConnected()) {
PutDataMapRequest dataMapRequest = PutDataMapRequest.create(ACTION_CHANGECONFIRMATION_REQUEST_PATH);
PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.ACTION_CHANGECONFIRMATION_REQUEST_PATH);
//unique content
dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis());
dataMapRequest.getDataMap().putString("changeConfirmationRequest", "changeConfirmationRequest");
@ -648,7 +634,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
private void sendCancelNotificationRequest(String actionstring) {
if (googleApiClient != null && googleApiClient.isConnected()) {
PutDataMapRequest dataMapRequest = PutDataMapRequest.create(ACTION_CANCELNOTIFICATION_REQUEST_PATH);
PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.ACTION_CANCELNOTIFICATION_REQUEST_PATH);
//unique content
dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis());
dataMapRequest.getDataMap().putString("cancelNotificationRequest", "cancelNotificationRequest");
@ -704,7 +690,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
openApsStatus = nsDeviceStatus.getOpenApsTimestamp();
}
PutDataMapRequest dataMapRequest = PutDataMapRequest.create(NEW_STATUS_PATH);
PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.NEW_STATUS_PATH);
//unique content
dataMapRequest.getDataMap().putString("externalStatusString", status);
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 maxCarbs = sp.getInt(R.string.key_treatmentssafety_maxcarbs, 48);
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
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_units_mgdl), mgdl);
dataMapRequest.getDataMap().putInt(rh.gs(R.string.key_boluswizard_percentage), percentage);
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();
Wearable.DataApi.putDataItem(googleApiClient, putDataRequest);
} else {
@ -753,14 +739,14 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
if (googleApiClient != null && googleApiClient.isConnected()) {
int size = quickWizard.size();
ArrayList<DataMap> entities = new ArrayList<>();
for(int i=0; i < size; i++) {
for (int i = 0; i < size; i++) {
QuickWizardEntry q = quickWizard.get(i);
if (q.forDevice(QuickWizardEntry.DEVICE_WATCH)) {
entities.add(quickMap(q));
}
}
PutDataMapRequest dataMapRequest = PutDataMapRequest.create(QUICK_WIZARD_PATH);
PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.QUICK_WIZARD_PATH);
DataMap dm = dataMapRequest.getDataMap();
dm.putLong("timestamp", System.currentTimeMillis());
@ -794,7 +780,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
return status;
}
if (!((PluginBase)loop).isEnabled()) {
if (!((PluginBase) loop).isEnabled()) {
status += rh.gs(R.string.disabledloop) + "\n";
lastLoopStatus = false;
} else {

View file

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

View file

@ -1,6 +1,5 @@
package info.nightscout.androidaps.plugins.iob.iobCobCalculator
import android.os.SystemClock
import androidx.collection.LongSparseArray
import dagger.android.HasAndroidInjector
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.toTemporaryBasal
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.general.overview.OverviewData
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData
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.DecimalFormatter
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.resources.ResourceHelper
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 io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
@ -57,12 +55,11 @@ class IobCobCalculatorPlugin @Inject constructor(
rh: ResourceHelper,
private val profileFunction: ProfileFunction,
private val activePlugin: ActivePlugin,
private val sensitivityOref1Plugin: SensitivityOref1Plugin,
private val sensitivityAAPSPlugin: SensitivityAAPSPlugin,
private val sensitivityWeightedAveragePlugin: SensitivityWeightedAveragePlugin,
private val fabricPrivacy: FabricPrivacy,
private val dateUtil: DateUtil,
private val repository: AppRepository
private val repository: AppRepository,
val overviewData: OverviewData,
private val calculationWorkflow: CalculationWorkflow
) : PluginBase(
PluginDescription()
.mainType(PluginType.GENERAL)
@ -81,7 +78,6 @@ class IobCobCalculatorPlugin @Inject constructor(
override var ads: AutosensDataStore = AutosensDataStore()
private val dataLock = Any()
var stopCalculationTrigger = false
private var thread: Thread? = null
override fun onStart() {
@ -117,14 +113,6 @@ class IobCobCalculatorPlugin @Inject constructor(
resetDataAndRunCalculation("onEventPreferenceChange", event)
}
}, 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
disposable += rxBus
.toObservable(EventNewHistoryData::class.java)
@ -138,10 +126,10 @@ class IobCobCalculatorPlugin @Inject constructor(
}
private fun resetDataAndRunCalculation(reason: String, event: Event?) {
stopCalculation(reason)
calculationWorkflow.stopCalculation(CalculationWorkflow.MAIN_CALCULATION,reason)
clearCache()
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() {
@ -168,10 +156,9 @@ class IobCobCalculatorPlugin @Inject constructor(
return oldestTime
}
fun calculateDetectionStart(from: Long, limitDataToOldestAvailable: Boolean): Long {
override fun calculateDetectionStart(from: Long, limitDataToOldestAvailable: Boolean): Long {
val profile = profileFunction.getProfile(from)
var dia = Constants.defaultDIA
if (profile != null) dia = profile.dia
val dia = profile?.dia ?: Constants.defaultDIA
val oldestDataAvailable = oldestDataAvailable()
val getBGDataFrom: Long
if (limitDataToOldestAvailable) {
@ -302,11 +289,7 @@ class IobCobCalculatorPlugin @Inject constructor(
override fun getMealDataWithWaitingForCalculationFinish(): MealData {
val result = MealData()
val now = System.currentTimeMillis()
val maxAbsorptionHours: Double = if (sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled()) {
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 maxAbsorptionHours: Double = activePlugin.activeSensitivity.maxAbsorptionHours()
val absorptionTimeAgo = now - (maxAbsorptionHours * T.hours(1).msecs()).toLong()
repository.getCarbsDataFromTimeToTimeExpanded(absorptionTimeAgo + 1, now, true)
.blockingGet()
@ -366,27 +349,6 @@ class IobCobCalculatorPlugin @Inject constructor(
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
private val historyWorker = Executors.newSingleThreadScheduledExecutor()
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
private fun newHistoryData(oldDataTimestamp: Long, bgDataReload: Boolean, event: Event) {
//log.debug("Locking onNewHistoryData");
stopCalculation("onEventNewHistoryData")
calculationWorkflow.stopCalculation(CalculationWorkflow.MAIN_CALCULATION,"onEventNewHistoryData")
synchronized(dataLock) {
// clear up 5 min back for proper COB calculation
@ -452,7 +414,7 @@ class IobCobCalculatorPlugin @Inject constructor(
}
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");
}
@ -574,17 +536,17 @@ class IobCobCalculatorPlugin @Inject constructor(
override fun getTempBasalIncludingConvertedExtended(timestamp: Long): TemporaryBasal? {
val tb = repository.getTemporaryBasalActiveAt(timestamp).blockingGet()
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?> {
val tempBasals = HashMap<Long, TemporaryBasal?>();
val tempBasals = HashMap<Long, TemporaryBasal?>()
val tbs = repository.getTemporaryBasalsDataActiveBetweenTimeAndTime(startTime, endTime).blockingGet()
for (t in startTime until endTime step calculationStep) {
val tb = tbs.firstOrNull { basal -> basal.timestamp <= t && (basal.timestamp + basal.duration) > t }
tempBasals[t] = tb ?: getConvertedExtended(t)
}
return tempBasals;
return tempBasals
}
override fun calculateAbsoluteIobFromBaseBasals(toTime: Long): IobTotal {

View file

@ -1,8 +1,10 @@
package info.nightscout.androidaps.plugins.iob.iobCobCalculator
import android.content.Context
import android.os.PowerManager
import android.os.SystemClock
import androidx.work.Worker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants
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.extensions.target
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.IobCobCalculator
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.bus.RxBus
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.sensitivity.SensitivityAAPSPlugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin
import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DecimalFormatter
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.buildHelper.BuildHelper
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 java.util.*
import javax.inject.Inject
@ -38,15 +43,10 @@ import kotlin.math.max
import kotlin.math.min
import kotlin.math.roundToLong
class IobCobOref1Thread internal constructor(
private val injector: HasAndroidInjector,
private val iobCobCalculatorPlugin: IobCobCalculatorPlugin, // cannot be injected : HistoryBrowser uses different instance
private val from: String,
private val end: Long,
private val bgDataReload: Boolean,
private val limitDataToOldestAvailable: Boolean,
private val cause: Event?
) : Thread() {
class IobCobOref1Worker(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var sp: SP
@ -62,48 +62,53 @@ class IobCobOref1Thread internal constructor(
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var repository: AppRepository
private var mWakeLock: PowerManager.WakeLock? = null
@Inject lateinit var dataWorker: DataWorker
@Inject lateinit var calculationWorkflow: CalculationWorkflow
init {
injector.androidInjector().inject(this)
mWakeLock = (context.applicationContext.getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, rh.gs(R.string.app_name) + ":iobCobThread")
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
}
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()
mWakeLock?.acquire(T.mins(10).msecs())
try {
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread started: $from")
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread started: ${data.from}")
if (!profileFunction.isProfileValid("IobCobThread")) {
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No profile): $from")
return // app still initializing
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No profile): ${data.from}")
return Result.failure(workDataOf("Error" to "app still initializing"))
}
//log.debug("Locking calculateSensitivityData");
val oldestTimeWithData = iobCobCalculatorPlugin.calculateDetectionStart(end, limitDataToOldestAvailable)
if (bgDataReload) {
iobCobCalculatorPlugin.ads.loadBgData(end, repository, aapsLogger, dateUtil, rxBus)
iobCobCalculatorPlugin.clearCache()
}
val oldestTimeWithData = data.iobCobCalculator.calculateDetectionStart(data.end, data.limitDataToOldestAvailable)
// 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 autosensDataTable = ads.autosensDataTable
if (bucketedData == null || bucketedData.size < 3) {
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No bucketed data available): $from")
return
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No bucketed data available): ${data.from}")
return Result.failure(workDataOf("Error" to "Aborting calculation thread (No bucketed data available): ${data.from}"))
}
val prevDataTime = ads.roundUpTime(bucketedData[bucketedData.size - 3].timestamp)
aapsLogger.debug(LTag.AUTOSENS, "Prev data time: " + dateUtil.dateAndTimeString(prevDataTime))
var previous = autosensDataTable[prevDataTime]
// start from oldest to be able sub cob
for (i in bucketedData.size - 4 downTo 0) {
val progress = i.toString() + if (buildHelper.isDev()) " ($from)" else ""
rxBus.send(EventIobCalculationProgress(progress, cause))
if (iobCobCalculatorPlugin.stopCalculationTrigger) {
iobCobCalculatorPlugin.stopCalculationTrigger = false
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (trigger): $from")
return
rxBus.send(EventIobCalculationProgress(CalculationWorkflow.ProgressData.IOB_COB_OREF, 100 - (100.0 * i / bucketedData.size).toInt(), data.cause))
if (isStopped) {
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (trigger): ${data.from}")
return Result.failure(workDataOf("Error" to "Aborting calculation thread (trigger): ${data.from}"))
}
// check if data already exists
var bgTime = bucketedData[i].timestamp
@ -116,12 +121,12 @@ class IobCobOref1Thread internal constructor(
}
val profile = profileFunction.getProfile(bgTime)
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
}
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 autosensData = AutosensData(injector)
val autosensData = AutosensData(data.injector)
autosensData.time = bgTime
if (previous != null) autosensData.activeCarbsList = previous.cloneCarbsList() else autosensData.activeCarbsList = ArrayList()
@ -136,7 +141,7 @@ class IobCobOref1Thread internal constructor(
autosensData.bg = bg
delta = bg - bucketedData[i + 1].value
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 deviation = delta - bgi
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)
previous = 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)
aapsLogger.debug(LTag.AUTOSENS, "Sensitivity result: $sensitivity")
autosensData.autosensResult = sensitivity
aapsLogger.debug(LTag.AUTOSENS, autosensData.toString())
}
iobCobCalculatorPlugin.ads = ads
data.iobCobCalculator.ads = ads
Thread {
SystemClock.sleep(1000)
rxBus.send(EventAutosensCalculationFinished(cause))
rxBus.send(EventAutosensCalculationFinished(data.cause))
}.start()
} finally {
mWakeLock?.release()
rxBus.send(EventIobCalculationProgress("", cause))
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread ended: $from")
rxBus.send(EventIobCalculationProgress(CalculationWorkflow.ProgressData.IOB_COB_OREF, 100, data.cause))
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread ended: ${data.from}")
profiler.log(LTag.AUTOSENS, "IobCobOref1Thread", start)
}
return Result.success()
}
}

View file

@ -1,8 +1,10 @@
package info.nightscout.androidaps.plugins.iob.iobCobCalculator
import android.content.Context
import android.os.PowerManager
import android.os.SystemClock
import androidx.work.Worker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants
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.EventAutosensCalculationFinished
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.IobCobCalculator
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.bus.RxBus
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.sensitivity.SensitivityAAPSPlugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin
import info.nightscout.androidaps.receivers.DataWorker
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.DecimalFormatter
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.buildHelper.BuildHelper
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 java.util.*
import javax.inject.Inject
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import kotlin.math.roundToLong
class IobCobThread @Inject internal constructor(
private val injector: HasAndroidInjector,
private val iobCobCalculatorPlugin: IobCobCalculatorPlugin, // cannot be injected : HistoryBrowser uses different instance
private val from: String,
private val end: Long,
private val bgDataReload: Boolean,
private val limitDataToOldestAvailable: Boolean,
private val cause: Event?
) : Thread() {
class IobCobOrefWorker @Inject internal constructor(
context: Context,
params: WorkerParameters
) : Worker(context, params) {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var sp: SP
@ -60,48 +59,51 @@ class IobCobThread @Inject internal constructor(
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var repository: AppRepository
private var mWakeLock: PowerManager.WakeLock? = null
@Inject lateinit var dataWorker: DataWorker
init {
injector.androidInjector().inject(this)
mWakeLock = (context.applicationContext.getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, rh.gs(R.string.app_name) + ":iobCobThread")
(context.applicationContext as HasAndroidInjector).androidInjector().inject(this)
}
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()
mWakeLock?.acquire(T.mins(10).msecs())
try {
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread started: $from")
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread started: ${data.from}")
if (!profileFunction.isProfileValid("IobCobThread")) {
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No profile): $from")
return // app still initializing
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No profile): ${data.from}")
return Result.failure(workDataOf("Error" to "app still initializing"))
}
//log.debug("Locking calculateSensitivityData");
val oldestTimeWithData = iobCobCalculatorPlugin.calculateDetectionStart(end, limitDataToOldestAvailable)
if (bgDataReload) {
iobCobCalculatorPlugin.ads.loadBgData(end, repository, aapsLogger, dateUtil, rxBus)
iobCobCalculatorPlugin.clearCache()
}
val oldestTimeWithData = data.iobCobCalculatorPlugin.calculateDetectionStart(data.end, data.limitDataToOldestAvailable)
// 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 autosensDataTable = ads.autosensDataTable
if (bucketedData == null || bucketedData.size < 3) {
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No bucketed data available): $from")
return
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No bucketed data available): ${data.from}")
return Result.failure(workDataOf("Error" to "Aborting calculation thread (No bucketed data available): ${data.from}"))
}
val prevDataTime = ads.roundUpTime(bucketedData[bucketedData.size - 3].timestamp)
aapsLogger.debug(LTag.AUTOSENS, "Prev data time: " + dateUtil.dateAndTimeString(prevDataTime))
var previous = autosensDataTable[prevDataTime]
// start from oldest to be able sub cob
for (i in bucketedData.size - 4 downTo 0) {
val progress = i.toString() + if (buildHelper.isDev()) " ($from)" else ""
rxBus.send(EventIobCalculationProgress(progress, cause))
if (iobCobCalculatorPlugin.stopCalculationTrigger) {
iobCobCalculatorPlugin.stopCalculationTrigger = false
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (trigger): $from")
return
rxBus.send(EventIobCalculationProgress(CalculationWorkflow.ProgressData.IOB_COB_OREF, 100 - (100.0 * i / bucketedData.size).toInt(), data.cause))
if (isStopped) {
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (trigger): ${data.from}")
return Result.failure(workDataOf("Error" to "Aborting calculation thread (trigger): ${data.from}"))
}
// check if data already exists
var bgTime = bucketedData[i].timestamp
@ -114,12 +116,12 @@ class IobCobThread @Inject internal constructor(
}
val profile = profileFunction.getProfile(bgTime)
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
}
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 autosensData = AutosensData(injector)
val autosensData = AutosensData(data.injector)
autosensData.time = bgTime
if (previous != null) autosensData.activeCarbsList = previous.cloneCarbsList() else autosensData.activeCarbsList = ArrayList()
@ -134,7 +136,7 @@ class IobCobThread @Inject internal constructor(
autosensData.bg = bg
delta = bg - bucketedData[i + 1].value
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 deviation = delta - bgi
val avgDeviation = ((avgDelta - bgi) * 1000).roundToLong() / 1000.0
@ -158,7 +160,7 @@ class IobCobThread @Inject internal constructor(
if (ad == null) {
aapsLogger.debug(LTag.AUTOSENS, autosensDataTable.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)
rxBus.send(EventNewNotification(notification))
sp.putBoolean("log_AUTOSENS", true)
@ -181,7 +183,7 @@ class IobCobThread @Inject internal constructor(
fabricPrivacy.logException(e)
aapsLogger.debug(autosensDataTable.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)
rxBus.send(EventNewNotification(notification))
sp.putBoolean("log_AUTOSENS", true)
@ -258,22 +260,27 @@ class IobCobThread @Inject internal constructor(
}
previous = 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)
aapsLogger.debug(LTag.AUTOSENS, "Sensitivity result: $sensitivity")
autosensData.autosensResult = sensitivity
aapsLogger.debug(LTag.AUTOSENS, autosensData.toString())
}
iobCobCalculatorPlugin.ads = ads
data.iobCobCalculatorPlugin.ads = ads
Thread {
SystemClock.sleep(1000)
rxBus.send(EventAutosensCalculationFinished(cause))
rxBus.send(EventAutosensCalculationFinished(data.cause))
}.start()
} finally {
mWakeLock?.release()
rxBus.send(EventIobCalculationProgress("", cause))
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread ended: $from")
rxBus.send(EventIobCalculationProgress(CalculationWorkflow.ProgressData.IOB_COB_OREF, 100, data.cause))
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread ended: ${data.from}")
profiler.log(LTag.AUTOSENS, "IobCobThread", start)
}
return Result.success()
}
}

View file

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

View file

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

View file

@ -53,8 +53,6 @@ class SensitivityOref1Plugin @Inject constructor(
) {
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()
if (profile == null) {
aapsLogger.error("No profile")
@ -204,6 +202,8 @@ class SensitivityOref1Plugin @Inject constructor(
return output
}
override fun maxAbsorptionHours(): Double = sp.getDouble(R.string.key_absorption_cutoff, Constants.DEFAULT_MAX_ABSORPTION_TIME)
override fun configuration(): JSONObject {
val c = JSONObject()
try {

View file

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

View file

@ -1,10 +1,9 @@
package info.nightscout.androidaps.plugins.source
import android.graphics.Paint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.util.SparseArray
import android.view.*
import androidx.core.util.forEach
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import dagger.android.support.DaggerFragment
@ -26,6 +25,7 @@ import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.utils.ActionModeHelper
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.T
@ -54,11 +54,10 @@ class BGSourceFragment : DaggerFragment() {
private val disposable = CompositeDisposable()
private val millsToThePast = T.hours(36).msecs()
private lateinit var actionHelper: ActionModeHelper<GlucoseValue>
private var _binding: BgsourceFragmentBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
// This property is only valid between onCreateView and onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
@ -66,6 +65,10 @@ class BGSourceFragment : DaggerFragment() {
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(actionHelper.inMenu)
binding.recyclerview.setHasFixedSize(true)
binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
@ -94,10 +97,21 @@ class BGSourceFragment : DaggerFragment() {
@Synchronized
override fun onPause() {
actionHelper.finish()
disposable.clear()
super.onPause()
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
actionHelper.onCreateOptionsMenu(menu, inflater)
}
override fun onPrepareOptionsMenu(menu: Menu) {
super.onPrepareOptionsMenu(menu)
actionHelper.onPrepareOptionsMenu(menu)
}
@Synchronized
override fun onDestroyView() {
super.onDestroyView()
@ -105,6 +119,9 @@ class BGSourceFragment : DaggerFragment() {
_binding = null
}
override fun onOptionsItemSelected(item: MenuItem) =
actionHelper.onOptionsItemSelected(item)
inner class RecyclerViewAdapter internal constructor(private var glucoseValues: List<GlucoseValue>) : RecyclerView.Adapter<RecyclerViewAdapter.GlucoseValuesViewHolder>() {
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): GlucoseValuesViewHolder {
@ -116,58 +133,83 @@ class BGSourceFragment : DaggerFragment() {
val glucoseValue = glucoseValues[position]
holder.binding.ns.visibility = (glucoseValue.interfaceIDs.nightscoutId != null).toVisibility()
holder.binding.invalid.visibility = (!glucoseValue.isValid).toVisibility()
val sameDayPrevious = position > 0 && dateUtil.isSameDay(glucoseValue.timestamp, glucoseValues[position-1].timestamp)
holder.binding.date.visibility = sameDayPrevious.not().toVisibility()
holder.binding.date.text = dateUtil.dateString(glucoseValue.timestamp)
val newDay = position == 0 || !dateUtil.isSameDay(glucoseValue.timestamp, glucoseValues[position - 1].timestamp)
holder.binding.date.visibility = newDay.toVisibility()
holder.binding.date.text = if (newDay) dateUtil.dateStringRelative(glucoseValue.timestamp, rh) else ""
holder.binding.time.text = dateUtil.timeString(glucoseValue.timestamp)
holder.binding.value.text = glucoseValue.valueToUnitsString(profileFunction.getUnits())
holder.binding.direction.setImageResource(glucoseValue.trendArrow.directionToIcon())
holder.binding.remove.tag = glucoseValue
if (position > 0) {
val previous = glucoseValues[position - 1]
val diff = previous.timestamp - glucoseValue.timestamp
if (diff < T.secs(20).msecs())
holder.binding.root.setBackgroundColor(rh.gc(R.color.errorAlertBackground))
holder.binding.root.setBackgroundColor(rh.gac(context, R.attr.bgsourceError))
}
holder.binding.root.setOnLongClickListener {
if (actionHelper.startRemove()) {
holder.binding.cbRemove.toggle()
actionHelper.updateSelection(position, glucoseValue, holder.binding.cbRemove.isChecked)
return@setOnLongClickListener true
}
false
}
holder.binding.root.setOnClickListener {
if (actionHelper.isRemoving) {
holder.binding.cbRemove.toggle()
actionHelper.updateSelection(position, glucoseValue, holder.binding.cbRemove.isChecked)
}
}
holder.binding.cbRemove.setOnCheckedChangeListener { _, value ->
actionHelper.updateSelection(position, glucoseValue, value)
}
holder.binding.cbRemove.isChecked = actionHelper.isSelected(position)
holder.binding.cbRemove.visibility = actionHelper.isRemoving.toVisibility()
}
override fun getItemCount(): Int = glucoseValues.size
override fun getItemCount() = glucoseValues.size
inner class GlucoseValuesViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val binding = BgsourceItemBinding.bind(view)
}
}
init {
binding.remove.paintFlags = binding.remove.paintFlags or Paint.UNDERLINE_TEXT_FLAG
binding.remove.setOnClickListener { v: View ->
val glucoseValue = v.tag as GlucoseValue
activity?.let { activity ->
val text = dateUtil.dateAndTimeString(glucoseValue.timestamp) + "\n" + glucoseValue.valueToUnitsString(profileFunction.getUnits())
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), text, Runnable {
val source = when ((activePlugin.activeBgSource as PluginBase).pluginDescription.pluginName) {
R.string.dexcom_app_patched -> Sources.Dexcom
R.string.eversense -> Sources.Eversense
R.string.Glimp -> Sources.Glimp
R.string.MM640g -> Sources.MM640g
R.string.nsclientbg -> Sources.NSClientSource
R.string.poctech -> Sources.PocTech
R.string.tomato -> Sources.Tomato
R.string.glunovo -> Sources.Glunovo
R.string.xdrip -> Sources.Xdrip
else -> Sources.Unknown
}
uel.log(
Action.BG_REMOVED, source,
ValueWithUnit.Timestamp(glucoseValue.timestamp)
)
repository.runTransactionForResult(InvalidateGlucoseValueTransaction(glucoseValue.id))
.doOnError { aapsLogger.error(LTag.DATABASE, "Error while invalidating BG value", it) }
.blockingGet()
.also { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated bg $it") } }
})
private fun getConfirmationText(selectedItems: SparseArray<GlucoseValue>): String {
if (selectedItems.size() == 1) {
val glucoseValue = selectedItems.valueAt(0)
return dateUtil.dateAndTimeString(glucoseValue.timestamp) + "\n" + glucoseValue.valueToUnitsString(profileFunction.getUnits())
}
return rh.gs(R.string.confirm_remove_multiple_items, selectedItems.size())
}
private fun removeSelected(selectedItems: SparseArray<GlucoseValue>) {
activity?.let { activity ->
OKDialog.showConfirmation(activity, rh.gs(R.string.removerecord), getConfirmationText(selectedItems), Runnable {
selectedItems.forEach { _, glucoseValue ->
val source = when ((activePlugin.activeBgSource as PluginBase).pluginDescription.pluginName) {
R.string.dexcom_app_patched -> Sources.Dexcom
R.string.eversense -> Sources.Eversense
R.string.Glimp -> Sources.Glimp
R.string.MM640g -> Sources.MM640g
R.string.nsclientbg -> Sources.NSClientSource
R.string.poctech -> Sources.PocTech
R.string.tomato -> Sources.Tomato
R.string.glunovo -> Sources.Glunovo
R.string.xdrip -> Sources.Xdrip
else -> Sources.Unknown
}
uel.log(
Action.BG_REMOVED, source,
ValueWithUnit.Timestamp(glucoseValue.timestamp)
)
repository.runTransactionForResult(InvalidateGlucoseValueTransaction(glucoseValue.id))
.doOnError { aapsLogger.error(LTag.DATABASE, "Error while invalidating BG value", it) }
.blockingGet()
.also { result -> result.invalidated.forEach { aapsLogger.debug(LTag.DATABASE, "Invalidated bg $it") } }
}
}
actionHelper.finish()
})
}
}
}

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