diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..23ab6f030d --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,8 @@ +version: 2 +updates: +- package-ecosystem: gradle + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 + target-branch: dev diff --git a/.gitignore b/.gitignore index 961dd40194..affd38763c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .gradle /local.properties .DS_Store +app/jacoco.exec /build /captures *.apk diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2ccca5cea7..381dffa690 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -36,7 +36,7 @@ Translations ------------ * If possible, always use Android translation mechanism (with strings.xml and @strings/id) instead of hardcoded texts -* Provide only English strings - all other languages will be crowd translated via Crowdn https://translations.androidaps.org/ +* Provide only English strings - all other languages will be crowd translated via Crowdin (https://crowdin.com/project/androidaps and https://crowdin.com/project/androidapsdocs) Hints ----- diff --git a/app/build.gradle b/app/build.gradle index c9e31881d3..45e91a54d6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,6 +1,5 @@ buildscript { repositories { - jcenter() maven { url "https://plugins.gradle.org/m2/" } // jacoco 0.2 } @@ -11,7 +10,6 @@ buildscript { } apply plugin: 'com.android.application' apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' apply plugin: 'com.google.gms.google-services' //apply plugin: 'jacoco-android' @@ -23,7 +21,6 @@ jacoco { } repositories { - jcenter { url "https://jcenter.bintray.com/" } mavenCentral() google() } @@ -87,7 +84,7 @@ def gitAvailable = { -> } -def allCommited = { -> +def allCommitted = { -> StringBuilder stringBuilder = new StringBuilder() try { def stdout = new ByteArrayOutputStream() @@ -116,15 +113,16 @@ android { ndkVersion "21.1.6352462" defaultConfig { - minSdkVersion 24 + minSdkVersion 26 targetSdkVersion 28 multiDexEnabled true versionCode 1500 - version "2.7.2-dev" + version "2.8.2.1-dev-a" buildConfigField "String", "VERSION", '"' + version + '"' buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"' buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"' buildConfigField "String", "HEAD", '"' + generateGitBuild() + '"' + buildConfigField "String", "COMMITTED", '"' + allCommitted() + '"' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" // if you change minSdkVersion to less than 11, you need to change executeTask for wear @@ -156,8 +154,8 @@ android { ext.enableCrashlytics = false } } + flavorDimensions "standard" productFlavors { - flavorDimensions "standard" full { applicationId "info.nightscout.androidaps" dimension "standard" @@ -218,9 +216,6 @@ android { useLibrary "org.apache.http.legacy" - configurations.all { - resolutionStrategy.force 'com.google.code.findbugs:jsr305:1.3.9' - } } allprojects { @@ -235,34 +230,34 @@ dependencies { wearApp project(':wear') implementation project(':core') + implementation project(':database') implementation project(':dana') implementation project(':danars') implementation project(':danar') implementation project(':rileylink') implementation project(':medtronic') - implementation project(':omnipod') + implementation project(':omnipod-eros') + implementation project(':omnipod-dash') implementation fileTree(include: ['*.jar'], dir: 'libs') testImplementation "junit:junit:$junit_version" - testImplementation 'org.json:json:20200518' + testImplementation 'org.json:json:20201115' testImplementation "org.mockito:mockito-core:${mockitoVersion}" testImplementation "org.powermock:powermock-api-mockito2:${powermockVersion}" testImplementation "org.powermock:powermock-module-junit4-rule-agent:${powermockVersion}" testImplementation "org.powermock:powermock-module-junit4-rule:${powermockVersion}" testImplementation "org.powermock:powermock-module-junit4:${powermockVersion}" - testImplementation 'joda-time:joda-time:2.10.6' - testImplementation('com.google.truth:truth:1.0.1') { + testImplementation "joda-time:joda-time:$jodatime_version" + testImplementation('com.google.truth:truth:1.1.2') { exclude group: "com.google.guava", module: "guava" - exclude group: "com.google.code.findbugs", module: "jsr305" } testImplementation "org.skyscreamer:jsonassert:1.5.0" testImplementation "org.hamcrest:hamcrest-all:1.3" - androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0-alpha03' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0-alpha04' androidTestImplementation "androidx.test.ext:junit:$androidx_junit" androidTestImplementation "androidx.test:rules:$androidx_rules" - androidTestImplementation 'com.google.code.findbugs:jsr305:3.0.2' /* Dagger2 - We are going to use dagger.android which includes * support for Activity and fragment injection so we need to include @@ -314,12 +309,12 @@ tasks.whenTaskAdded { task -> printf('--------------\n') printf('isMaster: %s\n', isMaster().toString()) printf('gitAvailable: %s\n', gitAvailable().toString()) -printf('allCommited: %s\n', allCommited().toString()) +printf('allCommitted: %s\n', allCommitted().toString()) printf('--------------\n') if (isMaster() && !gitAvailable()) { throw new GradleException('GIT system is not available. On Windows try to run Android Studio as an Administrator. Check if GIT is installed and Studio have permissions to use it') } -if (isMaster() && !allCommited()) { +if (isMaster() && !allCommitted()) { throw new GradleException('There are uncommitted changes. Clone sources again as described in wiki and do not allow gradle update') } diff --git a/app/libs/wearpreferenceactivity-0.5.0.aar b/app/libs/wearpreferenceactivity-0.5.0.aar new file mode 100644 index 0000000000..66b3cad816 Binary files /dev/null and b/app/libs/wearpreferenceactivity-0.5.0.aar differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e0e2a6b05b..cb897b727c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -135,10 +135,6 @@ android:resource="@xml/filepaths" /> - - diff --git a/app/src/main/assets/OpenAPSSMB/determine-basal.js b/app/src/main/assets/OpenAPSSMB/determine-basal.js index c4930b7789..13a220e528 100644 --- a/app/src/main/assets/OpenAPSSMB/determine-basal.js +++ b/app/src/main/assets/OpenAPSSMB/determine-basal.js @@ -151,7 +151,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ } } //cherry pick from oref upstream dev cb8e94990301277fb1016c778b4e9efa55a6edbc - if (bg <= 10 || bg === 38 || noise >= 3 || minAgo > 12 || minAgo < -5 || ( bg > 60 && glucose_status.delta == 0 && glucose_status.short_avgdelta > -1 && glucose_status.short_avgdelta < 1 && glucose_status.long_avgdelta > -1 && glucose_status.long_avgdelta < 1 ) ) { + if (bg <= 10 || bg === 38 || noise >= 3 || minAgo > 12 || minAgo < -5 || ( bg > 60 && glucose_status.delta == 0 && glucose_status.short_avgdelta > -1 && glucose_status.short_avgdelta < 1 && glucose_status.long_avgdelta > -1 && glucose_status.long_avgdelta < 1 ) && !isSaveCgmSource ) { if (currenttemp.rate > basal) { // high temp is running rT.reason += ". Replacing high temp basal of "+currenttemp.rate+" with neutral temp of "+basal; rT.deliverAt = deliverAt; diff --git a/app/src/main/assets/revoked_certs.txt b/app/src/main/assets/revoked_certs.txt index e0d12d2a46..473b5d0322 100644 --- a/app/src/main/assets/revoked_certs.txt +++ b/app/src/main/assets/revoked_certs.txt @@ -2,5 +2,9 @@ 51:6D:12:67:4C:27:F4:9B:9F:E5:42:9B:01:B3:98:E4:66:2B:85:B7:A8:DD:70:32:B7:6A:D7:97:9A:0D:97:10 #Leaked 55:5D:70:C9:BE:10:41:7E:4B:01:A9:C4:C6:44:4A:F8:69:71:35:25:ED:95:23:16:C7:15:E8:EB:C6:08:FC:B1 +# àqΣnΖ`ZϼγwÛ/τàΒϳ9Φ'$ΑϵžλUΛ`ÆÌΣЃA E0:71:A3:6E:96:60:5A:FC:B3:77:DB:2F:C4:E0:92:F3:39:A6:27:24:91:F5:7E:BB:55:9B:60:C6:CC:A3:03:41 +# 2ΙšÄΠΒϨÒÇeЄtЄЗž-*Ж*ZcHijЊÄœ<|x"Ε 32:99:61:C4:A0:92:E8:D2:C7:65:04:74:04:17:7E:2D:2A:16:2A:5A:63:48:69:6A:0A:C4:53:3C:7C:78:22:95 +# mςRr¡ЇζΛLφK&ĐΨ5CnЎϴPDñЍŒkϼ{ΨδwВb +6D:C2:52:72:A1:07:B6:9B:4C:C6:4B:26:10:A8:35:43:6E:0E:F4:50:44:F1:0D:52:6B:FC:7B:A8:B4:77:12:62 \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/Config.kt b/app/src/main/java/info/nightscout/androidaps/Config.kt index c5edf0c438..a598adc440 100644 --- a/app/src/main/java/info/nightscout/androidaps/Config.kt +++ b/app/src/main/java/info/nightscout/androidaps/Config.kt @@ -1,15 +1,21 @@ package info.nightscout.androidaps +import android.os.Build import info.nightscout.androidaps.interfaces.ConfigInterface import javax.inject.Inject import javax.inject.Singleton @Singleton -class Config @Inject constructor(): ConfigInterface{ +class Config @Inject constructor() : ConfigInterface { override val SUPPORTEDNSVERSION = 1002 // 0.10.00 override val APS = BuildConfig.FLAVOR == "full" override val NSCLIENT = BuildConfig.FLAVOR == "nsclient" || BuildConfig.FLAVOR == "nsclient2" override val PUMPCONTROL = BuildConfig.FLAVOR == "pumpcontrol" override val PUMPDRIVERS = BuildConfig.FLAVOR == "full" || BuildConfig.FLAVOR == "pumpcontrol" + override val FLAVOR = BuildConfig.FLAVOR + override val VERSION_NAME = BuildConfig.VERSION_NAME + + override val currentDeviceModelString = + Build.MANUFACTURER + " " + Build.MODEL + " (" + Build.DEVICE + ")" } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/MainActivity.kt b/app/src/main/java/info/nightscout/androidaps/MainActivity.kt index 951638d9ae..09505d0e18 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/MainActivity.kt @@ -2,7 +2,6 @@ package info.nightscout.androidaps import android.content.Context import android.content.Intent -import android.content.pm.PackageManager import android.graphics.Rect import android.os.Bundle import android.os.PersistableBundle @@ -22,9 +21,9 @@ import android.widget.TextView import androidx.appcompat.app.ActionBarDrawerToggle import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.Toolbar -import androidx.core.app.ActivityCompat import androidx.viewpager2.widget.ViewPager2 import com.google.android.material.tabs.TabLayoutMediator +import com.google.firebase.crashlytics.FirebaseCrashlytics import com.joanzapata.iconify.Iconify import com.joanzapata.iconify.fonts.FontAwesomeModule import dev.doubledot.doki.ui.DokiActivity @@ -33,6 +32,7 @@ import info.nightscout.androidaps.activities.PreferencesActivity import info.nightscout.androidaps.activities.ProfileHelperActivity import info.nightscout.androidaps.activities.SingleFragmentActivity import info.nightscout.androidaps.activities.StatsActivity +import info.nightscout.androidaps.databinding.ActivityMainBinding import info.nightscout.androidaps.events.EventAppExit import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.events.EventRebuildTabs @@ -46,8 +46,6 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.constraints.signatureVerifier.SignatureVerifierPlugin import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerUtils -import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefs -import info.nightscout.androidaps.plugins.general.maintenance.PrefsFileContract import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin import info.nightscout.androidaps.setupwizard.SetupWizardActivity @@ -59,13 +57,11 @@ import info.nightscout.androidaps.utils.extensions.isRunningRealPumpTest import info.nightscout.androidaps.utils.locale.LocaleHelper import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.resources.IconsProvider -import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.tabs.TabPageAdapter import info.nightscout.androidaps.utils.ui.UIRunnable -import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable -import kotlinx.android.synthetic.main.activity_main.* import java.util.* import javax.inject.Inject import kotlin.system.exitProcess @@ -75,10 +71,10 @@ class MainActivity : NoSplashAppCompatActivity() { private val disposable = CompositeDisposable() @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var androidPermission: AndroidPermission @Inject lateinit var sp: SP - @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var versionCheckerUtils: VersionCheckerUtils @Inject lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin @Inject lateinit var loopPlugin: LoopPlugin @@ -91,40 +87,36 @@ class MainActivity : NoSplashAppCompatActivity() { @Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var signatureVerifierPlugin: SignatureVerifierPlugin @Inject lateinit var config: Config - @Inject lateinit var importExportPrefs: ImportExportPrefs private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle private var pluginPreferencesMenuItem: MenuItem? = null private var menu: Menu? = null - val callForPrefFile = registerForActivityResult(PrefsFileContract()) { - it?.let { - importExportPrefs.importSharedPreferences(this, it) - } - } + private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Iconify.with(FontAwesomeModule()) LocaleHelper.update(applicationContext) - setContentView(R.layout.activity_main) - setSupportActionBar(toolbar) + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + setSupportActionBar(binding.toolbar) supportActionBar?.setDisplayShowTitleEnabled(false) supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setHomeButtonEnabled(true) - actionBarDrawerToggle = ActionBarDrawerToggle(this, main_drawer_layout, R.string.open_navigation, R.string.close_navigation).also { - main_drawer_layout.addDrawerListener(it) + actionBarDrawerToggle = ActionBarDrawerToggle(this, binding.mainDrawerLayout, R.string.open_navigation, R.string.close_navigation).also { + binding.mainDrawerLayout.addDrawerListener(it) it.syncState() } // initialize screen wake lock processPreferenceChange(EventPreferenceChange(resourceHelper.gs(R.string.key_keep_screen_on))) - main_pager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { + binding.mainPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { override fun onPageScrollStateChanged(state: Int) {} override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {} override fun onPageSelected(position: Int) { setPluginPreferenceMenuName() - checkPluginPreferences(main_pager) + checkPluginPreferences(binding.mainPager) } }) @@ -134,7 +126,7 @@ class MainActivity : NoSplashAppCompatActivity() { setupViews() disposable.add(rxBus .toObservable(EventRebuildTabs::class.java) - .observeOn(AndroidSchedulers.mainThread()) + .observeOn(aapsSchedulers.main) .subscribe({ if (it.recreate) recreate() else setupViews() @@ -143,7 +135,7 @@ class MainActivity : NoSplashAppCompatActivity() { ) disposable.add(rxBus .toObservable(EventPreferenceChange::class.java) - .observeOn(AndroidSchedulers.mainThread()) + .observeOn(aapsSchedulers.main) .subscribe({ processPreferenceChange(it) }, fabricPrivacy::logException) ) if (!sp.getBoolean(R.string.key_setupwizard_processed, false) && !isRunningRealPumpTest()) { @@ -195,69 +187,53 @@ class MainActivity : NoSplashAppCompatActivity() { private fun setupViews() { // Menu val pageAdapter = TabPageAdapter(this) - main_navigation_view.setNavigationItemSelectedListener { true } - val menu = main_navigation_view.menu.also { it.clear() } + binding.mainNavigationView.setNavigationItemSelectedListener { true } + val menu = binding.mainNavigationView.menu.also { it.clear() } for (p in activePlugin.getPluginsList()) { pageAdapter.registerNewFragment(p) if (p.isEnabled() && p.hasFragment() && !p.isFragmentVisible() && !p.pluginDescription.neverVisible) { val menuItem = menu.add(p.name) menuItem.isCheckable = true - if(p.menuIcon != -1) { + if (p.menuIcon != -1) { menuItem.setIcon(p.menuIcon) - } else - { + } else { menuItem.setIcon(R.drawable.ic_settings) } menuItem.setOnMenuItemClickListener { val intent = Intent(this, SingleFragmentActivity::class.java) intent.putExtra("plugin", activePlugin.getPluginsList().indexOf(p)) startActivity(intent) - main_drawer_layout.closeDrawers() + binding.mainDrawerLayout.closeDrawers() true } } } - main_pager.adapter = pageAdapter - main_pager.offscreenPageLimit = 8 // This may cause more memory consumption - checkPluginPreferences(main_pager) + binding.mainPager.adapter = pageAdapter + binding.mainPager.offscreenPageLimit = 8 // This may cause more memory consumption + checkPluginPreferences(binding.mainPager) // Tabs if (sp.getBoolean(R.string.key_short_tabtitles, false)) { - tabs_normal.visibility = View.GONE - tabs_compact.visibility = View.VISIBLE - toolbar.layoutParams = LinearLayout.LayoutParams(Toolbar.LayoutParams.MATCH_PARENT, resources.getDimension(R.dimen.compact_height).toInt()) - TabLayoutMediator(tabs_compact, main_pager) { tab, position -> - tab.text = (main_pager.adapter as TabPageAdapter).getPluginAt(position).nameShort + binding.tabsNormal.visibility = View.GONE + binding.tabsCompact.visibility = View.VISIBLE + binding.toolbar.layoutParams = LinearLayout.LayoutParams(Toolbar.LayoutParams.MATCH_PARENT, resources.getDimension(R.dimen.compact_height).toInt()) + TabLayoutMediator(binding.tabsCompact, binding.mainPager) { tab, position -> + tab.text = (binding.mainPager.adapter as TabPageAdapter).getPluginAt(position).nameShort }.attach() } else { - tabs_normal.visibility = View.VISIBLE - tabs_compact.visibility = View.GONE + binding.tabsNormal.visibility = View.VISIBLE + binding.tabsCompact.visibility = View.GONE val typedValue = TypedValue() if (theme.resolveAttribute(R.attr.actionBarSize, typedValue, true)) { - toolbar.layoutParams = LinearLayout.LayoutParams(Toolbar.LayoutParams.MATCH_PARENT, + binding.toolbar.layoutParams = LinearLayout.LayoutParams(Toolbar.LayoutParams.MATCH_PARENT, TypedValue.complexToDimensionPixelSize(typedValue.data, resources.displayMetrics)) } - TabLayoutMediator(tabs_normal, main_pager) { tab, position -> - tab.text = (main_pager.adapter as TabPageAdapter).getPluginAt(position).name + TabLayoutMediator(binding.tabsNormal, binding.mainPager) { tab, position -> + tab.text = (binding.mainPager.adapter as TabPageAdapter).getPluginAt(position).name }.attach() } } - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - if (permissions.isNotEmpty()) { - if (ActivityCompat.checkSelfPermission(this, permissions[0]) == PackageManager.PERMISSION_GRANTED) { - when (requestCode) { - AndroidPermission.CASE_STORAGE -> //show dialog after permission is granted - OKDialog.show(this, "", resourceHelper.gs(R.string.alert_dialog_storage_permission_text)) - - AndroidPermission.CASE_LOCATION, AndroidPermission.CASE_SMS, AndroidPermission.CASE_BATTERY, AndroidPermission.CASE_PHONE_STATE, AndroidPermission.CASE_SYSTEM_WINDOW -> { - } - } - } - } - } - override fun dispatchTouchEvent(event: MotionEvent): Boolean { if (event.action == MotionEvent.ACTION_DOWN) { val v = currentFocus @@ -275,16 +251,18 @@ class MainActivity : NoSplashAppCompatActivity() { } private fun setPluginPreferenceMenuName() { - val plugin = (main_pager.adapter as TabPageAdapter).getPluginAt(main_pager.currentItem) - this.menu?.findItem(R.id.nav_plugin_preferences)?.title = resourceHelper.gs(R.string.nav_preferences_plugin, plugin.name) + if (binding.mainPager.currentItem >= 0) { + val plugin = (binding.mainPager.adapter as TabPageAdapter).getPluginAt(binding.mainPager.currentItem) + this.menu?.findItem(R.id.nav_plugin_preferences)?.title = resourceHelper.gs(R.string.nav_preferences_plugin, plugin.name) + } } override fun onCreateOptionsMenu(menu: Menu): Boolean { - this.menu = menu + this.menu = menu menuInflater.inflate(R.menu.menu_main, menu) pluginPreferencesMenuItem = menu.findItem(R.id.nav_plugin_preferences) setPluginPreferenceMenuName() - checkPluginPreferences(main_pager) + checkPluginPreferences(binding.mainPager) return true } @@ -316,6 +294,7 @@ class MainActivity : NoSplashAppCompatActivity() { message += "Flavor: ${BuildConfig.FLAVOR}${BuildConfig.BUILD_TYPE}\n" message += "${resourceHelper.gs(R.string.configbuilder_nightscoutversion_label)} ${nsSettingsStatus.nightscoutVersionName}" if (buildHelper.isEngineeringMode()) message += "\n${resourceHelper.gs(R.string.engineering_mode_enabled)}" + if (!fabricPrivacy.fabricEnabled()) message += "\n${resourceHelper.gs(R.string.fabric_upload_disabled)}" message += resourceHelper.gs(R.string.about_link_urls) val messageSpanned = SpannableString(message) Linkify.addLinks(messageSpanned, Linkify.WEB_URLS) @@ -341,7 +320,7 @@ class MainActivity : NoSplashAppCompatActivity() { } R.id.nav_plugin_preferences -> { - val plugin = (main_pager.adapter as TabPageAdapter).getPluginAt(main_pager.currentItem) + val plugin = (binding.mainPager.adapter as TabPageAdapter).getPluginAt(binding.mainPager.currentItem) protectionCheck.queryProtection(this, ProtectionCheck.Protection.PREFERENCES, { val i = Intent(this, PreferencesActivity::class.java) i.putExtra("id", plugin.preferencesId) @@ -396,6 +375,12 @@ class MainActivity : NoSplashAppCompatActivity() { fabricPrivacy.firebaseAnalytics.setUserProperty("Profile", activePlugin.activeProfileInterface.javaClass.simpleName) activePlugin.activeSensitivity.let { fabricPrivacy.firebaseAnalytics.setUserProperty("Sensitivity", it::class.java.simpleName) } activePlugin.activeInsulin.let { fabricPrivacy.firebaseAnalytics.setUserProperty("Insulin", it::class.java.simpleName) } + // Add to crash log too + FirebaseCrashlytics.getInstance().setCustomKey("HEAD", BuildConfig.HEAD) + FirebaseCrashlytics.getInstance().setCustomKey("Version", BuildConfig.VERSION) + FirebaseCrashlytics.getInstance().setCustomKey("Remote", remote) + FirebaseCrashlytics.getInstance().setCustomKey("Committed", BuildConfig.COMMITTED) + FirebaseCrashlytics.getInstance().setCustomKey("Hash", hashes[0]) } -} \ No newline at end of file +} diff --git a/app/src/main/java/info/nightscout/androidaps/MainApp.java b/app/src/main/java/info/nightscout/androidaps/MainApp.java index ca60ca93d4..56c8b99a0a 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainApp.java +++ b/app/src/main/java/info/nightscout/androidaps/MainApp.java @@ -3,7 +3,6 @@ package info.nightscout.androidaps; import android.bluetooth.BluetoothDevice; import android.content.Intent; import android.content.IntentFilter; -import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.wifi.WifiManager; @@ -19,6 +18,9 @@ import javax.inject.Inject; import dagger.android.AndroidInjector; import dagger.android.DaggerApplication; +import info.nightscout.androidaps.database.AppRepository; +import info.nightscout.androidaps.database.transactions.VersionChangeTransaction; +import info.nightscout.androidaps.db.CompatDBHelper; import info.nightscout.androidaps.db.DatabaseHelper; import info.nightscout.androidaps.db.StaticInjector; import info.nightscout.androidaps.dependencyInjection.DaggerAppComponent; @@ -39,14 +41,14 @@ import info.nightscout.androidaps.services.Intents; import info.nightscout.androidaps.utils.ActivityMonitor; import info.nightscout.androidaps.utils.locale.LocaleHelper; import info.nightscout.androidaps.utils.sharedPreferences.SP; +import io.reactivex.disposables.CompositeDisposable; public class MainApp extends DaggerApplication { - static MainApp sInstance; - private static Resources sResources; - static DatabaseHelper sDatabaseHelper = null; + private final CompositeDisposable disposable = new CompositeDisposable(); + @Inject PluginStore pluginStore; @Inject AAPSLogger aapsLogger; @Inject ActivityMonitor activityMonitor; @@ -58,6 +60,8 @@ public class MainApp extends DaggerApplication { @Inject ConfigBuilderPlugin configBuilderPlugin; @Inject KeepAliveReceiver.KeepAliveManager keepAliveManager; @Inject List plugins; + @Inject CompatDBHelper compatDBHelper; + @Inject AppRepository repository; @Inject StaticInjector staticInjector; // TODO avoid , here fake only to initialize @@ -66,10 +70,8 @@ public class MainApp extends DaggerApplication { super.onCreate(); aapsLogger.debug("onCreate"); - sInstance = this; - sResources = getResources(); LocaleHelper.INSTANCE.update(this); - sDatabaseHelper = OpenHelperManager.getHelper(sInstance, DatabaseHelper.class); + sDatabaseHelper = OpenHelperManager.getHelper(this, DatabaseHelper.class); /* Thread.setDefaultUncaughtExceptionHandler((thread, ex) -> { if (ex instanceof InternalError) { @@ -79,6 +81,15 @@ public class MainApp extends DaggerApplication { aapsLogger.error("Uncaught exception crashing app", ex); }); */ + String gitRemote = BuildConfig.REMOTE; + String commitHash = BuildConfig.HEAD; + if (gitRemote.contains("NoGitSystemAvailable")) { + gitRemote = null; + commitHash = null; + } + disposable.add(repository.runTransaction(new VersionChangeTransaction(BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE, gitRemote, commitHash)).subscribe()); + disposable.add(compatDBHelper.dbChangeDisposable()); + registerActivityLifecycleCallbacks(activityMonitor); JodaTimeAndroid.init(this); @@ -121,7 +132,6 @@ public class MainApp extends DaggerApplication { .build(); } - @SuppressWarnings("deprecation") private void registerLocalBroadcastReceiver() { IntentFilter filter = new IntentFilter(); filter.addAction(Intents.ACTION_NEW_TREATMENT); diff --git a/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt index 16a870ce61..aed679673b 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/MyPreferenceFragment.kt @@ -420,6 +420,6 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang fun setFilter(filter: String) { this.filter = filter - updateFilterVisibility(filter, preferenceScreen) + preferenceManager?.preferenceScreen?.let { updateFilterVisibility(filter, it) } } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.kt index 3a2fda604b..d209670b01 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/PreferencesActivity.kt @@ -7,23 +7,22 @@ import android.text.TextWatcher import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceScreen import info.nightscout.androidaps.R +import info.nightscout.androidaps.databinding.ActivityPreferencesBinding import info.nightscout.androidaps.utils.locale.LocaleHelper -import info.nightscout.androidaps.utils.resources.ResourceHelper -import kotlinx.android.synthetic.main.activity_preferences.* -import javax.inject.Inject class PreferencesActivity : NoSplashAppCompatActivity(), PreferenceFragmentCompat.OnPreferenceStartScreenCallback { - @Inject lateinit var resourceHelper: ResourceHelper + private var preferenceId = 0 + private var myPreferenceFragment: MyPreferenceFragment? = null - var preferenceId = 0 - var myPreferenceFragment: MyPreferenceFragment? = null + private lateinit var binding: ActivityPreferencesBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_preferences) + binding = ActivityPreferencesBinding.inflate(layoutInflater) + setContentView(binding.root) - pref_filter.addTextChangedListener(object : TextWatcher { + binding.prefFilter.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) { filterPreferences() @@ -37,23 +36,21 @@ class PreferencesActivity : NoSplashAppCompatActivity(), PreferenceFragmentCompa supportActionBar?.setDisplayShowHomeEnabled(true) myPreferenceFragment = MyPreferenceFragment() preferenceId = intent.getIntExtra("id", -1) - val args = Bundle() - args.putInt("id", preferenceId) - args.putString("filter", pref_filter.text.toString()) - myPreferenceFragment?.arguments = args - supportFragmentManager.beginTransaction().replace(R.id.frame_layout, myPreferenceFragment!!).commit() + myPreferenceFragment?.arguments = Bundle().also { + it.putInt("id", preferenceId) + it.putString("filter", binding.prefFilter.text.toString()) + } + if (savedInstanceState == null) + supportFragmentManager.beginTransaction().replace(R.id.frame_layout, myPreferenceFragment!!).commit() } override fun onPreferenceStartScreen(caller: PreferenceFragmentCompat, pref: PreferenceScreen): Boolean { val fragment = MyPreferenceFragment() - val args = Bundle() - args.putString(PreferenceFragmentCompat.ARG_PREFERENCE_ROOT, pref.key) - args.putInt("id", preferenceId) - fragment.arguments = args - supportFragmentManager.beginTransaction() - .replace(R.id.frame_layout, fragment, pref.key) - .addToBackStack(pref.key) - .commit() + fragment.arguments = Bundle().also { + it.putString(PreferenceFragmentCompat.ARG_PREFERENCE_ROOT, pref.key) + it.putInt("id", preferenceId) + } + supportFragmentManager.beginTransaction().replace(R.id.frame_layout, fragment, pref.key).addToBackStack(pref.key).commit() return true } @@ -62,6 +59,6 @@ class PreferencesActivity : NoSplashAppCompatActivity(), PreferenceFragmentCompa } private fun filterPreferences() { - myPreferenceFragment?.setFilter(pref_filter.text.toString()) + myPreferenceFragment?.setFilter(binding.prefFilter.text.toString()) } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/activities/ProfileHelperActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/ProfileHelperActivity.kt index efb7e57b19..9950f0818a 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/ProfileHelperActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/ProfileHelperActivity.kt @@ -10,6 +10,7 @@ import info.nightscout.androidaps.R import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.data.defaultProfile.DefaultProfile import info.nightscout.androidaps.data.defaultProfile.DefaultProfileDPV +import info.nightscout.androidaps.databinding.ActivityProfilehelperBinding import info.nightscout.androidaps.db.ProfileSwitch import info.nightscout.androidaps.dialogs.ProfileViewerDialog import info.nightscout.androidaps.interfaces.ActivePluginProvider @@ -24,15 +25,13 @@ import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.extensions.toVisibility -import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.stats.TddCalculator -import kotlinx.android.synthetic.main.activity_profilehelper.* import java.text.DecimalFormat import javax.inject.Inject class ProfileHelperActivity : NoSplashAppCompatActivity() { + @Inject lateinit var aapsLogger: AAPSLogger - @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var tddCalculator: TddCalculator @Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var defaultProfile: DefaultProfile @@ -65,27 +64,31 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { private lateinit var profileSwitch: List private val profileSwitchUsed = arrayOf(0, 0) + private lateinit var binding: ActivityProfilehelperBinding + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_profilehelper) - profilehelper_menu1.setOnClickListener { + binding = ActivityProfilehelperBinding.inflate(layoutInflater) + setContentView(binding.root) + + binding.menu1.setOnClickListener { switchTab(0, typeSelected[0]) } - profilehelper_menu2.setOnClickListener { + binding.menu2.setOnClickListener { switchTab(1, typeSelected[1]) } - profilehelper_profiletype.setOnClickListener { - PopupMenu(this, profilehelper_profiletype).apply { + binding.profiletype.setOnClickListener { + PopupMenu(this, binding.profiletype).apply { menuInflater.inflate(R.menu.menu_profilehelper, menu) setOnMenuItemClickListener { item -> - profilehelper_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) - R.id.menu_current -> switchTab(tabSelected, ProfileType.CURRENT) - R.id.menu_available -> switchTab(tabSelected, ProfileType.AVAILABLE_PROFILE) + R.id.menu_default -> switchTab(tabSelected, ProfileType.MOTOL_DEFAULT) + R.id.menu_default_dpv -> switchTab(tabSelected, ProfileType.DPV_DEFAULT) + R.id.menu_current -> switchTab(tabSelected, ProfileType.CURRENT) + R.id.menu_available -> switchTab(tabSelected, ProfileType.AVAILABLE_PROFILE) R.id.menu_profileswitch -> switchTab(tabSelected, ProfileType.PROFILE_SWITCH) } true @@ -97,12 +100,12 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { // Active profile profileList = activePlugin.activeProfileInterface.profile?.getProfileList() ?: ArrayList() - profilehelper_available_profile_list.setOnClickListener { - PopupMenu(this, profilehelper_available_profile_list).apply { + binding.availableProfileList.setOnClickListener { + PopupMenu(this, binding.availableProfileList).apply { var order = 0 for (name in profileList) menu.add(Menu.NONE, order, order++, name) setOnMenuItemClickListener { item -> - profilehelper_available_profile_list.setText(item.title) + binding.availableProfileList.setText(item.title) profileUsed[tabSelected] = item.itemId true } @@ -113,12 +116,12 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { // Profile switch profileSwitch = databaseHelper.getProfileSwitchData(dateUtil._now() - T.months(2).msecs(), true) - profilehelper_profileswitch_list.setOnClickListener { - PopupMenu(this, profilehelper_profileswitch_list).apply { + binding.profileswitchList.setOnClickListener { + PopupMenu(this, binding.profileswitchList).apply { var order = 0 for (name in profileSwitch) menu.add(Menu.NONE, order, order++, name.customizedName) setOnMenuItemClickListener { item -> - profilehelper_profileswitch_list.setText(item.title) + binding.profileswitchList.setText(item.title) profileSwitchUsed[tabSelected] = item.itemId true } @@ -127,7 +130,7 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { } // Default profile - profilehelper_copytolocalprofile.setOnClickListener { + binding.copytolocalprofile.setOnClickListener { val age = ageUsed[tabSelected] val weight = weightUsed[tabSelected] val tdd = tddUsed[tabSelected] @@ -142,31 +145,31 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { } } - profilehelper_age.setParams(0.0, 1.0, 18.0, 1.0, DecimalFormat("0"), false, null) - profilehelper_weight.setParams(0.0, 0.0, 150.0, 1.0, DecimalFormat("0"), false, null, object : TextWatcher { + binding.age.setParams(0.0, 1.0, 18.0, 1.0, DecimalFormat("0"), false, null) + binding.weight.setParams(0.0, 0.0, 150.0, 1.0, DecimalFormat("0"), false, null, object : TextWatcher { override fun afterTextChanged(s: Editable) {} override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - profilehelper_tdd_row.visibility = (profilehelper_weight.value == 0.0).toVisibility() + binding.tddRow.visibility = (binding.weight.value == 0.0).toVisibility() } }) - profilehelper_tdd.setParams(0.0, 0.0, 200.0, 1.0, DecimalFormat("0"), false, null, object : TextWatcher { + binding.tdd.setParams(0.0, 0.0, 200.0, 1.0, DecimalFormat("0"), false, null, object : TextWatcher { override fun afterTextChanged(s: Editable) {} override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - profilehelper_weight_row.visibility = (profilehelper_tdd.value == 0.0).toVisibility() + binding.weightRow.visibility = (binding.tdd.value == 0.0).toVisibility() } }) - profilehelper_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) - profilehelper_tdds.text = tddCalculator.stats() + binding.tdds.text = tddCalculator.stats() // Current profile - profilehelper_current_profile_text.text = profileFunction.getProfileName() + binding.currentProfileText.text = profileFunction.getProfileName() // General - profilehelper_compareprofile.setOnClickListener { + binding.compareprofile.setOnClickListener { storeValues() for (i in 0..1) { if (typeSelected[i] == ProfileType.MOTOL_DEFAULT) { @@ -221,28 +224,32 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { } private fun getProfile(age: Double, tdd: Double, weight: Double, basalPct: Double, tab: Int): Profile? = - when (typeSelected[tab]) { - ProfileType.MOTOL_DEFAULT -> defaultProfile.profile(age, tdd, weight, profileFunction.getUnits()) - ProfileType.DPV_DEFAULT -> defaultProfileDPV.profile(age, tdd, basalPct, profileFunction.getUnits()) - ProfileType.CURRENT -> profileFunction.getProfile()?.convertToNonCustomizedProfile() - ProfileType.AVAILABLE_PROFILE -> activePlugin.activeProfileInterface.profile?.getSpecificProfile(profileList[profileUsed[tab]].toString()) - ProfileType.PROFILE_SWITCH -> profileSwitch[profileSwitchUsed[tab]].profileObject?.convertToNonCustomizedProfile() + try { // profile must not exist + when (typeSelected[tab]) { + ProfileType.MOTOL_DEFAULT -> defaultProfile.profile(age, tdd, weight, profileFunction.getUnits()) + ProfileType.DPV_DEFAULT -> defaultProfileDPV.profile(age, tdd, basalPct, profileFunction.getUnits()) + ProfileType.CURRENT -> profileFunction.getProfile()?.convertToNonCustomizedProfile() + ProfileType.AVAILABLE_PROFILE -> activePlugin.activeProfileInterface.profile?.getSpecificProfile(profileList[profileUsed[tab]].toString()) + ProfileType.PROFILE_SWITCH -> profileSwitch[profileSwitchUsed[tab]].profileObject?.convertToNonCustomizedProfile() + } + } catch (e: Exception) { + null } private fun getProfileName(age: Double, tdd: Double, weight: Double, basalSumPct: Double, tab: Int): String = when (typeSelected[tab]) { - ProfileType.MOTOL_DEFAULT -> if (tdd > 0) resourceHelper.gs(R.string.formatwithtdd, age, tdd) else resourceHelper.gs(R.string.formatwithweight, age, weight) - ProfileType.DPV_DEFAULT -> resourceHelper.gs(R.string.formatwittddandpct, age, tdd, (basalSumPct * 100).toInt()) - ProfileType.CURRENT -> profileFunction.getProfileName() + ProfileType.MOTOL_DEFAULT -> if (tdd > 0) resourceHelper.gs(R.string.formatwithtdd, age, tdd) else resourceHelper.gs(R.string.formatwithweight, age, weight) + ProfileType.DPV_DEFAULT -> resourceHelper.gs(R.string.formatwittddandpct, age, tdd, (basalSumPct * 100).toInt()) + ProfileType.CURRENT -> profileFunction.getProfileName() ProfileType.AVAILABLE_PROFILE -> profileList[profileUsed[tab]].toString() - ProfileType.PROFILE_SWITCH -> profileSwitch[profileSwitchUsed[tab]].customizedName + ProfileType.PROFILE_SWITCH -> profileSwitch[profileSwitchUsed[tab]].customizedName } private fun storeValues() { - ageUsed[tabSelected] = profilehelper_age.value - weightUsed[tabSelected] = profilehelper_weight.value - tddUsed[tabSelected] = profilehelper_tdd.value - pctUsed[tabSelected] = profilehelper_basalpctfromtdd.value + ageUsed[tabSelected] = binding.age.value + weightUsed[tabSelected] = binding.weight.value + tddUsed[tabSelected] = binding.tdd.value + pctUsed[tabSelected] = binding.basalpctfromtdd.value } private fun switchTab(tab: Int, newContent: ProfileType, storeOld: Boolean = true) { @@ -252,10 +259,10 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { tabSelected = tab typeSelected[tabSelected] = newContent - profilehelper_profiletype_title.defaultHintTextColor = ColorStateList.valueOf(resourceHelper.gc(if (tab == 0) R.color.tabBgColorSelected else R.color.examinedProfile)) + binding.profiletypeTitle.defaultHintTextColor = ColorStateList.valueOf(resourceHelper.gc(if (tab == 0) R.color.tabBgColorSelected else R.color.examinedProfile)) // show new content - profilehelper_profiletype.setText( + binding.profiletype.setText( when (typeSelected[tabSelected]) { ProfileType.MOTOL_DEFAULT -> resourceHelper.gs(R.string.motoldefaultprofile) ProfileType.DPV_DEFAULT -> resourceHelper.gs(R.string.dpvdefaultprofile) @@ -263,26 +270,26 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { ProfileType.AVAILABLE_PROFILE -> resourceHelper.gs(R.string.availableprofile) ProfileType.PROFILE_SWITCH -> resourceHelper.gs(R.string.careportal_profileswitch) }) - profilehelper_default_profile.visibility = (newContent == ProfileType.MOTOL_DEFAULT || newContent == ProfileType.DPV_DEFAULT).toVisibility() - profilehelper_current_profile.visibility = (newContent == ProfileType.CURRENT).toVisibility() - profilehelper_available_profile.visibility = (newContent == ProfileType.AVAILABLE_PROFILE).toVisibility() - profilehelper_profile_switch.visibility = (newContent == ProfileType.PROFILE_SWITCH).toVisibility() + binding.defaultProfile.visibility = (newContent == ProfileType.MOTOL_DEFAULT || newContent == ProfileType.DPV_DEFAULT).toVisibility() + binding.currentProfile.visibility = (newContent == ProfileType.CURRENT).toVisibility() + binding.availableProfile.visibility = (newContent == ProfileType.AVAILABLE_PROFILE).toVisibility() + binding.profileSwitch.visibility = (newContent == ProfileType.PROFILE_SWITCH).toVisibility() // restore selected values - profilehelper_age.value = ageUsed[tabSelected] - profilehelper_weight.value = weightUsed[tabSelected] - profilehelper_tdd.value = tddUsed[tabSelected] - profilehelper_basalpctfromtdd.value = pctUsed[tabSelected] + binding.age.value = ageUsed[tabSelected] + binding.weight.value = weightUsed[tabSelected] + binding.tdd.value = tddUsed[tabSelected] + binding.basalpctfromtdd.value = pctUsed[tabSelected] - profilehelper_basalpctfromtdd_row.visibility = (newContent == ProfileType.DPV_DEFAULT).toVisibility() + binding.basalpctfromtddRow.visibility = (newContent == ProfileType.DPV_DEFAULT).toVisibility() if (profileList.isNotEmpty()) - profilehelper_available_profile_list.setText(profileList[profileUsed[tabSelected]].toString()) + binding.availableProfileList.setText(profileList[profileUsed[tabSelected]].toString()) if (profileSwitch.isNotEmpty()) - profilehelper_profileswitch_list.setText(profileSwitch[profileSwitchUsed[tabSelected]].customizedName) + binding.profileswitchList.setText(profileSwitch[profileSwitchUsed[tabSelected]].customizedName) } private fun setBackgroundColorOnSelected(tab: Int) { - profilehelper_menu1.setBackgroundColor(resourceHelper.gc(if (tab == 1) R.color.defaultbackground else R.color.tabBgColorSelected)) - profilehelper_menu2.setBackgroundColor(resourceHelper.gc(if (tab == 0) R.color.defaultbackground else R.color.examinedProfile)) + binding.menu1.setBackgroundColor(resourceHelper.gc(if (tab == 1) R.color.defaultbackground else R.color.tabBgColorSelected)) + binding.menu2.setBackgroundColor(resourceHelper.gc(if (tab == 0) R.color.defaultbackground else R.color.examinedProfile)) } } diff --git a/app/src/main/java/info/nightscout/androidaps/activities/RequestDexcomPermissionActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/RequestDexcomPermissionActivity.kt index a87224dc30..8eb93156b1 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/RequestDexcomPermissionActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/RequestDexcomPermissionActivity.kt @@ -15,6 +15,7 @@ class RequestDexcomPermissionActivity : DialogAppCompatActivity() { } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) finish() } diff --git a/app/src/main/java/info/nightscout/androidaps/activities/SingleFragmentActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/SingleFragmentActivity.kt index 862509249e..9b01150122 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/SingleFragmentActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/SingleFragmentActivity.kt @@ -5,29 +5,20 @@ import android.content.Intent import android.os.Bundle import android.view.Menu import android.view.MenuItem -import dagger.android.support.DaggerAppCompatActivity import info.nightscout.androidaps.R import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.plugins.configBuilder.PluginStore -import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefs -import info.nightscout.androidaps.plugins.general.maintenance.PrefsFileContract import info.nightscout.androidaps.utils.locale.LocaleHelper import info.nightscout.androidaps.utils.protection.ProtectionCheck import javax.inject.Inject -class SingleFragmentActivity : DaggerAppCompatActivity() { +class SingleFragmentActivity : DaggerAppCompatActivityWithResult() { + @Inject lateinit var pluginStore: PluginStore @Inject lateinit var protectionCheck: ProtectionCheck - @Inject lateinit var importExportPrefs: ImportExportPrefs private var plugin: PluginBase? = null - val callForPrefFile = registerForActivityResult(PrefsFileContract()) { - it?.let { - importExportPrefs.importSharedPreferences(this, it) - } - } - public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_single_fragment) diff --git a/app/src/main/java/info/nightscout/androidaps/activities/StatsActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/StatsActivity.kt index ca26266f8f..233c293f12 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/StatsActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/StatsActivity.kt @@ -2,35 +2,39 @@ package info.nightscout.androidaps.activities import android.os.Bundle import info.nightscout.androidaps.R +import info.nightscout.androidaps.databinding.ActivityStatsBinding +import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.utils.ActivityMonitor -import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.stats.TddCalculator import info.nightscout.androidaps.utils.stats.TirCalculator -import info.nightscout.androidaps.utils.resources.ResourceHelper -import kotlinx.android.synthetic.main.activity_stats.* import javax.inject.Inject class StatsActivity : NoSplashAppCompatActivity() { + @Inject lateinit var tddCalculator: TddCalculator @Inject lateinit var tirCalculator: TirCalculator - @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var activityMonitor: ActivityMonitor + @Inject lateinit var uel: UserEntryLogger + + private lateinit var binding: ActivityStatsBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_stats) + binding = ActivityStatsBinding.inflate(layoutInflater) + setContentView(binding.root) - stats_tdds.text = tddCalculator.stats() - stats_tir.text = tirCalculator.stats() - stats_activity.text = activityMonitor.stats() + binding.tdds.text = tddCalculator.stats() + binding.tir.text = tirCalculator.stats() + binding.activity.text = activityMonitor.stats() - ok.setOnClickListener { finish() } - stats_reset.setOnClickListener { - OKDialog.showConfirmation(this, resourceHelper.gs(R.string.doyouwantresetstats), Runnable { + binding.ok.setOnClickListener { finish() } + binding.reset.setOnClickListener { + OKDialog.showConfirmation(this, resourceHelper.gs(R.string.doyouwantresetstats)) { + uel.log("STATS RESET") activityMonitor.reset() recreate() - }) + } } } } diff --git a/app/src/main/java/info/nightscout/androidaps/activities/SurveyActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/SurveyActivity.kt index 1d03ddb344..5286afc33d 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/SurveyActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/SurveyActivity.kt @@ -6,6 +6,7 @@ import com.google.firebase.auth.FirebaseAuth import com.google.firebase.database.FirebaseDatabase import info.nightscout.androidaps.R import info.nightscout.androidaps.data.defaultProfile.DefaultProfile +import info.nightscout.androidaps.databinding.ActivitySurveyBinding import info.nightscout.androidaps.dialogs.ProfileViewerDialog import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.ProfileFunction @@ -16,15 +17,13 @@ import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.InstanceId import info.nightscout.androidaps.utils.SafeParse import info.nightscout.androidaps.utils.ToastUtils -import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.stats.TddCalculator import info.nightscout.androidaps.utils.stats.TirCalculator -import kotlinx.android.synthetic.main.activity_survey.* import javax.inject.Inject class SurveyActivity : NoSplashAppCompatActivity() { + @Inject lateinit var aapsLogger: AAPSLogger - @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var activePlugin: ActivePluginProvider @Inject lateinit var tddCalculator: TddCalculator @Inject lateinit var tirCalculator: TirCalculator @@ -32,24 +31,27 @@ class SurveyActivity : NoSplashAppCompatActivity() { @Inject lateinit var activityMonitor: ActivityMonitor @Inject lateinit var defaultProfile: DefaultProfile + private lateinit var binding: ActivitySurveyBinding + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_survey) + binding = ActivitySurveyBinding.inflate(layoutInflater) + setContentView(binding.root) - survey_id.text = InstanceId.instanceId() + binding.id.text = InstanceId.instanceId() val profileStore = activePlugin.activeProfileInterface.profile val profileList = profileStore?.getProfileList() ?: return - survey_spinner.adapter = ArrayAdapter(this, R.layout.spinner_centered, profileList) + binding.spinner.adapter = ArrayAdapter(this, R.layout.spinner_centered, profileList) - survey_tdds.text = tddCalculator.stats() - survey_tir.text = tirCalculator.stats() - survey_activity.text = activityMonitor.stats() + binding.tdds.text = tddCalculator.stats() + binding.tir.text = tirCalculator.stats() + binding.activity.text = activityMonitor.stats() - survey_profile.setOnClickListener { - val age = SafeParse.stringToDouble(survey_age.text.toString()) - val weight = SafeParse.stringToDouble(survey_weight.text.toString()) - val tdd = SafeParse.stringToDouble(survey_tdd.text.toString()) + binding.profile.setOnClickListener { + val age = SafeParse.stringToDouble(binding.age.text.toString()) + val weight = SafeParse.stringToDouble(binding.weight.text.toString()) + val tdd = SafeParse.stringToDouble(binding.tdd.text.toString()) if (age < 1 || age > 120) { ToastUtils.showToastInUiThread(this, R.string.invalidage) return@setOnClickListener @@ -78,11 +80,11 @@ class SurveyActivity : NoSplashAppCompatActivity() { } } - survey_submit.setOnClickListener { + binding.submit.setOnClickListener { val r = FirebaseRecord() r.id = InstanceId.instanceId() - r.age = SafeParse.stringToInt(survey_age.text.toString()) - r.weight = SafeParse.stringToInt(survey_weight.text.toString()) + r.age = SafeParse.stringToInt(binding.age.text.toString()) + r.weight = SafeParse.stringToInt(binding.weight.text.toString()) if (r.age < 1 || r.age > 120) { ToastUtils.showToastInUiThread(this, R.string.invalidage) return@setOnClickListener @@ -92,9 +94,9 @@ class SurveyActivity : NoSplashAppCompatActivity() { return@setOnClickListener } - if (survey_spinner.selectedItem == null) + if (binding.spinner.selectedItem == null) return@setOnClickListener - val profileName = survey_spinner.selectedItem.toString() + val profileName = binding.spinner.selectedItem.toString() val specificProfile = profileStore.getSpecificProfile(profileName) r.profileJson = specificProfile.toString() @@ -121,6 +123,7 @@ class SurveyActivity : NoSplashAppCompatActivity() { } inner class FirebaseRecord { + var id = "" var age: Int = 0 var weight: Int = 0 diff --git a/app/src/main/java/info/nightscout/androidaps/db/CompatDBHelper.kt b/app/src/main/java/info/nightscout/androidaps/db/CompatDBHelper.kt new file mode 100644 index 0000000000..7aa4667ac1 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/db/CompatDBHelper.kt @@ -0,0 +1,43 @@ +package info.nightscout.androidaps.db + +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.entities.GlucoseValue +import info.nightscout.androidaps.database.entities.TemporaryTarget +import info.nightscout.androidaps.database.interfaces.TraceableDBEntry +import info.nightscout.androidaps.events.EventNewBG +import info.nightscout.androidaps.events.EventTempTargetChange +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData +import io.reactivex.disposables.Disposable +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class CompatDBHelper @Inject constructor( + val aapsLogger: AAPSLogger, + val repository: AppRepository, + val rxBus: RxBusWrapper +) { + + fun dbChangeDisposable(): Disposable = repository + .changeObservable() + .doOnSubscribe { + rxBus.send(EventNewBG(null)) + } + .subscribe { + it.filterIsInstance().firstOrNull()?.let { + aapsLogger.debug(LTag.DATABASE, "Firing EventNewHistoryData") + rxBus.send(EventNewHistoryData(it.timestamp)) + } + it.filterIsInstance().lastOrNull()?.let { + aapsLogger.debug(LTag.DATABASE, "Firing EventNewBg") + rxBus.send(EventNewBG(it)) + } + it.filterIsInstance().firstOrNull()?.let { + aapsLogger.debug(LTag.DATABASE, "Firing EventTempTargetChange") + rxBus.send(EventTempTargetChange()) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java index 679e119b21..ae7a6e4436 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java +++ b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelper.java @@ -33,13 +33,11 @@ import java.util.concurrent.TimeUnit; import javax.inject.Inject; import info.nightscout.androidaps.Constants; -import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.dana.comm.RecordTypes; import info.nightscout.androidaps.data.NonOverlappingIntervals; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.events.EventCareportalEventChange; import info.nightscout.androidaps.events.EventExtendedBolusChange; -import info.nightscout.androidaps.events.EventNewBG; import info.nightscout.androidaps.events.EventProfileNeedsUpdate; import info.nightscout.androidaps.events.EventRefreshOverview; import info.nightscout.androidaps.events.EventReloadProfileSwitchData; @@ -54,9 +52,7 @@ import info.nightscout.androidaps.logging.AAPSLogger; import info.nightscout.androidaps.logging.LTag; import info.nightscout.androidaps.plugins.bus.RxBusWrapper; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryBgData; import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventNewHistoryData; import info.nightscout.androidaps.plugins.pump.insight.database.InsightBolusID; import info.nightscout.androidaps.plugins.pump.insight.database.InsightHistoryOffset; @@ -64,7 +60,6 @@ import info.nightscout.androidaps.plugins.pump.insight.database.InsightPumpID; import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin; import info.nightscout.androidaps.utils.JsonHelper; import info.nightscout.androidaps.utils.PercentageSplitter; -import info.nightscout.androidaps.utils.T; /** * This Helper contains all resource to provide a central DB management functionality. Only methods handling @@ -81,7 +76,6 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { @Inject OpenHumansUploader openHumansUploader; public static final String DATABASE_NAME = "AndroidAPSDb"; - public static final String DATABASE_BGREADINGS = "BgReadings"; public static final String DATABASE_TEMPORARYBASALS = "TemporaryBasals"; public static final String DATABASE_EXTENDEDBOLUSES = "ExtendedBoluses"; public static final String DATABASE_TEMPTARGETS = "TempTargets"; @@ -101,10 +95,6 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { private static final ScheduledExecutorService bgWorker = Executors.newSingleThreadScheduledExecutor(); private static ScheduledFuture scheduledBgPost = null; - private static final ScheduledExecutorService bgHistoryWorker = Executors.newSingleThreadScheduledExecutor(); - private static ScheduledFuture scheduledBgHistoryPost = null; - private static long oldestBgHistoryChange = 0; - private static final ScheduledExecutorService tempBasalsWorker = Executors.newSingleThreadScheduledExecutor(); private static ScheduledFuture scheduledTemBasalsPost = null; @@ -135,7 +125,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { try { aapsLogger.info(LTag.DATABASE, "onCreate"); TableUtils.createTableIfNotExists(connectionSource, TempTarget.class); - TableUtils.createTableIfNotExists(connectionSource, BgReading.class); + //TableUtils.createTableIfNotExists(connectionSource, BgReading.class); TableUtils.createTableIfNotExists(connectionSource, DanaRHistoryRecord.class); TableUtils.createTableIfNotExists(connectionSource, DbRequest.class); TableUtils.createTableIfNotExists(connectionSource, TemporaryBasal.class); @@ -167,7 +157,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { if (oldVersion < 7) { aapsLogger.info(LTag.DATABASE, "onUpgrade"); TableUtils.dropTable(connectionSource, TempTarget.class, true); - TableUtils.dropTable(connectionSource, BgReading.class, true); + //TableUtils.dropTable(connectionSource, BgReading.class, true); TableUtils.dropTable(connectionSource, DanaRHistoryRecord.class, true); TableUtils.dropTable(connectionSource, DbRequest.class, true); TableUtils.dropTable(connectionSource, TemporaryBasal.class, true); @@ -217,7 +207,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { public void resetDatabases() { try { TableUtils.dropTable(connectionSource, TempTarget.class, true); - TableUtils.dropTable(connectionSource, BgReading.class, true); + //TableUtils.dropTable(connectionSource, BgReading.class, true); TableUtils.dropTable(connectionSource, DanaRHistoryRecord.class, true); TableUtils.dropTable(connectionSource, DbRequest.class, true); TableUtils.dropTable(connectionSource, TemporaryBasal.class, true); @@ -227,7 +217,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { TableUtils.dropTable(connectionSource, TDD.class, true); TableUtils.dropTable(connectionSource, OmnipodHistoryRecord.class, true); TableUtils.createTableIfNotExists(connectionSource, TempTarget.class); - TableUtils.createTableIfNotExists(connectionSource, BgReading.class); + //TableUtils.createTableIfNotExists(connectionSource, BgReading.class); TableUtils.createTableIfNotExists(connectionSource, DanaRHistoryRecord.class); TableUtils.createTableIfNotExists(connectionSource, DbRequest.class); TableUtils.createTableIfNotExists(connectionSource, TemporaryBasal.class); @@ -241,7 +231,6 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { aapsLogger.error("Unhandled exception", e); } virtualPumpPlugin.setFakingStatus(true); - scheduleBgChange(null); // trigger refresh scheduleTemporaryBasalChange(); scheduleExtendedBolusChange(); scheduleTemporaryTargetChange(); @@ -326,10 +315,6 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { return getDao(TempTarget.class); } - private Dao getDaoBgReadings() throws SQLException { - return getDao(BgReading.class); - } - private Dao getDaoDanaRHistory() throws SQLException { return getDao(DanaRHistoryRecord.class); } @@ -384,144 +369,6 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { aapsLogger.debug(LTag.DATABASE, "Rounding " + date + " to " + rounded); return rounded; } - // ------------------- BgReading handling ----------------------- - - public boolean createIfNotExists(BgReading bgReading, String from) { - try { - bgReading.date = roundDateToSec(bgReading.date); - BgReading old = getDaoBgReadings().queryForId(bgReading.date); - if (old == null) { - getDaoBgReadings().create(bgReading); - openHumansUploader.enqueueBGReading(bgReading); - aapsLogger.debug(LTag.DATABASE, "BG: New record from: " + from + " " + bgReading.toString()); - scheduleBgChange(bgReading); - return true; - } - if (!old.isEqual(bgReading)) { - aapsLogger.debug(LTag.DATABASE, "BG: Similiar found: " + old.toString()); - old.copyFrom(bgReading); - getDaoBgReadings().update(old); - openHumansUploader.enqueueBGReading(old); - aapsLogger.debug(LTag.DATABASE, "BG: Updating record from: " + from + " New data: " + old.toString()); - scheduleBgHistoryChange(old.date); // trigger cache invalidation - return false; - } - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return false; - } - - public void update(BgReading bgReading) { - bgReading.date = roundDateToSec(bgReading.date); - try { - getDaoBgReadings().update(bgReading); - openHumansUploader.enqueueBGReading(bgReading); - aapsLogger.debug(LTag.DATABASE, "BG: Updating record from: "+ bgReading.toString()); - scheduleBgHistoryChange(bgReading.date); // trigger cache invalidation - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - } - - private void scheduleBgChange(@Nullable final BgReading bgReading) { - class PostRunnable implements Runnable { - public void run() { - aapsLogger.debug(LTag.DATABASE, "Firing EventNewBg"); - rxBus.send(new EventNewBG(bgReading)); - scheduledBgPost = null; - } - } - // prepare task for execution in 1 sec - // cancel waiting task to prevent sending multiple posts - if (scheduledBgPost != null) - scheduledBgPost.cancel(false); - Runnable task = new PostRunnable(); - final int sec = 1; - scheduledBgPost = bgWorker.schedule(task, sec, TimeUnit.SECONDS); - - } - - private void scheduleBgHistoryChange(@Nullable final long timestamp) { - class PostRunnable implements Runnable { - public void run() { - aapsLogger.debug(LTag.DATABASE, "Firing EventNewBg"); - rxBus.send(new EventNewHistoryBgData(oldestBgHistoryChange)); - scheduledBgHistoryPost = null; - oldestBgHistoryChange = 0; - } - } - // prepare task for execution in 1 sec - // cancel waiting task to prevent sending multiple posts - if (scheduledBgHistoryPost != null) - scheduledBgHistoryPost.cancel(false); - Runnable task = new PostRunnable(); - final int sec = 3; - if (oldestBgHistoryChange == 0 || oldestBgHistoryChange > timestamp) - oldestBgHistoryChange = timestamp; - scheduledBgHistoryPost = bgHistoryWorker.schedule(task, sec, TimeUnit.SECONDS); - - } - - public List getBgreadingsDataFromTime(long mills, boolean ascending) { - try { - Dao daoBgreadings = getDaoBgReadings(); - List bgReadings; - QueryBuilder queryBuilder = daoBgreadings.queryBuilder(); - queryBuilder.orderBy("date", ascending); - Where where = queryBuilder.where(); - where.ge("date", mills).and().ge("value", 39).and().eq("isValid", true); - PreparedQuery preparedQuery = queryBuilder.prepare(); - bgReadings = daoBgreadings.query(preparedQuery); - return bgReadings; - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return new ArrayList<>(); - } - - public List getBgreadingsDataFromTime(long start, long end, boolean ascending) { - try { - Dao daoBgreadings = getDaoBgReadings(); - List bgReadings; - QueryBuilder queryBuilder = daoBgreadings.queryBuilder(); - queryBuilder.orderBy("date", ascending); - Where where = queryBuilder.where(); - where.between("date", start, end).and().ge("value", 39).and().eq("isValid", true); - PreparedQuery preparedQuery = queryBuilder.prepare(); - bgReadings = daoBgreadings.query(preparedQuery); - return bgReadings; - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return new ArrayList<>(); - } - - public List getAllBgreadingsDataFromTime(long mills, boolean ascending) { - try { - Dao daoBgreadings = getDaoBgReadings(); - List bgReadings; - QueryBuilder queryBuilder = daoBgreadings.queryBuilder(); - queryBuilder.orderBy("date", ascending); - Where where = queryBuilder.where(); - where.ge("date", mills); - PreparedQuery preparedQuery = queryBuilder.prepare(); - bgReadings = daoBgreadings.query(preparedQuery); - return bgReadings; - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return new ArrayList(); - } - - public List getAllBgReadings() { - try { - return getDaoBgReadings().queryForAll(); - } catch (SQLException e) { - aapsLogger.error("Unhandled exception", e); - } - return Collections.emptyList(); - } // ------------------- TDD handling ----------------------- public void createOrUpdateTDD(TDD tdd) { @@ -1672,6 +1519,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { } return Collections.emptyList(); } + @Nullable private ProfileSwitch getLastProfileSwitchWithoutDuration() { try { @@ -2010,7 +1858,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { return null; } - // Copied from xDrip+ +/* + TODO implement again for database branch // Copied from xDrip+ String calculateDirection(BgReading bgReading) { // Rework to get bgreaings from internal DB and calculate on that base @@ -2056,7 +1905,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { // aapsLogger.error(LTag.GLUCOSE, "Direction set to: " + arrow); return arrow; } - +*/ // ---------------- Open Humans Queue handling --------------- public void clearOpenHumansQueue() { @@ -2109,8 +1958,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { public long getCountOfAllRows() { try { - return getDaoBgReadings().countOf() - + getDaoCareportalEvents().countOf() + return getDaoCareportalEvents().countOf() + getDaoExtendedBolus().countOf() + getDaoCareportalEvents().countOf() + getDaoProfileSwitch().countOf() diff --git a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelperProvider.java b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelperProvider.java index ae3e52180c..41f7cabdd9 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelperProvider.java +++ b/app/src/main/java/info/nightscout/androidaps/db/DatabaseHelperProvider.java @@ -20,10 +20,6 @@ public class DatabaseHelperProvider implements DatabaseHelperInterface { @Inject DatabaseHelperProvider() { } - @NotNull @Override public List getAllBgreadingsDataFromTime(long mills, boolean ascending) { - return MainApp.getDbHelper().getAllBgreadingsDataFromTime(mills, ascending); - } - @Override public void createOrUpdate(@NotNull CareportalEvent careportalEvent) { MainApp.getDbHelper().createOrUpdate(careportalEvent); } diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ActivitiesModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ActivitiesModule.kt index f6790caa25..54f139f0e8 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ActivitiesModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ActivitiesModule.kt @@ -6,7 +6,6 @@ import info.nightscout.androidaps.MainActivity import info.nightscout.androidaps.activities.* import info.nightscout.androidaps.historyBrowser.HistoryBrowseActivity import info.nightscout.androidaps.plugins.general.maintenance.activities.LogSettingActivity -import info.nightscout.androidaps.plugins.general.maintenance.activities.PrefImportListActivity import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansLoginActivity import info.nightscout.androidaps.plugins.general.overview.activities.QuickWizardListActivity import info.nightscout.androidaps.plugins.general.smsCommunicator.activities.SmsCommunicatorOtpActivity @@ -40,7 +39,6 @@ abstract class ActivitiesModule { @ContributesAndroidInjector abstract fun contributesStatsActivity(): StatsActivity @ContributesAndroidInjector abstract fun contributesSurveyActivity(): SurveyActivity @ContributesAndroidInjector abstract fun contributesDefaultProfileActivity(): ProfileHelperActivity - @ContributesAndroidInjector abstract fun contributesPrefImportListActivity(): PrefImportListActivity @ContributesAndroidInjector abstract fun contributesOpenHumansLoginActivity(): OpenHumansLoginActivity } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt index ef728a2193..61cb5334b0 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppComponent.kt @@ -9,14 +9,16 @@ import info.nightscout.androidaps.core.di.CoreModule import info.nightscout.androidaps.dana.di.DanaModule import info.nightscout.androidaps.danar.di.DanaRModule import info.nightscout.androidaps.danars.di.DanaRSModule +import info.nightscout.androidaps.database.DatabaseModule import info.nightscout.androidaps.plugins.pump.common.dagger.RileyLinkModule -import info.nightscout.androidaps.plugins.pump.omnipod.dagger.OmnipodModule +import info.nightscout.androidaps.plugins.pump.omnipod.eros.dagger.OmnipodErosModule import javax.inject.Singleton @Singleton @Component( modules = [ AndroidInjectionModule::class, + DatabaseModule::class, PluginsModule::class, SkinsModule::class, ActivitiesModule::class, @@ -30,7 +32,7 @@ import javax.inject.Singleton WizardModule::class, RileyLinkModule::class, MedtronicModule::class, - OmnipodModule::class, + OmnipodErosModule::class, APSModule::class, PreferencesModule::class, OverviewModule::class, @@ -41,6 +43,7 @@ import javax.inject.Singleton DanaModule::class, DanaRModule::class, DanaRSModule::class, + WorkersModule::class, OHUploaderModule::class ] ) diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt index 4fb00e9bb5..e807a8d7f7 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/AppModule.kt @@ -12,10 +12,13 @@ import info.nightscout.androidaps.db.DatabaseHelperProvider import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin import info.nightscout.androidaps.plugins.configBuilder.PluginStore +import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefs import info.nightscout.androidaps.plugins.general.nsclient.UploadQueue import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.queue.CommandQueue import info.nightscout.androidaps.utils.androidNotification.NotificationHolder +import info.nightscout.androidaps.utils.rx.AapsSchedulers +import info.nightscout.androidaps.utils.rx.DefaultAapsSchedulers import info.nightscout.androidaps.utils.storage.FileStorage import info.nightscout.androidaps.utils.storage.Storage import javax.inject.Singleton @@ -45,6 +48,10 @@ open class AppModule { return FileStorage() } + @Provides + @Singleton + internal fun provideSchedulers(): AapsSchedulers = DefaultAapsSchedulers() + @Module interface AppBindings { @@ -58,5 +65,6 @@ open class AppModule { @Binds fun bindDatabaseHelperInterface(databaseHelperProvider: DatabaseHelperProvider): DatabaseHelperInterface @Binds fun bindUploadQueueInterface(uploadQueue: UploadQueue): UploadQueueInterface @Binds fun bindNotificationHolderInterface(notificationHolder: NotificationHolder): NotificationHolderInterface + @Binds fun bindImportExportPrefsInterface(importExportPrefs: ImportExportPrefs): ImportExportPrefsInterface } } diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/DataClassesModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/DataClassesModule.kt index c63e70ace8..98f15e668c 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/DataClassesModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/DataClassesModule.kt @@ -2,11 +2,9 @@ package info.nightscout.androidaps.dependencyInjection import dagger.Module import dagger.android.ContributesAndroidInjector -import info.nightscout.androidaps.data.Profile -import info.nightscout.androidaps.db.* -import info.nightscout.androidaps.interfaces.ProfileStore -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus +import info.nightscout.androidaps.db.DatabaseHelper import info.nightscout.androidaps.plugins.general.food.FoodService +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus import info.nightscout.androidaps.plugins.treatments.TreatmentService import info.nightscout.androidaps.utils.wizard.BolusWizard import info.nightscout.androidaps.utils.wizard.QuickWizardEntry diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/FragmentsModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/FragmentsModule.kt index c7cfec45eb..d00f9b20fd 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/FragmentsModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/FragmentsModule.kt @@ -36,7 +36,7 @@ import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.dialog.RileyL import info.nightscout.androidaps.plugins.pump.insight.LocalInsightFragment import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicFragment import info.nightscout.androidaps.plugins.pump.medtronic.dialog.RileyLinkStatusDeviceMedtronic -import info.nightscout.androidaps.plugins.pump.omnipod.ui.OmnipodOverviewFragment +import info.nightscout.androidaps.plugins.pump.omnipod.eros.ui.OmnipodOverviewFragment import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpFragment import info.nightscout.androidaps.plugins.source.BGSourceFragment import info.nightscout.androidaps.plugins.treatments.TreatmentsFragment @@ -77,18 +77,13 @@ abstract class FragmentsModule { @ContributesAndroidInjector abstract fun contributesTidepoolFragment(): TidepoolFragment @ContributesAndroidInjector abstract fun contributesTreatmentsFragment(): TreatmentsFragment - @ContributesAndroidInjector - abstract fun contributesTreatmentsBolusFragment(): TreatmentsBolusFragment - @ContributesAndroidInjector - abstract fun contributesTreatmentsTemporaryBasalsFragment(): TreatmentsTemporaryBasalsFragment - @ContributesAndroidInjector - abstract fun contributesTreatmentsTempTargetFragment(): TreatmentsTempTargetFragment - @ContributesAndroidInjector - abstract fun contributesTreatmentsExtendedBolusesFragment(): TreatmentsExtendedBolusesFragment - @ContributesAndroidInjector - abstract fun contributesTreatmentsCareportalFragment(): TreatmentsCareportalFragment - @ContributesAndroidInjector - abstract fun contributesTreatmentsProfileSwitchFragment(): TreatmentsProfileSwitchFragment + @ContributesAndroidInjector abstract fun contributesTreatmentsBolusFragment(): TreatmentsBolusFragment + @ContributesAndroidInjector abstract fun contributesTreatmentsTemporaryBasalsFragment(): TreatmentsTemporaryBasalsFragment + @ContributesAndroidInjector abstract fun contributesTreatmentsTempTargetFragment(): TreatmentsTempTargetFragment + @ContributesAndroidInjector abstract fun contributesTreatmentsExtendedBolusesFragment(): TreatmentsExtendedBolusesFragment + @ContributesAndroidInjector abstract fun contributesTreatmentsCareportalFragment(): TreatmentsCareportalFragment + @ContributesAndroidInjector abstract fun contributesTreatmentsProfileSwitchFragment(): TreatmentsProfileSwitchFragment + @ContributesAndroidInjector abstract fun contributesTreatmentsUserEntryFragment(): TreatmentsUserEntryFragment @ContributesAndroidInjector abstract fun contributesVirtualPumpFragment(): VirtualPumpFragment diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PluginsModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PluginsModule.kt index b28073a055..02506911ec 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PluginsModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PluginsModule.kt @@ -42,7 +42,7 @@ import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin import info.nightscout.androidaps.plugins.pump.mdi.MDIPlugin import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin -import info.nightscout.androidaps.plugins.pump.omnipod.OmnipodPumpPlugin +import info.nightscout.androidaps.plugins.pump.omnipod.eros.OmnipodErosPumpPlugin import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin @@ -160,7 +160,7 @@ abstract class PluginsModule { @PumpDriver @IntoMap @IntKey(155) - abstract fun bindOmnipodPumpPlugin(plugin: OmnipodPumpPlugin): PluginBase + abstract fun bindOmnipodPumpPlugin(plugin: OmnipodErosPumpPlugin): PluginBase @Binds @NotNSClient @@ -205,7 +205,7 @@ abstract class PluginsModule { abstract fun bindLocalProfilePlugin(plugin: LocalProfilePlugin): PluginBase @Binds - @APS + @AllConfigs @IntoMap @IntKey(250) abstract fun bindAutomationPlugin(plugin: AutomationPlugin): PluginBase diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PreferencesModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PreferencesModule.kt index 39fb959c15..738f53872a 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PreferencesModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/PreferencesModule.kt @@ -2,7 +2,6 @@ package info.nightscout.androidaps.dependencyInjection import dagger.Module import dagger.android.ContributesAndroidInjector -import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefs import info.nightscout.androidaps.plugins.general.maintenance.PrefFileListProvider import info.nightscout.androidaps.plugins.general.maintenance.formats.ClassicPrefsFormat import info.nightscout.androidaps.plugins.general.maintenance.formats.EncryptedPrefsFormat @@ -13,7 +12,6 @@ import info.nightscout.androidaps.utils.CryptoUtil abstract class PreferencesModule { @ContributesAndroidInjector abstract fun cryptoUtilInjector(): CryptoUtil - @ContributesAndroidInjector abstract fun importExportPrefsInjector(): ImportExportPrefs @ContributesAndroidInjector abstract fun encryptedPrefsFormatInjector(): EncryptedPrefsFormat @ContributesAndroidInjector abstract fun classicPrefsFormatInjector(): ClassicPrefsFormat @ContributesAndroidInjector abstract fun prefImportListProviderInjector(): PrefFileListProvider diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ReceiversModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ReceiversModule.kt index 4c6e67608c..a6e7e4104c 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ReceiversModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ReceiversModule.kt @@ -12,6 +12,7 @@ import info.nightscout.androidaps.receivers.* @Suppress("unused") abstract class ReceiversModule { + @ContributesAndroidInjector abstract fun contributesAutoStartReceiver(): AutoStartReceiver @ContributesAndroidInjector abstract fun contributesBTReceiver(): BTReceiver @ContributesAndroidInjector abstract fun contributesChargingStateReceiver(): ChargingStateReceiver @ContributesAndroidInjector abstract fun contributesDataReceiver(): DataReceiver diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ServicesModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ServicesModule.kt index 6ba01130ae..50f8695bf9 100644 --- a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ServicesModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/ServicesModule.kt @@ -10,9 +10,8 @@ import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.Riley import info.nightscout.androidaps.plugins.pump.insight.InsightAlertService import info.nightscout.androidaps.plugins.pump.insight.connection_service.InsightConnectionService import info.nightscout.androidaps.plugins.pump.medtronic.service.RileyLinkMedtronicService -import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.service.RileyLinkOmnipodService +import info.nightscout.androidaps.plugins.pump.omnipod.eros.rileylink.service.RileyLinkOmnipodService import info.nightscout.androidaps.services.AlarmSoundService -import info.nightscout.androidaps.services.DataService import info.nightscout.androidaps.services.LocationService @Module @@ -20,7 +19,6 @@ import info.nightscout.androidaps.services.LocationService abstract class ServicesModule { @ContributesAndroidInjector abstract fun contributesAlarmSoundService(): AlarmSoundService - @ContributesAndroidInjector abstract fun contributesDataService(): DataService @ContributesAndroidInjector abstract fun contributesDismissNotificationService(): DismissNotificationService @ContributesAndroidInjector abstract fun contributesDummyService(): DummyService @ContributesAndroidInjector abstract fun contributesLocationService(): LocationService diff --git a/app/src/main/java/info/nightscout/androidaps/dependencyInjection/WorkersModule.kt b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/WorkersModule.kt new file mode 100644 index 0000000000..5f48be718c --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/dependencyInjection/WorkersModule.kt @@ -0,0 +1,25 @@ +package info.nightscout.androidaps.dependencyInjection + +import dagger.Module +import dagger.android.ContributesAndroidInjector +import info.nightscout.androidaps.plugins.general.nsclient.NSClientWorker +import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin +import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin +import info.nightscout.androidaps.plugins.source.* + +@Module +@Suppress("unused") +abstract class WorkersModule { + + @ContributesAndroidInjector abstract fun contributesXdripWorker(): XdripPlugin.XdripWorker + @ContributesAndroidInjector abstract fun contributesDexcomWorker(): DexcomPlugin.DexcomWorker + @ContributesAndroidInjector abstract fun contributesMM640gWorker(): MM640gPlugin.MM640gWorker + @ContributesAndroidInjector abstract fun contributesGlimpWorker(): GlimpPlugin.GlimpWorker + @ContributesAndroidInjector abstract fun contributesPoctechWorker(): PoctechPlugin.PoctechWorker + @ContributesAndroidInjector abstract fun contributesTomatoWorker(): TomatoPlugin.TomatoWorker + @ContributesAndroidInjector abstract fun contributesEversenseWorker(): EversensePlugin.EversenseWorker + @ContributesAndroidInjector abstract fun contributesNSClientSourceWorker(): NSClientSourcePlugin.NSClientSourceWorker + @ContributesAndroidInjector abstract fun contributesNSProfileWorker(): NSProfilePlugin.NSProfileWorker + @ContributesAndroidInjector abstract fun contributesSmsCommunicatorWorker(): SmsCommunicatorPlugin.SmsCommunicatorWorker + @ContributesAndroidInjector abstract fun contributesNSClientWorker(): NSClientWorker +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/CalibrationDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/CalibrationDialog.kt index 894f017995..63acaef52c 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/CalibrationDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/CalibrationDialog.kt @@ -9,14 +9,14 @@ import dagger.android.HasAndroidInjector import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.databinding.DialogCalibrationBinding import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus import info.nightscout.androidaps.utils.HtmlHelper -import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.XdripCalibrations +import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.resources.ResourceHelper -import kotlinx.android.synthetic.main.dialog_calibration.* -import kotlinx.android.synthetic.main.okcancel.* import java.text.DecimalFormat import java.util.* import javax.inject.Inject @@ -27,16 +27,24 @@ class CalibrationDialog : DialogFragmentWithDate() { @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var xdripCalibrations: XdripCalibrations + @Inject lateinit var uel: UserEntryLogger + + private var _binding: DialogCalibrationBinding? = 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("overview_calibration_bg", overview_calibration_bg.value) + savedInstanceState.putDouble("bg", binding.bg.value) } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { + savedInstanceState: Bundle?): View { onCreateViewGeneral() - return inflater.inflate(R.layout.dialog_calibration, container, false) + _binding = DialogCalibrationBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -46,24 +54,30 @@ class CalibrationDialog : DialogFragmentWithDate() { val bg = Profile.fromMgdlToUnits(GlucoseStatus(injector).glucoseStatusData?.glucose ?: 0.0, units) if (units == Constants.MMOL) - overview_calibration_bg.setParams(savedInstanceState?.getDouble("overview_calibration_bg") - ?: bg, 2.0, 30.0, 0.1, DecimalFormat("0.0"), false, ok) + binding.bg.setParams(savedInstanceState?.getDouble("bg") + ?: bg, 2.0, 30.0, 0.1, DecimalFormat("0.0"), false, binding.okcancel.ok) else - overview_calibration_bg.setParams(savedInstanceState?.getDouble("overview_calibration_bg") - ?: bg, 36.0, 500.0, 1.0, DecimalFormat("0"), false, ok) - overview_calibration_units.text = if (units == Constants.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl) + binding.bg.setParams(savedInstanceState?.getDouble("bg") + ?: bg, 36.0, 500.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok) + binding.units.text = if (units == Constants.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl) + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null } override fun submit(): Boolean { + if (_binding == null) return false val units = profileFunction.getUnits() val unitLabel = if (units == Constants.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl) val actions: LinkedList = LinkedList() - val bg = overview_calibration_bg?.value ?: return false + val bg = binding.bg.value ?: return false actions.add(resourceHelper.gs(R.string.treatments_wizard_bg_label) + ": " + Profile.toCurrentUnitsString(profileFunction, bg) + " " + unitLabel) if (bg > 0) { activity?.let { activity -> - OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.overview_calibration), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), Runnable { - aapsLogger.debug("USER ENTRY: CALIBRATION $bg") + OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.overview_calibration), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), { + uel.log("CALIBRATION", d1 = bg) xdripCalibrations.sendIntent(bg) }) } diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt index f911c1ef66..59de46fc32 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt @@ -11,11 +11,13 @@ import info.nightscout.androidaps.Constants import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.R import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.databinding.DialogCarbsBinding import info.nightscout.androidaps.db.CareportalEvent import info.nightscout.androidaps.db.Source import info.nightscout.androidaps.db.TempTarget import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin @@ -29,15 +31,13 @@ import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.extensions.formatColor import info.nightscout.androidaps.utils.resources.ResourceHelper -import kotlinx.android.synthetic.main.dialog_carbs.* -import kotlinx.android.synthetic.main.notes.* -import kotlinx.android.synthetic.main.okcancel.* import java.text.DecimalFormat import java.util.* import javax.inject.Inject import kotlin.math.max class CarbsDialog : DialogFragmentWithDate() { + @Inject lateinit var mainApp: MainApp @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var constraintChecker: ConstraintChecker @@ -47,8 +47,10 @@ class CarbsDialog : DialogFragmentWithDate() { @Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin @Inject lateinit var nsUpload: NSUpload @Inject lateinit var carbsGenerator: CarbsGenerator + @Inject lateinit var uel: UserEntryLogger companion object { + private const val FAV1_DEFAULT = 5 private const val FAV2_DEFAULT = 10 private const val FAV3_DEFAULT = 20 @@ -65,92 +67,105 @@ class CarbsDialog : DialogFragmentWithDate() { private fun validateInputs() { val maxCarbs = constraintChecker.getMaxCarbsAllowed().value().toDouble() - val time = overview_carbs_time.value.toInt() + val time = binding.time.value.toInt() if (time > 12 * 60 || time < -12 * 60) { - overview_carbs_time.value = 0.0 + binding.time.value = 0.0 ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.constraintapllied)) } - if (overview_carbs_duration.value > 10) { - overview_carbs_duration.value = 0.0 + if (binding.duration.value > 10) { + binding.duration.value = 0.0 ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.constraintapllied)) } - if (overview_carbs_carbs.value.toInt() > maxCarbs) { - overview_carbs_carbs.value = 0.0 + if (binding.carbs.value.toInt() > maxCarbs) { + binding.carbs.value = 0.0 ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.carbsconstraintapplied)) } } + private var _binding: DialogCarbsBinding? = 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("overview_carbs_time", overview_carbs_time.value) - savedInstanceState.putDouble("overview_carbs_duration", overview_carbs_duration.value) - savedInstanceState.putDouble("overview_carbs_carbs", overview_carbs_carbs.value) + savedInstanceState.putDouble("time", binding.time.value) + savedInstanceState.putDouble("duration", binding.duration.value) + savedInstanceState.putDouble("carbs", binding.carbs.value) } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { + savedInstanceState: Bundle?): View { onCreateViewGeneral() - return inflater.inflate(R.layout.dialog_carbs, container, false) + _binding = DialogCarbsBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val maxCarbs = constraintChecker.getMaxCarbsAllowed().value().toDouble() - overview_carbs_time.setParams(savedInstanceState?.getDouble("overview_carbs_time") - ?: 0.0, -12 * 60.0, 12 * 60.0, 5.0, DecimalFormat("0"), false, ok, textWatcher) + binding.time.setParams(savedInstanceState?.getDouble("time") + ?: 0.0, -12 * 60.0, 12 * 60.0, 5.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher) - overview_carbs_duration.setParams(savedInstanceState?.getDouble("overview_carbs_duration") - ?: 0.0, 0.0, 10.0, 1.0, DecimalFormat("0"), false, ok, textWatcher) + binding.duration.setParams(savedInstanceState?.getDouble("duration") + ?: 0.0, 0.0, 10.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher) - overview_carbs_carbs.setParams(savedInstanceState?.getDouble("overview_carbs_carbs") - ?: 0.0, 0.0, maxCarbs, 1.0, DecimalFormat("0"), false, ok, textWatcher) + binding.carbs.setParams(savedInstanceState?.getDouble("carbs") + ?: 0.0, 0.0, maxCarbs, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher) - overview_carbs_plus1.text = toSignedString(sp.getInt(R.string.key_carbs_button_increment_1, FAV1_DEFAULT)) - overview_carbs_plus1.setOnClickListener { - overview_carbs_carbs.value = max(0.0, overview_carbs_carbs.value + binding.plus1.text = toSignedString(sp.getInt(R.string.key_carbs_button_increment_1, FAV1_DEFAULT)) + binding.plus1.setOnClickListener { + binding.carbs.value = max(0.0, binding.carbs.value + sp.getInt(R.string.key_carbs_button_increment_1, FAV1_DEFAULT)) validateInputs() } - overview_carbs_plus2.text = toSignedString(sp.getInt(R.string.key_carbs_button_increment_2, FAV2_DEFAULT)) - overview_carbs_plus2.setOnClickListener { - overview_carbs_carbs.value = max(0.0, overview_carbs_carbs.value + binding.plus2.text = toSignedString(sp.getInt(R.string.key_carbs_button_increment_2, FAV2_DEFAULT)) + binding.plus2.setOnClickListener { + binding.carbs.value = max(0.0, binding.carbs.value + sp.getInt(R.string.key_carbs_button_increment_2, FAV2_DEFAULT)) validateInputs() } - overview_carbs_plus3.text = toSignedString(sp.getInt(R.string.key_carbs_button_increment_3, FAV3_DEFAULT)) - overview_carbs_plus3.setOnClickListener { - overview_carbs_carbs.value = max(0.0, overview_carbs_carbs.value + binding.plus3.text = toSignedString(sp.getInt(R.string.key_carbs_button_increment_3, FAV3_DEFAULT)) + binding.plus3.setOnClickListener { + binding.carbs.value = max(0.0, binding.carbs.value + sp.getInt(R.string.key_carbs_button_increment_3, FAV3_DEFAULT)) validateInputs() } iobCobCalculatorPlugin.actualBg()?.let { bgReading -> if (bgReading.value < 72) - overview_carbs_hypo_tt.isChecked = true + binding.hypoTt.isChecked = true } - overview_carbs_hypo_tt.setOnClickListener { - overview_carbs_activity_tt.isChecked = false - overview_carbs_eating_soon_tt.isChecked = false + binding.hypoTt.setOnClickListener { + binding.activityTt.isChecked = false + binding.eatingSoonTt.isChecked = false } - overview_carbs_activity_tt.setOnClickListener { - overview_carbs_hypo_tt.isChecked = false - overview_carbs_eating_soon_tt.isChecked = false + binding.activityTt.setOnClickListener { + binding.hypoTt.isChecked = false + binding.eatingSoonTt.isChecked = false } - overview_carbs_eating_soon_tt.setOnClickListener { - overview_carbs_hypo_tt.isChecked = false - overview_carbs_activity_tt.isChecked = false + binding.eatingSoonTt.setOnClickListener { + binding.hypoTt.isChecked = false + binding.activityTt.isChecked = false } } + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + private fun toSignedString(value: Int): String { return if (value > 0) "+$value" else value.toString() } override fun submit(): Boolean { - val carbs = overview_carbs_carbs?.value?.toInt() ?: return false + if (_binding == null) return false + val carbs = binding.carbs.value?.toInt() ?: return false val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value() val units = profileFunction.getUnits() val activityTTDuration = defaultValueHelper.determineActivityTTDuration() @@ -162,22 +177,22 @@ class CarbsDialog : DialogFragmentWithDate() { val actions: LinkedList = LinkedList() val unitLabel = if (units == Constants.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl) - val activitySelected = overview_carbs_activity_tt.isChecked + val activitySelected = binding.activityTt.isChecked if (activitySelected) actions.add(resourceHelper.gs(R.string.temptargetshort) + ": " + (DecimalFormatter.to1Decimal(activityTT) + " " + unitLabel + " (" + resourceHelper.gs(R.string.format_mins, activityTTDuration) + ")").formatColor(resourceHelper, R.color.tempTargetConfirmation)) - val eatingSoonSelected = overview_carbs_eating_soon_tt.isChecked + val eatingSoonSelected = binding.eatingSoonTt.isChecked if (eatingSoonSelected) actions.add(resourceHelper.gs(R.string.temptargetshort) + ": " + (DecimalFormatter.to1Decimal(eatingSoonTT) + " " + unitLabel + " (" + resourceHelper.gs(R.string.format_mins, eatingSoonTTDuration) + ")").formatColor(resourceHelper, R.color.tempTargetConfirmation)) - val hypoSelected = overview_carbs_hypo_tt.isChecked + val hypoSelected = binding.hypoTt.isChecked if (hypoSelected) actions.add(resourceHelper.gs(R.string.temptargetshort) + ": " + (DecimalFormatter.to1Decimal(hypoTT) + " " + unitLabel + " (" + resourceHelper.gs(R.string.format_mins, hypoTTDuration) + ")").formatColor(resourceHelper, R.color.tempTargetConfirmation)) - val timeOffset = overview_carbs_time.value.toInt() + val timeOffset = binding.time.value.toInt() eventTime -= eventTime % 1000 val time = eventTime + timeOffset * 1000 * 60 if (timeOffset != 0) actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(time)) - val duration = overview_carbs_duration.value.toInt() + val duration = binding.duration.value.toInt() if (duration > 0) actions.add(resourceHelper.gs(R.string.duration) + ": " + duration + resourceHelper.gs(R.string.shorthour)) if (carbsAfterConstraints > 0) { @@ -185,7 +200,7 @@ class CarbsDialog : DialogFragmentWithDate() { if (carbsAfterConstraints != carbs) actions.add("" + resourceHelper.gs(R.string.carbsconstraintapplied) + "") } - val notes = notes.text.toString() + val notes = binding.notesLayout.notes.text.toString() if (notes.isNotEmpty()) actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes) @@ -194,10 +209,10 @@ class CarbsDialog : DialogFragmentWithDate() { if (carbsAfterConstraints > 0 || activitySelected || eatingSoonSelected || hypoSelected) { activity?.let { activity -> - OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.carbs), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), Runnable { + OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.carbs), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), { when { activitySelected -> { - aapsLogger.debug("USER ENTRY: TEMPTARGET ACTIVITY $activityTT duration: $activityTTDuration") + uel.log("TT ACTIVITY", d1 = activityTT, i1 = activityTTDuration) val tempTarget = TempTarget() .date(System.currentTimeMillis()) .duration(activityTTDuration) @@ -209,7 +224,7 @@ class CarbsDialog : DialogFragmentWithDate() { } eatingSoonSelected -> { - aapsLogger.debug("USER ENTRY: TEMPTARGET EATING SOON $eatingSoonTT duration: $eatingSoonTTDuration") + uel.log("TT EATING SOON", d1 = eatingSoonTT, i1 = eatingSoonTTDuration) val tempTarget = TempTarget() .date(System.currentTimeMillis()) .duration(eatingSoonTTDuration) @@ -221,7 +236,7 @@ class CarbsDialog : DialogFragmentWithDate() { } hypoSelected -> { - aapsLogger.debug("USER ENTRY: TEMPTARGET HYPO $hypoTT duration: $hypoTTDuration") + uel.log("TT HYPO", d1 = hypoTT, i1 = hypoTTDuration) val tempTarget = TempTarget() .date(System.currentTimeMillis()) .duration(hypoTTDuration) @@ -234,10 +249,10 @@ class CarbsDialog : DialogFragmentWithDate() { } if (carbsAfterConstraints > 0) { if (duration == 0) { - aapsLogger.debug("USER ENTRY: CARBS $carbsAfterConstraints time: $time") + uel.log("CARBS", d1 = carbsAfterConstraints.toDouble(), i1 = timeOffset) carbsGenerator.createCarb(carbsAfterConstraints, time, CareportalEvent.CARBCORRECTION, notes) } else { - aapsLogger.debug("USER ENTRY: CARBS $carbsAfterConstraints time: $time duration: $duration") + uel.log("CARBS", d1 = carbsAfterConstraints.toDouble(), i1 = timeOffset, i2 = duration) carbsGenerator.generateCarbs(carbsAfterConstraints, time, duration, notes) nsUpload.uploadEvent(CareportalEvent.NOTE, DateUtil.now() - 2000, resourceHelper.gs(R.string.generated_ecarbs_note, carbsAfterConstraints, duration, timeOffset)) } diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/CareDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/CareDialog.kt index 3dae80e53f..4f34ffe8d4 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/CareDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/CareDialog.kt @@ -13,9 +13,11 @@ import info.nightscout.androidaps.Constants import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.R import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.databinding.DialogCareBinding import info.nightscout.androidaps.db.CareportalEvent import info.nightscout.androidaps.db.Source import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus import info.nightscout.androidaps.utils.DateUtil @@ -23,21 +25,20 @@ import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.Translator import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.resources.ResourceHelper -import kotlinx.android.synthetic.main.dialog_care.* -import kotlinx.android.synthetic.main.notes.* -import kotlinx.android.synthetic.main.okcancel.* import org.json.JSONObject import java.text.DecimalFormat import java.util.* import javax.inject.Inject class CareDialog : DialogFragmentWithDate() { + @Inject lateinit var injector: HasAndroidInjector @Inject lateinit var mainApp: MainApp @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var nsUpload: NSUpload @Inject lateinit var translator: Translator + @Inject lateinit var uel: UserEntryLogger enum class EventType { BGCHECK, @@ -60,18 +61,25 @@ class CareDialog : DialogFragmentWithDate() { return this } + private var _binding: DialogCareBinding? = 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("actions_care_bg", actions_care_bg.value) - savedInstanceState.putDouble("actions_care_duration", actions_care_duration.value) + savedInstanceState.putDouble("bg", binding.bg.value) + savedInstanceState.putDouble("duration", binding.duration.value) savedInstanceState.putInt("event", event) savedInstanceState.putInt("options", options.ordinal) } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { + savedInstanceState: Bundle?): View { onCreateViewGeneral() - return inflater.inflate(R.layout.dialog_care, container, false) + _binding = DialogCareBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -82,7 +90,7 @@ class CareDialog : DialogFragmentWithDate() { options = EventType.values()[savedInstanceState.getInt("options", 0)] } - actions_care_icon.setImageResource(when (options) { + binding.icon.setImageResource(when (options) { EventType.BGCHECK -> R.drawable.ic_cp_bgcheck EventType.SENSOR_INSERT -> R.drawable.ic_cp_cgm_insert EventType.BATTERY_CHANGE -> R.drawable.ic_cp_pump_battery @@ -91,7 +99,7 @@ class CareDialog : DialogFragmentWithDate() { EventType.QUESTION -> R.drawable.ic_cp_question EventType.ANNOUNCEMENT -> R.drawable.ic_cp_announcement }) - actions_care_title.text = resourceHelper.gs(when (options) { + binding.title.text = resourceHelper.gs(when (options) { EventType.BGCHECK -> R.string.careportal_bgcheck EventType.SENSOR_INSERT -> R.string.careportal_cgmsensorinsert EventType.BATTERY_CHANGE -> R.string.careportal_pumpbatterychange @@ -104,21 +112,21 @@ class CareDialog : DialogFragmentWithDate() { when (options) { EventType.QUESTION, EventType.ANNOUNCEMENT, - EventType.BGCHECK -> { - action_care_duration_layout.visibility = View.GONE + EventType.BGCHECK -> { + binding.durationLayout.visibility = View.GONE } EventType.SENSOR_INSERT, EventType.BATTERY_CHANGE -> { - action_care_bg_layout.visibility = View.GONE - actions_care_bgsource.visibility = View.GONE - action_care_duration_layout.visibility = View.GONE + binding.bgLayout.visibility = View.GONE + binding.bgsource.visibility = View.GONE + binding.durationLayout.visibility = View.GONE } EventType.NOTE, - EventType.EXERCISE -> { - action_care_bg_layout.visibility = View.GONE - actions_care_bgsource.visibility = View.GONE + EventType.EXERCISE -> { + binding.bgLayout.visibility = View.GONE + binding.bgsource.visibility = View.GONE } } @@ -128,23 +136,28 @@ class CareDialog : DialogFragmentWithDate() { override fun afterTextChanged(s: Editable) {} override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - if (actions_care_sensor.isChecked) actions_care_meter.isChecked = true + if (binding.sensor.isChecked) binding.meter.isChecked = true } } if (profileFunction.getUnits() == Constants.MMOL) { - actions_care_bgunits.text = resourceHelper.gs(R.string.mmol) - actions_care_bg.setParams(savedInstanceState?.getDouble("actions_care_bg") - ?: bg, 2.0, 30.0, 0.1, DecimalFormat("0.0"), false, ok, bgTextWatcher) + binding.bgunits.text = resourceHelper.gs(R.string.mmol) + binding.bg.setParams(savedInstanceState?.getDouble("bg") + ?: bg, 2.0, 30.0, 0.1, DecimalFormat("0.0"), false, binding.okcancel.ok, bgTextWatcher) } else { - actions_care_bgunits.text = resourceHelper.gs(R.string.mgdl) - actions_care_bg.setParams(savedInstanceState?.getDouble("actions_care_bg") - ?: bg, 36.0, 500.0, 1.0, DecimalFormat("0"), false, ok, bgTextWatcher) + binding.bgunits.text = resourceHelper.gs(R.string.mgdl) + binding.bg.setParams(savedInstanceState?.getDouble("bg") + ?: bg, 36.0, 500.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, bgTextWatcher) } - actions_care_duration.setParams(savedInstanceState?.getDouble("actions_care_duration") - ?: 0.0, 0.0, Constants.MAX_PROFILE_SWITCH_DURATION, 10.0, DecimalFormat("0"), false, ok) - if (options == EventType.NOTE || options == EventType.QUESTION || options == EventType.ANNOUNCEMENT) - notes_layout?.visibility = View.VISIBLE // independent to preferences + binding.duration.setParams(savedInstanceState?.getDouble("duration") + ?: 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 + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null } override fun submit(): Boolean { @@ -156,20 +169,20 @@ class CareDialog : DialogFragmentWithDate() { if (options == EventType.BGCHECK || options == EventType.QUESTION || options == EventType.ANNOUNCEMENT) { val type = when { - actions_care_meter.isChecked -> CareportalEvent.FINGER - actions_care_sensor.isChecked -> CareportalEvent.SENSOR - else -> CareportalEvent.MANUAL + binding.meter.isChecked -> CareportalEvent.FINGER + binding.sensor.isChecked -> CareportalEvent.SENSOR + else -> CareportalEvent.MANUAL } actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_glucosetype) + ": " + translator.translate(type)) - actions.add(resourceHelper.gs(R.string.treatments_wizard_bg_label) + ": " + Profile.toCurrentUnitsString(profileFunction, actions_care_bg.value) + " " + resourceHelper.gs(unitResId)) - json.put("glucose", actions_care_bg.value) + actions.add(resourceHelper.gs(R.string.treatments_wizard_bg_label) + ": " + Profile.toCurrentUnitsString(profileFunction, binding.bg.value) + " " + resourceHelper.gs(unitResId)) + json.put("glucose", binding.bg.value) json.put("glucoseType", type) } if (options == EventType.NOTE || options == EventType.EXERCISE) { - actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_duration_label) + ": " + resourceHelper.gs(R.string.format_mins, actions_care_duration.value.toInt())) - json.put("duration", actions_care_duration.value.toInt()) + actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_duration_label) + ": " + resourceHelper.gs(R.string.format_mins, binding.duration.value.toInt())) + json.put("duration", binding.duration.value.toInt()) } - val notes = notes.text.toString() + val notes = binding.notesLayout.notes.text.toString() if (notes.isNotEmpty()) { actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes) json.put("notes", notes) @@ -195,7 +208,7 @@ class CareDialog : DialogFragmentWithDate() { json.put("enteredBy", enteredBy) activity?.let { activity -> - OKDialog.showConfirmation(activity, resourceHelper.gs(event), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), Runnable { + OKDialog.showConfirmation(activity, resourceHelper.gs(event), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), { val careportalEvent = CareportalEvent(injector) careportalEvent.date = eventTime careportalEvent.source = Source.USER @@ -209,7 +222,7 @@ class CareDialog : DialogFragmentWithDate() { EventType.ANNOUNCEMENT -> CareportalEvent.ANNOUNCEMENT } careportalEvent.json = json.toString() - aapsLogger.debug("USER ENTRY: CAREPORTAL ${careportalEvent.eventType} json: ${careportalEvent.json}") + uel.log("CAREPORTAL", careportalEvent.eventType) MainApp.getDbHelper().createOrUpdate(careportalEvent) nsUpload.uploadCareportalEntryToNS(json) }, null) diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/DialogFragmentWithDate.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/DialogFragmentWithDate.kt index 5631463153..f2980a9b65 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/DialogFragmentWithDate.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/DialogFragmentWithDate.kt @@ -8,6 +8,8 @@ import android.view.View import android.view.ViewGroup import android.view.Window import android.view.WindowManager +import android.widget.Button +import android.widget.TextView import androidx.fragment.app.FragmentManager import dagger.android.support.DaggerDialogFragment import info.nightscout.androidaps.R @@ -16,9 +18,6 @@ import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.extensions.toVisibility import info.nightscout.androidaps.utils.sharedPreferences.SP -import kotlinx.android.synthetic.main.datetime.* -import kotlinx.android.synthetic.main.notes.* -import kotlinx.android.synthetic.main.okcancel.* import java.util.* import javax.inject.Inject @@ -58,10 +57,14 @@ abstract class DialogFragmentWithDate : DaggerDialogFragment() { } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + val eventDateView = view.findViewById(R.id.eventdate) as TextView? + val eventTimeView = view.findViewById(R.id.eventtime) as TextView? + eventTime = savedInstanceState?.getLong("eventTime") ?: DateUtil.now() eventTimeChanged = savedInstanceState?.getBoolean("eventTimeChanged") ?: false - overview_eventdate?.text = DateUtil.dateString(eventTime) - overview_eventtime?.text = dateUtil.timeString(eventTime) + + eventDateView?.text = DateUtil.dateString(eventTime) + eventTimeView?.text = dateUtil.timeString(eventTime) // create an OnDateSetListener val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth -> @@ -72,10 +75,10 @@ abstract class DialogFragmentWithDate : DaggerDialogFragment() { cal.set(Calendar.DAY_OF_MONTH, dayOfMonth) eventTime = cal.timeInMillis eventTimeChanged = true - overview_eventdate?.text = DateUtil.dateString(eventTime) + eventDateView?.text = DateUtil.dateString(eventTime) } - overview_eventdate?.setOnClickListener { + eventDateView?.setOnClickListener { context?.let { val cal = Calendar.getInstance() cal.timeInMillis = eventTime @@ -96,10 +99,10 @@ abstract class DialogFragmentWithDate : DaggerDialogFragment() { cal.set(Calendar.SECOND, seconds++) // randomize seconds to prevent creating record of the same time, if user choose time manually eventTime = cal.timeInMillis eventTimeChanged = true - overview_eventtime?.text = dateUtil.timeString(eventTime) + eventTimeView?.text = dateUtil.timeString(eventTime) } - overview_eventtime?.setOnClickListener { + eventTimeView?.setOnClickListener { context?.let { val cal = Calendar.getInstance() cal.timeInMillis = eventTime @@ -111,9 +114,9 @@ abstract class DialogFragmentWithDate : DaggerDialogFragment() { } } - notes_layout?.visibility = sp.getBoolean(R.string.key_show_notes_entry_dialogs, false).toVisibility() + (view.findViewById(R.id.notes_layout) as View?)?.visibility = sp.getBoolean(R.string.key_show_notes_entry_dialogs, false).toVisibility() - ok.setOnClickListener { + (view.findViewById(R.id.ok) as Button?)?.setOnClickListener { synchronized(okClicked) { if (okClicked) { aapsLogger.warn(LTag.UI, "guarding: ok already clicked") @@ -124,7 +127,7 @@ abstract class DialogFragmentWithDate : DaggerDialogFragment() { } } } - cancel.setOnClickListener { dismiss() } + (view.findViewById(R.id.cancel) as Button?)?.setOnClickListener { dismiss() } } override fun show(manager: FragmentManager, tag: String?) { diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/ExtendedBolusDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/ExtendedBolusDialog.kt index dce221bf9a..36246c4c2c 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/ExtendedBolusDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/ExtendedBolusDialog.kt @@ -1,48 +1,56 @@ package info.nightscout.androidaps.dialogs -import android.content.Intent +import android.content.Context import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import com.google.common.base.Joiner -import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.ErrorHelperActivity +import info.nightscout.androidaps.databinding.DialogExtendedbolusBinding import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.interfaces.Constraint +import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.utils.HtmlHelper -import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.SafeParse +import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.extensions.formatColor import info.nightscout.androidaps.utils.resources.ResourceHelper -import kotlinx.android.synthetic.main.dialog_extendedbolus.* -import kotlinx.android.synthetic.main.okcancel.* import java.text.DecimalFormat import java.util.* import javax.inject.Inject import kotlin.math.abs class ExtendedBolusDialog : DialogFragmentWithDate() { - @Inject lateinit var mainApp: MainApp + + @Inject lateinit var ctx: Context @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var commandQueue: CommandQueueProvider @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var uel: UserEntryLogger + + private var _binding: DialogExtendedbolusBinding? = 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("actions_extendedbolus_insulin", actions_extendedbolus_insulin.value) - savedInstanceState.putDouble("actions_extendedbolus_duration", actions_extendedbolus_duration.value) + savedInstanceState.putDouble("insulin", binding.insulin.value) + savedInstanceState.putDouble("duration", binding.duration.value) } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { + savedInstanceState: Bundle?): View { onCreateViewGeneral() - return inflater.inflate(R.layout.dialog_extendedbolus, container, false) + _binding = DialogExtendedbolusBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -52,18 +60,24 @@ class ExtendedBolusDialog : DialogFragmentWithDate() { val maxInsulin = constraintChecker.getMaxExtendedBolusAllowed().value() val extendedStep = pumpDescription.extendedBolusStep - actions_extendedbolus_insulin.setParams(savedInstanceState?.getDouble("actions_extendedbolus_insulin") - ?: extendedStep, extendedStep, maxInsulin, extendedStep, DecimalFormat("0.00"), false, ok) + binding.insulin.setParams(savedInstanceState?.getDouble("insulin") + ?: extendedStep, extendedStep, maxInsulin, extendedStep, DecimalFormat("0.00"), false, binding.okcancel.ok) val extendedDurationStep = pumpDescription.extendedBolusDurationStep val extendedMaxDuration = pumpDescription.extendedBolusMaxDuration - actions_extendedbolus_duration.setParams(savedInstanceState?.getDouble("actions_extendedbolus_duration") - ?: extendedDurationStep, extendedDurationStep, extendedMaxDuration, extendedDurationStep, DecimalFormat("0"), false, ok) + binding.duration.setParams(savedInstanceState?.getDouble("duration") + ?: extendedDurationStep, extendedDurationStep, extendedMaxDuration, extendedDurationStep, DecimalFormat("0"), false, binding.okcancel.ok) + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null } override fun submit(): Boolean { - val insulin = SafeParse.stringToDouble(actions_extendedbolus_insulin?.text ?: return false) - val durationInMinutes = actions_extendedbolus_duration.value.toInt() + if (_binding == null) return false + val insulin = SafeParse.stringToDouble(binding.insulin.text ?: return false) + val durationInMinutes = binding.duration.value.toInt() val actions: LinkedList = LinkedList() val insulinAfterConstraint = constraintChecker.applyExtendedBolusConstraints(Constraint(insulin)).value() actions.add(resourceHelper.gs(R.string.formatinsulinunits, insulinAfterConstraint)) @@ -72,17 +86,12 @@ class ExtendedBolusDialog : DialogFragmentWithDate() { actions.add(resourceHelper.gs(R.string.constraintapllied).formatColor(resourceHelper, R.color.warning)) activity?.let { activity -> - OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.extended_bolus), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), Runnable { - aapsLogger.debug("USER ENTRY: EXTENDED BOLUS $insulinAfterConstraint duration: $durationInMinutes") + OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.extended_bolus), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), { + uel.log("EXTENDED BOLUS", d1 = insulinAfterConstraint, i1 = durationInMinutes) commandQueue.extendedBolus(insulinAfterConstraint, durationInMinutes, object : Callback() { override fun run() { if (!result.success) { - val i = Intent(mainApp, ErrorHelperActivity::class.java) - i.putExtra("soundid", R.raw.boluserror) - i.putExtra("status", result.comment) - i.putExtra("title", resourceHelper.gs(R.string.treatmentdeliveryerror)) - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - mainApp.startActivity(i) + ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror) } } }) diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/FillDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/FillDialog.kt index 167ee2cd84..90ee51cf63 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/FillDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/FillDialog.kt @@ -1,7 +1,6 @@ package info.nightscout.androidaps.dialogs import android.content.Context -import android.content.Intent import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -10,11 +9,13 @@ import com.google.common.base.Joiner import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.ErrorHelperActivity import info.nightscout.androidaps.data.DetailedBolusInfo +import info.nightscout.androidaps.databinding.DialogFillBinding import info.nightscout.androidaps.db.CareportalEvent import info.nightscout.androidaps.db.Source import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.interfaces.Constraint +import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.queue.Callback @@ -24,30 +25,36 @@ import info.nightscout.androidaps.utils.SafeParse import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.extensions.formatColor import info.nightscout.androidaps.utils.resources.ResourceHelper -import kotlinx.android.synthetic.main.dialog_fill.* -import kotlinx.android.synthetic.main.notes.* -import kotlinx.android.synthetic.main.okcancel.* import java.util.* import javax.inject.Inject import kotlin.math.abs class FillDialog : DialogFragmentWithDate() { + @Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var ctx: Context @Inject lateinit var nsUpload: NSUpload @Inject lateinit var commandQueue: CommandQueueProvider @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var uel: UserEntryLogger + + private var _binding: DialogFillBinding? = 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("fill_insulin_amount", fill_insulinamount.value) + savedInstanceState.putDouble("fill_insulin_amount", binding.fillInsulinamount.value) } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { + savedInstanceState: Bundle?): View { onCreateViewGeneral() - return inflater.inflate(R.layout.dialog_fill, container, false) + _binding = DialogFillBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -55,37 +62,43 @@ class FillDialog : DialogFragmentWithDate() { val maxInsulin = constraintChecker.getMaxBolusAllowed().value() val bolusStep = activePlugin.activePump.pumpDescription.bolusStep - fill_insulinamount.setParams(savedInstanceState?.getDouble("fill_insulin_amount") - ?: 0.0, 0.0, maxInsulin, bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), true, ok) + binding.fillInsulinamount.setParams(savedInstanceState?.getDouble("fill_insulin_amount") + ?: 0.0, 0.0, maxInsulin, bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), true, binding.okcancel.ok) val amount1 = sp.getDouble("fill_button1", 0.3) if (amount1 > 0) { - fill_preset_button1.visibility = View.VISIBLE - fill_preset_button1.text = DecimalFormatter.toPumpSupportedBolus(amount1, activePlugin.activePump) // + "U"); - fill_preset_button1.setOnClickListener { fill_insulinamount.value = amount1 } + binding.fillPresetButton1.visibility = View.VISIBLE + binding.fillPresetButton1.text = DecimalFormatter.toPumpSupportedBolus(amount1, activePlugin.activePump) // + "U"); + binding.fillPresetButton1.setOnClickListener { binding.fillInsulinamount.value = amount1 } } else { - fill_preset_button1.visibility = View.GONE + binding.fillPresetButton1.visibility = View.GONE } val amount2 = sp.getDouble("fill_button2", 0.0) if (amount2 > 0) { - fill_preset_button2.visibility = View.VISIBLE - fill_preset_button2.text = DecimalFormatter.toPumpSupportedBolus(amount2, activePlugin.activePump) // + "U"); - fill_preset_button2.setOnClickListener { fill_insulinamount.value = amount2 } + binding.fillPresetButton2.visibility = View.VISIBLE + binding.fillPresetButton2.text = DecimalFormatter.toPumpSupportedBolus(amount2, activePlugin.activePump) // + "U"); + binding.fillPresetButton2.setOnClickListener { binding.fillInsulinamount.value = amount2 } } else { - fill_preset_button2.visibility = View.GONE + binding.fillPresetButton2.visibility = View.GONE } val amount3 = sp.getDouble("fill_button3", 0.0) if (amount3 > 0) { - fill_preset_button3.visibility = View.VISIBLE - fill_preset_button3.text = DecimalFormatter.toPumpSupportedBolus(amount3, activePlugin.activePump) // + "U"); - fill_preset_button3.setOnClickListener { fill_insulinamount.value = amount3 } + binding.fillPresetButton3.visibility = View.VISIBLE + binding.fillPresetButton3.text = DecimalFormatter.toPumpSupportedBolus(amount3, activePlugin.activePump) // + "U"); + binding.fillPresetButton3.setOnClickListener { binding.fillInsulinamount.value = amount3 } } else { - fill_preset_button3.visibility = View.GONE + binding.fillPresetButton3.visibility = View.GONE } } + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + override fun submit(): Boolean { - val insulin = SafeParse.stringToDouble(fill_insulinamount?.text ?: return false) + if (_binding == null) return false + val insulin = SafeParse.stringToDouble(binding.fillInsulinamount.text ?: return false) val actions: LinkedList = LinkedList() val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(insulin)).value() @@ -96,13 +109,13 @@ class FillDialog : DialogFragmentWithDate() { if (abs(insulinAfterConstraints - insulin) > 0.01) actions.add(resourceHelper.gs(R.string.bolusconstraintappliedwarn, insulin, insulinAfterConstraints).formatColor(resourceHelper, R.color.warning)) } - val siteChange = fill_catheter_change.isChecked + val siteChange = binding.fillCatheterChange.isChecked if (siteChange) actions.add(resourceHelper.gs(R.string.record_pump_site_change).formatColor(resourceHelper, R.color.actionsConfirm)) - val insulinChange = fill_cartridge_change.isChecked + val insulinChange = binding.fillCartridgeChange.isChecked if (insulinChange) actions.add(resourceHelper.gs(R.string.record_insulin_cartridge_change).formatColor(resourceHelper, R.color.actionsConfirm)) - val notes = notes.text.toString() + val notes = binding.notesLayout.notes.text.toString() if (notes.isNotEmpty()) actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes) eventTime -= eventTime % 1000 @@ -110,20 +123,20 @@ class FillDialog : DialogFragmentWithDate() { if (eventTimeChanged) actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(eventTime)) - if (insulinAfterConstraints > 0 || fill_catheter_change.isChecked || fill_cartridge_change.isChecked) { + if (insulinAfterConstraints > 0 || binding.fillCatheterChange.isChecked || binding.fillCartridgeChange.isChecked) { activity?.let { activity -> - OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.primefill), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), Runnable { + OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.primefill), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), { if (insulinAfterConstraints > 0) { - aapsLogger.debug("USER ENTRY: PRIME BOLUS $insulinAfterConstraints") + uel.log("PRIME BOLUS", d1 = insulinAfterConstraints) requestPrimeBolus(insulinAfterConstraints, notes) } if (siteChange) { - aapsLogger.debug("USER ENTRY: SITE CHANGE") + uel.log("SITE CHANGE") nsUpload.generateCareportalEvent(CareportalEvent.SITECHANGE, eventTime, notes) } if (insulinChange) { // add a second for case of both checked - aapsLogger.debug("USER ENTRY: INSULIN CHANGE") + uel.log("INSULIN CHANGE") nsUpload.generateCareportalEvent(CareportalEvent.INSULINCHANGE, eventTime + 1000, notes) } }, null) @@ -147,12 +160,7 @@ class FillDialog : DialogFragmentWithDate() { commandQueue.bolus(detailedBolusInfo, object : Callback() { override fun run() { if (!result.success) { - val i = Intent(ctx, ErrorHelperActivity::class.java) - i.putExtra("soundid", R.raw.boluserror) - i.putExtra("status", result.comment) - i.putExtra("title", resourceHelper.gs(R.string.treatmentdeliveryerror)) - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - ctx.startActivity(i) + ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror) } } }) diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt index 881d5f8de0..afc68a1e30 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt @@ -1,7 +1,6 @@ package info.nightscout.androidaps.dialogs import android.content.Context -import android.content.Intent import android.os.Bundle import android.text.Editable import android.text.TextWatcher @@ -15,14 +14,17 @@ import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.ErrorHelperActivity import info.nightscout.androidaps.data.DetailedBolusInfo import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.databinding.DialogInsulinBinding import info.nightscout.androidaps.db.CareportalEvent import info.nightscout.androidaps.db.Source import info.nightscout.androidaps.db.TempTarget import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.interfaces.Constraint -import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.androidaps.logging.UserEntryLogger +import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.utils.* import info.nightscout.androidaps.utils.alertDialogs.OKDialog @@ -30,9 +32,6 @@ import info.nightscout.androidaps.utils.extensions.formatColor import info.nightscout.androidaps.utils.extensions.toSignedString import info.nightscout.androidaps.utils.extensions.toVisibility import info.nightscout.androidaps.utils.resources.ResourceHelper -import kotlinx.android.synthetic.main.dialog_insulin.* -import kotlinx.android.synthetic.main.notes.* -import kotlinx.android.synthetic.main.okcancel.* import java.text.DecimalFormat import java.util.* import javax.inject.Inject @@ -40,6 +39,7 @@ import kotlin.math.abs import kotlin.math.max class InsulinDialog : DialogFragmentWithDate() { + @Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var defaultValueHelper: DefaultValueHelper @@ -47,9 +47,12 @@ class InsulinDialog : DialogFragmentWithDate() { @Inject lateinit var commandQueue: CommandQueueProvider @Inject lateinit var activePlugin: ActivePluginProvider @Inject lateinit var ctx: Context + @Inject lateinit var repository: AppRepository @Inject lateinit var config: Config + @Inject lateinit var uel: UserEntryLogger companion object { + private const val PLUS1_DEFAULT = 0.5 private const val PLUS2_DEFAULT = 1.0 private const val PLUS3_DEFAULT = 2.0 @@ -64,78 +67,91 @@ 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(overview_insulin_time.value.toInt()) > 12 * 60) { - overview_insulin_time.value = 0.0 + if (abs(binding.time.value.toInt()) > 12 * 60) { + binding.time.value = 0.0 ToastUtils.showToastInUiThread(context, resourceHelper.gs(R.string.constraintapllied)) } - if (overview_insulin_amount.value > maxInsulin) { - overview_insulin_amount.value = 0.0 + if (binding.amount.value > maxInsulin) { + binding.amount.value = 0.0 ToastUtils.showToastInUiThread(context, resourceHelper.gs(R.string.bolusconstraintapplied)) } } override fun onSaveInstanceState(savedInstanceState: Bundle) { super.onSaveInstanceState(savedInstanceState) - savedInstanceState.putDouble("overview_insulin_time", overview_insulin_time.value) - savedInstanceState.putDouble("overview_insulin_amount", overview_insulin_amount.value) + savedInstanceState.putDouble("time", binding.time.value) + savedInstanceState.putDouble("amount", binding.amount.value) } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { + savedInstanceState: Bundle?): View { onCreateViewGeneral() - return inflater.inflate(R.layout.dialog_insulin, container, false) + _binding = DialogInsulinBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) if (config.NSCLIENT) { - overview_insulin_record_only.isChecked = true - overview_insulin_record_only.isEnabled = false + binding.recordOnly.isChecked = true + binding.recordOnly.isEnabled = false } val maxInsulin = constraintChecker.getMaxBolusAllowed().value() - overview_insulin_time.setParams(savedInstanceState?.getDouble("overview_insulin_time") - ?: 0.0, -12 * 60.0, 12 * 60.0, 5.0, DecimalFormat("0"), false, ok, textWatcher) - overview_insulin_amount.setParams(savedInstanceState?.getDouble("overview_insulin_amount") - ?: 0.0, 0.0, maxInsulin, activePlugin.activePump.pumpDescription.bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, ok, textWatcher) + binding.time.setParams(savedInstanceState?.getDouble("time") + ?: 0.0, -12 * 60.0, 12 * 60.0, 5.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher) + binding.amount.setParams(savedInstanceState?.getDouble("amount") + ?: 0.0, 0.0, maxInsulin, activePlugin.activePump.pumpDescription.bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, binding.okcancel.ok, textWatcher) - overview_insulin_plus05.text = sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_1), PLUS1_DEFAULT).toSignedString(activePlugin.activePump) - overview_insulin_plus05.setOnClickListener { - overview_insulin_amount.value = max(0.0, overview_insulin_amount.value + binding.plus05.text = sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_1), PLUS1_DEFAULT).toSignedString(activePlugin.activePump) + binding.plus05.setOnClickListener { + binding.amount.value = max(0.0, binding.amount.value + sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_1), PLUS1_DEFAULT)) validateInputs() } - overview_insulin_plus10.text = sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_2), PLUS2_DEFAULT).toSignedString(activePlugin.activePump) - overview_insulin_plus10.setOnClickListener { - overview_insulin_amount.value = max(0.0, overview_insulin_amount.value + binding.plus10.text = sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_2), PLUS2_DEFAULT).toSignedString(activePlugin.activePump) + binding.plus10.setOnClickListener { + binding.amount.value = max(0.0, binding.amount.value + sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_2), PLUS2_DEFAULT)) validateInputs() } - overview_insulin_plus20.text = sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_3), PLUS3_DEFAULT).toSignedString(activePlugin.activePump) - overview_insulin_plus20.setOnClickListener { - overview_insulin_amount.value = max(0.0, overview_insulin_amount.value + binding.plus20.text = sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_3), PLUS3_DEFAULT).toSignedString(activePlugin.activePump) + binding.plus20.setOnClickListener { + binding.amount.value = max(0.0, binding.amount.value + sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_3), PLUS3_DEFAULT)) validateInputs() } - overview_insulin_time_layout.visibility = View.GONE - overview_insulin_record_only.setOnCheckedChangeListener { _, isChecked: Boolean -> - overview_insulin_time_layout.visibility = isChecked.toVisibility() + binding.timeLayout.visibility = View.GONE + binding.recordOnly.setOnCheckedChangeListener { _, isChecked: Boolean -> + binding.timeLayout.visibility = isChecked.toVisibility() } } + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + override fun submit(): Boolean { + if (_binding == null) return false val pumpDescription = activePlugin.activePump.pumpDescription - val insulin = SafeParse.stringToDouble(overview_insulin_amount?.text ?: return false) + val insulin = SafeParse.stringToDouble(binding.amount.text ?: return false) val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(insulin)).value() val actions: LinkedList = LinkedList() val units = profileFunction.getUnits() val unitLabel = if (units == Constants.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl) - val recordOnlyChecked = overview_insulin_record_only.isChecked - val eatingSoonChecked = overview_insulin_start_eating_soon_tt.isChecked + val recordOnlyChecked = binding.recordOnly.isChecked + val eatingSoonChecked = binding.startEatingSoonTt.isChecked if (insulinAfterConstraints > 0) { actions.add(resourceHelper.gs(R.string.bolus) + ": " + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints, activePlugin.activePump, resourceHelper).formatColor(resourceHelper, R.color.bolus)) @@ -149,20 +165,20 @@ class InsulinDialog : DialogFragmentWithDate() { if (eatingSoonChecked) actions.add(resourceHelper.gs(R.string.temptargetshort) + ": " + (DecimalFormatter.to1Decimal(eatingSoonTT) + " " + unitLabel + " (" + resourceHelper.gs(R.string.format_mins, eatingSoonTTDuration) + ")").formatColor(resourceHelper, R.color.tempTargetConfirmation)) - val timeOffset = overview_insulin_time.value.toInt() + val timeOffset = binding.time.value.toInt() val time = DateUtil.now() + T.mins(timeOffset.toLong()).msecs() if (timeOffset != 0) actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(time)) - val notes = notes.text.toString() + val notes = binding.notesLayout.notes.text.toString() if (notes.isNotEmpty()) actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes) if (insulinAfterConstraints > 0 || eatingSoonChecked) { activity?.let { activity -> - OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.bolus), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), Runnable { + OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.bolus), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), { if (eatingSoonChecked) { - aapsLogger.debug("USER ENTRY: TEMPTARGET EATING SOON $eatingSoonTT duration: $eatingSoonTTDuration") + uel.log("TT EATING SOON", d1 = eatingSoonTT, i1 = eatingSoonTTDuration) val tempTarget = TempTarget() .date(System.currentTimeMillis()) .duration(eatingSoonTTDuration) @@ -180,21 +196,16 @@ class InsulinDialog : DialogFragmentWithDate() { detailedBolusInfo.source = Source.USER detailedBolusInfo.notes = notes if (recordOnlyChecked) { - aapsLogger.debug("USER ENTRY: BOLUS RECORD ONLY $insulinAfterConstraints") + uel.log("BOLUS RECORD", d1 = insulinAfterConstraints, i1 = timeOffset) detailedBolusInfo.date = time activePlugin.activeTreatments.addToHistoryTreatment(detailedBolusInfo, false) } else { - aapsLogger.debug("USER ENTRY: BOLUS $insulinAfterConstraints") + uel.log("BOLUS", d1 = insulinAfterConstraints) detailedBolusInfo.date = DateUtil.now() commandQueue.bolus(detailedBolusInfo, object : Callback() { override fun run() { if (!result.success) { - val i = Intent(ctx, ErrorHelperActivity::class.java) - i.putExtra("soundid", R.raw.boluserror) - i.putExtra("status", result.comment) - i.putExtra("title", resourceHelper.gs(R.string.treatmentdeliveryerror)) - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - ctx.startActivity(i) + ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror) } } }) diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/LoopDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/LoopDialog.kt index eeda33c9b3..484e445136 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/LoopDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/LoopDialog.kt @@ -1,7 +1,8 @@ package info.nightscout.androidaps.dialogs -import android.content.Intent +import android.content.Context import android.os.Bundle +import android.os.Handler import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -9,7 +10,6 @@ import android.view.Window import android.view.WindowManager import androidx.fragment.app.FragmentManager import dagger.android.support.DaggerDialogFragment -import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.ErrorHelperActivity import info.nightscout.androidaps.databinding.DialogLoopBinding @@ -17,8 +17,8 @@ import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.events.EventRefreshOverview import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin -import info.nightscout.androidaps.plugins.aps.loop.events.EventNewOpenLoopNotification import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker @@ -30,14 +30,12 @@ import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.extensions.toVisibility import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.CompositeDisposable import javax.inject.Inject class LoopDialog : DaggerDialogFragment() { @Inject lateinit var aapsLogger: AAPSLogger - @Inject lateinit var mainApp: MainApp + @Inject lateinit var ctx: Context @Inject lateinit var sp: SP @Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var fabricPrivacy: FabricPrivacy @@ -49,11 +47,12 @@ class LoopDialog : DaggerDialogFragment() { @Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var commandQueue: CommandQueueProvider @Inject lateinit var configBuilderPlugin: ConfigBuilderPlugin - - private var disposable: CompositeDisposable = CompositeDisposable() + @Inject lateinit var uel: UserEntryLogger private var showOkCancel: Boolean = true private var _binding: DialogLoopBinding? = null + private var loopHandler = Handler() + private var refreshDialog: Runnable? = null // This property is only valid between onCreateView and // onDestroyView. @@ -107,21 +106,33 @@ class LoopDialog : DaggerDialogFragment() { // cancel button binding.cancel.setOnClickListener { dismiss() } - // bus - disposable.add(rxBus - .toObservable(EventNewOpenLoopNotification::class.java) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ - activity?.runOnUiThread { updateGUI("EventNewOpenLoopNotification") } - }, fabricPrivacy::logException) - ) + refreshDialog = Runnable { + scheduleUpdateGUI("refreshDialog") + loopHandler.postDelayed(refreshDialog, 15 * 1000L) + } + loopHandler.postDelayed(refreshDialog, 15 * 1000L) } @Synchronized override fun onDestroyView() { super.onDestroyView() - disposable.clear() _binding = null + loopHandler.removeCallbacksAndMessages(null) + } + + var task: Runnable? = null + + private fun scheduleUpdateGUI(from: String) { + class UpdateRunnable : Runnable { + + override fun run() { + updateGUI(from) + task = null + } + } + view?.removeCallbacks(task) + task = UpdateRunnable() + view?.postDelayed(task, 500) } @Synchronized @@ -190,14 +201,14 @@ class LoopDialog : DaggerDialogFragment() { val profileStore = activePlugin.activeProfileInterface.profile if (profile == null || profileStore == null) { - ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.noprofile)) + ToastUtils.showToastInUiThread(ctx, resourceHelper.gs(R.string.noprofile)) dismiss() return } } - fun onClickOkCancelEnabled(v: View): Boolean { + private fun onClickOkCancelEnabled(v: View): Boolean { var description = "" when (v.id) { R.id.overview_closeloop -> description = resourceHelper.gs(R.string.closedloop) @@ -229,25 +240,28 @@ class LoopDialog : DaggerDialogFragment() { val profile = profileFunction.getProfile() ?: return true when (v.id) { R.id.overview_closeloop -> { + uel.log("CLOSED LOOP MODE") sp.putString(R.string.key_aps_mode, "closed") rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.closedloop))) return true } R.id.overview_lgsloop -> { + uel.log("LGS LOOP MODE") sp.putString(R.string.key_aps_mode, "lgs") rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.lowglucosesuspend))) return true } R.id.overview_openloop -> { + uel.log("OPEN LOOP MODE") sp.putString(R.string.key_aps_mode, "open") rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.lowglucosesuspend))) return true } R.id.overview_disable -> { - aapsLogger.debug("USER ENTRY: LOOP DISABLED") + uel.log("LOOP DISABLED") loopPlugin.setPluginEnabled(PluginType.LOOP, false) loopPlugin.setFragmentVisible(PluginType.LOOP, false) configBuilderPlugin.storeSettings("DisablingLoop") @@ -255,7 +269,7 @@ class LoopDialog : DaggerDialogFragment() { commandQueue.cancelTempBasal(true, object : Callback() { override fun run() { if (!result.success) { - ToastUtils.showToastInUiThread(context, resourceHelper.gs(R.string.tempbasaldeliveryerror)) + ToastUtils.showToastInUiThread(ctx, resourceHelper.gs(R.string.tempbasaldeliveryerror)) } } }) @@ -264,7 +278,7 @@ class LoopDialog : DaggerDialogFragment() { } R.id.overview_enable -> { - aapsLogger.debug("USER ENTRY: LOOP ENABLED") + uel.log("LOOP ENABLED") loopPlugin.setPluginEnabled(PluginType.LOOP, true) loopPlugin.setFragmentVisible(PluginType.LOOP, true) configBuilderPlugin.storeSettings("EnablingLoop") @@ -274,18 +288,13 @@ class LoopDialog : DaggerDialogFragment() { } R.id.overview_resume, R.id.overview_reconnect -> { - aapsLogger.debug("USER ENTRY: RESUME") + uel.log("RESUME") loopPlugin.suspendTo(0L) rxBus.send(EventRefreshOverview("suspendmenu")) commandQueue.cancelTempBasal(true, object : Callback() { override fun run() { if (!result.success) { - val i = Intent(context, ErrorHelperActivity::class.java) - i.putExtra("soundid", R.raw.boluserror) - i.putExtra("status", result.comment) - i.putExtra("title", resourceHelper.gs(R.string.tempbasaldeliveryerror)) - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - context?.startActivity(i) + ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror) } } }) @@ -295,49 +304,49 @@ class LoopDialog : DaggerDialogFragment() { } R.id.overview_suspend_1h -> { - aapsLogger.debug("USER ENTRY: SUSPEND 1h") + uel.log("SUSPEND 1h") loopPlugin.suspendLoop(60) rxBus.send(EventRefreshOverview("suspendmenu")) return true } R.id.overview_suspend_2h -> { - aapsLogger.debug("USER ENTRY: SUSPEND 2h") + uel.log("SUSPEND 2h") loopPlugin.suspendLoop(120) rxBus.send(EventRefreshOverview("suspendmenu")) return true } R.id.overview_suspend_3h -> { - aapsLogger.debug("USER ENTRY: SUSPEND 3h") + uel.log("SUSPEND 3h") loopPlugin.suspendLoop(180) rxBus.send(EventRefreshOverview("suspendmenu")) return true } R.id.overview_suspend_10h -> { - aapsLogger.debug("USER ENTRY: SUSPEND 10h") + uel.log("SUSPEND 10h") loopPlugin.suspendLoop(600) rxBus.send(EventRefreshOverview("suspendmenu")) return true } R.id.overview_disconnect_15m -> { - aapsLogger.debug("USER ENTRY: DISCONNECT 15m") + uel.log("DISCONNECT 15m") loopPlugin.disconnectPump(15, profile) rxBus.send(EventRefreshOverview("suspendmenu")) return true } R.id.overview_disconnect_30m -> { - aapsLogger.debug("USER ENTRY: DISCONNECT 30m") + uel.log("DISCONNECT 30m") loopPlugin.disconnectPump(30, profile) rxBus.send(EventRefreshOverview("suspendmenu")) return true } R.id.overview_disconnect_1h -> { - aapsLogger.debug("USER ENTRY: DISCONNECT 1h") + uel.log("DISCONNECT 1h") loopPlugin.disconnectPump(60, profile) sp.putBoolean(R.string.key_objectiveusedisconnect, true) rxBus.send(EventRefreshOverview("suspendmenu")) @@ -345,14 +354,14 @@ class LoopDialog : DaggerDialogFragment() { } R.id.overview_disconnect_2h -> { - aapsLogger.debug("USER ENTRY: DISCONNECT 2h") + uel.log("DISCONNECT 2h") loopPlugin.disconnectPump(120, profile) rxBus.send(EventRefreshOverview("suspendmenu")) return true } R.id.overview_disconnect_3h -> { - aapsLogger.debug("USER ENTRY: DISCONNECT 3h") + uel.log("DISCONNECT 3h") loopPlugin.disconnectPump(180, profile) rxBus.send(EventRefreshOverview("suspendmenu")) return true diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt index 31cc463bdc..c4ed84ba45 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt @@ -8,53 +8,61 @@ import android.widget.ArrayAdapter import com.google.common.base.Joiner import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R +import info.nightscout.androidaps.databinding.DialogProfileswitchBinding import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.resources.ResourceHelper -import kotlinx.android.synthetic.main.dialog_profileswitch.* -import kotlinx.android.synthetic.main.notes.* -import kotlinx.android.synthetic.main.okcancel.* import java.text.DecimalFormat import java.util.* import javax.inject.Inject class ProfileSwitchDialog : DialogFragmentWithDate() { + @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var treatmentsPlugin: TreatmentsPlugin @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var uel: UserEntryLogger - var profileIndex: Int? = null + private var profileIndex: Int? = null + + private var _binding: DialogProfileswitchBinding? = 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("overview_profileswitch_duration", overview_profileswitch_duration.value) - savedInstanceState.putDouble("overview_profileswitch_percentage", overview_profileswitch_percentage.value) - savedInstanceState.putDouble("overview_profileswitch_timeshift", overview_profileswitch_timeshift.value) + savedInstanceState.putDouble("duration", binding.duration.value) + savedInstanceState.putDouble("percentage", binding.percentage.value) + savedInstanceState.putDouble("timeshift", binding.timeshift.value) } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { + savedInstanceState: Bundle?): View { onCreateViewGeneral() arguments?.let { bundle -> profileIndex = bundle.getInt("profileIndex", 0) } - return inflater.inflate(R.layout.dialog_profileswitch, container, false) + _binding = DialogProfileswitchBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - overview_profileswitch_duration.setParams(savedInstanceState?.getDouble("overview_profileswitch_duration") - ?: 0.0, 0.0, Constants.MAX_PROFILE_SWITCH_DURATION, 10.0, DecimalFormat("0"), false, ok) - overview_profileswitch_percentage.setParams(savedInstanceState?.getDouble("overview_profileswitch_percentage") - ?: 100.0, Constants.CPP_MIN_PERCENTAGE.toDouble(), Constants.CPP_MAX_PERCENTAGE.toDouble(), 5.0, DecimalFormat("0"), false, ok) - overview_profileswitch_timeshift.setParams(savedInstanceState?.getDouble("overview_profileswitch_timeshift") - ?: 0.0, Constants.CPP_MIN_TIMESHIFT.toDouble(), Constants.CPP_MAX_TIMESHIFT.toDouble(), 1.0, DecimalFormat("0"), false, ok) + binding.duration.setParams(savedInstanceState?.getDouble("duration") + ?: 0.0, 0.0, Constants.MAX_PROFILE_SWITCH_DURATION, 10.0, DecimalFormat("0"), false, binding.okcancel.ok) + binding.percentage.setParams(savedInstanceState?.getDouble("percentage") + ?: 100.0, Constants.CPP_MIN_PERCENTAGE.toDouble(), Constants.CPP_MAX_PERCENTAGE.toDouble(), 5.0, DecimalFormat("0"), false, binding.okcancel.ok) + binding.timeshift.setParams(savedInstanceState?.getDouble("timeshift") + ?: 0.0, Constants.CPP_MIN_TIMESHIFT.toDouble(), Constants.CPP_MAX_TIMESHIFT.toDouble(), 1.0, DecimalFormat("0"), false, binding.okcancel.ok) // profile context?.let { context -> @@ -62,55 +70,61 @@ class ProfileSwitchDialog : DialogFragmentWithDate() { ?: return val profileList = profileStore.getProfileList() val adapter = ArrayAdapter(context, R.layout.spinner_centered, profileList) - overview_profileswitch_profile.adapter = adapter + binding.profile.adapter = adapter // set selected to actual profile if (profileIndex != null) - overview_profileswitch_profile.setSelection(profileIndex as Int) + binding.profile.setSelection(profileIndex as Int) else for (p in profileList.indices) if (profileList[p] == profileFunction.getProfileName(false)) - overview_profileswitch_profile.setSelection(p) + binding.profile.setSelection(p) } ?: return treatmentsPlugin.getProfileSwitchFromHistory(DateUtil.now())?.let { ps -> if (ps.isCPP) { - overview_profileswitch_reuselayout.visibility = View.VISIBLE - overview_profileswitch_reusebutton.text = resourceHelper.gs(R.string.reuse) + " " + ps.percentage + "% " + ps.timeshift + "h" - overview_profileswitch_reusebutton.setOnClickListener { - overview_profileswitch_percentage.value = ps.percentage.toDouble() - overview_profileswitch_timeshift.value = ps.timeshift.toDouble() + binding.reuselayout.visibility = View.VISIBLE + binding.reusebutton.text = resourceHelper.gs(R.string.reuse_profile_pct_hours, ps.percentage, ps.timeshift) + binding.reusebutton.setOnClickListener { + binding.percentage.value = ps.percentage.toDouble() + binding.timeshift.value = ps.timeshift.toDouble() } } else { - overview_profileswitch_reuselayout.visibility = View.GONE + binding.reuselayout.visibility = View.GONE } } } + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + override fun submit(): Boolean { + if (_binding == null) return false val profileStore = activePlugin.activeProfileInterface.profile ?: return false val actions: LinkedList = LinkedList() - val duration = overview_profileswitch_duration?.value?.toInt() ?: return false + val duration = binding.duration.value?.toInt() ?: return false if (duration > 0) actions.add(resourceHelper.gs(R.string.duration) + ": " + resourceHelper.gs(R.string.format_mins, duration)) - val profile = overview_profileswitch_profile.selectedItem.toString() + val profile = binding.profile.selectedItem.toString() actions.add(resourceHelper.gs(R.string.profile) + ": " + profile) - val percent = overview_profileswitch_percentage.value.toInt() + val percent = binding.percentage.value.toInt() if (percent != 100) actions.add(resourceHelper.gs(R.string.percent) + ": " + percent + "%") - val timeShift = overview_profileswitch_timeshift.value.toInt() + val timeShift = binding.timeshift.value.toInt() if (timeShift != 0) actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_timeshift_label) + ": " + resourceHelper.gs(R.string.format_hours, timeShift.toDouble())) - val notes = notes.text.toString() + val notes = binding.notesLayout.notes.text.toString() if (notes.isNotEmpty()) actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes) if (eventTimeChanged) actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(eventTime)) activity?.let { activity -> - OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal_profileswitch), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), Runnable { - aapsLogger.debug("USER ENTRY: PROFILE SWITCH $profile percent: $percent timeshift: $timeShift duration: $duration") + OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal_profileswitch), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), { + uel.log("PROFILE SWITCH", d1 = percent.toDouble(), i1 = timeShift, i2 = duration) treatmentsPlugin.doProfileSwitch(profileStore, profile, duration, percent, timeShift, eventTime) }) } diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/TempBasalDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/TempBasalDialog.kt index cf8fad96b6..b72625a664 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/TempBasalDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/TempBasalDialog.kt @@ -1,7 +1,6 @@ package info.nightscout.androidaps.dialogs import android.content.Context -import android.content.Intent import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -9,46 +8,55 @@ import android.view.ViewGroup import com.google.common.base.Joiner import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.ErrorHelperActivity +import info.nightscout.androidaps.databinding.DialogTempbasalBinding import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.interfaces.Constraint -import info.nightscout.androidaps.interfaces.PumpDescription -import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.androidaps.interfaces.PumpDescription +import info.nightscout.androidaps.logging.UserEntryLogger +import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.utils.HtmlHelper -import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.SafeParse +import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.extensions.formatColor import info.nightscout.androidaps.utils.resources.ResourceHelper -import kotlinx.android.synthetic.main.dialog_tempbasal.* -import kotlinx.android.synthetic.main.okcancel.* import java.text.DecimalFormat import java.util.* import javax.inject.Inject import kotlin.math.abs class TempBasalDialog : DialogFragmentWithDate() { + @Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var activePlugin: ActivePluginProvider @Inject lateinit var commandQueue: CommandQueueProvider @Inject lateinit var ctx: Context + @Inject lateinit var uel: UserEntryLogger private var isPercentPump = true + private var _binding: DialogTempbasalBinding? = 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("actions_tempbasal_duration", actions_tempbasal_duration.value) - savedInstanceState.putDouble("actions_tempbasal_basalpercentinput", actions_tempbasal_basalpercentinput.value) - savedInstanceState.putDouble("actions_tempbasal_basalabsoluteinput", actions_tempbasal_basalabsoluteinput.value) + savedInstanceState.putDouble("duration", binding.duration.value) + savedInstanceState.putDouble("basalpercentinput", binding.basalpercentinput.value) + savedInstanceState.putDouble("basalabsoluteinput", binding.basalabsoluteinput.value) } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { + savedInstanceState: Bundle?): View { onCreateViewGeneral() - return inflater.inflate(R.layout.dialog_tempbasal, container, false) + _binding = DialogTempbasalBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -60,41 +68,47 @@ class TempBasalDialog : DialogFragmentWithDate() { val maxTempPercent = pumpDescription.maxTempPercent.toDouble() val tempPercentStep = pumpDescription.tempPercentStep.toDouble() - actions_tempbasal_basalpercentinput.setParams(savedInstanceState?.getDouble("actions_tempbasal_basalpercentinput") - ?: 100.0, 0.0, maxTempPercent, tempPercentStep, DecimalFormat("0"), true, ok) + binding.basalpercentinput.setParams(savedInstanceState?.getDouble("basalpercentinput") + ?: 100.0, 0.0, maxTempPercent, tempPercentStep, DecimalFormat("0"), true, binding.okcancel.ok) - actions_tempbasal_basalabsoluteinput.setParams(savedInstanceState?.getDouble("actions_tempbasal_basalabsoluteinput") - ?: profile.basal, 0.0, pumpDescription.maxTempAbsolute, pumpDescription.tempAbsoluteStep, DecimalFormat("0.00"), true, ok) + binding.basalabsoluteinput.setParams(savedInstanceState?.getDouble("basalabsoluteinput") + ?: profile.basal, 0.0, pumpDescription.maxTempAbsolute, pumpDescription.tempAbsoluteStep, DecimalFormat("0.00"), true, binding.okcancel.ok) val tempDurationStep = pumpDescription.tempDurationStep.toDouble() val tempMaxDuration = pumpDescription.tempMaxDuration.toDouble() - actions_tempbasal_duration.setParams(savedInstanceState?.getDouble("actions_tempbasal_duration") - ?: tempDurationStep, tempDurationStep, tempMaxDuration, tempDurationStep, DecimalFormat("0"), false, ok) + binding.duration.setParams(savedInstanceState?.getDouble("duration") + ?: tempDurationStep, tempDurationStep, tempMaxDuration, tempDurationStep, DecimalFormat("0"), false, binding.okcancel.ok) isPercentPump = pumpDescription.tempBasalStyle and PumpDescription.PERCENT == PumpDescription.PERCENT if (isPercentPump) { - actions_tempbasal_percent_layout.visibility = View.VISIBLE - actions_tempbasal_absolute_layout.visibility = View.GONE + binding.percentLayout.visibility = View.VISIBLE + binding.absoluteLayout.visibility = View.GONE } else { - actions_tempbasal_percent_layout.visibility = View.GONE - actions_tempbasal_absolute_layout.visibility = View.VISIBLE + binding.percentLayout.visibility = View.GONE + binding.absoluteLayout.visibility = View.VISIBLE } } + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + override fun submit(): Boolean { + if (_binding == null) return false var percent = 0 var absolute = 0.0 - val durationInMinutes = actions_tempbasal_duration?.value?.toInt() ?: return false + val durationInMinutes = binding.duration.value?.toInt() ?: return false val profile = profileFunction.getProfile() ?: return false val actions: LinkedList = LinkedList() if (isPercentPump) { - val basalPercentInput = SafeParse.stringToInt(actions_tempbasal_basalpercentinput.text) + val basalPercentInput = SafeParse.stringToInt(binding.basalpercentinput.text) percent = constraintChecker.applyBasalPercentConstraints(Constraint(basalPercentInput), profile).value() actions.add(resourceHelper.gs(R.string.tempbasal_label) + ": $percent%") actions.add(resourceHelper.gs(R.string.duration) + ": " + resourceHelper.gs(R.string.format_mins, durationInMinutes)) if (percent != basalPercentInput) actions.add(resourceHelper.gs(R.string.constraintapllied)) } else { - val basalAbsoluteInput = SafeParse.stringToDouble(actions_tempbasal_basalabsoluteinput.text) + val basalAbsoluteInput = SafeParse.stringToDouble(binding.basalabsoluteinput.text) absolute = constraintChecker.applyBasalConstraints(Constraint(basalAbsoluteInput), profile).value() actions.add(resourceHelper.gs(R.string.tempbasal_label) + ": " + resourceHelper.gs(R.string.pump_basebasalrate, absolute)) actions.add(resourceHelper.gs(R.string.duration) + ": " + resourceHelper.gs(R.string.format_mins, durationInMinutes)) @@ -102,24 +116,19 @@ class TempBasalDialog : DialogFragmentWithDate() { actions.add(resourceHelper.gs(R.string.constraintapllied).formatColor(resourceHelper, R.color.warning)) } activity?.let { activity -> - OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.tempbasal_label), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), Runnable { + OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.tempbasal_label), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), { val callback: Callback = object : Callback() { override fun run() { if (!result.success) { - val i = Intent(ctx, ErrorHelperActivity::class.java) - i.putExtra("soundid", R.raw.boluserror) - i.putExtra("status", result.comment) - i.putExtra("title", resourceHelper.gs(R.string.tempbasaldeliveryerror)) - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - ctx.startActivity(i) + ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror) } } } if (isPercentPump) { - aapsLogger.debug("USER ENTRY: TEMP BASAL $percent% duration: $durationInMinutes") + uel.log("TEMP BASAL", d1 = percent.toDouble(), i1 = durationInMinutes) commandQueue.tempBasalPercent(percent, durationInMinutes, true, profile, callback) } else { - aapsLogger.debug("USER ENTRY: TEMP BASAL $absolute duration: $durationInMinutes") + uel.log("TEMP BASAL", d1 = absolute, i1 = durationInMinutes) commandQueue.tempBasalAbsolute(absolute, durationInMinutes, true, profile, callback) } }) diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/TempTargetDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/TempTargetDialog.kt index 57c1689ada..fbe5123035 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/TempTargetDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/TempTargetDialog.kt @@ -4,77 +4,85 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.AdapterView import android.widget.ArrayAdapter import com.google.common.base.Joiner import com.google.common.collect.Lists import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.databinding.DialogTemptargetBinding import info.nightscout.androidaps.db.Source import info.nightscout.androidaps.db.TempTarget import info.nightscout.androidaps.interfaces.ActivePluginProvider -import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.androidaps.logging.UserEntryLogger +import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.utils.DefaultValueHelper import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.resources.ResourceHelper -import kotlinx.android.synthetic.main.dialog_temptarget.* -import kotlinx.android.synthetic.main.okcancel.* import java.text.DecimalFormat import java.util.* import javax.inject.Inject class TempTargetDialog : DialogFragmentWithDate() { + @Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var defaultValueHelper: DefaultValueHelper @Inject lateinit var treatmentsPlugin: TreatmentsPlugin @Inject lateinit var activePlugin: ActivePluginProvider + @Inject lateinit var uel: UserEntryLogger - lateinit var reasonList: List + private lateinit var reasonList: List + + private var _binding: DialogTemptargetBinding? = 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("overview_temptarget_duration", overview_temptarget_duration.value) - savedInstanceState.putDouble("overview_temptarget_temptarget", overview_temptarget_temptarget.value) + savedInstanceState.putDouble("duration", binding.duration.value) + savedInstanceState.putDouble("temptarget", binding.temptarget.value) } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { + savedInstanceState: Bundle?): View { onCreateViewGeneral() - return inflater.inflate(R.layout.dialog_temptarget, container, false) + _binding = DialogTemptargetBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - overview_temptarget_duration.setParams(savedInstanceState?.getDouble("overview_temptarget_duration") - ?: 0.0, 0.0, Constants.MAX_PROFILE_SWITCH_DURATION, 10.0, DecimalFormat("0"), false, ok) + binding.duration.setParams(savedInstanceState?.getDouble("duration") + ?: 0.0, 0.0, Constants.MAX_PROFILE_SWITCH_DURATION, 10.0, DecimalFormat("0"), false, binding.okcancel.ok) if (profileFunction.getUnits() == Constants.MMOL) - overview_temptarget_temptarget.setParams( - savedInstanceState?.getDouble("overview_temptarget_temptarget") - ?: Constants.MIN_TT_MMOL, - Constants.MIN_TT_MMOL, Constants.MAX_TT_MMOL, 0.1, DecimalFormat("0.0"), false, ok) + binding.temptarget.setParams( + savedInstanceState?.getDouble("temptarget") + ?: 8.0, + Constants.MIN_TT_MMOL, Constants.MAX_TT_MMOL, 0.1, DecimalFormat("0.0"), false, binding.okcancel.ok) else - overview_temptarget_temptarget.setParams( - savedInstanceState?.getDouble("overview_temptarget_temptarget") - ?: Constants.MIN_TT_MGDL, - Constants.MIN_TT_MGDL, Constants.MAX_TT_MGDL, 1.0, DecimalFormat("0"), false, ok) + binding.temptarget.setParams( + savedInstanceState?.getDouble("temptarget") + ?: 144.0, + Constants.MIN_TT_MGDL, Constants.MAX_TT_MGDL, 1.0, DecimalFormat("0"), false, binding.okcancel.ok) val units = profileFunction.getUnits() - overview_temptarget_units.text = if (units == Constants.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl) + binding.units.text = if (units == Constants.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl) // temp target context?.let { context -> if (activePlugin.activeTreatments.tempTargetFromHistory != null) - overview_temptarget_cancel?.visibility = View.VISIBLE + binding.targetCancel.visibility = View.VISIBLE else - overview_temptarget_cancel?.visibility = View.GONE + binding.targetCancel.visibility = View.GONE reasonList = Lists.newArrayList( resourceHelper.gs(R.string.manual), @@ -83,59 +91,67 @@ class TempTargetDialog : DialogFragmentWithDate() { resourceHelper.gs(R.string.hypo) ) val adapterReason = ArrayAdapter(context, R.layout.spinner_centered, reasonList) - overview_temptarget_reason.adapter = adapterReason + binding.reason.adapter = adapterReason - overview_temptarget_cancel?.setOnClickListener { shortClick(it) } - overview_temptarget_eating_soon?.setOnClickListener { shortClick(it) } - overview_temptarget_activity?.setOnClickListener { shortClick(it) } - overview_temptarget_hypo?.setOnClickListener { shortClick(it) } + binding.targetCancel.setOnClickListener { shortClick(it) } + binding.eatingSoon.setOnClickListener { shortClick(it) } + binding.activity.setOnClickListener { shortClick(it) } + binding.hypo.setOnClickListener { shortClick(it) } - overview_temptarget_eating_soon?.setOnLongClickListener { + binding.eatingSoon.setOnLongClickListener { longClick(it) return@setOnLongClickListener true } - overview_temptarget_activity?.setOnLongClickListener { + binding.activity.setOnLongClickListener { longClick(it) return@setOnLongClickListener true } - overview_temptarget_hypo?.setOnLongClickListener { + binding.hypo.setOnLongClickListener { longClick(it) return@setOnLongClickListener true } } } - private fun shortClick(v:View){ + private fun shortClick(v: View) { v.performLongClick() if (submit()) dismiss() } - private fun longClick(v:View) { + private fun longClick(v: View) { when (v.id) { - R.id.overview_temptarget_eating_soon -> { - overview_temptarget_temptarget.value = defaultValueHelper.determineEatingSoonTT() - overview_temptarget_duration.value = defaultValueHelper.determineEatingSoonTTDuration().toDouble() - overview_temptarget_reason.setSelection(reasonList.indexOf( resourceHelper.gs(R.string.eatingsoon))) + R.id.eating_soon -> { + binding.temptarget.value = defaultValueHelper.determineEatingSoonTT() + binding.duration.value = defaultValueHelper.determineEatingSoonTTDuration().toDouble() + binding.reason.setSelection(reasonList.indexOf(resourceHelper.gs(R.string.eatingsoon))) } - R.id.overview_temptarget_activity -> { - overview_temptarget_temptarget.value = defaultValueHelper.determineActivityTT() - overview_temptarget_duration.value = defaultValueHelper.determineActivityTTDuration().toDouble() - overview_temptarget_reason.setSelection(reasonList.indexOf(resourceHelper.gs(R.string.activity))) + + R.id.activity -> { + binding.temptarget.value = defaultValueHelper.determineActivityTT() + binding.duration.value = defaultValueHelper.determineActivityTTDuration().toDouble() + binding.reason.setSelection(reasonList.indexOf(resourceHelper.gs(R.string.activity))) } - R.id.overview_temptarget_hypo -> { - overview_temptarget_temptarget.value = defaultValueHelper.determineHypoTT() - overview_temptarget_duration.value = defaultValueHelper.determineHypoTTDuration().toDouble() - overview_temptarget_reason.setSelection(reasonList.indexOf(resourceHelper.gs(R.string.hypo))) + + R.id.hypo -> { + binding.temptarget.value = defaultValueHelper.determineHypoTT() + binding.duration.value = defaultValueHelper.determineHypoTTDuration().toDouble() + binding.reason.setSelection(reasonList.indexOf(resourceHelper.gs(R.string.hypo))) } } } + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + override fun submit(): Boolean { + if (_binding == null) return false val actions: LinkedList = LinkedList() - val reason = overview_temptarget_reason?.selectedItem?.toString() ?: return false + val reason = binding.reason.selectedItem?.toString() ?: return false val unitResId = if (profileFunction.getUnits() == Constants.MGDL) R.string.mgdl else R.string.mmol - val target = overview_temptarget_temptarget.value - val duration = overview_temptarget_duration.value.toInt() + val target = binding.temptarget.value + val duration = binding.duration.value.toInt() if (target != 0.0 && duration != 0) { actions.add(resourceHelper.gs(R.string.reason) + ": " + reason) actions.add(resourceHelper.gs(R.string.target_label) + ": " + Profile.toCurrentUnitsString(profileFunction, target) + " " + resourceHelper.gs(unitResId)) @@ -147,8 +163,8 @@ class TempTargetDialog : DialogFragmentWithDate() { actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(eventTime)) activity?.let { activity -> - OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal_temporarytarget), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), Runnable { - aapsLogger.debug("USER ENTRY: TEMP TARGET $target duration: $duration") + OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal_temporarytarget), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), { + uel.log("TT", d1 = target, i1 = duration) if (target == 0.0 || duration == 0) { val tempTarget = TempTarget() .date(eventTime) diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/TreatmentDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/TreatmentDialog.kt index c54f7b8378..fe45d727a2 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/TreatmentDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/TreatmentDialog.kt @@ -1,7 +1,6 @@ package info.nightscout.androidaps.dialogs import android.content.Context -import android.content.Intent import android.os.Bundle import android.text.Editable import android.text.TextWatcher @@ -13,35 +12,36 @@ import info.nightscout.androidaps.Config import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.ErrorHelperActivity import info.nightscout.androidaps.data.DetailedBolusInfo +import info.nightscout.androidaps.databinding.DialogTreatmentBinding import info.nightscout.androidaps.db.CareportalEvent import info.nightscout.androidaps.db.Source import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.interfaces.Constraint +import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.utils.DecimalFormatter import info.nightscout.androidaps.utils.HtmlHelper -import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.SafeParse import info.nightscout.androidaps.utils.ToastUtils +import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.extensions.formatColor import info.nightscout.androidaps.utils.resources.ResourceHelper -import kotlinx.android.synthetic.main.dialog_insulin.* -import kotlinx.android.synthetic.main.dialog_treatment.* -import kotlinx.android.synthetic.main.okcancel.* import java.text.DecimalFormat import java.util.* import javax.inject.Inject import kotlin.math.abs class TreatmentDialog : DialogFragmentWithDate() { + @Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var activePlugin: ActivePluginProvider @Inject lateinit var commandQueue: CommandQueueProvider @Inject lateinit var ctx: Context @Inject lateinit var config: Config + @Inject lateinit var uel: UserEntryLogger private val textWatcher: TextWatcher = object : TextWatcher { override fun afterTextChanged(s: Editable) {} @@ -54,49 +54,62 @@ class TreatmentDialog : DialogFragmentWithDate() { private fun validateInputs() { val maxCarbs = constraintChecker.getMaxCarbsAllowed().value().toDouble() val maxInsulin = constraintChecker.getMaxBolusAllowed().value() - if (SafeParse.stringToInt(overview_treatment_carbs.text) > maxCarbs) { - overview_treatment_carbs.value = 0.0 + if (SafeParse.stringToInt(binding.carbs.text) > maxCarbs) { + binding.carbs.value = 0.0 ToastUtils.showToastInUiThread(context, resourceHelper.gs(R.string.carbsconstraintapplied)) } - if (SafeParse.stringToDouble(overview_treatment_insulin.text) > maxInsulin) { - overview_treatment_insulin.value = 0.0 + if (SafeParse.stringToDouble(binding.insulin.text) > maxInsulin) { + binding.insulin.value = 0.0 ToastUtils.showToastInUiThread(context, resourceHelper.gs(R.string.bolusconstraintapplied)) } } + 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("overview_treatment_carbs", overview_treatment_carbs.value) - savedInstanceState.putDouble("overview_treatment_insulin", overview_treatment_insulin.value) + savedInstanceState.putDouble("carbs", binding.carbs.value) + savedInstanceState.putDouble("insulin", binding.insulin.value) } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { + savedInstanceState: Bundle?): View { onCreateViewGeneral() - return inflater.inflate(R.layout.dialog_treatment, container, false) + _binding = DialogTreatmentBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) if (config.NSCLIENT) { - overview_treatment_record_only.isChecked = true - overview_treatment_record_only.isEnabled = false + binding.recordOnly.isChecked = true + binding.recordOnly.isEnabled = false } val maxCarbs = constraintChecker.getMaxCarbsAllowed().value().toDouble() val maxInsulin = constraintChecker.getMaxBolusAllowed().value() val pumpDescription = activePlugin.activePump.pumpDescription - overview_treatment_carbs.setParams(savedInstanceState?.getDouble("overview_treatment_carbs") - ?: 0.0, 0.0, maxCarbs, 1.0, DecimalFormat("0"), false, ok, textWatcher) - overview_treatment_insulin.setParams(savedInstanceState?.getDouble("overview_treatment_insulin") - ?: 0.0, 0.0, maxInsulin, pumpDescription.bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, ok, textWatcher) + binding.carbs.setParams(savedInstanceState?.getDouble("carbs") + ?: 0.0, 0.0, maxCarbs, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher) + binding.insulin.setParams(savedInstanceState?.getDouble("insulin") + ?: 0.0, 0.0, maxInsulin, pumpDescription.bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, binding.okcancel.ok, textWatcher) + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null } override fun submit(): Boolean { + if (_binding == null) return false val pumpDescription = activePlugin.activePump.pumpDescription - val insulin = SafeParse.stringToDouble(overview_treatment_insulin?.text ?: return false) - val carbs = SafeParse.stringToInt(overview_treatment_carbs.text) - val recordOnlyChecked = overview_treatment_record_only.isChecked + val insulin = SafeParse.stringToDouble(binding.insulin.text ?: return false) + val carbs = SafeParse.stringToInt(binding.carbs.text) + val recordOnlyChecked = binding.recordOnly.isChecked val actions: LinkedList = LinkedList() val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(insulin)).value() val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value() @@ -115,8 +128,8 @@ class TreatmentDialog : DialogFragmentWithDate() { } if (insulinAfterConstraints > 0 || carbsAfterConstraints > 0) { activity?.let { activity -> - OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.overview_treatment_label), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), Runnable { - aapsLogger.debug("USER ENTRY: BOLUS insulin $insulin carbs: $carbs") + OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.overview_treatment_label), HtmlHelper.fromHtml(Joiner.on("
").join(actions)), { + uel.log("TREATMENT", d1 = insulin, i1 = carbs) val detailedBolusInfo = DetailedBolusInfo() if (insulinAfterConstraints == 0.0) detailedBolusInfo.eventType = CareportalEvent.CARBCORRECTION if (carbsAfterConstraints == 0) detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS @@ -128,12 +141,7 @@ class TreatmentDialog : DialogFragmentWithDate() { commandQueue.bolus(detailedBolusInfo, object : Callback() { override fun run() { if (!result.success) { - val i = Intent(ctx, ErrorHelperActivity::class.java) - i.putExtra("soundid", R.raw.boluserror) - i.putExtra("status", result.comment) - i.putExtra("title", resourceHelper.gs(R.string.treatmentdeliveryerror)) - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - ctx.startActivity(i) + ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror) } } }) diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt index 45eebec6f4..543e38ddcb 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt @@ -13,12 +13,13 @@ import android.widget.AdapterView.OnItemSelectedListener import android.widget.ArrayAdapter import android.widget.CompoundButton import androidx.fragment.app.FragmentManager +import dagger.android.HasAndroidInjector import dagger.android.support.DaggerDialogFragment import info.nightscout.androidaps.Constants import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.R import info.nightscout.androidaps.data.Profile -import info.nightscout.androidaps.db.BgReading +import info.nightscout.androidaps.databinding.DialogWizardBinding import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.interfaces.ProfileFunction @@ -35,11 +36,11 @@ import info.nightscout.androidaps.utils.SafeParse import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.extensions.toVisibility import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.sharedPreferences.SP +import info.nightscout.androidaps.utils.valueToUnits import info.nightscout.androidaps.utils.wizard.BolusWizard -import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable -import kotlinx.android.synthetic.main.dialog_wizard.* import java.text.DecimalFormat import java.util.* import javax.inject.Inject @@ -47,7 +48,9 @@ import kotlin.math.abs class WizardDialog : DaggerDialogFragment() { + @Inject lateinit var injector: HasAndroidInjector @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var mainApp: MainApp @Inject lateinit var sp: SP @@ -72,8 +75,23 @@ class WizardDialog : DaggerDialogFragment() { } } + private val timeTextWatcher = object : TextWatcher { + override fun afterTextChanged(s: Editable) {} + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { + calculateInsulin() + binding.alarm.isChecked = binding.carbTimeInput.value > 0 + } + } + private var disposable: CompositeDisposable = CompositeDisposable() + 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) @@ -81,49 +99,50 @@ class WizardDialog : DaggerDialogFragment() { override fun onSaveInstanceState(savedInstanceState: Bundle) { super.onSaveInstanceState(savedInstanceState) - savedInstanceState.putDouble("treatments_wizard_bg_input", treatments_wizard_bg_input.value) - savedInstanceState.putDouble("treatments_wizard_carbs_input", treatments_wizard_carbs_input.value) - savedInstanceState.putDouble("treatments_wizard_correction_input", treatments_wizard_correction_input.value) - savedInstanceState.putDouble("treatments_wizard_carb_time_input", treatments_wizard_carb_time_input.value) + savedInstanceState.putDouble("bg_input", binding.bgInput.value) + savedInstanceState.putDouble("carbs_input", binding.carbsInput.value) + savedInstanceState.putDouble("correction_input", binding.correctionInput.value) + savedInstanceState.putDouble("carb_time_input", binding.carbTimeInput.value) } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { + savedInstanceState: Bundle?): View { dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE) dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) isCancelable = true dialog?.setCanceledOnTouchOutside(false) - return inflater.inflate(R.layout.dialog_wizard, container, false) + _binding = DialogWizardBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { loadCheckedStates() processCobCheckBox() - treatments_wizard_sbcheckbox.visibility = sp.getBoolean(R.string.key_usesuperbolus, false).toVisibility() - treatments_wizard_notes_layout.visibility = sp.getBoolean(R.string.key_show_notes_entry_dialogs, false).toVisibility() + binding.sbcheckbox.visibility = sp.getBoolean(R.string.key_usesuperbolus, false).toVisibility() + binding.notesLayout.visibility = sp.getBoolean(R.string.key_show_notes_entry_dialogs, false).toVisibility() val maxCarbs = constraintChecker.getMaxCarbsAllowed().value() val maxCorrection = constraintChecker.getMaxBolusAllowed().value() if (profileFunction.getUnits() == Constants.MGDL) - treatments_wizard_bg_input.setParams(savedInstanceState?.getDouble("treatments_wizard_bg_input") - ?: 0.0, 0.0, 500.0, 1.0, DecimalFormat("0"), false, ok, textWatcher) + binding.bgInput.setParams(savedInstanceState?.getDouble("bg_input") + ?: 0.0, 0.0, 500.0, 1.0, DecimalFormat("0"), false, binding.ok, timeTextWatcher) else - treatments_wizard_bg_input.setParams(savedInstanceState?.getDouble("treatments_wizard_bg_input") - ?: 0.0, 0.0, 30.0, 0.1, DecimalFormat("0.0"), false, ok, textWatcher) - treatments_wizard_carbs_input.setParams(savedInstanceState?.getDouble("treatments_wizard_carbs_input") - ?: 0.0, 0.0, maxCarbs.toDouble(), 1.0, DecimalFormat("0"), false, ok, textWatcher) + binding.bgInput.setParams(savedInstanceState?.getDouble("bg_input") + ?: 0.0, 0.0, 30.0, 0.1, DecimalFormat("0.0"), false, binding.ok, textWatcher) + binding.carbsInput.setParams(savedInstanceState?.getDouble("carbs_input") + ?: 0.0, 0.0, maxCarbs.toDouble(), 1.0, DecimalFormat("0"), false, binding.ok, textWatcher) val bolusStep = activePlugin.activePump.pumpDescription.bolusStep - treatments_wizard_correction_input.setParams(savedInstanceState?.getDouble("treatments_wizard_correction_input") - ?: 0.0, -maxCorrection, maxCorrection, bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, ok, textWatcher) - treatments_wizard_carb_time_input.setParams(savedInstanceState?.getDouble("treatments_wizard_carb_time_input") - ?: 0.0, -60.0, 60.0, 5.0, DecimalFormat("0"), false, ok, textWatcher) + binding.correctionInput.setParams(savedInstanceState?.getDouble("correction_input") + ?: 0.0, -maxCorrection, maxCorrection, bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, binding.ok, textWatcher) + binding.carbTimeInput.setParams(savedInstanceState?.getDouble("carb_time_input") + ?: 0.0, -60.0, 60.0, 5.0, DecimalFormat("0"), false, binding.ok, timeTextWatcher) initDialog() - treatments_wizard_percent_used.text = resourceHelper.gs(R.string.format_percent, sp.getInt(R.string.key_boluswizard_percentage, 100)) + binding.percentUsed.text = resourceHelper.gs(R.string.format_percent, sp.getInt(R.string.key_boluswizard_percentage, 100)) // ok button - ok.setOnClickListener { + binding.ok.setOnClickListener { if (okClicked) { aapsLogger.debug(LTag.UI, "guarding: ok already clicked") } else { @@ -136,46 +155,46 @@ class WizardDialog : DaggerDialogFragment() { dismiss() } // cancel button - cancel.setOnClickListener { dismiss() } + binding.cancel.setOnClickListener { dismiss() } // checkboxes - treatments_wizard_bgcheckbox.setOnCheckedChangeListener(::onCheckedChanged) - treatments_wizard_ttcheckbox.setOnCheckedChangeListener(::onCheckedChanged) - treatments_wizard_cobcheckbox.setOnCheckedChangeListener(::onCheckedChanged) - treatments_wizard_basaliobcheckbox.setOnCheckedChangeListener(::onCheckedChanged) - treatments_wizard_bolusiobcheckbox.setOnCheckedChangeListener(::onCheckedChanged) - treatments_wizard_bgtrendcheckbox.setOnCheckedChangeListener(::onCheckedChanged) - treatments_wizard_sbcheckbox.setOnCheckedChangeListener(::onCheckedChanged) + binding.bgcheckbox.setOnCheckedChangeListener(::onCheckedChanged) + binding.ttcheckbox.setOnCheckedChangeListener(::onCheckedChanged) + binding.cobcheckbox.setOnCheckedChangeListener(::onCheckedChanged) + binding.basaliobcheckbox.setOnCheckedChangeListener(::onCheckedChanged) + binding.bolusiobcheckbox.setOnCheckedChangeListener(::onCheckedChanged) + binding.bgtrendcheckbox.setOnCheckedChangeListener(::onCheckedChanged) + binding.sbcheckbox.setOnCheckedChangeListener(::onCheckedChanged) val showCalc = sp.getBoolean(R.string.key_wizard_calculation_visible, false) - treatments_wizard_delimiter.visibility = showCalc.toVisibility() - treatments_wizard_resulttable.visibility = showCalc.toVisibility() - treatments_wizard_calculationcheckbox.isChecked = showCalc - treatments_wizard_calculationcheckbox.setOnCheckedChangeListener { _, isChecked -> + binding.delimiter.visibility = showCalc.toVisibility() + binding.resulttable.visibility = showCalc.toVisibility() + binding.calculationcheckbox.isChecked = showCalc + binding.calculationcheckbox.setOnCheckedChangeListener { _, isChecked -> run { sp.putBoolean(resourceHelper.gs(R.string.key_wizard_calculation_visible), isChecked) - treatments_wizard_delimiter.visibility = isChecked.toVisibility() - treatments_wizard_resulttable.visibility = isChecked.toVisibility() + binding.delimiter.visibility = isChecked.toVisibility() + binding.resulttable.visibility = isChecked.toVisibility() } } // profile spinner - treatments_wizard_profile.onItemSelectedListener = object : OnItemSelectedListener { + binding.profile.onItemSelectedListener = object : OnItemSelectedListener { override fun onNothingSelected(parent: AdapterView<*>?) { ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.noprofileselected)) - ok.visibility = View.GONE + binding.ok.visibility = View.GONE } override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { calculateInsulin() - ok.visibility = View.VISIBLE + binding.ok.visibility = View.VISIBLE } } // bus disposable.add(rxBus .toObservable(EventAutosensCalculationFinished::class.java) - .observeOn(AndroidSchedulers.mainThread()) + .observeOn(aapsSchedulers.main) .subscribe({ activity?.runOnUiThread { calculateInsulin() } - }, { fabricPrivacy.logException(it) }) + }, fabricPrivacy::logException) ) } @@ -183,38 +202,43 @@ class WizardDialog : DaggerDialogFragment() { override fun onDestroyView() { super.onDestroyView() disposable.clear() + _binding = null } private fun onCheckedChanged(buttonView: CompoundButton, @Suppress("UNUSED_PARAMETER") state: Boolean) { saveCheckedStates() - treatments_wizard_ttcheckbox.isEnabled = treatments_wizard_bgcheckbox.isChecked && treatmentsPlugin.tempTargetFromHistory != null - if (buttonView.id == treatments_wizard_cobcheckbox.id) + binding.ttcheckbox.isEnabled = binding.bgcheckbox.isChecked && treatmentsPlugin.tempTargetFromHistory != null + if (buttonView.id == binding.cobcheckbox.id) processCobCheckBox() calculateInsulin() } private fun processCobCheckBox() { - if (treatments_wizard_cobcheckbox.isChecked) { - treatments_wizard_bolusiobcheckbox.isEnabled = false - treatments_wizard_basaliobcheckbox.isEnabled = false - treatments_wizard_bolusiobcheckbox.isChecked = true - treatments_wizard_basaliobcheckbox.isChecked = true + if (binding.cobcheckbox.isChecked) { + binding.bolusiobcheckbox.isEnabled = false + binding.basaliobcheckbox.isEnabled = false + binding.bolusiobcheckbox.isChecked = true + binding.basaliobcheckbox.isChecked = true } else { - treatments_wizard_bolusiobcheckbox.isEnabled = true - treatments_wizard_basaliobcheckbox.isEnabled = true + binding.bolusiobcheckbox.isEnabled = true + binding.basaliobcheckbox.isEnabled = true } } private fun saveCheckedStates() { - sp.putBoolean(resourceHelper.gs(R.string.key_wizard_include_cob), treatments_wizard_cobcheckbox.isChecked) - sp.putBoolean(resourceHelper.gs(R.string.key_wizard_include_trend_bg), treatments_wizard_bgtrendcheckbox.isChecked) + sp.putBoolean(R.string.key_wizard_include_cob, binding.cobcheckbox.isChecked) + sp.putBoolean(R.string.key_wizard_include_trend_bg, binding.bgtrendcheckbox.isChecked) } private fun loadCheckedStates() { - treatments_wizard_bgtrendcheckbox.isChecked = sp.getBoolean(R.string.key_wizard_include_trend_bg, false) - treatments_wizard_cobcheckbox.isChecked = sp.getBoolean(R.string.key_wizard_include_cob, false) + binding.bgtrendcheckbox.isChecked = sp.getBoolean(R.string.key_wizard_include_trend_bg, false) + binding.cobcheckbox.isChecked = sp.getBoolean(R.string.key_wizard_include_cob, false) } + private fun valueToUnitsToString(value: Double, units: String): String = + if (units == Constants.MGDL) DecimalFormatter.to0Decimal(value) + else DecimalFormatter.to1Decimal(value * Constants.MGDL_TO_MMOLL) + private fun initDialog() { val profile = profileFunction.getProfile() val profileStore = activePlugin.activeProfileInterface.profile @@ -225,30 +249,29 @@ class WizardDialog : DaggerDialogFragment() { return } - val profileList: ArrayList - profileList = profileStore.getProfileList() + val profileList: ArrayList = profileStore.getProfileList() profileList.add(0, resourceHelper.gs(R.string.active)) context?.let { context -> val adapter = ArrayAdapter(context, R.layout.spinner_centered, profileList) - treatments_wizard_profile.adapter = adapter + binding.profile.adapter = adapter } ?: return val units = profileFunction.getUnits() - treatments_wizard_bgunits.text = units + binding.bgunits.text = units if (units == Constants.MGDL) - treatments_wizard_bg_input.setStep(1.0) + binding.bgInput.setStep(1.0) else - treatments_wizard_bg_input.setStep(0.1) + binding.bgInput.setStep(0.1) // Set BG if not old val lastBg = iobCobCalculatorPlugin.actualBg() if (lastBg != null) { - treatments_wizard_bg_input.value = lastBg.valueToUnits(units) + binding.bgInput.value = lastBg.valueToUnits(units) } else { - treatments_wizard_bg_input.value = 0.0 + binding.bgInput.value = 0.0 } - treatments_wizard_ttcheckbox.isEnabled = treatmentsPlugin.tempTargetFromHistory != null + binding.ttcheckbox.isEnabled = treatmentsPlugin.tempTargetFromHistory != null // IOB calculation treatmentsPlugin.updateTotalIOBTreatments() @@ -256,19 +279,19 @@ class WizardDialog : DaggerDialogFragment() { treatmentsPlugin.updateTotalIOBTempBasals() val basalIob = treatmentsPlugin.lastCalculationTempBasals.round() - treatments_wizard_bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, -bolusIob.iob) - treatments_wizard_basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, -basalIob.basaliob) + binding.bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, -bolusIob.iob) + binding.basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, -basalIob.basaliob) calculateInsulin() - treatments_wizard_percent_used.visibility = (sp.getInt(R.string.key_boluswizard_percentage, 100) != 100).toVisibility() + binding.percentUsed.visibility = (sp.getInt(R.string.key_boluswizard_percentage, 100) != 100).toVisibility() } private fun calculateInsulin() { val profileStore = activePlugin.activeProfileInterface.profile - if (treatments_wizard_profile?.selectedItem == null || profileStore == null) + if (binding.profile.selectedItem == null || profileStore == null) return // not initialized yet - var profileName = treatments_wizard_profile.selectedItem.toString() + var profileName = binding.profile.selectedItem.toString() val specificProfile: Profile? if (profileName == resourceHelper.gs(R.string.active)) { specificProfile = profileFunction.getProfile() @@ -279,82 +302,83 @@ class WizardDialog : DaggerDialogFragment() { if (specificProfile == null) return // Entered values - var bg = SafeParse.stringToDouble(treatments_wizard_bg_input.text) - val carbs = SafeParse.stringToInt(treatments_wizard_carbs_input.text) - val correction = SafeParse.stringToDouble(treatments_wizard_correction_input.text) + var bg = SafeParse.stringToDouble(binding.bgInput.text) + val carbs = SafeParse.stringToInt(binding.carbsInput.text) + val correction = SafeParse.stringToDouble(binding.correctionInput.text) val carbsAfterConstraint = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value() if (abs(carbs - carbsAfterConstraint) > 0.01) { - treatments_wizard_carbs_input.value = 0.0 + binding.carbsInput.value = 0.0 ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.carbsconstraintapplied)) return } - bg = if (treatments_wizard_bgcheckbox.isChecked) bg else 0.0 - val tempTarget = if (treatments_wizard_ttcheckbox.isChecked) treatmentsPlugin.tempTargetFromHistory else null + bg = if (binding.bgcheckbox.isChecked) bg else 0.0 + val tempTarget = if (binding.ttcheckbox.isChecked) treatmentsPlugin.tempTargetFromHistory else null // COB var cob = 0.0 - if (treatments_wizard_cobcheckbox.isChecked) { + if (binding.cobcheckbox.isChecked) { val cobInfo = iobCobCalculatorPlugin.getCobInfo(false, "Wizard COB") cobInfo.displayCob?.let { cob = it } } - val carbTime = SafeParse.stringToInt(treatments_wizard_carb_time_input.text) + val carbTime = SafeParse.stringToInt(binding.carbTimeInput.text) wizard = BolusWizard(mainApp).doCalc(specificProfile, profileName, tempTarget, carbsAfterConstraint, cob, bg, correction, sp.getInt(R.string.key_boluswizard_percentage, 100).toDouble(), - treatments_wizard_bgcheckbox.isChecked, - treatments_wizard_cobcheckbox.isChecked, - treatments_wizard_bolusiobcheckbox.isChecked, - treatments_wizard_basaliobcheckbox.isChecked, - treatments_wizard_sbcheckbox.isChecked, - treatments_wizard_ttcheckbox.isChecked, - treatments_wizard_bgtrendcheckbox.isChecked, - treatment_wizard_notes.text.toString(), carbTime) + binding.bgcheckbox.isChecked, + binding.cobcheckbox.isChecked, + binding.bolusiobcheckbox.isChecked, + binding.basaliobcheckbox.isChecked, + binding.sbcheckbox.isChecked, + binding.ttcheckbox.isChecked, + binding.bgtrendcheckbox.isChecked, + binding.alarm.isChecked, + binding.notes.text.toString(), carbTime) wizard?.let { wizard -> - treatments_wizard_bg.text = String.format(resourceHelper.gs(R.string.format_bg_isf), BgReading().value(Profile.toMgdl(bg, profileFunction.getUnits())).valueToUnitsToString(profileFunction.getUnits()), wizard.sens) - treatments_wizard_bginsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromBG) + binding.bg.text = String.format(resourceHelper.gs(R.string.format_bg_isf), valueToUnitsToString(Profile.toMgdl(bg, profileFunction.getUnits()), profileFunction.getUnits()), wizard.sens) + binding.bginsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromBG) - treatments_wizard_carbs.text = String.format(resourceHelper.gs(R.string.format_carbs_ic), carbs.toDouble(), wizard.ic) - treatments_wizard_carbsinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromCarbs) + binding.carbs.text = String.format(resourceHelper.gs(R.string.format_carbs_ic), carbs.toDouble(), wizard.ic) + binding.carbsinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromCarbs) - treatments_wizard_bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromBolusIOB) - treatments_wizard_basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromBasalsIOB) + binding.bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromBolusIOB) + binding.basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromBasalIOB) - treatments_wizard_correctioninsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromCorrection) + binding.correctioninsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromCorrection) // Superbolus - treatments_wizard_sb.text = if (treatments_wizard_sbcheckbox.isChecked) resourceHelper.gs(R.string.twohours) else "" - treatments_wizard_sbinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromSuperBolus) + binding.sb.text = if (binding.sbcheckbox.isChecked) resourceHelper.gs(R.string.twohours) else "" + binding.sbinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromSuperBolus) // Trend - if (treatments_wizard_bgtrendcheckbox.isChecked && wizard.glucoseStatus != null) { - treatments_wizard_bgtrend.text = ((if (wizard.trend > 0) "+" else "") + if (binding.bgtrendcheckbox.isChecked && wizard.glucoseStatus != null) { + binding.bgtrend.text = ((if (wizard.trend > 0) "+" else "") + Profile.toUnitsString(wizard.trend * 3, wizard.trend * 3 / Constants.MMOLL_TO_MGDL, profileFunction.getUnits()) + " " + profileFunction.getUnits()) } else { - treatments_wizard_bgtrend.text = "" + binding.bgtrend.text = "" } - treatments_wizard_bgtrendinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromTrend) + binding.bgtrendinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromTrend) // COB - if (treatments_wizard_cobcheckbox.isChecked) { - treatments_wizard_cob.text = String.format(resourceHelper.gs(R.string.format_cob_ic), cob, wizard.ic) - treatments_wizard_cobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromCOB) + if (binding.cobcheckbox.isChecked) { + binding.cob.text = String.format(resourceHelper.gs(R.string.format_cob_ic), cob, wizard.ic) + binding.cobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromCOB) } else { - treatments_wizard_cob.text = "" - treatments_wizard_cobinsulin.text = "" + binding.cob.text = "" + binding.cobinsulin.text = "" } if (wizard.calculatedTotalInsulin > 0.0 || carbsAfterConstraint > 0.0) { val insulinText = if (wizard.calculatedTotalInsulin > 0.0) resourceHelper.gs(R.string.formatinsulinunits, wizard.calculatedTotalInsulin) else "" val carbsText = if (carbsAfterConstraint > 0.0) resourceHelper.gs(R.string.format_carbs, carbsAfterConstraint) else "" - treatments_wizard_total.text = resourceHelper.gs(R.string.result_insulin_carbs, insulinText, carbsText) - ok.visibility = View.VISIBLE + binding.total.text = resourceHelper.gs(R.string.result_insulin_carbs, insulinText, carbsText) + binding.ok.visibility = View.VISIBLE } else { - treatments_wizard_total.text = resourceHelper.gs(R.string.missing_carbs, wizard.carbsEquivalent.toInt()) - ok.visibility = View.INVISIBLE + binding.total.text = resourceHelper.gs(R.string.missing_carbs, wizard.carbsEquivalent.toInt()) + binding.ok.visibility = View.INVISIBLE } } diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/WizardInfoDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/WizardInfoDialog.kt index 2bac82dbc6..aa76bde8c3 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/WizardInfoDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/WizardInfoDialog.kt @@ -9,15 +9,16 @@ import android.view.WindowManager import dagger.android.support.DaggerDialogFragment import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R +import info.nightscout.androidaps.databinding.DialogWizardinfoBinding import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.utils.DecimalFormatter import info.nightscout.androidaps.utils.JsonHelper import info.nightscout.androidaps.utils.resources.ResourceHelper -import kotlinx.android.synthetic.main.dialog_wizardinfo.* import org.json.JSONObject import javax.inject.Inject class WizardInfoDialog : DaggerDialogFragment() { + @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var profileFunction: ProfileFunction @@ -27,62 +28,74 @@ class WizardInfoDialog : DaggerDialogFragment() { this.json = json } + private var _binding: DialogWizardinfoBinding? = null + + // This property is only valid between onCreateView and + // onDestroyView. + private val binding get() = _binding!! + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { + savedInstanceState: Bundle?): View { dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE) dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) isCancelable = true dialog?.setCanceledOnTouchOutside(false) - return inflater.inflate(R.layout.dialog_wizardinfo, container, false) + _binding = DialogWizardinfoBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - close.setOnClickListener { dismiss() } + binding.close.setOnClickListener { dismiss() } val units = profileFunction.getUnits() val bgString = if (units == Constants.MGDL) DecimalFormatter.to0Decimal(JsonHelper.safeGetDouble(json, "bg")) else DecimalFormatter.to1Decimal(JsonHelper.safeGetDouble(json, "bg")) // BG - treatments_wizard_bg.text = resourceHelper.gs(R.string.format_bg_isf, bgString, JsonHelper.safeGetDouble(json, "isf")) - treatments_wizard_bginsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulinbg")) - treatments_wizard_bgcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "insulinbgused") - treatments_wizard_ttcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "ttused") + binding.bg.text = resourceHelper.gs(R.string.format_bg_isf, bgString, JsonHelper.safeGetDouble(json, "isf")) + binding.bginsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulinbg")) + binding.bgcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "insulinbgused") + binding.ttcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "ttused") // Trend - treatments_wizard_bgtrend.text = JsonHelper.safeGetString(json, "trend") - treatments_wizard_bgtrendinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulintrend")) - treatments_wizard_bgtrendcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "trendused") + binding.bgtrend.text = JsonHelper.safeGetString(json, "trend") + binding.bgtrendinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulintrend")) + binding.bgtrendcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "trendused") // COB - treatments_wizard_cob.text = resourceHelper.gs(R.string.format_cob_ic, JsonHelper.safeGetDouble(json, "cob"), JsonHelper.safeGetDouble(json, "ic")) - treatments_wizard_cobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulincob")) - treatments_wizard_cobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "cobused") + binding.cob.text = resourceHelper.gs(R.string.format_cob_ic, JsonHelper.safeGetDouble(json, "cob"), JsonHelper.safeGetDouble(json, "ic")) + binding.cobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulincob")) + binding.cobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "cobused") // Bolus IOB - treatments_wizard_bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "bolusiob")) - treatments_wizard_bolusiobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "bolusiobused") + binding.bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "bolusiob")) + binding.bolusiobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "bolusiobused") // Basal IOB - treatments_wizard_basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "basaliob")) - treatments_wizard_basaliobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "basaliobused") + binding.basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "basaliob")) + binding.basaliobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "basaliobused") // Superbolus - treatments_wizard_sbinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulinsuperbolus")) - treatments_wizard_sbcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "superbolusused") + binding.sbinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulinsuperbolus")) + binding.sbcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "superbolusused") // Carbs - treatments_wizard_carbs.text = resourceHelper.gs(R.string.format_carbs_ic, JsonHelper.safeGetDouble(json, "carbs"), JsonHelper.safeGetDouble(json, "ic")) - treatments_wizard_carbsinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulincarbs")) + binding.carbs.text = resourceHelper.gs(R.string.format_carbs_ic, JsonHelper.safeGetDouble(json, "carbs"), JsonHelper.safeGetDouble(json, "ic")) + binding.carbsinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulincarbs")) // Correction - treatments_wizard_correctioninsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "othercorrection")) + binding.correctioninsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "othercorrection")) // Profile - treatments_wizard_profile.text = JsonHelper.safeGetString(json, "profile") + binding.profile.text = JsonHelper.safeGetString(json, "profile") // Notes - treatments_wizard_notes.text = JsonHelper.safeGetString(json, "notes") + binding.notes.text = JsonHelper.safeGetString(json, "notes") // Percentage - treatments_wizard_percent_used.text = DecimalFormatter.to0Decimal(JsonHelper.safeGetDouble(json, "percentageCorrection", 100.0)) + "%" + binding.percentUsed.text = resourceHelper.gs(R.string.format_percent, (JsonHelper.safeGetInt(json, "percentageCorrection", 100))) // Total - treatments_wizard_totalinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulin")) + binding.totalinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulin")) } override fun onStart() { super.onStart() dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } } diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventNewBG.kt b/app/src/main/java/info/nightscout/androidaps/events/EventNewBG.kt index 08c05407c9..4f05f83018 100644 --- a/app/src/main/java/info/nightscout/androidaps/events/EventNewBG.kt +++ b/app/src/main/java/info/nightscout/androidaps/events/EventNewBG.kt @@ -1,5 +1,5 @@ package info.nightscout.androidaps.events -import info.nightscout.androidaps.db.BgReading +import info.nightscout.androidaps.database.entities.GlucoseValue -class EventNewBG(val bgReading: BgReading?) : EventLoop() \ No newline at end of file +class EventNewBG(val glucoseValue: GlucoseValue?) : EventLoop() \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/historyBrowser/HistoryBrowseActivity.kt b/app/src/main/java/info/nightscout/androidaps/historyBrowser/HistoryBrowseActivity.kt index e72154ab1c..ca4ecbc5d1 100644 --- a/app/src/main/java/info/nightscout/androidaps/historyBrowser/HistoryBrowseActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/historyBrowser/HistoryBrowseActivity.kt @@ -13,6 +13,7 @@ import com.jjoe64.graphview.GraphView import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.NoSplashAppCompatActivity +import info.nightscout.androidaps.databinding.ActivityHistorybrowseBinding import info.nightscout.androidaps.events.EventCustomCalculationFinished import info.nightscout.androidaps.events.EventRefreshOverview import info.nightscout.androidaps.interfaces.ActivePluginProvider @@ -31,12 +32,9 @@ import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.extensions.toVisibility -import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.sharedPreferences.SP -import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable -import io.reactivex.schedulers.Schedulers -import kotlinx.android.synthetic.main.activity_historybrowse.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -44,11 +42,12 @@ import java.util.* import javax.inject.Inject class HistoryBrowseActivity : NoSplashAppCompatActivity() { + @Inject lateinit var injector: HasAndroidInjector @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var sp: SP - @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var defaultValueHelper: DefaultValueHelper @Inject lateinit var iobCobCalculatorPluginHistory: IobCobCalculatorPluginHistory @@ -68,21 +67,27 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() { private var rangeToDisplay = 24 // for graph private var start: Long = 0 + private val graphLock = Object() + private var eventCustomCalculationFinished = EventCustomCalculationFinished() + private lateinit var binding: ActivityHistorybrowseBinding + private var destroyed = false + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.activity_historybrowse) + binding = ActivityHistorybrowseBinding.inflate(layoutInflater) + setContentView(binding.root) - historybrowse_left.setOnClickListener { + binding.left.setOnClickListener { start -= T.hours(rangeToDisplay.toLong()).msecs() runCalculation("onClickLeft") } - historybrowse_right.setOnClickListener { + binding.right.setOnClickListener { start += T.hours(rangeToDisplay.toLong()).msecs() runCalculation("onClickRight") } - historybrowse_end.setOnClickListener { + binding.end.setOnClickListener { val calendar = Calendar.getInstance() calendar.timeInMillis = System.currentTimeMillis() calendar[Calendar.MILLISECOND] = 0 @@ -92,12 +97,12 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() { start = calendar.timeInMillis runCalculation("onClickEnd") } - historybrowse_zoom.setOnClickListener { + binding.zoom.setOnClickListener { rangeToDisplay += 6 rangeToDisplay = if (rangeToDisplay > 24) 6 else rangeToDisplay updateGUI("rangeChange", false) } - historybrowse_zoom.setOnLongClickListener { + binding.zoom.setOnLongClickListener { val calendar = Calendar.getInstance() calendar.timeInMillis = start calendar[Calendar.MILLISECOND] = 0 @@ -121,11 +126,11 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() { cal[Calendar.MINUTE] = 0 cal[Calendar.HOUR_OF_DAY] = 0 start = cal.timeInMillis - historybrowse_date?.text = dateUtil.dateAndTimeString(start) + binding.date.text = dateUtil.dateAndTimeString(start) runCalculation("onClickDate") } - historybrowse_date.setOnClickListener { + binding.date.setOnClickListener { val cal = Calendar.getInstance() cal.timeInMillis = start DatePickerDialog(this, dateSetListener, @@ -139,12 +144,12 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() { 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 - historybrowse_bggraph?.gridLabelRenderer?.gridColor = resourceHelper.gc(R.color.graphgrid) - historybrowse_bggraph?.gridLabelRenderer?.reloadStyles() - historybrowse_bggraph?.gridLabelRenderer?.labelVerticalWidth = axisWidth + binding.bggraph.gridLabelRenderer?.gridColor = resourceHelper.gc(R.color.graphgrid) + binding.bggraph.gridLabelRenderer?.reloadStyles() + binding.bggraph.gridLabelRenderer?.labelVerticalWidth = axisWidth - overviewMenus.setupChartMenu(overview_chartMenuButton) - prepareGraphs() + overviewMenus.setupChartMenu(binding.chartMenuButton) + prepareGraphsIfNeeded(overviewMenus.setting.size) savedInstanceState?.let { bundle -> rangeToDisplay = bundle.getInt("rangeToDisplay", 0) start = bundle.getLong("start", 0) @@ -158,42 +163,47 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() { iobCobCalculatorPluginHistory.stopCalculation("onPause") } + @Synchronized + override fun onDestroy() { + destroyed = true + super.onDestroy() + } + public override fun onResume() { super.onResume() disposable.add(rxBus .toObservable(EventAutosensCalculationFinished::class.java) - .observeOn(Schedulers.io()) + .observeOn(aapsSchedulers.io) .subscribe({ // catch only events from iobCobCalculatorPluginHistory if (it.cause is EventCustomCalculationFinished) { updateGUI("EventAutosensCalculationFinished", bgOnly = false) } - }, fabricPrivacy::logException ) + }, fabricPrivacy::logException) ) disposable.add(rxBus .toObservable(EventAutosensBgLoaded::class.java) - .observeOn(Schedulers.io()) + .observeOn(aapsSchedulers.io) .subscribe({ // catch only events from iobCobCalculatorPluginHistory if (it.cause is EventCustomCalculationFinished) { updateGUI("EventAutosensCalculationFinished", bgOnly = true) } - }, fabricPrivacy::logException ) + }, fabricPrivacy::logException) ) disposable.add(rxBus .toObservable(EventIobCalculationProgress::class.java) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ overview_iobcalculationprogess?.text = it.progress }, fabricPrivacy::logException ) + .observeOn(aapsSchedulers.main) + .subscribe({ binding.overviewIobcalculationprogess.text = it.progress }, fabricPrivacy::logException) ) disposable.add(rxBus .toObservable(EventRefreshOverview::class.java) - .observeOn(AndroidSchedulers.mainThread()) + .observeOn(aapsSchedulers.main) .subscribe({ if (it.now) { - prepareGraphs() updateGUI("EventRefreshOverview", bgOnly = false) } - }, fabricPrivacy::logException ) + }, fabricPrivacy::logException) ) if (start == 0L) { // set start of current day @@ -217,42 +227,41 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() { } - private fun prepareGraphs() { - val numOfGraphs = overviewMenus.setting.size + private fun prepareGraphsIfNeeded(numOfGraphs: Int) { + synchronized(graphLock) { + if (numOfGraphs != secondaryGraphs.size - 1) { + //aapsLogger.debug("New secondary graph count ${numOfGraphs-1}") + // rebuild needed + secondaryGraphs.clear() + secondaryGraphsLabel.clear() + binding.iobGraph.removeAllViews() + for (i in 1 until numOfGraphs) { + val relativeLayout = RelativeLayout(this) + relativeLayout.layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) - if (numOfGraphs != secondaryGraphs.size - 1) { - //aapsLogger.debug("New secondary graph count ${numOfGraphs-1}") - // rebuild needed - secondaryGraphs.clear() - secondaryGraphsLabel.clear() - history_iobgraph.removeAllViews() - for (i in 1 until numOfGraphs) { - val relativeLayout = RelativeLayout(this) - relativeLayout.layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) + val graph = GraphView(this) + graph.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, resourceHelper.dpToPx(100)).also { it.setMargins(0, resourceHelper.dpToPx(15), 0, resourceHelper.dpToPx(10)) } + graph.gridLabelRenderer?.gridColor = resourceHelper.gc(R.color.graphgrid) + graph.gridLabelRenderer?.reloadStyles() + graph.gridLabelRenderer?.isHorizontalLabelsVisible = false + graph.gridLabelRenderer?.labelVerticalWidth = axisWidth + graph.gridLabelRenderer?.numVerticalLabels = 3 + graph.viewport.backgroundColor = Color.argb(20, 255, 255, 255) // 8% of gray + relativeLayout.addView(graph) - val graph = GraphView(this) - graph.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, resourceHelper.dpToPx(100)).also { it.setMargins(0, resourceHelper.dpToPx(15), 0, resourceHelper.dpToPx(10)) } - graph.gridLabelRenderer?.gridColor = resourceHelper.gc(R.color.graphgrid) - graph.gridLabelRenderer?.reloadStyles() - graph.gridLabelRenderer?.isHorizontalLabelsVisible = false - graph.gridLabelRenderer?.labelVerticalWidth = axisWidth - graph.gridLabelRenderer?.numVerticalLabels = 3 - graph.viewport.backgroundColor = Color.argb(20, 255, 255, 255) // 8% of gray - relativeLayout.addView(graph) + val label = TextView(this) + val layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).also { it.setMargins(resourceHelper.dpToPx(30), resourceHelper.dpToPx(25), 0, 0) } + layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP) + layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT) + label.layoutParams = layoutParams + relativeLayout.addView(label) + secondaryGraphsLabel.add(label) - val label = TextView(this) - val layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).also { it.setMargins(resourceHelper.dpToPx(30), resourceHelper.dpToPx(25), 0, 0) } - layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP) - layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT) - label.layoutParams = layoutParams - relativeLayout.addView(label) - secondaryGraphsLabel.add(label) - - history_iobgraph.addView(relativeLayout) - secondaryGraphs.add(graph) + binding.iobGraph.addView(relativeLayout) + secondaryGraphs.add(graph) + } } } - } private fun runCalculation(from: String) { @@ -265,7 +274,10 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() { } } + @Synchronized fun updateGUI(from: String, bgOnly: Boolean) { + val menuChartSettings = overviewMenus.setting + prepareGraphsIfNeeded(menuChartSettings.size) aapsLogger.debug(LTag.UI, "updateGUI from: $from") val pump = activePlugin.activePump val profile = profileFunction.getProfile() @@ -274,13 +286,13 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() { val highLine = defaultValueHelper.determineHighLine() lifecycleScope.launch(Dispatchers.Main) { - historybrowse_noprofile?.visibility = (profile == null).toVisibility() + binding.noprofile.visibility = (profile == null).toVisibility() profile ?: return@launch - historybrowse_bggraph ?: return@launch - historybrowse_date?.text = dateUtil.dateAndTimeString(start) - historybrowse_zoom?.text = rangeToDisplay.toString() - val graphData = GraphData(injector, historybrowse_bggraph, iobCobCalculatorPluginHistory, treatmentsPluginHistory) + if (destroyed) return@launch + binding.date.text = dateUtil.dateAndTimeString(start) + binding.zoom.text = rangeToDisplay.toString() + val graphData = GraphData(injector, binding.bggraph, iobCobCalculatorPluginHistory, treatmentsPluginHistory) val secondaryGraphsData: ArrayList = ArrayList() // do preparation in different thread @@ -296,9 +308,6 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() { // **** BG **** graphData.addBgReadings(fromTime, toTime, lowLine, highLine, null) - // set manual x bounds to have nice steps - graphData.formatAxis(fromTime, toTime) - // add target line graphData.addTargetLine(fromTime, toTime, profile, null) @@ -308,63 +317,74 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() { if (!bgOnly) { // Treatments graphData.addTreatments(fromTime, toTime) - if (overviewMenus.setting[0][OverviewMenus.CharType.ACT.ordinal]) + if (menuChartSettings[0][OverviewMenus.CharType.ACT.ordinal]) graphData.addActivity(fromTime, toTime, false, 0.8) // add basal data - if (pump.pumpDescription.isTempBasalCapable && overviewMenus.setting[0][OverviewMenus.CharType.BAS.ordinal]) { + if (pump.pumpDescription.isTempBasalCapable && menuChartSettings[0][OverviewMenus.CharType.BAS.ordinal]) { graphData.addBasals(fromTime, toTime, lowLine / graphData.maxY / 1.2) } // ------------------ 2nd graph - for (g in 0 until secondaryGraphs.size) { - val secondGraphData = GraphData(injector, secondaryGraphs[g], iobCobCalculatorPluginHistory, treatmentsPluginHistory) - var useIobForScale = false - var useCobForScale = false - var useDevForScale = false - var useRatioForScale = false - var useDSForScale = false - var useIAForScale = false - var useABSForScale = false - when { - overviewMenus.setting[g + 1][OverviewMenus.CharType.IOB.ordinal] -> useIobForScale = true - overviewMenus.setting[g + 1][OverviewMenus.CharType.COB.ordinal] -> useCobForScale = true - overviewMenus.setting[g + 1][OverviewMenus.CharType.DEV.ordinal] -> useDevForScale = true - overviewMenus.setting[g + 1][OverviewMenus.CharType.SEN.ordinal] -> useRatioForScale = true - overviewMenus.setting[g + 1][OverviewMenus.CharType.ACT.ordinal] -> useIAForScale = true - overviewMenus.setting[g + 1][OverviewMenus.CharType.ABS.ordinal] -> useABSForScale = true - overviewMenus.setting[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] -> useDSForScale = true + synchronized(graphLock) { + for (g in 0 until secondaryGraphs.size) { + val secondGraphData = GraphData(injector, secondaryGraphs[g], iobCobCalculatorPluginHistory, treatmentsPluginHistory) + var useIobForScale = false + var useCobForScale = false + var useDevForScale = false + var useRatioForScale = false + var useDSForScale = false + var useBGIForScale = false + var useABSForScale = false + when { + 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.SEN.ordinal] -> useRatioForScale = true + menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal] -> useBGIForScale = true + menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] -> useABSForScale = true + menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] -> useDSForScale = true + } + + var alignIobScale = menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] && menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal] + var alignDevBgiScale = menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] && menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal] + + if (menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal]) secondGraphData.addAbsIob(fromTime, toTime, useABSForScale, 1.0) + if (menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal]) secondGraphData.addIob(fromTime, toTime, useIobForScale, 1.0, menuChartSettings[g + 1][OverviewMenus.CharType.PRE.ordinal], alignIobScale) + if (menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal]) secondGraphData.addCob(fromTime, toTime, useCobForScale, if (useCobForScale) 1.0 else 0.5) + if (menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal]) secondGraphData.addDeviations(fromTime, toTime, useDevForScale, 1.0, alignDevBgiScale) + if (menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal]) secondGraphData.addRatio(fromTime, toTime, useRatioForScale, 1.0) + if (menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal]) secondGraphData.addMinusBGI(fromTime, toTime, useBGIForScale, if (alignDevBgiScale) 1.0 else 0.8, alignDevBgiScale) + if (menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] && buildHelper.isDev()) secondGraphData.addDeviationSlope(fromTime, toTime, useDSForScale, 1.0) + + // set manual x bounds to have nice steps + secondGraphData.formatAxis(fromTime, toTime) + secondGraphData.addNowLine(pointer) + secondaryGraphsData.add(secondGraphData) } - - if (overviewMenus.setting[g + 1][OverviewMenus.CharType.IOB.ordinal]) secondGraphData.addIob(fromTime, toTime, useIobForScale, 1.0, overviewMenus.setting[g + 1][OverviewMenus.CharType.PRE.ordinal]) - if (overviewMenus.setting[g + 1][OverviewMenus.CharType.COB.ordinal]) secondGraphData.addCob(fromTime, toTime, useCobForScale, if (useCobForScale) 1.0 else 0.5) - if (overviewMenus.setting[g + 1][OverviewMenus.CharType.DEV.ordinal]) secondGraphData.addDeviations(fromTime, toTime, useDevForScale, 1.0) - if (overviewMenus.setting[g + 1][OverviewMenus.CharType.SEN.ordinal]) secondGraphData.addRatio(fromTime, toTime, useRatioForScale, 1.0) - if (overviewMenus.setting[g + 1][OverviewMenus.CharType.ACT.ordinal]) secondGraphData.addActivity(fromTime, toTime, useIAForScale, 0.8) - if (overviewMenus.setting[g + 1][OverviewMenus.CharType.ABS.ordinal]) secondGraphData.addAbsIob(fromTime, toTime, useABSForScale, 1.0) - if (overviewMenus.setting[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] && buildHelper.isDev()) secondGraphData.addDeviationSlope(fromTime, toTime, useDSForScale, 1.0) - - // set manual x bounds to have nice steps - secondGraphData.formatAxis(fromTime, toTime) - secondGraphData.addNowLine(pointer) - secondaryGraphsData.add(secondGraphData) } } + + // set manual x bounds to have nice steps + graphData.setNumVerticalLables() + graphData.formatAxis(fromTime, toTime) } // finally enforce drawing of graphs in UI thread graphData.performUpdate() if (!bgOnly) - for (g in 0 until secondaryGraphs.size) { - secondaryGraphsLabel[g].text = overviewMenus.enabledTypes(g + 1) - secondaryGraphs[g].visibility = (!bgOnly && ( - overviewMenus.setting[g + 1][OverviewMenus.CharType.IOB.ordinal] || - overviewMenus.setting[g + 1][OverviewMenus.CharType.COB.ordinal] || - overviewMenus.setting[g + 1][OverviewMenus.CharType.DEV.ordinal] || - overviewMenus.setting[g + 1][OverviewMenus.CharType.SEN.ordinal] || - overviewMenus.setting[g + 1][OverviewMenus.CharType.ACT.ordinal] || - overviewMenus.setting[g + 1][OverviewMenus.CharType.ABS.ordinal] || - overviewMenus.setting[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] - )).toVisibility() - secondaryGraphsData[g].performUpdate() + synchronized(graphLock) { + for (g in 0 until secondaryGraphs.size) { + secondaryGraphsLabel[g].text = overviewMenus.enabledTypes(g + 1) + secondaryGraphs[g].visibility = (!bgOnly && ( + menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal] || + menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal] || + menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] || + menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal] || + menuChartSettings[g + 1][OverviewMenus.CharType.ACT.ordinal] || + menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] || + menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] + )).toVisibility() + secondaryGraphsData[g].performUpdate() + } } } } diff --git a/app/src/main/java/info/nightscout/androidaps/historyBrowser/IobCobCalculatorPluginHistory.kt b/app/src/main/java/info/nightscout/androidaps/historyBrowser/IobCobCalculatorPluginHistory.kt index bf7401036a..8dc1ebfb32 100644 --- a/app/src/main/java/info/nightscout/androidaps/historyBrowser/IobCobCalculatorPluginHistory.kt +++ b/app/src/main/java/info/nightscout/androidaps/historyBrowser/IobCobCalculatorPluginHistory.kt @@ -1,6 +1,7 @@ package info.nightscout.androidaps.historyBrowser import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.AAPSLogger @@ -12,6 +13,7 @@ import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAverage 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.androidaps.utils.sharedPreferences.SP import javax.inject.Inject import javax.inject.Singleton @@ -20,6 +22,7 @@ import javax.inject.Singleton class IobCobCalculatorPluginHistory @Inject constructor( injector: HasAndroidInjector, aapsLogger: AAPSLogger, + aapsSchedulers: AapsSchedulers, rxBus: RxBusWrapper, sp: SP, resourceHelper: ResourceHelper, @@ -30,9 +33,10 @@ class IobCobCalculatorPluginHistory @Inject constructor( sensitivityAAPSPlugin: SensitivityAAPSPlugin, sensitivityWeightedAveragePlugin: SensitivityWeightedAveragePlugin, fabricPrivacy: FabricPrivacy, - dateUtil: DateUtil -) : IobCobCalculatorPlugin(injector, aapsLogger, rxBus, sp, resourceHelper, profileFunction, - activePlugin, treatmentsPluginHistory, sensitivityOref1Plugin, sensitivityAAPSPlugin, sensitivityWeightedAveragePlugin, fabricPrivacy, dateUtil) { + dateUtil: DateUtil, + repository: AppRepository +) : IobCobCalculatorPlugin(injector, aapsLogger, aapsSchedulers, rxBus, sp, resourceHelper, profileFunction, + activePlugin, treatmentsPluginHistory, sensitivityOref1Plugin, sensitivityAAPSPlugin, sensitivityWeightedAveragePlugin, fabricPrivacy, dateUtil, repository) { override fun onStart() { // do not attach to rxbus } diff --git a/app/src/main/java/info/nightscout/androidaps/historyBrowser/TreatmentsPluginHistory.kt b/app/src/main/java/info/nightscout/androidaps/historyBrowser/TreatmentsPluginHistory.kt index f9d0a4019b..c38c5845bf 100644 --- a/app/src/main/java/info/nightscout/androidaps/historyBrowser/TreatmentsPluginHistory.kt +++ b/app/src/main/java/info/nightscout/androidaps/historyBrowser/TreatmentsPluginHistory.kt @@ -13,6 +13,7 @@ import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin 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.androidaps.utils.sharedPreferences.SP import javax.inject.Inject import javax.inject.Singleton @@ -21,6 +22,7 @@ import javax.inject.Singleton class TreatmentsPluginHistory @Inject constructor( injector: HasAndroidInjector, aapsLogger: AAPSLogger, + aapsSchedulers: AapsSchedulers, rxBus: RxBusWrapper, resourceHelper: ResourceHelper, context: Context, @@ -31,7 +33,7 @@ class TreatmentsPluginHistory @Inject constructor( fabricPrivacy: FabricPrivacy, dateUtil: DateUtil, uploadQueue: UploadQueue -) : TreatmentsPlugin(injector, aapsLogger, rxBus, resourceHelper, context, sp, profileFunction, activePlugin, nsUpload, fabricPrivacy, dateUtil, uploadQueue) { +) : TreatmentsPlugin(injector, aapsLogger, rxBus, aapsSchedulers, resourceHelper, context, sp, profileFunction, activePlugin, nsUpload, fabricPrivacy, dateUtil, uploadQueue) { init { onStart() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/logger/LoggerCallback.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/logger/LoggerCallback.java deleted file mode 100644 index 2fa1816ad5..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/logger/LoggerCallback.java +++ /dev/null @@ -1,63 +0,0 @@ -package info.nightscout.androidaps.plugins.aps.logger; - -import org.mozilla.javascript.ScriptableObject; - -import javax.inject.Inject; - -import info.nightscout.androidaps.db.StaticInjector; -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; - -/** - * Created by adrian on 15/10/17. - */ - - -public class LoggerCallback extends ScriptableObject { - - @Inject - AAPSLogger aapsLogger; - - private static StringBuffer errorBuffer = new StringBuffer(); - private static StringBuffer logBuffer = new StringBuffer(); - - - public LoggerCallback() { - //empty constructor needed for Rhino - errorBuffer = new StringBuffer(); - logBuffer = new StringBuffer(); - StaticInjector.Companion.getInstance().androidInjector().inject(this); - } - - @Override - public String getClassName() { - return "LoggerCallback"; - } - - public void jsConstructor() { - //empty constructor on JS site; could work as setter - } - - public void jsFunction_log(Object obj1) { - aapsLogger.debug(LTag.APS, obj1.toString().trim()); - logBuffer.append(obj1.toString()); - } - - public void jsFunction_error(Object obj1) { - aapsLogger.error(LTag.APS, obj1.toString().trim()); - errorBuffer.append(obj1.toString()); - } - - - public static String getScriptDebug() { - String ret = ""; - if (errorBuffer.length() > 0) { - ret += "e:\n" + errorBuffer.toString(); - } - if (ret.length() > 0 && logBuffer.length() > 0) ret += '\n'; - if (logBuffer.length() > 0) { - ret += "d:\n" + logBuffer.toString(); - } - return ret; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/logger/LoggerCallback.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/logger/LoggerCallback.kt new file mode 100644 index 0000000000..c1b4f076d4 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/logger/LoggerCallback.kt @@ -0,0 +1,61 @@ +package info.nightscout.androidaps.plugins.aps.logger + +import info.nightscout.androidaps.db.StaticInjector +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import org.mozilla.javascript.ScriptableObject +import javax.inject.Inject + +@Suppress("unused", "FunctionName") +class LoggerCallback : ScriptableObject() { + + @Inject lateinit var aapsLogger: AAPSLogger + + override fun getClassName(): String = "LoggerCallback" + + fun jsConstructor() { + //empty constructor on JS site; could work as setter + } + + fun jsFunction_log(obj1: Any) { + aapsLogger.debug(LTag.APS, obj1.toString().trim { it <= ' ' }) + logBuffer.append(obj1.toString()) + } + + fun jsFunction_error(obj1: Any) { + aapsLogger.error(LTag.APS, obj1.toString().trim { it <= ' ' }) + errorBuffer.append(obj1.toString()) + } + + companion object { + + private var errorBuffer = StringBuffer() + private var logBuffer = StringBuffer() + val scriptDebug: String + get() { + var ret = "" + if (errorBuffer.isNotEmpty()) { + ret += """ + e: + $errorBuffer + """.trimIndent() + } + if (ret.isNotEmpty() && logBuffer.isNotEmpty()) ret += '\n' + if (logBuffer.isNotEmpty()) { + ret += """ + d: + $logBuffer + """.trimIndent() + } + return ret + } + } + + init { + //empty constructor needed for Rhino + errorBuffer = StringBuffer() + logBuffer = StringBuffer() + @Suppress("DEPRECATION") + StaticInjector.Companion.getInstance().androidInjector().inject(this) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopFragment.kt index 4092f1d602..74f581b298 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopFragment.kt @@ -6,6 +6,7 @@ import android.view.View import android.view.ViewGroup import dagger.android.support.DaggerFragment import info.nightscout.androidaps.R +import info.nightscout.androidaps.databinding.LoopFragmentBinding import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopSetLastRunGui @@ -14,16 +15,17 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.HtmlHelper -import info.nightscout.androidaps.utils.extensions.plusAssign import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.sharedPreferences.SP -import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable -import kotlinx.android.synthetic.main.loop_fragment.* +import io.reactivex.rxkotlin.plusAssign import javax.inject.Inject class LoopFragment : DaggerFragment() { + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var sp: SP @Inject lateinit var resourceHelper: ResourceHelper @@ -33,16 +35,23 @@ class LoopFragment : DaggerFragment() { private var disposable: CompositeDisposable = CompositeDisposable() + private var _binding: LoopFragmentBinding? = null + + // This property is only valid between onCreateView and + // onDestroyView. + private val binding get() = _binding!! + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.loop_fragment, container, false) + savedInstanceState: Bundle?): View { + _binding = LoopFragmentBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - loop_run.setOnClickListener { - loop_lastrun.text = resourceHelper.gs(R.string.executing) + binding.run.setOnClickListener { + binding.lastrun.text = resourceHelper.gs(R.string.executing) Thread { loopPlugin.invoke("Loop button", true) }.start() } } @@ -52,18 +61,18 @@ class LoopFragment : DaggerFragment() { super.onResume() disposable += rxBus .toObservable(EventLoopUpdateGui::class.java) - .observeOn(AndroidSchedulers.mainThread()) + .observeOn(aapsSchedulers.main) .subscribe({ updateGUI() - }, { fabricPrivacy.logException(it) }) + }, fabricPrivacy::logException) disposable += rxBus .toObservable(EventLoopSetLastRunGui::class.java) - .observeOn(AndroidSchedulers.mainThread()) + .observeOn(aapsSchedulers.main) .subscribe({ clearGUI() - loop_lastrun?.text = it.text - }, { fabricPrivacy.logException(it) }) + binding.lastrun.text = it.text + }, fabricPrivacy::logException) updateGUI() sp.putBoolean(R.string.key_objectiveuseloop, true) @@ -76,22 +85,28 @@ class LoopFragment : DaggerFragment() { } @Synchronized - fun updateGUI() { - if (loop_request == null) return - loopPlugin.lastRun?.let { - loop_request?.text = it.request?.toSpanned() ?: "" - loop_constraintsprocessed?.text = it.constraintsProcessed?.toSpanned() ?: "" - loop_source?.text = it.source ?: "" - loop_lastrun?.text = dateUtil.dateAndTimeString(it.lastAPSRun) - ?: "" - loop_smbrequest_time?.text = dateUtil.dateAndTimeAndSecondsString(it.lastSMBRequest) - loop_smbexecution_time?.text = dateUtil.dateAndTimeAndSecondsString(it.lastSMBEnact) - loop_tbrrequest_time?.text = dateUtil.dateAndTimeAndSecondsString(it.lastTBRRequest) - loop_tbrexecution_time?.text = dateUtil.dateAndTimeAndSecondsString(it.lastTBREnact) + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } - loop_tbrsetbypump?.text = it.tbrSetByPump?.let { tbrSetByPump -> HtmlHelper.fromHtml(tbrSetByPump.toHtml()) } + @Synchronized + fun updateGUI() { + if (_binding == null) return + loopPlugin.lastRun?.let { + binding.request.text = it.request?.toSpanned() ?: "" + binding.constraintsprocessed.text = it.constraintsProcessed?.toSpanned() ?: "" + binding.source.text = it.source ?: "" + binding.lastrun.text = dateUtil.dateAndTimeString(it.lastAPSRun) ?: "" - loop_smbsetbypump?.text = it.smbSetByPump?.let { smbSetByPump -> HtmlHelper.fromHtml(smbSetByPump.toHtml()) } + binding.smbrequestTime.text = dateUtil.dateAndTimeAndSecondsString(it.lastSMBRequest) + binding.smbexecutionTime.text = dateUtil.dateAndTimeAndSecondsString(it.lastSMBEnact) + binding.tbrrequestTime.text = dateUtil.dateAndTimeAndSecondsString(it.lastTBRRequest) + binding.tbrexecutionTime.text = dateUtil.dateAndTimeAndSecondsString(it.lastTBREnact) + + binding.tbrsetbypump.text = it.tbrSetByPump?.let { tbrSetByPump -> HtmlHelper.fromHtml(tbrSetByPump.toHtml()) } + ?: "" + binding.smbsetbypump.text = it.smbSetByPump?.let { smbSetByPump -> HtmlHelper.fromHtml(smbSetByPump.toHtml()) } ?: "" val constraints = @@ -101,22 +116,22 @@ class LoopFragment : DaggerFragment() { constraintsProcessed.smbConstraint?.let { smbConstraint -> allConstraints.copyReasons(smbConstraint) } allConstraints.getMostLimitedReasons(aapsLogger) } ?: "" - loop_constraints?.text = constraints + binding.constraints.text = constraints } } @Synchronized private fun clearGUI() { - loop_request?.text = "" - loop_constraints?.text = "" - loop_constraintsprocessed?.text = "" - loop_source?.text = "" - loop_lastrun?.text = "" - loop_smbrequest_time?.text = "" - loop_smbexecution_time?.text = "" - loop_tbrrequest_time?.text = "" - loop_tbrexecution_time?.text = "" - loop_tbrsetbypump?.text = "" - loop_smbsetbypump?.text = "" + binding.request.text = "" + binding.constraints.text = "" + binding.constraintsprocessed.text = "" + binding.source.text = "" + binding.lastrun.text = "" + binding.smbrequestTime.text = "" + binding.smbexecutionTime.text = "" + binding.tbrrequestTime.text = "" + binding.tbrexecutionTime.text = "" + binding.tbrsetbypump.text = "" + binding.smbsetbypump.text = "" } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java deleted file mode 100644 index 0c27aeac44..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.java +++ /dev/null @@ -1,884 +0,0 @@ -package info.nightscout.androidaps.plugins.aps.loop; - -import android.annotation.SuppressLint; -import android.app.NotificationChannel; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.app.TaskStackBuilder; -import android.content.Context; -import android.content.Intent; -import android.os.Build; -import android.os.SystemClock; - -import androidx.core.app.NotificationCompat; - -import org.jetbrains.annotations.Nullable; -import org.json.JSONException; -import org.json.JSONObject; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import dagger.Lazy; -import dagger.android.HasAndroidInjector; -import info.nightscout.androidaps.BuildConfig; -import info.nightscout.androidaps.Config; -import info.nightscout.androidaps.Constants; -import info.nightscout.androidaps.MainActivity; -import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.activities.ErrorHelperActivity; -import info.nightscout.androidaps.data.DetailedBolusInfo; -import info.nightscout.androidaps.data.Profile; -import info.nightscout.androidaps.data.PumpEnactResult; -import info.nightscout.androidaps.db.BgReading; -import info.nightscout.androidaps.db.CareportalEvent; -import info.nightscout.androidaps.db.Source; -import info.nightscout.androidaps.db.TemporaryBasal; -import info.nightscout.androidaps.events.EventAcceptOpenLoopChange; -import info.nightscout.androidaps.events.EventNewBG; -import info.nightscout.androidaps.events.EventTempTargetChange; -import info.nightscout.androidaps.interfaces.APSInterface; -import info.nightscout.androidaps.interfaces.ActivePluginProvider; -import info.nightscout.androidaps.interfaces.CommandQueueProvider; -import info.nightscout.androidaps.interfaces.Constraint; -import info.nightscout.androidaps.interfaces.LoopInterface; -import info.nightscout.androidaps.interfaces.PluginBase; -import info.nightscout.androidaps.interfaces.PluginDescription; -import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.interfaces.ProfileFunction; -import info.nightscout.androidaps.interfaces.PumpDescription; -import info.nightscout.androidaps.interfaces.PumpInterface; -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopSetLastRunGui; -import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopUpdateGui; -import info.nightscout.androidaps.plugins.aps.loop.events.EventNewOpenLoopNotification; -import info.nightscout.androidaps.plugins.bus.RxBusWrapper; -import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker; -import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; -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.notifications.Notification; -import info.nightscout.androidaps.plugins.general.wear.ActionStringHandler; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished; -import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin; -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; -import info.nightscout.androidaps.queue.Callback; -import info.nightscout.androidaps.queue.commands.Command; -import info.nightscout.androidaps.receivers.ReceiverStatusStore; -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.utils.resources.ResourceHelper; -import info.nightscout.androidaps.utils.sharedPreferences.SP; -import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.schedulers.Schedulers; - -@Singleton -public class LoopPlugin extends PluginBase implements LoopInterface { - private final HasAndroidInjector injector; - private final SP sp; - private final RxBusWrapper rxBus; - private final ConstraintChecker constraintChecker; - private final ResourceHelper resourceHelper; - private final ProfileFunction profileFunction; - private final Context context; - private final CommandQueueProvider commandQueue; - private final ActivePluginProvider activePlugin; - private final TreatmentsPlugin treatmentsPlugin; - private final VirtualPumpPlugin virtualPumpPlugin; - private final Lazy actionStringHandler; - private final IobCobCalculatorPlugin iobCobCalculatorPlugin; - private final ReceiverStatusStore receiverStatusStore; - private final FabricPrivacy fabricPrivacy; - private final NSUpload nsUpload; - private final HardLimits hardLimits; - - private final CompositeDisposable disposable = new CompositeDisposable(); - - private static final String CHANNEL_ID = "AndroidAPS-Openloop"; - - private long lastBgTriggeredRun = 0; - - private long loopSuspendedTill; // end of manual loop suspend - private boolean isSuperBolus; - private boolean isDisconnected; - - private long carbsSuggestionsSuspendedUntil = 0; - private int prevCarbsreq = 0; - - @Nullable private LastRun lastRun = null; - - @Nullable @Override public LastRun getLastRun() { - return lastRun; - } - - @Override public void setLastRun(@Nullable LastRun lastRun) { - this.lastRun = lastRun; - } - - @Inject - public LoopPlugin( - HasAndroidInjector injector, - AAPSLogger aapsLogger, - RxBusWrapper rxBus, - SP sp, - Config config, - ConstraintChecker constraintChecker, - ResourceHelper resourceHelper, - ProfileFunction profileFunction, - Context context, - CommandQueueProvider commandQueue, - ActivePluginProvider activePlugin, - TreatmentsPlugin treatmentsPlugin, - VirtualPumpPlugin virtualPumpPlugin, - Lazy actionStringHandler, // TODO Adrian use RxBus instead of Lazy - IobCobCalculatorPlugin iobCobCalculatorPlugin, - ReceiverStatusStore receiverStatusStore, - FabricPrivacy fabricPrivacy, - NSUpload nsUpload, - HardLimits hardLimits - ) { - super(new PluginDescription() - .mainType(PluginType.LOOP) - .fragmentClass(LoopFragment.class.getName()) - .pluginIcon(R.drawable.ic_loop_closed_white) - .pluginName(R.string.loop) - .shortName(R.string.loop_shortname) - .preferencesId(R.xml.pref_loop) - .enableByDefault(config.getAPS()) - .description(R.string.description_loop), - aapsLogger, resourceHelper, injector - ); - this.injector = injector; - this.sp = sp; - this.rxBus = rxBus; - this.constraintChecker = constraintChecker; - this.resourceHelper = resourceHelper; - this.profileFunction = profileFunction; - this.context = context; - this.activePlugin = activePlugin; - this.commandQueue = commandQueue; - this.treatmentsPlugin = treatmentsPlugin; - this.virtualPumpPlugin = virtualPumpPlugin; - this.actionStringHandler = actionStringHandler; - this.iobCobCalculatorPlugin = iobCobCalculatorPlugin; - this.receiverStatusStore = receiverStatusStore; - this.fabricPrivacy = fabricPrivacy; - this.nsUpload = nsUpload; - this.hardLimits = hardLimits; - - loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L); - isSuperBolus = sp.getBoolean("isSuperBolus", false); - isDisconnected = sp.getBoolean("isDisconnected", false); - } - - @Override - protected void onStart() { - createNotificationChannel(); - super.onStart(); - disposable.add(rxBus - .toObservable(EventTempTargetChange.class) - .observeOn(Schedulers.io()) - .subscribe(event -> 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. - *

- */ - disposable.add(rxBus - .toObservable(EventAutosensCalculationFinished.class) - .observeOn(Schedulers.io()) - .subscribe(event -> { - // Autosens calculation not triggered by a new BG - if (!(event.getCause() instanceof EventNewBG)) return; - - BgReading bgReading = iobCobCalculatorPlugin.actualBg(); - // BG outdated - if (bgReading == null) return; - // already looped with that value - if (bgReading.date <= lastBgTriggeredRun) return; - - lastBgTriggeredRun = bgReading.date; - invoke("AutosenseCalculation for " + bgReading, true); - }, fabricPrivacy::logException) - ); - } - - private void createNotificationChannel() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - - NotificationManager mNotificationManager = - (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - @SuppressLint("WrongConstant") NotificationChannel channel = new NotificationChannel(CHANNEL_ID, - CHANNEL_ID, - NotificationManager.IMPORTANCE_HIGH); - mNotificationManager.createNotificationChannel(channel); - } - } - - @Override - protected void onStop() { - disposable.clear(); - super.onStop(); - } - - @Override - public boolean specialEnableCondition() { - try { - PumpInterface pump = activePlugin.getActivePump(); - return pump.getPumpDescription().isTempBasalCapable; - } catch (Exception ignored) { - // may fail during initialization - return true; - } - } - - public long suspendedTo() { - return loopSuspendedTill; - } - - public void suspendTo(long endTime) { - loopSuspendedTill = endTime; - isSuperBolus = false; - isDisconnected = false; - sp.putLong("loopSuspendedTill", loopSuspendedTill); - sp.putBoolean("isSuperBolus", isSuperBolus); - sp.putBoolean("isDisconnected", isDisconnected); - } - - public void superBolusTo(long endTime) { - loopSuspendedTill = endTime; - isSuperBolus = true; - isDisconnected = false; - sp.putLong("loopSuspendedTill", loopSuspendedTill); - sp.putBoolean("isSuperBolus", isSuperBolus); - sp.putBoolean("isDisconnected", isDisconnected); - } - - private void disconnectTo(long endTime) { - loopSuspendedTill = endTime; - isSuperBolus = false; - isDisconnected = true; - sp.putLong("loopSuspendedTill", loopSuspendedTill); - sp.putBoolean("isSuperBolus", isSuperBolus); - sp.putBoolean("isDisconnected", isDisconnected); - } - - public int minutesToEndOfSuspend() { - if (loopSuspendedTill == 0) - return 0; - - long now = System.currentTimeMillis(); - long msecDiff = loopSuspendedTill - now; - - if (loopSuspendedTill <= now) { // time exceeded - suspendTo(0L); - return 0; - } - - return (int) (msecDiff / 60d / 1000d); - } - - public boolean isSuspended() { - if (loopSuspendedTill == 0) - return false; - - long now = System.currentTimeMillis(); - - if (loopSuspendedTill <= now) { // time exceeded - suspendTo(0L); - return false; - } - - return true; - } - - public boolean isLGS() { - Constraint closedLoopEnabled = constraintChecker.isClosedLoopAllowed(); - Double MaxIOBallowed = constraintChecker.getMaxIOBAllowed().value(); - String APSmode = sp.getString(R.string.key_aps_mode, "open"); - PumpInterface pump = activePlugin.getActivePump(); - boolean isLGS = false; - - if (!isSuspended() && !pump.isSuspended()) - if (closedLoopEnabled.value()) - if ((MaxIOBallowed.equals(hardLimits.getMAXIOB_LGS())) || (APSmode.equals("lgs"))) - isLGS = true; - - return isLGS; - } - - public boolean isSuperBolus() { - if (loopSuspendedTill == 0) - return false; - - long now = System.currentTimeMillis(); - - if (loopSuspendedTill <= now) { // time exceeded - suspendTo(0L); - return false; - } - - return isSuperBolus; - } - - public boolean isDisconnected() { - if (loopSuspendedTill == 0) - return false; - - long now = System.currentTimeMillis(); - - if (loopSuspendedTill <= now) { // time exceeded - suspendTo(0L); - return false; - } - return isDisconnected; - } - public boolean treatmentTimethreshold(int duartionMinutes) { - long threshold = System.currentTimeMillis() + (duartionMinutes*60*1000); - boolean bool = false; - if (treatmentsPlugin.getLastBolusTime() > threshold || treatmentsPlugin.getLastCarbTime() > threshold) - bool = true; - - return bool; - } - - public synchronized void invoke(String initiator, boolean allowNotification) { - invoke(initiator, allowNotification, false); - } - - public synchronized void invoke(String initiator, boolean allowNotification, boolean tempBasalFallback) { - try { - getAapsLogger().debug(LTag.APS, "invoke from " + initiator); - Constraint loopEnabled = constraintChecker.isLoopInvocationAllowed(); - - if (!loopEnabled.value()) { - String message = resourceHelper.gs(R.string.loopdisabled) + "\n" + loopEnabled.getReasons(getAapsLogger()); - getAapsLogger().debug(LTag.APS, message); - rxBus.send(new EventLoopSetLastRunGui(message)); - return; - } - final PumpInterface pump = activePlugin.getActivePump(); - APSResult result = null; - - if (!isEnabled(PluginType.LOOP)) - return; - - Profile profile = profileFunction.getProfile(); - - if (profile == null || !profileFunction.isProfileValid("Loop")) { - getAapsLogger().debug(LTag.APS, resourceHelper.gs(R.string.noprofileselected)); - rxBus.send(new EventLoopSetLastRunGui(resourceHelper.gs(R.string.noprofileselected))); - return; - } - - // Check if pump info is loaded - if (pump.getBaseBasalRate() < 0.01d) return; - - APSInterface usedAPS = activePlugin.getActiveAPS(); - if (((PluginBase) usedAPS).isEnabled(PluginType.APS)) { - usedAPS.invoke(initiator, tempBasalFallback); - result = usedAPS.getLastAPSResult(); - } - - // Check if we have any result - if (result == null) { - rxBus.send(new EventLoopSetLastRunGui(resourceHelper.gs(R.string.noapsselected))); - return; - } - - // Prepare for pumps using % basals - if (pump.getPumpDescription().tempBasalStyle == PumpDescription.PERCENT && allowPercentage()) { - result.usePercent = true; - } - result.percent = (int) (result.rate / profile.getBasal() * 100); - - // check rate for constraints - final APSResult resultAfterConstraints = result.newAndClone(injector); - resultAfterConstraints.rateConstraint = new Constraint<>(resultAfterConstraints.rate); - resultAfterConstraints.rate = constraintChecker.applyBasalConstraints(resultAfterConstraints.rateConstraint, profile).value(); - - resultAfterConstraints.percentConstraint = new Constraint<>(resultAfterConstraints.percent); - resultAfterConstraints.percent = constraintChecker.applyBasalPercentConstraints(resultAfterConstraints.percentConstraint, profile).value(); - - resultAfterConstraints.smbConstraint = new Constraint<>(resultAfterConstraints.smb); - resultAfterConstraints.smb = constraintChecker.applyBolusConstraints(resultAfterConstraints.smbConstraint).value(); - - // safety check for multiple SMBs - long lastBolusTime = treatmentsPlugin.getLastBolusTime(); - if (lastBolusTime != 0 && lastBolusTime + T.mins(3).msecs() > System.currentTimeMillis()) { - getAapsLogger().debug(LTag.APS, "SMB requsted but still in 3 min interval"); - resultAfterConstraints.smb = 0; - } - - if (lastRun != null && lastRun.getConstraintsProcessed() != null) { - prevCarbsreq = lastRun.getConstraintsProcessed().carbsReq; - } - - if (lastRun == null) lastRun = new LastRun(); - lastRun.setRequest(result); - lastRun.setConstraintsProcessed(resultAfterConstraints); - lastRun.setLastAPSRun(DateUtil.now()); - lastRun.setSource(((PluginBase) usedAPS).getName()); - lastRun.setTbrSetByPump(null); - lastRun.setSmbSetByPump(null); - lastRun.setLastTBREnact(0); - lastRun.setLastTBRRequest(0); - lastRun.setLastSMBEnact(0); - lastRun.setLastSMBRequest(0); - - nsUpload.uploadDeviceStatus(this, iobCobCalculatorPlugin, profileFunction, activePlugin.getActivePump(), receiverStatusStore, BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION); - - if (isSuspended()) { - getAapsLogger().debug(LTag.APS, resourceHelper.gs(R.string.loopsuspended)); - rxBus.send(new EventLoopSetLastRunGui(resourceHelper.gs(R.string.loopsuspended))); - return; - } - - if (pump.isSuspended()) { - getAapsLogger().debug(LTag.APS, resourceHelper.gs(R.string.pumpsuspended)); - rxBus.send(new EventLoopSetLastRunGui(resourceHelper.gs(R.string.pumpsuspended))); - return; - } - - Constraint closedLoopEnabled = constraintChecker.isClosedLoopAllowed(); - - 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)) { - - if (sp.getBoolean(R.string.key_enable_carbs_required_alert_local,true) && !sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, false)) { - Notification carbreqlocal = new Notification(Notification.CARBS_REQUIRED, resultAfterConstraints.getCarbsRequiredText(), Notification.NORMAL); - rxBus.send(new EventNewNotification(carbreqlocal)); - } - if (sp.getBoolean(R.string.key_ns_create_announcements_from_carbs_req, false)) { - nsUpload.uploadError(resultAfterConstraints.getCarbsRequiredText()); - } - if (sp.getBoolean(R.string.key_enable_carbs_required_alert_local,true) && sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, false)){ - Intent intentAction5m = new Intent(context, CarbSuggestionReceiver.class); - intentAction5m.putExtra("ignoreDuration", 5); - PendingIntent pendingIntent5m = PendingIntent.getBroadcast(context, 1, intentAction5m, PendingIntent.FLAG_UPDATE_CURRENT); - NotificationCompat.Action actionIgnore5m = new - NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore5m,"Ignore 5m"), pendingIntent5m); - - Intent intentAction15m = new Intent(context, CarbSuggestionReceiver.class); - intentAction15m.putExtra("ignoreDuration", 15); - PendingIntent pendingIntent15m = PendingIntent.getBroadcast(context, 1, intentAction15m, PendingIntent.FLAG_UPDATE_CURRENT); - NotificationCompat.Action actionIgnore15m = new - NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore15m,"Ignore 15m"), pendingIntent15m); - - Intent intentAction30m = new Intent(context, CarbSuggestionReceiver.class); - intentAction30m.putExtra("ignoreDuration", 30); - PendingIntent pendingIntent30m = PendingIntent.getBroadcast(context, 1, intentAction30m, PendingIntent.FLAG_UPDATE_CURRENT); - NotificationCompat.Action actionIgnore30m = new - NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore30m,"Ignore 30m"), pendingIntent30m); - - NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID); - builder.setSmallIcon(R.drawable.notif_icon) - .setContentTitle(resourceHelper.gs(R.string.carbssuggestion)) - .setContentText(resultAfterConstraints.getCarbsRequiredText()) - .setAutoCancel(true) - .setPriority(Notification.IMPORTANCE_HIGH) - .setCategory(Notification.CATEGORY_ALARM) - .addAction(actionIgnore5m) - .addAction(actionIgnore15m) - .addAction(actionIgnore30m) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setVibrate(new long[]{1000, 1000, 1000, 1000, 1000}); - - NotificationManager mNotificationManager = - (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - - // mId allows you to update the notification later on. - mNotificationManager.notify(Constants.notificationID, builder.build()); - rxBus.send(new EventNewOpenLoopNotification()); - - //only send to wear if Native notifications are turned off - if (!sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, false)) { - // Send to Wear - actionStringHandler.get().handleInitiate("changeRequest"); - } - } - - } else { - //If carbs were required previously, but are no longer needed, dismiss notifications - if ( prevCarbsreq > 0 ) { - dismissSuggestion(); - rxBus.send(new EventDismissNotification(Notification.CARBS_REQUIRED)); - } - } - } - - if (resultAfterConstraints.isChangeRequested() - && !commandQueue.bolusInQueue() - && !commandQueue.isRunning(Command.CommandType.BOLUS)) { - final PumpEnactResult waiting = new PumpEnactResult(getInjector()); - waiting.queued = true; - if (resultAfterConstraints.tempBasalRequested) - lastRun.setTbrSetByPump(waiting); - if (resultAfterConstraints.bolusRequested) - lastRun.setSmbSetByPump(waiting); - rxBus.send(new EventLoopUpdateGui()); - fabricPrivacy.logCustom("APSRequest"); - applyTBRRequest(resultAfterConstraints, profile, new Callback() { - @Override - public void run() { - if (result.enacted || result.success) { - lastRun.setTbrSetByPump(result); - lastRun.setLastTBRRequest(lastRun.getLastAPSRun()); - lastRun.setLastTBREnact(DateUtil.now()); - rxBus.send(new EventLoopUpdateGui()); - applySMBRequest(resultAfterConstraints, new Callback() { - @Override - public void run() { - // Callback is only called if a bolus was actually requested - if (result.enacted || result.success) { - lastRun.setSmbSetByPump(result); - lastRun.setLastSMBRequest(lastRun.getLastAPSRun()); - lastRun.setLastSMBEnact(DateUtil.now()); - } else { - new Thread(() -> { - SystemClock.sleep(1000); - invoke("tempBasalFallback", allowNotification, true); - }).start(); - } - rxBus.send(new EventLoopUpdateGui()); - } - }); - } else { - lastRun.setTbrSetByPump(result); - lastRun.setLastTBRRequest(lastRun.getLastAPSRun()); - } - rxBus.send(new EventLoopUpdateGui()); - } - }); - } else { - lastRun.setTbrSetByPump(null); - lastRun.setSmbSetByPump(null); - } - } else { - if (resultAfterConstraints.isChangeRequested() && allowNotification) { - NotificationCompat.Builder builder = - new NotificationCompat.Builder(context, CHANNEL_ID); - builder.setSmallIcon(R.drawable.notif_icon) - .setContentTitle(resourceHelper.gs(R.string.openloop_newsuggestion)) - .setContentText(resultAfterConstraints.toString()) - .setAutoCancel(true) - .setPriority(Notification.IMPORTANCE_HIGH) - .setCategory(Notification.CATEGORY_ALARM) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC); - if (sp.getBoolean("wearcontrol", false)) { - builder.setLocalOnly(true); - } - presentSuggestion(builder); - } else if (allowNotification) { - dismissSuggestion(); - } - } - - rxBus.send(new EventLoopUpdateGui()); - } finally { - getAapsLogger().debug(LTag.APS, "invoke end"); - } - } - - public void disableCarbSuggestions(int duartionMinutes) { - carbsSuggestionsSuspendedUntil = System.currentTimeMillis() + (duartionMinutes*60*1000); - dismissSuggestion(); - } - - private void presentSuggestion(NotificationCompat.Builder builder) { - // Creates an explicit intent for an Activity in your app - Intent resultIntent = new Intent(context, MainActivity.class); - - // The stack builder object will contain an artificial back stack for the - // started Activity. - // This ensures that navigating backward from the Activity leads out of - // your application to the Home screen. - TaskStackBuilder stackBuilder = TaskStackBuilder.create(context); - stackBuilder.addParentStack(MainActivity.class); - // Adds the Intent that starts the Activity to the top of the stack - stackBuilder.addNextIntent(resultIntent); - PendingIntent resultPendingIntent = - stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); - builder.setContentIntent(resultPendingIntent); - builder.setVibrate(new long[]{1000, 1000, 1000, 1000, 1000}); - NotificationManager mNotificationManager = - (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - // mId allows you to update the notification later on. - mNotificationManager.notify(Constants.notificationID, builder.build()); - rxBus.send(new EventNewOpenLoopNotification()); - - // Send to Wear - actionStringHandler.get().handleInitiate("changeRequest"); - } - - private void dismissSuggestion() { - // dismiss notifications - NotificationManager notificationManager = - (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - notificationManager.cancel(Constants.notificationID); - actionStringHandler.get().handleInitiate("cancelChangeRequest"); - } - - public void acceptChangeRequest() { - Profile profile = profileFunction.getProfile(); - final LoopPlugin lp = this; - applyTBRRequest(lastRun.getConstraintsProcessed(), profile, new Callback() { - @Override - public void run() { - if (result.enacted) { - lastRun.setTbrSetByPump(result); - lastRun.setLastTBRRequest(lastRun.getLastAPSRun()); - lastRun.setLastTBREnact(DateUtil.now()); - lastRun.setLastOpenModeAccept(DateUtil.now()); - nsUpload.uploadDeviceStatus(lp, iobCobCalculatorPlugin, profileFunction, activePlugin.getActivePump(), receiverStatusStore, BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION); - sp.incInt(R.string.key_ObjectivesmanualEnacts); - } - rxBus.send(new EventAcceptOpenLoopChange()); - } - }); - fabricPrivacy.logCustom("AcceptTemp"); - } - - /** - * expect absolute request and allow both absolute and percent response based on pump capabilities - * TODO: update pump drivers to support APS request in % - */ - - private void applyTBRRequest(APSResult request, Profile profile, Callback callback) { - - if (!request.tempBasalRequested) { - if (callback != null) { - callback.result(new PumpEnactResult(getInjector()).enacted(false).success(true).comment(resourceHelper.gs(R.string.nochangerequested))).run(); - } - return; - } - - PumpInterface pump = activePlugin.getActivePump(); - - if (!pump.isInitialized()) { - getAapsLogger().debug(LTag.APS, "applyAPSRequest: " + resourceHelper.gs(R.string.pumpNotInitialized)); - if (callback != null) { - callback.result(new PumpEnactResult(getInjector()).comment(resourceHelper.gs(R.string.pumpNotInitialized)).enacted(false).success(false)).run(); - } - return; - } - - if (pump.isSuspended()) { - getAapsLogger().debug(LTag.APS, "applyAPSRequest: " + resourceHelper.gs(R.string.pumpsuspended)); - if (callback != null) { - callback.result(new PumpEnactResult(getInjector()).comment(resourceHelper.gs(R.string.pumpsuspended)).enacted(false).success(false)).run(); - } - return; - } - - getAapsLogger().debug(LTag.APS, "applyAPSRequest: " + request.toString()); - - long now = System.currentTimeMillis(); - TemporaryBasal activeTemp = treatmentsPlugin.getTempBasalFromHistory(now); - if (request.usePercent && allowPercentage()) { - if (request.percent == 100 && request.duration == 0) { - if (activeTemp != null) { - getAapsLogger().debug(LTag.APS, "applyAPSRequest: cancelTempBasal()"); - commandQueue.cancelTempBasal(false, callback); - } else { - getAapsLogger().debug(LTag.APS, "applyAPSRequest: Basal set correctly"); - if (callback != null) { - callback.result(new PumpEnactResult(getInjector()).percent(request.percent).duration(0) - .enacted(false).success(true).comment(resourceHelper.gs(R.string.basal_set_correctly))).run(); - } - } - } else if (activeTemp != null - && activeTemp.getPlannedRemainingMinutes() > 5 - && request.duration - activeTemp.getPlannedRemainingMinutes() < 30 - && request.percent == activeTemp.percentRate) { - getAapsLogger().debug(LTag.APS, "applyAPSRequest: Temp basal set correctly"); - if (callback != null) { - callback.result(new PumpEnactResult(getInjector()).percent(request.percent) - .enacted(false).success(true).duration(activeTemp.getPlannedRemainingMinutes()) - .comment(resourceHelper.gs(R.string.let_temp_basal_run))).run(); - } - } else { - getAapsLogger().debug(LTag.APS, "applyAPSRequest: tempBasalPercent()"); - commandQueue.tempBasalPercent(request.percent, request.duration, false, profile, callback); - } - } else { - if ((request.rate == 0 && request.duration == 0) || Math.abs(request.rate - pump.getBaseBasalRate()) < pump.getPumpDescription().basalStep) { - if (activeTemp != null) { - getAapsLogger().debug(LTag.APS, "applyAPSRequest: cancelTempBasal()"); - commandQueue.cancelTempBasal(false, callback); - } else { - getAapsLogger().debug(LTag.APS, "applyAPSRequest: Basal set correctly"); - if (callback != null) { - callback.result(new PumpEnactResult(getInjector()).absolute(request.rate).duration(0) - .enacted(false).success(true).comment(resourceHelper.gs(R.string.basal_set_correctly))).run(); - } - } - } else if (activeTemp != null - && activeTemp.getPlannedRemainingMinutes() > 5 - && request.duration - activeTemp.getPlannedRemainingMinutes() < 30 - && Math.abs(request.rate - activeTemp.tempBasalConvertedToAbsolute(now, profile)) < pump.getPumpDescription().basalStep) { - getAapsLogger().debug(LTag.APS, "applyAPSRequest: Temp basal set correctly"); - if (callback != null) { - callback.result(new PumpEnactResult(getInjector()).absolute(activeTemp.tempBasalConvertedToAbsolute(now, profile)) - .enacted(false).success(true).duration(activeTemp.getPlannedRemainingMinutes()) - .comment(resourceHelper.gs(R.string.let_temp_basal_run))).run(); - } - } else { - getAapsLogger().debug(LTag.APS, "applyAPSRequest: setTempBasalAbsolute()"); - commandQueue.tempBasalAbsolute(request.rate, request.duration, false, profile, callback); - } - } - } - - private void applySMBRequest(APSResult request, Callback callback) { - if (!request.bolusRequested) { - return; - } - - PumpInterface pump = activePlugin.getActivePump(); - - long lastBolusTime = treatmentsPlugin.getLastBolusTime(); - if (lastBolusTime != 0 && lastBolusTime + 3 * 60 * 1000 > System.currentTimeMillis()) { - getAapsLogger().debug(LTag.APS, "SMB requested but still in 3 min interval"); - if (callback != null) { - callback.result(new PumpEnactResult(getInjector()) - .comment(resourceHelper.gs(R.string.smb_frequency_exceeded)) - .enacted(false).success(false)).run(); - } - return; - } - - if (!pump.isInitialized()) { - getAapsLogger().debug(LTag.APS, "applySMBRequest: " + resourceHelper.gs(R.string.pumpNotInitialized)); - if (callback != null) { - callback.result(new PumpEnactResult(getInjector()).comment(resourceHelper.gs(R.string.pumpNotInitialized)).enacted(false).success(false)).run(); - } - return; - } - - if (pump.isSuspended()) { - getAapsLogger().debug(LTag.APS, "applySMBRequest: " + resourceHelper.gs(R.string.pumpsuspended)); - if (callback != null) { - callback.result(new PumpEnactResult(getInjector()).comment(resourceHelper.gs(R.string.pumpsuspended)).enacted(false).success(false)).run(); - } - return; - } - - getAapsLogger().debug(LTag.APS, "applySMBRequest: " + request.toString()); - - // deliver SMB - DetailedBolusInfo detailedBolusInfo = new DetailedBolusInfo(); - detailedBolusInfo.lastKnownBolusTime = treatmentsPlugin.getLastBolusTime(); - detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS; - detailedBolusInfo.insulin = request.smb; - detailedBolusInfo.isSMB = true; - detailedBolusInfo.source = Source.USER; - detailedBolusInfo.deliverAt = request.deliverAt; - getAapsLogger().debug(LTag.APS, "applyAPSRequest: bolus()"); - commandQueue.bolus(detailedBolusInfo, callback); - } - - private boolean allowPercentage() { - return virtualPumpPlugin.isEnabled(PluginType.PUMP); - } - - public void disconnectPump(int durationInMinutes, Profile profile) { - PumpInterface pump = activePlugin.getActivePump(); - - disconnectTo(System.currentTimeMillis() + durationInMinutes * 60 * 1000L); - - if (pump.getPumpDescription().tempBasalStyle == PumpDescription.ABSOLUTE) { - commandQueue.tempBasalAbsolute(0, durationInMinutes, true, profile, new Callback() { - @Override - public void run() { - if (!result.success) { - Intent i = new Intent(context, ErrorHelperActivity.class); - i.putExtra("soundid", R.raw.boluserror); - i.putExtra("status", result.comment); - i.putExtra("title", resourceHelper.gs(R.string.tempbasaldeliveryerror)); - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(i); - } - } - }); - } else { - commandQueue.tempBasalPercent(0, durationInMinutes, true, profile, new Callback() { - @Override - public void run() { - if (!result.success) { - Intent i = new Intent(context, ErrorHelperActivity.class); - i.putExtra("soundid", R.raw.boluserror); - i.putExtra("status", result.comment); - i.putExtra("title", resourceHelper.gs(R.string.tempbasaldeliveryerror)); - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(i); - } - } - }); - } - - if (pump.getPumpDescription().isExtendedBolusCapable && treatmentsPlugin.isInHistoryExtendedBoluslInProgress()) { - commandQueue.cancelExtended(new Callback() { - @Override - public void run() { - if (!result.success) { - Intent i = new Intent(context, ErrorHelperActivity.class); - i.putExtra("soundid", R.raw.boluserror); - i.putExtra("status", result.comment); - i.putExtra("title", resourceHelper.gs(R.string.extendedbolusdeliveryerror)); - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(i); - } - } - }); - } - createOfflineEvent(durationInMinutes); - } - - public void suspendLoop(int durationInMinutes) { - suspendTo(System.currentTimeMillis() + durationInMinutes * 60 * 1000); - commandQueue.cancelTempBasal(true, new Callback() { - @Override - public void run() { - if (!result.success) { - Intent i = new Intent(context, ErrorHelperActivity.class); - i.putExtra("soundid", R.raw.boluserror); - i.putExtra("status", result.comment); - i.putExtra("title", resourceHelper.gs(R.string.tempbasaldeliveryerror)); - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(i); - } - } - }); - createOfflineEvent(durationInMinutes); - } - - public void createOfflineEvent(int durationInMinutes) { - JSONObject data = new JSONObject(); - try { - data.put("eventType", CareportalEvent.OPENAPSOFFLINE); - data.put("duration", durationInMinutes); - } catch (JSONException e) { - getAapsLogger().error("Unhandled exception", e); - } - CareportalEvent event = new CareportalEvent(getInjector()); - event.date = DateUtil.now(); - event.source = Source.USER; - event.eventType = CareportalEvent.OPENAPSOFFLINE; - event.json = data.toString(); - MainApp.getDbHelper().createOrUpdate(event); - nsUpload.uploadOpenAPSOffline(event); - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt new file mode 100644 index 0000000000..91ced93606 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt @@ -0,0 +1,674 @@ +package info.nightscout.androidaps.plugins.aps.loop + +import android.annotation.SuppressLint +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.app.TaskStackBuilder +import android.content.Context +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.activities.ErrorHelperActivity +import info.nightscout.androidaps.data.DetailedBolusInfo +import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.data.PumpEnactResult +import info.nightscout.androidaps.db.CareportalEvent +import info.nightscout.androidaps.db.Source +import info.nightscout.androidaps.events.EventAcceptOpenLoopChange +import info.nightscout.androidaps.events.EventNewBG +import info.nightscout.androidaps.events.EventTempTargetChange +import info.nightscout.androidaps.interfaces.* +import info.nightscout.androidaps.interfaces.LoopInterface.LastRun +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopSetLastRunGui +import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopUpdateGui +import info.nightscout.androidaps.plugins.aps.loop.events.EventNewOpenLoopNotification +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker +import info.nightscout.androidaps.plugins.general.nsclient.NSUpload +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.notifications.Notification +import info.nightscout.androidaps.plugins.general.wear.events.EventWearDoAction +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished +import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin +import info.nightscout.androidaps.queue.Callback +import info.nightscout.androidaps.queue.commands.Command +import info.nightscout.androidaps.receivers.ReceiverStatusStore +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.utils.resources.ResourceHelper +import info.nightscout.androidaps.utils.rx.AapsSchedulers +import info.nightscout.androidaps.utils.sharedPreferences.SP +import io.reactivex.disposables.CompositeDisposable +import org.json.JSONException +import org.json.JSONObject +import javax.inject.Inject +import javax.inject.Singleton +import kotlin.math.abs + +@Singleton +open class LoopPlugin @Inject constructor( + injector: HasAndroidInjector, + aapsLogger: AAPSLogger?, + private val aapsSchedulers: AapsSchedulers, + private val rxBus: RxBusWrapper, + private val sp: SP, + config: Config, + private val constraintChecker: ConstraintChecker, + resourceHelper: ResourceHelper, + private val profileFunction: ProfileFunction, + private val context: Context, + private val commandQueue: CommandQueueProvider, + private val activePlugin: ActivePluginProvider, + private val treatmentsPlugin: TreatmentsPlugin, + private val virtualPumpPlugin: VirtualPumpPlugin, + private val iobCobCalculatorPlugin: IobCobCalculatorPlugin, + private val receiverStatusStore: ReceiverStatusStore, + private val fabricPrivacy: FabricPrivacy, + private val nsUpload: NSUpload, + private val hardLimits: HardLimits +) : 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!!, resourceHelper, injector +), LoopInterface { + + private val disposable = CompositeDisposable() + private var lastBgTriggeredRun: Long = 0 + private var carbsSuggestionsSuspendedUntil: Long = 0 + private var prevCarbsreq = 0 + override var lastRun: LastRun? = null + override fun onStart() { + createNotificationChannel() + super.onStart() + disposable.add(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. +

+ */ + 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 = iobCobCalculatorPlugin.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, + CHANNEL_ID, + NotificationManager.IMPORTANCE_HIGH) + mNotificationManager.createNotificationChannel(channel) + } + + override fun onStop() { + disposable.clear() + super.onStop() + } + + override fun specialEnableCondition(): Boolean { + return try { + val pump = activePlugin.activePump + pump.pumpDescription.isTempBasalCapable + } catch (ignored: Exception) { + // may fail during initialization + true + } + } + + fun suspendTo(endTime: Long) { + sp.putLong("loopSuspendedTill", endTime) + sp.putBoolean("isSuperBolus", false) + sp.putBoolean("isDisconnected", false) + } + + fun superBolusTo(endTime: Long) { + sp.putLong("loopSuspendedTill", endTime) + sp.putBoolean("isSuperBolus", true) + sp.putBoolean("isDisconnected", false) + } + + private fun disconnectTo(endTime: Long) { + sp.putLong("loopSuspendedTill", endTime) + sp.putBoolean("isSuperBolus", false) + sp.putBoolean("isDisconnected", true) + } + + fun minutesToEndOfSuspend(): Int { + val loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L) + if (loopSuspendedTill == 0L) return 0 + val now = System.currentTimeMillis() + val millisDiff = loopSuspendedTill - now + if (loopSuspendedTill <= now) { // time exceeded + suspendTo(0L) + return 0 + } + return (millisDiff / 60.0 / 1000.0).toInt() + } + + // time exceeded + val isSuspended: Boolean + get() { + val loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L) + if (loopSuspendedTill == 0L) return false + val now = System.currentTimeMillis() + if (loopSuspendedTill <= now) { // time exceeded + suspendTo(0L) + return false + } + return true + } + val isLGS: Boolean + get() { + val closedLoopEnabled = constraintChecker.isClosedLoopAllowed() + val maxIobAllowed = constraintChecker.getMaxIOBAllowed().value() + val apsMode = sp.getString(R.string.key_aps_mode, "open") + val pump = activePlugin.activePump + var isLGS = false + if (!isSuspended && !pump.isSuspended) if (closedLoopEnabled.value()) if (maxIobAllowed == hardLimits.MAXIOB_LGS || apsMode == "lgs") isLGS = true + return isLGS + } + + // time exceeded + val isSuperBolus: Boolean + get() { + val loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L) + if (loopSuspendedTill == 0L) return false + val now = System.currentTimeMillis() + if (loopSuspendedTill <= now) { // time exceeded + suspendTo(0L) + return false + } + return sp.getBoolean("isSuperBolus", false) + } + + // time exceeded + val isDisconnected: Boolean + get() { + val loopSuspendedTill = sp.getLong("loopSuspendedTill", 0L) + if (loopSuspendedTill == 0L) return false + val now = System.currentTimeMillis() + if (loopSuspendedTill <= now) { // time exceeded + suspendTo(0L) + return false + } + return sp.getBoolean("isDisconnected", false) + } + + @Suppress("SameParameterValue") + private fun treatmentTimeThreshold(durationMinutes: Int): Boolean { + val threshold = System.currentTimeMillis() + durationMinutes * 60 * 1000 + var bool = false + if (treatmentsPlugin.lastBolusTime > threshold || treatmentsPlugin.lastCarbTime > threshold) bool = true + return bool + } + + @Synchronized operator fun invoke(initiator: String, allowNotification: Boolean) { + invoke(initiator, allowNotification, false) + } + + @Synchronized + operator fun invoke(initiator: String, allowNotification: Boolean, tempBasalFallback: Boolean) { + try { + aapsLogger.debug(LTag.APS, "invoke from $initiator") + val loopEnabled = constraintChecker.isLoopInvocationAllowed() + if (!loopEnabled.value()) { + val message = """ + ${resourceHelper.gs(R.string.loopdisabled)} + ${loopEnabled.getReasons(aapsLogger)} + """.trimIndent() + aapsLogger.debug(LTag.APS, message) + rxBus.send(EventLoopSetLastRunGui(message)) + return + } + val pump = activePlugin.activePump + var apsResult: APSResult? = null + if (!isEnabled(PluginType.LOOP)) return + val profile = profileFunction.getProfile() + if (profile == null || !profileFunction.isProfileValid("Loop")) { + aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.noprofileselected)) + rxBus.send(EventLoopSetLastRunGui(resourceHelper.gs(R.string.noprofileselected))) + return + } + + // Check if pump info is loaded + if (pump.baseBasalRate < 0.01) return + val usedAPS = activePlugin.activeAPS + if ((usedAPS as PluginBase).isEnabled(PluginType.APS)) { + usedAPS.invoke(initiator, tempBasalFallback) + apsResult = usedAPS.lastAPSResult + } + + // Check if we have any result + if (apsResult == null) { + rxBus.send(EventLoopSetLastRunGui(resourceHelper.gs(R.string.noapsselected))) + return + } + + // Prepare for pumps using % basals + if (pump.pumpDescription.tempBasalStyle == PumpDescription.PERCENT && allowPercentage()) { + apsResult.usePercent = true + } + apsResult.percent = (apsResult.rate / profile.basal * 100).toInt() + + // check rate for constraints + val resultAfterConstraints = apsResult.newAndClone(injector) + resultAfterConstraints.rateConstraint = Constraint(resultAfterConstraints.rate) + resultAfterConstraints.rate = constraintChecker.applyBasalConstraints(resultAfterConstraints.rateConstraint!!, profile).value() + resultAfterConstraints.percentConstraint = Constraint(resultAfterConstraints.percent) + resultAfterConstraints.percent = constraintChecker.applyBasalPercentConstraints(resultAfterConstraints.percentConstraint!!, profile).value() + resultAfterConstraints.smbConstraint = Constraint(resultAfterConstraints.smb) + resultAfterConstraints.smb = constraintChecker.applyBolusConstraints(resultAfterConstraints.smbConstraint!!).value() + + // safety check for multiple SMBs + val lastBolusTime = treatmentsPlugin.lastBolusTime + if (lastBolusTime != 0L && lastBolusTime + T.mins(3).msecs() > System.currentTimeMillis()) { + aapsLogger.debug(LTag.APS, "SMB requested but still in 3 min interval") + resultAfterConstraints.smb = 0.0 + } + if (lastRun != null && lastRun!!.constraintsProcessed != null) { + prevCarbsreq = lastRun!!.constraintsProcessed!!.carbsReq + } + if (lastRun == null) lastRun = LastRun() + lastRun!!.request = apsResult + lastRun!!.constraintsProcessed = resultAfterConstraints + lastRun!!.lastAPSRun = DateUtil.now() + lastRun!!.source = (usedAPS as PluginBase).name + lastRun!!.tbrSetByPump = null + lastRun!!.smbSetByPump = null + lastRun!!.lastTBREnact = 0 + lastRun!!.lastTBRRequest = 0 + lastRun!!.lastSMBEnact = 0 + lastRun!!.lastSMBRequest = 0 + nsUpload.uploadDeviceStatus(this, iobCobCalculatorPlugin, profileFunction, activePlugin.activePump, receiverStatusStore, BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION) + if (isSuspended) { + aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.loopsuspended)) + rxBus.send(EventLoopSetLastRunGui(resourceHelper.gs(R.string.loopsuspended))) + return + } + if (pump.isSuspended) { + aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.pumpsuspended)) + rxBus.send(EventLoopSetLastRunGui(resourceHelper.gs(R.string.pumpsuspended))) + return + } + val closedLoopEnabled = constraintChecker.isClosedLoopAllowed() + 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)) { + 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)) + } + if (sp.getBoolean(R.string.key_ns_create_announcements_from_carbs_req, false)) { + nsUpload.uploadError(resultAfterConstraints.carbsRequiredText) + } + if (sp.getBoolean(R.string.key_enable_carbs_required_alert_local, true) && sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, true)) { + val intentAction5m = Intent(context, CarbSuggestionReceiver::class.java) + intentAction5m.putExtra("ignoreDuration", 5) + val pendingIntent5m = PendingIntent.getBroadcast(context, 1, intentAction5m, PendingIntent.FLAG_UPDATE_CURRENT) + val actionIgnore5m = NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore5m, "Ignore 5m"), pendingIntent5m) + val intentAction15m = Intent(context, CarbSuggestionReceiver::class.java) + intentAction15m.putExtra("ignoreDuration", 15) + val pendingIntent15m = PendingIntent.getBroadcast(context, 1, intentAction15m, PendingIntent.FLAG_UPDATE_CURRENT) + val actionIgnore15m = NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore15m, "Ignore 15m"), pendingIntent15m) + val intentAction30m = Intent(context, CarbSuggestionReceiver::class.java) + intentAction30m.putExtra("ignoreDuration", 30) + val pendingIntent30m = PendingIntent.getBroadcast(context, 1, intentAction30m, PendingIntent.FLAG_UPDATE_CURRENT) + val actionIgnore30m = NotificationCompat.Action(R.drawable.ic_notif_aaps, resourceHelper.gs(R.string.ignore30m, "Ignore 30m"), pendingIntent30m) + val builder = NotificationCompat.Builder(context, CHANNEL_ID) + builder.setSmallIcon(R.drawable.notif_icon) + .setContentTitle(resourceHelper.gs(R.string.carbssuggestion)) + .setContentText(resultAfterConstraints.carbsRequiredText) + .setAutoCancel(true) + .setPriority(Notification.IMPORTANCE_HIGH) + .setCategory(Notification.CATEGORY_ALARM) + .addAction(actionIgnore5m) + .addAction(actionIgnore15m) + .addAction(actionIgnore30m) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setVibrate(longArrayOf(1000, 1000, 1000, 1000, 1000)) + val mNotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + + // mId allows you to update the notification later on. + mNotificationManager.notify(Constants.notificationID, builder.build()) + rxBus.send(EventNewOpenLoopNotification()) + + //only send to wear if Native notifications are turned off + if (!sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, true)) { + // Send to Wear + rxBus.send(EventWearDoAction("changeRequest")) + } + } + } else { + //If carbs were required previously, but are no longer needed, dismiss notifications + if (prevCarbsreq > 0) { + dismissSuggestion() + rxBus.send(EventDismissNotification(Notification.CARBS_REQUIRED)) + } + } + } + if (resultAfterConstraints.isChangeRequested + && !commandQueue.bolusInQueue() + && !commandQueue.isRunning(Command.CommandType.BOLUS)) { + val waiting = PumpEnactResult(injector) + waiting.queued = true + if (resultAfterConstraints.tempBasalRequested) lastRun!!.tbrSetByPump = waiting + if (resultAfterConstraints.bolusRequested) lastRun!!.smbSetByPump = waiting + rxBus.send(EventLoopUpdateGui()) + fabricPrivacy.logCustom("APSRequest") + applyTBRRequest(resultAfterConstraints, profile, object : Callback() { + override fun run() { + if (result.enacted || result.success) { + lastRun!!.tbrSetByPump = result + lastRun!!.lastTBRRequest = lastRun!!.lastAPSRun + lastRun!!.lastTBREnact = DateUtil.now() + rxBus.send(EventLoopUpdateGui()) + applySMBRequest(resultAfterConstraints, object : Callback() { + override fun run() { + // Callback is only called if a bolus was actually requested + if (result.enacted || result.success) { + lastRun!!.smbSetByPump = result + lastRun!!.lastSMBRequest = lastRun!!.lastAPSRun + lastRun!!.lastSMBEnact = DateUtil.now() + } else { + Thread { + SystemClock.sleep(1000) + invoke("tempBasalFallback", allowNotification, true) + }.start() + } + rxBus.send(EventLoopUpdateGui()) + } + }) + } else { + lastRun!!.tbrSetByPump = result + lastRun!!.lastTBRRequest = lastRun!!.lastAPSRun + } + rxBus.send(EventLoopUpdateGui()) + } + }) + } else { + lastRun!!.tbrSetByPump = null + lastRun!!.smbSetByPump = null + } + } else { + if (resultAfterConstraints.isChangeRequested && allowNotification) { + val builder = NotificationCompat.Builder(context, CHANNEL_ID) + builder.setSmallIcon(R.drawable.notif_icon) + .setContentTitle(resourceHelper.gs(R.string.openloop_newsuggestion)) + .setContentText(resultAfterConstraints.toString()) + .setAutoCancel(true) + .setPriority(Notification.IMPORTANCE_HIGH) + .setCategory(Notification.CATEGORY_ALARM) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + if (sp.getBoolean(R.string.key_wear_control, false)) { + builder.setLocalOnly(true) + } + presentSuggestion(builder) + } else if (allowNotification) { + dismissSuggestion() + } + } + rxBus.send(EventLoopUpdateGui()) + } finally { + aapsLogger.debug(LTag.APS, "invoke end") + } + } + + fun disableCarbSuggestions(durationMinutes: Int) { + carbsSuggestionsSuspendedUntil = System.currentTimeMillis() + durationMinutes * 60 * 1000 + dismissSuggestion() + } + + private fun presentSuggestion(builder: NotificationCompat.Builder) { + // Creates an explicit intent for an Activity in your app + val resultIntent = Intent(context, MainActivity::class.java) + + // The stack builder object will contain an artificial back stack for the + // started Activity. + // This ensures that navigating backward from the Activity leads out of + // your application to the Home screen. + val stackBuilder = TaskStackBuilder.create(context) + stackBuilder.addParentStack(MainActivity::class.java) + // Adds the Intent that starts the Activity to the top of the stack + stackBuilder.addNextIntent(resultIntent) + val resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT) + builder.setContentIntent(resultPendingIntent) + builder.setVibrate(longArrayOf(1000, 1000, 1000, 1000, 1000)) + val mNotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + // mId allows you to update the notification later on. + mNotificationManager.notify(Constants.notificationID, builder.build()) + rxBus.send(EventNewOpenLoopNotification()) + + // Send to Wear + rxBus.send(EventWearDoAction("changeRequest")) + } + + private fun dismissSuggestion() { + // dismiss notifications + val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.cancel(Constants.notificationID) + rxBus.send(EventWearDoAction("cancelChangeRequest")) + } + + fun acceptChangeRequest() { + val profile = profileFunction.getProfile() + val lp = this + applyTBRRequest(lastRun!!.constraintsProcessed, profile, object : Callback() { + override fun run() { + if (result.enacted) { + lastRun!!.tbrSetByPump = result + lastRun!!.lastTBRRequest = lastRun!!.lastAPSRun + lastRun!!.lastTBREnact = DateUtil.now() + lastRun!!.lastOpenModeAccept = DateUtil.now() + nsUpload.uploadDeviceStatus(lp, iobCobCalculatorPlugin, profileFunction, activePlugin.activePump, receiverStatusStore, BuildConfig.VERSION_NAME + "-" + BuildConfig.BUILDVERSION) + sp.incInt(R.string.key_ObjectivesmanualEnacts) + } + rxBus.send(EventAcceptOpenLoopChange()) + } + }) + fabricPrivacy.logCustom("AcceptTemp") + } + + /** + * expect absolute request and allow both absolute and percent response based on pump capabilities + * TODO: update pump drivers to support APS request in % + */ + private fun applyTBRRequest(request: APSResult?, profile: Profile?, callback: Callback?) { + if (!request!!.tempBasalRequested) { + callback?.result(PumpEnactResult(injector).enacted(false).success(true).comment(resourceHelper.gs(R.string.nochangerequested)))?.run() + return + } + val pump = activePlugin.activePump + if (!pump.isInitialized) { + aapsLogger.debug(LTag.APS, "applyAPSRequest: " + resourceHelper.gs(R.string.pumpNotInitialized)) + callback?.result(PumpEnactResult(injector).comment(resourceHelper.gs(R.string.pumpNotInitialized)).enacted(false).success(false))?.run() + return + } + if (pump.isSuspended) { + aapsLogger.debug(LTag.APS, "applyAPSRequest: " + resourceHelper.gs(R.string.pumpsuspended)) + callback?.result(PumpEnactResult(injector).comment(resourceHelper.gs(R.string.pumpsuspended)).enacted(false).success(false))?.run() + return + } + aapsLogger.debug(LTag.APS, "applyAPSRequest: $request") + val now = System.currentTimeMillis() + val activeTemp = treatmentsPlugin.getTempBasalFromHistory(now) + if (request.usePercent && allowPercentage()) { + if (request.percent == 100 && request.duration == 0) { + if (activeTemp != null) { + aapsLogger.debug(LTag.APS, "applyAPSRequest: cancelTempBasal()") + 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(resourceHelper.gs(R.string.basal_set_correctly)))?.run() + } + } else if (activeTemp != null && activeTemp.plannedRemainingMinutes > 5 && request.duration - activeTemp.plannedRemainingMinutes < 30 && request.percent == activeTemp.percentRate) { + aapsLogger.debug(LTag.APS, "applyAPSRequest: Temp basal set correctly") + callback?.result(PumpEnactResult(injector).percent(request.percent) + .enacted(false).success(true).duration(activeTemp.plannedRemainingMinutes) + .comment(resourceHelper.gs(R.string.let_temp_basal_run)))?.run() + } else { + aapsLogger.debug(LTag.APS, "applyAPSRequest: tempBasalPercent()") + commandQueue.tempBasalPercent(request.percent, request.duration, false, profile!!, callback) + } + } else { + if (request.rate == 0.0 && request.duration == 0 || abs(request.rate - pump.baseBasalRate) < pump.pumpDescription.basalStep) { + if (activeTemp != null) { + aapsLogger.debug(LTag.APS, "applyAPSRequest: cancelTempBasal()") + 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(resourceHelper.gs(R.string.basal_set_correctly)))?.run() + } + } else if (activeTemp != null && activeTemp.plannedRemainingMinutes > 5 && request.duration - activeTemp.plannedRemainingMinutes < 30 && abs(request.rate - activeTemp.tempBasalConvertedToAbsolute(now, profile)) < pump.pumpDescription.basalStep) { + aapsLogger.debug(LTag.APS, "applyAPSRequest: Temp basal set correctly") + callback?.result(PumpEnactResult(injector).absolute(activeTemp.tempBasalConvertedToAbsolute(now, profile)) + .enacted(false).success(true).duration(activeTemp.plannedRemainingMinutes) + .comment(resourceHelper.gs(R.string.let_temp_basal_run)))?.run() + } else { + aapsLogger.debug(LTag.APS, "applyAPSRequest: setTempBasalAbsolute()") + commandQueue.tempBasalAbsolute(request.rate, request.duration, false, profile!!, callback) + } + } + } + + private fun applySMBRequest(request: APSResult, callback: Callback?) { + if (!request.bolusRequested) { + return + } + val pump = activePlugin.activePump + val lastBolusTime = treatmentsPlugin.lastBolusTime + 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(resourceHelper.gs(R.string.smb_frequency_exceeded)) + .enacted(false).success(false))?.run() + return + } + if (!pump.isInitialized) { + aapsLogger.debug(LTag.APS, "applySMBRequest: " + resourceHelper.gs(R.string.pumpNotInitialized)) + callback?.result(PumpEnactResult(injector).comment(resourceHelper.gs(R.string.pumpNotInitialized)).enacted(false).success(false))?.run() + return + } + if (pump.isSuspended) { + aapsLogger.debug(LTag.APS, "applySMBRequest: " + resourceHelper.gs(R.string.pumpsuspended)) + callback?.result(PumpEnactResult(injector).comment(resourceHelper.gs(R.string.pumpsuspended)).enacted(false).success(false))?.run() + return + } + aapsLogger.debug(LTag.APS, "applySMBRequest: $request") + + // deliver SMB + val detailedBolusInfo = DetailedBolusInfo() + detailedBolusInfo.lastKnownBolusTime = treatmentsPlugin.lastBolusTime + detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS + detailedBolusInfo.insulin = request.smb + detailedBolusInfo.isSMB = true + detailedBolusInfo.source = Source.USER + detailedBolusInfo.deliverAt = request.deliverAt + aapsLogger.debug(LTag.APS, "applyAPSRequest: bolus()") + commandQueue.bolus(detailedBolusInfo, callback) + } + + private fun allowPercentage(): Boolean { + return virtualPumpPlugin.isEnabled(PluginType.PUMP) + } + + fun disconnectPump(durationInMinutes: Int, profile: Profile?) { + val pump = activePlugin.activePump + disconnectTo(System.currentTimeMillis() + durationInMinutes * 60 * 1000L) + if (pump.pumpDescription.tempBasalStyle == PumpDescription.ABSOLUTE) { + commandQueue.tempBasalAbsolute(0.0, durationInMinutes, true, profile!!, object : Callback() { + override fun run() { + if (!result.success) { + ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror) + } + } + }) + } else { + commandQueue.tempBasalPercent(0, durationInMinutes, true, profile!!, object : Callback() { + override fun run() { + if (!result.success) { + ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror) + } + } + }) + } + if (pump.pumpDescription.isExtendedBolusCapable && treatmentsPlugin.isInHistoryExtendedBoluslInProgress) { + commandQueue.cancelExtended(object : Callback() { + override fun run() { + if (!result.success) { + ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.extendedbolusdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror) + } + } + }) + } + createOfflineEvent(durationInMinutes) + } + + fun suspendLoop(durationInMinutes: Int) { + suspendTo(System.currentTimeMillis() + durationInMinutes * 60 * 1000) + commandQueue.cancelTempBasal(true, object : Callback() { + override fun run() { + if (!result.success) { + ErrorHelperActivity.runAlarm(context, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror) + } + } + }) + createOfflineEvent(durationInMinutes) + } + + fun createOfflineEvent(durationInMinutes: Int) { + val data = JSONObject() + try { + data.put("eventType", CareportalEvent.OPENAPSOFFLINE) + data.put("duration", durationInMinutes) + } catch (e: JSONException) { + aapsLogger.error("Unhandled exception", e) + } + val event = CareportalEvent(injector) + event.date = DateUtil.now() + event.source = Source.USER + event.eventType = CareportalEvent.OPENAPSOFFLINE + event.json = data.toString() + MainApp.getDbHelper().createOrUpdate(event) + nsUpload.uploadOpenAPSOffline(event) + } + + companion object { + + private const val CHANNEL_ID = "AndroidAPS-OpenLoop" + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/ScriptReader.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/ScriptReader.java deleted file mode 100644 index e3a64dbece..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/ScriptReader.java +++ /dev/null @@ -1,41 +0,0 @@ -package info.nightscout.androidaps.plugins.aps.loop; - -import android.content.Context; -import android.content.res.AssetManager; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; - -public class ScriptReader { - - private final Context mContext; - - public ScriptReader(Context context) { - mContext = context; - } - - public byte[] readFile(String fileName) throws IOException { - - AssetManager assetManager = mContext.getAssets(); - InputStream is = assetManager.open(fileName); - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - - int nRead; - byte[] data = new byte[16384]; - - while ((nRead = is.read(data, 0, data.length)) != -1) { - buffer.write(data, 0, nRead); - } - - buffer.flush(); - - byte[] bytes = buffer.toByteArray(); - is.close(); - buffer.close(); - - - return bytes; - - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/ScriptReader.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/ScriptReader.kt new file mode 100644 index 0000000000..000cd55900 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/ScriptReader.kt @@ -0,0 +1,25 @@ +package info.nightscout.androidaps.plugins.aps.loop + +import android.content.Context +import java.io.ByteArrayOutputStream +import java.io.IOException + +class ScriptReader(private val context: Context) { + + @Throws(IOException::class) + fun readFile(fileName: String): ByteArray { + val assetManager = context.assets + val `is` = assetManager.open(fileName) + val buffer = ByteArrayOutputStream() + var nRead: Int + val data = ByteArray(16384) + while (`is`.read(data, 0, data.size).also { nRead = it } != -1) { + buffer.write(data, 0, nRead) + } + buffer.flush() + val bytes = buffer.toByteArray() + `is`.close() + buffer.close() + return bytes + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.java deleted file mode 100644 index a6876a3144..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.java +++ /dev/null @@ -1,299 +0,0 @@ -package info.nightscout.androidaps.plugins.aps.openAPSAMA; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.mozilla.javascript.Context; -import org.mozilla.javascript.Function; -import org.mozilla.javascript.NativeJSON; -import org.mozilla.javascript.NativeObject; -import org.mozilla.javascript.RhinoException; -import org.mozilla.javascript.Scriptable; -import org.mozilla.javascript.ScriptableObject; -import org.mozilla.javascript.Undefined; - -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.nio.charset.StandardCharsets; - -import javax.annotation.Nullable; -import javax.inject.Inject; - -import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader; -import dagger.android.HasAndroidInjector; -import info.nightscout.androidaps.Constants; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.IobTotal; -import info.nightscout.androidaps.data.MealData; -import info.nightscout.androidaps.data.Profile; -import info.nightscout.androidaps.db.TemporaryBasal; -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.aps.loop.ScriptReader; -import info.nightscout.androidaps.plugins.aps.logger.LoggerCallback; -import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults; -import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker; -import info.nightscout.androidaps.interfaces.ProfileFunction; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; -import info.nightscout.androidaps.utils.sharedPreferences.SP; - -public class DetermineBasalAdapterAMAJS { - private final HasAndroidInjector injector; - @Inject AAPSLogger aapsLogger; - @Inject ConstraintChecker constraintChecker; - @Inject SP sp; - @Inject ProfileFunction profileFunction; - @Inject TreatmentsPlugin treatmentsPlugin; - @Inject OpenHumansUploader openHumansUploader; - - private final ScriptReader mScriptReader; - - private JSONObject mProfile; - private JSONObject mGlucoseStatus; - private JSONArray mIobData; - private JSONObject mMealData; - private JSONObject mCurrentTemp; - private JSONObject mAutosensData = null; - - private String storedCurrentTemp = null; - private String storedIobData = null; - private String storedGlucoseStatus = null; - private String storedProfile = null; - private String storedMeal_data = null; - private String storedAutosens_data = null; - - private String scriptDebug = ""; - - DetermineBasalAdapterAMAJS(ScriptReader scriptReader, HasAndroidInjector injector) { - injector.androidInjector().inject(this); - mScriptReader = scriptReader; - this.injector = injector; - } - - @Nullable - public DetermineBasalResultAMA invoke() { - - aapsLogger.debug(LTag.APS, ">>> Invoking detemine_basal <<<"); - aapsLogger.debug(LTag.APS, "Glucose status: " + (storedGlucoseStatus = mGlucoseStatus.toString())); - aapsLogger.debug(LTag.APS, "IOB data: " + (storedIobData = mIobData.toString())); - aapsLogger.debug(LTag.APS, "Current temp: " + (storedCurrentTemp = mCurrentTemp.toString())); - aapsLogger.debug(LTag.APS, "Profile: " + (storedProfile = mProfile.toString())); - aapsLogger.debug(LTag.APS, "Meal data: " + (storedMeal_data = mMealData.toString())); - if (mAutosensData != null) - aapsLogger.debug(LTag.APS, "Autosens data: " + (storedAutosens_data = mAutosensData.toString())); - else - aapsLogger.debug(LTag.APS, "Autosens data: " + (storedAutosens_data = "undefined")); - - - DetermineBasalResultAMA determineBasalResultAMA = null; - - Context rhino = Context.enter(); - Scriptable scope = rhino.initStandardObjects(); - // Turn off optimization to make Rhino Android compatible - rhino.setOptimizationLevel(-1); - - try { - - //register logger callback for console.log and console.error - ScriptableObject.defineClass(scope, LoggerCallback.class); - Scriptable myLogger = rhino.newObject(scope, "LoggerCallback", null); - scope.put("console2", scope, myLogger); - rhino.evaluateString(scope, readFile("OpenAPSAMA/loggerhelper.js"), "JavaScript", 0, null); - - //set module parent - rhino.evaluateString(scope, "var module = {\"parent\":Boolean(1)};", "JavaScript", 0, null); - rhino.evaluateString(scope, "var round_basal = function round_basal(basal, profile) { return basal; };", "JavaScript", 0, null); - rhino.evaluateString(scope, "require = function() {return round_basal;};", "JavaScript", 0, null); - - //generate functions "determine_basal" and "setTempBasal" - rhino.evaluateString(scope, readFile("OpenAPSAMA/determine-basal.js"), "JavaScript", 0, null); - rhino.evaluateString(scope, readFile("OpenAPSAMA/basal-set-temp.js"), "setTempBasal.js", 0, null); - Object determineBasalObj = scope.get("determine_basal", scope); - Object setTempBasalFunctionsObj = scope.get("tempBasalFunctions", scope); - - //call determine-basal - if (determineBasalObj instanceof Function && setTempBasalFunctionsObj instanceof NativeObject) { - Function determineBasalJS = (Function) determineBasalObj; - - //prepare parameters - Object[] params = new Object[]{ - makeParam(mGlucoseStatus, rhino, scope), - makeParam(mCurrentTemp, rhino, scope), - makeParamArray(mIobData, rhino, scope), - makeParam(mProfile, rhino, scope), - makeParam(mAutosensData, rhino, scope), - makeParam(mMealData, rhino, scope), - setTempBasalFunctionsObj}; - - NativeObject jsResult = (NativeObject) determineBasalJS.call(rhino, scope, scope, params); - scriptDebug = LoggerCallback.getScriptDebug(); - - // Parse the jsResult object to a JSON-String - String result = NativeJSON.stringify(rhino, scope, jsResult, null, null).toString(); - aapsLogger.debug(LTag.APS, "Result: " + result); - try { - JSONObject resultJson = new JSONObject(result); - openHumansUploader.enqueueAMAData(mProfile, mGlucoseStatus, mIobData, mMealData, mCurrentTemp, mAutosensData, resultJson); - determineBasalResultAMA = new DetermineBasalResultAMA(injector, jsResult, resultJson); - } catch (JSONException e) { - aapsLogger.error(LTag.APS, "Unhandled exception", e); - } - } else { - aapsLogger.error(LTag.APS, "Problem loading JS Functions"); - } - } catch (IOException e) { - aapsLogger.error(LTag.APS, "IOException"); - } catch (RhinoException e) { - aapsLogger.error(LTag.APS, "RhinoException: (" + e.lineNumber() + "," + e.columnNumber() + ") " + e.toString()); - } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) { - aapsLogger.error(LTag.APS, e.toString()); - } finally { - Context.exit(); - } - - storedGlucoseStatus = mGlucoseStatus.toString(); - storedIobData = mIobData.toString(); - storedCurrentTemp = mCurrentTemp.toString(); - storedProfile = mProfile.toString(); - storedMeal_data = mMealData.toString(); - - return determineBasalResultAMA; - - } - - String getGlucoseStatusParam() { - return storedGlucoseStatus; - } - - String getCurrentTempParam() { - return storedCurrentTemp; - } - - String getIobDataParam() { - return storedIobData; - } - - String getProfileParam() { - return storedProfile; - } - - String getMealDataParam() { - return storedMeal_data; - } - - String getAutosensDataParam() { - return storedAutosens_data; - } - - String getScriptDebug() { - return scriptDebug; - } - - public void setData(Profile profile, - double maxIob, - double maxBasal, - double minBg, - double maxBg, - double targetBg, - double basalrate, - IobTotal[] iobArray, - GlucoseStatus glucoseStatus, - MealData mealData, - double autosensDataRatio, - boolean tempTargetSet) throws JSONException { - - mProfile = new JSONObject(); - mProfile.put("max_iob", maxIob); - mProfile.put("dia", Math.min(profile.getDia(), 3d)); - mProfile.put("type", "current"); - mProfile.put("max_daily_basal", profile.getMaxDailyBasal()); - mProfile.put("max_basal", maxBasal); - mProfile.put("min_bg", minBg); - mProfile.put("max_bg", maxBg); - mProfile.put("target_bg", targetBg); - mProfile.put("carb_ratio", profile.getIc()); - mProfile.put("sens", profile.getIsfMgdl()); - mProfile.put("max_daily_safety_multiplier", sp.getInt(R.string.key_openapsama_max_daily_safety_multiplier, 3)); - mProfile.put("current_basal_safety_multiplier", sp.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4d)); - mProfile.put("skip_neutral_temps", true); - mProfile.put("current_basal", basalrate); - mProfile.put("temptargetSet", tempTargetSet); - mProfile.put("autosens_adjust_targets", sp.getBoolean(R.string.key_openapsama_autosens_adjusttargets, true)); - //align with max-absorption model in AMA sensitivity - if (mealData.usedMinCarbsImpact > 0) { - mProfile.put("min_5m_carbimpact", mealData.usedMinCarbsImpact); - } else { - mProfile.put("min_5m_carbimpact", sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact)); - } - - if (profileFunction.getUnits().equals(Constants.MMOL)) { - mProfile.put("out_units", "mmol/L"); - } - - long now = System.currentTimeMillis(); - TemporaryBasal tb = treatmentsPlugin.getTempBasalFromHistory(now); - - mCurrentTemp = new JSONObject(); - mCurrentTemp.put("temp", "absolute"); - mCurrentTemp.put("duration", tb != null ? tb.getPlannedRemainingMinutes() : 0); - mCurrentTemp.put("rate", tb != null ? tb.tempBasalConvertedToAbsolute(now, profile) : 0d); - - // as we have non default temps longer than 30 mintues - TemporaryBasal tempBasal = treatmentsPlugin.getTempBasalFromHistory(System.currentTimeMillis()); - if (tempBasal != null) { - mCurrentTemp.put("minutesrunning", tempBasal.getRealDuration()); - } - - mIobData = IobCobCalculatorPlugin.convertToJSONArray(iobArray); - - mGlucoseStatus = new JSONObject(); - mGlucoseStatus.put("glucose", glucoseStatus.glucose); - - if (sp.getBoolean(R.string.key_always_use_shortavg, false)) { - mGlucoseStatus.put("delta", glucoseStatus.short_avgdelta); - } else { - mGlucoseStatus.put("delta", glucoseStatus.delta); - } - mGlucoseStatus.put("short_avgdelta", glucoseStatus.short_avgdelta); - mGlucoseStatus.put("long_avgdelta", glucoseStatus.long_avgdelta); - - mMealData = new JSONObject(); - mMealData.put("carbs", mealData.carbs); - mMealData.put("boluses", mealData.boluses); - mMealData.put("mealCOB", mealData.mealCOB); - - if (constraintChecker.isAutosensModeEnabled().value()) { - mAutosensData = new JSONObject(); - mAutosensData.put("ratio", autosensDataRatio); - } else { - mAutosensData = null; - } - } - - - private Object makeParam(JSONObject jsonObject, Context rhino, Scriptable scope) { - - if (jsonObject == null) return Undefined.instance; - - Object param = NativeJSON.parse(rhino, scope, jsonObject.toString(), (context, scriptable, scriptable1, objects) -> objects[1]); - return param; - } - - private Object makeParamArray(JSONArray jsonArray, Context rhino, Scriptable scope) { - //Object param = NativeJSON.parse(rhino, scope, "{myarray: " + jsonArray.toString() + " }", new Callable() { - Object param = NativeJSON.parse(rhino, scope, jsonArray.toString(), (context, scriptable, scriptable1, objects) -> objects[1]); - return param; - } - - private String readFile(String filename) throws IOException { - byte[] bytes = mScriptReader.readFile(filename); - String string = new String(bytes, StandardCharsets.UTF_8); - if (string.startsWith("#!/usr/bin/env node")) { - string = string.substring(20); - } - return string; - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.kt new file mode 100644 index 0000000000..ee0be787e4 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.kt @@ -0,0 +1,238 @@ +package info.nightscout.androidaps.plugins.aps.openAPSAMA + +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.Constants +import info.nightscout.androidaps.R +import info.nightscout.androidaps.data.IobTotal +import info.nightscout.androidaps.data.MealData +import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.aps.logger.LoggerCallback +import info.nightscout.androidaps.plugins.aps.loop.ScriptReader +import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults +import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker +import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin +import info.nightscout.androidaps.utils.sharedPreferences.SP +import org.json.JSONArray +import org.json.JSONException +import org.json.JSONObject +import org.mozilla.javascript.* +import org.mozilla.javascript.Function +import java.io.IOException +import java.lang.reflect.InvocationTargetException +import java.nio.charset.StandardCharsets +import javax.inject.Inject +import kotlin.math.min + +class DetermineBasalAdapterAMAJS internal constructor(scriptReader: ScriptReader, injector: HasAndroidInjector) { + + private val injector: HasAndroidInjector + + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var constraintChecker: ConstraintChecker + @Inject lateinit var sp: SP + @Inject lateinit var profileFunction: ProfileFunction + @Inject lateinit var treatmentsPlugin: TreatmentsPlugin + @Inject lateinit var openHumansUploader: OpenHumansUploader + + private val mScriptReader: ScriptReader + private var profile = JSONObject() + private var glucoseStatus = JSONObject() + private var iobData: JSONArray? = null + private var mealData = JSONObject() + private var currentTemp = JSONObject() + private var autosensData = JSONObject() + + var currentTempParam: String? = null + private set + var iobDataParam: String? = null + private set + var glucoseStatusParam: String? = null + private set + var profileParam: String? = null + private set + var mealDataParam: String? = null + private set + var scriptDebug = "" + private set + + operator fun invoke(): DetermineBasalResultAMA? { + aapsLogger.debug(LTag.APS, ">>> Invoking determine_basal <<<") + aapsLogger.debug(LTag.APS, "Glucose status: " + glucoseStatus.toString().also { glucoseStatusParam = it }) + aapsLogger.debug(LTag.APS, "IOB data: " + iobData.toString().also { iobDataParam = it }) + aapsLogger.debug(LTag.APS, "Current temp: " + currentTemp.toString().also { currentTempParam = it }) + aapsLogger.debug(LTag.APS, "Profile: " + profile.toString().also { profileParam = it }) + aapsLogger.debug(LTag.APS, "Meal data: " + mealData.toString().also { mealDataParam = it }) + aapsLogger.debug(LTag.APS, "Autosens data: $autosensData") + var determineBasalResultAMA: DetermineBasalResultAMA? = null + val rhino = Context.enter() + val scope: Scriptable = rhino.initStandardObjects() + // Turn off optimization to make Rhino Android compatible + rhino.optimizationLevel = -1 + try { + + //register logger callback for console.log and console.error + ScriptableObject.defineClass(scope, LoggerCallback::class.java) + val myLogger = rhino.newObject(scope, "LoggerCallback", null) + scope.put("console2", scope, myLogger) + rhino.evaluateString(scope, readFile("OpenAPSAMA/loggerhelper.js"), "JavaScript", 0, null) + + //set module parent + rhino.evaluateString(scope, "var module = {\"parent\":Boolean(1)};", "JavaScript", 0, null) + rhino.evaluateString(scope, "var round_basal = function round_basal(basal, profile) { return basal; };", "JavaScript", 0, null) + rhino.evaluateString(scope, "require = function() {return round_basal;};", "JavaScript", 0, null) + + //generate functions "determine_basal" and "setTempBasal" + rhino.evaluateString(scope, readFile("OpenAPSAMA/determine-basal.js"), "JavaScript", 0, null) + rhino.evaluateString(scope, readFile("OpenAPSAMA/basal-set-temp.js"), "setTempBasal.js", 0, null) + val determineBasalObj = scope["determine_basal", scope] + val setTempBasalFunctionsObj = scope["tempBasalFunctions", scope] + + //call determine-basal + if (determineBasalObj is Function && setTempBasalFunctionsObj is NativeObject) { + + //prepare parameters + val params = arrayOf( + makeParam(glucoseStatus, rhino, scope), + makeParam(currentTemp, rhino, scope), + makeParamArray(iobData, rhino, scope), + makeParam(profile, rhino, scope), + makeParam(autosensData, rhino, scope), + makeParam(mealData, rhino, scope), + setTempBasalFunctionsObj) + val jsResult = determineBasalObj.call(rhino, scope, scope, params) as NativeObject + scriptDebug = LoggerCallback.scriptDebug + + // Parse the jsResult object to a JSON-String + val result = NativeJSON.stringify(rhino, scope, jsResult, null, null).toString() + aapsLogger.debug(LTag.APS, "Result: $result") + try { + val resultJson = JSONObject(result) + openHumansUploader.enqueueAMAData(profile, glucoseStatus, iobData, mealData, currentTemp, autosensData, resultJson) + determineBasalResultAMA = DetermineBasalResultAMA(injector, jsResult, resultJson) + } catch (e: JSONException) { + aapsLogger.error(LTag.APS, "Unhandled exception", e) + } + } else { + aapsLogger.error(LTag.APS, "Problem loading JS Functions") + } + } catch (e: IOException) { + aapsLogger.error(LTag.APS, "IOException") + } catch (e: RhinoException) { + aapsLogger.error(LTag.APS, "RhinoException: (" + e.lineNumber() + "," + e.columnNumber() + ") " + e.toString()) + } catch (e: IllegalAccessException) { + aapsLogger.error(LTag.APS, e.toString()) + } catch (e: InstantiationException) { + aapsLogger.error(LTag.APS, e.toString()) + } catch (e: InvocationTargetException) { + aapsLogger.error(LTag.APS, e.toString()) + } finally { + Context.exit() + } + glucoseStatusParam = glucoseStatus.toString() + iobDataParam = iobData.toString() + currentTempParam = currentTemp.toString() + profileParam = profile.toString() + mealDataParam = mealData.toString() + return determineBasalResultAMA + } + + @Throws(JSONException::class) fun setData(profile: Profile, + maxIob: Double, + maxBasal: Double, + minBg: Double, + maxBg: Double, + targetBg: Double, + basalRate: Double, + iobArray: Array?, + glucoseStatus: GlucoseStatus, + mealData: MealData, + autosensDataRatio: Double, + tempTargetSet: Boolean) { + this.profile = JSONObject() + this.profile.put("max_iob", maxIob) + this.profile.put("dia", min(profile.dia, 3.0)) + this.profile.put("type", "current") + this.profile.put("max_daily_basal", profile.maxDailyBasal) + this.profile.put("max_basal", maxBasal) + this.profile.put("min_bg", minBg) + this.profile.put("max_bg", maxBg) + this.profile.put("target_bg", targetBg) + this.profile.put("carb_ratio", profile.ic) + this.profile.put("sens", profile.isfMgdl) + this.profile.put("max_daily_safety_multiplier", sp.getInt(R.string.key_openapsama_max_daily_safety_multiplier, 3)) + this.profile.put("current_basal_safety_multiplier", sp.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4.0)) + this.profile.put("skip_neutral_temps", true) + this.profile.put("current_basal", basalRate) + this.profile.put("temptargetSet", tempTargetSet) + this.profile.put("autosens_adjust_targets", sp.getBoolean(R.string.key_openapsama_autosens_adjusttargets, true)) + //align with max-absorption model in AMA sensitivity + if (mealData.usedMinCarbsImpact > 0) { + this.profile.put("min_5m_carbimpact", mealData.usedMinCarbsImpact) + } else { + this.profile.put("min_5m_carbimpact", sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact)) + } + if (profileFunction.getUnits() == Constants.MMOL) { + this.profile.put("out_units", "mmol/L") + } + val now = System.currentTimeMillis() + val tb = treatmentsPlugin.getTempBasalFromHistory(now) + currentTemp = JSONObject() + currentTemp.put("temp", "absolute") + currentTemp.put("duration", tb?.plannedRemainingMinutes ?: 0) + currentTemp.put("rate", tb?.tempBasalConvertedToAbsolute(now, profile) ?: 0.0) + + // as we have non default temps longer than 30 minutes + val tempBasal = treatmentsPlugin.getTempBasalFromHistory(System.currentTimeMillis()) + if (tempBasal != null) { + currentTemp.put("minutesrunning", tempBasal.realDuration) + } + iobData = IobCobCalculatorPlugin.convertToJSONArray(iobArray) + this.glucoseStatus = JSONObject() + this.glucoseStatus.put("glucose", glucoseStatus.glucose) + if (sp.getBoolean(R.string.key_always_use_shortavg, false)) { + this.glucoseStatus.put("delta", glucoseStatus.short_avgdelta) + } else { + this.glucoseStatus.put("delta", glucoseStatus.delta) + } + this.glucoseStatus.put("short_avgdelta", glucoseStatus.short_avgdelta) + this.glucoseStatus.put("long_avgdelta", glucoseStatus.long_avgdelta) + this.mealData = JSONObject() + this.mealData.put("carbs", mealData.carbs) + this.mealData.put("boluses", mealData.boluses) + this.mealData.put("mealCOB", mealData.mealCOB) + if (constraintChecker.isAutosensModeEnabled().value()) { + autosensData.put("ratio", autosensDataRatio) + } else { + autosensData.put("ratio", 1.0) + } + } + + private fun makeParam(jsonObject: JSONObject?, rhino: Context, scope: Scriptable): Any { + return if (jsonObject == null) Undefined.instance else NativeJSON.parse(rhino, scope, jsonObject.toString()) { _: Context?, _: Scriptable?, _: Scriptable?, objects: Array -> objects[1] } + } + + private fun makeParamArray(jsonArray: JSONArray?, rhino: Context, scope: Scriptable): Any { + return NativeJSON.parse(rhino, scope, jsonArray.toString()) { _: Context?, _: Scriptable?, _: Scriptable?, objects: Array -> objects[1] } + } + + @Throws(IOException::class) private fun readFile(filename: String): String { + val bytes = mScriptReader.readFile(filename) + var string = String(bytes, StandardCharsets.UTF_8) + if (string.startsWith("#!/usr/bin/env node")) { + string = string.substring(20) + } + return string + } + + init { + injector.androidInjector().inject(this) + mScriptReader = scriptReader + this.injector = injector + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalResultAMA.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalResultAMA.java deleted file mode 100644 index 0bf4839f84..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalResultAMA.java +++ /dev/null @@ -1,77 +0,0 @@ -package info.nightscout.androidaps.plugins.aps.openAPSAMA; - -import org.json.JSONException; -import org.json.JSONObject; -import org.mozilla.javascript.NativeObject; - -import dagger.android.HasAndroidInjector; -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.aps.loop.APSResult; -import info.nightscout.androidaps.utils.DateUtil; - -public class DetermineBasalResultAMA extends APSResult { - private AAPSLogger aapsLogger; - - private double eventualBG; - private double snoozeBG; - - DetermineBasalResultAMA(HasAndroidInjector injector, NativeObject result, JSONObject j) { - this(injector); - date = DateUtil.now(); - json = j; - if (result.containsKey("error")) { - reason = result.get("error").toString(); - tempBasalRequested = false; - rate = -1; - duration = -1; - } else { - reason = result.get("reason").toString(); - if (result.containsKey("eventualBG")) eventualBG = (Double) result.get("eventualBG"); - if (result.containsKey("snoozeBG")) snoozeBG = (Double) result.get("snoozeBG"); - if (result.containsKey("rate")) { - rate = (Double) result.get("rate"); - if (rate < 0d) rate = 0d; - tempBasalRequested = true; - } else { - rate = -1; - tempBasalRequested = false; - } - if (result.containsKey("duration")) { - duration = ((Double) result.get("duration")).intValue(); - //changeRequested as above - } else { - duration = -1; - tempBasalRequested = false; - } - } - bolusRequested = false; - } - - private DetermineBasalResultAMA(HasAndroidInjector injector) { - super(injector); - hasPredictions = true; - } - - @Override - public DetermineBasalResultAMA newAndClone(HasAndroidInjector injector) { - DetermineBasalResultAMA newResult = new DetermineBasalResultAMA(injector); - doClone(newResult); - - newResult.eventualBG = eventualBG; - newResult.snoozeBG = snoozeBG; - return newResult; - } - - @Override - public JSONObject json() { - try { - JSONObject ret = new JSONObject(this.json.toString()); - return ret; - } catch (JSONException e) { - aapsLogger.error(LTag.APS, "Unhandled exception", e); - } - return null; - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalResultAMA.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalResultAMA.kt new file mode 100644 index 0000000000..8f462b2f19 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/DetermineBasalResultAMA.kt @@ -0,0 +1,67 @@ +package info.nightscout.androidaps.plugins.aps.openAPSAMA + +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.aps.loop.APSResult +import info.nightscout.androidaps.utils.DateUtil +import org.json.JSONException +import org.json.JSONObject +import org.mozilla.javascript.NativeObject + +class DetermineBasalResultAMA private constructor(injector: HasAndroidInjector) : APSResult(injector) { + + private var eventualBG = 0.0 + private var snoozeBG = 0.0 + + internal constructor(injector: HasAndroidInjector, result: NativeObject, j: JSONObject) : this(injector) { + date = DateUtil.now() + json = j + if (result.containsKey("error")) { + reason = result["error"].toString() + tempBasalRequested = false + rate = (-1).toDouble() + duration = -1 + } else { + reason = result["reason"].toString() + if (result.containsKey("eventualBG")) eventualBG = result["eventualBG"] as Double + if (result.containsKey("snoozeBG")) snoozeBG = result["snoozeBG"] as Double + if (result.containsKey("rate")) { + rate = result["rate"] as Double + if (rate < 0.0) rate = 0.0 + tempBasalRequested = true + } else { + rate = (-1).toDouble() + tempBasalRequested = false + } + if (result.containsKey("duration")) { + duration = (result["duration"] as Double).toInt() + //changeRequested as above + } else { + duration = -1 + tempBasalRequested = false + } + } + bolusRequested = false + } + + override fun newAndClone(injector: HasAndroidInjector): DetermineBasalResultAMA { + val newResult = DetermineBasalResultAMA(injector) + doClone(newResult) + newResult.eventualBG = eventualBG + newResult.snoozeBG = snoozeBG + return newResult + } + + override fun json(): JSONObject? { + try { + return JSONObject(json.toString()) + } catch (e: JSONException) { + aapsLogger.error(LTag.APS, "Unhandled exception", e) + } + return null + } + + init { + hasPredictions = true + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAFragment.kt index fcd78a9adc..84bc71daf7 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAFragment.kt @@ -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.databinding.OpenapsamaFragmentBinding import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui @@ -15,34 +16,42 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.JSONFormatter -import info.nightscout.androidaps.utils.extensions.plusAssign import info.nightscout.androidaps.utils.resources.ResourceHelper -import io.reactivex.android.schedulers.AndroidSchedulers +import info.nightscout.androidaps.utils.rx.AapsSchedulers import io.reactivex.disposables.CompositeDisposable -import kotlinx.android.synthetic.main.openapsama_fragment.* +import io.reactivex.rxkotlin.plusAssign import org.json.JSONArray import org.json.JSONException import javax.inject.Inject class OpenAPSAMAFragment : DaggerFragment() { + private var disposable: CompositeDisposable = CompositeDisposable() @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var openAPSAMAPlugin: OpenAPSAMAPlugin @Inject lateinit var dateUtil: DateUtil + private var _binding: OpenapsamaFragmentBinding? = null + + // This property is only valid between onCreateView and + // onDestroyView. + private val binding get() = _binding!! + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.openapsama_fragment, container, false) + savedInstanceState: Bundle?): View { + _binding = OpenapsamaFragmentBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - openapsma_run.setOnClickListener { + binding.run.setOnClickListener { openAPSAMAPlugin.invoke("OpenAPSAMA button", false) } } @@ -53,16 +62,16 @@ class OpenAPSAMAFragment : DaggerFragment() { disposable += rxBus .toObservable(EventOpenAPSUpdateGui::class.java) - .observeOn(AndroidSchedulers.mainThread()) + .observeOn(aapsSchedulers.main) .subscribe({ updateGUI() - }, { fabricPrivacy.logException(it) }) + }, fabricPrivacy::logException) disposable += rxBus .toObservable(EventOpenAPSUpdateResultGui::class.java) - .observeOn(AndroidSchedulers.mainThread()) + .observeOn(aapsSchedulers.main) .subscribe({ updateResultGUI(it.text) - }, { fabricPrivacy.logException(it) }) + }, fabricPrivacy::logException) updateGUI() } @@ -73,47 +82,53 @@ class OpenAPSAMAFragment : DaggerFragment() { disposable.clear() } + @Synchronized + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + @Synchronized private fun updateGUI() { - if (openapsma_result == null) return + if (_binding == null) return openAPSAMAPlugin.lastAPSResult?.let { lastAPSResult -> - openapsma_result.text = JSONFormatter.format(lastAPSResult.json) - openapsma_request.text = lastAPSResult.toSpanned() + binding.result.text = JSONFormatter.format(lastAPSResult.json) + binding.request.text = lastAPSResult.toSpanned() } openAPSAMAPlugin.lastDetermineBasalAdapterAMAJS?.let { determineBasalAdapterAMAJS -> - openapsma_glucosestatus.text = JSONFormatter.format(determineBasalAdapterAMAJS.glucoseStatusParam) - openapsma_currenttemp.text = JSONFormatter.format(determineBasalAdapterAMAJS.currentTempParam) + binding.glucosestatus.text = JSONFormatter.format(determineBasalAdapterAMAJS.glucoseStatusParam) + binding.currenttemp.text = JSONFormatter.format(determineBasalAdapterAMAJS.currentTempParam) try { val iobArray = JSONArray(determineBasalAdapterAMAJS.iobDataParam) - openapsma_iobdata.text = TextUtils.concat(resourceHelper.gs(R.string.array_of_elements, iobArray.length()) + "\n", JSONFormatter.format(iobArray.getString(0))) + binding.iobdata.text = TextUtils.concat(resourceHelper.gs(R.string.array_of_elements, iobArray.length()) + "\n", JSONFormatter.format(iobArray.getString(0))) } catch (e: JSONException) { aapsLogger.error(LTag.APS, "Unhandled exception", e) @Suppress("SetTextI18n") - openapsma_iobdata.text = "JSONException see log for details" + binding.iobdata.text = "JSONException see log for details" } - openapsma_profile.text = JSONFormatter.format(determineBasalAdapterAMAJS.profileParam) - openapsma_mealdata.text = JSONFormatter.format(determineBasalAdapterAMAJS.mealDataParam) - openapsma_scriptdebugdata.text = determineBasalAdapterAMAJS.scriptDebug + binding.profile.text = JSONFormatter.format(determineBasalAdapterAMAJS.profileParam) + binding.mealdata.text = JSONFormatter.format(determineBasalAdapterAMAJS.mealDataParam) + binding.scriptdebugdata.text = determineBasalAdapterAMAJS.scriptDebug } if (openAPSAMAPlugin.lastAPSRun != 0L) { - openapsma_lastrun.text = dateUtil.dateAndTimeString(openAPSAMAPlugin.lastAPSRun) + binding.lastrun.text = dateUtil.dateAndTimeString(openAPSAMAPlugin.lastAPSRun) } - openAPSAMAPlugin.lastAutosensResult?.let { - openapsma_autosensdata.text = JSONFormatter.format(it.json()) + openAPSAMAPlugin.lastAutosensResult.let { + binding.autosensdata.text = JSONFormatter.format(it.json()) } } private fun updateResultGUI(text: String) { - openapsma_result.text = text - openapsma_glucosestatus.text = "" - openapsma_currenttemp.text = "" - openapsma_iobdata.text = "" - openapsma_profile.text = "" - openapsma_mealdata.text = "" - openapsma_autosensdata.text = "" - openapsma_scriptdebugdata.text = "" - openapsma_request.text = "" - openapsma_lastrun.text = "" + binding.result.text = text + binding.glucosestatus.text = "" + binding.currenttemp.text = "" + binding.iobdata.text = "" + binding.profile.text = "" + binding.mealdata.text = "" + binding.autosensdata.text = "" + binding.scriptdebugdata.text = "" + binding.request.text = "" + binding.lastrun.text = "" } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.java deleted file mode 100644 index 7d132db4d3..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.java +++ /dev/null @@ -1,263 +0,0 @@ -package info.nightscout.androidaps.plugins.aps.openAPSAMA; - -import android.content.Context; - -import org.json.JSONException; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import dagger.android.HasAndroidInjector; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.IobTotal; -import info.nightscout.androidaps.data.MealData; -import info.nightscout.androidaps.data.Profile; -import info.nightscout.androidaps.db.TempTarget; -import info.nightscout.androidaps.interfaces.APSInterface; -import info.nightscout.androidaps.interfaces.ActivePluginProvider; -import info.nightscout.androidaps.interfaces.PluginBase; -import info.nightscout.androidaps.interfaces.PluginDescription; -import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.interfaces.PumpInterface; -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.aps.loop.APSResult; -import info.nightscout.androidaps.plugins.aps.loop.ScriptReader; -import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui; -import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateResultGui; -import info.nightscout.androidaps.plugins.bus.RxBusWrapper; -import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker; -import info.nightscout.androidaps.interfaces.ProfileFunction; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.FabricPrivacy; -import info.nightscout.androidaps.utils.HardLimits; -import info.nightscout.androidaps.utils.Profiler; -import info.nightscout.androidaps.utils.Round; -import info.nightscout.androidaps.utils.resources.ResourceHelper; - -@Singleton -public class OpenAPSAMAPlugin extends PluginBase implements APSInterface { - private final AAPSLogger aapsLogger; - private final RxBusWrapper rxBus; - private final ConstraintChecker constraintChecker; - private final ResourceHelper resourceHelper; - private final ProfileFunction profileFunction; - private final Context context; - private final ActivePluginProvider activePlugin; - private final TreatmentsPlugin treatmentsPlugin; - private final IobCobCalculatorPlugin iobCobCalculatorPlugin; - private final HardLimits hardLimits; - private final Profiler profiler; - private final FabricPrivacy fabricPrivacy; - - // last values - DetermineBasalAdapterAMAJS lastDetermineBasalAdapterAMAJS = null; - long lastAPSRun = 0; - DetermineBasalResultAMA lastAPSResult = null; - AutosensResult lastAutosensResult = null; - - @Inject - public OpenAPSAMAPlugin( - HasAndroidInjector injector, - AAPSLogger aapsLogger, - RxBusWrapper rxBus, - ConstraintChecker constraintChecker, - ResourceHelper resourceHelper, - ProfileFunction profileFunction, - Context context, - ActivePluginProvider activePlugin, - TreatmentsPlugin treatmentsPlugin, - IobCobCalculatorPlugin iobCobCalculatorPlugin, - HardLimits hardLimits, - Profiler profiler, - FabricPrivacy fabricPrivacy - ) { - super(new PluginDescription() - .mainType(PluginType.APS) - .fragmentClass(OpenAPSAMAFragment.class.getName()) - .pluginIcon(R.drawable.ic_generic_icon) - .pluginName(R.string.openapsama) - .shortName(R.string.oaps_shortname) - .preferencesId(R.xml.pref_openapsama) - .description(R.string.description_ama), - aapsLogger, resourceHelper, injector - ); - this.aapsLogger = aapsLogger; - this.rxBus = rxBus; - this.constraintChecker = constraintChecker; - this.resourceHelper = resourceHelper; - this.profileFunction = profileFunction; - this.context = context; - this.activePlugin = activePlugin; - this.treatmentsPlugin = treatmentsPlugin; - this.iobCobCalculatorPlugin = iobCobCalculatorPlugin; - this.hardLimits = hardLimits; - this.profiler = profiler; - this.fabricPrivacy = fabricPrivacy; - } - - @Override - public boolean specialEnableCondition() { - try { - PumpInterface pump = activePlugin.getActivePump(); - return pump.getPumpDescription().isTempBasalCapable; - } catch (Exception ignored) { - // may fail during initialization - return true; - } - } - - @Override - public boolean specialShowInListCondition() { - PumpInterface pump = activePlugin.getActivePump(); - return pump.getPumpDescription().isTempBasalCapable; - } - - @Override - public APSResult getLastAPSResult() { - return lastAPSResult; - } - - @Override - public long getLastAPSRun() { - return lastAPSRun; - } - - @Override - public void invoke(String initiator, boolean tempBasalFallback) { - aapsLogger.debug(LTag.APS, "invoke from " + initiator + " tempBasalFallback: " + tempBasalFallback); - lastAPSResult = null; - DetermineBasalAdapterAMAJS determineBasalAdapterAMAJS; - determineBasalAdapterAMAJS = new DetermineBasalAdapterAMAJS(new ScriptReader(context), getInjector()); - - GlucoseStatus glucoseStatus = new GlucoseStatus(getInjector()).getGlucoseStatusData(); - Profile profile = profileFunction.getProfile(); - PumpInterface pump = activePlugin.getActivePump(); - - if (profile == null) { - rxBus.send(new EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.noprofileselected))); - aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.noprofileselected)); - return; - } - - if (!isEnabled(PluginType.APS)) { - rxBus.send(new EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openapsma_disabled))); - aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.openapsma_disabled)); - return; - } - - if (glucoseStatus == null) { - rxBus.send(new EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openapsma_noglucosedata))); - aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.openapsma_noglucosedata)); - return; - } - - double maxBasal = constraintChecker.getMaxBasalAllowed(profile).value(); - double minBg = profile.getTargetLowMgdl(); - double maxBg = profile.getTargetHighMgdl(); - double targetBg = profile.getTargetMgdl(); - - minBg = Round.roundTo(minBg, 0.1d); - maxBg = Round.roundTo(maxBg, 0.1d); - - long start = System.currentTimeMillis(); - long startPart = System.currentTimeMillis(); - IobTotal[] iobArray = iobCobCalculatorPlugin.calculateIobArrayInDia(profile); - profiler.log(LTag.APS, "calculateIobArrayInDia()", startPart); - - startPart = System.currentTimeMillis(); - MealData mealData = iobCobCalculatorPlugin.getMealData(); - profiler.log(LTag.APS, "getMealData()", startPart); - - double maxIob = constraintChecker.getMaxIOBAllowed().value(); - - minBg = hardLimits.verifyHardLimits(minBg, "minBg", hardLimits.getVERY_HARD_LIMIT_MIN_BG()[0], hardLimits.getVERY_HARD_LIMIT_MIN_BG()[1]); - maxBg = hardLimits.verifyHardLimits(maxBg, "maxBg", hardLimits.getVERY_HARD_LIMIT_MAX_BG()[0], hardLimits.getVERY_HARD_LIMIT_MAX_BG()[1]); - targetBg = hardLimits.verifyHardLimits(targetBg, "targetBg", hardLimits.getVERY_HARD_LIMIT_TARGET_BG()[0], hardLimits.getVERY_HARD_LIMIT_TARGET_BG()[1]); - - boolean isTempTarget = false; - TempTarget tempTarget = treatmentsPlugin.getTempTargetFromHistory(System.currentTimeMillis()); - if (tempTarget != null) { - isTempTarget = true; - minBg = hardLimits.verifyHardLimits(tempTarget.low, "minBg", hardLimits.getVERY_HARD_LIMIT_TEMP_MIN_BG()[0], hardLimits.getVERY_HARD_LIMIT_TEMP_MIN_BG()[1]); - maxBg = hardLimits.verifyHardLimits(tempTarget.high, "maxBg", hardLimits.getVERY_HARD_LIMIT_TEMP_MAX_BG()[0], hardLimits.getVERY_HARD_LIMIT_TEMP_MAX_BG()[1]); - targetBg = hardLimits.verifyHardLimits(tempTarget.target(), "targetBg", hardLimits.getVERY_HARD_LIMIT_TEMP_TARGET_BG()[0], hardLimits.getVERY_HARD_LIMIT_TEMP_TARGET_BG()[1]); - } - - - if (!hardLimits.checkOnlyHardLimits(profile.getDia(), "dia", hardLimits.minDia(), hardLimits.maxDia())) - return; - if (!hardLimits.checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), "carbratio", hardLimits.minIC(), hardLimits.maxIC())) - return; - if (!hardLimits.checkOnlyHardLimits(profile.getIsfMgdl(), "sens", hardLimits.getMINISF(), hardLimits.getMAXISF())) - return; - if (!hardLimits.checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.02, hardLimits.maxBasal())) - return; - if (!hardLimits.checkOnlyHardLimits(pump.getBaseBasalRate(), "current_basal", 0.01, hardLimits.maxBasal())) - return; - - startPart = System.currentTimeMillis(); - if (constraintChecker.isAutosensModeEnabled().value()) { - AutosensData autosensData = iobCobCalculatorPlugin.getLastAutosensDataSynchronized("OpenAPSPlugin"); - if (autosensData == null) { - rxBus.send(new EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openaps_noasdata))); - return; - } - lastAutosensResult = autosensData.autosensResult; - } else { - lastAutosensResult = new AutosensResult(); - lastAutosensResult.sensResult = "autosens disabled"; - } - profiler.log(LTag.APS, "detectSensitivityandCarbAbsorption()", startPart); - profiler.log(LTag.APS, "AMA data gathering", start); - - start = System.currentTimeMillis(); - - try { - determineBasalAdapterAMAJS.setData(profile, maxIob, maxBasal, minBg, maxBg, targetBg, activePlugin.getActivePump().getBaseBasalRate(), iobArray, glucoseStatus, mealData, - lastAutosensResult.ratio, //autosensDataRatio - isTempTarget - ); - } catch (JSONException e) { - fabricPrivacy.logException(e); - return; - } - - - DetermineBasalResultAMA determineBasalResultAMA = determineBasalAdapterAMAJS.invoke(); - profiler.log(LTag.APS, "AMA calculation", start); - // Fix bug determine basal - if (determineBasalResultAMA == null) { - aapsLogger.error(LTag.APS, "SMB calculation returned null"); - lastDetermineBasalAdapterAMAJS = null; - lastAPSResult = null; - lastAPSRun = 0; - } else { - if (determineBasalResultAMA.rate == 0d && determineBasalResultAMA.duration == 0 && !treatmentsPlugin.isTempBasalInProgress()) - determineBasalResultAMA.tempBasalRequested = false; - - determineBasalResultAMA.iob = iobArray[0]; - - long now = System.currentTimeMillis(); - - try { - determineBasalResultAMA.json.put("timestamp", DateUtil.toISOString(now)); - } catch (JSONException e) { - aapsLogger.error(LTag.APS, "Unhandled exception", e); - } - - lastDetermineBasalAdapterAMAJS = determineBasalAdapterAMAJS; - lastAPSResult = determineBasalResultAMA; - lastAPSRun = now; - } - rxBus.send(new EventOpenAPSUpdateGui()); - - //deviceStatus.suggested = determineBasalResultAMA.json; - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.kt new file mode 100644 index 0000000000..97a6079e4a --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSAMA/OpenAPSAMAPlugin.kt @@ -0,0 +1,172 @@ +package info.nightscout.androidaps.plugins.aps.openAPSAMA + +import android.content.Context +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.R +import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.interfaces.* +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui +import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateResultGui +import info.nightscout.androidaps.plugins.aps.loop.ScriptReader +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.HardLimits +import info.nightscout.androidaps.utils.Profiler +import info.nightscout.androidaps.utils.Round +import info.nightscout.androidaps.utils.resources.ResourceHelper +import org.json.JSONException +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +open class OpenAPSAMAPlugin @Inject constructor( + injector: HasAndroidInjector, + aapsLogger: AAPSLogger, + private val rxBus: RxBusWrapper, + private val constraintChecker: ConstraintChecker, + resourceHelper: ResourceHelper, + private val profileFunction: ProfileFunction, + private val context: Context, + private val activePlugin: ActivePluginProvider, + private val treatmentsPlugin: TreatmentsPlugin, + private val iobCobCalculatorPlugin: IobCobCalculatorPlugin, + private val hardLimits: HardLimits, + private val profiler: Profiler, + private val fabricPrivacy: FabricPrivacy +) : PluginBase(PluginDescription() + .mainType(PluginType.APS) + .fragmentClass(OpenAPSAMAFragment::class.java.name) + .pluginIcon(R.drawable.ic_generic_icon) + .pluginName(R.string.openapsama) + .shortName(R.string.oaps_shortname) + .preferencesId(R.xml.pref_openapsama) + .description(R.string.description_ama), + aapsLogger, resourceHelper, injector +), APSInterface { + + // last values + override var lastAPSRun: Long = 0 + override var lastAPSResult: DetermineBasalResultAMA? = null + var lastDetermineBasalAdapterAMAJS: DetermineBasalAdapterAMAJS? = null + var lastAutosensResult: AutosensResult = AutosensResult() + + override fun specialEnableCondition(): Boolean { + return try { + val pump = activePlugin.activePump + pump.pumpDescription.isTempBasalCapable + } catch (ignored: Exception) { + // may fail during initialization + true + } + } + + override fun specialShowInListCondition(): Boolean { + val pump = activePlugin.activePump + return pump.pumpDescription.isTempBasalCapable + } + + override fun invoke(initiator: String, tempBasalFallback: Boolean) { + aapsLogger.debug(LTag.APS, "invoke from $initiator tempBasalFallback: $tempBasalFallback") + lastAPSResult = null + val determineBasalAdapterAMAJS = DetermineBasalAdapterAMAJS(ScriptReader(context), injector) + val glucoseStatus = GlucoseStatus(injector).glucoseStatusData + val profile = profileFunction.getProfile() + val pump = activePlugin.activePump + if (profile == null) { + rxBus.send(EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.noprofileselected))) + aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.noprofileselected)) + return + } + if (!isEnabled(PluginType.APS)) { + rxBus.send(EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openapsma_disabled))) + aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.openapsma_disabled)) + return + } + if (glucoseStatus == null) { + rxBus.send(EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openapsma_noglucosedata))) + aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.openapsma_noglucosedata)) + return + } + val inputConstraints = Constraint(0.0) // fake. only for collecting all results + val maxBasal = constraintChecker.getMaxBasalAllowed(profile).also { + inputConstraints.copyReasons(it) + }.value() + var start = System.currentTimeMillis() + var startPart = System.currentTimeMillis() + val iobArray = iobCobCalculatorPlugin.calculateIobArrayInDia(profile) + profiler.log(LTag.APS, "calculateIobArrayInDia()", startPart) + startPart = System.currentTimeMillis() + val mealData = iobCobCalculatorPlugin.mealData + profiler.log(LTag.APS, "getMealData()", startPart) + val maxIob = constraintChecker.getMaxIOBAllowed().also { maxIOBAllowedConstraint -> + inputConstraints.copyReasons(maxIOBAllowedConstraint) + }.value() + var minBg = hardLimits.verifyHardLimits(Round.roundTo(profile.targetLowMgdl, 0.1), "minBg", hardLimits.VERY_HARD_LIMIT_MIN_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_MIN_BG[1].toDouble()) + var maxBg = hardLimits.verifyHardLimits(Round.roundTo(profile.targetHighMgdl, 0.1), "maxBg", hardLimits.VERY_HARD_LIMIT_MAX_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_MAX_BG[1].toDouble()) + var targetBg = hardLimits.verifyHardLimits(profile.targetMgdl, "targetBg", hardLimits.VERY_HARD_LIMIT_TARGET_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TARGET_BG[1].toDouble()) + var isTempTarget = false + treatmentsPlugin.getTempTargetFromHistory(System.currentTimeMillis())?.let { tempTarget -> + isTempTarget = true + minBg = hardLimits.verifyHardLimits(tempTarget.low, "minBg", hardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[1].toDouble()) + maxBg = hardLimits.verifyHardLimits(tempTarget.high, "maxBg", hardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[1].toDouble()) + targetBg = hardLimits.verifyHardLimits(tempTarget.target(), "targetBg", hardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[1].toDouble()) + } + if (!hardLimits.checkOnlyHardLimits(profile.dia, "dia", hardLimits.minDia(), hardLimits.maxDia())) return + if (!hardLimits.checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), "carbratio", hardLimits.minIC(), hardLimits.maxIC())) return + if (!hardLimits.checkOnlyHardLimits(profile.isfMgdl, "sens", hardLimits.MINISF, hardLimits.MAXISF)) return + if (!hardLimits.checkOnlyHardLimits(profile.maxDailyBasal, "max_daily_basal", 0.02, hardLimits.maxBasal())) return + if (!hardLimits.checkOnlyHardLimits(pump.baseBasalRate, "current_basal", 0.01, hardLimits.maxBasal())) return + startPart = System.currentTimeMillis() + if (constraintChecker.isAutosensModeEnabled().value()) { + val autosensData = iobCobCalculatorPlugin.getLastAutosensDataSynchronized("OpenAPSPlugin") + if (autosensData == null) { + rxBus.send(EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openaps_noasdata))) + return + } + lastAutosensResult = autosensData.autosensResult + } else { + lastAutosensResult.sensResult = "autosens disabled" + } + profiler.log(LTag.APS, "detectSensitivityAndCarbAbsorption()", startPart) + profiler.log(LTag.APS, "AMA data gathering", start) + start = System.currentTimeMillis() + try { + determineBasalAdapterAMAJS.setData(profile, maxIob, maxBasal, minBg, maxBg, targetBg, activePlugin.activePump.baseBasalRate, iobArray, glucoseStatus, mealData, + lastAutosensResult.ratio, + isTempTarget + ) + } catch (e: JSONException) { + fabricPrivacy.logException(e) + return + } + val determineBasalResultAMA = determineBasalAdapterAMAJS.invoke() + profiler.log(LTag.APS, "AMA calculation", start) + // Fix bug determine basal + if (determineBasalResultAMA == null) { + aapsLogger.error(LTag.APS, "SMB calculation returned null") + lastDetermineBasalAdapterAMAJS = null + lastAPSResult = null + lastAPSRun = 0 + } else { + if (determineBasalResultAMA.rate == 0.0 && determineBasalResultAMA.duration == 0 && !treatmentsPlugin.isTempBasalInProgress) determineBasalResultAMA.tempBasalRequested = false + determineBasalResultAMA.iob = iobArray[0] + val now = System.currentTimeMillis() + determineBasalResultAMA.json?.put("timestamp", DateUtil.toISOString(now)) + determineBasalResultAMA.inputConstraints = inputConstraints + lastDetermineBasalAdapterAMAJS = determineBasalAdapterAMAJS + lastAPSResult = determineBasalResultAMA + lastAPSRun = now + } + rxBus.send(EventOpenAPSUpdateGui()) + + //deviceStatus.suggested = determineBasalResultAMA.json; + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java deleted file mode 100644 index a94581ca94..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.java +++ /dev/null @@ -1,386 +0,0 @@ -package info.nightscout.androidaps.plugins.aps.openAPSSMB; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.mozilla.javascript.Context; -import org.mozilla.javascript.Function; -import org.mozilla.javascript.NativeJSON; -import org.mozilla.javascript.NativeObject; -import org.mozilla.javascript.RhinoException; -import org.mozilla.javascript.Scriptable; -import org.mozilla.javascript.ScriptableObject; -import org.mozilla.javascript.Undefined; - -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.nio.charset.StandardCharsets; - -import javax.annotation.Nullable; -import javax.inject.Inject; - -import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader; -import dagger.android.HasAndroidInjector; -import info.nightscout.androidaps.Constants; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.IobTotal; -import info.nightscout.androidaps.data.MealData; -import info.nightscout.androidaps.data.Profile; -import info.nightscout.androidaps.db.TemporaryBasal; -import info.nightscout.androidaps.interfaces.ActivePluginProvider; -import info.nightscout.androidaps.interfaces.PumpInterface; -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.aps.logger.LoggerCallback; -import info.nightscout.androidaps.plugins.aps.loop.ScriptReader; -import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker; -import info.nightscout.androidaps.interfaces.ProfileFunction; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; -import info.nightscout.androidaps.utils.SafeParse; -import info.nightscout.androidaps.utils.resources.ResourceHelper; -import info.nightscout.androidaps.utils.sharedPreferences.SP; - - -public class DetermineBasalAdapterSMBJS { - private final HasAndroidInjector injector; - @Inject AAPSLogger aapsLogger; - @Inject ConstraintChecker constraintChecker; - @Inject SP sp; - @Inject ResourceHelper resourceHelper; - @Inject ProfileFunction profileFunction; - @Inject TreatmentsPlugin treatmentsPlugin; - @Inject ActivePluginProvider activePluginProvider; - @Inject OpenHumansUploader openHumansUploader; - - - private final ScriptReader mScriptReader; - private JSONObject mProfile; - private JSONObject mGlucoseStatus; - private JSONArray mIobData; - private JSONObject mMealData; - private JSONObject mCurrentTemp; - private JSONObject mAutosensData = null; - private boolean mMicrobolusAllowed; - private boolean mSMBAlwaysAllowed; - private long mCurrentTime; - private boolean mIsSaveCgmSource; - - private String storedCurrentTemp = null; - private String storedIobData = null; - - private String storedGlucoseStatus = null; - private String storedProfile = null; - private String storedMeal_data = null; - private String storedAutosens_data = null; - private String storedMicroBolusAllowed = null; - private String storedSMBAlwaysAllowed = null; - private String storedCurrentTime = null; - - private String scriptDebug = ""; - - /** - * Main code - */ - - DetermineBasalAdapterSMBJS(ScriptReader scriptReader, HasAndroidInjector injector) { - mScriptReader = scriptReader; - this.injector = injector; - injector.androidInjector().inject(this); - } - - - @Nullable - public DetermineBasalResultSMB invoke() { - - - aapsLogger.debug(LTag.APS, ">>> Invoking detemine_basal <<<"); - aapsLogger.debug(LTag.APS, "Glucose status: " + (storedGlucoseStatus = mGlucoseStatus.toString())); - aapsLogger.debug(LTag.APS, "IOB data: " + (storedIobData = mIobData.toString())); - aapsLogger.debug(LTag.APS, "Current temp: " + (storedCurrentTemp = mCurrentTemp.toString())); - aapsLogger.debug(LTag.APS, "Profile: " + (storedProfile = mProfile.toString())); - aapsLogger.debug(LTag.APS, "Meal data: " + (storedMeal_data = mMealData.toString())); - if (mAutosensData != null) - aapsLogger.debug(LTag.APS, "Autosens data: " + (storedAutosens_data = mAutosensData.toString())); - else - aapsLogger.debug(LTag.APS, "Autosens data: " + (storedAutosens_data = "undefined")); - aapsLogger.debug(LTag.APS, "Reservoir data: " + "undefined"); - aapsLogger.debug(LTag.APS, "MicroBolusAllowed: " + (storedMicroBolusAllowed = "" + mMicrobolusAllowed)); - aapsLogger.debug(LTag.APS, "SMBAlwaysAllowed: " + (storedSMBAlwaysAllowed = "" + mSMBAlwaysAllowed)); - aapsLogger.debug(LTag.APS, "CurrentTime: " + (storedCurrentTime = "" + mCurrentTime)); - aapsLogger.debug(LTag.APS, "isSaveCgmSource: " + mIsSaveCgmSource); - - - DetermineBasalResultSMB determineBasalResultSMB = null; - - Context rhino = Context.enter(); - Scriptable scope = rhino.initStandardObjects(); - // Turn off optimization to make Rhino Android compatible - rhino.setOptimizationLevel(-1); - - try { - - //register logger callback for console.log and console.error - ScriptableObject.defineClass(scope, LoggerCallback.class); - Scriptable myLogger = rhino.newObject(scope, "LoggerCallback", null); - scope.put("console2", scope, myLogger); - rhino.evaluateString(scope, readFile("OpenAPSAMA/loggerhelper.js"), "JavaScript", 0, null); - - //set module parent - rhino.evaluateString(scope, "var module = {\"parent\":Boolean(1)};", "JavaScript", 0, null); - rhino.evaluateString(scope, "var round_basal = function round_basal(basal, profile) { return basal; };", "JavaScript", 0, null); - rhino.evaluateString(scope, "require = function() {return round_basal;};", "JavaScript", 0, null); - - //generate functions "determine_basal" and "setTempBasal" - rhino.evaluateString(scope, readFile("OpenAPSSMB/determine-basal.js"), "JavaScript", 0, null); - rhino.evaluateString(scope, readFile("OpenAPSSMB/basal-set-temp.js"), "setTempBasal.js", 0, null); - Object determineBasalObj = scope.get("determine_basal", scope); - Object setTempBasalFunctionsObj = scope.get("tempBasalFunctions", scope); - - //call determine-basal - if (determineBasalObj instanceof Function && setTempBasalFunctionsObj instanceof NativeObject) { - Function determineBasalJS = (Function) determineBasalObj; - - //prepare parameters - Object[] params = new Object[]{ - makeParam(mGlucoseStatus, rhino, scope), - makeParam(mCurrentTemp, rhino, scope), - makeParamArray(mIobData, rhino, scope), - makeParam(mProfile, rhino, scope), - makeParam(mAutosensData, rhino, scope), - makeParam(mMealData, rhino, scope), - setTempBasalFunctionsObj, - new Boolean(mMicrobolusAllowed), - makeParam(null, rhino, scope), // reservoir data as undefined - new Long(mCurrentTime) - }; - - - NativeObject jsResult = (NativeObject) determineBasalJS.call(rhino, scope, scope, params); - scriptDebug = LoggerCallback.getScriptDebug(); - - // Parse the jsResult object to a JSON-String - String result = NativeJSON.stringify(rhino, scope, jsResult, null, null).toString(); - aapsLogger.debug(LTag.APS, "Result: " + result); - try { - JSONObject resultJson = new JSONObject(result); - openHumansUploader.enqueueSMBData(mProfile, mGlucoseStatus, mIobData, mMealData, mCurrentTemp, mAutosensData, mMicrobolusAllowed, mSMBAlwaysAllowed, resultJson); - determineBasalResultSMB = new DetermineBasalResultSMB(injector, resultJson); - } catch (JSONException e) { - aapsLogger.error(LTag.APS, "Unhandled exception", e); - } - } else { - aapsLogger.error(LTag.APS, "Problem loading JS Functions"); - } - } catch (IOException e) { - aapsLogger.error(LTag.APS, "IOException"); - } catch (RhinoException e) { - aapsLogger.error(LTag.APS, "RhinoException: (" + e.lineNumber() + "," + e.columnNumber() + ") " + e.toString()); - } catch (IllegalAccessException | InstantiationException | InvocationTargetException e) { - aapsLogger.error(LTag.APS, e.toString()); - } finally { - Context.exit(); - } - - storedGlucoseStatus = mGlucoseStatus.toString(); - storedIobData = mIobData.toString(); - storedCurrentTemp = mCurrentTemp.toString(); - storedProfile = mProfile.toString(); - storedMeal_data = mMealData.toString(); - - return determineBasalResultSMB; - - } - - String getGlucoseStatusParam() { - return storedGlucoseStatus; - } - - String getCurrentTempParam() { - return storedCurrentTemp; - } - - String getIobDataParam() { - return storedIobData; - } - - String getProfileParam() { - return storedProfile; - } - - String getMealDataParam() { - return storedMeal_data; - } - - String getAutosensDataParam() { - return storedAutosens_data; - } - - String getMicroBolusAllowedParam() { - return storedMicroBolusAllowed; - } - - String getScriptDebug() { - return scriptDebug; - } - - public void setData(Profile profile, - double maxIob, - double maxBasal, - double minBg, - double maxBg, - double targetBg, - double basalrate, - IobTotal[] iobArray, - GlucoseStatus glucoseStatus, - MealData mealData, - double autosensDataRatio, - boolean tempTargetSet, - boolean microBolusAllowed, - boolean uamAllowed, - boolean advancedFiltering, - boolean isSaveCgmSource - ) throws JSONException { - - String units = profile.getUnits(); - PumpInterface pump = activePluginProvider.getActivePump(); - Double pumpbolusstep = pump.getPumpDescription().bolusStep; - mProfile = new JSONObject(); - - mProfile.put("max_iob", maxIob); - //mProfile.put("dia", profile.getDia()); - mProfile.put("type", "current"); - mProfile.put("max_daily_basal", profile.getMaxDailyBasal()); - mProfile.put("max_basal", maxBasal); - mProfile.put("min_bg", minBg); - mProfile.put("max_bg", maxBg); - mProfile.put("target_bg", targetBg); - mProfile.put("carb_ratio", profile.getIc()); - mProfile.put("sens", profile.getIsfMgdl()); - mProfile.put("max_daily_safety_multiplier", sp.getInt(R.string.key_openapsama_max_daily_safety_multiplier, 3)); - mProfile.put("current_basal_safety_multiplier", sp.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4d)); - - //mProfile.put("high_temptarget_raises_sensitivity", SP.getBoolean(R.string.key_high_temptarget_raises_sensitivity, SMBDefaults.high_temptarget_raises_sensitivity)); - mProfile.put("high_temptarget_raises_sensitivity", false); - //mProfile.put("low_temptarget_lowers_sensitivity", SP.getBoolean(R.string.key_low_temptarget_lowers_sensitivity, SMBDefaults.low_temptarget_lowers_sensitivity)); - mProfile.put("low_temptarget_lowers_sensitivity", false); - - - mProfile.put("sensitivity_raises_target", sp.getBoolean(R.string.key_sensitivity_raises_target,SMBDefaults.sensitivity_raises_target)); - mProfile.put("resistance_lowers_target", sp.getBoolean(R.string.key_resistance_lowers_target,SMBDefaults.resistance_lowers_target)); - mProfile.put("adv_target_adjustments", SMBDefaults.adv_target_adjustments); - mProfile.put("exercise_mode", SMBDefaults.exercise_mode); - mProfile.put("half_basal_exercise_target", SMBDefaults.half_basal_exercise_target); - mProfile.put("maxCOB", SMBDefaults.maxCOB); - mProfile.put("skip_neutral_temps", pump.setNeutralTempAtFullHour()); - // min_5m_carbimpact is not used within SMB determinebasal - //if (mealData.usedMinCarbsImpact > 0) { - // mProfile.put("min_5m_carbimpact", mealData.usedMinCarbsImpact); - //} else { - // mProfile.put("min_5m_carbimpact", SP.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact)); - //} - mProfile.put("remainingCarbsCap", SMBDefaults.remainingCarbsCap); - mProfile.put("enableUAM", uamAllowed); - mProfile.put("A52_risk_enable", SMBDefaults.A52_risk_enable); - - boolean smbEnabled = sp.getBoolean(R.string.key_use_smb, false); - mProfile.put("SMBInterval", sp.getInt(R.string.key_smbinterval, SMBDefaults.SMBInterval)); - mProfile.put("enableSMB_with_COB", smbEnabled && sp.getBoolean(R.string.key_enableSMB_with_COB, false)); - mProfile.put("enableSMB_with_temptarget", smbEnabled && sp.getBoolean(R.string.key_enableSMB_with_temptarget, false)); - mProfile.put("allowSMB_with_high_temptarget", smbEnabled && sp.getBoolean(R.string.key_allowSMB_with_high_temptarget, false)); - mProfile.put("enableSMB_always", smbEnabled && sp.getBoolean(R.string.key_enableSMB_always, false) && advancedFiltering); - mProfile.put("enableSMB_after_carbs", smbEnabled && sp.getBoolean(R.string.key_enableSMB_after_carbs, false) && advancedFiltering); - mProfile.put("maxSMBBasalMinutes", sp.getInt(R.string.key_smbmaxminutes, SMBDefaults.maxSMBBasalMinutes)); - mProfile.put("maxUAMSMBBasalMinutes", sp.getInt(R.string.key_uamsmbmaxminutes, SMBDefaults.maxUAMSMBBasalMinutes)); - //set the min SMB amount to be the amount set by the pump. - mProfile.put("bolus_increment", pumpbolusstep); - mProfile.put("carbsReqThreshold", sp.getInt(R.string.key_carbsReqThreshold, SMBDefaults.carbsReqThreshold)); - - mProfile.put("current_basal", basalrate); - mProfile.put("temptargetSet", tempTargetSet); - mProfile.put("autosens_max", SafeParse.stringToDouble(sp.getString(R.string.key_openapsama_autosens_max, "1.2"))); - - if (profileFunction.getUnits().equals(Constants.MMOL)) { - mProfile.put("out_units", "mmol/L"); - } - - - long now = System.currentTimeMillis(); - TemporaryBasal tb = treatmentsPlugin.getTempBasalFromHistory(now); - - mCurrentTemp = new JSONObject(); - mCurrentTemp.put("temp", "absolute"); - mCurrentTemp.put("duration", tb != null ? tb.getPlannedRemainingMinutes() : 0); - mCurrentTemp.put("rate", tb != null ? tb.tempBasalConvertedToAbsolute(now, profile) : 0d); - - // as we have non default temps longer than 30 mintues - TemporaryBasal tempBasal = treatmentsPlugin.getTempBasalFromHistory(System.currentTimeMillis()); - if (tempBasal != null) { - mCurrentTemp.put("minutesrunning", tempBasal.getRealDuration()); - } - - mIobData = IobCobCalculatorPlugin.convertToJSONArray(iobArray); - - mGlucoseStatus = new JSONObject(); - mGlucoseStatus.put("glucose", glucoseStatus.glucose); - mGlucoseStatus.put("noise", glucoseStatus.noise); - - if (sp.getBoolean(R.string.key_always_use_shortavg, false)) { - mGlucoseStatus.put("delta", glucoseStatus.short_avgdelta); - } else { - mGlucoseStatus.put("delta", glucoseStatus.delta); - } - mGlucoseStatus.put("short_avgdelta", glucoseStatus.short_avgdelta); - mGlucoseStatus.put("long_avgdelta", glucoseStatus.long_avgdelta); - mGlucoseStatus.put("date", glucoseStatus.date); - - mMealData = new JSONObject(); - mMealData.put("carbs", mealData.carbs); - mMealData.put("boluses", mealData.boluses); - mMealData.put("mealCOB", mealData.mealCOB); - mMealData.put("slopeFromMaxDeviation", mealData.slopeFromMaxDeviation); - mMealData.put("slopeFromMinDeviation", mealData.slopeFromMinDeviation); - mMealData.put("lastBolusTime", mealData.lastBolusTime); - mMealData.put("lastCarbTime", mealData.lastCarbTime); - - - if (constraintChecker.isAutosensModeEnabled().value()) { - mAutosensData = new JSONObject(); - mAutosensData.put("ratio", autosensDataRatio); - } else { - mAutosensData = new JSONObject(); - mAutosensData.put("ratio", 1.0); - } - mMicrobolusAllowed = microBolusAllowed; - mSMBAlwaysAllowed = advancedFiltering; - - mCurrentTime = now; - - mIsSaveCgmSource = isSaveCgmSource; - } - - private Object makeParam(JSONObject jsonObject, Context rhino, Scriptable scope) { - - if (jsonObject == null) return Undefined.instance; - - Object param = NativeJSON.parse(rhino, scope, jsonObject.toString(), (context, scriptable, scriptable1, objects) -> objects[1]); - return param; - } - - private Object makeParamArray(JSONArray jsonArray, Context rhino, Scriptable scope) { - //Object param = NativeJSON.parse(rhino, scope, "{myarray: " + jsonArray.toString() + " }", new Callable() { - Object param = NativeJSON.parse(rhino, scope, jsonArray.toString(), (context, scriptable, scriptable1, objects) -> objects[1]); - return param; - } - - private String readFile(String filename) throws IOException { - byte[] bytes = mScriptReader.readFile(filename); - String string = new String(bytes, StandardCharsets.UTF_8); - if (string.startsWith("#!/usr/bin/env node")) { - string = string.substring(20); - } - return string; - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.kt new file mode 100644 index 0000000000..13a11fa774 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.kt @@ -0,0 +1,290 @@ +package info.nightscout.androidaps.plugins.aps.openAPSSMB + +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.Constants +import info.nightscout.androidaps.R +import info.nightscout.androidaps.data.IobTotal +import info.nightscout.androidaps.data.MealData +import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.interfaces.ActivePluginProvider +import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.aps.logger.LoggerCallback +import info.nightscout.androidaps.plugins.aps.loop.ScriptReader +import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker +import info.nightscout.androidaps.plugins.general.openhumans.OpenHumansUploader +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin +import info.nightscout.androidaps.utils.SafeParse +import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.androidaps.utils.sharedPreferences.SP +import org.json.JSONArray +import org.json.JSONException +import org.json.JSONObject +import org.mozilla.javascript.* +import org.mozilla.javascript.Function +import java.io.IOException +import java.lang.reflect.InvocationTargetException +import java.nio.charset.StandardCharsets +import javax.inject.Inject + +class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader: ScriptReader, private val injector: HasAndroidInjector) { + + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var constraintChecker: ConstraintChecker + @Inject lateinit var sp: SP + @Inject lateinit var resourceHelper: ResourceHelper + @Inject lateinit var profileFunction: ProfileFunction + @Inject lateinit var treatmentsPlugin: TreatmentsPlugin + @Inject lateinit var activePluginProvider: ActivePluginProvider + @Inject lateinit var openHumansUploader: OpenHumansUploader + + private var profile = JSONObject() + private var mGlucoseStatus = JSONObject() + private var iobData: JSONArray? = null + private var mealData = JSONObject() + private var currentTemp = JSONObject() + private var autosensData = JSONObject() + private var microBolusAllowed = false + private var smbAlwaysAllowed = false + private var currentTime: Long = 0 + private var saveCgmSource = false + var currentTempParam: String? = null + private set + var iobDataParam: String? = null + private set + var glucoseStatusParam: String? = null + private set + var profileParam: String? = null + private set + var mealDataParam: String? = null + private set + var scriptDebug = "" + private set + + @Suppress("SpellCheckingInspection") + operator fun invoke(): DetermineBasalResultSMB? { + aapsLogger.debug(LTag.APS, ">>> Invoking determine_basal <<<") + aapsLogger.debug(LTag.APS, "Glucose status: " + mGlucoseStatus.toString().also { glucoseStatusParam = it }) + aapsLogger.debug(LTag.APS, "IOB data: " + iobData.toString().also { iobDataParam = it }) + aapsLogger.debug(LTag.APS, "Current temp: " + currentTemp.toString().also { currentTempParam = it }) + aapsLogger.debug(LTag.APS, "Profile: " + profile.toString().also { profileParam = it }) + aapsLogger.debug(LTag.APS, "Meal data: " + mealData.toString().also { mealDataParam = it }) + aapsLogger.debug(LTag.APS, "Autosens data: $autosensData") + aapsLogger.debug(LTag.APS, "Reservoir data: " + "undefined") + aapsLogger.debug(LTag.APS, "MicroBolusAllowed: $microBolusAllowed") + aapsLogger.debug(LTag.APS, "SMBAlwaysAllowed: $smbAlwaysAllowed") + aapsLogger.debug(LTag.APS, "CurrentTime: $currentTime") + aapsLogger.debug(LTag.APS, "isSaveCgmSource: $saveCgmSource") + var determineBasalResultSMB: DetermineBasalResultSMB? = null + val rhino = Context.enter() + val scope: Scriptable = rhino.initStandardObjects() + // Turn off optimization to make Rhino Android compatible + rhino.optimizationLevel = -1 + try { + + //register logger callback for console.log and console.error + ScriptableObject.defineClass(scope, LoggerCallback::class.java) + val myLogger = rhino.newObject(scope, "LoggerCallback", null) + scope.put("console2", scope, myLogger) + rhino.evaluateString(scope, readFile("OpenAPSAMA/loggerhelper.js"), "JavaScript", 0, null) + + //set module parent + rhino.evaluateString(scope, "var module = {\"parent\":Boolean(1)};", "JavaScript", 0, null) + rhino.evaluateString(scope, "var round_basal = function round_basal(basal, profile) { return basal; };", "JavaScript", 0, null) + rhino.evaluateString(scope, "require = function() {return round_basal;};", "JavaScript", 0, null) + + //generate functions "determine_basal" and "setTempBasal" + rhino.evaluateString(scope, readFile("OpenAPSSMB/determine-basal.js"), "JavaScript", 0, null) + rhino.evaluateString(scope, readFile("OpenAPSSMB/basal-set-temp.js"), "setTempBasal.js", 0, null) + val determineBasalObj = scope["determine_basal", scope] + val setTempBasalFunctionsObj = scope["tempBasalFunctions", scope] + + //call determine-basal + if (determineBasalObj is Function && setTempBasalFunctionsObj is NativeObject) { + + //prepare parameters + val params = arrayOf( + makeParam(mGlucoseStatus, rhino, scope), + makeParam(currentTemp, rhino, scope), + makeParamArray(iobData, rhino, scope), + makeParam(profile, rhino, scope), + makeParam(autosensData, rhino, scope), + makeParam(mealData, rhino, scope), + setTempBasalFunctionsObj, + java.lang.Boolean.valueOf(microBolusAllowed), + makeParam(null, rhino, scope), // reservoir data as undefined + java.lang.Long.valueOf(currentTime), + java.lang.Boolean.valueOf(saveCgmSource) + ) + val jsResult = determineBasalObj.call(rhino, scope, scope, params) as NativeObject + scriptDebug = LoggerCallback.scriptDebug + + // Parse the jsResult object to a JSON-String + val result = NativeJSON.stringify(rhino, scope, jsResult, null, null).toString() + aapsLogger.debug(LTag.APS, "Result: $result") + try { + val resultJson = JSONObject(result) + openHumansUploader.enqueueSMBData(profile, mGlucoseStatus, iobData, mealData, currentTemp, autosensData, microBolusAllowed, smbAlwaysAllowed, resultJson) + determineBasalResultSMB = DetermineBasalResultSMB(injector, resultJson) + } catch (e: JSONException) { + aapsLogger.error(LTag.APS, "Unhandled exception", e) + } + } else { + aapsLogger.error(LTag.APS, "Problem loading JS Functions") + } + } catch (e: IOException) { + aapsLogger.error(LTag.APS, "IOException") + } catch (e: RhinoException) { + aapsLogger.error(LTag.APS, "RhinoException: (" + e.lineNumber() + "," + e.columnNumber() + ") " + e.toString()) + } catch (e: IllegalAccessException) { + aapsLogger.error(LTag.APS, e.toString()) + } catch (e: InstantiationException) { + aapsLogger.error(LTag.APS, e.toString()) + } catch (e: InvocationTargetException) { + aapsLogger.error(LTag.APS, e.toString()) + } finally { + Context.exit() + } + glucoseStatusParam = mGlucoseStatus.toString() + iobDataParam = iobData.toString() + currentTempParam = currentTemp.toString() + profileParam = profile.toString() + mealDataParam = mealData.toString() + return determineBasalResultSMB + } + + @Suppress("SpellCheckingInspection") fun setData(profile: Profile, + maxIob: Double, + maxBasal: Double, + minBg: Double, + maxBg: Double, + targetBg: Double, + basalRate: Double, + iobArray: Array, + glucoseStatus: GlucoseStatus, + mealData: MealData, + autosensDataRatio: Double, + tempTargetSet: Boolean, + microBolusAllowed: Boolean, + uamAllowed: Boolean, + advancedFiltering: Boolean, + isSaveCgmSource: Boolean + ) { + val pump = activePluginProvider.activePump + val pumpBolusStep = pump.pumpDescription.bolusStep + this.profile.put("max_iob", maxIob) + //mProfile.put("dia", profile.getDia()); + this.profile.put("type", "current") + this.profile.put("max_daily_basal", profile.maxDailyBasal) + this.profile.put("max_basal", maxBasal) + this.profile.put("min_bg", minBg) + this.profile.put("max_bg", maxBg) + this.profile.put("target_bg", targetBg) + this.profile.put("carb_ratio", profile.ic) + this.profile.put("sens", profile.isfMgdl) + this.profile.put("max_daily_safety_multiplier", sp.getInt(R.string.key_openapsama_max_daily_safety_multiplier, 3)) + 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) + //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("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) + this.profile.put("exercise_mode", SMBDefaults.exercise_mode) + this.profile.put("half_basal_exercise_target", SMBDefaults.half_basal_exercise_target) + this.profile.put("maxCOB", SMBDefaults.maxCOB) + this.profile.put("skip_neutral_temps", pump.setNeutralTempAtFullHour()) + // min_5m_carbimpact is not used within SMB determinebasal + //if (mealData.usedMinCarbsImpact > 0) { + // mProfile.put("min_5m_carbimpact", mealData.usedMinCarbsImpact); + //} else { + // mProfile.put("min_5m_carbimpact", SP.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact)); + //} + this.profile.put("remainingCarbsCap", SMBDefaults.remainingCarbsCap) + this.profile.put("enableUAM", uamAllowed) + this.profile.put("A52_risk_enable", SMBDefaults.A52_risk_enable) + val smbEnabled = sp.getBoolean(R.string.key_use_smb, false) + this.profile.put("SMBInterval", sp.getInt(R.string.key_smbinterval, SMBDefaults.SMBInterval)) + this.profile.put("enableSMB_with_COB", smbEnabled && sp.getBoolean(R.string.key_enableSMB_with_COB, false)) + this.profile.put("enableSMB_with_temptarget", smbEnabled && sp.getBoolean(R.string.key_enableSMB_with_temptarget, false)) + this.profile.put("allowSMB_with_high_temptarget", smbEnabled && sp.getBoolean(R.string.key_allowSMB_with_high_temptarget, false)) + this.profile.put("enableSMB_always", smbEnabled && sp.getBoolean(R.string.key_enableSMB_always, false) && advancedFiltering) + this.profile.put("enableSMB_after_carbs", smbEnabled && sp.getBoolean(R.string.key_enableSMB_after_carbs, false) && advancedFiltering) + this.profile.put("maxSMBBasalMinutes", sp.getInt(R.string.key_smbmaxminutes, SMBDefaults.maxSMBBasalMinutes)) + this.profile.put("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"))) + if (profileFunction.getUnits() == Constants.MMOL) { + this.profile.put("out_units", "mmol/L") + } + val now = System.currentTimeMillis() + val tb = treatmentsPlugin.getTempBasalFromHistory(now) + currentTemp.put("temp", "absolute") + currentTemp.put("duration", tb?.plannedRemainingMinutes ?: 0) + currentTemp.put("rate", tb?.tempBasalConvertedToAbsolute(now, profile) ?: 0.0) + + // as we have non default temps longer than 30 mintues + val tempBasal = treatmentsPlugin.getTempBasalFromHistory(System.currentTimeMillis()) + if (tempBasal != null) { + currentTemp.put("minutesrunning", tempBasal.realDuration) + } + iobData = IobCobCalculatorPlugin.convertToJSONArray(iobArray) + mGlucoseStatus.put("glucose", glucoseStatus.glucose) + mGlucoseStatus.put("noise", glucoseStatus.noise) + if (sp.getBoolean(R.string.key_always_use_shortavg, false)) { + mGlucoseStatus.put("delta", glucoseStatus.short_avgdelta) + } else { + mGlucoseStatus.put("delta", glucoseStatus.delta) + } + mGlucoseStatus.put("short_avgdelta", glucoseStatus.short_avgdelta) + mGlucoseStatus.put("long_avgdelta", glucoseStatus.long_avgdelta) + mGlucoseStatus.put("date", glucoseStatus.date) + this.mealData.put("carbs", mealData.carbs) + this.mealData.put("boluses", mealData.boluses) + this.mealData.put("mealCOB", mealData.mealCOB) + this.mealData.put("slopeFromMaxDeviation", mealData.slopeFromMaxDeviation) + this.mealData.put("slopeFromMinDeviation", mealData.slopeFromMinDeviation) + this.mealData.put("lastBolusTime", mealData.lastBolusTime) + this.mealData.put("lastCarbTime", mealData.lastCarbTime) + if (constraintChecker.isAutosensModeEnabled().value()) { + autosensData.put("ratio", autosensDataRatio) + } else { + autosensData.put("ratio", 1.0) + } + this.microBolusAllowed = microBolusAllowed + smbAlwaysAllowed = advancedFiltering + currentTime = now + saveCgmSource = isSaveCgmSource + } + + private fun makeParam(jsonObject: JSONObject?, rhino: Context, scope: Scriptable): Any { + return if (jsonObject == null) Undefined.instance + else NativeJSON.parse(rhino, scope, jsonObject.toString()) { _: Context?, _: Scriptable?, _: Scriptable?, objects: Array -> objects[1] } + } + + private fun makeParamArray(jsonArray: JSONArray?, rhino: Context, scope: Scriptable): Any { + return NativeJSON.parse(rhino, scope, jsonArray.toString()) { _: Context?, _: Scriptable?, _: Scriptable?, objects: Array -> objects[1] } + } + + @Throws(IOException::class) private fun readFile(filename: String): String { + val bytes = scriptReader.readFile(filename) + var string = String(bytes, StandardCharsets.UTF_8) + if (string.startsWith("#!/usr/bin/env node")) { + string = string.substring(20) + } + return string + } + + init { + injector.androidInjector().inject(this) + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalResultSMB.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalResultSMB.java deleted file mode 100644 index 640d1b1585..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalResultSMB.java +++ /dev/null @@ -1,97 +0,0 @@ -package info.nightscout.androidaps.plugins.aps.openAPSSMB; - -import org.json.JSONException; -import org.json.JSONObject; - -import javax.inject.Inject; - -import dagger.android.HasAndroidInjector; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.plugins.aps.loop.APSResult; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.sharedPreferences.SP; - -public class DetermineBasalResultSMB extends APSResult { - @Inject SP sp; - - private double eventualBG; - private double snoozeBG; - - private DetermineBasalResultSMB(HasAndroidInjector injector) { - super(injector); - hasPredictions = true; - } - - DetermineBasalResultSMB(HasAndroidInjector injector, JSONObject result) { - this(injector); - date = DateUtil.now(); - json = result; - try { - if (result.has("error")) { - reason = result.getString("error"); - return; - } - - reason = result.getString("reason"); - if (result.has("eventualBG")) eventualBG = result.getDouble("eventualBG"); - if (result.has("snoozeBG")) snoozeBG = result.getDouble("snoozeBG"); - //if (result.has("insulinReq")) insulinReq = result.getDouble("insulinReq"); - - if (result.has("carbsReq")) carbsReq = result.getInt("carbsReq"); - if (result.has("carbsReqWithin")) carbsReqWithin = result.getInt("carbsReqWithin"); - - - if (result.has("rate") && result.has("duration")) { - tempBasalRequested = true; - rate = result.getDouble("rate"); - if (rate < 0d) rate = 0d; - duration = result.getInt("duration"); - } else { - rate = -1; - duration = -1; - } - - if (result.has("units")) { - bolusRequested = true; - smb = result.getDouble("units"); - } else { - smb = 0d; - } - if (result.has("targetBG")) { - targetBG = result.getDouble("targetBG"); - } - - if (result.has("deliverAt")) { - String date = result.getString("deliverAt"); - try { - deliverAt = DateUtil.fromISODateString(date).getTime(); - } catch (Exception e) { - aapsLogger.error(LTag.APS, "Error parsing 'deliverAt' date: " + date, e); - } - } - } catch (JSONException e) { - aapsLogger.error(LTag.APS, "Error parsing determine-basal result JSON", e); - } - } - - @Override - public DetermineBasalResultSMB newAndClone(HasAndroidInjector injector) { - DetermineBasalResultSMB newResult = new DetermineBasalResultSMB(injector); - doClone(newResult); - - newResult.eventualBG = eventualBG; - newResult.snoozeBG = snoozeBG; - return newResult; - } - - @Override - public JSONObject json() { - try { - return new JSONObject(this.json.toString()); - } catch (JSONException e) { - aapsLogger.error(LTag.APS, "Error converting determine-basal result to JSON", e); - } - return null; - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalResultSMB.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalResultSMB.kt new file mode 100644 index 0000000000..46b68bbd0a --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/DetermineBasalResultSMB.kt @@ -0,0 +1,80 @@ +package info.nightscout.androidaps.plugins.aps.openAPSSMB + +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.aps.loop.APSResult +import info.nightscout.androidaps.utils.DateUtil +import org.json.JSONException +import org.json.JSONObject + +class DetermineBasalResultSMB private constructor(injector: HasAndroidInjector) : APSResult(injector) { + + private var eventualBG = 0.0 + private var snoozeBG = 0.0 + + internal constructor(injector: HasAndroidInjector, result: JSONObject) : this(injector) { + date = DateUtil.now() + json = result + try { + if (result.has("error")) { + reason = result.getString("error") + return + } + reason = result.getString("reason") + if (result.has("eventualBG")) eventualBG = result.getDouble("eventualBG") + if (result.has("snoozeBG")) snoozeBG = result.getDouble("snoozeBG") + //if (result.has("insulinReq")) insulinReq = result.getDouble("insulinReq"); + if (result.has("carbsReq")) carbsReq = result.getInt("carbsReq") + if (result.has("carbsReqWithin")) carbsReqWithin = result.getInt("carbsReqWithin") + if (result.has("rate") && result.has("duration")) { + tempBasalRequested = true + rate = result.getDouble("rate") + if (rate < 0.0) rate = 0.0 + duration = result.getInt("duration") + } else { + rate = (-1).toDouble() + duration = -1 + } + if (result.has("units")) { + bolusRequested = true + smb = result.getDouble("units") + } else { + smb = 0.0 + } + if (result.has("targetBG")) { + targetBG = result.getDouble("targetBG") + } + if (result.has("deliverAt")) { + val date = result.getString("deliverAt") + try { + deliverAt = DateUtil.fromISODateString(date).time + } catch (e: Exception) { + aapsLogger.error(LTag.APS, "Error parsing 'deliverAt' date: $date", e) + } + } + } catch (e: JSONException) { + aapsLogger.error(LTag.APS, "Error parsing determine-basal result JSON", e) + } + } + + override fun newAndClone(injector: HasAndroidInjector): DetermineBasalResultSMB { + val newResult = DetermineBasalResultSMB(injector) + doClone(newResult) + newResult.eventualBG = eventualBG + newResult.snoozeBG = snoozeBG + return newResult + } + + override fun json(): JSONObject? { + try { + return JSONObject(json.toString()) + } catch (e: JSONException) { + aapsLogger.error(LTag.APS, "Error converting determine-basal result to JSON", e) + } + return null + } + + init { + hasPredictions = true + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBFragment.kt index c16a432370..be4e1015fa 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBFragment.kt @@ -8,6 +8,7 @@ import android.view.View import android.view.ViewGroup import dagger.android.support.DaggerFragment import info.nightscout.androidaps.R +import info.nightscout.androidaps.databinding.OpenapsamaFragmentBinding import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui @@ -16,34 +17,42 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.JSONFormatter -import info.nightscout.androidaps.utils.extensions.plusAssign import info.nightscout.androidaps.utils.resources.ResourceHelper -import io.reactivex.android.schedulers.AndroidSchedulers +import info.nightscout.androidaps.utils.rx.AapsSchedulers import io.reactivex.disposables.CompositeDisposable -import kotlinx.android.synthetic.main.openapsama_fragment.* +import io.reactivex.rxkotlin.plusAssign import org.json.JSONArray import org.json.JSONException import javax.inject.Inject class OpenAPSSMBFragment : DaggerFragment() { + private var disposable: CompositeDisposable = CompositeDisposable() @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var openAPSSMBPlugin: OpenAPSSMBPlugin @Inject lateinit var dateUtil: DateUtil + private var _binding: OpenapsamaFragmentBinding? = null + + // This property is only valid between onCreateView and + // onDestroyView. + private val binding get() = _binding!! + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.openapsama_fragment, container, false) + savedInstanceState: Bundle?): View { + _binding = OpenapsamaFragmentBinding.inflate(inflater, container, false) + return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - openapsma_run.setOnClickListener { + binding.run.setOnClickListener { openAPSSMBPlugin.invoke("OpenAPSSMB button", false) } } @@ -53,16 +62,16 @@ class OpenAPSSMBFragment : DaggerFragment() { super.onResume() disposable += rxBus .toObservable(EventOpenAPSUpdateGui::class.java) - .observeOn(AndroidSchedulers.mainThread()) + .observeOn(aapsSchedulers.main) .subscribe({ updateGUI() - }, { fabricPrivacy.logException(it) }) + }, fabricPrivacy::logException) disposable += rxBus .toObservable(EventOpenAPSUpdateResultGui::class.java) - .observeOn(AndroidSchedulers.mainThread()) + .observeOn(aapsSchedulers.main) .subscribe({ updateResultGUI(it.text) - }, { fabricPrivacy.logException(it) }) + }, fabricPrivacy::logException) updateGUI() } @@ -73,52 +82,58 @@ class OpenAPSSMBFragment : DaggerFragment() { disposable.clear() } + @Synchronized + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + @Synchronized fun updateGUI() { - if (openapsma_result == null) return + if (_binding == null) return openAPSSMBPlugin.lastAPSResult?.let { lastAPSResult -> - openapsma_result.text = JSONFormatter.format(lastAPSResult.json) - openapsma_request.text = lastAPSResult.toSpanned() + binding.result.text = JSONFormatter.format(lastAPSResult.json) + binding.request.text = lastAPSResult.toSpanned() } openAPSSMBPlugin.lastDetermineBasalAdapterSMBJS?.let { determineBasalAdapterSMBJS -> - openapsma_glucosestatus.text = JSONFormatter.format(determineBasalAdapterSMBJS.glucoseStatusParam) - openapsma_currenttemp.text = JSONFormatter.format(determineBasalAdapterSMBJS.currentTempParam) + binding.glucosestatus.text = JSONFormatter.format(determineBasalAdapterSMBJS.glucoseStatusParam) + binding.currenttemp.text = JSONFormatter.format(determineBasalAdapterSMBJS.currentTempParam) try { val iobArray = JSONArray(determineBasalAdapterSMBJS.iobDataParam) - openapsma_iobdata.text = TextUtils.concat(resourceHelper.gs(R.string.array_of_elements, iobArray.length()) + "\n", JSONFormatter.format(iobArray.getString(0))) + binding.iobdata.text = TextUtils.concat(resourceHelper.gs(R.string.array_of_elements, iobArray.length()) + "\n", JSONFormatter.format(iobArray.getString(0))) } catch (e: JSONException) { aapsLogger.error(LTag.APS, "Unhandled exception", e) @SuppressLint("SetTextI18n") - openapsma_iobdata.text = "JSONException see log for details" + binding.iobdata.text = "JSONException see log for details" } - openapsma_profile.text = JSONFormatter.format(determineBasalAdapterSMBJS.profileParam) - openapsma_mealdata.text = JSONFormatter.format(determineBasalAdapterSMBJS.mealDataParam) - openapsma_scriptdebugdata.text = determineBasalAdapterSMBJS.scriptDebug + binding.profile.text = JSONFormatter.format(determineBasalAdapterSMBJS.profileParam) + binding.mealdata.text = JSONFormatter.format(determineBasalAdapterSMBJS.mealDataParam) + binding.scriptdebugdata.text = determineBasalAdapterSMBJS.scriptDebug openAPSSMBPlugin.lastAPSResult?.inputConstraints?.let { - openapsma_constraints.text = it.getReasons(aapsLogger) + binding.constraints.text = it.getReasons(aapsLogger) } } if (openAPSSMBPlugin.lastAPSRun != 0L) { - openapsma_lastrun.text = dateUtil.dateAndTimeString(openAPSSMBPlugin.lastAPSRun) + binding.lastrun.text = dateUtil.dateAndTimeString(openAPSSMBPlugin.lastAPSRun) } - openAPSSMBPlugin.lastAutosensResult?.let { - openapsma_autosensdata.text = JSONFormatter.format(it.json()) + openAPSSMBPlugin.lastAutosensResult.let { + binding.autosensdata.text = JSONFormatter.format(it.json()) } } @Synchronized private fun updateResultGUI(text: String) { - if (openapsma_result == null) return - openapsma_result.text = text - openapsma_glucosestatus.text = "" - openapsma_currenttemp.text = "" - openapsma_iobdata.text = "" - openapsma_profile.text = "" - openapsma_mealdata.text = "" - openapsma_autosensdata.text = "" - openapsma_scriptdebugdata.text = "" - openapsma_request.text = "" - openapsma_lastrun.text = "" + if (_binding == null) return + binding.result.text = text + binding.glucosestatus.text = "" + binding.currenttemp.text = "" + binding.iobdata.text = "" + binding.profile.text = "" + binding.mealdata.text = "" + binding.autosensdata.text = "" + binding.scriptdebugdata.text = "" + binding.request.text = "" + binding.lastrun.text = "" } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.java deleted file mode 100644 index 4a6ec60b7d..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.java +++ /dev/null @@ -1,323 +0,0 @@ -package info.nightscout.androidaps.plugins.aps.openAPSSMB; - -import android.content.Context; - -import androidx.preference.PreferenceFragmentCompat; -import androidx.preference.SwitchPreference; - -import org.jetbrains.annotations.NotNull; -import org.json.JSONException; - -import javax.inject.Inject; -import javax.inject.Singleton; - -import dagger.android.HasAndroidInjector; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.IobTotal; -import info.nightscout.androidaps.data.MealData; -import info.nightscout.androidaps.data.Profile; -import info.nightscout.androidaps.db.TempTarget; -import info.nightscout.androidaps.interfaces.APSInterface; -import info.nightscout.androidaps.interfaces.ActivePluginProvider; -import info.nightscout.androidaps.interfaces.Constraint; -import info.nightscout.androidaps.interfaces.ConstraintsInterface; -import info.nightscout.androidaps.interfaces.PluginBase; -import info.nightscout.androidaps.interfaces.PluginDescription; -import info.nightscout.androidaps.interfaces.PluginType; -import info.nightscout.androidaps.interfaces.PumpInterface; -import info.nightscout.androidaps.logging.AAPSLogger; -import info.nightscout.androidaps.logging.LTag; -import info.nightscout.androidaps.plugins.aps.loop.APSResult; -import info.nightscout.androidaps.plugins.aps.loop.ScriptReader; -import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui; -import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateResultGui; -import info.nightscout.androidaps.plugins.bus.RxBusWrapper; -import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker; -import info.nightscout.androidaps.interfaces.ProfileFunction; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin; -import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.FabricPrivacy; -import info.nightscout.androidaps.utils.HardLimits; -import info.nightscout.androidaps.utils.Profiler; -import info.nightscout.androidaps.utils.Round; -import info.nightscout.androidaps.utils.resources.ResourceHelper; -import info.nightscout.androidaps.utils.sharedPreferences.SP; - -@Singleton -public class OpenAPSSMBPlugin extends PluginBase implements APSInterface, ConstraintsInterface { - private final ConstraintChecker constraintChecker; - private final ResourceHelper resourceHelper; - private final ProfileFunction profileFunction; - private final Context context; - private final RxBusWrapper rxBus; - private final ActivePluginProvider activePlugin; - private final TreatmentsPlugin treatmentsPlugin; - private final IobCobCalculatorPlugin iobCobCalculatorPlugin; - private final HardLimits hardLimits; - private final Profiler profiler; - private final FabricPrivacy fabricPrivacy; - private final SP sp; - - // last values - DetermineBasalAdapterSMBJS lastDetermineBasalAdapterSMBJS = null; - long lastAPSRun = 0; - DetermineBasalResultSMB lastAPSResult = null; - AutosensResult lastAutosensResult = null; - - @Inject - public OpenAPSSMBPlugin( - HasAndroidInjector injector, - AAPSLogger aapsLogger, - RxBusWrapper rxBus, - ConstraintChecker constraintChecker, - ResourceHelper resourceHelper, - ProfileFunction profileFunction, - Context context, - ActivePluginProvider activePlugin, - TreatmentsPlugin treatmentsPlugin, - IobCobCalculatorPlugin iobCobCalculatorPlugin, - HardLimits hardLimits, - Profiler profiler, - FabricPrivacy fabricPrivacy, - SP sp - ) { - super(new PluginDescription() - .mainType(PluginType.APS) - .fragmentClass(OpenAPSSMBFragment.class.getName()) - .pluginIcon(R.drawable.ic_generic_icon) - .pluginName(R.string.openapssmb) - .shortName(R.string.smb_shortname) - .preferencesId(R.xml.pref_openapssmb) - .description(R.string.description_smb) - .setDefault(), - aapsLogger, resourceHelper, injector - ); - this.constraintChecker = constraintChecker; - this.resourceHelper = resourceHelper; - this.profileFunction = profileFunction; - this.rxBus = rxBus; - this.context = context; - this.activePlugin = activePlugin; - this.treatmentsPlugin = treatmentsPlugin; - this.iobCobCalculatorPlugin = iobCobCalculatorPlugin; - this.hardLimits = hardLimits; - this.profiler = profiler; - this.fabricPrivacy = fabricPrivacy; - this.sp = sp; - } - - @Override - public boolean specialEnableCondition() { - try { - PumpInterface pump = activePlugin.getActivePump(); - return pump.getPumpDescription().isTempBasalCapable; - } catch (Exception ignored) { - // may fail during initialization - return true; - } - } - - @Override - public boolean specialShowInListCondition() { - PumpInterface pump = activePlugin.getActivePump(); - return pump.getPumpDescription().isTempBasalCapable; - } - - @Override - public void preprocessPreferences(@NotNull PreferenceFragmentCompat preferenceFragment) { - super.preprocessPreferences(preferenceFragment); - boolean smbAlwaysEnabled = sp.getBoolean(R.string.key_enableSMB_always, false); - - SwitchPreference withCOB = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_enableSMB_with_COB)); - if (withCOB != null) { - withCOB.setVisible(!smbAlwaysEnabled); - } - SwitchPreference withTempTarget = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_enableSMB_with_temptarget)); - if (withTempTarget != null) { - withTempTarget.setVisible(!smbAlwaysEnabled); - } - SwitchPreference afterCarbs = preferenceFragment.findPreference(resourceHelper.gs(R.string.key_enableSMB_after_carbs)); - if (afterCarbs != null) { - afterCarbs.setVisible(!smbAlwaysEnabled); - } - } - - @Override - public APSResult getLastAPSResult() { - return lastAPSResult; - } - - @Override - public long getLastAPSRun() { - return lastAPSRun; - } - - @Override - public void invoke(String initiator, boolean tempBasalFallback) { - getAapsLogger().debug(LTag.APS, "invoke from " + initiator + " tempBasalFallback: " + tempBasalFallback); - lastAPSResult = null; - DetermineBasalAdapterSMBJS determineBasalAdapterSMBJS; - determineBasalAdapterSMBJS = new DetermineBasalAdapterSMBJS(new ScriptReader(context), getInjector()); - - GlucoseStatus glucoseStatus = new GlucoseStatus(getInjector()).getGlucoseStatusData(); - Profile profile = profileFunction.getProfile(); - PumpInterface pump = activePlugin.getActivePump(); - - if (profile == null) { - rxBus.send(new EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.noprofileselected))); - getAapsLogger().debug(LTag.APS, resourceHelper.gs(R.string.noprofileselected)); - return; - } - - if (!isEnabled(PluginType.APS)) { - rxBus.send(new EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openapsma_disabled))); - getAapsLogger().debug(LTag.APS, resourceHelper.gs(R.string.openapsma_disabled)); - return; - } - - if (glucoseStatus == null) { - rxBus.send(new EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openapsma_noglucosedata))); - getAapsLogger().debug(LTag.APS, resourceHelper.gs(R.string.openapsma_noglucosedata)); - return; - } - - Constraint inputConstraints = new Constraint<>(0d); // fake. only for collecting all results - - Constraint maxBasalConstraint = constraintChecker.getMaxBasalAllowed(profile); - inputConstraints.copyReasons(maxBasalConstraint); - double maxBasal = maxBasalConstraint.value(); - double minBg = profile.getTargetLowMgdl(); - double maxBg = profile.getTargetHighMgdl(); - double targetBg = profile.getTargetMgdl(); - - minBg = Round.roundTo(minBg, 0.1d); - maxBg = Round.roundTo(maxBg, 0.1d); - - long start = System.currentTimeMillis(); - long startPart = System.currentTimeMillis(); - - MealData mealData = iobCobCalculatorPlugin.getMealData(); - profiler.log(LTag.APS, "getMealData()", startPart); - - Constraint maxIOBAllowedConstraint = constraintChecker.getMaxIOBAllowed(); - inputConstraints.copyReasons(maxIOBAllowedConstraint); - double maxIob = maxIOBAllowedConstraint.value(); - - minBg = hardLimits.verifyHardLimits(minBg, "minBg", hardLimits.getVERY_HARD_LIMIT_MIN_BG()[0], hardLimits.getVERY_HARD_LIMIT_MIN_BG()[1]); - maxBg = hardLimits.verifyHardLimits(maxBg, "maxBg", hardLimits.getVERY_HARD_LIMIT_MAX_BG()[0], hardLimits.getVERY_HARD_LIMIT_MAX_BG()[1]); - targetBg = hardLimits.verifyHardLimits(targetBg, "targetBg", hardLimits.getVERY_HARD_LIMIT_TARGET_BG()[0], hardLimits.getVERY_HARD_LIMIT_TARGET_BG()[1]); - - boolean isTempTarget = false; - TempTarget tempTarget = treatmentsPlugin.getTempTargetFromHistory(System.currentTimeMillis()); - if (tempTarget != null) { - isTempTarget = true; - minBg = hardLimits.verifyHardLimits(tempTarget.low, "minBg", hardLimits.getVERY_HARD_LIMIT_TEMP_MIN_BG()[0], hardLimits.getVERY_HARD_LIMIT_TEMP_MIN_BG()[1]); - maxBg = hardLimits.verifyHardLimits(tempTarget.high, "maxBg", hardLimits.getVERY_HARD_LIMIT_TEMP_MAX_BG()[0], hardLimits.getVERY_HARD_LIMIT_TEMP_MAX_BG()[1]); - targetBg = hardLimits.verifyHardLimits(tempTarget.target(), "targetBg", hardLimits.getVERY_HARD_LIMIT_TEMP_TARGET_BG()[0], hardLimits.getVERY_HARD_LIMIT_TEMP_TARGET_BG()[1]); - } - - - if (!hardLimits.checkOnlyHardLimits(profile.getDia(), "dia", hardLimits.minDia(), hardLimits.maxDia())) - return; - if (!hardLimits.checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), "carbratio", hardLimits.minIC(), hardLimits.maxIC())) - return; - if (!hardLimits.checkOnlyHardLimits(profile.getIsfMgdl(), "sens", hardLimits.getMINISF(), hardLimits.getMAXISF())) - return; - if (!hardLimits.checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.02, hardLimits.maxBasal())) - return; - if (!hardLimits.checkOnlyHardLimits(pump.getBaseBasalRate(), "current_basal", 0.01, hardLimits.maxBasal())) - return; - - startPart = System.currentTimeMillis(); - if (constraintChecker.isAutosensModeEnabled().value()) { - AutosensData autosensData = iobCobCalculatorPlugin.getLastAutosensDataSynchronized("OpenAPSPlugin"); - if (autosensData == null) { - rxBus.send(new EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openaps_noasdata))); - return; - } - lastAutosensResult = autosensData.autosensResult; - } else { - lastAutosensResult = new AutosensResult(); - lastAutosensResult.sensResult = "autosens disabled"; - } - - IobTotal[] iobArray = iobCobCalculatorPlugin.calculateIobArrayForSMB(lastAutosensResult, SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget); - profiler.log(LTag.APS, "calculateIobArrayInDia()", startPart); - - startPart = System.currentTimeMillis(); - Constraint smbAllowed = new Constraint<>(!tempBasalFallback); - constraintChecker.isSMBModeEnabled(smbAllowed); - inputConstraints.copyReasons(smbAllowed); - - Constraint advancedFiltering = new Constraint<>(!tempBasalFallback); - constraintChecker.isAdvancedFilteringEnabled(advancedFiltering); - inputConstraints.copyReasons(advancedFiltering); - - Constraint uam = new Constraint<>(true); - constraintChecker.isUAMEnabled(uam); - inputConstraints.copyReasons(uam); - - profiler.log(LTag.APS, "detectSensitivityandCarbAbsorption()", startPart); - profiler.log(LTag.APS, "SMB data gathering", start); - - start = System.currentTimeMillis(); - try { - determineBasalAdapterSMBJS.setData(profile, maxIob, maxBasal, minBg, maxBg, targetBg, activePlugin.getActivePump().getBaseBasalRate(), iobArray, glucoseStatus, mealData, - lastAutosensResult.ratio, //autosensDataRatio - isTempTarget, - smbAllowed.value(), - uam.value(), - advancedFiltering.value(), - activePlugin.getActiveBgSource().getClass().getSimpleName().equals("DexcomPlugin") - ); - } catch (JSONException e) { - fabricPrivacy.logException(e); - return; - } - - long now = System.currentTimeMillis(); - - DetermineBasalResultSMB determineBasalResultSMB = determineBasalAdapterSMBJS.invoke(); - profiler.log(LTag.APS, "SMB calculation", start); - if (determineBasalResultSMB == null) { - getAapsLogger().error(LTag.APS, "SMB calculation returned null"); - lastDetermineBasalAdapterSMBJS = null; - lastAPSResult = null; - lastAPSRun = 0; - } else { - // TODO still needed with oref1? - // Fix bug determine basal - if (determineBasalResultSMB.rate == 0d && determineBasalResultSMB.duration == 0 && !treatmentsPlugin.isTempBasalInProgress()) - determineBasalResultSMB.tempBasalRequested = false; - - determineBasalResultSMB.iob = iobArray[0]; - - try { - determineBasalResultSMB.json.put("timestamp", DateUtil.toISOString(now)); - } catch (JSONException e) { - getAapsLogger().error(LTag.APS, "Unhandled exception", e); - } - - determineBasalResultSMB.inputConstraints = inputConstraints; - - lastDetermineBasalAdapterSMBJS = determineBasalAdapterSMBJS; - lastAPSResult = determineBasalResultSMB; - lastAPSRun = now; - } - rxBus.send(new EventOpenAPSUpdateGui()); - - //deviceStatus.suggested = determineBasalResultAMA.json; - } - - @NotNull - @Override - public Constraint isSuperBolusEnabled(Constraint value) { - value.set(getAapsLogger(), false); - return value; - } - -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.kt new file mode 100644 index 0000000000..f1245810f0 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.kt @@ -0,0 +1,202 @@ +package info.nightscout.androidaps.plugins.aps.openAPSSMB + +import android.content.Context +import androidx.preference.PreferenceFragmentCompat +import androidx.preference.SwitchPreference +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.R +import info.nightscout.androidaps.data.Profile +import info.nightscout.androidaps.interfaces.* +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui +import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateResultGui +import info.nightscout.androidaps.plugins.aps.loop.ScriptReader +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin +import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.HardLimits +import info.nightscout.androidaps.utils.Profiler +import info.nightscout.androidaps.utils.Round +import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.androidaps.utils.sharedPreferences.SP +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +open class OpenAPSSMBPlugin @Inject constructor( + injector: HasAndroidInjector, + aapsLogger: AAPSLogger, + private val rxBus: RxBusWrapper, + private val constraintChecker: ConstraintChecker, + resourceHelper: ResourceHelper, + private val profileFunction: ProfileFunction, + private val context: Context, + private val activePlugin: ActivePluginProvider, + private val treatmentsPlugin: TreatmentsPlugin, + private val iobCobCalculatorPlugin: IobCobCalculatorPlugin, + private val hardLimits: HardLimits, + private val profiler: Profiler, + private val sp: SP +) : PluginBase(PluginDescription() + .mainType(PluginType.APS) + .fragmentClass(OpenAPSSMBFragment::class.java.name) + .pluginIcon(R.drawable.ic_generic_icon) + .pluginName(R.string.openapssmb) + .shortName(R.string.smb_shortname) + .preferencesId(R.xml.pref_openapssmb) + .description(R.string.description_smb) + .setDefault(), + aapsLogger, resourceHelper, injector +), APSInterface, ConstraintsInterface { + + // last values + override var lastAPSRun: Long = 0 + override var lastAPSResult: DetermineBasalResultSMB? = null + var lastDetermineBasalAdapterSMBJS: DetermineBasalAdapterSMBJS? = null + var lastAutosensResult = AutosensResult() + + override fun specialEnableCondition(): Boolean { + return try { + activePlugin.activePump.pumpDescription.isTempBasalCapable + } catch (ignored: Exception) { + // may fail during initialization + true + } + } + + override fun specialShowInListCondition(): Boolean { + val pump = activePlugin.activePump + return pump.pumpDescription.isTempBasalCapable + } + + override fun preprocessPreferences(preferenceFragment: PreferenceFragmentCompat) { + super.preprocessPreferences(preferenceFragment) + val smbAlwaysEnabled = sp.getBoolean(R.string.key_enableSMB_always, false) + preferenceFragment.findPreference(resourceHelper.gs(R.string.key_enableSMB_with_COB))?.isVisible = !smbAlwaysEnabled + preferenceFragment.findPreference(resourceHelper.gs(R.string.key_enableSMB_with_temptarget))?.isVisible = !smbAlwaysEnabled + preferenceFragment.findPreference(resourceHelper.gs(R.string.key_enableSMB_after_carbs))?.isVisible = !smbAlwaysEnabled + } + + override fun invoke(initiator: String, tempBasalFallback: Boolean) { + aapsLogger.debug(LTag.APS, "invoke from $initiator tempBasalFallback: $tempBasalFallback") + lastAPSResult = null + val glucoseStatus = GlucoseStatus(injector).glucoseStatusData + val profile = profileFunction.getProfile() + val pump = activePlugin.activePump + if (profile == null) { + rxBus.send(EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.noprofileselected))) + aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.noprofileselected)) + return + } + if (!isEnabled(PluginType.APS)) { + rxBus.send(EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openapsma_disabled))) + aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.openapsma_disabled)) + return + } + if (glucoseStatus == null) { + rxBus.send(EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openapsma_noglucosedata))) + aapsLogger.debug(LTag.APS, resourceHelper.gs(R.string.openapsma_noglucosedata)) + return + } + + val inputConstraints = Constraint(0.0) // fake. only for collecting all results + val maxBasal = constraintChecker.getMaxBasalAllowed(profile).also { + inputConstraints.copyReasons(it) + }.value() + var start = System.currentTimeMillis() + var startPart = System.currentTimeMillis() + profiler.log(LTag.APS, "getMealData()", startPart) + val maxIob = constraintChecker.getMaxIOBAllowed().also { maxIOBAllowedConstraint -> + inputConstraints.copyReasons(maxIOBAllowedConstraint) + }.value() + + var minBg = hardLimits.verifyHardLimits(Round.roundTo(profile.targetLowMgdl, 0.1), "minBg", hardLimits.VERY_HARD_LIMIT_MIN_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_MIN_BG[1].toDouble()) + var maxBg = hardLimits.verifyHardLimits(Round.roundTo(profile.targetHighMgdl, 0.1), "maxBg", hardLimits.VERY_HARD_LIMIT_MAX_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_MAX_BG[1].toDouble()) + var targetBg = hardLimits.verifyHardLimits(profile.targetMgdl, "targetBg", hardLimits.VERY_HARD_LIMIT_TARGET_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TARGET_BG[1].toDouble()) + var isTempTarget = false + treatmentsPlugin.getTempTargetFromHistory(System.currentTimeMillis())?.let { tempTarget -> + isTempTarget = true + minBg = hardLimits.verifyHardLimits(tempTarget.low, "minBg", hardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[1].toDouble()) + maxBg = hardLimits.verifyHardLimits(tempTarget.high, "maxBg", hardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[1].toDouble()) + targetBg = hardLimits.verifyHardLimits(tempTarget.target(), "targetBg", hardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[0].toDouble(), hardLimits.VERY_HARD_LIMIT_TEMP_TARGET_BG[1].toDouble()) + } + if (!hardLimits.checkOnlyHardLimits(profile.dia, "dia", hardLimits.minDia(), hardLimits.maxDia())) return + if (!hardLimits.checkOnlyHardLimits(profile.getIcTimeFromMidnight(Profile.secondsFromMidnight()), "carbratio", hardLimits.minIC(), hardLimits.maxIC())) return + if (!hardLimits.checkOnlyHardLimits(profile.isfMgdl, "sens", hardLimits.MINISF, hardLimits.MAXISF)) return + if (!hardLimits.checkOnlyHardLimits(profile.maxDailyBasal, "max_daily_basal", 0.02, hardLimits.maxBasal())) return + if (!hardLimits.checkOnlyHardLimits(pump.baseBasalRate, "current_basal", 0.01, hardLimits.maxBasal())) return + startPart = System.currentTimeMillis() + if (constraintChecker.isAutosensModeEnabled().value()) { + val autosensData = iobCobCalculatorPlugin.getLastAutosensDataSynchronized("OpenAPSPlugin") + if (autosensData == null) { + rxBus.send(EventOpenAPSUpdateResultGui(resourceHelper.gs(R.string.openaps_noasdata))) + return + } + lastAutosensResult = autosensData.autosensResult + } else { + lastAutosensResult.sensResult = "autosens disabled" + } + val iobArray = iobCobCalculatorPlugin.calculateIobArrayForSMB(lastAutosensResult, SMBDefaults.exercise_mode, SMBDefaults.half_basal_exercise_target, isTempTarget) + profiler.log(LTag.APS, "calculateIobArrayInDia()", startPart) + startPart = System.currentTimeMillis() + val smbAllowed = Constraint(!tempBasalFallback).also { + constraintChecker.isSMBModeEnabled(it) + inputConstraints.copyReasons(it) + } + val advancedFiltering = Constraint(!tempBasalFallback).also { + constraintChecker.isAdvancedFilteringEnabled(it) + inputConstraints.copyReasons(it) + } + val uam = Constraint(true).also { + constraintChecker.isUAMEnabled(it) + inputConstraints.copyReasons(it) + } + profiler.log(LTag.APS, "detectSensitivityAndCarbAbsorption()", startPart) + profiler.log(LTag.APS, "SMB data gathering", start) + start = System.currentTimeMillis() + + DetermineBasalAdapterSMBJS(ScriptReader(context), injector).also { determineBasalAdapterSMBJS -> + determineBasalAdapterSMBJS.setData(profile, maxIob, maxBasal, minBg, maxBg, targetBg, + activePlugin.activePump.baseBasalRate, + iobArray, + glucoseStatus, + iobCobCalculatorPlugin.mealData, + lastAutosensResult.ratio, + isTempTarget, + smbAllowed.value(), + uam.value(), + advancedFiltering.value(), + activePlugin.activeBgSource.javaClass.simpleName == "DexcomPlugin") + val now = System.currentTimeMillis() + val determineBasalResultSMB = determineBasalAdapterSMBJS.invoke() + profiler.log(LTag.APS, "SMB calculation", start) + if (determineBasalResultSMB == null) { + aapsLogger.error(LTag.APS, "SMB calculation returned null") + lastDetermineBasalAdapterSMBJS = null + lastAPSResult = null + lastAPSRun = 0 + } else { + // TODO still needed with oref1? + // Fix bug determine basal + if (determineBasalResultSMB.rate == 0.0 && determineBasalResultSMB.duration == 0 && !treatmentsPlugin.isTempBasalInProgress) determineBasalResultSMB.tempBasalRequested = false + determineBasalResultSMB.iob = iobArray[0] + determineBasalResultSMB.json?.put("timestamp", DateUtil.toISOString(now)) + determineBasalResultSMB.inputConstraints = inputConstraints + lastDetermineBasalAdapterSMBJS = determineBasalAdapterSMBJS + lastAPSResult = determineBasalResultSMB + lastAPSRun = now + } + } + rxBus.send(EventOpenAPSUpdateGui()) + } + + override fun isSuperBolusEnabled(value: Constraint): Constraint { + value[aapsLogger] = false + return value + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt index 09bbf3c04d..8a19808833 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderFragment.kt @@ -12,22 +12,24 @@ import dagger.android.support.DaggerFragment import info.nightscout.androidaps.Config import info.nightscout.androidaps.R import info.nightscout.androidaps.activities.PreferencesActivity +import info.nightscout.androidaps.databinding.ConfigbuilderFragmentBinding import info.nightscout.androidaps.events.EventRebuildTabs import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.configBuilder.events.EventConfigBuilderUpdateGui import info.nightscout.androidaps.utils.FabricPrivacy -import info.nightscout.androidaps.utils.extensions.plusAssign +import io.reactivex.rxkotlin.plusAssign import info.nightscout.androidaps.utils.extensions.toVisibility import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.resources.ResourceHelper -import io.reactivex.android.schedulers.AndroidSchedulers +import info.nightscout.androidaps.utils.rx.AapsSchedulers import io.reactivex.disposables.CompositeDisposable -import kotlinx.android.synthetic.main.configbuilder_fragment.* import java.util.* import javax.inject.Inject class ConfigBuilderFragment : DaggerFragment() { + + @Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var configBuilderPlugin: ConfigBuilderPlugin @@ -39,25 +41,32 @@ class ConfigBuilderFragment : DaggerFragment() { private var disposable: CompositeDisposable = CompositeDisposable() private val pluginViewHolders = ArrayList() + private var _binding: ConfigbuilderFragmentBinding? = null + + // This property is only valid between onCreateView and + // onDestroyView. + private val binding get() = _binding!! + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.configbuilder_fragment, container, false) + 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)) - configbuilder_main_layout.visibility = View.GONE + binding.mainLayout.visibility = View.GONE else - unlock.visibility = View.GONE + binding.unlock.visibility = View.GONE - unlock.setOnClickListener { + binding.unlock.setOnClickListener { activity?.let { activity -> - protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, Runnable { + protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, { activity.runOnUiThread { - configbuilder_main_layout.visibility = View.VISIBLE - unlock.visibility = View.GONE + binding.mainLayout.visibility = View.VISIBLE + binding.unlock.visibility = View.GONE } }) } @@ -69,10 +78,10 @@ class ConfigBuilderFragment : DaggerFragment() { super.onResume() disposable += rxBus .toObservable(EventConfigBuilderUpdateGui::class.java) - .observeOn(AndroidSchedulers.mainThread()) + .observeOn(aapsSchedulers.main) .subscribe({ for (pluginViewHolder in pluginViewHolders) pluginViewHolder.update() - }, { fabricPrivacy.logException(it) }) + }, fabricPrivacy::logException) updateGUI() } @@ -82,9 +91,15 @@ class ConfigBuilderFragment : DaggerFragment() { disposable.clear() } + @Synchronized + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + @Synchronized private fun updateGUI() { - configbuilder_categories.removeAllViews() + binding.categories.removeAllViews() if (!config.NSCLIENT) { createViewsForPlugins(R.string.configbuilder_profile, R.string.configbuilder_profile_description, PluginType.PROFILE, activePlugin.getSpecificPluginsVisibleInListByInterface(ProfileInterface::class.java, PluginType.PROFILE)) } @@ -115,7 +130,7 @@ class ConfigBuilderFragment : DaggerFragment() { pluginContainer.addView(pluginViewHolder.baseView) pluginViewHolders.add(pluginViewHolder) } - configbuilder_categories.addView(parent) + binding.categories.addView(parent) } inner class PluginViewHolder internal constructor(private val fragment: ConfigBuilderFragment, @@ -157,7 +172,7 @@ class ConfigBuilderFragment : DaggerFragment() { pluginPreferences.setOnClickListener { fragment.activity?.let { activity -> - protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, Runnable { + protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, { val i = Intent(fragment.context, PreferencesActivity::class.java) i.putExtra("id", plugin.preferencesId) fragment.startActivity(i) @@ -174,7 +189,7 @@ class ConfigBuilderFragment : DaggerFragment() { enabledInclusive.isChecked = plugin.isEnabled(pluginType) enabledInclusive.isEnabled = !plugin.pluginDescription.alwaysEnabled enabledExclusive.isEnabled = !plugin.pluginDescription.alwaysEnabled - if(plugin.menuIcon != -1) { + if (plugin.menuIcon != -1) { pluginIcon.visibility = View.VISIBLE pluginIcon.setImageDrawable(context?.let { ContextCompat.getDrawable(it, plugin.menuIcon) }) } else { @@ -196,7 +211,5 @@ class ConfigBuilderFragment : DaggerFragment() { private fun areMultipleSelectionsAllowed(type: PluginType): Boolean { return type == PluginType.GENERAL || type == PluginType.CONSTRAINTS || type == PluginType.LOOP } - } - } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderPlugin.kt index 4fb140d55a..3f54b6e616 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/configBuilder/ConfigBuilderPlugin.kt @@ -9,9 +9,10 @@ import info.nightscout.androidaps.events.EventRebuildTabs import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.configBuilder.events.EventConfigBuilderUpdateGui -import info.nightscout.androidaps.utils.alertDialogs.OKDialog.showConfirmation +import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.sharedPreferences.SP import java.util.* @@ -25,7 +26,8 @@ class ConfigBuilderPlugin @Inject constructor( resourceHelper: ResourceHelper, private val sp: SP, private val rxBus: RxBusWrapper, - private val activePlugin: ActivePluginProvider + private val activePlugin: ActivePluginProvider, + private val uel: UserEntryLogger ) : PluginBase(PluginDescription() .mainType(PluginType.GENERAL) .fragmentClass(ConfigBuilderFragment::class.java.name) @@ -137,9 +139,10 @@ class ConfigBuilderPlugin @Inject constructor( if (allowHardwarePump || activity == null) { performPluginSwitch(changedPlugin, newState, type) } else { - showConfirmation(activity, resourceHelper.gs(R.string.allow_hardware_pump_text), Runnable { + OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.allow_hardware_pump_text), Runnable { performPluginSwitch(changedPlugin, newState, type) sp.putBoolean("allow_hardware_pump", true) + uel.log("HW PUMP ALLOWED") aapsLogger.debug(LTag.PUMP, "First time HW pump allowed!") }, Runnable { rxBus.send(EventConfigBuilderUpdateGui()) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt index 69a8892bdc..63c1719a45 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesFragment.kt @@ -9,8 +9,6 @@ import android.view.Gravity import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.Button -import android.widget.EditText import android.widget.LinearLayout import android.widget.TextView import androidx.appcompat.app.AppCompatActivity @@ -19,11 +17,14 @@ import androidx.recyclerview.widget.LinearSmoothScroller import androidx.recyclerview.widget.RecyclerView import dagger.android.support.DaggerFragment import info.nightscout.androidaps.R -import info.nightscout.androidaps.logging.AAPSLogger -import info.nightscout.androidaps.plugins.bus.RxBusWrapper -import info.nightscout.androidaps.plugins.constraints.objectives.activities.ObjectivesExamDialog +import info.nightscout.androidaps.databinding.ObjectivesFragmentBinding +import info.nightscout.androidaps.databinding.ObjectivesItemBinding import info.nightscout.androidaps.dialogs.NtpProgressDialog import info.nightscout.androidaps.events.EventNtpStatus +import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.UserEntryLogger +import info.nightscout.androidaps.plugins.bus.RxBusWrapper +import info.nightscout.androidaps.plugins.constraints.objectives.activities.ObjectivesExamDialog import info.nightscout.androidaps.plugins.constraints.objectives.events.EventObjectivesUpdateGui import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective.ExamTask import info.nightscout.androidaps.receivers.ReceiverStatusStore @@ -31,19 +32,20 @@ import info.nightscout.androidaps.setupwizard.events.EventSWUpdate import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.HtmlHelper -import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.SntpClient -import info.nightscout.androidaps.utils.extensions.plusAssign +import info.nightscout.androidaps.utils.alertDialogs.OKDialog +import io.reactivex.rxkotlin.plusAssign import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.sharedPreferences.SP -import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable -import kotlinx.android.synthetic.main.objectives_fragment.* import javax.inject.Inject class ObjectivesFragment : DaggerFragment() { + @Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var sp: SP @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var fabricPrivacy: FabricPrivacy @@ -51,6 +53,7 @@ class ObjectivesFragment : DaggerFragment() { @Inject lateinit var receiverStatusStore: ReceiverStatusStore @Inject lateinit var dateUtil: DateUtil @Inject lateinit var sntpClient: SntpClient + @Inject lateinit var uel: UserEntryLogger private val objectivesAdapter = ObjectivesAdapter() private val handler = Handler(Looper.getMainLooper()) @@ -64,19 +67,23 @@ class ObjectivesFragment : DaggerFragment() { } } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.objectives_fragment, container, false) - } + private var _binding: ObjectivesFragmentBinding? = null + + // This property is only valid between onCreateView and + // onDestroyView. + private val binding get() = _binding!! + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = + ObjectivesFragmentBinding.inflate(inflater, container, false).also { _binding = it }.root override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - objectives_recyclerview.layoutManager = LinearLayoutManager(view.context) - objectives_recyclerview.adapter = objectivesAdapter - objectives_fake.setOnClickListener { updateGUI() } - objectives_reset.setOnClickListener { + binding.recyclerview.layoutManager = LinearLayoutManager(view.context) + binding.recyclerview.adapter = objectivesAdapter + binding.fake.setOnClickListener { updateGUI() } + binding.reset.setOnClickListener { objectivesPlugin.reset() - objectives_recyclerview.adapter?.notifyDataSetChanged() + binding.recyclerview.adapter?.notifyDataSetChanged() scrollToCurrentObjective() } scrollToCurrentObjective() @@ -88,11 +95,10 @@ class ObjectivesFragment : DaggerFragment() { super.onResume() disposable += rxBus .toObservable(EventObjectivesUpdateGui::class.java) - .observeOn(AndroidSchedulers.mainThread()) + .observeOn(aapsSchedulers.main) .subscribe({ - objectives_recyclerview.adapter?.notifyDataSetChanged() - }, { fabricPrivacy.logException(it) } - ) + binding.recyclerview.adapter?.notifyDataSetChanged() + }, fabricPrivacy::logException) } @Synchronized @@ -105,6 +111,7 @@ class ObjectivesFragment : DaggerFragment() { override fun onDestroyView() { super.onDestroyView() handler.removeCallbacks(objectiveUpdater) + _binding = null } private fun startUpdateTimer() { @@ -129,7 +136,7 @@ class ObjectivesFragment : DaggerFragment() { override fun calculateTimeForScrolling(dx: Int): Int = super.calculateTimeForScrolling(dx) * 4 } smoothScroller.targetPosition = i - objectives_recyclerview.layoutManager?.startSmoothScroll(smoothScroller) + binding.recyclerview.layoutManager?.startSmoothScroll(smoothScroller) } break } @@ -145,67 +152,66 @@ class ObjectivesFragment : DaggerFragment() { override fun onBindViewHolder(holder: ViewHolder, position: Int) { val objective = objectivesPlugin.objectives[position] - holder.title.text = resourceHelper.gs(R.string.nth_objective, position + 1) + holder.binding.title.text = resourceHelper.gs(R.string.nth_objective, position + 1) if (objective.objective != 0) { - holder.objective.visibility = View.VISIBLE - holder.objective.text = resourceHelper.gs(objective.objective) + holder.binding.objective.visibility = View.VISIBLE + holder.binding.objective.text = resourceHelper.gs(objective.objective) } else - holder.objective.visibility = View.GONE + holder.binding.objective.visibility = View.GONE if (objective.gate != 0) { - holder.gate.visibility = View.VISIBLE - holder.gate.text = resourceHelper.gs(objective.gate) + holder.binding.gate.visibility = View.VISIBLE + holder.binding.gate.text = resourceHelper.gs(objective.gate) } else - holder.gate.visibility = View.GONE + holder.binding.gate.visibility = View.GONE if (!objective.isStarted) { - holder.gate.setTextColor(-0x1) - holder.verify.visibility = View.GONE - holder.progress.visibility = View.GONE - holder.accomplished.visibility = View.GONE - holder.unFinish.visibility = View.GONE - holder.unStart.visibility = View.GONE + holder.binding.gate.setTextColor(-0x1) + holder.binding.verify.visibility = View.GONE + holder.binding.progress.visibility = View.GONE + holder.binding.accomplished.visibility = View.GONE + holder.binding.unfinish.visibility = View.GONE + holder.binding.unstart.visibility = View.GONE if (position == 0 || objectivesPlugin.allPriorAccomplished(position)) - holder.start.visibility = View.VISIBLE + holder.binding.start.visibility = View.VISIBLE else - holder.start.visibility = View.GONE + holder.binding.start.visibility = View.GONE } else if (objective.isAccomplished) { - holder.gate.setTextColor(-0xb350b0) - holder.verify.visibility = View.GONE - holder.progress.visibility = View.GONE - holder.start.visibility = View.GONE - holder.accomplished.visibility = View.VISIBLE - holder.unFinish.visibility = View.VISIBLE - holder.unStart.visibility = View.GONE + holder.binding.gate.setTextColor(-0xb350b0) + holder.binding.verify.visibility = View.GONE + holder.binding.progress.visibility = View.GONE + holder.binding.start.visibility = View.GONE + holder.binding.accomplished.visibility = View.VISIBLE + holder.binding.unfinish.visibility = View.VISIBLE + holder.binding.unstart.visibility = View.GONE } else if (objective.isStarted) { - holder.gate.setTextColor(-0x1) - holder.verify.visibility = View.VISIBLE - holder.verify.isEnabled = objective.isCompleted || objectives_fake.isChecked - holder.start.visibility = View.GONE - holder.accomplished.visibility = View.GONE - holder.unFinish.visibility = View.GONE - holder.unStart.visibility = View.VISIBLE - holder.progress.visibility = View.VISIBLE - holder.progress.removeAllViews() + holder.binding.gate.setTextColor(-0x1) + holder.binding.verify.visibility = View.VISIBLE + holder.binding.verify.isEnabled = objective.isCompleted || binding.fake.isChecked + holder.binding.start.visibility = View.GONE + holder.binding.accomplished.visibility = View.GONE + holder.binding.unfinish.visibility = View.GONE + holder.binding.unstart.visibility = View.VISIBLE + holder.binding.progress.visibility = View.VISIBLE + holder.binding.progress.removeAllViews() for (task in objective.tasks) { if (task.shouldBeIgnored()) continue // name - val name = TextView(holder.progress.context) - @Suppress("SetTextlI8n") - name.text = resourceHelper.gs(task.task) + ":" + val name = TextView(holder.binding.progress.context) + name.text = "${resourceHelper.gs(task.task)}:" name.setTextColor(-0x1) - holder.progress.addView(name, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT) + holder.binding.progress.addView(name, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT) // hint task.hints.forEach { h -> - if (!task.isCompleted) - holder.progress.addView(h.generate(context)) + if (!task.isCompleted()) + context?.let { holder.binding.progress.addView(h.generate(it)) } } // state - val state = TextView(holder.progress.context) + val state = TextView(holder.binding.progress.context) state.setTextColor(-0x1) val basicHTML = "%2\$s" - val formattedHTML = String.format(basicHTML, if (task.isCompleted) "#4CAF50" else "#FF9800", task.progress) + val formattedHTML = String.format(basicHTML, if (task.isCompleted()) "#4CAF50" else "#FF9800", task.progress) state.text = HtmlHelper.fromHtml(formattedHTML) state.gravity = Gravity.END - holder.progress.addView(state, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT) + holder.binding.progress.addView(state, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT) if (task is ExamTask) { state.setOnClickListener { val dialog = ObjectivesExamDialog() @@ -218,16 +224,16 @@ class ObjectivesFragment : DaggerFragment() { } } // horizontal line - val separator = View(holder.progress.context) + val separator = View(holder.binding.progress.context) separator.setBackgroundColor(Color.DKGRAY) - holder.progress.addView(separator, LinearLayout.LayoutParams.MATCH_PARENT, 2) + holder.binding.progress.addView(separator, LinearLayout.LayoutParams.MATCH_PARENT, 2) } } - holder.accomplished.text = resourceHelper.gs(R.string.accomplished, dateUtil.dateAndTimeString(objective.accomplishedOn)) - holder.accomplished.setTextColor(-0x3e3e3f) - holder.verify.setOnClickListener { + holder.binding.accomplished.text = resourceHelper.gs(R.string.accomplished, dateUtil.dateAndTimeString(objective.accomplishedOn)) + holder.binding.accomplished.setTextColor(-0x3e3e3f) + holder.binding.verify.setOnClickListener { receiverStatusStore.updateNetworkStatus() - if (objectives_fake.isChecked) { + if (binding.fake.isChecked) { objective.accomplishedOn = DateUtil.now() scrollToCurrentObjective() startUpdateTimer() @@ -264,9 +270,9 @@ class ObjectivesFragment : DaggerFragment() { }.start() } } - holder.start.setOnClickListener { + holder.binding.start.setOnClickListener { receiverStatusStore.updateNetworkStatus() - if (objectives_fake.isChecked) { + if (binding.fake.isChecked) { objective.startedOn = DateUtil.now() scrollToCurrentObjective() startUpdateTimer() @@ -298,9 +304,10 @@ class ObjectivesFragment : DaggerFragment() { }, receiverStatusStore.isConnected) }.start() } - holder.unStart.setOnClickListener { + holder.binding.unstart.setOnClickListener { activity?.let { activity -> OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.objectives), resourceHelper.gs(R.string.doyouwantresetstart), Runnable { + uel.log("OBJECTIVE UNSTARTED", i1 = position + 1) objective.startedOn = 0 scrollToCurrentObjective() rxBus.send(EventObjectivesUpdateGui()) @@ -308,7 +315,7 @@ class ObjectivesFragment : DaggerFragment() { }) } } - holder.unFinish.setOnClickListener { + holder.binding.unfinish.setOnClickListener { objective.accomplishedOn = 0 scrollToCurrentObjective() rxBus.send(EventObjectivesUpdateGui()) @@ -318,21 +325,21 @@ class ObjectivesFragment : DaggerFragment() { // generate random request code if none exists val request = sp.getString(R.string.key_objectives_request_code, String.format("%1$05d", (Math.random() * 99999).toInt())) sp.putString(R.string.key_objectives_request_code, request) - holder.requestCode.text = resourceHelper.gs(R.string.requestcode, request) - holder.requestCode.visibility = View.VISIBLE - holder.enterButton.visibility = View.VISIBLE - holder.input.visibility = View.VISIBLE - holder.inputHint.visibility = View.VISIBLE - holder.enterButton.setOnClickListener { - val input = holder.input.text.toString() - objective.specialAction(activity, input) + holder.binding.requestcode.text = resourceHelper.gs(R.string.requestcode, request) + holder.binding.requestcode.visibility = View.VISIBLE + holder.binding.enterbutton.visibility = View.VISIBLE + holder.binding.input.visibility = View.VISIBLE + holder.binding.inputhint.visibility = View.VISIBLE + holder.binding.enterbutton.setOnClickListener { + val input = holder.binding.input.text.toString() + activity?.let { activity -> objective.specialAction(activity, input) } rxBus.send(EventObjectivesUpdateGui()) } } else { - holder.enterButton.visibility = View.GONE - holder.input.visibility = View.GONE - holder.inputHint.visibility = View.GONE - holder.requestCode.visibility = View.GONE + holder.binding.enterbutton.visibility = View.GONE + holder.binding.input.visibility = View.GONE + holder.binding.inputhint.visibility = View.GONE + holder.binding.requestcode.visibility = View.GONE } } @@ -340,20 +347,9 @@ class ObjectivesFragment : DaggerFragment() { return objectivesPlugin.objectives.size } - inner class ViewHolder internal constructor(itemView: View) : RecyclerView.ViewHolder(itemView) { - val title: TextView = itemView.findViewById(R.id.objective_title) - val objective: TextView = itemView.findViewById(R.id.objective_objective) - val gate: TextView = itemView.findViewById(R.id.objective_gate) - val accomplished: TextView = itemView.findViewById(R.id.objective_accomplished) - val progress: LinearLayout = itemView.findViewById(R.id.objective_progress) - val verify: Button = itemView.findViewById(R.id.objective_verify) - val start: Button = itemView.findViewById(R.id.objective_start) - val unFinish: Button = itemView.findViewById(R.id.objective_unfinish) - val unStart: Button = itemView.findViewById(R.id.objective_unstart) - val inputHint: TextView = itemView.findViewById(R.id.objective_inputhint) - val input: EditText = itemView.findViewById(R.id.objective_input) - val enterButton: Button = itemView.findViewById(R.id.objective_enterbutton) - val requestCode: TextView = itemView.findViewById(R.id.objective_requestcode) + inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + + val binding = ObjectivesItemBinding.bind(itemView) } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt index 009812a1f2..c5b836ac71 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/ObjectivesPlugin.kt @@ -9,6 +9,7 @@ import info.nightscout.androidaps.Config import info.nightscout.androidaps.R import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.logging.AAPSLogger +import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.constraints.objectives.objectives.* import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.alertDialogs.OKDialog @@ -25,8 +26,8 @@ class ObjectivesPlugin @Inject constructor( resourceHelper: ResourceHelper, private val activePlugin: ActivePluginProvider, private val sp: SP, - private val config: Config - + config: Config, + private val uel: UserEntryLogger ) : PluginBase(PluginDescription() .mainType(PluginType.CONSTRAINTS) .fragmentClass(ObjectivesFragment::class.qualifiedName) @@ -141,6 +142,7 @@ class ObjectivesPlugin @Inject constructor( sp.putLong("Objectives_" + "auto" + "_accomplished", DateUtil.now()) setupObjectives() OKDialog.show(activity, resourceHelper.gs(R.string.objectives), resourceHelper.gs(R.string.codeaccepted)) + uel.log("OBJECTIVES SKIPPED") } else { OKDialog.show(activity, resourceHelper.gs(R.string.objectives), resourceHelper.gs(R.string.codeinvalid)) } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/activities/ObjectivesExamDialog.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/activities/ObjectivesExamDialog.kt index e44135eb45..c45d61bfd4 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/activities/ObjectivesExamDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/activities/ObjectivesExamDialog.kt @@ -6,6 +6,7 @@ import android.view.View import android.view.ViewGroup import dagger.android.support.DaggerDialogFragment import info.nightscout.androidaps.R +import info.nightscout.androidaps.databinding.ObjectivesExamFragmentBinding import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.constraints.objectives.events.EventObjectivesUpdateGui import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective @@ -15,28 +16,36 @@ import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.resources.ResourceHelper -import kotlinx.android.synthetic.main.objectives_exam_fragment.* import javax.inject.Inject class ObjectivesExamDialog : DaggerDialogFragment() { + @Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var dateUtil: DateUtil companion object { + var objective: Objective? = null } private var currentTask = 0 + private var _binding: ObjectivesExamFragmentBinding? = null + + // This property is only valid between onCreateView and + // onDestroyView. + private val binding get() = _binding!! + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { + savedInstanceState: Bundle?): View { // load data from bundle (savedInstanceState ?: arguments)?.let { bundle -> currentTask = bundle.getInt("currentTask", 0) } - return inflater.inflate(R.layout.objectives_exam_fragment, container, false) + _binding = ObjectivesExamFragmentBinding.inflate(inflater, container, false) + return binding.root } override fun onStart() { @@ -55,36 +64,46 @@ class ObjectivesExamDialog : DaggerDialogFragment() { bundle.putInt("currentTask", currentTask) } + @Synchronized + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + @Synchronized fun updateGui() { + if (_binding == null) return objective?.let { objective -> val task: ExamTask = objective.tasks[currentTask] as ExamTask - objectives_exam_name.setText(task.task) - objectives_exam_question.setText(task.question) + binding.examName.setText(task.task) + binding.examQuestion.setText(task.question) // Options - objectives_exam_options.removeAllViews() + binding.examOptions.removeAllViews() task.options.forEach { - val cb = it.generate(context) - if (task.answered) { - cb.isEnabled = false - if (it.isCorrect) - cb.isChecked = true + context?.let { context -> + val cb = it.generate(context) + if (task.answered) { + cb.isEnabled = false + if (it.isCorrect) + cb.isChecked = true + } + binding.examOptions.addView(cb) } - objectives_exam_options.addView(cb) } // Hints - objectives_exam_hints.removeAllViews() + binding.examHints.removeAllViews() for (h in task.hints) { - objectives_exam_hints.addView(h.generate(context)) + context?.let { binding.examHints.addView(h.generate(it)) } } // Disabled to - objectives_exam_disabledto.text = resourceHelper.gs(R.string.answerdisabledto, dateUtil.timeString(task.disabledTo)) - objectives_exam_disabledto.visibility = if (task.isEnabledAnswer) View.GONE else View.VISIBLE + binding.examDisabledto.text = resourceHelper.gs(R.string.answerdisabledto, dateUtil.timeString(task.disabledTo)) + binding.examDisabledto.visibility = if (task.isEnabledAnswer()) View.GONE else View.VISIBLE // Buttons - objectives_exam_verify.isEnabled = !task.answered && task.isEnabledAnswer - objectives_exam_verify.setOnClickListener { + binding.examVerify.isEnabled = !task.answered && task.isEnabledAnswer() + binding.examVerify.setOnClickListener { var result = true for (o in task.options) { - val option: Option = o as Option + val option: Option = o result = result && option.evaluate() } task.answered = result @@ -95,35 +114,35 @@ class ObjectivesExamDialog : DaggerDialogFragment() { updateGui() rxBus.send(EventObjectivesUpdateGui()) } - close.setOnClickListener { dismiss() } - objectives_exam_reset.setOnClickListener { + binding.close.setOnClickListener { dismiss() } + binding.examReset.setOnClickListener { task.answered = false //task.disabledTo = 0 updateGui() rxBus.send(EventObjectivesUpdateGui()) } - objectives_back_button.isEnabled = currentTask != 0 - objectives_back_button.setOnClickListener { + binding.backButton.isEnabled = currentTask != 0 + binding.backButton.setOnClickListener { currentTask-- updateGui() } - objectives_next_button.isEnabled = currentTask != objective.tasks.size - 1 - objectives_next_button.setOnClickListener { + binding.nextButton.isEnabled = currentTask != objective.tasks.size - 1 + binding.nextButton.setOnClickListener { currentTask++ updateGui() } - objectives_next_unanswered_button.isEnabled = !objective.isCompleted - objectives_next_unanswered_button.setOnClickListener { + binding.nextUnansweredButton.isEnabled = !objective.isCompleted + binding.nextUnansweredButton.setOnClickListener { for (i in (currentTask + 1) until objective.tasks.size) { - if (!objective.tasks[i].isCompleted) { + if (!objective.tasks[i].isCompleted()) { currentTask = i updateGui() return@setOnClickListener } } for (i in 0..currentTask) { - if (!objective.tasks[i].isCompleted) { + if (!objective.tasks[i].isCompleted()) { currentTask = i updateGui() return@setOnClickListener diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective.java b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective.java deleted file mode 100644 index feef4a4222..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/objectives/objectives/Objective.java +++ /dev/null @@ -1,296 +0,0 @@ -package info.nightscout.androidaps.plugins.constraints.objectives.objectives; - -import android.app.Activity; -import android.content.Context; -import android.graphics.Color; -import android.text.util.Linkify; -import android.widget.CheckBox; -import android.widget.TextView; - -import androidx.annotation.StringRes; -import androidx.fragment.app.FragmentActivity; - -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; - -import dagger.android.HasAndroidInjector; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.utils.DateUtil; -import info.nightscout.androidaps.utils.T; -import info.nightscout.androidaps.utils.resources.ResourceHelper; -import info.nightscout.androidaps.utils.sharedPreferences.SP; - -public abstract class Objective { - @Inject public SP sp; - @Inject public ResourceHelper resourceHelper; - - private final String spName; - @StringRes private final int objective; - @StringRes private final int gate; - private long startedOn; - private long accomplishedOn; - List tasks = new ArrayList<>(); - public boolean hasSpecialInput = false; - - public Objective(HasAndroidInjector injector, String spName, @StringRes int objective, @StringRes int gate) { - injector.androidInjector().inject(this); - this.spName = spName; - this.objective = objective; - this.gate = gate; - startedOn = sp.getLong("Objectives_" + spName + "_started", 0L); - accomplishedOn = sp.getLong("Objectives_" + spName + "_accomplished", 0L); - if ((accomplishedOn - DateUtil.now()) > T.hours(3).msecs() || (startedOn - DateUtil.now()) > T.hours(3).msecs()) { // more than 3 hours in the future - startedOn = 0; - accomplishedOn = 0; - } - setupTasks(tasks); - for (Task task : tasks) task.objective = this; - } - - public boolean isCompleted() { - for (Task task : tasks) { - if (!task.shouldBeIgnored() && !task.isCompleted()) - return false; - } - return true; - } - - public boolean isCompleted(long trueTime) { - for (Task task : tasks) { - if (!task.shouldBeIgnored() && !task.isCompleted(trueTime)) - return false; - } - return true; - } - - public boolean isAccomplished() { - return accomplishedOn != 0 && accomplishedOn < DateUtil.now(); - } - - public boolean isStarted() { - return startedOn != 0; - } - - public long getStartedOn() { - return startedOn; - } - - public int getObjective() { - return objective; - } - - public int getGate() { - return gate; - } - - public void setStartedOn(long startedOn) { - this.startedOn = startedOn; - sp.putLong("Objectives_" + spName + "_started", startedOn); - } - - public void setAccomplishedOn(long accomplishedOn) { - this.accomplishedOn = accomplishedOn; - sp.putLong("Objectives_" + spName + "_accomplished", accomplishedOn); - } - - public long getAccomplishedOn() { - return accomplishedOn; - } - - protected void setupTasks(List tasks) { - - } - - public List getTasks() { - return tasks; - } - - public boolean specialActionEnabled() { - return true; - } - - public void specialAction(FragmentActivity activity, String input) { - } - - public abstract class Task { - @StringRes - private final int task; - private Objective objective; - ArrayList hints = new ArrayList<>(); - - public Task(@StringRes int task) { - this.task = task; - } - - public @StringRes int getTask() { - return task; - } - - protected Objective getObjective() { - return objective; - } - - public abstract boolean isCompleted(); - - public boolean isCompleted(long trueTime) { - return isCompleted(); - } - - public String getProgress() { - return resourceHelper.gs(isCompleted() ? R.string.completed_well_done : R.string.not_completed_yet); - } - - Task hint(Hint hint) { - hints.add(hint); - return this; - } - - public ArrayList getHints() { - return hints; - } - - public boolean shouldBeIgnored() { - return false; - } - } - - public class MinimumDurationTask extends Task { - - private final long minimumDuration; - - MinimumDurationTask(long minimumDuration) { - super(R.string.time_elapsed); - this.minimumDuration = minimumDuration; - } - - @Override - public boolean isCompleted() { - return getObjective().isStarted() && System.currentTimeMillis() - getObjective().getStartedOn() >= minimumDuration; - } - - @Override - public boolean isCompleted(long trueTime) { - return getObjective().isStarted() && trueTime - getObjective().getStartedOn() >= minimumDuration; - } - - @Override - public String getProgress() { - return getDurationText(System.currentTimeMillis() - getObjective().getStartedOn()) - + " / " + getDurationText(minimumDuration); - } - - private String getDurationText(long duration) { - int days = (int) Math.floor((double) duration / T.days(1).msecs()); - int hours = (int) Math.floor((double) duration / T.hours(1).msecs()); - int minutes = (int) Math.floor((double) duration / T.mins(1).msecs()); - if (days > 0) return resourceHelper.gq(R.plurals.objective_days, days, days); - else if (hours > 0) return resourceHelper.gq(R.plurals.objective_hours, hours, hours); - else return resourceHelper.gq(R.plurals.objective_minutes, minutes, minutes); - } - } - - public class ExamTask extends Task { - @StringRes - int question; - ArrayList