This commit is contained in:
Milos Kozak 2021-02-15 12:54:43 +01:00
commit 5c910306c6
1142 changed files with 41874 additions and 23554 deletions

8
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,8 @@
version: 2
updates:
- package-ecosystem: gradle
directory: "/"
schedule:
interval: daily
open-pull-requests-limit: 10
target-branch: dev

1
.gitignore vendored
View file

@ -2,6 +2,7 @@
.gradle .gradle
/local.properties /local.properties
.DS_Store .DS_Store
app/jacoco.exec
/build /build
/captures /captures
*.apk *.apk

View file

@ -36,7 +36,7 @@ Translations
------------ ------------
* If possible, always use Android translation mechanism (with strings.xml and @strings/id) instead of hardcoded texts * 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 Hints
----- -----

View file

@ -1,6 +1,5 @@
buildscript { buildscript {
repositories { repositories {
jcenter()
maven { url "https://plugins.gradle.org/m2/" } // jacoco 0.2 maven { url "https://plugins.gradle.org/m2/" } // jacoco 0.2
} }
@ -11,7 +10,6 @@ buildscript {
} }
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-kapt'
apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.gms.google-services'
//apply plugin: 'jacoco-android' //apply plugin: 'jacoco-android'
@ -23,7 +21,6 @@ jacoco {
} }
repositories { repositories {
jcenter { url "https://jcenter.bintray.com/" }
mavenCentral() mavenCentral()
google() google()
} }
@ -87,7 +84,7 @@ def gitAvailable = { ->
} }
def allCommited = { -> def allCommitted = { ->
StringBuilder stringBuilder = new StringBuilder() StringBuilder stringBuilder = new StringBuilder()
try { try {
def stdout = new ByteArrayOutputStream() def stdout = new ByteArrayOutputStream()
@ -116,15 +113,16 @@ android {
ndkVersion "21.1.6352462" ndkVersion "21.1.6352462"
defaultConfig { defaultConfig {
minSdkVersion 24 minSdkVersion 26
targetSdkVersion 28 targetSdkVersion 28
multiDexEnabled true multiDexEnabled true
versionCode 1500 versionCode 1500
version "2.7.2-dev" version "2.8.2.1-dev-a"
buildConfigField "String", "VERSION", '"' + version + '"' buildConfigField "String", "VERSION", '"' + version + '"'
buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"' buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"'
buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"' buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"'
buildConfigField "String", "HEAD", '"' + generateGitBuild() + '"' buildConfigField "String", "HEAD", '"' + generateGitBuild() + '"'
buildConfigField "String", "COMMITTED", '"' + allCommitted() + '"'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
// if you change minSdkVersion to less than 11, you need to change executeTask for wear // if you change minSdkVersion to less than 11, you need to change executeTask for wear
@ -156,8 +154,8 @@ android {
ext.enableCrashlytics = false ext.enableCrashlytics = false
} }
} }
flavorDimensions "standard"
productFlavors { productFlavors {
flavorDimensions "standard"
full { full {
applicationId "info.nightscout.androidaps" applicationId "info.nightscout.androidaps"
dimension "standard" dimension "standard"
@ -218,9 +216,6 @@ android {
useLibrary "org.apache.http.legacy" useLibrary "org.apache.http.legacy"
configurations.all {
resolutionStrategy.force 'com.google.code.findbugs:jsr305:1.3.9'
}
} }
allprojects { allprojects {
@ -235,34 +230,34 @@ dependencies {
wearApp project(':wear') wearApp project(':wear')
implementation project(':core') implementation project(':core')
implementation project(':database')
implementation project(':dana') implementation project(':dana')
implementation project(':danars') implementation project(':danars')
implementation project(':danar') implementation project(':danar')
implementation project(':rileylink') implementation project(':rileylink')
implementation project(':medtronic') implementation project(':medtronic')
implementation project(':omnipod') implementation project(':omnipod-eros')
implementation project(':omnipod-dash')
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
testImplementation "junit:junit:$junit_version" testImplementation "junit:junit:$junit_version"
testImplementation 'org.json:json:20200518' testImplementation 'org.json:json:20201115'
testImplementation "org.mockito:mockito-core:${mockitoVersion}" testImplementation "org.mockito:mockito-core:${mockitoVersion}"
testImplementation "org.powermock:powermock-api-mockito2:${powermockVersion}" testImplementation "org.powermock:powermock-api-mockito2:${powermockVersion}"
testImplementation "org.powermock:powermock-module-junit4-rule-agent:${powermockVersion}" testImplementation "org.powermock:powermock-module-junit4-rule-agent:${powermockVersion}"
testImplementation "org.powermock:powermock-module-junit4-rule:${powermockVersion}" testImplementation "org.powermock:powermock-module-junit4-rule:${powermockVersion}"
testImplementation "org.powermock:powermock-module-junit4:${powermockVersion}" testImplementation "org.powermock:powermock-module-junit4:${powermockVersion}"
testImplementation 'joda-time:joda-time:2.10.6' testImplementation "joda-time:joda-time:$jodatime_version"
testImplementation('com.google.truth:truth:1.0.1') { testImplementation('com.google.truth:truth:1.1.2') {
exclude group: "com.google.guava", module: "guava" exclude group: "com.google.guava", module: "guava"
exclude group: "com.google.code.findbugs", module: "jsr305"
} }
testImplementation "org.skyscreamer:jsonassert:1.5.0" testImplementation "org.skyscreamer:jsonassert:1.5.0"
testImplementation "org.hamcrest:hamcrest-all:1.3" 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.ext:junit:$androidx_junit"
androidTestImplementation "androidx.test:rules:$androidx_rules" 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 /* Dagger2 - We are going to use dagger.android which includes
* support for Activity and fragment injection so we need to include * support for Activity and fragment injection so we need to include
@ -314,12 +309,12 @@ tasks.whenTaskAdded { task ->
printf('--------------\n') printf('--------------\n')
printf('isMaster: %s\n', isMaster().toString()) printf('isMaster: %s\n', isMaster().toString())
printf('gitAvailable: %s\n', gitAvailable().toString()) printf('gitAvailable: %s\n', gitAvailable().toString())
printf('allCommited: %s\n', allCommited().toString()) printf('allCommitted: %s\n', allCommitted().toString())
printf('--------------\n') printf('--------------\n')
if (isMaster() && !gitAvailable()) { 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') 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') throw new GradleException('There are uncommitted changes. Clone sources again as described in wiki and do not allow gradle update')
} }

Binary file not shown.

View file

@ -135,10 +135,6 @@
android:resource="@xml/filepaths" /> android:resource="@xml/filepaths" />
</provider> </provider>
<!-- Service processing incomming data -->
<service
android:name=".services.DataService"
android:exported="false" />
<service <service
android:name=".services.LocationService" android:name=".services.LocationService"
android:exported="false" /> android:exported="false" />

View file

@ -151,7 +151,7 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_
} }
} }
//cherry pick from oref upstream dev cb8e94990301277fb1016c778b4e9efa55a6edbc //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 if (currenttemp.rate > basal) { // high temp is running
rT.reason += ". Replacing high temp basal of "+currenttemp.rate+" with neutral temp of "+basal; rT.reason += ". Replacing high temp basal of "+currenttemp.rate+" with neutral temp of "+basal;
rT.deliverAt = deliverAt; rT.deliverAt = deliverAt;

View file

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

View file

@ -1,15 +1,21 @@
package info.nightscout.androidaps package info.nightscout.androidaps
import android.os.Build
import info.nightscout.androidaps.interfaces.ConfigInterface import info.nightscout.androidaps.interfaces.ConfigInterface
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
class Config @Inject constructor(): ConfigInterface{ class Config @Inject constructor() : ConfigInterface {
override val SUPPORTEDNSVERSION = 1002 // 0.10.00 override val SUPPORTEDNSVERSION = 1002 // 0.10.00
override val APS = BuildConfig.FLAVOR == "full" override val APS = BuildConfig.FLAVOR == "full"
override val NSCLIENT = BuildConfig.FLAVOR == "nsclient" || BuildConfig.FLAVOR == "nsclient2" override val NSCLIENT = BuildConfig.FLAVOR == "nsclient" || BuildConfig.FLAVOR == "nsclient2"
override val PUMPCONTROL = BuildConfig.FLAVOR == "pumpcontrol" override val PUMPCONTROL = BuildConfig.FLAVOR == "pumpcontrol"
override val PUMPDRIVERS = BuildConfig.FLAVOR == "full" || 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 + ")"
} }

View file

@ -2,7 +2,6 @@ package info.nightscout.androidaps
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Rect import android.graphics.Rect
import android.os.Bundle import android.os.Bundle
import android.os.PersistableBundle import android.os.PersistableBundle
@ -22,9 +21,9 @@ import android.widget.TextView
import androidx.appcompat.app.ActionBarDrawerToggle import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.core.app.ActivityCompat
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.tabs.TabLayoutMediator import com.google.android.material.tabs.TabLayoutMediator
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.joanzapata.iconify.Iconify import com.joanzapata.iconify.Iconify
import com.joanzapata.iconify.fonts.FontAwesomeModule import com.joanzapata.iconify.fonts.FontAwesomeModule
import dev.doubledot.doki.ui.DokiActivity 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.ProfileHelperActivity
import info.nightscout.androidaps.activities.SingleFragmentActivity import info.nightscout.androidaps.activities.SingleFragmentActivity
import info.nightscout.androidaps.activities.StatsActivity import info.nightscout.androidaps.activities.StatsActivity
import info.nightscout.androidaps.databinding.ActivityMainBinding
import info.nightscout.androidaps.events.EventAppExit import info.nightscout.androidaps.events.EventAppExit
import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventRebuildTabs 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.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.constraints.signatureVerifier.SignatureVerifierPlugin import info.nightscout.androidaps.plugins.constraints.signatureVerifier.SignatureVerifierPlugin
import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerUtils 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.nsclient.data.NSSettingsStatus
import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin import info.nightscout.androidaps.plugins.general.smsCommunicator.SmsCommunicatorPlugin
import info.nightscout.androidaps.setupwizard.SetupWizardActivity 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.locale.LocaleHelper
import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.resources.IconsProvider 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.sharedPreferences.SP
import info.nightscout.androidaps.utils.tabs.TabPageAdapter import info.nightscout.androidaps.utils.tabs.TabPageAdapter
import info.nightscout.androidaps.utils.ui.UIRunnable import info.nightscout.androidaps.utils.ui.UIRunnable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.activity_main.*
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
import kotlin.system.exitProcess import kotlin.system.exitProcess
@ -75,10 +71,10 @@ class MainActivity : NoSplashAppCompatActivity() {
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
@Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var androidPermission: AndroidPermission @Inject lateinit var androidPermission: AndroidPermission
@Inject lateinit var sp: SP @Inject lateinit var sp: SP
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var versionCheckerUtils: VersionCheckerUtils @Inject lateinit var versionCheckerUtils: VersionCheckerUtils
@Inject lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin @Inject lateinit var smsCommunicatorPlugin: SmsCommunicatorPlugin
@Inject lateinit var loopPlugin: LoopPlugin @Inject lateinit var loopPlugin: LoopPlugin
@ -91,40 +87,36 @@ class MainActivity : NoSplashAppCompatActivity() {
@Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var signatureVerifierPlugin: SignatureVerifierPlugin @Inject lateinit var signatureVerifierPlugin: SignatureVerifierPlugin
@Inject lateinit var config: Config @Inject lateinit var config: Config
@Inject lateinit var importExportPrefs: ImportExportPrefs
private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle
private var pluginPreferencesMenuItem: MenuItem? = null private var pluginPreferencesMenuItem: MenuItem? = null
private var menu: Menu? = null private var menu: Menu? = null
val callForPrefFile = registerForActivityResult(PrefsFileContract()) { private lateinit var binding: ActivityMainBinding
it?.let {
importExportPrefs.importSharedPreferences(this, it)
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
Iconify.with(FontAwesomeModule()) Iconify.with(FontAwesomeModule())
LocaleHelper.update(applicationContext) LocaleHelper.update(applicationContext)
setContentView(R.layout.activity_main) binding = ActivityMainBinding.inflate(layoutInflater)
setSupportActionBar(toolbar) setContentView(binding.root)
setSupportActionBar(binding.toolbar)
supportActionBar?.setDisplayShowTitleEnabled(false) supportActionBar?.setDisplayShowTitleEnabled(false)
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setHomeButtonEnabled(true) supportActionBar?.setHomeButtonEnabled(true)
actionBarDrawerToggle = ActionBarDrawerToggle(this, main_drawer_layout, R.string.open_navigation, R.string.close_navigation).also { actionBarDrawerToggle = ActionBarDrawerToggle(this, binding.mainDrawerLayout, R.string.open_navigation, R.string.close_navigation).also {
main_drawer_layout.addDrawerListener(it) binding.mainDrawerLayout.addDrawerListener(it)
it.syncState() it.syncState()
} }
// initialize screen wake lock // initialize screen wake lock
processPreferenceChange(EventPreferenceChange(resourceHelper.gs(R.string.key_keep_screen_on))) 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 onPageScrollStateChanged(state: Int) {}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {} override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
override fun onPageSelected(position: Int) { override fun onPageSelected(position: Int) {
setPluginPreferenceMenuName() setPluginPreferenceMenuName()
checkPluginPreferences(main_pager) checkPluginPreferences(binding.mainPager)
} }
}) })
@ -134,7 +126,7 @@ class MainActivity : NoSplashAppCompatActivity() {
setupViews() setupViews()
disposable.add(rxBus disposable.add(rxBus
.toObservable(EventRebuildTabs::class.java) .toObservable(EventRebuildTabs::class.java)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(aapsSchedulers.main)
.subscribe({ .subscribe({
if (it.recreate) recreate() if (it.recreate) recreate()
else setupViews() else setupViews()
@ -143,7 +135,7 @@ class MainActivity : NoSplashAppCompatActivity() {
) )
disposable.add(rxBus disposable.add(rxBus
.toObservable(EventPreferenceChange::class.java) .toObservable(EventPreferenceChange::class.java)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(aapsSchedulers.main)
.subscribe({ processPreferenceChange(it) }, fabricPrivacy::logException) .subscribe({ processPreferenceChange(it) }, fabricPrivacy::logException)
) )
if (!sp.getBoolean(R.string.key_setupwizard_processed, false) && !isRunningRealPumpTest()) { if (!sp.getBoolean(R.string.key_setupwizard_processed, false) && !isRunningRealPumpTest()) {
@ -195,69 +187,53 @@ class MainActivity : NoSplashAppCompatActivity() {
private fun setupViews() { private fun setupViews() {
// Menu // Menu
val pageAdapter = TabPageAdapter(this) val pageAdapter = TabPageAdapter(this)
main_navigation_view.setNavigationItemSelectedListener { true } binding.mainNavigationView.setNavigationItemSelectedListener { true }
val menu = main_navigation_view.menu.also { it.clear() } val menu = binding.mainNavigationView.menu.also { it.clear() }
for (p in activePlugin.getPluginsList()) { for (p in activePlugin.getPluginsList()) {
pageAdapter.registerNewFragment(p) pageAdapter.registerNewFragment(p)
if (p.isEnabled() && p.hasFragment() && !p.isFragmentVisible() && !p.pluginDescription.neverVisible) { if (p.isEnabled() && p.hasFragment() && !p.isFragmentVisible() && !p.pluginDescription.neverVisible) {
val menuItem = menu.add(p.name) val menuItem = menu.add(p.name)
menuItem.isCheckable = true menuItem.isCheckable = true
if(p.menuIcon != -1) { if (p.menuIcon != -1) {
menuItem.setIcon(p.menuIcon) menuItem.setIcon(p.menuIcon)
} else } else {
{
menuItem.setIcon(R.drawable.ic_settings) menuItem.setIcon(R.drawable.ic_settings)
} }
menuItem.setOnMenuItemClickListener { menuItem.setOnMenuItemClickListener {
val intent = Intent(this, SingleFragmentActivity::class.java) val intent = Intent(this, SingleFragmentActivity::class.java)
intent.putExtra("plugin", activePlugin.getPluginsList().indexOf(p)) intent.putExtra("plugin", activePlugin.getPluginsList().indexOf(p))
startActivity(intent) startActivity(intent)
main_drawer_layout.closeDrawers() binding.mainDrawerLayout.closeDrawers()
true true
} }
} }
} }
main_pager.adapter = pageAdapter binding.mainPager.adapter = pageAdapter
main_pager.offscreenPageLimit = 8 // This may cause more memory consumption binding.mainPager.offscreenPageLimit = 8 // This may cause more memory consumption
checkPluginPreferences(main_pager) checkPluginPreferences(binding.mainPager)
// Tabs // Tabs
if (sp.getBoolean(R.string.key_short_tabtitles, false)) { if (sp.getBoolean(R.string.key_short_tabtitles, false)) {
tabs_normal.visibility = View.GONE binding.tabsNormal.visibility = View.GONE
tabs_compact.visibility = View.VISIBLE binding.tabsCompact.visibility = View.VISIBLE
toolbar.layoutParams = LinearLayout.LayoutParams(Toolbar.LayoutParams.MATCH_PARENT, resources.getDimension(R.dimen.compact_height).toInt()) binding.toolbar.layoutParams = LinearLayout.LayoutParams(Toolbar.LayoutParams.MATCH_PARENT, resources.getDimension(R.dimen.compact_height).toInt())
TabLayoutMediator(tabs_compact, main_pager) { tab, position -> TabLayoutMediator(binding.tabsCompact, binding.mainPager) { tab, position ->
tab.text = (main_pager.adapter as TabPageAdapter).getPluginAt(position).nameShort tab.text = (binding.mainPager.adapter as TabPageAdapter).getPluginAt(position).nameShort
}.attach() }.attach()
} else { } else {
tabs_normal.visibility = View.VISIBLE binding.tabsNormal.visibility = View.VISIBLE
tabs_compact.visibility = View.GONE binding.tabsCompact.visibility = View.GONE
val typedValue = TypedValue() val typedValue = TypedValue()
if (theme.resolveAttribute(R.attr.actionBarSize, typedValue, true)) { 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)) TypedValue.complexToDimensionPixelSize(typedValue.data, resources.displayMetrics))
} }
TabLayoutMediator(tabs_normal, main_pager) { tab, position -> TabLayoutMediator(binding.tabsNormal, binding.mainPager) { tab, position ->
tab.text = (main_pager.adapter as TabPageAdapter).getPluginAt(position).name tab.text = (binding.mainPager.adapter as TabPageAdapter).getPluginAt(position).name
}.attach() }.attach()
} }
} }
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, 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 { override fun dispatchTouchEvent(event: MotionEvent): Boolean {
if (event.action == MotionEvent.ACTION_DOWN) { if (event.action == MotionEvent.ACTION_DOWN) {
val v = currentFocus val v = currentFocus
@ -275,16 +251,18 @@ class MainActivity : NoSplashAppCompatActivity() {
} }
private fun setPluginPreferenceMenuName() { private fun setPluginPreferenceMenuName() {
val plugin = (main_pager.adapter as TabPageAdapter).getPluginAt(main_pager.currentItem) if (binding.mainPager.currentItem >= 0) {
this.menu?.findItem(R.id.nav_plugin_preferences)?.title = resourceHelper.gs(R.string.nav_preferences_plugin, plugin.name) 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 { override fun onCreateOptionsMenu(menu: Menu): Boolean {
this.menu = menu this.menu = menu
menuInflater.inflate(R.menu.menu_main, menu) menuInflater.inflate(R.menu.menu_main, menu)
pluginPreferencesMenuItem = menu.findItem(R.id.nav_plugin_preferences) pluginPreferencesMenuItem = menu.findItem(R.id.nav_plugin_preferences)
setPluginPreferenceMenuName() setPluginPreferenceMenuName()
checkPluginPreferences(main_pager) checkPluginPreferences(binding.mainPager)
return true return true
} }
@ -316,6 +294,7 @@ class MainActivity : NoSplashAppCompatActivity() {
message += "Flavor: ${BuildConfig.FLAVOR}${BuildConfig.BUILD_TYPE}\n" message += "Flavor: ${BuildConfig.FLAVOR}${BuildConfig.BUILD_TYPE}\n"
message += "${resourceHelper.gs(R.string.configbuilder_nightscoutversion_label)} ${nsSettingsStatus.nightscoutVersionName}" message += "${resourceHelper.gs(R.string.configbuilder_nightscoutversion_label)} ${nsSettingsStatus.nightscoutVersionName}"
if (buildHelper.isEngineeringMode()) message += "\n${resourceHelper.gs(R.string.engineering_mode_enabled)}" 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) message += resourceHelper.gs(R.string.about_link_urls)
val messageSpanned = SpannableString(message) val messageSpanned = SpannableString(message)
Linkify.addLinks(messageSpanned, Linkify.WEB_URLS) Linkify.addLinks(messageSpanned, Linkify.WEB_URLS)
@ -341,7 +320,7 @@ class MainActivity : NoSplashAppCompatActivity() {
} }
R.id.nav_plugin_preferences -> { 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, { protectionCheck.queryProtection(this, ProtectionCheck.Protection.PREFERENCES, {
val i = Intent(this, PreferencesActivity::class.java) val i = Intent(this, PreferencesActivity::class.java)
i.putExtra("id", plugin.preferencesId) i.putExtra("id", plugin.preferencesId)
@ -396,6 +375,12 @@ class MainActivity : NoSplashAppCompatActivity() {
fabricPrivacy.firebaseAnalytics.setUserProperty("Profile", activePlugin.activeProfileInterface.javaClass.simpleName) fabricPrivacy.firebaseAnalytics.setUserProperty("Profile", activePlugin.activeProfileInterface.javaClass.simpleName)
activePlugin.activeSensitivity.let { fabricPrivacy.firebaseAnalytics.setUserProperty("Sensitivity", it::class.java.simpleName) } activePlugin.activeSensitivity.let { fabricPrivacy.firebaseAnalytics.setUserProperty("Sensitivity", it::class.java.simpleName) }
activePlugin.activeInsulin.let { fabricPrivacy.firebaseAnalytics.setUserProperty("Insulin", 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])
} }
} }

View file

@ -3,7 +3,6 @@ package info.nightscout.androidaps;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.res.Resources;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.wifi.WifiManager; import android.net.wifi.WifiManager;
@ -19,6 +18,9 @@ import javax.inject.Inject;
import dagger.android.AndroidInjector; import dagger.android.AndroidInjector;
import dagger.android.DaggerApplication; 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.DatabaseHelper;
import info.nightscout.androidaps.db.StaticInjector; import info.nightscout.androidaps.db.StaticInjector;
import info.nightscout.androidaps.dependencyInjection.DaggerAppComponent; 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.ActivityMonitor;
import info.nightscout.androidaps.utils.locale.LocaleHelper; import info.nightscout.androidaps.utils.locale.LocaleHelper;
import info.nightscout.androidaps.utils.sharedPreferences.SP; import info.nightscout.androidaps.utils.sharedPreferences.SP;
import io.reactivex.disposables.CompositeDisposable;
public class MainApp extends DaggerApplication { public class MainApp extends DaggerApplication {
static MainApp sInstance;
private static Resources sResources;
static DatabaseHelper sDatabaseHelper = null; static DatabaseHelper sDatabaseHelper = null;
private final CompositeDisposable disposable = new CompositeDisposable();
@Inject PluginStore pluginStore; @Inject PluginStore pluginStore;
@Inject AAPSLogger aapsLogger; @Inject AAPSLogger aapsLogger;
@Inject ActivityMonitor activityMonitor; @Inject ActivityMonitor activityMonitor;
@ -58,6 +60,8 @@ public class MainApp extends DaggerApplication {
@Inject ConfigBuilderPlugin configBuilderPlugin; @Inject ConfigBuilderPlugin configBuilderPlugin;
@Inject KeepAliveReceiver.KeepAliveManager keepAliveManager; @Inject KeepAliveReceiver.KeepAliveManager keepAliveManager;
@Inject List<PluginBase> plugins; @Inject List<PluginBase> plugins;
@Inject CompatDBHelper compatDBHelper;
@Inject AppRepository repository;
@Inject StaticInjector staticInjector; // TODO avoid , here fake only to initialize @Inject StaticInjector staticInjector; // TODO avoid , here fake only to initialize
@ -66,10 +70,8 @@ public class MainApp extends DaggerApplication {
super.onCreate(); super.onCreate();
aapsLogger.debug("onCreate"); aapsLogger.debug("onCreate");
sInstance = this;
sResources = getResources();
LocaleHelper.INSTANCE.update(this); LocaleHelper.INSTANCE.update(this);
sDatabaseHelper = OpenHelperManager.getHelper(sInstance, DatabaseHelper.class); sDatabaseHelper = OpenHelperManager.getHelper(this, DatabaseHelper.class);
/* /*
Thread.setDefaultUncaughtExceptionHandler((thread, ex) -> { Thread.setDefaultUncaughtExceptionHandler((thread, ex) -> {
if (ex instanceof InternalError) { if (ex instanceof InternalError) {
@ -79,6 +81,15 @@ public class MainApp extends DaggerApplication {
aapsLogger.error("Uncaught exception crashing app", ex); 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); registerActivityLifecycleCallbacks(activityMonitor);
JodaTimeAndroid.init(this); JodaTimeAndroid.init(this);
@ -121,7 +132,6 @@ public class MainApp extends DaggerApplication {
.build(); .build();
} }
@SuppressWarnings("deprecation")
private void registerLocalBroadcastReceiver() { private void registerLocalBroadcastReceiver() {
IntentFilter filter = new IntentFilter(); IntentFilter filter = new IntentFilter();
filter.addAction(Intents.ACTION_NEW_TREATMENT); filter.addAction(Intents.ACTION_NEW_TREATMENT);

View file

@ -420,6 +420,6 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
fun setFilter(filter: String) { fun setFilter(filter: String) {
this.filter = filter this.filter = filter
updateFilterVisibility(filter, preferenceScreen) preferenceManager?.preferenceScreen?.let { updateFilterVisibility(filter, it) }
} }
} }

View file

@ -7,23 +7,22 @@ import android.text.TextWatcher
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.databinding.ActivityPreferencesBinding
import info.nightscout.androidaps.utils.locale.LocaleHelper 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 { class PreferencesActivity : NoSplashAppCompatActivity(), PreferenceFragmentCompat.OnPreferenceStartScreenCallback {
@Inject lateinit var resourceHelper: ResourceHelper private var preferenceId = 0
private var myPreferenceFragment: MyPreferenceFragment? = null
var preferenceId = 0 private lateinit var binding: ActivityPreferencesBinding
var myPreferenceFragment: MyPreferenceFragment? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) 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 beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
filterPreferences() filterPreferences()
@ -37,23 +36,21 @@ class PreferencesActivity : NoSplashAppCompatActivity(), PreferenceFragmentCompa
supportActionBar?.setDisplayShowHomeEnabled(true) supportActionBar?.setDisplayShowHomeEnabled(true)
myPreferenceFragment = MyPreferenceFragment() myPreferenceFragment = MyPreferenceFragment()
preferenceId = intent.getIntExtra("id", -1) preferenceId = intent.getIntExtra("id", -1)
val args = Bundle() myPreferenceFragment?.arguments = Bundle().also {
args.putInt("id", preferenceId) it.putInt("id", preferenceId)
args.putString("filter", pref_filter.text.toString()) it.putString("filter", binding.prefFilter.text.toString())
myPreferenceFragment?.arguments = args }
supportFragmentManager.beginTransaction().replace(R.id.frame_layout, myPreferenceFragment!!).commit() if (savedInstanceState == null)
supportFragmentManager.beginTransaction().replace(R.id.frame_layout, myPreferenceFragment!!).commit()
} }
override fun onPreferenceStartScreen(caller: PreferenceFragmentCompat, pref: PreferenceScreen): Boolean { override fun onPreferenceStartScreen(caller: PreferenceFragmentCompat, pref: PreferenceScreen): Boolean {
val fragment = MyPreferenceFragment() val fragment = MyPreferenceFragment()
val args = Bundle() fragment.arguments = Bundle().also {
args.putString(PreferenceFragmentCompat.ARG_PREFERENCE_ROOT, pref.key) it.putString(PreferenceFragmentCompat.ARG_PREFERENCE_ROOT, pref.key)
args.putInt("id", preferenceId) it.putInt("id", preferenceId)
fragment.arguments = args }
supportFragmentManager.beginTransaction() supportFragmentManager.beginTransaction().replace(R.id.frame_layout, fragment, pref.key).addToBackStack(pref.key).commit()
.replace(R.id.frame_layout, fragment, pref.key)
.addToBackStack(pref.key)
.commit()
return true return true
} }
@ -62,6 +59,6 @@ class PreferencesActivity : NoSplashAppCompatActivity(), PreferenceFragmentCompa
} }
private fun filterPreferences() { private fun filterPreferences() {
myPreferenceFragment?.setFilter(pref_filter.text.toString()) myPreferenceFragment?.setFilter(binding.prefFilter.text.toString())
} }
} }

View file

@ -10,6 +10,7 @@ import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.data.defaultProfile.DefaultProfile import info.nightscout.androidaps.data.defaultProfile.DefaultProfile
import info.nightscout.androidaps.data.defaultProfile.DefaultProfileDPV import info.nightscout.androidaps.data.defaultProfile.DefaultProfileDPV
import info.nightscout.androidaps.databinding.ActivityProfilehelperBinding
import info.nightscout.androidaps.db.ProfileSwitch import info.nightscout.androidaps.db.ProfileSwitch
import info.nightscout.androidaps.dialogs.ProfileViewerDialog import info.nightscout.androidaps.dialogs.ProfileViewerDialog
import info.nightscout.androidaps.interfaces.ActivePluginProvider 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.ToastUtils
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.toVisibility import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.stats.TddCalculator import info.nightscout.androidaps.utils.stats.TddCalculator
import kotlinx.android.synthetic.main.activity_profilehelper.*
import java.text.DecimalFormat import java.text.DecimalFormat
import javax.inject.Inject import javax.inject.Inject
class ProfileHelperActivity : NoSplashAppCompatActivity() { class ProfileHelperActivity : NoSplashAppCompatActivity() {
@Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var tddCalculator: TddCalculator @Inject lateinit var tddCalculator: TddCalculator
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var defaultProfile: DefaultProfile @Inject lateinit var defaultProfile: DefaultProfile
@ -65,27 +64,31 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
private lateinit var profileSwitch: List<ProfileSwitch> private lateinit var profileSwitch: List<ProfileSwitch>
private val profileSwitchUsed = arrayOf(0, 0) private val profileSwitchUsed = arrayOf(0, 0)
private lateinit var binding: ActivityProfilehelperBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) 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]) switchTab(0, typeSelected[0])
} }
profilehelper_menu2.setOnClickListener { binding.menu2.setOnClickListener {
switchTab(1, typeSelected[1]) switchTab(1, typeSelected[1])
} }
profilehelper_profiletype.setOnClickListener { binding.profiletype.setOnClickListener {
PopupMenu(this, profilehelper_profiletype).apply { PopupMenu(this, binding.profiletype).apply {
menuInflater.inflate(R.menu.menu_profilehelper, menu) menuInflater.inflate(R.menu.menu_profilehelper, menu)
setOnMenuItemClickListener { item -> setOnMenuItemClickListener { item ->
profilehelper_profiletype.setText(item.title) binding.profiletype.setText(item.title)
when (item.itemId) { when (item.itemId) {
R.id.menu_default -> switchTab(tabSelected, ProfileType.MOTOL_DEFAULT) R.id.menu_default -> switchTab(tabSelected, ProfileType.MOTOL_DEFAULT)
R.id.menu_default_dpv -> switchTab(tabSelected, ProfileType.DPV_DEFAULT) R.id.menu_default_dpv -> switchTab(tabSelected, ProfileType.DPV_DEFAULT)
R.id.menu_current -> switchTab(tabSelected, ProfileType.CURRENT) R.id.menu_current -> switchTab(tabSelected, ProfileType.CURRENT)
R.id.menu_available -> switchTab(tabSelected, ProfileType.AVAILABLE_PROFILE) R.id.menu_available -> switchTab(tabSelected, ProfileType.AVAILABLE_PROFILE)
R.id.menu_profileswitch -> switchTab(tabSelected, ProfileType.PROFILE_SWITCH) R.id.menu_profileswitch -> switchTab(tabSelected, ProfileType.PROFILE_SWITCH)
} }
true true
@ -97,12 +100,12 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
// Active profile // Active profile
profileList = activePlugin.activeProfileInterface.profile?.getProfileList() ?: ArrayList() profileList = activePlugin.activeProfileInterface.profile?.getProfileList() ?: ArrayList()
profilehelper_available_profile_list.setOnClickListener { binding.availableProfileList.setOnClickListener {
PopupMenu(this, profilehelper_available_profile_list).apply { PopupMenu(this, binding.availableProfileList).apply {
var order = 0 var order = 0
for (name in profileList) menu.add(Menu.NONE, order, order++, name) for (name in profileList) menu.add(Menu.NONE, order, order++, name)
setOnMenuItemClickListener { item -> setOnMenuItemClickListener { item ->
profilehelper_available_profile_list.setText(item.title) binding.availableProfileList.setText(item.title)
profileUsed[tabSelected] = item.itemId profileUsed[tabSelected] = item.itemId
true true
} }
@ -113,12 +116,12 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
// Profile switch // Profile switch
profileSwitch = databaseHelper.getProfileSwitchData(dateUtil._now() - T.months(2).msecs(), true) profileSwitch = databaseHelper.getProfileSwitchData(dateUtil._now() - T.months(2).msecs(), true)
profilehelper_profileswitch_list.setOnClickListener { binding.profileswitchList.setOnClickListener {
PopupMenu(this, profilehelper_profileswitch_list).apply { PopupMenu(this, binding.profileswitchList).apply {
var order = 0 var order = 0
for (name in profileSwitch) menu.add(Menu.NONE, order, order++, name.customizedName) for (name in profileSwitch) menu.add(Menu.NONE, order, order++, name.customizedName)
setOnMenuItemClickListener { item -> setOnMenuItemClickListener { item ->
profilehelper_profileswitch_list.setText(item.title) binding.profileswitchList.setText(item.title)
profileSwitchUsed[tabSelected] = item.itemId profileSwitchUsed[tabSelected] = item.itemId
true true
} }
@ -127,7 +130,7 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
} }
// Default profile // Default profile
profilehelper_copytolocalprofile.setOnClickListener { binding.copytolocalprofile.setOnClickListener {
val age = ageUsed[tabSelected] val age = ageUsed[tabSelected]
val weight = weightUsed[tabSelected] val weight = weightUsed[tabSelected]
val tdd = tddUsed[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) binding.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.weight.setParams(0.0, 0.0, 150.0, 1.0, DecimalFormat("0"), false, null, object : TextWatcher {
override fun afterTextChanged(s: Editable) {} override fun afterTextChanged(s: Editable) {}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
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 afterTextChanged(s: Editable) {}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
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 // Current profile
profilehelper_current_profile_text.text = profileFunction.getProfileName() binding.currentProfileText.text = profileFunction.getProfileName()
// General // General
profilehelper_compareprofile.setOnClickListener { binding.compareprofile.setOnClickListener {
storeValues() storeValues()
for (i in 0..1) { for (i in 0..1) {
if (typeSelected[i] == ProfileType.MOTOL_DEFAULT) { if (typeSelected[i] == ProfileType.MOTOL_DEFAULT) {
@ -221,28 +224,32 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
} }
private fun getProfile(age: Double, tdd: Double, weight: Double, basalPct: Double, tab: Int): Profile? = private fun getProfile(age: Double, tdd: Double, weight: Double, basalPct: Double, tab: Int): Profile? =
when (typeSelected[tab]) { try { // profile must not exist
ProfileType.MOTOL_DEFAULT -> defaultProfile.profile(age, tdd, weight, profileFunction.getUnits()) when (typeSelected[tab]) {
ProfileType.DPV_DEFAULT -> defaultProfileDPV.profile(age, tdd, basalPct, profileFunction.getUnits()) ProfileType.MOTOL_DEFAULT -> defaultProfile.profile(age, tdd, weight, profileFunction.getUnits())
ProfileType.CURRENT -> profileFunction.getProfile()?.convertToNonCustomizedProfile() ProfileType.DPV_DEFAULT -> defaultProfileDPV.profile(age, tdd, basalPct, profileFunction.getUnits())
ProfileType.AVAILABLE_PROFILE -> activePlugin.activeProfileInterface.profile?.getSpecificProfile(profileList[profileUsed[tab]].toString()) ProfileType.CURRENT -> profileFunction.getProfile()?.convertToNonCustomizedProfile()
ProfileType.PROFILE_SWITCH -> profileSwitch[profileSwitchUsed[tab]].profileObject?.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 = private fun getProfileName(age: Double, tdd: Double, weight: Double, basalSumPct: Double, tab: Int): String =
when (typeSelected[tab]) { 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.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.DPV_DEFAULT -> resourceHelper.gs(R.string.formatwittddandpct, age, tdd, (basalSumPct * 100).toInt())
ProfileType.CURRENT -> profileFunction.getProfileName() ProfileType.CURRENT -> profileFunction.getProfileName()
ProfileType.AVAILABLE_PROFILE -> profileList[profileUsed[tab]].toString() ProfileType.AVAILABLE_PROFILE -> profileList[profileUsed[tab]].toString()
ProfileType.PROFILE_SWITCH -> profileSwitch[profileSwitchUsed[tab]].customizedName ProfileType.PROFILE_SWITCH -> profileSwitch[profileSwitchUsed[tab]].customizedName
} }
private fun storeValues() { private fun storeValues() {
ageUsed[tabSelected] = profilehelper_age.value ageUsed[tabSelected] = binding.age.value
weightUsed[tabSelected] = profilehelper_weight.value weightUsed[tabSelected] = binding.weight.value
tddUsed[tabSelected] = profilehelper_tdd.value tddUsed[tabSelected] = binding.tdd.value
pctUsed[tabSelected] = profilehelper_basalpctfromtdd.value pctUsed[tabSelected] = binding.basalpctfromtdd.value
} }
private fun switchTab(tab: Int, newContent: ProfileType, storeOld: Boolean = true) { private fun switchTab(tab: Int, newContent: ProfileType, storeOld: Boolean = true) {
@ -252,10 +259,10 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
tabSelected = tab tabSelected = tab
typeSelected[tabSelected] = newContent 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 // show new content
profilehelper_profiletype.setText( binding.profiletype.setText(
when (typeSelected[tabSelected]) { when (typeSelected[tabSelected]) {
ProfileType.MOTOL_DEFAULT -> resourceHelper.gs(R.string.motoldefaultprofile) ProfileType.MOTOL_DEFAULT -> resourceHelper.gs(R.string.motoldefaultprofile)
ProfileType.DPV_DEFAULT -> resourceHelper.gs(R.string.dpvdefaultprofile) ProfileType.DPV_DEFAULT -> resourceHelper.gs(R.string.dpvdefaultprofile)
@ -263,26 +270,26 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
ProfileType.AVAILABLE_PROFILE -> resourceHelper.gs(R.string.availableprofile) ProfileType.AVAILABLE_PROFILE -> resourceHelper.gs(R.string.availableprofile)
ProfileType.PROFILE_SWITCH -> resourceHelper.gs(R.string.careportal_profileswitch) ProfileType.PROFILE_SWITCH -> resourceHelper.gs(R.string.careportal_profileswitch)
}) })
profilehelper_default_profile.visibility = (newContent == ProfileType.MOTOL_DEFAULT || newContent == ProfileType.DPV_DEFAULT).toVisibility() binding.defaultProfile.visibility = (newContent == ProfileType.MOTOL_DEFAULT || newContent == ProfileType.DPV_DEFAULT).toVisibility()
profilehelper_current_profile.visibility = (newContent == ProfileType.CURRENT).toVisibility() binding.currentProfile.visibility = (newContent == ProfileType.CURRENT).toVisibility()
profilehelper_available_profile.visibility = (newContent == ProfileType.AVAILABLE_PROFILE).toVisibility() binding.availableProfile.visibility = (newContent == ProfileType.AVAILABLE_PROFILE).toVisibility()
profilehelper_profile_switch.visibility = (newContent == ProfileType.PROFILE_SWITCH).toVisibility() binding.profileSwitch.visibility = (newContent == ProfileType.PROFILE_SWITCH).toVisibility()
// restore selected values // restore selected values
profilehelper_age.value = ageUsed[tabSelected] binding.age.value = ageUsed[tabSelected]
profilehelper_weight.value = weightUsed[tabSelected] binding.weight.value = weightUsed[tabSelected]
profilehelper_tdd.value = tddUsed[tabSelected] binding.tdd.value = tddUsed[tabSelected]
profilehelper_basalpctfromtdd.value = pctUsed[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()) if (profileList.isNotEmpty())
profilehelper_available_profile_list.setText(profileList[profileUsed[tabSelected]].toString()) binding.availableProfileList.setText(profileList[profileUsed[tabSelected]].toString())
if (profileSwitch.isNotEmpty()) if (profileSwitch.isNotEmpty())
profilehelper_profileswitch_list.setText(profileSwitch[profileSwitchUsed[tabSelected]].customizedName) binding.profileswitchList.setText(profileSwitch[profileSwitchUsed[tabSelected]].customizedName)
} }
private fun setBackgroundColorOnSelected(tab: Int) { private fun setBackgroundColorOnSelected(tab: Int) {
profilehelper_menu1.setBackgroundColor(resourceHelper.gc(if (tab == 1) R.color.defaultbackground else R.color.tabBgColorSelected)) binding.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.menu2.setBackgroundColor(resourceHelper.gc(if (tab == 0) R.color.defaultbackground else R.color.examinedProfile))
} }
} }

View file

@ -15,6 +15,7 @@ class RequestDexcomPermissionActivity : DialogAppCompatActivity() {
} }
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
finish() finish()
} }

View file

@ -5,29 +5,20 @@ import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import dagger.android.support.DaggerAppCompatActivity
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.plugins.configBuilder.PluginStore 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.locale.LocaleHelper
import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.protection.ProtectionCheck
import javax.inject.Inject import javax.inject.Inject
class SingleFragmentActivity : DaggerAppCompatActivity() { class SingleFragmentActivity : DaggerAppCompatActivityWithResult() {
@Inject lateinit var pluginStore: PluginStore @Inject lateinit var pluginStore: PluginStore
@Inject lateinit var protectionCheck: ProtectionCheck @Inject lateinit var protectionCheck: ProtectionCheck
@Inject lateinit var importExportPrefs: ImportExportPrefs
private var plugin: PluginBase? = null private var plugin: PluginBase? = null
val callForPrefFile = registerForActivityResult(PrefsFileContract()) {
it?.let {
importExportPrefs.importSharedPreferences(this, it)
}
}
public override fun onCreate(savedInstanceState: Bundle?) { public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_single_fragment) setContentView(R.layout.activity_single_fragment)

View file

@ -2,35 +2,39 @@ package info.nightscout.androidaps.activities
import android.os.Bundle import android.os.Bundle
import info.nightscout.androidaps.R 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.ActivityMonitor
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.stats.TddCalculator import info.nightscout.androidaps.utils.stats.TddCalculator
import info.nightscout.androidaps.utils.stats.TirCalculator import info.nightscout.androidaps.utils.stats.TirCalculator
import info.nightscout.androidaps.utils.resources.ResourceHelper
import kotlinx.android.synthetic.main.activity_stats.*
import javax.inject.Inject import javax.inject.Inject
class StatsActivity : NoSplashAppCompatActivity() { class StatsActivity : NoSplashAppCompatActivity() {
@Inject lateinit var tddCalculator: TddCalculator @Inject lateinit var tddCalculator: TddCalculator
@Inject lateinit var tirCalculator: TirCalculator @Inject lateinit var tirCalculator: TirCalculator
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var activityMonitor: ActivityMonitor @Inject lateinit var activityMonitor: ActivityMonitor
@Inject lateinit var uel: UserEntryLogger
private lateinit var binding: ActivityStatsBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_stats) binding = ActivityStatsBinding.inflate(layoutInflater)
setContentView(binding.root)
stats_tdds.text = tddCalculator.stats() binding.tdds.text = tddCalculator.stats()
stats_tir.text = tirCalculator.stats() binding.tir.text = tirCalculator.stats()
stats_activity.text = activityMonitor.stats() binding.activity.text = activityMonitor.stats()
ok.setOnClickListener { finish() } binding.ok.setOnClickListener { finish() }
stats_reset.setOnClickListener { binding.reset.setOnClickListener {
OKDialog.showConfirmation(this, resourceHelper.gs(R.string.doyouwantresetstats), Runnable { OKDialog.showConfirmation(this, resourceHelper.gs(R.string.doyouwantresetstats)) {
uel.log("STATS RESET")
activityMonitor.reset() activityMonitor.reset()
recreate() recreate()
}) }
} }
} }
} }

View file

@ -6,6 +6,7 @@ import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.database.FirebaseDatabase import com.google.firebase.database.FirebaseDatabase
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.defaultProfile.DefaultProfile import info.nightscout.androidaps.data.defaultProfile.DefaultProfile
import info.nightscout.androidaps.databinding.ActivitySurveyBinding
import info.nightscout.androidaps.dialogs.ProfileViewerDialog import info.nightscout.androidaps.dialogs.ProfileViewerDialog
import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.ProfileFunction 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.InstanceId
import info.nightscout.androidaps.utils.SafeParse import info.nightscout.androidaps.utils.SafeParse
import info.nightscout.androidaps.utils.ToastUtils 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.TddCalculator
import info.nightscout.androidaps.utils.stats.TirCalculator import info.nightscout.androidaps.utils.stats.TirCalculator
import kotlinx.android.synthetic.main.activity_survey.*
import javax.inject.Inject import javax.inject.Inject
class SurveyActivity : NoSplashAppCompatActivity() { class SurveyActivity : NoSplashAppCompatActivity() {
@Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var activePlugin: ActivePluginProvider @Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var tddCalculator: TddCalculator @Inject lateinit var tddCalculator: TddCalculator
@Inject lateinit var tirCalculator: TirCalculator @Inject lateinit var tirCalculator: TirCalculator
@ -32,24 +31,27 @@ class SurveyActivity : NoSplashAppCompatActivity() {
@Inject lateinit var activityMonitor: ActivityMonitor @Inject lateinit var activityMonitor: ActivityMonitor
@Inject lateinit var defaultProfile: DefaultProfile @Inject lateinit var defaultProfile: DefaultProfile
private lateinit var binding: ActivitySurveyBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) 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 profileStore = activePlugin.activeProfileInterface.profile
val profileList = profileStore?.getProfileList() ?: return 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() binding.tdds.text = tddCalculator.stats()
survey_tir.text = tirCalculator.stats() binding.tir.text = tirCalculator.stats()
survey_activity.text = activityMonitor.stats() binding.activity.text = activityMonitor.stats()
survey_profile.setOnClickListener { binding.profile.setOnClickListener {
val age = SafeParse.stringToDouble(survey_age.text.toString()) val age = SafeParse.stringToDouble(binding.age.text.toString())
val weight = SafeParse.stringToDouble(survey_weight.text.toString()) val weight = SafeParse.stringToDouble(binding.weight.text.toString())
val tdd = SafeParse.stringToDouble(survey_tdd.text.toString()) val tdd = SafeParse.stringToDouble(binding.tdd.text.toString())
if (age < 1 || age > 120) { if (age < 1 || age > 120) {
ToastUtils.showToastInUiThread(this, R.string.invalidage) ToastUtils.showToastInUiThread(this, R.string.invalidage)
return@setOnClickListener return@setOnClickListener
@ -78,11 +80,11 @@ class SurveyActivity : NoSplashAppCompatActivity() {
} }
} }
survey_submit.setOnClickListener { binding.submit.setOnClickListener {
val r = FirebaseRecord() val r = FirebaseRecord()
r.id = InstanceId.instanceId() r.id = InstanceId.instanceId()
r.age = SafeParse.stringToInt(survey_age.text.toString()) r.age = SafeParse.stringToInt(binding.age.text.toString())
r.weight = SafeParse.stringToInt(survey_weight.text.toString()) r.weight = SafeParse.stringToInt(binding.weight.text.toString())
if (r.age < 1 || r.age > 120) { if (r.age < 1 || r.age > 120) {
ToastUtils.showToastInUiThread(this, R.string.invalidage) ToastUtils.showToastInUiThread(this, R.string.invalidage)
return@setOnClickListener return@setOnClickListener
@ -92,9 +94,9 @@ class SurveyActivity : NoSplashAppCompatActivity() {
return@setOnClickListener return@setOnClickListener
} }
if (survey_spinner.selectedItem == null) if (binding.spinner.selectedItem == null)
return@setOnClickListener return@setOnClickListener
val profileName = survey_spinner.selectedItem.toString() val profileName = binding.spinner.selectedItem.toString()
val specificProfile = profileStore.getSpecificProfile(profileName) val specificProfile = profileStore.getSpecificProfile(profileName)
r.profileJson = specificProfile.toString() r.profileJson = specificProfile.toString()
@ -121,6 +123,7 @@ class SurveyActivity : NoSplashAppCompatActivity() {
} }
inner class FirebaseRecord { inner class FirebaseRecord {
var id = "" var id = ""
var age: Int = 0 var age: Int = 0
var weight: Int = 0 var weight: Int = 0

View file

@ -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<GlucoseValue>().firstOrNull()?.let {
aapsLogger.debug(LTag.DATABASE, "Firing EventNewHistoryData")
rxBus.send(EventNewHistoryData(it.timestamp))
}
it.filterIsInstance<GlucoseValue>().lastOrNull()?.let {
aapsLogger.debug(LTag.DATABASE, "Firing EventNewBg")
rxBus.send(EventNewBG(it))
}
it.filterIsInstance<TemporaryTarget>().firstOrNull()?.let {
aapsLogger.debug(LTag.DATABASE, "Firing EventTempTargetChange")
rxBus.send(EventTempTargetChange())
}
}
}

View file

@ -33,13 +33,11 @@ import java.util.concurrent.TimeUnit;
import javax.inject.Inject; import javax.inject.Inject;
import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.Constants;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.dana.comm.RecordTypes; import info.nightscout.androidaps.dana.comm.RecordTypes;
import info.nightscout.androidaps.data.NonOverlappingIntervals; import info.nightscout.androidaps.data.NonOverlappingIntervals;
import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.events.EventCareportalEventChange; import info.nightscout.androidaps.events.EventCareportalEventChange;
import info.nightscout.androidaps.events.EventExtendedBolusChange; import info.nightscout.androidaps.events.EventExtendedBolusChange;
import info.nightscout.androidaps.events.EventNewBG;
import info.nightscout.androidaps.events.EventProfileNeedsUpdate; import info.nightscout.androidaps.events.EventProfileNeedsUpdate;
import info.nightscout.androidaps.events.EventRefreshOverview; import info.nightscout.androidaps.events.EventRefreshOverview;
import info.nightscout.androidaps.events.EventReloadProfileSwitchData; 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.logging.LTag;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper; import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; 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.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.iob.iobCobCalculator.events.EventNewHistoryData;
import info.nightscout.androidaps.plugins.pump.insight.database.InsightBolusID; import info.nightscout.androidaps.plugins.pump.insight.database.InsightBolusID;
import info.nightscout.androidaps.plugins.pump.insight.database.InsightHistoryOffset; 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.plugins.pump.virtual.VirtualPumpPlugin;
import info.nightscout.androidaps.utils.JsonHelper; import info.nightscout.androidaps.utils.JsonHelper;
import info.nightscout.androidaps.utils.PercentageSplitter; 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 * 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; @Inject OpenHumansUploader openHumansUploader;
public static final String DATABASE_NAME = "AndroidAPSDb"; 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_TEMPORARYBASALS = "TemporaryBasals";
public static final String DATABASE_EXTENDEDBOLUSES = "ExtendedBoluses"; public static final String DATABASE_EXTENDEDBOLUSES = "ExtendedBoluses";
public static final String DATABASE_TEMPTARGETS = "TempTargets"; 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 final ScheduledExecutorService bgWorker = Executors.newSingleThreadScheduledExecutor();
private static ScheduledFuture<?> scheduledBgPost = null; 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 final ScheduledExecutorService tempBasalsWorker = Executors.newSingleThreadScheduledExecutor();
private static ScheduledFuture<?> scheduledTemBasalsPost = null; private static ScheduledFuture<?> scheduledTemBasalsPost = null;
@ -135,7 +125,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
try { try {
aapsLogger.info(LTag.DATABASE, "onCreate"); aapsLogger.info(LTag.DATABASE, "onCreate");
TableUtils.createTableIfNotExists(connectionSource, TempTarget.class); TableUtils.createTableIfNotExists(connectionSource, TempTarget.class);
TableUtils.createTableIfNotExists(connectionSource, BgReading.class); //TableUtils.createTableIfNotExists(connectionSource, BgReading.class);
TableUtils.createTableIfNotExists(connectionSource, DanaRHistoryRecord.class); TableUtils.createTableIfNotExists(connectionSource, DanaRHistoryRecord.class);
TableUtils.createTableIfNotExists(connectionSource, DbRequest.class); TableUtils.createTableIfNotExists(connectionSource, DbRequest.class);
TableUtils.createTableIfNotExists(connectionSource, TemporaryBasal.class); TableUtils.createTableIfNotExists(connectionSource, TemporaryBasal.class);
@ -167,7 +157,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
if (oldVersion < 7) { if (oldVersion < 7) {
aapsLogger.info(LTag.DATABASE, "onUpgrade"); aapsLogger.info(LTag.DATABASE, "onUpgrade");
TableUtils.dropTable(connectionSource, TempTarget.class, true); 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, DanaRHistoryRecord.class, true);
TableUtils.dropTable(connectionSource, DbRequest.class, true); TableUtils.dropTable(connectionSource, DbRequest.class, true);
TableUtils.dropTable(connectionSource, TemporaryBasal.class, true); TableUtils.dropTable(connectionSource, TemporaryBasal.class, true);
@ -217,7 +207,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
public void resetDatabases() { public void resetDatabases() {
try { try {
TableUtils.dropTable(connectionSource, TempTarget.class, true); 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, DanaRHistoryRecord.class, true);
TableUtils.dropTable(connectionSource, DbRequest.class, true); TableUtils.dropTable(connectionSource, DbRequest.class, true);
TableUtils.dropTable(connectionSource, TemporaryBasal.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, TDD.class, true);
TableUtils.dropTable(connectionSource, OmnipodHistoryRecord.class, true); TableUtils.dropTable(connectionSource, OmnipodHistoryRecord.class, true);
TableUtils.createTableIfNotExists(connectionSource, TempTarget.class); TableUtils.createTableIfNotExists(connectionSource, TempTarget.class);
TableUtils.createTableIfNotExists(connectionSource, BgReading.class); //TableUtils.createTableIfNotExists(connectionSource, BgReading.class);
TableUtils.createTableIfNotExists(connectionSource, DanaRHistoryRecord.class); TableUtils.createTableIfNotExists(connectionSource, DanaRHistoryRecord.class);
TableUtils.createTableIfNotExists(connectionSource, DbRequest.class); TableUtils.createTableIfNotExists(connectionSource, DbRequest.class);
TableUtils.createTableIfNotExists(connectionSource, TemporaryBasal.class); TableUtils.createTableIfNotExists(connectionSource, TemporaryBasal.class);
@ -241,7 +231,6 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
aapsLogger.error("Unhandled exception", e); aapsLogger.error("Unhandled exception", e);
} }
virtualPumpPlugin.setFakingStatus(true); virtualPumpPlugin.setFakingStatus(true);
scheduleBgChange(null); // trigger refresh
scheduleTemporaryBasalChange(); scheduleTemporaryBasalChange();
scheduleExtendedBolusChange(); scheduleExtendedBolusChange();
scheduleTemporaryTargetChange(); scheduleTemporaryTargetChange();
@ -326,10 +315,6 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
return getDao(TempTarget.class); return getDao(TempTarget.class);
} }
private Dao<BgReading, Long> getDaoBgReadings() throws SQLException {
return getDao(BgReading.class);
}
private Dao<DanaRHistoryRecord, String> getDaoDanaRHistory() throws SQLException { private Dao<DanaRHistoryRecord, String> getDaoDanaRHistory() throws SQLException {
return getDao(DanaRHistoryRecord.class); return getDao(DanaRHistoryRecord.class);
} }
@ -384,144 +369,6 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
aapsLogger.debug(LTag.DATABASE, "Rounding " + date + " to " + rounded); aapsLogger.debug(LTag.DATABASE, "Rounding " + date + " to " + rounded);
return 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<BgReading> getBgreadingsDataFromTime(long mills, boolean ascending) {
try {
Dao<BgReading, Long> daoBgreadings = getDaoBgReadings();
List<BgReading> bgReadings;
QueryBuilder<BgReading, Long> queryBuilder = daoBgreadings.queryBuilder();
queryBuilder.orderBy("date", ascending);
Where where = queryBuilder.where();
where.ge("date", mills).and().ge("value", 39).and().eq("isValid", true);
PreparedQuery<BgReading> preparedQuery = queryBuilder.prepare();
bgReadings = daoBgreadings.query(preparedQuery);
return bgReadings;
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
return new ArrayList<>();
}
public List<BgReading> getBgreadingsDataFromTime(long start, long end, boolean ascending) {
try {
Dao<BgReading, Long> daoBgreadings = getDaoBgReadings();
List<BgReading> bgReadings;
QueryBuilder<BgReading, Long> 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<BgReading> preparedQuery = queryBuilder.prepare();
bgReadings = daoBgreadings.query(preparedQuery);
return bgReadings;
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
return new ArrayList<>();
}
public List<BgReading> getAllBgreadingsDataFromTime(long mills, boolean ascending) {
try {
Dao<BgReading, Long> daoBgreadings = getDaoBgReadings();
List<BgReading> bgReadings;
QueryBuilder<BgReading, Long> queryBuilder = daoBgreadings.queryBuilder();
queryBuilder.orderBy("date", ascending);
Where where = queryBuilder.where();
where.ge("date", mills);
PreparedQuery<BgReading> preparedQuery = queryBuilder.prepare();
bgReadings = daoBgreadings.query(preparedQuery);
return bgReadings;
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
return new ArrayList<BgReading>();
}
public List<BgReading> getAllBgReadings() {
try {
return getDaoBgReadings().queryForAll();
} catch (SQLException e) {
aapsLogger.error("Unhandled exception", e);
}
return Collections.emptyList();
}
// ------------------- TDD handling ----------------------- // ------------------- TDD handling -----------------------
public void createOrUpdateTDD(TDD tdd) { public void createOrUpdateTDD(TDD tdd) {
@ -1672,6 +1519,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
} }
return Collections.emptyList(); return Collections.emptyList();
} }
@Nullable @Nullable
private ProfileSwitch getLastProfileSwitchWithoutDuration() { private ProfileSwitch getLastProfileSwitchWithoutDuration() {
try { try {
@ -2010,7 +1858,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
return null; return null;
} }
// Copied from xDrip+ /*
TODO implement again for database branch // Copied from xDrip+
String calculateDirection(BgReading bgReading) { String calculateDirection(BgReading bgReading) {
// Rework to get bgreaings from internal DB and calculate on that base // 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); // aapsLogger.error(LTag.GLUCOSE, "Direction set to: " + arrow);
return arrow; return arrow;
} }
*/
// ---------------- Open Humans Queue handling --------------- // ---------------- Open Humans Queue handling ---------------
public void clearOpenHumansQueue() { public void clearOpenHumansQueue() {
@ -2109,8 +1958,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
public long getCountOfAllRows() { public long getCountOfAllRows() {
try { try {
return getDaoBgReadings().countOf() return getDaoCareportalEvents().countOf()
+ getDaoCareportalEvents().countOf()
+ getDaoExtendedBolus().countOf() + getDaoExtendedBolus().countOf()
+ getDaoCareportalEvents().countOf() + getDaoCareportalEvents().countOf()
+ getDaoProfileSwitch().countOf() + getDaoProfileSwitch().countOf()

View file

@ -20,10 +20,6 @@ public class DatabaseHelperProvider implements DatabaseHelperInterface {
@Inject DatabaseHelperProvider() { @Inject DatabaseHelperProvider() {
} }
@NotNull @Override public List<BgReading> getAllBgreadingsDataFromTime(long mills, boolean ascending) {
return MainApp.getDbHelper().getAllBgreadingsDataFromTime(mills, ascending);
}
@Override public void createOrUpdate(@NotNull CareportalEvent careportalEvent) { @Override public void createOrUpdate(@NotNull CareportalEvent careportalEvent) {
MainApp.getDbHelper().createOrUpdate(careportalEvent); MainApp.getDbHelper().createOrUpdate(careportalEvent);
} }

View file

@ -6,7 +6,6 @@ import info.nightscout.androidaps.MainActivity
import info.nightscout.androidaps.activities.* import info.nightscout.androidaps.activities.*
import info.nightscout.androidaps.historyBrowser.HistoryBrowseActivity import info.nightscout.androidaps.historyBrowser.HistoryBrowseActivity
import info.nightscout.androidaps.plugins.general.maintenance.activities.LogSettingActivity 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.openhumans.OpenHumansLoginActivity
import info.nightscout.androidaps.plugins.general.overview.activities.QuickWizardListActivity import info.nightscout.androidaps.plugins.general.overview.activities.QuickWizardListActivity
import info.nightscout.androidaps.plugins.general.smsCommunicator.activities.SmsCommunicatorOtpActivity import info.nightscout.androidaps.plugins.general.smsCommunicator.activities.SmsCommunicatorOtpActivity
@ -40,7 +39,6 @@ abstract class ActivitiesModule {
@ContributesAndroidInjector abstract fun contributesStatsActivity(): StatsActivity @ContributesAndroidInjector abstract fun contributesStatsActivity(): StatsActivity
@ContributesAndroidInjector abstract fun contributesSurveyActivity(): SurveyActivity @ContributesAndroidInjector abstract fun contributesSurveyActivity(): SurveyActivity
@ContributesAndroidInjector abstract fun contributesDefaultProfileActivity(): ProfileHelperActivity @ContributesAndroidInjector abstract fun contributesDefaultProfileActivity(): ProfileHelperActivity
@ContributesAndroidInjector abstract fun contributesPrefImportListActivity(): PrefImportListActivity
@ContributesAndroidInjector abstract fun contributesOpenHumansLoginActivity(): OpenHumansLoginActivity @ContributesAndroidInjector abstract fun contributesOpenHumansLoginActivity(): OpenHumansLoginActivity
} }

View file

@ -9,14 +9,16 @@ import info.nightscout.androidaps.core.di.CoreModule
import info.nightscout.androidaps.dana.di.DanaModule import info.nightscout.androidaps.dana.di.DanaModule
import info.nightscout.androidaps.danar.di.DanaRModule import info.nightscout.androidaps.danar.di.DanaRModule
import info.nightscout.androidaps.danars.di.DanaRSModule 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.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 import javax.inject.Singleton
@Singleton @Singleton
@Component( @Component(
modules = [ modules = [
AndroidInjectionModule::class, AndroidInjectionModule::class,
DatabaseModule::class,
PluginsModule::class, PluginsModule::class,
SkinsModule::class, SkinsModule::class,
ActivitiesModule::class, ActivitiesModule::class,
@ -30,7 +32,7 @@ import javax.inject.Singleton
WizardModule::class, WizardModule::class,
RileyLinkModule::class, RileyLinkModule::class,
MedtronicModule::class, MedtronicModule::class,
OmnipodModule::class, OmnipodErosModule::class,
APSModule::class, APSModule::class,
PreferencesModule::class, PreferencesModule::class,
OverviewModule::class, OverviewModule::class,
@ -41,6 +43,7 @@ import javax.inject.Singleton
DanaModule::class, DanaModule::class,
DanaRModule::class, DanaRModule::class,
DanaRSModule::class, DanaRSModule::class,
WorkersModule::class,
OHUploaderModule::class OHUploaderModule::class
] ]
) )

View file

@ -12,10 +12,13 @@ import info.nightscout.androidaps.db.DatabaseHelperProvider
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin
import info.nightscout.androidaps.plugins.configBuilder.PluginStore 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.general.nsclient.UploadQueue
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.queue.CommandQueue import info.nightscout.androidaps.queue.CommandQueue
import info.nightscout.androidaps.utils.androidNotification.NotificationHolder 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.FileStorage
import info.nightscout.androidaps.utils.storage.Storage import info.nightscout.androidaps.utils.storage.Storage
import javax.inject.Singleton import javax.inject.Singleton
@ -45,6 +48,10 @@ open class AppModule {
return FileStorage() return FileStorage()
} }
@Provides
@Singleton
internal fun provideSchedulers(): AapsSchedulers = DefaultAapsSchedulers()
@Module @Module
interface AppBindings { interface AppBindings {
@ -58,5 +65,6 @@ open class AppModule {
@Binds fun bindDatabaseHelperInterface(databaseHelperProvider: DatabaseHelperProvider): DatabaseHelperInterface @Binds fun bindDatabaseHelperInterface(databaseHelperProvider: DatabaseHelperProvider): DatabaseHelperInterface
@Binds fun bindUploadQueueInterface(uploadQueue: UploadQueue): UploadQueueInterface @Binds fun bindUploadQueueInterface(uploadQueue: UploadQueue): UploadQueueInterface
@Binds fun bindNotificationHolderInterface(notificationHolder: NotificationHolder): NotificationHolderInterface @Binds fun bindNotificationHolderInterface(notificationHolder: NotificationHolder): NotificationHolderInterface
@Binds fun bindImportExportPrefsInterface(importExportPrefs: ImportExportPrefs): ImportExportPrefsInterface
} }
} }

View file

@ -2,11 +2,9 @@ package info.nightscout.androidaps.dependencyInjection
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.db.DatabaseHelper
import info.nightscout.androidaps.db.*
import info.nightscout.androidaps.interfaces.ProfileStore
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
import info.nightscout.androidaps.plugins.general.food.FoodService 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.plugins.treatments.TreatmentService
import info.nightscout.androidaps.utils.wizard.BolusWizard import info.nightscout.androidaps.utils.wizard.BolusWizard
import info.nightscout.androidaps.utils.wizard.QuickWizardEntry import info.nightscout.androidaps.utils.wizard.QuickWizardEntry

View file

@ -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.insight.LocalInsightFragment
import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicFragment import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicFragment
import info.nightscout.androidaps.plugins.pump.medtronic.dialog.RileyLinkStatusDeviceMedtronic 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.pump.virtual.VirtualPumpFragment
import info.nightscout.androidaps.plugins.source.BGSourceFragment import info.nightscout.androidaps.plugins.source.BGSourceFragment
import info.nightscout.androidaps.plugins.treatments.TreatmentsFragment import info.nightscout.androidaps.plugins.treatments.TreatmentsFragment
@ -77,18 +77,13 @@ abstract class FragmentsModule {
@ContributesAndroidInjector abstract fun contributesTidepoolFragment(): TidepoolFragment @ContributesAndroidInjector abstract fun contributesTidepoolFragment(): TidepoolFragment
@ContributesAndroidInjector abstract fun contributesTreatmentsFragment(): TreatmentsFragment @ContributesAndroidInjector abstract fun contributesTreatmentsFragment(): TreatmentsFragment
@ContributesAndroidInjector @ContributesAndroidInjector abstract fun contributesTreatmentsBolusFragment(): TreatmentsBolusFragment
abstract fun contributesTreatmentsBolusFragment(): TreatmentsBolusFragment @ContributesAndroidInjector abstract fun contributesTreatmentsTemporaryBasalsFragment(): TreatmentsTemporaryBasalsFragment
@ContributesAndroidInjector @ContributesAndroidInjector abstract fun contributesTreatmentsTempTargetFragment(): TreatmentsTempTargetFragment
abstract fun contributesTreatmentsTemporaryBasalsFragment(): TreatmentsTemporaryBasalsFragment @ContributesAndroidInjector abstract fun contributesTreatmentsExtendedBolusesFragment(): TreatmentsExtendedBolusesFragment
@ContributesAndroidInjector @ContributesAndroidInjector abstract fun contributesTreatmentsCareportalFragment(): TreatmentsCareportalFragment
abstract fun contributesTreatmentsTempTargetFragment(): TreatmentsTempTargetFragment @ContributesAndroidInjector abstract fun contributesTreatmentsProfileSwitchFragment(): TreatmentsProfileSwitchFragment
@ContributesAndroidInjector @ContributesAndroidInjector abstract fun contributesTreatmentsUserEntryFragment(): TreatmentsUserEntryFragment
abstract fun contributesTreatmentsExtendedBolusesFragment(): TreatmentsExtendedBolusesFragment
@ContributesAndroidInjector
abstract fun contributesTreatmentsCareportalFragment(): TreatmentsCareportalFragment
@ContributesAndroidInjector
abstract fun contributesTreatmentsProfileSwitchFragment(): TreatmentsProfileSwitchFragment
@ContributesAndroidInjector abstract fun contributesVirtualPumpFragment(): VirtualPumpFragment @ContributesAndroidInjector abstract fun contributesVirtualPumpFragment(): VirtualPumpFragment

View file

@ -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.insight.LocalInsightPlugin
import info.nightscout.androidaps.plugins.pump.mdi.MDIPlugin import info.nightscout.androidaps.plugins.pump.mdi.MDIPlugin
import info.nightscout.androidaps.plugins.pump.medtronic.MedtronicPumpPlugin 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.pump.virtual.VirtualPumpPlugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin
@ -160,7 +160,7 @@ abstract class PluginsModule {
@PumpDriver @PumpDriver
@IntoMap @IntoMap
@IntKey(155) @IntKey(155)
abstract fun bindOmnipodPumpPlugin(plugin: OmnipodPumpPlugin): PluginBase abstract fun bindOmnipodPumpPlugin(plugin: OmnipodErosPumpPlugin): PluginBase
@Binds @Binds
@NotNSClient @NotNSClient
@ -205,7 +205,7 @@ abstract class PluginsModule {
abstract fun bindLocalProfilePlugin(plugin: LocalProfilePlugin): PluginBase abstract fun bindLocalProfilePlugin(plugin: LocalProfilePlugin): PluginBase
@Binds @Binds
@APS @AllConfigs
@IntoMap @IntoMap
@IntKey(250) @IntKey(250)
abstract fun bindAutomationPlugin(plugin: AutomationPlugin): PluginBase abstract fun bindAutomationPlugin(plugin: AutomationPlugin): PluginBase

View file

@ -2,7 +2,6 @@ package info.nightscout.androidaps.dependencyInjection
import dagger.Module import dagger.Module
import dagger.android.ContributesAndroidInjector 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.PrefFileListProvider
import info.nightscout.androidaps.plugins.general.maintenance.formats.ClassicPrefsFormat import info.nightscout.androidaps.plugins.general.maintenance.formats.ClassicPrefsFormat
import info.nightscout.androidaps.plugins.general.maintenance.formats.EncryptedPrefsFormat import info.nightscout.androidaps.plugins.general.maintenance.formats.EncryptedPrefsFormat
@ -13,7 +12,6 @@ import info.nightscout.androidaps.utils.CryptoUtil
abstract class PreferencesModule { abstract class PreferencesModule {
@ContributesAndroidInjector abstract fun cryptoUtilInjector(): CryptoUtil @ContributesAndroidInjector abstract fun cryptoUtilInjector(): CryptoUtil
@ContributesAndroidInjector abstract fun importExportPrefsInjector(): ImportExportPrefs
@ContributesAndroidInjector abstract fun encryptedPrefsFormatInjector(): EncryptedPrefsFormat @ContributesAndroidInjector abstract fun encryptedPrefsFormatInjector(): EncryptedPrefsFormat
@ContributesAndroidInjector abstract fun classicPrefsFormatInjector(): ClassicPrefsFormat @ContributesAndroidInjector abstract fun classicPrefsFormatInjector(): ClassicPrefsFormat
@ContributesAndroidInjector abstract fun prefImportListProviderInjector(): PrefFileListProvider @ContributesAndroidInjector abstract fun prefImportListProviderInjector(): PrefFileListProvider

View file

@ -12,6 +12,7 @@ import info.nightscout.androidaps.receivers.*
@Suppress("unused") @Suppress("unused")
abstract class ReceiversModule { abstract class ReceiversModule {
@ContributesAndroidInjector abstract fun contributesAutoStartReceiver(): AutoStartReceiver
@ContributesAndroidInjector abstract fun contributesBTReceiver(): BTReceiver @ContributesAndroidInjector abstract fun contributesBTReceiver(): BTReceiver
@ContributesAndroidInjector abstract fun contributesChargingStateReceiver(): ChargingStateReceiver @ContributesAndroidInjector abstract fun contributesChargingStateReceiver(): ChargingStateReceiver
@ContributesAndroidInjector abstract fun contributesDataReceiver(): DataReceiver @ContributesAndroidInjector abstract fun contributesDataReceiver(): DataReceiver

View file

@ -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.InsightAlertService
import info.nightscout.androidaps.plugins.pump.insight.connection_service.InsightConnectionService 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.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.AlarmSoundService
import info.nightscout.androidaps.services.DataService
import info.nightscout.androidaps.services.LocationService import info.nightscout.androidaps.services.LocationService
@Module @Module
@ -20,7 +19,6 @@ import info.nightscout.androidaps.services.LocationService
abstract class ServicesModule { abstract class ServicesModule {
@ContributesAndroidInjector abstract fun contributesAlarmSoundService(): AlarmSoundService @ContributesAndroidInjector abstract fun contributesAlarmSoundService(): AlarmSoundService
@ContributesAndroidInjector abstract fun contributesDataService(): DataService
@ContributesAndroidInjector abstract fun contributesDismissNotificationService(): DismissNotificationService @ContributesAndroidInjector abstract fun contributesDismissNotificationService(): DismissNotificationService
@ContributesAndroidInjector abstract fun contributesDummyService(): DummyService @ContributesAndroidInjector abstract fun contributesDummyService(): DummyService
@ContributesAndroidInjector abstract fun contributesLocationService(): LocationService @ContributesAndroidInjector abstract fun contributesLocationService(): LocationService

View file

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

View file

@ -9,14 +9,14 @@ import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.databinding.DialogCalibrationBinding
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.XdripCalibrations import info.nightscout.androidaps.utils.XdripCalibrations
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.resources.ResourceHelper 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.text.DecimalFormat
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
@ -27,16 +27,24 @@ class CalibrationDialog : DialogFragmentWithDate() {
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var xdripCalibrations: XdripCalibrations @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) { override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState) 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?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? { savedInstanceState: Bundle?): View {
onCreateViewGeneral() 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -46,24 +54,30 @@ class CalibrationDialog : DialogFragmentWithDate() {
val bg = Profile.fromMgdlToUnits(GlucoseStatus(injector).glucoseStatusData?.glucose val bg = Profile.fromMgdlToUnits(GlucoseStatus(injector).glucoseStatusData?.glucose
?: 0.0, units) ?: 0.0, units)
if (units == Constants.MMOL) if (units == Constants.MMOL)
overview_calibration_bg.setParams(savedInstanceState?.getDouble("overview_calibration_bg") binding.bg.setParams(savedInstanceState?.getDouble("bg")
?: bg, 2.0, 30.0, 0.1, DecimalFormat("0.0"), false, ok) ?: bg, 2.0, 30.0, 0.1, DecimalFormat("0.0"), false, binding.okcancel.ok)
else else
overview_calibration_bg.setParams(savedInstanceState?.getDouble("overview_calibration_bg") binding.bg.setParams(savedInstanceState?.getDouble("bg")
?: bg, 36.0, 500.0, 1.0, DecimalFormat("0"), false, ok) ?: bg, 36.0, 500.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok)
overview_calibration_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)
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
} }
override fun submit(): Boolean { override fun submit(): Boolean {
if (_binding == null) return false
val units = profileFunction.getUnits() val units = profileFunction.getUnits()
val unitLabel = if (units == Constants.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl) val unitLabel = if (units == Constants.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl)
val actions: LinkedList<String?> = LinkedList() val actions: LinkedList<String?> = 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) actions.add(resourceHelper.gs(R.string.treatments_wizard_bg_label) + ": " + Profile.toCurrentUnitsString(profileFunction, bg) + " " + unitLabel)
if (bg > 0) { if (bg > 0) {
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.overview_calibration), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), Runnable { OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.overview_calibration), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
aapsLogger.debug("USER ENTRY: CALIBRATION $bg") uel.log("CALIBRATION", d1 = bg)
xdripCalibrations.sendIntent(bg) xdripCalibrations.sendIntent(bg)
}) })
} }

View file

@ -11,11 +11,13 @@ import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.databinding.DialogCarbsBinding
import info.nightscout.androidaps.db.CareportalEvent import info.nightscout.androidaps.db.CareportalEvent
import info.nightscout.androidaps.db.Source import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.db.TempTarget import info.nightscout.androidaps.db.TempTarget
import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin 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.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.formatColor import info.nightscout.androidaps.utils.extensions.formatColor
import info.nightscout.androidaps.utils.resources.ResourceHelper 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.text.DecimalFormat
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.max import kotlin.math.max
class CarbsDialog : DialogFragmentWithDate() { class CarbsDialog : DialogFragmentWithDate() {
@Inject lateinit var mainApp: MainApp @Inject lateinit var mainApp: MainApp
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var constraintChecker: ConstraintChecker
@ -47,8 +47,10 @@ class CarbsDialog : DialogFragmentWithDate() {
@Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin @Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin
@Inject lateinit var nsUpload: NSUpload @Inject lateinit var nsUpload: NSUpload
@Inject lateinit var carbsGenerator: CarbsGenerator @Inject lateinit var carbsGenerator: CarbsGenerator
@Inject lateinit var uel: UserEntryLogger
companion object { companion object {
private const val FAV1_DEFAULT = 5 private const val FAV1_DEFAULT = 5
private const val FAV2_DEFAULT = 10 private const val FAV2_DEFAULT = 10
private const val FAV3_DEFAULT = 20 private const val FAV3_DEFAULT = 20
@ -65,92 +67,105 @@ class CarbsDialog : DialogFragmentWithDate() {
private fun validateInputs() { private fun validateInputs() {
val maxCarbs = constraintChecker.getMaxCarbsAllowed().value().toDouble() 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) { 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)) ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.constraintapllied))
} }
if (overview_carbs_duration.value > 10) { if (binding.duration.value > 10) {
overview_carbs_duration.value = 0.0 binding.duration.value = 0.0
ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.constraintapllied)) ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.constraintapllied))
} }
if (overview_carbs_carbs.value.toInt() > maxCarbs) { if (binding.carbs.value.toInt() > maxCarbs) {
overview_carbs_carbs.value = 0.0 binding.carbs.value = 0.0
ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.carbsconstraintapplied)) 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) { override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState) super.onSaveInstanceState(savedInstanceState)
savedInstanceState.putDouble("overview_carbs_time", overview_carbs_time.value) savedInstanceState.putDouble("time", binding.time.value)
savedInstanceState.putDouble("overview_carbs_duration", overview_carbs_duration.value) savedInstanceState.putDouble("duration", binding.duration.value)
savedInstanceState.putDouble("overview_carbs_carbs", overview_carbs_carbs.value) savedInstanceState.putDouble("carbs", binding.carbs.value)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? { savedInstanceState: Bundle?): View {
onCreateViewGeneral() 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
val maxCarbs = constraintChecker.getMaxCarbsAllowed().value().toDouble() val maxCarbs = constraintChecker.getMaxCarbsAllowed().value().toDouble()
overview_carbs_time.setParams(savedInstanceState?.getDouble("overview_carbs_time") binding.time.setParams(savedInstanceState?.getDouble("time")
?: 0.0, -12 * 60.0, 12 * 60.0, 5.0, DecimalFormat("0"), false, ok, textWatcher) ?: 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") binding.duration.setParams(savedInstanceState?.getDouble("duration")
?: 0.0, 0.0, 10.0, 1.0, DecimalFormat("0"), false, ok, textWatcher) ?: 0.0, 0.0, 10.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher)
overview_carbs_carbs.setParams(savedInstanceState?.getDouble("overview_carbs_carbs") binding.carbs.setParams(savedInstanceState?.getDouble("carbs")
?: 0.0, 0.0, maxCarbs, 1.0, DecimalFormat("0"), false, ok, textWatcher) ?: 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)) binding.plus1.text = toSignedString(sp.getInt(R.string.key_carbs_button_increment_1, FAV1_DEFAULT))
overview_carbs_plus1.setOnClickListener { binding.plus1.setOnClickListener {
overview_carbs_carbs.value = max(0.0, overview_carbs_carbs.value binding.carbs.value = max(0.0, binding.carbs.value
+ sp.getInt(R.string.key_carbs_button_increment_1, FAV1_DEFAULT)) + sp.getInt(R.string.key_carbs_button_increment_1, FAV1_DEFAULT))
validateInputs() validateInputs()
} }
overview_carbs_plus2.text = toSignedString(sp.getInt(R.string.key_carbs_button_increment_2, FAV2_DEFAULT)) binding.plus2.text = toSignedString(sp.getInt(R.string.key_carbs_button_increment_2, FAV2_DEFAULT))
overview_carbs_plus2.setOnClickListener { binding.plus2.setOnClickListener {
overview_carbs_carbs.value = max(0.0, overview_carbs_carbs.value binding.carbs.value = max(0.0, binding.carbs.value
+ sp.getInt(R.string.key_carbs_button_increment_2, FAV2_DEFAULT)) + sp.getInt(R.string.key_carbs_button_increment_2, FAV2_DEFAULT))
validateInputs() validateInputs()
} }
overview_carbs_plus3.text = toSignedString(sp.getInt(R.string.key_carbs_button_increment_3, FAV3_DEFAULT)) binding.plus3.text = toSignedString(sp.getInt(R.string.key_carbs_button_increment_3, FAV3_DEFAULT))
overview_carbs_plus3.setOnClickListener { binding.plus3.setOnClickListener {
overview_carbs_carbs.value = max(0.0, overview_carbs_carbs.value binding.carbs.value = max(0.0, binding.carbs.value
+ sp.getInt(R.string.key_carbs_button_increment_3, FAV3_DEFAULT)) + sp.getInt(R.string.key_carbs_button_increment_3, FAV3_DEFAULT))
validateInputs() validateInputs()
} }
iobCobCalculatorPlugin.actualBg()?.let { bgReading -> iobCobCalculatorPlugin.actualBg()?.let { bgReading ->
if (bgReading.value < 72) if (bgReading.value < 72)
overview_carbs_hypo_tt.isChecked = true binding.hypoTt.isChecked = true
} }
overview_carbs_hypo_tt.setOnClickListener { binding.hypoTt.setOnClickListener {
overview_carbs_activity_tt.isChecked = false binding.activityTt.isChecked = false
overview_carbs_eating_soon_tt.isChecked = false binding.eatingSoonTt.isChecked = false
} }
overview_carbs_activity_tt.setOnClickListener { binding.activityTt.setOnClickListener {
overview_carbs_hypo_tt.isChecked = false binding.hypoTt.isChecked = false
overview_carbs_eating_soon_tt.isChecked = false binding.eatingSoonTt.isChecked = false
} }
overview_carbs_eating_soon_tt.setOnClickListener { binding.eatingSoonTt.setOnClickListener {
overview_carbs_hypo_tt.isChecked = false binding.hypoTt.isChecked = false
overview_carbs_activity_tt.isChecked = false binding.activityTt.isChecked = false
} }
} }
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
private fun toSignedString(value: Int): String { private fun toSignedString(value: Int): String {
return if (value > 0) "+$value" else value.toString() return if (value > 0) "+$value" else value.toString()
} }
override fun submit(): Boolean { 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 carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value()
val units = profileFunction.getUnits() val units = profileFunction.getUnits()
val activityTTDuration = defaultValueHelper.determineActivityTTDuration() val activityTTDuration = defaultValueHelper.determineActivityTTDuration()
@ -162,22 +177,22 @@ class CarbsDialog : DialogFragmentWithDate() {
val actions: LinkedList<String?> = LinkedList() val actions: LinkedList<String?> = LinkedList()
val unitLabel = if (units == Constants.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl) 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) 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)) 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) 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)) 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) 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)) 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 eventTime -= eventTime % 1000
val time = eventTime + timeOffset * 1000 * 60 val time = eventTime + timeOffset * 1000 * 60
if (timeOffset != 0) if (timeOffset != 0)
actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(time)) 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) if (duration > 0)
actions.add(resourceHelper.gs(R.string.duration) + ": " + duration + resourceHelper.gs(R.string.shorthour)) actions.add(resourceHelper.gs(R.string.duration) + ": " + duration + resourceHelper.gs(R.string.shorthour))
if (carbsAfterConstraints > 0) { if (carbsAfterConstraints > 0) {
@ -185,7 +200,7 @@ class CarbsDialog : DialogFragmentWithDate() {
if (carbsAfterConstraints != carbs) if (carbsAfterConstraints != carbs)
actions.add("<font color='" + resourceHelper.gc(R.color.warning) + "'>" + resourceHelper.gs(R.string.carbsconstraintapplied) + "</font>") actions.add("<font color='" + resourceHelper.gc(R.color.warning) + "'>" + resourceHelper.gs(R.string.carbsconstraintapplied) + "</font>")
} }
val notes = notes.text.toString() val notes = binding.notesLayout.notes.text.toString()
if (notes.isNotEmpty()) if (notes.isNotEmpty())
actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes) actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes)
@ -194,10 +209,10 @@ class CarbsDialog : DialogFragmentWithDate() {
if (carbsAfterConstraints > 0 || activitySelected || eatingSoonSelected || hypoSelected) { if (carbsAfterConstraints > 0 || activitySelected || eatingSoonSelected || hypoSelected) {
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.carbs), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), Runnable { OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.carbs), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
when { when {
activitySelected -> { activitySelected -> {
aapsLogger.debug("USER ENTRY: TEMPTARGET ACTIVITY $activityTT duration: $activityTTDuration") uel.log("TT ACTIVITY", d1 = activityTT, i1 = activityTTDuration)
val tempTarget = TempTarget() val tempTarget = TempTarget()
.date(System.currentTimeMillis()) .date(System.currentTimeMillis())
.duration(activityTTDuration) .duration(activityTTDuration)
@ -209,7 +224,7 @@ class CarbsDialog : DialogFragmentWithDate() {
} }
eatingSoonSelected -> { eatingSoonSelected -> {
aapsLogger.debug("USER ENTRY: TEMPTARGET EATING SOON $eatingSoonTT duration: $eatingSoonTTDuration") uel.log("TT EATING SOON", d1 = eatingSoonTT, i1 = eatingSoonTTDuration)
val tempTarget = TempTarget() val tempTarget = TempTarget()
.date(System.currentTimeMillis()) .date(System.currentTimeMillis())
.duration(eatingSoonTTDuration) .duration(eatingSoonTTDuration)
@ -221,7 +236,7 @@ class CarbsDialog : DialogFragmentWithDate() {
} }
hypoSelected -> { hypoSelected -> {
aapsLogger.debug("USER ENTRY: TEMPTARGET HYPO $hypoTT duration: $hypoTTDuration") uel.log("TT HYPO", d1 = hypoTT, i1 = hypoTTDuration)
val tempTarget = TempTarget() val tempTarget = TempTarget()
.date(System.currentTimeMillis()) .date(System.currentTimeMillis())
.duration(hypoTTDuration) .duration(hypoTTDuration)
@ -234,10 +249,10 @@ class CarbsDialog : DialogFragmentWithDate() {
} }
if (carbsAfterConstraints > 0) { if (carbsAfterConstraints > 0) {
if (duration == 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) carbsGenerator.createCarb(carbsAfterConstraints, time, CareportalEvent.CARBCORRECTION, notes)
} else { } 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) carbsGenerator.generateCarbs(carbsAfterConstraints, time, duration, notes)
nsUpload.uploadEvent(CareportalEvent.NOTE, DateUtil.now() - 2000, resourceHelper.gs(R.string.generated_ecarbs_note, carbsAfterConstraints, duration, timeOffset)) nsUpload.uploadEvent(CareportalEvent.NOTE, DateUtil.now() - 2000, resourceHelper.gs(R.string.generated_ecarbs_note, carbsAfterConstraints, duration, timeOffset))
} }

View file

@ -13,9 +13,11 @@ import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.databinding.DialogCareBinding
import info.nightscout.androidaps.db.CareportalEvent import info.nightscout.androidaps.db.CareportalEvent
import info.nightscout.androidaps.db.Source import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.interfaces.ProfileFunction 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.general.nsclient.NSUpload
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
import info.nightscout.androidaps.utils.DateUtil 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.Translator
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import kotlinx.android.synthetic.main.dialog_care.*
import kotlinx.android.synthetic.main.notes.*
import kotlinx.android.synthetic.main.okcancel.*
import org.json.JSONObject import org.json.JSONObject
import java.text.DecimalFormat import java.text.DecimalFormat
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
class CareDialog : DialogFragmentWithDate() { class CareDialog : DialogFragmentWithDate() {
@Inject lateinit var injector: HasAndroidInjector @Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var mainApp: MainApp @Inject lateinit var mainApp: MainApp
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var nsUpload: NSUpload @Inject lateinit var nsUpload: NSUpload
@Inject lateinit var translator: Translator @Inject lateinit var translator: Translator
@Inject lateinit var uel: UserEntryLogger
enum class EventType { enum class EventType {
BGCHECK, BGCHECK,
@ -60,18 +61,25 @@ class CareDialog : DialogFragmentWithDate() {
return this 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) { override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState) super.onSaveInstanceState(savedInstanceState)
savedInstanceState.putDouble("actions_care_bg", actions_care_bg.value) savedInstanceState.putDouble("bg", binding.bg.value)
savedInstanceState.putDouble("actions_care_duration", actions_care_duration.value) savedInstanceState.putDouble("duration", binding.duration.value)
savedInstanceState.putInt("event", event) savedInstanceState.putInt("event", event)
savedInstanceState.putInt("options", options.ordinal) savedInstanceState.putInt("options", options.ordinal)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? { savedInstanceState: Bundle?): View {
onCreateViewGeneral() 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -82,7 +90,7 @@ class CareDialog : DialogFragmentWithDate() {
options = EventType.values()[savedInstanceState.getInt("options", 0)] 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.BGCHECK -> R.drawable.ic_cp_bgcheck
EventType.SENSOR_INSERT -> R.drawable.ic_cp_cgm_insert EventType.SENSOR_INSERT -> R.drawable.ic_cp_cgm_insert
EventType.BATTERY_CHANGE -> R.drawable.ic_cp_pump_battery EventType.BATTERY_CHANGE -> R.drawable.ic_cp_pump_battery
@ -91,7 +99,7 @@ class CareDialog : DialogFragmentWithDate() {
EventType.QUESTION -> R.drawable.ic_cp_question EventType.QUESTION -> R.drawable.ic_cp_question
EventType.ANNOUNCEMENT -> R.drawable.ic_cp_announcement 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.BGCHECK -> R.string.careportal_bgcheck
EventType.SENSOR_INSERT -> R.string.careportal_cgmsensorinsert EventType.SENSOR_INSERT -> R.string.careportal_cgmsensorinsert
EventType.BATTERY_CHANGE -> R.string.careportal_pumpbatterychange EventType.BATTERY_CHANGE -> R.string.careportal_pumpbatterychange
@ -104,21 +112,21 @@ class CareDialog : DialogFragmentWithDate() {
when (options) { when (options) {
EventType.QUESTION, EventType.QUESTION,
EventType.ANNOUNCEMENT, EventType.ANNOUNCEMENT,
EventType.BGCHECK -> { EventType.BGCHECK -> {
action_care_duration_layout.visibility = View.GONE binding.durationLayout.visibility = View.GONE
} }
EventType.SENSOR_INSERT, EventType.SENSOR_INSERT,
EventType.BATTERY_CHANGE -> { EventType.BATTERY_CHANGE -> {
action_care_bg_layout.visibility = View.GONE binding.bgLayout.visibility = View.GONE
actions_care_bgsource.visibility = View.GONE binding.bgsource.visibility = View.GONE
action_care_duration_layout.visibility = View.GONE binding.durationLayout.visibility = View.GONE
} }
EventType.NOTE, EventType.NOTE,
EventType.EXERCISE -> { EventType.EXERCISE -> {
action_care_bg_layout.visibility = View.GONE binding.bgLayout.visibility = View.GONE
actions_care_bgsource.visibility = View.GONE binding.bgsource.visibility = View.GONE
} }
} }
@ -128,23 +136,28 @@ class CareDialog : DialogFragmentWithDate() {
override fun afterTextChanged(s: Editable) {} override fun afterTextChanged(s: Editable) {}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
if (actions_care_sensor.isChecked) actions_care_meter.isChecked = true if (binding.sensor.isChecked) binding.meter.isChecked = true
} }
} }
if (profileFunction.getUnits() == Constants.MMOL) { if (profileFunction.getUnits() == Constants.MMOL) {
actions_care_bgunits.text = resourceHelper.gs(R.string.mmol) binding.bgunits.text = resourceHelper.gs(R.string.mmol)
actions_care_bg.setParams(savedInstanceState?.getDouble("actions_care_bg") binding.bg.setParams(savedInstanceState?.getDouble("bg")
?: bg, 2.0, 30.0, 0.1, DecimalFormat("0.0"), false, ok, bgTextWatcher) ?: bg, 2.0, 30.0, 0.1, DecimalFormat("0.0"), false, binding.okcancel.ok, bgTextWatcher)
} else { } else {
actions_care_bgunits.text = resourceHelper.gs(R.string.mgdl) binding.bgunits.text = resourceHelper.gs(R.string.mgdl)
actions_care_bg.setParams(savedInstanceState?.getDouble("actions_care_bg") binding.bg.setParams(savedInstanceState?.getDouble("bg")
?: bg, 36.0, 500.0, 1.0, DecimalFormat("0"), false, ok, bgTextWatcher) ?: bg, 36.0, 500.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, bgTextWatcher)
} }
actions_care_duration.setParams(savedInstanceState?.getDouble("actions_care_duration") binding.duration.setParams(savedInstanceState?.getDouble("duration")
?: 0.0, 0.0, Constants.MAX_PROFILE_SWITCH_DURATION, 10.0, DecimalFormat("0"), false, ok) ?: 0.0, 0.0, Constants.MAX_PROFILE_SWITCH_DURATION, 10.0, DecimalFormat("0"), false, binding.okcancel.ok)
if (options == EventType.NOTE || options == EventType.QUESTION || options == EventType.ANNOUNCEMENT) if (options == EventType.NOTE || options == EventType.QUESTION || options == EventType.ANNOUNCEMENT || options == EventType.EXERCISE)
notes_layout?.visibility = View.VISIBLE // independent to preferences binding.notesLayout.root.visibility = View.VISIBLE // independent to preferences
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
} }
override fun submit(): Boolean { override fun submit(): Boolean {
@ -156,20 +169,20 @@ class CareDialog : DialogFragmentWithDate() {
if (options == EventType.BGCHECK || options == EventType.QUESTION || options == EventType.ANNOUNCEMENT) { if (options == EventType.BGCHECK || options == EventType.QUESTION || options == EventType.ANNOUNCEMENT) {
val type = val type =
when { when {
actions_care_meter.isChecked -> CareportalEvent.FINGER binding.meter.isChecked -> CareportalEvent.FINGER
actions_care_sensor.isChecked -> CareportalEvent.SENSOR binding.sensor.isChecked -> CareportalEvent.SENSOR
else -> CareportalEvent.MANUAL else -> CareportalEvent.MANUAL
} }
actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_glucosetype) + ": " + translator.translate(type)) 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)) actions.add(resourceHelper.gs(R.string.treatments_wizard_bg_label) + ": " + Profile.toCurrentUnitsString(profileFunction, binding.bg.value) + " " + resourceHelper.gs(unitResId))
json.put("glucose", actions_care_bg.value) json.put("glucose", binding.bg.value)
json.put("glucoseType", type) json.put("glucoseType", type)
} }
if (options == EventType.NOTE || options == EventType.EXERCISE) { 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())) actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_duration_label) + ": " + resourceHelper.gs(R.string.format_mins, binding.duration.value.toInt()))
json.put("duration", actions_care_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()) { if (notes.isNotEmpty()) {
actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes) actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes)
json.put("notes", notes) json.put("notes", notes)
@ -195,7 +208,7 @@ class CareDialog : DialogFragmentWithDate() {
json.put("enteredBy", enteredBy) json.put("enteredBy", enteredBy)
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(event), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), Runnable { OKDialog.showConfirmation(activity, resourceHelper.gs(event), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
val careportalEvent = CareportalEvent(injector) val careportalEvent = CareportalEvent(injector)
careportalEvent.date = eventTime careportalEvent.date = eventTime
careportalEvent.source = Source.USER careportalEvent.source = Source.USER
@ -209,7 +222,7 @@ class CareDialog : DialogFragmentWithDate() {
EventType.ANNOUNCEMENT -> CareportalEvent.ANNOUNCEMENT EventType.ANNOUNCEMENT -> CareportalEvent.ANNOUNCEMENT
} }
careportalEvent.json = json.toString() careportalEvent.json = json.toString()
aapsLogger.debug("USER ENTRY: CAREPORTAL ${careportalEvent.eventType} json: ${careportalEvent.json}") uel.log("CAREPORTAL", careportalEvent.eventType)
MainApp.getDbHelper().createOrUpdate(careportalEvent) MainApp.getDbHelper().createOrUpdate(careportalEvent)
nsUpload.uploadCareportalEntryToNS(json) nsUpload.uploadCareportalEntryToNS(json)
}, null) }, null)

View file

@ -8,6 +8,8 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.Window import android.view.Window
import android.view.WindowManager import android.view.WindowManager
import android.widget.Button
import android.widget.TextView
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import dagger.android.support.DaggerDialogFragment import dagger.android.support.DaggerDialogFragment
import info.nightscout.androidaps.R 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.DateUtil
import info.nightscout.androidaps.utils.extensions.toVisibility import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.utils.sharedPreferences.SP 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 java.util.*
import javax.inject.Inject import javax.inject.Inject
@ -58,10 +57,14 @@ abstract class DialogFragmentWithDate : DaggerDialogFragment() {
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 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() eventTime = savedInstanceState?.getLong("eventTime") ?: DateUtil.now()
eventTimeChanged = savedInstanceState?.getBoolean("eventTimeChanged") ?: false 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 // create an OnDateSetListener
val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth -> val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth ->
@ -72,10 +75,10 @@ abstract class DialogFragmentWithDate : DaggerDialogFragment() {
cal.set(Calendar.DAY_OF_MONTH, dayOfMonth) cal.set(Calendar.DAY_OF_MONTH, dayOfMonth)
eventTime = cal.timeInMillis eventTime = cal.timeInMillis
eventTimeChanged = true eventTimeChanged = true
overview_eventdate?.text = DateUtil.dateString(eventTime) eventDateView?.text = DateUtil.dateString(eventTime)
} }
overview_eventdate?.setOnClickListener { eventDateView?.setOnClickListener {
context?.let { context?.let {
val cal = Calendar.getInstance() val cal = Calendar.getInstance()
cal.timeInMillis = eventTime 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 cal.set(Calendar.SECOND, seconds++) // randomize seconds to prevent creating record of the same time, if user choose time manually
eventTime = cal.timeInMillis eventTime = cal.timeInMillis
eventTimeChanged = true eventTimeChanged = true
overview_eventtime?.text = dateUtil.timeString(eventTime) eventTimeView?.text = dateUtil.timeString(eventTime)
} }
overview_eventtime?.setOnClickListener { eventTimeView?.setOnClickListener {
context?.let { context?.let {
val cal = Calendar.getInstance() val cal = Calendar.getInstance()
cal.timeInMillis = eventTime 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) { synchronized(okClicked) {
if (okClicked) { if (okClicked) {
aapsLogger.warn(LTag.UI, "guarding: ok already clicked") 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?) { override fun show(manager: FragmentManager, tag: String?) {

View file

@ -1,48 +1,56 @@
package info.nightscout.androidaps.dialogs package info.nightscout.androidaps.dialogs
import android.content.Intent import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import com.google.common.base.Joiner import com.google.common.base.Joiner
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.ErrorHelperActivity import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.databinding.DialogExtendedbolusBinding
import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.SafeParse 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.extensions.formatColor
import info.nightscout.androidaps.utils.resources.ResourceHelper 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.text.DecimalFormat
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.abs import kotlin.math.abs
class ExtendedBolusDialog : DialogFragmentWithDate() { class ExtendedBolusDialog : DialogFragmentWithDate() {
@Inject lateinit var mainApp: MainApp
@Inject lateinit var ctx: Context
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var commandQueue: CommandQueueProvider @Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var activePlugin: ActivePluginProvider @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) { override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState) super.onSaveInstanceState(savedInstanceState)
savedInstanceState.putDouble("actions_extendedbolus_insulin", actions_extendedbolus_insulin.value) savedInstanceState.putDouble("insulin", binding.insulin.value)
savedInstanceState.putDouble("actions_extendedbolus_duration", actions_extendedbolus_duration.value) savedInstanceState.putDouble("duration", binding.duration.value)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? { savedInstanceState: Bundle?): View {
onCreateViewGeneral() 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -52,18 +60,24 @@ class ExtendedBolusDialog : DialogFragmentWithDate() {
val maxInsulin = constraintChecker.getMaxExtendedBolusAllowed().value() val maxInsulin = constraintChecker.getMaxExtendedBolusAllowed().value()
val extendedStep = pumpDescription.extendedBolusStep val extendedStep = pumpDescription.extendedBolusStep
actions_extendedbolus_insulin.setParams(savedInstanceState?.getDouble("actions_extendedbolus_insulin") binding.insulin.setParams(savedInstanceState?.getDouble("insulin")
?: extendedStep, extendedStep, maxInsulin, extendedStep, DecimalFormat("0.00"), false, ok) ?: extendedStep, extendedStep, maxInsulin, extendedStep, DecimalFormat("0.00"), false, binding.okcancel.ok)
val extendedDurationStep = pumpDescription.extendedBolusDurationStep val extendedDurationStep = pumpDescription.extendedBolusDurationStep
val extendedMaxDuration = pumpDescription.extendedBolusMaxDuration val extendedMaxDuration = pumpDescription.extendedBolusMaxDuration
actions_extendedbolus_duration.setParams(savedInstanceState?.getDouble("actions_extendedbolus_duration") binding.duration.setParams(savedInstanceState?.getDouble("duration")
?: extendedDurationStep, extendedDurationStep, extendedMaxDuration, extendedDurationStep, DecimalFormat("0"), false, ok) ?: extendedDurationStep, extendedDurationStep, extendedMaxDuration, extendedDurationStep, DecimalFormat("0"), false, binding.okcancel.ok)
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
} }
override fun submit(): Boolean { override fun submit(): Boolean {
val insulin = SafeParse.stringToDouble(actions_extendedbolus_insulin?.text ?: return false) if (_binding == null) return false
val durationInMinutes = actions_extendedbolus_duration.value.toInt() val insulin = SafeParse.stringToDouble(binding.insulin.text ?: return false)
val durationInMinutes = binding.duration.value.toInt()
val actions: LinkedList<String> = LinkedList() val actions: LinkedList<String> = LinkedList()
val insulinAfterConstraint = constraintChecker.applyExtendedBolusConstraints(Constraint(insulin)).value() val insulinAfterConstraint = constraintChecker.applyExtendedBolusConstraints(Constraint(insulin)).value()
actions.add(resourceHelper.gs(R.string.formatinsulinunits, insulinAfterConstraint)) 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)) actions.add(resourceHelper.gs(R.string.constraintapllied).formatColor(resourceHelper, R.color.warning))
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.extended_bolus), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), Runnable { OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.extended_bolus), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
aapsLogger.debug("USER ENTRY: EXTENDED BOLUS $insulinAfterConstraint duration: $durationInMinutes") uel.log("EXTENDED BOLUS", d1 = insulinAfterConstraint, i1 = durationInMinutes)
commandQueue.extendedBolus(insulinAfterConstraint, durationInMinutes, object : Callback() { commandQueue.extendedBolus(insulinAfterConstraint, durationInMinutes, object : Callback() {
override fun run() { override fun run() {
if (!result.success) { if (!result.success) {
val i = Intent(mainApp, ErrorHelperActivity::class.java) ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
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)
} }
} }
}) })

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.dialogs package info.nightscout.androidaps.dialogs
import android.content.Context import android.content.Context
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@ -10,11 +9,13 @@ import com.google.common.base.Joiner
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.ErrorHelperActivity import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.data.DetailedBolusInfo import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.databinding.DialogFillBinding
import info.nightscout.androidaps.db.CareportalEvent import info.nightscout.androidaps.db.CareportalEvent
import info.nightscout.androidaps.db.Source import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.queue.Callback 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.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.formatColor import info.nightscout.androidaps.utils.extensions.formatColor
import info.nightscout.androidaps.utils.resources.ResourceHelper 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 java.util.*
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.abs import kotlin.math.abs
class FillDialog : DialogFragmentWithDate() { class FillDialog : DialogFragmentWithDate() {
@Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var ctx: Context @Inject lateinit var ctx: Context
@Inject lateinit var nsUpload: NSUpload @Inject lateinit var nsUpload: NSUpload
@Inject lateinit var commandQueue: CommandQueueProvider @Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var activePlugin: ActivePluginProvider @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) { override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState) 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?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? { savedInstanceState: Bundle?): View {
onCreateViewGeneral() 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -55,37 +62,43 @@ class FillDialog : DialogFragmentWithDate() {
val maxInsulin = constraintChecker.getMaxBolusAllowed().value() val maxInsulin = constraintChecker.getMaxBolusAllowed().value()
val bolusStep = activePlugin.activePump.pumpDescription.bolusStep val bolusStep = activePlugin.activePump.pumpDescription.bolusStep
fill_insulinamount.setParams(savedInstanceState?.getDouble("fill_insulin_amount") binding.fillInsulinamount.setParams(savedInstanceState?.getDouble("fill_insulin_amount")
?: 0.0, 0.0, maxInsulin, bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), true, ok) ?: 0.0, 0.0, maxInsulin, bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), true, binding.okcancel.ok)
val amount1 = sp.getDouble("fill_button1", 0.3) val amount1 = sp.getDouble("fill_button1", 0.3)
if (amount1 > 0) { if (amount1 > 0) {
fill_preset_button1.visibility = View.VISIBLE binding.fillPresetButton1.visibility = View.VISIBLE
fill_preset_button1.text = DecimalFormatter.toPumpSupportedBolus(amount1, activePlugin.activePump) // + "U"); binding.fillPresetButton1.text = DecimalFormatter.toPumpSupportedBolus(amount1, activePlugin.activePump) // + "U");
fill_preset_button1.setOnClickListener { fill_insulinamount.value = amount1 } binding.fillPresetButton1.setOnClickListener { binding.fillInsulinamount.value = amount1 }
} else { } else {
fill_preset_button1.visibility = View.GONE binding.fillPresetButton1.visibility = View.GONE
} }
val amount2 = sp.getDouble("fill_button2", 0.0) val amount2 = sp.getDouble("fill_button2", 0.0)
if (amount2 > 0) { if (amount2 > 0) {
fill_preset_button2.visibility = View.VISIBLE binding.fillPresetButton2.visibility = View.VISIBLE
fill_preset_button2.text = DecimalFormatter.toPumpSupportedBolus(amount2, activePlugin.activePump) // + "U"); binding.fillPresetButton2.text = DecimalFormatter.toPumpSupportedBolus(amount2, activePlugin.activePump) // + "U");
fill_preset_button2.setOnClickListener { fill_insulinamount.value = amount2 } binding.fillPresetButton2.setOnClickListener { binding.fillInsulinamount.value = amount2 }
} else { } else {
fill_preset_button2.visibility = View.GONE binding.fillPresetButton2.visibility = View.GONE
} }
val amount3 = sp.getDouble("fill_button3", 0.0) val amount3 = sp.getDouble("fill_button3", 0.0)
if (amount3 > 0) { if (amount3 > 0) {
fill_preset_button3.visibility = View.VISIBLE binding.fillPresetButton3.visibility = View.VISIBLE
fill_preset_button3.text = DecimalFormatter.toPumpSupportedBolus(amount3, activePlugin.activePump) // + "U"); binding.fillPresetButton3.text = DecimalFormatter.toPumpSupportedBolus(amount3, activePlugin.activePump) // + "U");
fill_preset_button3.setOnClickListener { fill_insulinamount.value = amount3 } binding.fillPresetButton3.setOnClickListener { binding.fillInsulinamount.value = amount3 }
} else { } else {
fill_preset_button3.visibility = View.GONE binding.fillPresetButton3.visibility = View.GONE
} }
} }
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
override fun submit(): Boolean { 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<String?> = LinkedList() val actions: LinkedList<String?> = LinkedList()
val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(insulin)).value() val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(insulin)).value()
@ -96,13 +109,13 @@ class FillDialog : DialogFragmentWithDate() {
if (abs(insulinAfterConstraints - insulin) > 0.01) if (abs(insulinAfterConstraints - insulin) > 0.01)
actions.add(resourceHelper.gs(R.string.bolusconstraintappliedwarn, insulin, insulinAfterConstraints).formatColor(resourceHelper, R.color.warning)) 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) if (siteChange)
actions.add(resourceHelper.gs(R.string.record_pump_site_change).formatColor(resourceHelper, R.color.actionsConfirm)) 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) if (insulinChange)
actions.add(resourceHelper.gs(R.string.record_insulin_cartridge_change).formatColor(resourceHelper, R.color.actionsConfirm)) 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()) if (notes.isNotEmpty())
actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes) actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes)
eventTime -= eventTime % 1000 eventTime -= eventTime % 1000
@ -110,20 +123,20 @@ class FillDialog : DialogFragmentWithDate() {
if (eventTimeChanged) if (eventTimeChanged)
actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(eventTime)) 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 -> activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.primefill), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), Runnable { OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.primefill), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
if (insulinAfterConstraints > 0) { if (insulinAfterConstraints > 0) {
aapsLogger.debug("USER ENTRY: PRIME BOLUS $insulinAfterConstraints") uel.log("PRIME BOLUS", d1 = insulinAfterConstraints)
requestPrimeBolus(insulinAfterConstraints, notes) requestPrimeBolus(insulinAfterConstraints, notes)
} }
if (siteChange) { if (siteChange) {
aapsLogger.debug("USER ENTRY: SITE CHANGE") uel.log("SITE CHANGE")
nsUpload.generateCareportalEvent(CareportalEvent.SITECHANGE, eventTime, notes) nsUpload.generateCareportalEvent(CareportalEvent.SITECHANGE, eventTime, notes)
} }
if (insulinChange) { if (insulinChange) {
// add a second for case of both checked // 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) nsUpload.generateCareportalEvent(CareportalEvent.INSULINCHANGE, eventTime + 1000, notes)
} }
}, null) }, null)
@ -147,12 +160,7 @@ class FillDialog : DialogFragmentWithDate() {
commandQueue.bolus(detailedBolusInfo, object : Callback() { commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() { override fun run() {
if (!result.success) { if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java) ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
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)
} }
} }
}) })

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.dialogs package info.nightscout.androidaps.dialogs
import android.content.Context import android.content.Context
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
@ -15,14 +14,17 @@ import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.ErrorHelperActivity import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.data.DetailedBolusInfo import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.data.Profile 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.CareportalEvent
import info.nightscout.androidaps.db.Source import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.db.TempTarget import info.nightscout.androidaps.db.TempTarget
import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.interfaces.ProfileFunction 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.queue.Callback
import info.nightscout.androidaps.utils.* import info.nightscout.androidaps.utils.*
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
@ -30,9 +32,6 @@ import info.nightscout.androidaps.utils.extensions.formatColor
import info.nightscout.androidaps.utils.extensions.toSignedString import info.nightscout.androidaps.utils.extensions.toSignedString
import info.nightscout.androidaps.utils.extensions.toVisibility import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.utils.resources.ResourceHelper 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.text.DecimalFormat
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
@ -40,6 +39,7 @@ import kotlin.math.abs
import kotlin.math.max import kotlin.math.max
class InsulinDialog : DialogFragmentWithDate() { class InsulinDialog : DialogFragmentWithDate() {
@Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var defaultValueHelper: DefaultValueHelper @Inject lateinit var defaultValueHelper: DefaultValueHelper
@ -47,9 +47,12 @@ class InsulinDialog : DialogFragmentWithDate() {
@Inject lateinit var commandQueue: CommandQueueProvider @Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var activePlugin: ActivePluginProvider @Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var ctx: Context @Inject lateinit var ctx: Context
@Inject lateinit var repository: AppRepository
@Inject lateinit var config: Config @Inject lateinit var config: Config
@Inject lateinit var uel: UserEntryLogger
companion object { companion object {
private const val PLUS1_DEFAULT = 0.5 private const val PLUS1_DEFAULT = 0.5
private const val PLUS2_DEFAULT = 1.0 private const val PLUS2_DEFAULT = 1.0
private const val PLUS3_DEFAULT = 2.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) {} override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
} }
private var _binding: DialogInsulinBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
private fun validateInputs() { private fun validateInputs() {
val maxInsulin = constraintChecker.getMaxBolusAllowed().value() val maxInsulin = constraintChecker.getMaxBolusAllowed().value()
if (abs(overview_insulin_time.value.toInt()) > 12 * 60) { if (abs(binding.time.value.toInt()) > 12 * 60) {
overview_insulin_time.value = 0.0 binding.time.value = 0.0
ToastUtils.showToastInUiThread(context, resourceHelper.gs(R.string.constraintapllied)) ToastUtils.showToastInUiThread(context, resourceHelper.gs(R.string.constraintapllied))
} }
if (overview_insulin_amount.value > maxInsulin) { if (binding.amount.value > maxInsulin) {
overview_insulin_amount.value = 0.0 binding.amount.value = 0.0
ToastUtils.showToastInUiThread(context, resourceHelper.gs(R.string.bolusconstraintapplied)) ToastUtils.showToastInUiThread(context, resourceHelper.gs(R.string.bolusconstraintapplied))
} }
} }
override fun onSaveInstanceState(savedInstanceState: Bundle) { override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState) super.onSaveInstanceState(savedInstanceState)
savedInstanceState.putDouble("overview_insulin_time", overview_insulin_time.value) savedInstanceState.putDouble("time", binding.time.value)
savedInstanceState.putDouble("overview_insulin_amount", overview_insulin_amount.value) savedInstanceState.putDouble("amount", binding.amount.value)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? { savedInstanceState: Bundle?): View {
onCreateViewGeneral() 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
if (config.NSCLIENT) { if (config.NSCLIENT) {
overview_insulin_record_only.isChecked = true binding.recordOnly.isChecked = true
overview_insulin_record_only.isEnabled = false binding.recordOnly.isEnabled = false
} }
val maxInsulin = constraintChecker.getMaxBolusAllowed().value() val maxInsulin = constraintChecker.getMaxBolusAllowed().value()
overview_insulin_time.setParams(savedInstanceState?.getDouble("overview_insulin_time") binding.time.setParams(savedInstanceState?.getDouble("time")
?: 0.0, -12 * 60.0, 12 * 60.0, 5.0, DecimalFormat("0"), false, ok, textWatcher) ?: 0.0, -12 * 60.0, 12 * 60.0, 5.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher)
overview_insulin_amount.setParams(savedInstanceState?.getDouble("overview_insulin_amount") binding.amount.setParams(savedInstanceState?.getDouble("amount")
?: 0.0, 0.0, maxInsulin, activePlugin.activePump.pumpDescription.bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, ok, textWatcher) ?: 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) binding.plus05.text = sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_1), PLUS1_DEFAULT).toSignedString(activePlugin.activePump)
overview_insulin_plus05.setOnClickListener { binding.plus05.setOnClickListener {
overview_insulin_amount.value = max(0.0, overview_insulin_amount.value binding.amount.value = max(0.0, binding.amount.value
+ sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_1), PLUS1_DEFAULT)) + sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_1), PLUS1_DEFAULT))
validateInputs() validateInputs()
} }
overview_insulin_plus10.text = sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_2), PLUS2_DEFAULT).toSignedString(activePlugin.activePump) binding.plus10.text = sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_2), PLUS2_DEFAULT).toSignedString(activePlugin.activePump)
overview_insulin_plus10.setOnClickListener { binding.plus10.setOnClickListener {
overview_insulin_amount.value = max(0.0, overview_insulin_amount.value binding.amount.value = max(0.0, binding.amount.value
+ sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_2), PLUS2_DEFAULT)) + sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_2), PLUS2_DEFAULT))
validateInputs() validateInputs()
} }
overview_insulin_plus20.text = sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_3), PLUS3_DEFAULT).toSignedString(activePlugin.activePump) binding.plus20.text = sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_3), PLUS3_DEFAULT).toSignedString(activePlugin.activePump)
overview_insulin_plus20.setOnClickListener { binding.plus20.setOnClickListener {
overview_insulin_amount.value = max(0.0, overview_insulin_amount.value binding.amount.value = max(0.0, binding.amount.value
+ sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_3), PLUS3_DEFAULT)) + sp.getDouble(resourceHelper.gs(R.string.key_insulin_button_increment_3), PLUS3_DEFAULT))
validateInputs() validateInputs()
} }
overview_insulin_time_layout.visibility = View.GONE binding.timeLayout.visibility = View.GONE
overview_insulin_record_only.setOnCheckedChangeListener { _, isChecked: Boolean -> binding.recordOnly.setOnCheckedChangeListener { _, isChecked: Boolean ->
overview_insulin_time_layout.visibility = isChecked.toVisibility() binding.timeLayout.visibility = isChecked.toVisibility()
} }
} }
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
override fun submit(): Boolean { override fun submit(): Boolean {
if (_binding == null) return false
val pumpDescription = activePlugin.activePump.pumpDescription 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 insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(insulin)).value()
val actions: LinkedList<String?> = LinkedList() val actions: LinkedList<String?> = LinkedList()
val units = profileFunction.getUnits() val units = profileFunction.getUnits()
val unitLabel = if (units == Constants.MMOL) resourceHelper.gs(R.string.mmol) else resourceHelper.gs(R.string.mgdl) 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 recordOnlyChecked = binding.recordOnly.isChecked
val eatingSoonChecked = overview_insulin_start_eating_soon_tt.isChecked val eatingSoonChecked = binding.startEatingSoonTt.isChecked
if (insulinAfterConstraints > 0) { if (insulinAfterConstraints > 0) {
actions.add(resourceHelper.gs(R.string.bolus) + ": " + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints, activePlugin.activePump, resourceHelper).formatColor(resourceHelper, R.color.bolus)) 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) 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)) 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() val time = DateUtil.now() + T.mins(timeOffset.toLong()).msecs()
if (timeOffset != 0) if (timeOffset != 0)
actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(time)) 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()) if (notes.isNotEmpty())
actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes) actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes)
if (insulinAfterConstraints > 0 || eatingSoonChecked) { if (insulinAfterConstraints > 0 || eatingSoonChecked) {
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.bolus), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), Runnable { OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.bolus), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
if (eatingSoonChecked) { if (eatingSoonChecked) {
aapsLogger.debug("USER ENTRY: TEMPTARGET EATING SOON $eatingSoonTT duration: $eatingSoonTTDuration") uel.log("TT EATING SOON", d1 = eatingSoonTT, i1 = eatingSoonTTDuration)
val tempTarget = TempTarget() val tempTarget = TempTarget()
.date(System.currentTimeMillis()) .date(System.currentTimeMillis())
.duration(eatingSoonTTDuration) .duration(eatingSoonTTDuration)
@ -180,21 +196,16 @@ class InsulinDialog : DialogFragmentWithDate() {
detailedBolusInfo.source = Source.USER detailedBolusInfo.source = Source.USER
detailedBolusInfo.notes = notes detailedBolusInfo.notes = notes
if (recordOnlyChecked) { if (recordOnlyChecked) {
aapsLogger.debug("USER ENTRY: BOLUS RECORD ONLY $insulinAfterConstraints") uel.log("BOLUS RECORD", d1 = insulinAfterConstraints, i1 = timeOffset)
detailedBolusInfo.date = time detailedBolusInfo.date = time
activePlugin.activeTreatments.addToHistoryTreatment(detailedBolusInfo, false) activePlugin.activeTreatments.addToHistoryTreatment(detailedBolusInfo, false)
} else { } else {
aapsLogger.debug("USER ENTRY: BOLUS $insulinAfterConstraints") uel.log("BOLUS", d1 = insulinAfterConstraints)
detailedBolusInfo.date = DateUtil.now() detailedBolusInfo.date = DateUtil.now()
commandQueue.bolus(detailedBolusInfo, object : Callback() { commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() { override fun run() {
if (!result.success) { if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java) ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
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)
} }
} }
}) })

View file

@ -1,7 +1,8 @@
package info.nightscout.androidaps.dialogs package info.nightscout.androidaps.dialogs
import android.content.Intent import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -9,7 +10,6 @@ import android.view.Window
import android.view.WindowManager import android.view.WindowManager
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import dagger.android.support.DaggerDialogFragment import dagger.android.support.DaggerDialogFragment
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.ErrorHelperActivity import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.databinding.DialogLoopBinding 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.events.EventRefreshOverview
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger 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.LoopPlugin
import info.nightscout.androidaps.plugins.aps.loop.events.EventNewOpenLoopNotification
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker 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.extensions.toVisibility
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import javax.inject.Inject import javax.inject.Inject
class LoopDialog : DaggerDialogFragment() { class LoopDialog : DaggerDialogFragment() {
@Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var mainApp: MainApp @Inject lateinit var ctx: Context
@Inject lateinit var sp: SP @Inject lateinit var sp: SP
@Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var fabricPrivacy: FabricPrivacy
@ -49,11 +47,12 @@ class LoopDialog : DaggerDialogFragment() {
@Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var commandQueue: CommandQueueProvider @Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var configBuilderPlugin: ConfigBuilderPlugin @Inject lateinit var configBuilderPlugin: ConfigBuilderPlugin
@Inject lateinit var uel: UserEntryLogger
private var disposable: CompositeDisposable = CompositeDisposable()
private var showOkCancel: Boolean = true private var showOkCancel: Boolean = true
private var _binding: DialogLoopBinding? = null private var _binding: DialogLoopBinding? = null
private var loopHandler = Handler()
private var refreshDialog: Runnable? = null
// This property is only valid between onCreateView and // This property is only valid between onCreateView and
// onDestroyView. // onDestroyView.
@ -107,21 +106,33 @@ class LoopDialog : DaggerDialogFragment() {
// cancel button // cancel button
binding.cancel.setOnClickListener { dismiss() } binding.cancel.setOnClickListener { dismiss() }
// bus refreshDialog = Runnable {
disposable.add(rxBus scheduleUpdateGUI("refreshDialog")
.toObservable(EventNewOpenLoopNotification::class.java) loopHandler.postDelayed(refreshDialog, 15 * 1000L)
.observeOn(AndroidSchedulers.mainThread()) }
.subscribe({ loopHandler.postDelayed(refreshDialog, 15 * 1000L)
activity?.runOnUiThread { updateGUI("EventNewOpenLoopNotification") }
}, fabricPrivacy::logException)
)
} }
@Synchronized @Synchronized
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
disposable.clear()
_binding = null _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 @Synchronized
@ -190,14 +201,14 @@ class LoopDialog : DaggerDialogFragment() {
val profileStore = activePlugin.activeProfileInterface.profile val profileStore = activePlugin.activeProfileInterface.profile
if (profile == null || profileStore == null) { if (profile == null || profileStore == null) {
ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.noprofile)) ToastUtils.showToastInUiThread(ctx, resourceHelper.gs(R.string.noprofile))
dismiss() dismiss()
return return
} }
} }
fun onClickOkCancelEnabled(v: View): Boolean { private fun onClickOkCancelEnabled(v: View): Boolean {
var description = "" var description = ""
when (v.id) { when (v.id) {
R.id.overview_closeloop -> description = resourceHelper.gs(R.string.closedloop) R.id.overview_closeloop -> description = resourceHelper.gs(R.string.closedloop)
@ -229,25 +240,28 @@ class LoopDialog : DaggerDialogFragment() {
val profile = profileFunction.getProfile() ?: return true val profile = profileFunction.getProfile() ?: return true
when (v.id) { when (v.id) {
R.id.overview_closeloop -> { R.id.overview_closeloop -> {
uel.log("CLOSED LOOP MODE")
sp.putString(R.string.key_aps_mode, "closed") sp.putString(R.string.key_aps_mode, "closed")
rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.closedloop))) rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.closedloop)))
return true return true
} }
R.id.overview_lgsloop -> { R.id.overview_lgsloop -> {
uel.log("LGS LOOP MODE")
sp.putString(R.string.key_aps_mode, "lgs") sp.putString(R.string.key_aps_mode, "lgs")
rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.lowglucosesuspend))) rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.lowglucosesuspend)))
return true return true
} }
R.id.overview_openloop -> { R.id.overview_openloop -> {
uel.log("OPEN LOOP MODE")
sp.putString(R.string.key_aps_mode, "open") sp.putString(R.string.key_aps_mode, "open")
rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.lowglucosesuspend))) rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.lowglucosesuspend)))
return true return true
} }
R.id.overview_disable -> { R.id.overview_disable -> {
aapsLogger.debug("USER ENTRY: LOOP DISABLED") uel.log("LOOP DISABLED")
loopPlugin.setPluginEnabled(PluginType.LOOP, false) loopPlugin.setPluginEnabled(PluginType.LOOP, false)
loopPlugin.setFragmentVisible(PluginType.LOOP, false) loopPlugin.setFragmentVisible(PluginType.LOOP, false)
configBuilderPlugin.storeSettings("DisablingLoop") configBuilderPlugin.storeSettings("DisablingLoop")
@ -255,7 +269,7 @@ class LoopDialog : DaggerDialogFragment() {
commandQueue.cancelTempBasal(true, object : Callback() { commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() { override fun run() {
if (!result.success) { 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 -> { R.id.overview_enable -> {
aapsLogger.debug("USER ENTRY: LOOP ENABLED") uel.log("LOOP ENABLED")
loopPlugin.setPluginEnabled(PluginType.LOOP, true) loopPlugin.setPluginEnabled(PluginType.LOOP, true)
loopPlugin.setFragmentVisible(PluginType.LOOP, true) loopPlugin.setFragmentVisible(PluginType.LOOP, true)
configBuilderPlugin.storeSettings("EnablingLoop") configBuilderPlugin.storeSettings("EnablingLoop")
@ -274,18 +288,13 @@ class LoopDialog : DaggerDialogFragment() {
} }
R.id.overview_resume, R.id.overview_reconnect -> { R.id.overview_resume, R.id.overview_reconnect -> {
aapsLogger.debug("USER ENTRY: RESUME") uel.log("RESUME")
loopPlugin.suspendTo(0L) loopPlugin.suspendTo(0L)
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspendmenu"))
commandQueue.cancelTempBasal(true, object : Callback() { commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() { override fun run() {
if (!result.success) { if (!result.success) {
val i = Intent(context, ErrorHelperActivity::class.java) ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
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)
} }
} }
}) })
@ -295,49 +304,49 @@ class LoopDialog : DaggerDialogFragment() {
} }
R.id.overview_suspend_1h -> { R.id.overview_suspend_1h -> {
aapsLogger.debug("USER ENTRY: SUSPEND 1h") uel.log("SUSPEND 1h")
loopPlugin.suspendLoop(60) loopPlugin.suspendLoop(60)
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspendmenu"))
return true return true
} }
R.id.overview_suspend_2h -> { R.id.overview_suspend_2h -> {
aapsLogger.debug("USER ENTRY: SUSPEND 2h") uel.log("SUSPEND 2h")
loopPlugin.suspendLoop(120) loopPlugin.suspendLoop(120)
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspendmenu"))
return true return true
} }
R.id.overview_suspend_3h -> { R.id.overview_suspend_3h -> {
aapsLogger.debug("USER ENTRY: SUSPEND 3h") uel.log("SUSPEND 3h")
loopPlugin.suspendLoop(180) loopPlugin.suspendLoop(180)
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspendmenu"))
return true return true
} }
R.id.overview_suspend_10h -> { R.id.overview_suspend_10h -> {
aapsLogger.debug("USER ENTRY: SUSPEND 10h") uel.log("SUSPEND 10h")
loopPlugin.suspendLoop(600) loopPlugin.suspendLoop(600)
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspendmenu"))
return true return true
} }
R.id.overview_disconnect_15m -> { R.id.overview_disconnect_15m -> {
aapsLogger.debug("USER ENTRY: DISCONNECT 15m") uel.log("DISCONNECT 15m")
loopPlugin.disconnectPump(15, profile) loopPlugin.disconnectPump(15, profile)
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspendmenu"))
return true return true
} }
R.id.overview_disconnect_30m -> { R.id.overview_disconnect_30m -> {
aapsLogger.debug("USER ENTRY: DISCONNECT 30m") uel.log("DISCONNECT 30m")
loopPlugin.disconnectPump(30, profile) loopPlugin.disconnectPump(30, profile)
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspendmenu"))
return true return true
} }
R.id.overview_disconnect_1h -> { R.id.overview_disconnect_1h -> {
aapsLogger.debug("USER ENTRY: DISCONNECT 1h") uel.log("DISCONNECT 1h")
loopPlugin.disconnectPump(60, profile) loopPlugin.disconnectPump(60, profile)
sp.putBoolean(R.string.key_objectiveusedisconnect, true) sp.putBoolean(R.string.key_objectiveusedisconnect, true)
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspendmenu"))
@ -345,14 +354,14 @@ class LoopDialog : DaggerDialogFragment() {
} }
R.id.overview_disconnect_2h -> { R.id.overview_disconnect_2h -> {
aapsLogger.debug("USER ENTRY: DISCONNECT 2h") uel.log("DISCONNECT 2h")
loopPlugin.disconnectPump(120, profile) loopPlugin.disconnectPump(120, profile)
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspendmenu"))
return true return true
} }
R.id.overview_disconnect_3h -> { R.id.overview_disconnect_3h -> {
aapsLogger.debug("USER ENTRY: DISCONNECT 3h") uel.log("DISCONNECT 3h")
loopPlugin.disconnectPump(180, profile) loopPlugin.disconnectPump(180, profile)
rxBus.send(EventRefreshOverview("suspendmenu")) rxBus.send(EventRefreshOverview("suspendmenu"))
return true return true

View file

@ -8,53 +8,61 @@ import android.widget.ArrayAdapter
import com.google.common.base.Joiner import com.google.common.base.Joiner
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.databinding.DialogProfileswitchBinding
import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import kotlinx.android.synthetic.main.dialog_profileswitch.*
import kotlinx.android.synthetic.main.notes.*
import kotlinx.android.synthetic.main.okcancel.*
import java.text.DecimalFormat import java.text.DecimalFormat
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
class ProfileSwitchDialog : DialogFragmentWithDate() { class ProfileSwitchDialog : DialogFragmentWithDate() {
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin @Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var activePlugin: ActivePluginProvider @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) { override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState) super.onSaveInstanceState(savedInstanceState)
savedInstanceState.putDouble("overview_profileswitch_duration", overview_profileswitch_duration.value) savedInstanceState.putDouble("duration", binding.duration.value)
savedInstanceState.putDouble("overview_profileswitch_percentage", overview_profileswitch_percentage.value) savedInstanceState.putDouble("percentage", binding.percentage.value)
savedInstanceState.putDouble("overview_profileswitch_timeshift", overview_profileswitch_timeshift.value) savedInstanceState.putDouble("timeshift", binding.timeshift.value)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? { savedInstanceState: Bundle?): View {
onCreateViewGeneral() onCreateViewGeneral()
arguments?.let { bundle -> arguments?.let { bundle ->
profileIndex = bundle.getInt("profileIndex", 0) 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
overview_profileswitch_duration.setParams(savedInstanceState?.getDouble("overview_profileswitch_duration") binding.duration.setParams(savedInstanceState?.getDouble("duration")
?: 0.0, 0.0, Constants.MAX_PROFILE_SWITCH_DURATION, 10.0, DecimalFormat("0"), false, ok) ?: 0.0, 0.0, Constants.MAX_PROFILE_SWITCH_DURATION, 10.0, DecimalFormat("0"), false, binding.okcancel.ok)
overview_profileswitch_percentage.setParams(savedInstanceState?.getDouble("overview_profileswitch_percentage") binding.percentage.setParams(savedInstanceState?.getDouble("percentage")
?: 100.0, Constants.CPP_MIN_PERCENTAGE.toDouble(), Constants.CPP_MAX_PERCENTAGE.toDouble(), 5.0, DecimalFormat("0"), false, ok) ?: 100.0, Constants.CPP_MIN_PERCENTAGE.toDouble(), Constants.CPP_MAX_PERCENTAGE.toDouble(), 5.0, DecimalFormat("0"), false, binding.okcancel.ok)
overview_profileswitch_timeshift.setParams(savedInstanceState?.getDouble("overview_profileswitch_timeshift") binding.timeshift.setParams(savedInstanceState?.getDouble("timeshift")
?: 0.0, Constants.CPP_MIN_TIMESHIFT.toDouble(), Constants.CPP_MAX_TIMESHIFT.toDouble(), 1.0, DecimalFormat("0"), false, ok) ?: 0.0, Constants.CPP_MIN_TIMESHIFT.toDouble(), Constants.CPP_MAX_TIMESHIFT.toDouble(), 1.0, DecimalFormat("0"), false, binding.okcancel.ok)
// profile // profile
context?.let { context -> context?.let { context ->
@ -62,55 +70,61 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
?: return ?: return
val profileList = profileStore.getProfileList() val profileList = profileStore.getProfileList()
val adapter = ArrayAdapter(context, R.layout.spinner_centered, profileList) val adapter = ArrayAdapter(context, R.layout.spinner_centered, profileList)
overview_profileswitch_profile.adapter = adapter binding.profile.adapter = adapter
// set selected to actual profile // set selected to actual profile
if (profileIndex != null) if (profileIndex != null)
overview_profileswitch_profile.setSelection(profileIndex as Int) binding.profile.setSelection(profileIndex as Int)
else else
for (p in profileList.indices) for (p in profileList.indices)
if (profileList[p] == profileFunction.getProfileName(false)) if (profileList[p] == profileFunction.getProfileName(false))
overview_profileswitch_profile.setSelection(p) binding.profile.setSelection(p)
} ?: return } ?: return
treatmentsPlugin.getProfileSwitchFromHistory(DateUtil.now())?.let { ps -> treatmentsPlugin.getProfileSwitchFromHistory(DateUtil.now())?.let { ps ->
if (ps.isCPP) { if (ps.isCPP) {
overview_profileswitch_reuselayout.visibility = View.VISIBLE binding.reuselayout.visibility = View.VISIBLE
overview_profileswitch_reusebutton.text = resourceHelper.gs(R.string.reuse) + " " + ps.percentage + "% " + ps.timeshift + "h" binding.reusebutton.text = resourceHelper.gs(R.string.reuse_profile_pct_hours, ps.percentage, ps.timeshift)
overview_profileswitch_reusebutton.setOnClickListener { binding.reusebutton.setOnClickListener {
overview_profileswitch_percentage.value = ps.percentage.toDouble() binding.percentage.value = ps.percentage.toDouble()
overview_profileswitch_timeshift.value = ps.timeshift.toDouble() binding.timeshift.value = ps.timeshift.toDouble()
} }
} else { } else {
overview_profileswitch_reuselayout.visibility = View.GONE binding.reuselayout.visibility = View.GONE
} }
} }
} }
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
override fun submit(): Boolean { override fun submit(): Boolean {
if (_binding == null) return false
val profileStore = activePlugin.activeProfileInterface.profile val profileStore = activePlugin.activeProfileInterface.profile
?: return false ?: return false
val actions: LinkedList<String> = LinkedList() val actions: LinkedList<String> = LinkedList()
val duration = overview_profileswitch_duration?.value?.toInt() ?: return false val duration = binding.duration.value?.toInt() ?: return false
if (duration > 0) if (duration > 0)
actions.add(resourceHelper.gs(R.string.duration) + ": " + resourceHelper.gs(R.string.format_mins, duration)) 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) actions.add(resourceHelper.gs(R.string.profile) + ": " + profile)
val percent = overview_profileswitch_percentage.value.toInt() val percent = binding.percentage.value.toInt()
if (percent != 100) if (percent != 100)
actions.add(resourceHelper.gs(R.string.percent) + ": " + percent + "%") actions.add(resourceHelper.gs(R.string.percent) + ": " + percent + "%")
val timeShift = overview_profileswitch_timeshift.value.toInt() val timeShift = binding.timeshift.value.toInt()
if (timeShift != 0) if (timeShift != 0)
actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_timeshift_label) + ": " + resourceHelper.gs(R.string.format_hours, timeShift.toDouble())) 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()) if (notes.isNotEmpty())
actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes) actions.add(resourceHelper.gs(R.string.careportal_newnstreatment_notes_label) + ": " + notes)
if (eventTimeChanged) if (eventTimeChanged)
actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(eventTime)) actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(eventTime))
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal_profileswitch), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), Runnable { OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal_profileswitch), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
aapsLogger.debug("USER ENTRY: PROFILE SWITCH $profile percent: $percent timeshift: $timeShift duration: $duration") uel.log("PROFILE SWITCH", d1 = percent.toDouble(), i1 = timeShift, i2 = duration)
treatmentsPlugin.doProfileSwitch(profileStore, profile, duration, percent, timeShift, eventTime) treatmentsPlugin.doProfileSwitch(profileStore, profile, duration, percent, timeShift, eventTime)
}) })
} }

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.dialogs package info.nightscout.androidaps.dialogs
import android.content.Context import android.content.Context
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@ -9,46 +8,55 @@ import android.view.ViewGroup
import com.google.common.base.Joiner import com.google.common.base.Joiner
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.ErrorHelperActivity import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.databinding.DialogTempbasalBinding
import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.Constraint 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.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.queue.Callback
import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.SafeParse 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.extensions.formatColor
import info.nightscout.androidaps.utils.resources.ResourceHelper 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.text.DecimalFormat
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.abs import kotlin.math.abs
class TempBasalDialog : DialogFragmentWithDate() { class TempBasalDialog : DialogFragmentWithDate() {
@Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var activePlugin: ActivePluginProvider @Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var commandQueue: CommandQueueProvider @Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var ctx: Context @Inject lateinit var ctx: Context
@Inject lateinit var uel: UserEntryLogger
private var isPercentPump = true 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) { override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState) super.onSaveInstanceState(savedInstanceState)
savedInstanceState.putDouble("actions_tempbasal_duration", actions_tempbasal_duration.value) savedInstanceState.putDouble("duration", binding.duration.value)
savedInstanceState.putDouble("actions_tempbasal_basalpercentinput", actions_tempbasal_basalpercentinput.value) savedInstanceState.putDouble("basalpercentinput", binding.basalpercentinput.value)
savedInstanceState.putDouble("actions_tempbasal_basalabsoluteinput", actions_tempbasal_basalabsoluteinput.value) savedInstanceState.putDouble("basalabsoluteinput", binding.basalabsoluteinput.value)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? { savedInstanceState: Bundle?): View {
onCreateViewGeneral() onCreateViewGeneral()
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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -60,41 +68,47 @@ class TempBasalDialog : DialogFragmentWithDate() {
val maxTempPercent = pumpDescription.maxTempPercent.toDouble() val maxTempPercent = pumpDescription.maxTempPercent.toDouble()
val tempPercentStep = pumpDescription.tempPercentStep.toDouble() val tempPercentStep = pumpDescription.tempPercentStep.toDouble()
actions_tempbasal_basalpercentinput.setParams(savedInstanceState?.getDouble("actions_tempbasal_basalpercentinput") binding.basalpercentinput.setParams(savedInstanceState?.getDouble("basalpercentinput")
?: 100.0, 0.0, maxTempPercent, tempPercentStep, DecimalFormat("0"), true, ok) ?: 100.0, 0.0, maxTempPercent, tempPercentStep, DecimalFormat("0"), true, binding.okcancel.ok)
actions_tempbasal_basalabsoluteinput.setParams(savedInstanceState?.getDouble("actions_tempbasal_basalabsoluteinput") binding.basalabsoluteinput.setParams(savedInstanceState?.getDouble("basalabsoluteinput")
?: profile.basal, 0.0, pumpDescription.maxTempAbsolute, pumpDescription.tempAbsoluteStep, DecimalFormat("0.00"), true, ok) ?: profile.basal, 0.0, pumpDescription.maxTempAbsolute, pumpDescription.tempAbsoluteStep, DecimalFormat("0.00"), true, binding.okcancel.ok)
val tempDurationStep = pumpDescription.tempDurationStep.toDouble() val tempDurationStep = pumpDescription.tempDurationStep.toDouble()
val tempMaxDuration = pumpDescription.tempMaxDuration.toDouble() val tempMaxDuration = pumpDescription.tempMaxDuration.toDouble()
actions_tempbasal_duration.setParams(savedInstanceState?.getDouble("actions_tempbasal_duration") binding.duration.setParams(savedInstanceState?.getDouble("duration")
?: tempDurationStep, tempDurationStep, tempMaxDuration, tempDurationStep, DecimalFormat("0"), false, ok) ?: tempDurationStep, tempDurationStep, tempMaxDuration, tempDurationStep, DecimalFormat("0"), false, binding.okcancel.ok)
isPercentPump = pumpDescription.tempBasalStyle and PumpDescription.PERCENT == PumpDescription.PERCENT isPercentPump = pumpDescription.tempBasalStyle and PumpDescription.PERCENT == PumpDescription.PERCENT
if (isPercentPump) { if (isPercentPump) {
actions_tempbasal_percent_layout.visibility = View.VISIBLE binding.percentLayout.visibility = View.VISIBLE
actions_tempbasal_absolute_layout.visibility = View.GONE binding.absoluteLayout.visibility = View.GONE
} else { } else {
actions_tempbasal_percent_layout.visibility = View.GONE binding.percentLayout.visibility = View.GONE
actions_tempbasal_absolute_layout.visibility = View.VISIBLE binding.absoluteLayout.visibility = View.VISIBLE
} }
} }
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
override fun submit(): Boolean { override fun submit(): Boolean {
if (_binding == null) return false
var percent = 0 var percent = 0
var absolute = 0.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 profile = profileFunction.getProfile() ?: return false
val actions: LinkedList<String> = LinkedList() val actions: LinkedList<String> = LinkedList()
if (isPercentPump) { if (isPercentPump) {
val basalPercentInput = SafeParse.stringToInt(actions_tempbasal_basalpercentinput.text) val basalPercentInput = SafeParse.stringToInt(binding.basalpercentinput.text)
percent = constraintChecker.applyBasalPercentConstraints(Constraint(basalPercentInput), profile).value() percent = constraintChecker.applyBasalPercentConstraints(Constraint(basalPercentInput), profile).value()
actions.add(resourceHelper.gs(R.string.tempbasal_label) + ": $percent%") actions.add(resourceHelper.gs(R.string.tempbasal_label) + ": $percent%")
actions.add(resourceHelper.gs(R.string.duration) + ": " + resourceHelper.gs(R.string.format_mins, durationInMinutes)) actions.add(resourceHelper.gs(R.string.duration) + ": " + resourceHelper.gs(R.string.format_mins, durationInMinutes))
if (percent != basalPercentInput) actions.add(resourceHelper.gs(R.string.constraintapllied)) if (percent != basalPercentInput) actions.add(resourceHelper.gs(R.string.constraintapllied))
} else { } else {
val basalAbsoluteInput = SafeParse.stringToDouble(actions_tempbasal_basalabsoluteinput.text) val basalAbsoluteInput = SafeParse.stringToDouble(binding.basalabsoluteinput.text)
absolute = constraintChecker.applyBasalConstraints(Constraint(basalAbsoluteInput), profile).value() 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.tempbasal_label) + ": " + resourceHelper.gs(R.string.pump_basebasalrate, absolute))
actions.add(resourceHelper.gs(R.string.duration) + ": " + resourceHelper.gs(R.string.format_mins, durationInMinutes)) 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)) actions.add(resourceHelper.gs(R.string.constraintapllied).formatColor(resourceHelper, R.color.warning))
} }
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.tempbasal_label), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), Runnable { OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.tempbasal_label), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
val callback: Callback = object : Callback() { val callback: Callback = object : Callback() {
override fun run() { override fun run() {
if (!result.success) { if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java) ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
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)
} }
} }
} }
if (isPercentPump) { 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) commandQueue.tempBasalPercent(percent, durationInMinutes, true, profile, callback)
} else { } 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) commandQueue.tempBasalAbsolute(absolute, durationInMinutes, true, profile, callback)
} }
}) })

View file

@ -4,77 +4,85 @@ import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import com.google.common.base.Joiner import com.google.common.base.Joiner
import com.google.common.collect.Lists import com.google.common.collect.Lists
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.databinding.DialogTemptargetBinding
import info.nightscout.androidaps.db.Source import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.db.TempTarget import info.nightscout.androidaps.db.TempTarget
import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.interfaces.ProfileFunction 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.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.DefaultValueHelper import info.nightscout.androidaps.utils.DefaultValueHelper
import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.resources.ResourceHelper 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.text.DecimalFormat
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
class TempTargetDialog : DialogFragmentWithDate() { class TempTargetDialog : DialogFragmentWithDate() {
@Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var defaultValueHelper: DefaultValueHelper @Inject lateinit var defaultValueHelper: DefaultValueHelper
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin @Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var activePlugin: ActivePluginProvider @Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var uel: UserEntryLogger
lateinit var reasonList: List<String> private lateinit var reasonList: List<String>
private var _binding: DialogTemptargetBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onSaveInstanceState(savedInstanceState: Bundle) { override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState) super.onSaveInstanceState(savedInstanceState)
savedInstanceState.putDouble("overview_temptarget_duration", overview_temptarget_duration.value) savedInstanceState.putDouble("duration", binding.duration.value)
savedInstanceState.putDouble("overview_temptarget_temptarget", overview_temptarget_temptarget.value) savedInstanceState.putDouble("temptarget", binding.temptarget.value)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? { savedInstanceState: Bundle?): View {
onCreateViewGeneral() onCreateViewGeneral()
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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
overview_temptarget_duration.setParams(savedInstanceState?.getDouble("overview_temptarget_duration") binding.duration.setParams(savedInstanceState?.getDouble("duration")
?: 0.0, 0.0, Constants.MAX_PROFILE_SWITCH_DURATION, 10.0, DecimalFormat("0"), false, ok) ?: 0.0, 0.0, Constants.MAX_PROFILE_SWITCH_DURATION, 10.0, DecimalFormat("0"), false, binding.okcancel.ok)
if (profileFunction.getUnits() == Constants.MMOL) if (profileFunction.getUnits() == Constants.MMOL)
overview_temptarget_temptarget.setParams( binding.temptarget.setParams(
savedInstanceState?.getDouble("overview_temptarget_temptarget") savedInstanceState?.getDouble("temptarget")
?: Constants.MIN_TT_MMOL, ?: 8.0,
Constants.MIN_TT_MMOL, Constants.MAX_TT_MMOL, 0.1, DecimalFormat("0.0"), false, ok) Constants.MIN_TT_MMOL, Constants.MAX_TT_MMOL, 0.1, DecimalFormat("0.0"), false, binding.okcancel.ok)
else else
overview_temptarget_temptarget.setParams( binding.temptarget.setParams(
savedInstanceState?.getDouble("overview_temptarget_temptarget") savedInstanceState?.getDouble("temptarget")
?: Constants.MIN_TT_MGDL, ?: 144.0,
Constants.MIN_TT_MGDL, Constants.MAX_TT_MGDL, 1.0, DecimalFormat("0"), false, ok) Constants.MIN_TT_MGDL, Constants.MAX_TT_MGDL, 1.0, DecimalFormat("0"), false, binding.okcancel.ok)
val units = profileFunction.getUnits() 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 // temp target
context?.let { context -> context?.let { context ->
if (activePlugin.activeTreatments.tempTargetFromHistory != null) if (activePlugin.activeTreatments.tempTargetFromHistory != null)
overview_temptarget_cancel?.visibility = View.VISIBLE binding.targetCancel.visibility = View.VISIBLE
else else
overview_temptarget_cancel?.visibility = View.GONE binding.targetCancel.visibility = View.GONE
reasonList = Lists.newArrayList( reasonList = Lists.newArrayList(
resourceHelper.gs(R.string.manual), resourceHelper.gs(R.string.manual),
@ -83,59 +91,67 @@ class TempTargetDialog : DialogFragmentWithDate() {
resourceHelper.gs(R.string.hypo) resourceHelper.gs(R.string.hypo)
) )
val adapterReason = ArrayAdapter(context, R.layout.spinner_centered, reasonList) val adapterReason = ArrayAdapter(context, R.layout.spinner_centered, reasonList)
overview_temptarget_reason.adapter = adapterReason binding.reason.adapter = adapterReason
overview_temptarget_cancel?.setOnClickListener { shortClick(it) } binding.targetCancel.setOnClickListener { shortClick(it) }
overview_temptarget_eating_soon?.setOnClickListener { shortClick(it) } binding.eatingSoon.setOnClickListener { shortClick(it) }
overview_temptarget_activity?.setOnClickListener { shortClick(it) } binding.activity.setOnClickListener { shortClick(it) }
overview_temptarget_hypo?.setOnClickListener { shortClick(it) } binding.hypo.setOnClickListener { shortClick(it) }
overview_temptarget_eating_soon?.setOnLongClickListener { binding.eatingSoon.setOnLongClickListener {
longClick(it) longClick(it)
return@setOnLongClickListener true return@setOnLongClickListener true
} }
overview_temptarget_activity?.setOnLongClickListener { binding.activity.setOnLongClickListener {
longClick(it) longClick(it)
return@setOnLongClickListener true return@setOnLongClickListener true
} }
overview_temptarget_hypo?.setOnLongClickListener { binding.hypo.setOnLongClickListener {
longClick(it) longClick(it)
return@setOnLongClickListener true return@setOnLongClickListener true
} }
} }
} }
private fun shortClick(v:View){ private fun shortClick(v: View) {
v.performLongClick() v.performLongClick()
if (submit()) dismiss() if (submit()) dismiss()
} }
private fun longClick(v:View) { private fun longClick(v: View) {
when (v.id) { when (v.id) {
R.id.overview_temptarget_eating_soon -> { R.id.eating_soon -> {
overview_temptarget_temptarget.value = defaultValueHelper.determineEatingSoonTT() binding.temptarget.value = defaultValueHelper.determineEatingSoonTT()
overview_temptarget_duration.value = defaultValueHelper.determineEatingSoonTTDuration().toDouble() binding.duration.value = defaultValueHelper.determineEatingSoonTTDuration().toDouble()
overview_temptarget_reason.setSelection(reasonList.indexOf( resourceHelper.gs(R.string.eatingsoon))) binding.reason.setSelection(reasonList.indexOf(resourceHelper.gs(R.string.eatingsoon)))
} }
R.id.overview_temptarget_activity -> {
overview_temptarget_temptarget.value = defaultValueHelper.determineActivityTT() R.id.activity -> {
overview_temptarget_duration.value = defaultValueHelper.determineActivityTTDuration().toDouble() binding.temptarget.value = defaultValueHelper.determineActivityTT()
overview_temptarget_reason.setSelection(reasonList.indexOf(resourceHelper.gs(R.string.activity))) 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() R.id.hypo -> {
overview_temptarget_duration.value = defaultValueHelper.determineHypoTTDuration().toDouble() binding.temptarget.value = defaultValueHelper.determineHypoTT()
overview_temptarget_reason.setSelection(reasonList.indexOf(resourceHelper.gs(R.string.hypo))) 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 { override fun submit(): Boolean {
if (_binding == null) return false
val actions: LinkedList<String> = LinkedList() val actions: LinkedList<String> = 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 unitResId = if (profileFunction.getUnits() == Constants.MGDL) R.string.mgdl else R.string.mmol
val target = overview_temptarget_temptarget.value val target = binding.temptarget.value
val duration = overview_temptarget_duration.value.toInt() val duration = binding.duration.value.toInt()
if (target != 0.0 && duration != 0) { if (target != 0.0 && duration != 0) {
actions.add(resourceHelper.gs(R.string.reason) + ": " + reason) actions.add(resourceHelper.gs(R.string.reason) + ": " + reason)
actions.add(resourceHelper.gs(R.string.target_label) + ": " + Profile.toCurrentUnitsString(profileFunction, target) + " " + resourceHelper.gs(unitResId)) 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)) actions.add(resourceHelper.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(eventTime))
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal_temporarytarget), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), Runnable { OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.careportal_temporarytarget), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
aapsLogger.debug("USER ENTRY: TEMP TARGET $target duration: $duration") uel.log("TT", d1 = target, i1 = duration)
if (target == 0.0 || duration == 0) { if (target == 0.0 || duration == 0) {
val tempTarget = TempTarget() val tempTarget = TempTarget()
.date(eventTime) .date(eventTime)

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.dialogs package info.nightscout.androidaps.dialogs
import android.content.Context import android.content.Context
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
@ -13,35 +12,36 @@ import info.nightscout.androidaps.Config
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.ErrorHelperActivity import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.data.DetailedBolusInfo import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.databinding.DialogTreatmentBinding
import info.nightscout.androidaps.db.CareportalEvent import info.nightscout.androidaps.db.CareportalEvent
import info.nightscout.androidaps.db.Source import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.CommandQueueProvider import info.nightscout.androidaps.interfaces.CommandQueueProvider
import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.DecimalFormatter import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.SafeParse import info.nightscout.androidaps.utils.SafeParse
import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.formatColor import info.nightscout.androidaps.utils.extensions.formatColor
import info.nightscout.androidaps.utils.resources.ResourceHelper 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.text.DecimalFormat
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.abs import kotlin.math.abs
class TreatmentDialog : DialogFragmentWithDate() { class TreatmentDialog : DialogFragmentWithDate() {
@Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var activePlugin: ActivePluginProvider @Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var commandQueue: CommandQueueProvider @Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var ctx: Context @Inject lateinit var ctx: Context
@Inject lateinit var config: Config @Inject lateinit var config: Config
@Inject lateinit var uel: UserEntryLogger
private val textWatcher: TextWatcher = object : TextWatcher { private val textWatcher: TextWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {} override fun afterTextChanged(s: Editable) {}
@ -54,49 +54,62 @@ class TreatmentDialog : DialogFragmentWithDate() {
private fun validateInputs() { private fun validateInputs() {
val maxCarbs = constraintChecker.getMaxCarbsAllowed().value().toDouble() val maxCarbs = constraintChecker.getMaxCarbsAllowed().value().toDouble()
val maxInsulin = constraintChecker.getMaxBolusAllowed().value() val maxInsulin = constraintChecker.getMaxBolusAllowed().value()
if (SafeParse.stringToInt(overview_treatment_carbs.text) > maxCarbs) { if (SafeParse.stringToInt(binding.carbs.text) > maxCarbs) {
overview_treatment_carbs.value = 0.0 binding.carbs.value = 0.0
ToastUtils.showToastInUiThread(context, resourceHelper.gs(R.string.carbsconstraintapplied)) ToastUtils.showToastInUiThread(context, resourceHelper.gs(R.string.carbsconstraintapplied))
} }
if (SafeParse.stringToDouble(overview_treatment_insulin.text) > maxInsulin) { if (SafeParse.stringToDouble(binding.insulin.text) > maxInsulin) {
overview_treatment_insulin.value = 0.0 binding.insulin.value = 0.0
ToastUtils.showToastInUiThread(context, resourceHelper.gs(R.string.bolusconstraintapplied)) 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) { override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState) super.onSaveInstanceState(savedInstanceState)
savedInstanceState.putDouble("overview_treatment_carbs", overview_treatment_carbs.value) savedInstanceState.putDouble("carbs", binding.carbs.value)
savedInstanceState.putDouble("overview_treatment_insulin", overview_treatment_insulin.value) savedInstanceState.putDouble("insulin", binding.insulin.value)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? { savedInstanceState: Bundle?): View {
onCreateViewGeneral() 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
if (config.NSCLIENT) { if (config.NSCLIENT) {
overview_treatment_record_only.isChecked = true binding.recordOnly.isChecked = true
overview_treatment_record_only.isEnabled = false binding.recordOnly.isEnabled = false
} }
val maxCarbs = constraintChecker.getMaxCarbsAllowed().value().toDouble() val maxCarbs = constraintChecker.getMaxCarbsAllowed().value().toDouble()
val maxInsulin = constraintChecker.getMaxBolusAllowed().value() val maxInsulin = constraintChecker.getMaxBolusAllowed().value()
val pumpDescription = activePlugin.activePump.pumpDescription val pumpDescription = activePlugin.activePump.pumpDescription
overview_treatment_carbs.setParams(savedInstanceState?.getDouble("overview_treatment_carbs") binding.carbs.setParams(savedInstanceState?.getDouble("carbs")
?: 0.0, 0.0, maxCarbs, 1.0, DecimalFormat("0"), false, ok, textWatcher) ?: 0.0, 0.0, maxCarbs, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher)
overview_treatment_insulin.setParams(savedInstanceState?.getDouble("overview_treatment_insulin") binding.insulin.setParams(savedInstanceState?.getDouble("insulin")
?: 0.0, 0.0, maxInsulin, pumpDescription.bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, ok, textWatcher) ?: 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 { override fun submit(): Boolean {
if (_binding == null) return false
val pumpDescription = activePlugin.activePump.pumpDescription val pumpDescription = activePlugin.activePump.pumpDescription
val insulin = SafeParse.stringToDouble(overview_treatment_insulin?.text ?: return false) val insulin = SafeParse.stringToDouble(binding.insulin.text ?: return false)
val carbs = SafeParse.stringToInt(overview_treatment_carbs.text) val carbs = SafeParse.stringToInt(binding.carbs.text)
val recordOnlyChecked = overview_treatment_record_only.isChecked val recordOnlyChecked = binding.recordOnly.isChecked
val actions: LinkedList<String?> = LinkedList() val actions: LinkedList<String?> = LinkedList()
val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(insulin)).value() val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(insulin)).value()
val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value() val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value()
@ -115,8 +128,8 @@ class TreatmentDialog : DialogFragmentWithDate() {
} }
if (insulinAfterConstraints > 0 || carbsAfterConstraints > 0) { if (insulinAfterConstraints > 0 || carbsAfterConstraints > 0) {
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.overview_treatment_label), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), Runnable { OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.overview_treatment_label), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
aapsLogger.debug("USER ENTRY: BOLUS insulin $insulin carbs: $carbs") uel.log("TREATMENT", d1 = insulin, i1 = carbs)
val detailedBolusInfo = DetailedBolusInfo() val detailedBolusInfo = DetailedBolusInfo()
if (insulinAfterConstraints == 0.0) detailedBolusInfo.eventType = CareportalEvent.CARBCORRECTION if (insulinAfterConstraints == 0.0) detailedBolusInfo.eventType = CareportalEvent.CARBCORRECTION
if (carbsAfterConstraints == 0) detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS if (carbsAfterConstraints == 0) detailedBolusInfo.eventType = CareportalEvent.CORRECTIONBOLUS
@ -128,12 +141,7 @@ class TreatmentDialog : DialogFragmentWithDate() {
commandQueue.bolus(detailedBolusInfo, object : Callback() { commandQueue.bolus(detailedBolusInfo, object : Callback() {
override fun run() { override fun run() {
if (!result.success) { if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java) ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.treatmentdeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
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)
} }
} }
}) })

View file

@ -13,12 +13,13 @@ import android.widget.AdapterView.OnItemSelectedListener
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.CompoundButton import android.widget.CompoundButton
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import dagger.android.HasAndroidInjector
import dagger.android.support.DaggerDialogFragment import dagger.android.support.DaggerDialogFragment
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile 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.ActivePluginProvider
import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.interfaces.ProfileFunction 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.ToastUtils
import info.nightscout.androidaps.utils.extensions.toVisibility import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import info.nightscout.androidaps.utils.valueToUnits
import info.nightscout.androidaps.utils.wizard.BolusWizard import info.nightscout.androidaps.utils.wizard.BolusWizard
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.dialog_wizard.*
import java.text.DecimalFormat import java.text.DecimalFormat
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
@ -47,7 +48,9 @@ import kotlin.math.abs
class WizardDialog : DaggerDialogFragment() { class WizardDialog : DaggerDialogFragment() {
@Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var constraintChecker: ConstraintChecker @Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var mainApp: MainApp @Inject lateinit var mainApp: MainApp
@Inject lateinit var sp: SP @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 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() { override fun onStart() {
super.onStart() super.onStart()
dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
@ -81,49 +99,50 @@ class WizardDialog : DaggerDialogFragment() {
override fun onSaveInstanceState(savedInstanceState: Bundle) { override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState) super.onSaveInstanceState(savedInstanceState)
savedInstanceState.putDouble("treatments_wizard_bg_input", treatments_wizard_bg_input.value) savedInstanceState.putDouble("bg_input", binding.bgInput.value)
savedInstanceState.putDouble("treatments_wizard_carbs_input", treatments_wizard_carbs_input.value) savedInstanceState.putDouble("carbs_input", binding.carbsInput.value)
savedInstanceState.putDouble("treatments_wizard_correction_input", treatments_wizard_correction_input.value) savedInstanceState.putDouble("correction_input", binding.correctionInput.value)
savedInstanceState.putDouble("treatments_wizard_carb_time_input", treatments_wizard_carb_time_input.value) savedInstanceState.putDouble("carb_time_input", binding.carbTimeInput.value)
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? { savedInstanceState: Bundle?): View {
dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE) dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE)
dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
isCancelable = true isCancelable = true
dialog?.setCanceledOnTouchOutside(false) 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
loadCheckedStates() loadCheckedStates()
processCobCheckBox() processCobCheckBox()
treatments_wizard_sbcheckbox.visibility = sp.getBoolean(R.string.key_usesuperbolus, false).toVisibility() binding.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.notesLayout.visibility = sp.getBoolean(R.string.key_show_notes_entry_dialogs, false).toVisibility()
val maxCarbs = constraintChecker.getMaxCarbsAllowed().value() val maxCarbs = constraintChecker.getMaxCarbsAllowed().value()
val maxCorrection = constraintChecker.getMaxBolusAllowed().value() val maxCorrection = constraintChecker.getMaxBolusAllowed().value()
if (profileFunction.getUnits() == Constants.MGDL) if (profileFunction.getUnits() == Constants.MGDL)
treatments_wizard_bg_input.setParams(savedInstanceState?.getDouble("treatments_wizard_bg_input") binding.bgInput.setParams(savedInstanceState?.getDouble("bg_input")
?: 0.0, 0.0, 500.0, 1.0, DecimalFormat("0"), false, ok, textWatcher) ?: 0.0, 0.0, 500.0, 1.0, DecimalFormat("0"), false, binding.ok, timeTextWatcher)
else else
treatments_wizard_bg_input.setParams(savedInstanceState?.getDouble("treatments_wizard_bg_input") binding.bgInput.setParams(savedInstanceState?.getDouble("bg_input")
?: 0.0, 0.0, 30.0, 0.1, DecimalFormat("0.0"), false, ok, textWatcher) ?: 0.0, 0.0, 30.0, 0.1, DecimalFormat("0.0"), false, binding.ok, textWatcher)
treatments_wizard_carbs_input.setParams(savedInstanceState?.getDouble("treatments_wizard_carbs_input") binding.carbsInput.setParams(savedInstanceState?.getDouble("carbs_input")
?: 0.0, 0.0, maxCarbs.toDouble(), 1.0, DecimalFormat("0"), false, ok, textWatcher) ?: 0.0, 0.0, maxCarbs.toDouble(), 1.0, DecimalFormat("0"), false, binding.ok, textWatcher)
val bolusStep = activePlugin.activePump.pumpDescription.bolusStep val bolusStep = activePlugin.activePump.pumpDescription.bolusStep
treatments_wizard_correction_input.setParams(savedInstanceState?.getDouble("treatments_wizard_correction_input") binding.correctionInput.setParams(savedInstanceState?.getDouble("correction_input")
?: 0.0, -maxCorrection, maxCorrection, bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, ok, textWatcher) ?: 0.0, -maxCorrection, maxCorrection, bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, binding.ok, textWatcher)
treatments_wizard_carb_time_input.setParams(savedInstanceState?.getDouble("treatments_wizard_carb_time_input") binding.carbTimeInput.setParams(savedInstanceState?.getDouble("carb_time_input")
?: 0.0, -60.0, 60.0, 5.0, DecimalFormat("0"), false, ok, textWatcher) ?: 0.0, -60.0, 60.0, 5.0, DecimalFormat("0"), false, binding.ok, timeTextWatcher)
initDialog() 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 button
ok.setOnClickListener { binding.ok.setOnClickListener {
if (okClicked) { if (okClicked) {
aapsLogger.debug(LTag.UI, "guarding: ok already clicked") aapsLogger.debug(LTag.UI, "guarding: ok already clicked")
} else { } else {
@ -136,46 +155,46 @@ class WizardDialog : DaggerDialogFragment() {
dismiss() dismiss()
} }
// cancel button // cancel button
cancel.setOnClickListener { dismiss() } binding.cancel.setOnClickListener { dismiss() }
// checkboxes // checkboxes
treatments_wizard_bgcheckbox.setOnCheckedChangeListener(::onCheckedChanged) binding.bgcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
treatments_wizard_ttcheckbox.setOnCheckedChangeListener(::onCheckedChanged) binding.ttcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
treatments_wizard_cobcheckbox.setOnCheckedChangeListener(::onCheckedChanged) binding.cobcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
treatments_wizard_basaliobcheckbox.setOnCheckedChangeListener(::onCheckedChanged) binding.basaliobcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
treatments_wizard_bolusiobcheckbox.setOnCheckedChangeListener(::onCheckedChanged) binding.bolusiobcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
treatments_wizard_bgtrendcheckbox.setOnCheckedChangeListener(::onCheckedChanged) binding.bgtrendcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
treatments_wizard_sbcheckbox.setOnCheckedChangeListener(::onCheckedChanged) binding.sbcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
val showCalc = sp.getBoolean(R.string.key_wizard_calculation_visible, false) val showCalc = sp.getBoolean(R.string.key_wizard_calculation_visible, false)
treatments_wizard_delimiter.visibility = showCalc.toVisibility() binding.delimiter.visibility = showCalc.toVisibility()
treatments_wizard_resulttable.visibility = showCalc.toVisibility() binding.resulttable.visibility = showCalc.toVisibility()
treatments_wizard_calculationcheckbox.isChecked = showCalc binding.calculationcheckbox.isChecked = showCalc
treatments_wizard_calculationcheckbox.setOnCheckedChangeListener { _, isChecked -> binding.calculationcheckbox.setOnCheckedChangeListener { _, isChecked ->
run { run {
sp.putBoolean(resourceHelper.gs(R.string.key_wizard_calculation_visible), isChecked) sp.putBoolean(resourceHelper.gs(R.string.key_wizard_calculation_visible), isChecked)
treatments_wizard_delimiter.visibility = isChecked.toVisibility() binding.delimiter.visibility = isChecked.toVisibility()
treatments_wizard_resulttable.visibility = isChecked.toVisibility() binding.resulttable.visibility = isChecked.toVisibility()
} }
} }
// profile spinner // profile spinner
treatments_wizard_profile.onItemSelectedListener = object : OnItemSelectedListener { binding.profile.onItemSelectedListener = object : OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) { override fun onNothingSelected(parent: AdapterView<*>?) {
ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.noprofileselected)) 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) { override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
calculateInsulin() calculateInsulin()
ok.visibility = View.VISIBLE binding.ok.visibility = View.VISIBLE
} }
} }
// bus // bus
disposable.add(rxBus disposable.add(rxBus
.toObservable(EventAutosensCalculationFinished::class.java) .toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(aapsSchedulers.main)
.subscribe({ .subscribe({
activity?.runOnUiThread { calculateInsulin() } activity?.runOnUiThread { calculateInsulin() }
}, { fabricPrivacy.logException(it) }) }, fabricPrivacy::logException)
) )
} }
@ -183,38 +202,43 @@ class WizardDialog : DaggerDialogFragment() {
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
disposable.clear() disposable.clear()
_binding = null
} }
private fun onCheckedChanged(buttonView: CompoundButton, @Suppress("UNUSED_PARAMETER") state: Boolean) { private fun onCheckedChanged(buttonView: CompoundButton, @Suppress("UNUSED_PARAMETER") state: Boolean) {
saveCheckedStates() saveCheckedStates()
treatments_wizard_ttcheckbox.isEnabled = treatments_wizard_bgcheckbox.isChecked && treatmentsPlugin.tempTargetFromHistory != null binding.ttcheckbox.isEnabled = binding.bgcheckbox.isChecked && treatmentsPlugin.tempTargetFromHistory != null
if (buttonView.id == treatments_wizard_cobcheckbox.id) if (buttonView.id == binding.cobcheckbox.id)
processCobCheckBox() processCobCheckBox()
calculateInsulin() calculateInsulin()
} }
private fun processCobCheckBox() { private fun processCobCheckBox() {
if (treatments_wizard_cobcheckbox.isChecked) { if (binding.cobcheckbox.isChecked) {
treatments_wizard_bolusiobcheckbox.isEnabled = false binding.bolusiobcheckbox.isEnabled = false
treatments_wizard_basaliobcheckbox.isEnabled = false binding.basaliobcheckbox.isEnabled = false
treatments_wizard_bolusiobcheckbox.isChecked = true binding.bolusiobcheckbox.isChecked = true
treatments_wizard_basaliobcheckbox.isChecked = true binding.basaliobcheckbox.isChecked = true
} else { } else {
treatments_wizard_bolusiobcheckbox.isEnabled = true binding.bolusiobcheckbox.isEnabled = true
treatments_wizard_basaliobcheckbox.isEnabled = true binding.basaliobcheckbox.isEnabled = true
} }
} }
private fun saveCheckedStates() { private fun saveCheckedStates() {
sp.putBoolean(resourceHelper.gs(R.string.key_wizard_include_cob), treatments_wizard_cobcheckbox.isChecked) sp.putBoolean(R.string.key_wizard_include_cob, binding.cobcheckbox.isChecked)
sp.putBoolean(resourceHelper.gs(R.string.key_wizard_include_trend_bg), treatments_wizard_bgtrendcheckbox.isChecked) sp.putBoolean(R.string.key_wizard_include_trend_bg, binding.bgtrendcheckbox.isChecked)
} }
private fun loadCheckedStates() { private fun loadCheckedStates() {
treatments_wizard_bgtrendcheckbox.isChecked = sp.getBoolean(R.string.key_wizard_include_trend_bg, false) binding.bgtrendcheckbox.isChecked = sp.getBoolean(R.string.key_wizard_include_trend_bg, false)
treatments_wizard_cobcheckbox.isChecked = sp.getBoolean(R.string.key_wizard_include_cob, 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() { private fun initDialog() {
val profile = profileFunction.getProfile() val profile = profileFunction.getProfile()
val profileStore = activePlugin.activeProfileInterface.profile val profileStore = activePlugin.activeProfileInterface.profile
@ -225,30 +249,29 @@ class WizardDialog : DaggerDialogFragment() {
return return
} }
val profileList: ArrayList<CharSequence> val profileList: ArrayList<CharSequence> = profileStore.getProfileList()
profileList = profileStore.getProfileList()
profileList.add(0, resourceHelper.gs(R.string.active)) profileList.add(0, resourceHelper.gs(R.string.active))
context?.let { context -> context?.let { context ->
val adapter = ArrayAdapter(context, R.layout.spinner_centered, profileList) val adapter = ArrayAdapter(context, R.layout.spinner_centered, profileList)
treatments_wizard_profile.adapter = adapter binding.profile.adapter = adapter
} ?: return } ?: return
val units = profileFunction.getUnits() val units = profileFunction.getUnits()
treatments_wizard_bgunits.text = units binding.bgunits.text = units
if (units == Constants.MGDL) if (units == Constants.MGDL)
treatments_wizard_bg_input.setStep(1.0) binding.bgInput.setStep(1.0)
else else
treatments_wizard_bg_input.setStep(0.1) binding.bgInput.setStep(0.1)
// Set BG if not old // Set BG if not old
val lastBg = iobCobCalculatorPlugin.actualBg() val lastBg = iobCobCalculatorPlugin.actualBg()
if (lastBg != null) { if (lastBg != null) {
treatments_wizard_bg_input.value = lastBg.valueToUnits(units) binding.bgInput.value = lastBg.valueToUnits(units)
} else { } 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 // IOB calculation
treatmentsPlugin.updateTotalIOBTreatments() treatmentsPlugin.updateTotalIOBTreatments()
@ -256,19 +279,19 @@ class WizardDialog : DaggerDialogFragment() {
treatmentsPlugin.updateTotalIOBTempBasals() treatmentsPlugin.updateTotalIOBTempBasals()
val basalIob = treatmentsPlugin.lastCalculationTempBasals.round() val basalIob = treatmentsPlugin.lastCalculationTempBasals.round()
treatments_wizard_bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, -bolusIob.iob) binding.bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, -bolusIob.iob)
treatments_wizard_basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, -basalIob.basaliob) binding.basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, -basalIob.basaliob)
calculateInsulin() 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() { private fun calculateInsulin() {
val profileStore = activePlugin.activeProfileInterface.profile val profileStore = activePlugin.activeProfileInterface.profile
if (treatments_wizard_profile?.selectedItem == null || profileStore == null) if (binding.profile.selectedItem == null || profileStore == null)
return // not initialized yet return // not initialized yet
var profileName = treatments_wizard_profile.selectedItem.toString() var profileName = binding.profile.selectedItem.toString()
val specificProfile: Profile? val specificProfile: Profile?
if (profileName == resourceHelper.gs(R.string.active)) { if (profileName == resourceHelper.gs(R.string.active)) {
specificProfile = profileFunction.getProfile() specificProfile = profileFunction.getProfile()
@ -279,82 +302,83 @@ class WizardDialog : DaggerDialogFragment() {
if (specificProfile == null) return if (specificProfile == null) return
// Entered values // Entered values
var bg = SafeParse.stringToDouble(treatments_wizard_bg_input.text) var bg = SafeParse.stringToDouble(binding.bgInput.text)
val carbs = SafeParse.stringToInt(treatments_wizard_carbs_input.text) val carbs = SafeParse.stringToInt(binding.carbsInput.text)
val correction = SafeParse.stringToDouble(treatments_wizard_correction_input.text) val correction = SafeParse.stringToDouble(binding.correctionInput.text)
val carbsAfterConstraint = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value() val carbsAfterConstraint = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value()
if (abs(carbs - carbsAfterConstraint) > 0.01) { 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)) ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.carbsconstraintapplied))
return return
} }
bg = if (treatments_wizard_bgcheckbox.isChecked) bg else 0.0 bg = if (binding.bgcheckbox.isChecked) bg else 0.0
val tempTarget = if (treatments_wizard_ttcheckbox.isChecked) treatmentsPlugin.tempTargetFromHistory else null val tempTarget = if (binding.ttcheckbox.isChecked) treatmentsPlugin.tempTargetFromHistory else null
// COB // COB
var cob = 0.0 var cob = 0.0
if (treatments_wizard_cobcheckbox.isChecked) { if (binding.cobcheckbox.isChecked) {
val cobInfo = iobCobCalculatorPlugin.getCobInfo(false, "Wizard COB") val cobInfo = iobCobCalculatorPlugin.getCobInfo(false, "Wizard COB")
cobInfo.displayCob?.let { cob = it } 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, wizard = BolusWizard(mainApp).doCalc(specificProfile, profileName, tempTarget, carbsAfterConstraint, cob, bg, correction,
sp.getInt(R.string.key_boluswizard_percentage, 100).toDouble(), sp.getInt(R.string.key_boluswizard_percentage, 100).toDouble(),
treatments_wizard_bgcheckbox.isChecked, binding.bgcheckbox.isChecked,
treatments_wizard_cobcheckbox.isChecked, binding.cobcheckbox.isChecked,
treatments_wizard_bolusiobcheckbox.isChecked, binding.bolusiobcheckbox.isChecked,
treatments_wizard_basaliobcheckbox.isChecked, binding.basaliobcheckbox.isChecked,
treatments_wizard_sbcheckbox.isChecked, binding.sbcheckbox.isChecked,
treatments_wizard_ttcheckbox.isChecked, binding.ttcheckbox.isChecked,
treatments_wizard_bgtrendcheckbox.isChecked, binding.bgtrendcheckbox.isChecked,
treatment_wizard_notes.text.toString(), carbTime) binding.alarm.isChecked,
binding.notes.text.toString(), carbTime)
wizard?.let { wizard -> 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) binding.bg.text = String.format(resourceHelper.gs(R.string.format_bg_isf), valueToUnitsToString(Profile.toMgdl(bg, profileFunction.getUnits()), profileFunction.getUnits()), wizard.sens)
treatments_wizard_bginsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromBG) 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) binding.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.carbsinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromCarbs)
treatments_wizard_bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromBolusIOB) binding.bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromBolusIOB)
treatments_wizard_basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromBasalsIOB) 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 // Superbolus
treatments_wizard_sb.text = if (treatments_wizard_sbcheckbox.isChecked) resourceHelper.gs(R.string.twohours) else "" binding.sb.text = if (binding.sbcheckbox.isChecked) resourceHelper.gs(R.string.twohours) else ""
treatments_wizard_sbinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromSuperBolus) binding.sbinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromSuperBolus)
// Trend // Trend
if (treatments_wizard_bgtrendcheckbox.isChecked && wizard.glucoseStatus != null) { if (binding.bgtrendcheckbox.isChecked && wizard.glucoseStatus != null) {
treatments_wizard_bgtrend.text = ((if (wizard.trend > 0) "+" else "") binding.bgtrend.text = ((if (wizard.trend > 0) "+" else "")
+ Profile.toUnitsString(wizard.trend * 3, wizard.trend * 3 / Constants.MMOLL_TO_MGDL, profileFunction.getUnits()) + Profile.toUnitsString(wizard.trend * 3, wizard.trend * 3 / Constants.MMOLL_TO_MGDL, profileFunction.getUnits())
+ " " + profileFunction.getUnits()) + " " + profileFunction.getUnits())
} else { } 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 // COB
if (treatments_wizard_cobcheckbox.isChecked) { if (binding.cobcheckbox.isChecked) {
treatments_wizard_cob.text = String.format(resourceHelper.gs(R.string.format_cob_ic), cob, wizard.ic) binding.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) binding.cobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromCOB)
} else { } else {
treatments_wizard_cob.text = "" binding.cob.text = ""
treatments_wizard_cobinsulin.text = "" binding.cobinsulin.text = ""
} }
if (wizard.calculatedTotalInsulin > 0.0 || carbsAfterConstraint > 0.0) { if (wizard.calculatedTotalInsulin > 0.0 || carbsAfterConstraint > 0.0) {
val insulinText = if (wizard.calculatedTotalInsulin > 0.0) resourceHelper.gs(R.string.formatinsulinunits, wizard.calculatedTotalInsulin) else "" 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 "" 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) binding.total.text = resourceHelper.gs(R.string.result_insulin_carbs, insulinText, carbsText)
ok.visibility = View.VISIBLE binding.ok.visibility = View.VISIBLE
} else { } else {
treatments_wizard_total.text = resourceHelper.gs(R.string.missing_carbs, wizard.carbsEquivalent.toInt()) binding.total.text = resourceHelper.gs(R.string.missing_carbs, wizard.carbsEquivalent.toInt())
ok.visibility = View.INVISIBLE binding.ok.visibility = View.INVISIBLE
} }
} }

View file

@ -9,15 +9,16 @@ import android.view.WindowManager
import dagger.android.support.DaggerDialogFragment import dagger.android.support.DaggerDialogFragment
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.databinding.DialogWizardinfoBinding
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.utils.DecimalFormatter import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.JsonHelper import info.nightscout.androidaps.utils.JsonHelper
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import kotlinx.android.synthetic.main.dialog_wizardinfo.*
import org.json.JSONObject import org.json.JSONObject
import javax.inject.Inject import javax.inject.Inject
class WizardInfoDialog : DaggerDialogFragment() { class WizardInfoDialog : DaggerDialogFragment() {
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@ -27,62 +28,74 @@ class WizardInfoDialog : DaggerDialogFragment() {
this.json = json 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?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? { savedInstanceState: Bundle?): View {
dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE) dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE)
dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
isCancelable = true isCancelable = true
dialog?.setCanceledOnTouchOutside(false) 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
close.setOnClickListener { dismiss() } binding.close.setOnClickListener { dismiss() }
val units = profileFunction.getUnits() val units = profileFunction.getUnits()
val bgString = val bgString =
if (units == Constants.MGDL) DecimalFormatter.to0Decimal(JsonHelper.safeGetDouble(json, "bg")) if (units == Constants.MGDL) DecimalFormatter.to0Decimal(JsonHelper.safeGetDouble(json, "bg"))
else DecimalFormatter.to1Decimal(JsonHelper.safeGetDouble(json, "bg")) else DecimalFormatter.to1Decimal(JsonHelper.safeGetDouble(json, "bg"))
// BG // BG
treatments_wizard_bg.text = resourceHelper.gs(R.string.format_bg_isf, bgString, JsonHelper.safeGetDouble(json, "isf")) binding.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")) binding.bginsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulinbg"))
treatments_wizard_bgcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "insulinbgused") binding.bgcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "insulinbgused")
treatments_wizard_ttcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "ttused") binding.ttcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "ttused")
// Trend // Trend
treatments_wizard_bgtrend.text = JsonHelper.safeGetString(json, "trend") binding.bgtrend.text = JsonHelper.safeGetString(json, "trend")
treatments_wizard_bgtrendinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulintrend")) binding.bgtrendinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulintrend"))
treatments_wizard_bgtrendcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "trendused") binding.bgtrendcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "trendused")
// COB // COB
treatments_wizard_cob.text = resourceHelper.gs(R.string.format_cob_ic, JsonHelper.safeGetDouble(json, "cob"), JsonHelper.safeGetDouble(json, "ic")) binding.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")) binding.cobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulincob"))
treatments_wizard_cobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "cobused") binding.cobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "cobused")
// Bolus IOB // Bolus IOB
treatments_wizard_bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "bolusiob")) binding.bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "bolusiob"))
treatments_wizard_bolusiobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "bolusiobused") binding.bolusiobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "bolusiobused")
// Basal IOB // Basal IOB
treatments_wizard_basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "basaliob")) binding.basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "basaliob"))
treatments_wizard_basaliobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "basaliobused") binding.basaliobcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "basaliobused")
// Superbolus // Superbolus
treatments_wizard_sbinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulinsuperbolus")) binding.sbinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulinsuperbolus"))
treatments_wizard_sbcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "superbolusused") binding.sbcheckbox.isChecked = JsonHelper.safeGetBoolean(json, "superbolusused")
// Carbs // Carbs
treatments_wizard_carbs.text = resourceHelper.gs(R.string.format_carbs_ic, JsonHelper.safeGetDouble(json, "carbs"), JsonHelper.safeGetDouble(json, "ic")) binding.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.carbsinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, JsonHelper.safeGetDouble(json, "insulincarbs"))
// Correction // 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 // Profile
treatments_wizard_profile.text = JsonHelper.safeGetString(json, "profile") binding.profile.text = JsonHelper.safeGetString(json, "profile")
// Notes // Notes
treatments_wizard_notes.text = JsonHelper.safeGetString(json, "notes") binding.notes.text = JsonHelper.safeGetString(json, "notes")
// Percentage // 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 // 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() { override fun onStart() {
super.onStart() super.onStart()
dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
} }
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
} }

View file

@ -1,5 +1,5 @@
package info.nightscout.androidaps.events package info.nightscout.androidaps.events
import info.nightscout.androidaps.db.BgReading import info.nightscout.androidaps.database.entities.GlucoseValue
class EventNewBG(val bgReading: BgReading?) : EventLoop() class EventNewBG(val glucoseValue: GlucoseValue?) : EventLoop()

View file

@ -13,6 +13,7 @@ import com.jjoe64.graphview.GraphView
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.NoSplashAppCompatActivity import info.nightscout.androidaps.activities.NoSplashAppCompatActivity
import info.nightscout.androidaps.databinding.ActivityHistorybrowseBinding
import info.nightscout.androidaps.events.EventCustomCalculationFinished import info.nightscout.androidaps.events.EventCustomCalculationFinished
import info.nightscout.androidaps.events.EventRefreshOverview import info.nightscout.androidaps.events.EventRefreshOverview
import info.nightscout.androidaps.interfaces.ActivePluginProvider 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.T
import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.extensions.toVisibility 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.sharedPreferences.SP
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.activity_historybrowse.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -44,11 +42,12 @@ import java.util.*
import javax.inject.Inject import javax.inject.Inject
class HistoryBrowseActivity : NoSplashAppCompatActivity() { class HistoryBrowseActivity : NoSplashAppCompatActivity() {
@Inject lateinit var injector: HasAndroidInjector @Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var sp: SP @Inject lateinit var sp: SP
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var defaultValueHelper: DefaultValueHelper @Inject lateinit var defaultValueHelper: DefaultValueHelper
@Inject lateinit var iobCobCalculatorPluginHistory: IobCobCalculatorPluginHistory @Inject lateinit var iobCobCalculatorPluginHistory: IobCobCalculatorPluginHistory
@ -68,21 +67,27 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
private var rangeToDisplay = 24 // for graph private var rangeToDisplay = 24 // for graph
private var start: Long = 0 private var start: Long = 0
private val graphLock = Object()
private var eventCustomCalculationFinished = EventCustomCalculationFinished() private var eventCustomCalculationFinished = EventCustomCalculationFinished()
private lateinit var binding: ActivityHistorybrowseBinding
private var destroyed = false
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) 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() start -= T.hours(rangeToDisplay.toLong()).msecs()
runCalculation("onClickLeft") runCalculation("onClickLeft")
} }
historybrowse_right.setOnClickListener { binding.right.setOnClickListener {
start += T.hours(rangeToDisplay.toLong()).msecs() start += T.hours(rangeToDisplay.toLong()).msecs()
runCalculation("onClickRight") runCalculation("onClickRight")
} }
historybrowse_end.setOnClickListener { binding.end.setOnClickListener {
val calendar = Calendar.getInstance() val calendar = Calendar.getInstance()
calendar.timeInMillis = System.currentTimeMillis() calendar.timeInMillis = System.currentTimeMillis()
calendar[Calendar.MILLISECOND] = 0 calendar[Calendar.MILLISECOND] = 0
@ -92,12 +97,12 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
start = calendar.timeInMillis start = calendar.timeInMillis
runCalculation("onClickEnd") runCalculation("onClickEnd")
} }
historybrowse_zoom.setOnClickListener { binding.zoom.setOnClickListener {
rangeToDisplay += 6 rangeToDisplay += 6
rangeToDisplay = if (rangeToDisplay > 24) 6 else rangeToDisplay rangeToDisplay = if (rangeToDisplay > 24) 6 else rangeToDisplay
updateGUI("rangeChange", false) updateGUI("rangeChange", false)
} }
historybrowse_zoom.setOnLongClickListener { binding.zoom.setOnLongClickListener {
val calendar = Calendar.getInstance() val calendar = Calendar.getInstance()
calendar.timeInMillis = start calendar.timeInMillis = start
calendar[Calendar.MILLISECOND] = 0 calendar[Calendar.MILLISECOND] = 0
@ -121,11 +126,11 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
cal[Calendar.MINUTE] = 0 cal[Calendar.MINUTE] = 0
cal[Calendar.HOUR_OF_DAY] = 0 cal[Calendar.HOUR_OF_DAY] = 0
start = cal.timeInMillis start = cal.timeInMillis
historybrowse_date?.text = dateUtil.dateAndTimeString(start) binding.date.text = dateUtil.dateAndTimeString(start)
runCalculation("onClickDate") runCalculation("onClickDate")
} }
historybrowse_date.setOnClickListener { binding.date.setOnClickListener {
val cal = Calendar.getInstance() val cal = Calendar.getInstance()
cal.timeInMillis = start cal.timeInMillis = start
DatePickerDialog(this, dateSetListener, DatePickerDialog(this, dateSetListener,
@ -139,12 +144,12 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
windowManager?.defaultDisplay?.getMetrics(dm) windowManager?.defaultDisplay?.getMetrics(dm)
axisWidth = if (dm.densityDpi <= 120) 3 else if (dm.densityDpi <= 160) 10 else if (dm.densityDpi <= 320) 35 else if (dm.densityDpi <= 420) 50 else if (dm.densityDpi <= 560) 70 else 80 axisWidth = if (dm.densityDpi <= 120) 3 else if (dm.densityDpi <= 160) 10 else if (dm.densityDpi <= 320) 35 else if (dm.densityDpi <= 420) 50 else if (dm.densityDpi <= 560) 70 else 80
historybrowse_bggraph?.gridLabelRenderer?.gridColor = resourceHelper.gc(R.color.graphgrid) binding.bggraph.gridLabelRenderer?.gridColor = resourceHelper.gc(R.color.graphgrid)
historybrowse_bggraph?.gridLabelRenderer?.reloadStyles() binding.bggraph.gridLabelRenderer?.reloadStyles()
historybrowse_bggraph?.gridLabelRenderer?.labelVerticalWidth = axisWidth binding.bggraph.gridLabelRenderer?.labelVerticalWidth = axisWidth
overviewMenus.setupChartMenu(overview_chartMenuButton) overviewMenus.setupChartMenu(binding.chartMenuButton)
prepareGraphs() prepareGraphsIfNeeded(overviewMenus.setting.size)
savedInstanceState?.let { bundle -> savedInstanceState?.let { bundle ->
rangeToDisplay = bundle.getInt("rangeToDisplay", 0) rangeToDisplay = bundle.getInt("rangeToDisplay", 0)
start = bundle.getLong("start", 0) start = bundle.getLong("start", 0)
@ -158,42 +163,47 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
iobCobCalculatorPluginHistory.stopCalculation("onPause") iobCobCalculatorPluginHistory.stopCalculation("onPause")
} }
@Synchronized
override fun onDestroy() {
destroyed = true
super.onDestroy()
}
public override fun onResume() { public override fun onResume() {
super.onResume() super.onResume()
disposable.add(rxBus disposable.add(rxBus
.toObservable(EventAutosensCalculationFinished::class.java) .toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(Schedulers.io()) .observeOn(aapsSchedulers.io)
.subscribe({ .subscribe({
// catch only events from iobCobCalculatorPluginHistory // catch only events from iobCobCalculatorPluginHistory
if (it.cause is EventCustomCalculationFinished) { if (it.cause is EventCustomCalculationFinished) {
updateGUI("EventAutosensCalculationFinished", bgOnly = false) updateGUI("EventAutosensCalculationFinished", bgOnly = false)
} }
}, fabricPrivacy::logException ) }, fabricPrivacy::logException)
) )
disposable.add(rxBus disposable.add(rxBus
.toObservable(EventAutosensBgLoaded::class.java) .toObservable(EventAutosensBgLoaded::class.java)
.observeOn(Schedulers.io()) .observeOn(aapsSchedulers.io)
.subscribe({ .subscribe({
// catch only events from iobCobCalculatorPluginHistory // catch only events from iobCobCalculatorPluginHistory
if (it.cause is EventCustomCalculationFinished) { if (it.cause is EventCustomCalculationFinished) {
updateGUI("EventAutosensCalculationFinished", bgOnly = true) updateGUI("EventAutosensCalculationFinished", bgOnly = true)
} }
}, fabricPrivacy::logException ) }, fabricPrivacy::logException)
) )
disposable.add(rxBus disposable.add(rxBus
.toObservable(EventIobCalculationProgress::class.java) .toObservable(EventIobCalculationProgress::class.java)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(aapsSchedulers.main)
.subscribe({ overview_iobcalculationprogess?.text = it.progress }, fabricPrivacy::logException ) .subscribe({ binding.overviewIobcalculationprogess.text = it.progress }, fabricPrivacy::logException)
) )
disposable.add(rxBus disposable.add(rxBus
.toObservable(EventRefreshOverview::class.java) .toObservable(EventRefreshOverview::class.java)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(aapsSchedulers.main)
.subscribe({ .subscribe({
if (it.now) { if (it.now) {
prepareGraphs()
updateGUI("EventRefreshOverview", bgOnly = false) updateGUI("EventRefreshOverview", bgOnly = false)
} }
}, fabricPrivacy::logException ) }, fabricPrivacy::logException)
) )
if (start == 0L) { if (start == 0L) {
// set start of current day // set start of current day
@ -217,42 +227,41 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
} }
private fun prepareGraphs() { private fun prepareGraphsIfNeeded(numOfGraphs: Int) {
val numOfGraphs = overviewMenus.setting.size 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) { val graph = GraphView(this)
//aapsLogger.debug("New secondary graph count ${numOfGraphs-1}") graph.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, resourceHelper.dpToPx(100)).also { it.setMargins(0, resourceHelper.dpToPx(15), 0, resourceHelper.dpToPx(10)) }
// rebuild needed graph.gridLabelRenderer?.gridColor = resourceHelper.gc(R.color.graphgrid)
secondaryGraphs.clear() graph.gridLabelRenderer?.reloadStyles()
secondaryGraphsLabel.clear() graph.gridLabelRenderer?.isHorizontalLabelsVisible = false
history_iobgraph.removeAllViews() graph.gridLabelRenderer?.labelVerticalWidth = axisWidth
for (i in 1 until numOfGraphs) { graph.gridLabelRenderer?.numVerticalLabels = 3
val relativeLayout = RelativeLayout(this) graph.viewport.backgroundColor = Color.argb(20, 255, 255, 255) // 8% of gray
relativeLayout.layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) relativeLayout.addView(graph)
val graph = GraphView(this) val label = TextView(this)
graph.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, resourceHelper.dpToPx(100)).also { it.setMargins(0, resourceHelper.dpToPx(15), 0, resourceHelper.dpToPx(10)) } val layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).also { it.setMargins(resourceHelper.dpToPx(30), resourceHelper.dpToPx(25), 0, 0) }
graph.gridLabelRenderer?.gridColor = resourceHelper.gc(R.color.graphgrid) layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP)
graph.gridLabelRenderer?.reloadStyles() layoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT)
graph.gridLabelRenderer?.isHorizontalLabelsVisible = false label.layoutParams = layoutParams
graph.gridLabelRenderer?.labelVerticalWidth = axisWidth relativeLayout.addView(label)
graph.gridLabelRenderer?.numVerticalLabels = 3 secondaryGraphsLabel.add(label)
graph.viewport.backgroundColor = Color.argb(20, 255, 255, 255) // 8% of gray
relativeLayout.addView(graph)
val label = TextView(this) binding.iobGraph.addView(relativeLayout)
val layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).also { it.setMargins(resourceHelper.dpToPx(30), resourceHelper.dpToPx(25), 0, 0) } secondaryGraphs.add(graph)
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)
} }
} }
} }
private fun runCalculation(from: String) { private fun runCalculation(from: String) {
@ -265,7 +274,10 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
} }
} }
@Synchronized
fun updateGUI(from: String, bgOnly: Boolean) { fun updateGUI(from: String, bgOnly: Boolean) {
val menuChartSettings = overviewMenus.setting
prepareGraphsIfNeeded(menuChartSettings.size)
aapsLogger.debug(LTag.UI, "updateGUI from: $from") aapsLogger.debug(LTag.UI, "updateGUI from: $from")
val pump = activePlugin.activePump val pump = activePlugin.activePump
val profile = profileFunction.getProfile() val profile = profileFunction.getProfile()
@ -274,13 +286,13 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
val highLine = defaultValueHelper.determineHighLine() val highLine = defaultValueHelper.determineHighLine()
lifecycleScope.launch(Dispatchers.Main) { lifecycleScope.launch(Dispatchers.Main) {
historybrowse_noprofile?.visibility = (profile == null).toVisibility() binding.noprofile.visibility = (profile == null).toVisibility()
profile ?: return@launch profile ?: return@launch
historybrowse_bggraph ?: return@launch if (destroyed) return@launch
historybrowse_date?.text = dateUtil.dateAndTimeString(start) binding.date.text = dateUtil.dateAndTimeString(start)
historybrowse_zoom?.text = rangeToDisplay.toString() binding.zoom.text = rangeToDisplay.toString()
val graphData = GraphData(injector, historybrowse_bggraph, iobCobCalculatorPluginHistory, treatmentsPluginHistory) val graphData = GraphData(injector, binding.bggraph, iobCobCalculatorPluginHistory, treatmentsPluginHistory)
val secondaryGraphsData: ArrayList<GraphData> = ArrayList() val secondaryGraphsData: ArrayList<GraphData> = ArrayList()
// do preparation in different thread // do preparation in different thread
@ -296,9 +308,6 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
// **** BG **** // **** BG ****
graphData.addBgReadings(fromTime, toTime, lowLine, highLine, null) graphData.addBgReadings(fromTime, toTime, lowLine, highLine, null)
// set manual x bounds to have nice steps
graphData.formatAxis(fromTime, toTime)
// add target line // add target line
graphData.addTargetLine(fromTime, toTime, profile, null) graphData.addTargetLine(fromTime, toTime, profile, null)
@ -308,63 +317,74 @@ class HistoryBrowseActivity : NoSplashAppCompatActivity() {
if (!bgOnly) { if (!bgOnly) {
// Treatments // Treatments
graphData.addTreatments(fromTime, toTime) 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) graphData.addActivity(fromTime, toTime, false, 0.8)
// add basal data // 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) graphData.addBasals(fromTime, toTime, lowLine / graphData.maxY / 1.2)
} }
// ------------------ 2nd graph // ------------------ 2nd graph
for (g in 0 until secondaryGraphs.size) { synchronized(graphLock) {
val secondGraphData = GraphData(injector, secondaryGraphs[g], iobCobCalculatorPluginHistory, treatmentsPluginHistory) for (g in 0 until secondaryGraphs.size) {
var useIobForScale = false val secondGraphData = GraphData(injector, secondaryGraphs[g], iobCobCalculatorPluginHistory, treatmentsPluginHistory)
var useCobForScale = false var useIobForScale = false
var useDevForScale = false var useCobForScale = false
var useRatioForScale = false var useDevForScale = false
var useDSForScale = false var useRatioForScale = false
var useIAForScale = false var useDSForScale = false
var useABSForScale = false var useBGIForScale = false
when { var useABSForScale = false
overviewMenus.setting[g + 1][OverviewMenus.CharType.IOB.ordinal] -> useIobForScale = true when {
overviewMenus.setting[g + 1][OverviewMenus.CharType.COB.ordinal] -> useCobForScale = true menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal] -> useIobForScale = true
overviewMenus.setting[g + 1][OverviewMenus.CharType.DEV.ordinal] -> useDevForScale = true menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal] -> useCobForScale = true
overviewMenus.setting[g + 1][OverviewMenus.CharType.SEN.ordinal] -> useRatioForScale = true menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] -> useDevForScale = true
overviewMenus.setting[g + 1][OverviewMenus.CharType.ACT.ordinal] -> useIAForScale = true menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal] -> useRatioForScale = true
overviewMenus.setting[g + 1][OverviewMenus.CharType.ABS.ordinal] -> useABSForScale = true menuChartSettings[g + 1][OverviewMenus.CharType.BGI.ordinal] -> useBGIForScale = true
overviewMenus.setting[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] -> useDSForScale = 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 // finally enforce drawing of graphs in UI thread
graphData.performUpdate() graphData.performUpdate()
if (!bgOnly) if (!bgOnly)
for (g in 0 until secondaryGraphs.size) { synchronized(graphLock) {
secondaryGraphsLabel[g].text = overviewMenus.enabledTypes(g + 1) for (g in 0 until secondaryGraphs.size) {
secondaryGraphs[g].visibility = (!bgOnly && ( secondaryGraphsLabel[g].text = overviewMenus.enabledTypes(g + 1)
overviewMenus.setting[g + 1][OverviewMenus.CharType.IOB.ordinal] || secondaryGraphs[g].visibility = (!bgOnly && (
overviewMenus.setting[g + 1][OverviewMenus.CharType.COB.ordinal] || menuChartSettings[g + 1][OverviewMenus.CharType.IOB.ordinal] ||
overviewMenus.setting[g + 1][OverviewMenus.CharType.DEV.ordinal] || menuChartSettings[g + 1][OverviewMenus.CharType.COB.ordinal] ||
overviewMenus.setting[g + 1][OverviewMenus.CharType.SEN.ordinal] || menuChartSettings[g + 1][OverviewMenus.CharType.DEV.ordinal] ||
overviewMenus.setting[g + 1][OverviewMenus.CharType.ACT.ordinal] || menuChartSettings[g + 1][OverviewMenus.CharType.SEN.ordinal] ||
overviewMenus.setting[g + 1][OverviewMenus.CharType.ABS.ordinal] || menuChartSettings[g + 1][OverviewMenus.CharType.ACT.ordinal] ||
overviewMenus.setting[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal] menuChartSettings[g + 1][OverviewMenus.CharType.ABS.ordinal] ||
)).toVisibility() menuChartSettings[g + 1][OverviewMenus.CharType.DEVSLOPE.ordinal]
secondaryGraphsData[g].performUpdate() )).toVisibility()
secondaryGraphsData[g].performUpdate()
}
} }
} }
} }

View file

@ -1,6 +1,7 @@
package info.nightscout.androidaps.historyBrowser package info.nightscout.androidaps.historyBrowser
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger 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.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -20,6 +22,7 @@ import javax.inject.Singleton
class IobCobCalculatorPluginHistory @Inject constructor( class IobCobCalculatorPluginHistory @Inject constructor(
injector: HasAndroidInjector, injector: HasAndroidInjector,
aapsLogger: AAPSLogger, aapsLogger: AAPSLogger,
aapsSchedulers: AapsSchedulers,
rxBus: RxBusWrapper, rxBus: RxBusWrapper,
sp: SP, sp: SP,
resourceHelper: ResourceHelper, resourceHelper: ResourceHelper,
@ -30,9 +33,10 @@ class IobCobCalculatorPluginHistory @Inject constructor(
sensitivityAAPSPlugin: SensitivityAAPSPlugin, sensitivityAAPSPlugin: SensitivityAAPSPlugin,
sensitivityWeightedAveragePlugin: SensitivityWeightedAveragePlugin, sensitivityWeightedAveragePlugin: SensitivityWeightedAveragePlugin,
fabricPrivacy: FabricPrivacy, fabricPrivacy: FabricPrivacy,
dateUtil: DateUtil dateUtil: DateUtil,
) : IobCobCalculatorPlugin(injector, aapsLogger, rxBus, sp, resourceHelper, profileFunction, repository: AppRepository
activePlugin, treatmentsPluginHistory, sensitivityOref1Plugin, sensitivityAAPSPlugin, sensitivityWeightedAveragePlugin, fabricPrivacy, dateUtil) { ) : IobCobCalculatorPlugin(injector, aapsLogger, aapsSchedulers, rxBus, sp, resourceHelper, profileFunction,
activePlugin, treatmentsPluginHistory, sensitivityOref1Plugin, sensitivityAAPSPlugin, sensitivityWeightedAveragePlugin, fabricPrivacy, dateUtil, repository) {
override fun onStart() { // do not attach to rxbus override fun onStart() { // do not attach to rxbus
} }

View file

@ -13,6 +13,7 @@ import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.resources.ResourceHelper 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.sharedPreferences.SP
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -21,6 +22,7 @@ import javax.inject.Singleton
class TreatmentsPluginHistory @Inject constructor( class TreatmentsPluginHistory @Inject constructor(
injector: HasAndroidInjector, injector: HasAndroidInjector,
aapsLogger: AAPSLogger, aapsLogger: AAPSLogger,
aapsSchedulers: AapsSchedulers,
rxBus: RxBusWrapper, rxBus: RxBusWrapper,
resourceHelper: ResourceHelper, resourceHelper: ResourceHelper,
context: Context, context: Context,
@ -31,7 +33,7 @@ class TreatmentsPluginHistory @Inject constructor(
fabricPrivacy: FabricPrivacy, fabricPrivacy: FabricPrivacy,
dateUtil: DateUtil, dateUtil: DateUtil,
uploadQueue: UploadQueue 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 { init {
onStart() onStart()

View file

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

View file

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

View file

@ -6,6 +6,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import dagger.android.support.DaggerFragment import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.databinding.LoopFragmentBinding
import info.nightscout.androidaps.interfaces.Constraint import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopSetLastRunGui 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.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.HtmlHelper 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.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.loop_fragment.* import io.reactivex.rxkotlin.plusAssign
import javax.inject.Inject import javax.inject.Inject
class LoopFragment : DaggerFragment() { class LoopFragment : DaggerFragment() {
@Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var sp: SP @Inject lateinit var sp: SP
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@ -33,16 +35,23 @@ class LoopFragment : DaggerFragment() {
private var disposable: CompositeDisposable = CompositeDisposable() 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?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? { savedInstanceState: Bundle?): View {
return inflater.inflate(R.layout.loop_fragment, container, false) _binding = LoopFragmentBinding.inflate(inflater, container, false)
return binding.root
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
loop_run.setOnClickListener { binding.run.setOnClickListener {
loop_lastrun.text = resourceHelper.gs(R.string.executing) binding.lastrun.text = resourceHelper.gs(R.string.executing)
Thread { loopPlugin.invoke("Loop button", true) }.start() Thread { loopPlugin.invoke("Loop button", true) }.start()
} }
} }
@ -52,18 +61,18 @@ class LoopFragment : DaggerFragment() {
super.onResume() super.onResume()
disposable += rxBus disposable += rxBus
.toObservable(EventLoopUpdateGui::class.java) .toObservable(EventLoopUpdateGui::class.java)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(aapsSchedulers.main)
.subscribe({ .subscribe({
updateGUI() updateGUI()
}, { fabricPrivacy.logException(it) }) }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventLoopSetLastRunGui::class.java) .toObservable(EventLoopSetLastRunGui::class.java)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(aapsSchedulers.main)
.subscribe({ .subscribe({
clearGUI() clearGUI()
loop_lastrun?.text = it.text binding.lastrun.text = it.text
}, { fabricPrivacy.logException(it) }) }, fabricPrivacy::logException)
updateGUI() updateGUI()
sp.putBoolean(R.string.key_objectiveuseloop, true) sp.putBoolean(R.string.key_objectiveuseloop, true)
@ -76,22 +85,28 @@ class LoopFragment : DaggerFragment() {
} }
@Synchronized @Synchronized
fun updateGUI() { override fun onDestroyView() {
if (loop_request == null) return super.onDestroyView()
loopPlugin.lastRun?.let { _binding = null
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)
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 = val constraints =
@ -101,22 +116,22 @@ class LoopFragment : DaggerFragment() {
constraintsProcessed.smbConstraint?.let { smbConstraint -> allConstraints.copyReasons(smbConstraint) } constraintsProcessed.smbConstraint?.let { smbConstraint -> allConstraints.copyReasons(smbConstraint) }
allConstraints.getMostLimitedReasons(aapsLogger) allConstraints.getMostLimitedReasons(aapsLogger)
} ?: "" } ?: ""
loop_constraints?.text = constraints binding.constraints.text = constraints
} }
} }
@Synchronized @Synchronized
private fun clearGUI() { private fun clearGUI() {
loop_request?.text = "" binding.request.text = ""
loop_constraints?.text = "" binding.constraints.text = ""
loop_constraintsprocessed?.text = "" binding.constraintsprocessed.text = ""
loop_source?.text = "" binding.source.text = ""
loop_lastrun?.text = "" binding.lastrun.text = ""
loop_smbrequest_time?.text = "" binding.smbrequestTime.text = ""
loop_smbexecution_time?.text = "" binding.smbexecutionTime.text = ""
loop_tbrrequest_time?.text = "" binding.tbrrequestTime.text = ""
loop_tbrexecution_time?.text = "" binding.tbrexecutionTime.text = ""
loop_tbrsetbypump?.text = "" binding.tbrsetbypump.text = ""
loop_smbsetbypump?.text = "" binding.smbsetbypump.text = ""
} }
} }

View file

@ -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> 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> 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.
* <p>
*/
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<Boolean> 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<Boolean> 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<Boolean> 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);
}
}

View file

@ -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.
<p>
*/
disposable.add(rxBus
.toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ event: EventAutosensCalculationFinished ->
// Autosens calculation not triggered by a new BG
if (event.cause !is EventNewBG) return@subscribe
val glucoseValue = 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"
}
}

View file

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

View file

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

View file

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

View file

@ -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<IobTotal?>?,
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<Any?> -> objects[1] }
}
private fun makeParamArray(jsonArray: JSONArray?, rhino: Context, scope: Scriptable): Any {
return NativeJSON.parse(rhino, scope, jsonArray.toString()) { _: Context?, _: Scriptable?, _: Scriptable?, objects: Array<Any?> -> 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
}
}

View file

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

View file

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

View file

@ -7,6 +7,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import dagger.android.support.DaggerFragment import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.databinding.OpenapsamaFragmentBinding
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui 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.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.JSONFormatter import info.nightscout.androidaps.utils.JSONFormatter
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper 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 io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.openapsama_fragment.* import io.reactivex.rxkotlin.plusAssign
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONException import org.json.JSONException
import javax.inject.Inject import javax.inject.Inject
class OpenAPSAMAFragment : DaggerFragment() { class OpenAPSAMAFragment : DaggerFragment() {
private var disposable: CompositeDisposable = CompositeDisposable() private var disposable: CompositeDisposable = CompositeDisposable()
@Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var openAPSAMAPlugin: OpenAPSAMAPlugin @Inject lateinit var openAPSAMAPlugin: OpenAPSAMAPlugin
@Inject lateinit var dateUtil: DateUtil @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?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? { savedInstanceState: Bundle?): View {
return inflater.inflate(R.layout.openapsama_fragment, container, false) _binding = OpenapsamaFragmentBinding.inflate(inflater, container, false)
return binding.root
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
openapsma_run.setOnClickListener { binding.run.setOnClickListener {
openAPSAMAPlugin.invoke("OpenAPSAMA button", false) openAPSAMAPlugin.invoke("OpenAPSAMA button", false)
} }
} }
@ -53,16 +62,16 @@ class OpenAPSAMAFragment : DaggerFragment() {
disposable += rxBus disposable += rxBus
.toObservable(EventOpenAPSUpdateGui::class.java) .toObservable(EventOpenAPSUpdateGui::class.java)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(aapsSchedulers.main)
.subscribe({ .subscribe({
updateGUI() updateGUI()
}, { fabricPrivacy.logException(it) }) }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventOpenAPSUpdateResultGui::class.java) .toObservable(EventOpenAPSUpdateResultGui::class.java)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(aapsSchedulers.main)
.subscribe({ .subscribe({
updateResultGUI(it.text) updateResultGUI(it.text)
}, { fabricPrivacy.logException(it) }) }, fabricPrivacy::logException)
updateGUI() updateGUI()
} }
@ -73,47 +82,53 @@ class OpenAPSAMAFragment : DaggerFragment() {
disposable.clear() disposable.clear()
} }
@Synchronized
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
@Synchronized @Synchronized
private fun updateGUI() { private fun updateGUI() {
if (openapsma_result == null) return if (_binding == null) return
openAPSAMAPlugin.lastAPSResult?.let { lastAPSResult -> openAPSAMAPlugin.lastAPSResult?.let { lastAPSResult ->
openapsma_result.text = JSONFormatter.format(lastAPSResult.json) binding.result.text = JSONFormatter.format(lastAPSResult.json)
openapsma_request.text = lastAPSResult.toSpanned() binding.request.text = lastAPSResult.toSpanned()
} }
openAPSAMAPlugin.lastDetermineBasalAdapterAMAJS?.let { determineBasalAdapterAMAJS -> openAPSAMAPlugin.lastDetermineBasalAdapterAMAJS?.let { determineBasalAdapterAMAJS ->
openapsma_glucosestatus.text = JSONFormatter.format(determineBasalAdapterAMAJS.glucoseStatusParam) binding.glucosestatus.text = JSONFormatter.format(determineBasalAdapterAMAJS.glucoseStatusParam)
openapsma_currenttemp.text = JSONFormatter.format(determineBasalAdapterAMAJS.currentTempParam) binding.currenttemp.text = JSONFormatter.format(determineBasalAdapterAMAJS.currentTempParam)
try { try {
val iobArray = JSONArray(determineBasalAdapterAMAJS.iobDataParam) 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) { } catch (e: JSONException) {
aapsLogger.error(LTag.APS, "Unhandled exception", e) aapsLogger.error(LTag.APS, "Unhandled exception", e)
@Suppress("SetTextI18n") @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) binding.profile.text = JSONFormatter.format(determineBasalAdapterAMAJS.profileParam)
openapsma_mealdata.text = JSONFormatter.format(determineBasalAdapterAMAJS.mealDataParam) binding.mealdata.text = JSONFormatter.format(determineBasalAdapterAMAJS.mealDataParam)
openapsma_scriptdebugdata.text = determineBasalAdapterAMAJS.scriptDebug binding.scriptdebugdata.text = determineBasalAdapterAMAJS.scriptDebug
} }
if (openAPSAMAPlugin.lastAPSRun != 0L) { if (openAPSAMAPlugin.lastAPSRun != 0L) {
openapsma_lastrun.text = dateUtil.dateAndTimeString(openAPSAMAPlugin.lastAPSRun) binding.lastrun.text = dateUtil.dateAndTimeString(openAPSAMAPlugin.lastAPSRun)
} }
openAPSAMAPlugin.lastAutosensResult?.let { openAPSAMAPlugin.lastAutosensResult.let {
openapsma_autosensdata.text = JSONFormatter.format(it.json()) binding.autosensdata.text = JSONFormatter.format(it.json())
} }
} }
private fun updateResultGUI(text: String) { private fun updateResultGUI(text: String) {
openapsma_result.text = text binding.result.text = text
openapsma_glucosestatus.text = "" binding.glucosestatus.text = ""
openapsma_currenttemp.text = "" binding.currenttemp.text = ""
openapsma_iobdata.text = "" binding.iobdata.text = ""
openapsma_profile.text = "" binding.profile.text = ""
openapsma_mealdata.text = "" binding.mealdata.text = ""
openapsma_autosensdata.text = "" binding.autosensdata.text = ""
openapsma_scriptdebugdata.text = "" binding.scriptdebugdata.text = ""
openapsma_request.text = "" binding.request.text = ""
openapsma_lastrun.text = "" binding.lastrun.text = ""
} }
} }

View file

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

View file

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

View file

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

View file

@ -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<IobTotal>,
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<Any?> -> objects[1] }
}
private fun makeParamArray(jsonArray: JSONArray?, rhino: Context, scope: Scriptable): Any {
return NativeJSON.parse(rhino, scope, jsonArray.toString()) { _: Context?, _: Scriptable?, _: Scriptable?, objects: Array<Any?> -> 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)
}
}

View file

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

View file

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

View file

@ -8,6 +8,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import dagger.android.support.DaggerFragment import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.databinding.OpenapsamaFragmentBinding
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui 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.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.JSONFormatter import info.nightscout.androidaps.utils.JSONFormatter
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.resources.ResourceHelper 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 io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.openapsama_fragment.* import io.reactivex.rxkotlin.plusAssign
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONException import org.json.JSONException
import javax.inject.Inject import javax.inject.Inject
class OpenAPSSMBFragment : DaggerFragment() { class OpenAPSSMBFragment : DaggerFragment() {
private var disposable: CompositeDisposable = CompositeDisposable() private var disposable: CompositeDisposable = CompositeDisposable()
@Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var openAPSSMBPlugin: OpenAPSSMBPlugin @Inject lateinit var openAPSSMBPlugin: OpenAPSSMBPlugin
@Inject lateinit var dateUtil: DateUtil @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?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? { savedInstanceState: Bundle?): View {
return inflater.inflate(R.layout.openapsama_fragment, container, false) _binding = OpenapsamaFragmentBinding.inflate(inflater, container, false)
return binding.root
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
openapsma_run.setOnClickListener { binding.run.setOnClickListener {
openAPSSMBPlugin.invoke("OpenAPSSMB button", false) openAPSSMBPlugin.invoke("OpenAPSSMB button", false)
} }
} }
@ -53,16 +62,16 @@ class OpenAPSSMBFragment : DaggerFragment() {
super.onResume() super.onResume()
disposable += rxBus disposable += rxBus
.toObservable(EventOpenAPSUpdateGui::class.java) .toObservable(EventOpenAPSUpdateGui::class.java)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(aapsSchedulers.main)
.subscribe({ .subscribe({
updateGUI() updateGUI()
}, { fabricPrivacy.logException(it) }) }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventOpenAPSUpdateResultGui::class.java) .toObservable(EventOpenAPSUpdateResultGui::class.java)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(aapsSchedulers.main)
.subscribe({ .subscribe({
updateResultGUI(it.text) updateResultGUI(it.text)
}, { fabricPrivacy.logException(it) }) }, fabricPrivacy::logException)
updateGUI() updateGUI()
} }
@ -73,52 +82,58 @@ class OpenAPSSMBFragment : DaggerFragment() {
disposable.clear() disposable.clear()
} }
@Synchronized
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
@Synchronized @Synchronized
fun updateGUI() { fun updateGUI() {
if (openapsma_result == null) return if (_binding == null) return
openAPSSMBPlugin.lastAPSResult?.let { lastAPSResult -> openAPSSMBPlugin.lastAPSResult?.let { lastAPSResult ->
openapsma_result.text = JSONFormatter.format(lastAPSResult.json) binding.result.text = JSONFormatter.format(lastAPSResult.json)
openapsma_request.text = lastAPSResult.toSpanned() binding.request.text = lastAPSResult.toSpanned()
} }
openAPSSMBPlugin.lastDetermineBasalAdapterSMBJS?.let { determineBasalAdapterSMBJS -> openAPSSMBPlugin.lastDetermineBasalAdapterSMBJS?.let { determineBasalAdapterSMBJS ->
openapsma_glucosestatus.text = JSONFormatter.format(determineBasalAdapterSMBJS.glucoseStatusParam) binding.glucosestatus.text = JSONFormatter.format(determineBasalAdapterSMBJS.glucoseStatusParam)
openapsma_currenttemp.text = JSONFormatter.format(determineBasalAdapterSMBJS.currentTempParam) binding.currenttemp.text = JSONFormatter.format(determineBasalAdapterSMBJS.currentTempParam)
try { try {
val iobArray = JSONArray(determineBasalAdapterSMBJS.iobDataParam) 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) { } catch (e: JSONException) {
aapsLogger.error(LTag.APS, "Unhandled exception", e) aapsLogger.error(LTag.APS, "Unhandled exception", e)
@SuppressLint("SetTextI18n") @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) binding.profile.text = JSONFormatter.format(determineBasalAdapterSMBJS.profileParam)
openapsma_mealdata.text = JSONFormatter.format(determineBasalAdapterSMBJS.mealDataParam) binding.mealdata.text = JSONFormatter.format(determineBasalAdapterSMBJS.mealDataParam)
openapsma_scriptdebugdata.text = determineBasalAdapterSMBJS.scriptDebug binding.scriptdebugdata.text = determineBasalAdapterSMBJS.scriptDebug
openAPSSMBPlugin.lastAPSResult?.inputConstraints?.let { openAPSSMBPlugin.lastAPSResult?.inputConstraints?.let {
openapsma_constraints.text = it.getReasons(aapsLogger) binding.constraints.text = it.getReasons(aapsLogger)
} }
} }
if (openAPSSMBPlugin.lastAPSRun != 0L) { if (openAPSSMBPlugin.lastAPSRun != 0L) {
openapsma_lastrun.text = dateUtil.dateAndTimeString(openAPSSMBPlugin.lastAPSRun) binding.lastrun.text = dateUtil.dateAndTimeString(openAPSSMBPlugin.lastAPSRun)
} }
openAPSSMBPlugin.lastAutosensResult?.let { openAPSSMBPlugin.lastAutosensResult.let {
openapsma_autosensdata.text = JSONFormatter.format(it.json()) binding.autosensdata.text = JSONFormatter.format(it.json())
} }
} }
@Synchronized @Synchronized
private fun updateResultGUI(text: String) { private fun updateResultGUI(text: String) {
if (openapsma_result == null) return if (_binding == null) return
openapsma_result.text = text binding.result.text = text
openapsma_glucosestatus.text = "" binding.glucosestatus.text = ""
openapsma_currenttemp.text = "" binding.currenttemp.text = ""
openapsma_iobdata.text = "" binding.iobdata.text = ""
openapsma_profile.text = "" binding.profile.text = ""
openapsma_mealdata.text = "" binding.mealdata.text = ""
openapsma_autosensdata.text = "" binding.autosensdata.text = ""
openapsma_scriptdebugdata.text = "" binding.scriptdebugdata.text = ""
openapsma_request.text = "" binding.request.text = ""
openapsma_lastrun.text = "" binding.lastrun.text = ""
} }
} }

View file

@ -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<Double> inputConstraints = new Constraint<>(0d); // fake. only for collecting all results
Constraint<Double> 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<Double> 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<Boolean> smbAllowed = new Constraint<>(!tempBasalFallback);
constraintChecker.isSMBModeEnabled(smbAllowed);
inputConstraints.copyReasons(smbAllowed);
Constraint<Boolean> advancedFiltering = new Constraint<>(!tempBasalFallback);
constraintChecker.isAdvancedFilteringEnabled(advancedFiltering);
inputConstraints.copyReasons(advancedFiltering);
Constraint<Boolean> 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<Boolean> isSuperBolusEnabled(Constraint<Boolean> value) {
value.set(getAapsLogger(), false);
return value;
}
}

View file

@ -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<SwitchPreference>(resourceHelper.gs(R.string.key_enableSMB_with_COB))?.isVisible = !smbAlwaysEnabled
preferenceFragment.findPreference<SwitchPreference>(resourceHelper.gs(R.string.key_enableSMB_with_temptarget))?.isVisible = !smbAlwaysEnabled
preferenceFragment.findPreference<SwitchPreference>(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<Boolean>): Constraint<Boolean> {
value[aapsLogger] = false
return value
}
}

View file

@ -12,22 +12,24 @@ import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.Config import info.nightscout.androidaps.Config
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.PreferencesActivity import info.nightscout.androidaps.activities.PreferencesActivity
import info.nightscout.androidaps.databinding.ConfigbuilderFragmentBinding
import info.nightscout.androidaps.events.EventRebuildTabs import info.nightscout.androidaps.events.EventRebuildTabs
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.events.EventConfigBuilderUpdateGui import info.nightscout.androidaps.plugins.configBuilder.events.EventConfigBuilderUpdateGui
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.extensions.plusAssign import io.reactivex.rxkotlin.plusAssign
import info.nightscout.androidaps.utils.extensions.toVisibility import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.utils.protection.ProtectionCheck import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import io.reactivex.android.schedulers.AndroidSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.configbuilder_fragment.*
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
class ConfigBuilderFragment : DaggerFragment() { class ConfigBuilderFragment : DaggerFragment() {
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var configBuilderPlugin: ConfigBuilderPlugin @Inject lateinit var configBuilderPlugin: ConfigBuilderPlugin
@ -39,25 +41,32 @@ class ConfigBuilderFragment : DaggerFragment() {
private var disposable: CompositeDisposable = CompositeDisposable() private var disposable: CompositeDisposable = CompositeDisposable()
private val pluginViewHolders = ArrayList<PluginViewHolder>() private val pluginViewHolders = ArrayList<PluginViewHolder>()
private var _binding: ConfigbuilderFragmentBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? { savedInstanceState: Bundle?): View {
return inflater.inflate(R.layout.configbuilder_fragment, container, false) _binding = ConfigbuilderFragmentBinding.inflate(inflater, container, false)
return binding.root
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
if (protectionCheck.isLocked(ProtectionCheck.Protection.PREFERENCES)) if (protectionCheck.isLocked(ProtectionCheck.Protection.PREFERENCES))
configbuilder_main_layout.visibility = View.GONE binding.mainLayout.visibility = View.GONE
else else
unlock.visibility = View.GONE binding.unlock.visibility = View.GONE
unlock.setOnClickListener { binding.unlock.setOnClickListener {
activity?.let { activity -> activity?.let { activity ->
protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, Runnable { protectionCheck.queryProtection(activity, ProtectionCheck.Protection.PREFERENCES, {
activity.runOnUiThread { activity.runOnUiThread {
configbuilder_main_layout.visibility = View.VISIBLE binding.mainLayout.visibility = View.VISIBLE
unlock.visibility = View.GONE binding.unlock.visibility = View.GONE
} }
}) })
} }
@ -69,10 +78,10 @@ class ConfigBuilderFragment : DaggerFragment() {
super.onResume() super.onResume()
disposable += rxBus disposable += rxBus
.toObservable(EventConfigBuilderUpdateGui::class.java) .toObservable(EventConfigBuilderUpdateGui::class.java)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(aapsSchedulers.main)
.subscribe({ .subscribe({
for (pluginViewHolder in pluginViewHolders) pluginViewHolder.update() for (pluginViewHolder in pluginViewHolders) pluginViewHolder.update()
}, { fabricPrivacy.logException(it) }) }, fabricPrivacy::logException)
updateGUI() updateGUI()
} }
@ -82,9 +91,15 @@ class ConfigBuilderFragment : DaggerFragment() {
disposable.clear() disposable.clear()
} }
@Synchronized
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
@Synchronized @Synchronized
private fun updateGUI() { private fun updateGUI() {
configbuilder_categories.removeAllViews() binding.categories.removeAllViews()
if (!config.NSCLIENT) { if (!config.NSCLIENT) {
createViewsForPlugins(R.string.configbuilder_profile, R.string.configbuilder_profile_description, PluginType.PROFILE, activePlugin.getSpecificPluginsVisibleInListByInterface(ProfileInterface::class.java, PluginType.PROFILE)) 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) pluginContainer.addView(pluginViewHolder.baseView)
pluginViewHolders.add(pluginViewHolder) pluginViewHolders.add(pluginViewHolder)
} }
configbuilder_categories.addView(parent) binding.categories.addView(parent)
} }
inner class PluginViewHolder internal constructor(private val fragment: ConfigBuilderFragment, inner class PluginViewHolder internal constructor(private val fragment: ConfigBuilderFragment,
@ -157,7 +172,7 @@ class ConfigBuilderFragment : DaggerFragment() {
pluginPreferences.setOnClickListener { pluginPreferences.setOnClickListener {
fragment.activity?.let { activity -> 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) val i = Intent(fragment.context, PreferencesActivity::class.java)
i.putExtra("id", plugin.preferencesId) i.putExtra("id", plugin.preferencesId)
fragment.startActivity(i) fragment.startActivity(i)
@ -174,7 +189,7 @@ class ConfigBuilderFragment : DaggerFragment() {
enabledInclusive.isChecked = plugin.isEnabled(pluginType) enabledInclusive.isChecked = plugin.isEnabled(pluginType)
enabledInclusive.isEnabled = !plugin.pluginDescription.alwaysEnabled enabledInclusive.isEnabled = !plugin.pluginDescription.alwaysEnabled
enabledExclusive.isEnabled = !plugin.pluginDescription.alwaysEnabled enabledExclusive.isEnabled = !plugin.pluginDescription.alwaysEnabled
if(plugin.menuIcon != -1) { if (plugin.menuIcon != -1) {
pluginIcon.visibility = View.VISIBLE pluginIcon.visibility = View.VISIBLE
pluginIcon.setImageDrawable(context?.let { ContextCompat.getDrawable(it, plugin.menuIcon) }) pluginIcon.setImageDrawable(context?.let { ContextCompat.getDrawable(it, plugin.menuIcon) })
} else { } else {
@ -196,7 +211,5 @@ class ConfigBuilderFragment : DaggerFragment() {
private fun areMultipleSelectionsAllowed(type: PluginType): Boolean { private fun areMultipleSelectionsAllowed(type: PluginType): Boolean {
return type == PluginType.GENERAL || type == PluginType.CONSTRAINTS || type == PluginType.LOOP return type == PluginType.GENERAL || type == PluginType.CONSTRAINTS || type == PluginType.LOOP
} }
} }
} }

View file

@ -9,9 +9,10 @@ import info.nightscout.androidaps.events.EventRebuildTabs
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.events.EventConfigBuilderUpdateGui 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.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import java.util.* import java.util.*
@ -25,7 +26,8 @@ class ConfigBuilderPlugin @Inject constructor(
resourceHelper: ResourceHelper, resourceHelper: ResourceHelper,
private val sp: SP, private val sp: SP,
private val rxBus: RxBusWrapper, private val rxBus: RxBusWrapper,
private val activePlugin: ActivePluginProvider private val activePlugin: ActivePluginProvider,
private val uel: UserEntryLogger
) : PluginBase(PluginDescription() ) : PluginBase(PluginDescription()
.mainType(PluginType.GENERAL) .mainType(PluginType.GENERAL)
.fragmentClass(ConfigBuilderFragment::class.java.name) .fragmentClass(ConfigBuilderFragment::class.java.name)
@ -137,9 +139,10 @@ class ConfigBuilderPlugin @Inject constructor(
if (allowHardwarePump || activity == null) { if (allowHardwarePump || activity == null) {
performPluginSwitch(changedPlugin, newState, type) performPluginSwitch(changedPlugin, newState, type)
} else { } 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) performPluginSwitch(changedPlugin, newState, type)
sp.putBoolean("allow_hardware_pump", true) sp.putBoolean("allow_hardware_pump", true)
uel.log("HW PUMP ALLOWED")
aapsLogger.debug(LTag.PUMP, "First time HW pump allowed!") aapsLogger.debug(LTag.PUMP, "First time HW pump allowed!")
}, Runnable { }, Runnable {
rxBus.send(EventConfigBuilderUpdateGui()) rxBus.send(EventConfigBuilderUpdateGui())

View file

@ -9,8 +9,6 @@ import android.view.Gravity
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
@ -19,11 +17,14 @@ import androidx.recyclerview.widget.LinearSmoothScroller
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import dagger.android.support.DaggerFragment import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.databinding.ObjectivesFragmentBinding
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.databinding.ObjectivesItemBinding
import info.nightscout.androidaps.plugins.constraints.objectives.activities.ObjectivesExamDialog
import info.nightscout.androidaps.dialogs.NtpProgressDialog import info.nightscout.androidaps.dialogs.NtpProgressDialog
import info.nightscout.androidaps.events.EventNtpStatus 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.events.EventObjectivesUpdateGui
import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective.ExamTask import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective.ExamTask
import info.nightscout.androidaps.receivers.ReceiverStatusStore 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.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.SntpClient 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.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.objectives_fragment.*
import javax.inject.Inject import javax.inject.Inject
class ObjectivesFragment : DaggerFragment() { class ObjectivesFragment : DaggerFragment() {
@Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var sp: SP @Inject lateinit var sp: SP
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var fabricPrivacy: FabricPrivacy
@ -51,6 +53,7 @@ class ObjectivesFragment : DaggerFragment() {
@Inject lateinit var receiverStatusStore: ReceiverStatusStore @Inject lateinit var receiverStatusStore: ReceiverStatusStore
@Inject lateinit var dateUtil: DateUtil @Inject lateinit var dateUtil: DateUtil
@Inject lateinit var sntpClient: SntpClient @Inject lateinit var sntpClient: SntpClient
@Inject lateinit var uel: UserEntryLogger
private val objectivesAdapter = ObjectivesAdapter() private val objectivesAdapter = ObjectivesAdapter()
private val handler = Handler(Looper.getMainLooper()) private val handler = Handler(Looper.getMainLooper())
@ -64,19 +67,23 @@ class ObjectivesFragment : DaggerFragment() {
} }
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, private var _binding: ObjectivesFragmentBinding? = null
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.objectives_fragment, container, false) // 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
objectives_recyclerview.layoutManager = LinearLayoutManager(view.context) binding.recyclerview.layoutManager = LinearLayoutManager(view.context)
objectives_recyclerview.adapter = objectivesAdapter binding.recyclerview.adapter = objectivesAdapter
objectives_fake.setOnClickListener { updateGUI() } binding.fake.setOnClickListener { updateGUI() }
objectives_reset.setOnClickListener { binding.reset.setOnClickListener {
objectivesPlugin.reset() objectivesPlugin.reset()
objectives_recyclerview.adapter?.notifyDataSetChanged() binding.recyclerview.adapter?.notifyDataSetChanged()
scrollToCurrentObjective() scrollToCurrentObjective()
} }
scrollToCurrentObjective() scrollToCurrentObjective()
@ -88,11 +95,10 @@ class ObjectivesFragment : DaggerFragment() {
super.onResume() super.onResume()
disposable += rxBus disposable += rxBus
.toObservable(EventObjectivesUpdateGui::class.java) .toObservable(EventObjectivesUpdateGui::class.java)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(aapsSchedulers.main)
.subscribe({ .subscribe({
objectives_recyclerview.adapter?.notifyDataSetChanged() binding.recyclerview.adapter?.notifyDataSetChanged()
}, { fabricPrivacy.logException(it) } }, fabricPrivacy::logException)
)
} }
@Synchronized @Synchronized
@ -105,6 +111,7 @@ class ObjectivesFragment : DaggerFragment() {
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
handler.removeCallbacks(objectiveUpdater) handler.removeCallbacks(objectiveUpdater)
_binding = null
} }
private fun startUpdateTimer() { private fun startUpdateTimer() {
@ -129,7 +136,7 @@ class ObjectivesFragment : DaggerFragment() {
override fun calculateTimeForScrolling(dx: Int): Int = super.calculateTimeForScrolling(dx) * 4 override fun calculateTimeForScrolling(dx: Int): Int = super.calculateTimeForScrolling(dx) * 4
} }
smoothScroller.targetPosition = i smoothScroller.targetPosition = i
objectives_recyclerview.layoutManager?.startSmoothScroll(smoothScroller) binding.recyclerview.layoutManager?.startSmoothScroll(smoothScroller)
} }
break break
} }
@ -145,67 +152,66 @@ class ObjectivesFragment : DaggerFragment() {
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val objective = objectivesPlugin.objectives[position] 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) { if (objective.objective != 0) {
holder.objective.visibility = View.VISIBLE holder.binding.objective.visibility = View.VISIBLE
holder.objective.text = resourceHelper.gs(objective.objective) holder.binding.objective.text = resourceHelper.gs(objective.objective)
} else } else
holder.objective.visibility = View.GONE holder.binding.objective.visibility = View.GONE
if (objective.gate != 0) { if (objective.gate != 0) {
holder.gate.visibility = View.VISIBLE holder.binding.gate.visibility = View.VISIBLE
holder.gate.text = resourceHelper.gs(objective.gate) holder.binding.gate.text = resourceHelper.gs(objective.gate)
} else } else
holder.gate.visibility = View.GONE holder.binding.gate.visibility = View.GONE
if (!objective.isStarted) { if (!objective.isStarted) {
holder.gate.setTextColor(-0x1) holder.binding.gate.setTextColor(-0x1)
holder.verify.visibility = View.GONE holder.binding.verify.visibility = View.GONE
holder.progress.visibility = View.GONE holder.binding.progress.visibility = View.GONE
holder.accomplished.visibility = View.GONE holder.binding.accomplished.visibility = View.GONE
holder.unFinish.visibility = View.GONE holder.binding.unfinish.visibility = View.GONE
holder.unStart.visibility = View.GONE holder.binding.unstart.visibility = View.GONE
if (position == 0 || objectivesPlugin.allPriorAccomplished(position)) if (position == 0 || objectivesPlugin.allPriorAccomplished(position))
holder.start.visibility = View.VISIBLE holder.binding.start.visibility = View.VISIBLE
else else
holder.start.visibility = View.GONE holder.binding.start.visibility = View.GONE
} else if (objective.isAccomplished) { } else if (objective.isAccomplished) {
holder.gate.setTextColor(-0xb350b0) holder.binding.gate.setTextColor(-0xb350b0)
holder.verify.visibility = View.GONE holder.binding.verify.visibility = View.GONE
holder.progress.visibility = View.GONE holder.binding.progress.visibility = View.GONE
holder.start.visibility = View.GONE holder.binding.start.visibility = View.GONE
holder.accomplished.visibility = View.VISIBLE holder.binding.accomplished.visibility = View.VISIBLE
holder.unFinish.visibility = View.VISIBLE holder.binding.unfinish.visibility = View.VISIBLE
holder.unStart.visibility = View.GONE holder.binding.unstart.visibility = View.GONE
} else if (objective.isStarted) { } else if (objective.isStarted) {
holder.gate.setTextColor(-0x1) holder.binding.gate.setTextColor(-0x1)
holder.verify.visibility = View.VISIBLE holder.binding.verify.visibility = View.VISIBLE
holder.verify.isEnabled = objective.isCompleted || objectives_fake.isChecked holder.binding.verify.isEnabled = objective.isCompleted || binding.fake.isChecked
holder.start.visibility = View.GONE holder.binding.start.visibility = View.GONE
holder.accomplished.visibility = View.GONE holder.binding.accomplished.visibility = View.GONE
holder.unFinish.visibility = View.GONE holder.binding.unfinish.visibility = View.GONE
holder.unStart.visibility = View.VISIBLE holder.binding.unstart.visibility = View.VISIBLE
holder.progress.visibility = View.VISIBLE holder.binding.progress.visibility = View.VISIBLE
holder.progress.removeAllViews() holder.binding.progress.removeAllViews()
for (task in objective.tasks) { for (task in objective.tasks) {
if (task.shouldBeIgnored()) continue if (task.shouldBeIgnored()) continue
// name // name
val name = TextView(holder.progress.context) val name = TextView(holder.binding.progress.context)
@Suppress("SetTextlI8n") name.text = "${resourceHelper.gs(task.task)}:"
name.text = resourceHelper.gs(task.task) + ":"
name.setTextColor(-0x1) 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 // hint
task.hints.forEach { h -> task.hints.forEach { h ->
if (!task.isCompleted) if (!task.isCompleted())
holder.progress.addView(h.generate(context)) context?.let { holder.binding.progress.addView(h.generate(it)) }
} }
// state // state
val state = TextView(holder.progress.context) val state = TextView(holder.binding.progress.context)
state.setTextColor(-0x1) state.setTextColor(-0x1)
val basicHTML = "<font color=\"%1\$s\"><b>%2\$s</b></font>" val basicHTML = "<font color=\"%1\$s\"><b>%2\$s</b></font>"
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.text = HtmlHelper.fromHtml(formattedHTML)
state.gravity = Gravity.END 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) { if (task is ExamTask) {
state.setOnClickListener { state.setOnClickListener {
val dialog = ObjectivesExamDialog() val dialog = ObjectivesExamDialog()
@ -218,16 +224,16 @@ class ObjectivesFragment : DaggerFragment() {
} }
} }
// horizontal line // horizontal line
val separator = View(holder.progress.context) val separator = View(holder.binding.progress.context)
separator.setBackgroundColor(Color.DKGRAY) 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.binding.accomplished.text = resourceHelper.gs(R.string.accomplished, dateUtil.dateAndTimeString(objective.accomplishedOn))
holder.accomplished.setTextColor(-0x3e3e3f) holder.binding.accomplished.setTextColor(-0x3e3e3f)
holder.verify.setOnClickListener { holder.binding.verify.setOnClickListener {
receiverStatusStore.updateNetworkStatus() receiverStatusStore.updateNetworkStatus()
if (objectives_fake.isChecked) { if (binding.fake.isChecked) {
objective.accomplishedOn = DateUtil.now() objective.accomplishedOn = DateUtil.now()
scrollToCurrentObjective() scrollToCurrentObjective()
startUpdateTimer() startUpdateTimer()
@ -264,9 +270,9 @@ class ObjectivesFragment : DaggerFragment() {
}.start() }.start()
} }
} }
holder.start.setOnClickListener { holder.binding.start.setOnClickListener {
receiverStatusStore.updateNetworkStatus() receiverStatusStore.updateNetworkStatus()
if (objectives_fake.isChecked) { if (binding.fake.isChecked) {
objective.startedOn = DateUtil.now() objective.startedOn = DateUtil.now()
scrollToCurrentObjective() scrollToCurrentObjective()
startUpdateTimer() startUpdateTimer()
@ -298,9 +304,10 @@ class ObjectivesFragment : DaggerFragment() {
}, receiverStatusStore.isConnected) }, receiverStatusStore.isConnected)
}.start() }.start()
} }
holder.unStart.setOnClickListener { holder.binding.unstart.setOnClickListener {
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.objectives), resourceHelper.gs(R.string.doyouwantresetstart), Runnable { OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.objectives), resourceHelper.gs(R.string.doyouwantresetstart), Runnable {
uel.log("OBJECTIVE UNSTARTED", i1 = position + 1)
objective.startedOn = 0 objective.startedOn = 0
scrollToCurrentObjective() scrollToCurrentObjective()
rxBus.send(EventObjectivesUpdateGui()) rxBus.send(EventObjectivesUpdateGui())
@ -308,7 +315,7 @@ class ObjectivesFragment : DaggerFragment() {
}) })
} }
} }
holder.unFinish.setOnClickListener { holder.binding.unfinish.setOnClickListener {
objective.accomplishedOn = 0 objective.accomplishedOn = 0
scrollToCurrentObjective() scrollToCurrentObjective()
rxBus.send(EventObjectivesUpdateGui()) rxBus.send(EventObjectivesUpdateGui())
@ -318,21 +325,21 @@ class ObjectivesFragment : DaggerFragment() {
// generate random request code if none exists // 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())) 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) sp.putString(R.string.key_objectives_request_code, request)
holder.requestCode.text = resourceHelper.gs(R.string.requestcode, request) holder.binding.requestcode.text = resourceHelper.gs(R.string.requestcode, request)
holder.requestCode.visibility = View.VISIBLE holder.binding.requestcode.visibility = View.VISIBLE
holder.enterButton.visibility = View.VISIBLE holder.binding.enterbutton.visibility = View.VISIBLE
holder.input.visibility = View.VISIBLE holder.binding.input.visibility = View.VISIBLE
holder.inputHint.visibility = View.VISIBLE holder.binding.inputhint.visibility = View.VISIBLE
holder.enterButton.setOnClickListener { holder.binding.enterbutton.setOnClickListener {
val input = holder.input.text.toString() val input = holder.binding.input.text.toString()
objective.specialAction(activity, input) activity?.let { activity -> objective.specialAction(activity, input) }
rxBus.send(EventObjectivesUpdateGui()) rxBus.send(EventObjectivesUpdateGui())
} }
} else { } else {
holder.enterButton.visibility = View.GONE holder.binding.enterbutton.visibility = View.GONE
holder.input.visibility = View.GONE holder.binding.input.visibility = View.GONE
holder.inputHint.visibility = View.GONE holder.binding.inputhint.visibility = View.GONE
holder.requestCode.visibility = View.GONE holder.binding.requestcode.visibility = View.GONE
} }
} }
@ -340,20 +347,9 @@ class ObjectivesFragment : DaggerFragment() {
return objectivesPlugin.objectives.size return objectivesPlugin.objectives.size
} }
inner class ViewHolder internal constructor(itemView: View) : RecyclerView.ViewHolder(itemView) { inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val title: TextView = itemView.findViewById(R.id.objective_title)
val objective: TextView = itemView.findViewById(R.id.objective_objective) val binding = ObjectivesItemBinding.bind(itemView)
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)
} }
} }

View file

@ -9,6 +9,7 @@ import info.nightscout.androidaps.Config
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.* import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.constraints.objectives.objectives.* import info.nightscout.androidaps.plugins.constraints.objectives.objectives.*
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.alertDialogs.OKDialog import info.nightscout.androidaps.utils.alertDialogs.OKDialog
@ -25,8 +26,8 @@ class ObjectivesPlugin @Inject constructor(
resourceHelper: ResourceHelper, resourceHelper: ResourceHelper,
private val activePlugin: ActivePluginProvider, private val activePlugin: ActivePluginProvider,
private val sp: SP, private val sp: SP,
private val config: Config config: Config,
private val uel: UserEntryLogger
) : PluginBase(PluginDescription() ) : PluginBase(PluginDescription()
.mainType(PluginType.CONSTRAINTS) .mainType(PluginType.CONSTRAINTS)
.fragmentClass(ObjectivesFragment::class.qualifiedName) .fragmentClass(ObjectivesFragment::class.qualifiedName)
@ -141,6 +142,7 @@ class ObjectivesPlugin @Inject constructor(
sp.putLong("Objectives_" + "auto" + "_accomplished", DateUtil.now()) sp.putLong("Objectives_" + "auto" + "_accomplished", DateUtil.now())
setupObjectives() setupObjectives()
OKDialog.show(activity, resourceHelper.gs(R.string.objectives), resourceHelper.gs(R.string.codeaccepted)) OKDialog.show(activity, resourceHelper.gs(R.string.objectives), resourceHelper.gs(R.string.codeaccepted))
uel.log("OBJECTIVES SKIPPED")
} else { } else {
OKDialog.show(activity, resourceHelper.gs(R.string.objectives), resourceHelper.gs(R.string.codeinvalid)) OKDialog.show(activity, resourceHelper.gs(R.string.objectives), resourceHelper.gs(R.string.codeinvalid))
} }

View file

@ -6,6 +6,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import dagger.android.support.DaggerDialogFragment import dagger.android.support.DaggerDialogFragment
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.databinding.ObjectivesExamFragmentBinding
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.constraints.objectives.events.EventObjectivesUpdateGui import info.nightscout.androidaps.plugins.constraints.objectives.events.EventObjectivesUpdateGui
import info.nightscout.androidaps.plugins.constraints.objectives.objectives.Objective 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.T
import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import kotlinx.android.synthetic.main.objectives_exam_fragment.*
import javax.inject.Inject import javax.inject.Inject
class ObjectivesExamDialog : DaggerDialogFragment() { class ObjectivesExamDialog : DaggerDialogFragment() {
@Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var dateUtil: DateUtil @Inject lateinit var dateUtil: DateUtil
companion object { companion object {
var objective: Objective? = null var objective: Objective? = null
} }
private var currentTask = 0 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?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? { savedInstanceState: Bundle?): View {
// load data from bundle // load data from bundle
(savedInstanceState ?: arguments)?.let { bundle -> (savedInstanceState ?: arguments)?.let { bundle ->
currentTask = bundle.getInt("currentTask", 0) 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() { override fun onStart() {
@ -55,36 +64,46 @@ class ObjectivesExamDialog : DaggerDialogFragment() {
bundle.putInt("currentTask", currentTask) bundle.putInt("currentTask", currentTask)
} }
@Synchronized
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
@Synchronized
fun updateGui() { fun updateGui() {
if (_binding == null) return
objective?.let { objective -> objective?.let { objective ->
val task: ExamTask = objective.tasks[currentTask] as ExamTask val task: ExamTask = objective.tasks[currentTask] as ExamTask
objectives_exam_name.setText(task.task) binding.examName.setText(task.task)
objectives_exam_question.setText(task.question) binding.examQuestion.setText(task.question)
// Options // Options
objectives_exam_options.removeAllViews() binding.examOptions.removeAllViews()
task.options.forEach { task.options.forEach {
val cb = it.generate(context) context?.let { context ->
if (task.answered) { val cb = it.generate(context)
cb.isEnabled = false if (task.answered) {
if (it.isCorrect) cb.isEnabled = false
cb.isChecked = true if (it.isCorrect)
cb.isChecked = true
}
binding.examOptions.addView(cb)
} }
objectives_exam_options.addView(cb)
} }
// Hints // Hints
objectives_exam_hints.removeAllViews() binding.examHints.removeAllViews()
for (h in task.hints) { for (h in task.hints) {
objectives_exam_hints.addView(h.generate(context)) context?.let { binding.examHints.addView(h.generate(it)) }
} }
// Disabled to // Disabled to
objectives_exam_disabledto.text = resourceHelper.gs(R.string.answerdisabledto, dateUtil.timeString(task.disabledTo)) binding.examDisabledto.text = resourceHelper.gs(R.string.answerdisabledto, dateUtil.timeString(task.disabledTo))
objectives_exam_disabledto.visibility = if (task.isEnabledAnswer) View.GONE else View.VISIBLE binding.examDisabledto.visibility = if (task.isEnabledAnswer()) View.GONE else View.VISIBLE
// Buttons // Buttons
objectives_exam_verify.isEnabled = !task.answered && task.isEnabledAnswer binding.examVerify.isEnabled = !task.answered && task.isEnabledAnswer()
objectives_exam_verify.setOnClickListener { binding.examVerify.setOnClickListener {
var result = true var result = true
for (o in task.options) { for (o in task.options) {
val option: Option = o as Option val option: Option = o
result = result && option.evaluate() result = result && option.evaluate()
} }
task.answered = result task.answered = result
@ -95,35 +114,35 @@ class ObjectivesExamDialog : DaggerDialogFragment() {
updateGui() updateGui()
rxBus.send(EventObjectivesUpdateGui()) rxBus.send(EventObjectivesUpdateGui())
} }
close.setOnClickListener { dismiss() } binding.close.setOnClickListener { dismiss() }
objectives_exam_reset.setOnClickListener { binding.examReset.setOnClickListener {
task.answered = false task.answered = false
//task.disabledTo = 0 //task.disabledTo = 0
updateGui() updateGui()
rxBus.send(EventObjectivesUpdateGui()) rxBus.send(EventObjectivesUpdateGui())
} }
objectives_back_button.isEnabled = currentTask != 0 binding.backButton.isEnabled = currentTask != 0
objectives_back_button.setOnClickListener { binding.backButton.setOnClickListener {
currentTask-- currentTask--
updateGui() updateGui()
} }
objectives_next_button.isEnabled = currentTask != objective.tasks.size - 1 binding.nextButton.isEnabled = currentTask != objective.tasks.size - 1
objectives_next_button.setOnClickListener { binding.nextButton.setOnClickListener {
currentTask++ currentTask++
updateGui() updateGui()
} }
objectives_next_unanswered_button.isEnabled = !objective.isCompleted binding.nextUnansweredButton.isEnabled = !objective.isCompleted
objectives_next_unanswered_button.setOnClickListener { binding.nextUnansweredButton.setOnClickListener {
for (i in (currentTask + 1) until objective.tasks.size) { for (i in (currentTask + 1) until objective.tasks.size) {
if (!objective.tasks[i].isCompleted) { if (!objective.tasks[i].isCompleted()) {
currentTask = i currentTask = i
updateGui() updateGui()
return@setOnClickListener return@setOnClickListener
} }
} }
for (i in 0..currentTask) { for (i in 0..currentTask) {
if (!objective.tasks[i].isCompleted) { if (!objective.tasks[i].isCompleted()) {
currentTask = i currentTask = i
updateGui() updateGui()
return@setOnClickListener return@setOnClickListener

View file

@ -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<Task> 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<Task> tasks) {
}
public List<Task> 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<Hint> 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<Hint> 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<Option> options = new ArrayList<>();
private final String spIdentifier;
private boolean answered;
private long disabledTo;
ExamTask(@StringRes int task, @StringRes int question, String spIdentifier) {
super(task);
this.question = question;
this.spIdentifier = spIdentifier;
answered = sp.getBoolean("ExamTask_" + spIdentifier, false);
disabledTo = sp.getLong("DisabledTo_" + spIdentifier, 0L);
}
public void setDisabledTo(long newState) {
disabledTo = newState;
sp.putLong("DisabledTo_" + spIdentifier, disabledTo);
}
public long getDisabledTo() {
return disabledTo;
}
public boolean isEnabledAnswer() {
return disabledTo < DateUtil.now();
}
public void setAnswered(boolean newState) {
answered = newState;
sp.putBoolean("ExamTask_" + spIdentifier, answered);
}
public boolean getAnswered() {
return answered;
}
ExamTask option(Option option) {
options.add(option);
return this;
}
public @StringRes int getQuestion() {
return question;
}
public List<Objective.Option> getOptions() {
return options;
}
@Override
public boolean isCompleted() {
return answered;
}
}
public class Option {
@StringRes int option;
boolean isCorrect;
CheckBox cb; // TODO: change it, this will block releasing memeory
Option(@StringRes int option, boolean isCorrect) {
this.option = option;
this.isCorrect = isCorrect;
}
public boolean isCorrect() {
return isCorrect;
}
public CheckBox generate(Context context) {
cb = new CheckBox(context);
cb.setText(option);
return cb;
}
public boolean evaluate() {
boolean selection = cb.isChecked();
if (selection && isCorrect) return true;
return !selection && !isCorrect;
}
}
public class Hint {
@StringRes int hint;
Hint(@StringRes int hint) {
this.hint = hint;
}
public TextView generate(Context context) {
TextView textView = new TextView(context);
textView.setText(hint);
textView.setAutoLinkMask(Linkify.WEB_URLS);
textView.setLinksClickable(true);
textView.setLinkTextColor(Color.YELLOW);
Linkify.addLinks(textView, Linkify.WEB_URLS);
return textView;
}
}
}

View file

@ -0,0 +1,183 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
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 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
import java.util.*
import javax.inject.Inject
import kotlin.math.floor
abstract class Objective(injector: HasAndroidInjector, spName: String, @StringRes objective: Int, @StringRes gate: Int) {
@Inject lateinit var sp: SP
@Inject lateinit var resourceHelper: ResourceHelper
private val spName: String
@StringRes val objective: Int
@StringRes val gate: Int
var startedOn: Long = 0
set(value) {
field = value
sp.putLong("Objectives_" + spName + "_started", startedOn)
}
var accomplishedOn: Long = 0
set(value) {
field = value
sp.putLong("Objectives_" + spName + "_accomplished", value)
}
var tasks: MutableList<Task> = ArrayList()
var hasSpecialInput = false
val isCompleted: Boolean
get() {
for (task in tasks) {
if (!task.shouldBeIgnored() && !task.isCompleted()) return false
}
return true
}
init {
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
}
}
fun isCompleted(trueTime: Long): Boolean {
for (task in tasks) {
if (!task.shouldBeIgnored() && !task.isCompleted(trueTime)) return false
}
return true
}
val isAccomplished: Boolean
get() = accomplishedOn != 0L && accomplishedOn < DateUtil.now()
val isStarted: Boolean
get() = startedOn != 0L
open fun specialActionEnabled(): Boolean {
return true
}
open fun specialAction(activity: FragmentActivity, input: String) {}
abstract inner class Task(var objective: Objective, @StringRes val task: Int) {
var hints = ArrayList<Hint>()
abstract fun isCompleted(): Boolean
open fun isCompleted(trueTime: Long): Boolean = isCompleted
open val progress: String
get() = resourceHelper.gs(if (isCompleted) R.string.completed_well_done else R.string.not_completed_yet)
fun hint(hint: Hint): Task {
hints.add(hint)
return this
}
open fun shouldBeIgnored(): Boolean = false
}
inner class MinimumDurationTask internal constructor(objective: Objective, private val minimumDuration: Long) : Task(objective, R.string.time_elapsed) {
override fun isCompleted(): Boolean =
objective.isStarted && System.currentTimeMillis() - objective.startedOn >= minimumDuration
override fun isCompleted(trueTime: Long): Boolean {
return objective.isStarted && trueTime - objective.startedOn >= minimumDuration
}
override val progress: String
get() = (getDurationText(System.currentTimeMillis() - objective.startedOn)
+ " / " + getDurationText(minimumDuration))
private fun getDurationText(duration: Long): String {
val days = floor(duration.toDouble() / T.days(1).msecs()).toInt()
val hours = floor(duration.toDouble() / T.hours(1).msecs()).toInt()
val minutes = floor(duration.toDouble() / T.mins(1).msecs()).toInt()
return when {
days > 0 -> resourceHelper.gq(R.plurals.days, days, days)
hours > 0 -> resourceHelper.gq(R.plurals.hours, hours, hours)
else -> resourceHelper.gq(R.plurals.minutes, minutes, minutes)
}
}
}
inner class ExamTask internal constructor(objective: Objective, @StringRes task: Int, @StringRes val question: Int, private val spIdentifier: String) : Task(objective, task) {
var options = ArrayList<Option>()
var answered: Boolean = false
set(value) {
field = value
sp.putBoolean("ExamTask_$spIdentifier", value)
}
var disabledTo: Long = 0
set(value) {
field = value
sp.putLong("DisabledTo_$spIdentifier", value)
}
init {
answered = sp.getBoolean("ExamTask_$spIdentifier", false)
disabledTo = sp.getLong("DisabledTo_$spIdentifier", 0L)
}
override fun isCompleted(): Boolean = answered
fun isEnabledAnswer(): Boolean = disabledTo < DateUtil.now()
fun option(option: Option): ExamTask {
options.add(option)
return this
}
}
inner class Option internal constructor(@StringRes var option: Int, var isCorrect: Boolean) {
var cb: CheckBox? = null // TODO: change it, this will block releasing memory
fun generate(context: Context): CheckBox {
cb = CheckBox(context)
cb?.setText(option)
return cb!!
}
fun evaluate(): Boolean {
val selection = cb!!.isChecked
return if (selection && isCorrect) true else !selection && !isCorrect
}
}
inner class Hint internal constructor(@StringRes var hint: Int) {
fun generate(context: Context): TextView {
val textView = TextView(context)
textView.setText(hint)
textView.autoLinkMask = Linkify.WEB_URLS
textView.linksClickable = true
textView.setLinkTextColor(Color.YELLOW)
Linkify.addLinks(textView, Linkify.WEB_URLS)
return textView
}
}
}

View file

@ -1,91 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import java.util.List;
import javax.inject.Inject;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.interfaces.APSInterface;
import info.nightscout.androidaps.interfaces.ActivePluginProvider;
import info.nightscout.androidaps.interfaces.PluginBase;
import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin;
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin;
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
public class Objective0 extends Objective {
@Inject SP sp;
@Inject ActivePluginProvider activePlugin;
@Inject VirtualPumpPlugin virtualPumpPlugin;
@Inject TreatmentsPlugin treatmentsPlugin;
@Inject LoopPlugin loopPlugin;
@Inject NSClientPlugin nsClientPlugin;
@Inject IobCobCalculatorPlugin iobCobCalculatorPlugin;
public Objective0(HasAndroidInjector injector) {
super(injector, "config", R.string.objectives_0_objective, R.string.objectives_0_gate);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new Task(R.string.objectives_bgavailableinns) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_ObjectivesbgIsAvailableInNS, false);
}
});
tasks.add(new Task(R.string.nsclienthaswritepermission) {
@Override
public boolean isCompleted() {
return nsClientPlugin.hasWritePermission();
}
});
tasks.add(new Task(R.string.virtualpump_uploadstatus_title) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_virtualpump_uploadstatus, false);
}
@Override
public boolean shouldBeIgnored() {
return !virtualPumpPlugin.isEnabled(PluginType.PUMP);
}
});
tasks.add(new Task(R.string.objectives_pumpstatusavailableinns) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_ObjectivespumpStatusIsAvailableInNS, false);
}
});
tasks.add(new Task(R.string.hasbgdata) {
@Override
public boolean isCompleted() {
return iobCobCalculatorPlugin.lastBg() != null;
}
});
tasks.add(new Task(R.string.loopenabled) {
@Override
public boolean isCompleted() {
return loopPlugin.isEnabled(PluginType.LOOP);
}
});
tasks.add(new Task(R.string.apsselected) {
@Override
public boolean isCompleted() {
APSInterface usedAPS = activePlugin.getActiveAPS();
return ((PluginBase) usedAPS).isEnabled(PluginType.APS);
}
});
tasks.add(new Task(R.string.activate_profile) {
@Override
public boolean isCompleted() {
return treatmentsPlugin.getProfileSwitchFromHistory(DateUtil.now()) != null;
}
});
}
}

View file

@ -0,0 +1,72 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.DateUtil
import javax.inject.Inject
class Objective0(injector: HasAndroidInjector) : Objective(injector, "config", R.string.objectives_0_objective, R.string.objectives_0_gate) {
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var virtualPumpPlugin: VirtualPumpPlugin
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var loopPlugin: LoopPlugin
@Inject lateinit var nsClientPlugin: NSClientPlugin
@Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin
init {
tasks.add(object : Task(this, R.string.objectives_bgavailableinns) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_ObjectivesbgIsAvailableInNS, false)
}
})
tasks.add(object : Task(this, R.string.nsclienthaswritepermission) {
override fun isCompleted(): Boolean {
return nsClientPlugin.hasWritePermission()
}
})
tasks.add(object : Task(this, R.string.virtualpump_uploadstatus_title) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_virtualpump_uploadstatus, false)
}
override fun shouldBeIgnored(): Boolean {
return !virtualPumpPlugin.isEnabled(PluginType.PUMP)
}
})
tasks.add(object : Task(this, R.string.objectives_pumpstatusavailableinns) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_ObjectivespumpStatusIsAvailableInNS, false)
}
})
tasks.add(object : Task(this, R.string.hasbgdata) {
override fun isCompleted(): Boolean {
return iobCobCalculatorPlugin.lastBg() != null
}
})
tasks.add(object : Task(this, R.string.loopenabled) {
override fun isCompleted(): Boolean {
return loopPlugin.isEnabled(PluginType.LOOP)
}
})
tasks.add(object : Task(this, R.string.apsselected) {
override fun isCompleted(): Boolean {
val usedAPS = activePlugin.activeAPS
return (usedAPS as PluginBase).isEnabled(PluginType.APS)
}
})
tasks.add(object : Task(this, R.string.activate_profile) {
override fun isCompleted(): Boolean {
return treatmentsPlugin.getProfileSwitchFromHistory(DateUtil.now()) != null
}
})
}
}

View file

@ -1,67 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import java.util.List;
import javax.inject.Inject;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.plugins.general.actions.ActionsPlugin;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
public class Objective1 extends Objective {
@Inject SP sp;
@Inject ActionsPlugin actionsPlugin;
@Inject
public Objective1(HasAndroidInjector injector) {
super(injector, "usage", R.string.objectives_usage_objective, R.string.objectives_usage_gate);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new Task(R.string.objectives_useprofileswitch) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_objectiveuseprofileswitch, false);
}
});
tasks.add(new Task(R.string.objectives_usedisconnectpump) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_objectiveusedisconnect, false);
}
}.hint(new Hint(R.string.disconnectpump_hint)));
tasks.add(new Task(R.string.objectives_usereconnectpump) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_objectiveusereconnect, false);
}
}.hint(new Hint(R.string.disconnectpump_hint)));
tasks.add(new Task(R.string.objectives_usetemptarget) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_objectiveusetemptarget, false);
}
}.hint(new Hint(R.string.usetemptarget_hint)));
tasks.add(new Task(R.string.objectives_useactions) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_objectiveuseactions, false) && actionsPlugin.isEnabled(PluginType.GENERAL) && actionsPlugin.isFragmentVisible();
}
}.hint(new Hint(R.string.useaction_hint)));
tasks.add(new Task(R.string.objectives_useloop) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_objectiveuseloop, false);
}
}.hint(new Hint(R.string.useaction_hint)));
tasks.add(new Task(R.string.objectives_usescale) {
@Override
public boolean isCompleted() {
return sp.getBoolean(R.string.key_objectiveusescale, false);
}
}.hint(new Hint(R.string.usescale_hint)));
}
}

View file

@ -0,0 +1,50 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.plugins.general.actions.ActionsPlugin
import javax.inject.Inject
class Objective1 @Inject constructor(injector: HasAndroidInjector) : Objective(injector, "usage", R.string.objectives_usage_objective, R.string.objectives_usage_gate) {
@Inject lateinit var actionsPlugin: ActionsPlugin
init {
tasks.add(object : Task(this, R.string.objectives_useprofileswitch) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_objectiveuseprofileswitch, false)
}
})
tasks.add(object : Task(this, R.string.objectives_usedisconnectpump) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_objectiveusedisconnect, false)
}
}.hint(Hint(R.string.disconnectpump_hint)))
tasks.add(object : Task(this, R.string.objectives_usereconnectpump) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_objectiveusereconnect, false)
}
}.hint(Hint(R.string.disconnectpump_hint)))
tasks.add(object : Task(this, R.string.objectives_usetemptarget) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_objectiveusetemptarget, false)
}
}.hint(Hint(R.string.usetemptarget_hint)))
tasks.add(object : Task(this, R.string.objectives_useactions) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_objectiveuseactions, false) && actionsPlugin.isEnabled(PluginType.GENERAL) && actionsPlugin.isFragmentVisible()
}
}.hint(Hint(R.string.useaction_hint)))
tasks.add(object : Task(this, R.string.objectives_useloop) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_objectiveuseloop, false)
}
}.hint(Hint(R.string.useaction_hint)))
tasks.add(object : Task(this, R.string.objectives_usescale) {
override fun isCompleted(): Boolean {
return sp.getBoolean(R.string.key_objectiveusescale, false)
}
}.hint(Hint(R.string.usescale_hint)))
}
}

View file

@ -1,19 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import java.util.List;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.utils.T;
public class Objective10 extends Objective {
public Objective10(HasAndroidInjector injector) {
super(injector, "auto", R.string.objectives_auto_objective, R.string.objectives_auto_gate);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new MinimumDurationTask(T.days(28).msecs()));
}
}

View file

@ -0,0 +1,12 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.utils.T
class Objective10(injector: HasAndroidInjector) : Objective(injector, "auto", R.string.objectives_auto_objective, R.string.objectives_auto_gate) {
init {
tasks.add(MinimumDurationTask(this, T.days(28).msecs()))
}
}

View file

@ -1,211 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import java.util.Collections;
import java.util.List;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
public class Objective2 extends Objective {
public Objective2(HasAndroidInjector injector) {
super(injector, "exam", R.string.objectives_exam_objective, R.string.objectives_exam_gate);
for (Task task : tasks) {
if (!task.isCompleted()) setAccomplishedOn(0);
}
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new ExamTask(R.string.dia_label_exam, R.string.dia_whatmeansdia,"dia")
.option(new Option(R.string.dia_minimumis3h, false))
.option(new Option(R.string.dia_minimumis5h, true))
.option(new Option(R.string.dia_meaningisequaltodiapump, false))
.option(new Option(R.string.dia_valuemustbedetermined, true))
.hint(new Hint(R.string.dia_hint1))
);
tasks.add(new ExamTask(R.string.hypott_label, R.string.hypott_whenhypott,"hypott")
.option(new Option(R.string.hypott_goinglow, false))
.option(new Option(R.string.hypott_preventoversmb, true))
.hint(new Hint(R.string.hypott_hint1))
);
tasks.add(new ExamTask(R.string.offlineprofile_label, R.string.offlineprofile_whatprofile,"offlineprofile")
.option(new Option(R.string.localprofile, true))
.option(new Option(R.string.nsprofile, false))
.option(new Option(R.string.offlineprofile_nsprofile, true))
.hint(new Hint(R.string.offlineprofile_hint1))
);
tasks.add(new ExamTask(R.string.pumpdisconnect_label, R.string.pumpdisconnect_label,"pumpdisconnect")
.option(new Option(R.string.pumpdisconnect_letknow, true))
.option(new Option(R.string.pumpdisconnect_suspend, false))
.option(new Option(R.string.pumpdisconnect_dontchnage, false))
.hint(new Hint(R.string.pumpdisconnect_hint1))
);
tasks.add(new ExamTask(R.string.objectives_label, R.string.objectives_howtosave,"objectives")
.option(new Option(R.string.objectives_exportsettings, true))
.option(new Option(R.string.objectives_storeelsewhere, true))
.option(new Option(R.string.objectives_doexportonstart, false))
.option(new Option(R.string.objectives_doexportafterchange, true))
.option(new Option(R.string.objectives_doexportafterobjective, true))
.option(new Option(R.string.objectives_doexportafterfirtssettings, true))
.hint(new Hint(R.string.objectives_hint1))
.hint(new Hint(R.string.objectives_hint2))
);
tasks.add(new ExamTask(R.string.noisycgm_label, R.string.noisycgm_whattodo,"noisycgm")
.option(new Option(R.string.nothing, false))
.option(new Option(R.string.disconnectpumpfor1h, false))
.option(new Option(R.string.noisycgm_pause, true))
.option(new Option(R.string.noisycgm_replacesensor, true))
.option(new Option(R.string.noisycgm_turnoffphone, false))
.option(new Option(R.string.noisycgm_checksmoothing, true))
.hint(new Hint(R.string.noisycgm_hint1))
);
tasks.add(new ExamTask(R.string.exercise_label, R.string.exercise_whattodo,"exercise")
.option(new Option(R.string.nothing, false))
.option(new Option(R.string.exercise_setactivitytt, true))
.option(new Option(R.string.exercise_switchprofilebelow100, true))
.option(new Option(R.string.exercise_switchprofileabove100, false))
.option(new Option(R.string.exercise_stoploop, false))
.option(new Option(R.string.exercise_doitbeforestart, true))
.option(new Option(R.string.exercise_afterstart, true))
.hint(new Hint(R.string.exercise_hint1))
);
tasks.add(new ExamTask(R.string.suspendloop_label, R.string.suspendloop_doigetinsulin,"suspendloop")
.option(new Option(R.string.suspendloop_yes, true))
.option(new Option(R.string.suspendloop_no, false))
);
tasks.add(new ExamTask(R.string.basaltest_label, R.string.basaltest_when,"basaltest")
.option(new Option(R.string.basaltest_beforeloop, true))
.option(new Option(R.string.basaltest_havingregularhypo, true))
.option(new Option(R.string.basaltest_havingregularhyper, true))
.hint(new Hint(R.string.basaltest_hint1))
);
tasks.add(new ExamTask(R.string.basalhelp_label, R.string.basalhelp_where,"basalhelp")
.option(new Option(R.string.basalhelp_diabetesteam, true))
.option(new Option(R.string.basalhelp_google, false))
.option(new Option(R.string.basalhelp_facebook, false))
.hint(new Hint(R.string.basalhelp_hint1))
);
tasks.add(new ExamTask(R.string.prerequisites_label, R.string.prerequisites_what, "prerequisites")
.option(new Option(R.string.prerequisites_determinedcorrectprofile, true))
.option(new Option(R.string.prerequisites_computer, true))
.option(new Option(R.string.prerequisites_phone, true))
.option(new Option(R.string.prerequisites_car, false))
.option(new Option(R.string.prerequisites_nightscout, true))
.option(new Option(R.string.prerequisites_tidepoolaccount, false))
.option(new Option(R.string.prerequisites_googleaccount, false))
.option(new Option(R.string.prerequisites_githubaccount, false))
.option(new Option(R.string.prerequisites_beanandroiddeveloper, false))
.option(new Option(R.string.prerequisites_own670g, false))
.option(new Option(R.string.prerequisites_smartwatch, false))
.option(new Option(R.string.prerequisites_supportedcgm, true))
.hint(new Hint(R.string.prerequisites_hint1))
);
tasks.add(new ExamTask(R.string.update_label, R.string.whatistrue,"update")
.option(new Option(R.string.update_git, true))
.option(new Option(R.string.update_asap, true))
.option(new Option(R.string.update_keys, true))
.option(new Option(R.string.update_neverupdate, false))
.option(new Option(R.string.update_askfriend, false))
.hint(new Hint(R.string.update_hint1))
);
tasks.add(new ExamTask(R.string.troubleshooting_label, R.string.troubleshooting_wheretoask,"troubleshooting")
.option(new Option(R.string.troubleshooting_fb, true))
.option(new Option(R.string.troubleshooting_wiki, true))
.option(new Option(R.string.troubleshooting_gitter, true))
.option(new Option(R.string.troubleshooting_googlesupport, false))
.option(new Option(R.string.troubleshooting_yourendo, false))
.hint(new Hint(R.string.troubleshooting_hint1))
.hint(new Hint(R.string.troubleshooting_hint2))
.hint(new Hint(R.string.troubleshooting_hint3))
);
tasks.add(new ExamTask(R.string.insulin_label, R.string.insulin_ultrarapid,"insulin")
.option(new Option(R.string.insulin_fiasp, true))
.option(new Option(R.string.insulin_novorapid, false))
.option(new Option(R.string.insulin_humalog, false))
.option(new Option(R.string.insulin_actrapid, false))
.hint(new Hint(R.string.insulin_hint1))
);
tasks.add(new ExamTask(R.string.sensitivity_label, R.string.sensitivity_which,"sensitivity")
.option(new Option(R.string.sensitivityweightedaverage, true))
.option(new Option(R.string.sensitivityoref1, false))
.option(new Option(R.string.sensitivityaaps, true))
.hint(new Hint(R.string.sensitivity_hint1))
);
tasks.add(new ExamTask(R.string.sensitivity_label, R.string.sensitivityuam_which,"sensitivityuam")
.option(new Option(R.string.sensitivityweightedaverage, false))
.option(new Option(R.string.sensitivityoref1, true))
.option(new Option(R.string.sensitivityaaps, false))
.hint(new Hint(R.string.sensitivity_hint1))
);
tasks.add(new ExamTask(R.string.wrongcarbs_label, R.string.wrongcarbs_whattodo,"wrongcarbs")
.option(new Option(R.string.wrongcarbs_addfakeinsulin, false))
.option(new Option(R.string.wrongcarbs_treatmentstab, true))
);
tasks.add(new ExamTask(R.string.extendedcarbs_label, R.string.extendedcarbs_handling,"extendedcarbs")
.option(new Option(R.string.extendedcarbs_useextendedcarbs, true))
.option(new Option(R.string.extendedcarbs_add, false))
.option(new Option(R.string.extendedcarbs_useextendedbolus, false))
.hint(new Hint(R.string.extendedcarbs_hint1))
);
tasks.add(new ExamTask(R.string.nsclient_label, R.string.nsclient_howcanyou,"nsclient")
.option(new Option(R.string.nsclient_nightscout, true))
.option(new Option(R.string.nsclientinternal, true))
.option(new Option(R.string.nsclient_dexcomfollow, true))
.option(new Option(R.string.nsclient_dexcomfollowxdrip, false))
.option(new Option(R.string.nsclient_xdripfollower, true))
.option(new Option(R.string.nsclient_looponiphone, false))
.option(new Option(R.string.nsclient_spikeiphone, true))
.hint(new Hint(R.string.nsclient_hint1))
);
tasks.add(new ExamTask(R.string.isf_label_exam, R.string.whatistrue,"isf")
.option(new Option(R.string.isf_increasingvalue, true))
.option(new Option(R.string.isf_decreasingvalue, false))
.option(new Option(R.string.isf_noeffect, false))
.option(new Option(R.string.isf_preferences, false))
.option(new Option(R.string.isf_profile, false))
.hint(new Hint(R.string.isf_hint1))
.hint(new Hint(R.string.isf_hint2))
);
tasks.add(new ExamTask(R.string.ic_label_exam, R.string.whatistrue,"ic")
.option(new Option(R.string.ic_increasingvalue, true))
.option(new Option(R.string.ic_decreasingvalue, false))
.option(new Option(R.string.ic_noeffect, false))
.option(new Option(R.string.ic_different, false))
.option(new Option(R.string.ic_meaning, false))
.hint(new Hint(R.string.ic_hint1))
);
tasks.add(new ExamTask(R.string.profileswitch_label, R.string.profileswitch_pctwillchange,"profileswitch")
.option(new Option(R.string.profileswitch_basalhigher, false))
.option(new Option(R.string.profileswitch_basallower, true))
.option(new Option(R.string.profileswitch_ichigher, true))
.option(new Option(R.string.profileswitch_iclower, false))
.option(new Option(R.string.profileswitch_isfhigher, true))
.option(new Option(R.string.profileswitch_isflower, false))
.option(new Option(R.string.profileswitch_overall, true))
.option(new Option(R.string.profileswitch_targethigher, false))
.option(new Option(R.string.profileswitch_targetlower, false))
.option(new Option(R.string.profileswitch_targetbottom, false))
.hint(new Hint(R.string.profileswitch_hint1))
);
tasks.add(new ExamTask(R.string.profileswitch_label, R.string.profileswitchtime_iwant,"profileswitchtime")
.option(new Option(R.string.profileswitchtime_1, false))
.option(new Option(R.string.profileswitchtime__1, true))
.option(new Option(R.string.profileswitchtime_60, false))
.option(new Option(R.string.profileswitchtime__60, false))
.hint(new Hint(R.string.profileswitchtime_hint1))
);
tasks.add(new ExamTask(R.string.other_medication_label, R.string.other_medication_text,"otherMedicationWarning")
.option(new Option(R.string.yes, true))
.option(new Option(R.string.no, false))
);
for (Task task : tasks)
Collections.shuffle(((ExamTask)task).options);
}
}

View file

@ -0,0 +1,211 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
@Suppress("SpellCheckingInspection")
class Objective2(injector: HasAndroidInjector) : Objective(injector, "exam", R.string.objectives_exam_objective, R.string.objectives_exam_gate) {
init {
tasks.add(ExamTask(this, R.string.prerequisites_label, R.string.prerequisites_what, "prerequisites")
.option(Option(R.string.prerequisites_nightscout, true))
.option(Option(R.string.prerequisites_computer, true))
.option(Option(R.string.prerequisites_pump, true))
.option(Option(R.string.prerequisites_beanandroiddeveloper, false))
.hint(Hint(R.string.prerequisites_hint1))
)
tasks.add(ExamTask(this, R.string.prerequisites2_label, R.string.prerequisites2_what, "prerequisites2")
.option(Option(R.string.prerequisites2_profile, true))
.option(Option(R.string.prerequisites2_device, true))
.option(Option(R.string.prerequisites2_internet, false))
.option(Option(R.string.prerequisites2_supportedcgm, true))
.hint(Hint(R.string.prerequisites2_hint1))
)
tasks.add(ExamTask(this, R.string.basaltest_label, R.string.basaltest_when, "basaltest")
.option(Option(R.string.basaltest_fixed, false))
.option(Option(R.string.basaltest_havingregularhighlow, true))
.option(Option(R.string.basaltest_weekly, false))
.option(Option(R.string.basaltest_beforeloop, true))
.hint(Hint(R.string.basaltest_hint1))
)
tasks.add(ExamTask(this, R.string.dia_label_exam, R.string.dia_whatmeansdia, "dia")
.option(Option(R.string.dia_profile, true))
.option(Option(R.string.dia_minimumis5h, true))
.option(Option(R.string.dia_meaningisequaltodiapump, false))
.option(Option(R.string.dia_valuemustbedetermined, true))
.hint(Hint(R.string.dia_hint1))
)
tasks.add(ExamTask(this, R.string.isf_label_exam, R.string.blank, "isf")
.option(Option(R.string.isf_decreasingvalue, true))
.option(Option(R.string.isf_preferences, false))
.option(Option(R.string.isf_increasingvalue, false))
.option(Option(R.string.isf_noeffect, false))
.hint(Hint(R.string.isf_hint1))
.hint(Hint(R.string.isf_hint2))
)
tasks.add(ExamTask(this, R.string.ic_label_exam, R.string.blank, "ic")
.option(Option(R.string.ic_increasingvalue, true))
.option(Option(R.string.ic_decreasingvalue, false))
.option(Option(R.string.ic_multiple, true))
.option(Option(R.string.ic_isf, false))
.hint(Hint(R.string.ic_hint1))
)
tasks.add(ExamTask(this, R.string.hypott_label, R.string.hypott_whenhypott, "hypott")
.option(Option(R.string.hypott_preventoversmb, true))
.option(Option(R.string.hypott_exercise, false))
.option(Option(R.string.hypott_wrongbasal, false))
.option(Option(R.string.hypott_0basal, false))
.hint(Hint(R.string.hypott_hint1))
)
tasks.add(ExamTask(this, R.string.profileswitch_label, R.string.profileswitch_pctwillchange, "profileswitch")
.option(Option(R.string.profileswitch_basallower, true))
.option(Option(R.string.profileswitch_isfhigher, true))
.option(Option(R.string.profileswitch_iclower, false))
.option(Option(R.string.profileswitch_unchanged, false))
.hint(Hint(R.string.profileswitch_hint1))
)
tasks.add(ExamTask(this, R.string.profileswitch2_label, R.string.profileswitch2_pctwillchange, "profileswitch2")
.option(Option(R.string.profileswitch2_bghigher, false))
.option(Option(R.string.profileswitch2_basalhigher, true))
.option(Option(R.string.profileswitch2_bgunchanged, true))
.option(Option(R.string.profileswitch2_isfhigher, false))
.hint(Hint(R.string.profileswitch_hint1))
)
tasks.add(ExamTask(this, R.string.profileswitchtime_label, R.string.profileswitchtime_iwant, "profileswitchtime")
.option(Option(R.string.profileswitchtime_2, false))
.option(Option(R.string.profileswitchtime__2, true))
.option(Option(R.string.profileswitchtime_tt, false))
.option(Option(R.string.profileswitchtime_100, false))
.hint(Hint(R.string.profileswitchtime_hint1))
)
tasks.add(ExamTask(this, R.string.profileswitch4_label, R.string.blank, "profileswitch4")
.option(Option(R.string.profileswitch4_rates, true))
.option(Option(R.string.profileswitch4_internet, true))
.option(Option(R.string.profileswitch4_sufficient, false))
.option(Option(R.string.profileswitch4_multi, true))
.hint(Hint(R.string.profileswitch_hint1))
)
tasks.add(ExamTask(this, R.string.exerciseprofile_label, R.string.exerciseprofile_whattodo, "exercise")
.option(Option(R.string.exerciseprofile_switchprofileabove100, false))
.option(Option(R.string.exerciseprofile_switchprofilebelow100, true))
.option(Option(R.string.exerciseprofile_suspendloop, false))
.option(Option(R.string.exerciseprofile_leaveat100, false))
.hint(Hint(R.string.exerciseprofile_hint1))
)
tasks.add(ExamTask(this, R.string.exercise_label, R.string.exercise_whattodo, "exercise2")
.option(Option(R.string.exercise_settt, true))
.option(Option(R.string.exercise_setfinished, false))
.option(Option(R.string.exercise_setunchanged, false))
.option(Option(R.string.exercise_15g, false))
.hint(Hint(R.string.exercise_hint1))
)
tasks.add(ExamTask(this, R.string.noisycgm_label, R.string.noisycgm_whattodo, "noisycgm")
.option(Option(R.string.noisycgm_nothing, false))
.option(Option(R.string.noisycgm_pause, true))
.option(Option(R.string.noisycgm_replacesensor, true))
.option(Option(R.string.noisycgm_checksmoothing, true))
.hint(Hint(R.string.noisycgm_hint1))
)
tasks.add(ExamTask(this, R.string.pumpdisconnect_label, R.string.blank, "pumpdisconnect")
.option(Option(R.string.pumpdisconnect_unnecessary, false))
.option(Option(R.string.pumpdisconnect_missinginsulin, true))
.option(Option(R.string.pumpdisconnect_notstop, false))
.option(Option(R.string.pumpdisconnect_openloop, false))
.hint(Hint(R.string.pumpdisconnect_hint1))
)
tasks.add(ExamTask(this, R.string.insulin_label, R.string.insulin_ultrarapid, "insulin")
.option(Option(R.string.insulin_novorapid, false))
.option(Option(R.string.insulin_humalog, false))
.option(Option(R.string.insulin_actrapid, false))
.option(Option(R.string.insulin_fiasp, true))
.hint(Hint(R.string.insulin_hint1))
)
tasks.add(ExamTask(this, R.string.sensitivity_label, R.string.blank, "sensitivity")
.option(Option(R.string.sensitivity_adjust, true))
.option(Option(R.string.sensitivity_edit, false))
.option(Option(R.string.sensitivity_cannula, true))
.option(Option(R.string.sensitivity_time, true))
.hint(Hint(R.string.sensitivity_hint1))
)
tasks.add(ExamTask(this, R.string.objectives_label, R.string.objectives_howtosave, "objectives")
.option(Option(R.string.objectives_notesettings, false))
.option(Option(R.string.objectives_afterobjective, true))
.option(Option(R.string.objectives_afterchange, true))
.option(Option(R.string.objectives_afterinitialsetup, true))
.hint(Hint(R.string.objectives_hint1))
.hint(Hint(R.string.objectives_hint2))
)
tasks.add(ExamTask(this, R.string.objectives2_label, R.string.objectives_howtosave, "objectives2")
.option(Option(R.string.objectives2_maintenance, true))
.option(Option(R.string.objectives2_internalstorage, true))
.option(Option(R.string.objectives2_cloud, true))
.option(Option(R.string.objectives2_easyrestore, false))
.hint(Hint(R.string.objectives_hint1))
.hint(Hint(R.string.objectives_hint2))
)
tasks.add(ExamTask(this, R.string.update_label, R.string.blank, "update")
.option(Option(R.string.update_git, true))
.option(Option(R.string.update_askfriend, false))
.option(Option(R.string.update_keys, true))
.option(Option(R.string.update_asap, true))
.hint(Hint(R.string.update_hint1))
)
tasks.add(ExamTask(this, R.string.troubleshooting_label, R.string.troubleshooting_wheretoask, "troubleshooting")
.option(Option(R.string.troubleshooting_fb, true))
.option(Option(R.string.troubleshooting_wiki, true))
.option(Option(R.string.troubleshooting_gitter, true))
.option(Option(R.string.troubleshooting_yourendo, false))
.hint(Hint(R.string.troubleshooting_hint1))
.hint(Hint(R.string.troubleshooting_hint2))
.hint(Hint(R.string.troubleshooting_hint3))
)
tasks.add(ExamTask(this, R.string.wrongcarbs_label, R.string.wrongcarbs_whattodo, "wrongcarbs")
.option(Option(R.string.wrongcarbs_addinsulin, false))
.option(Option(R.string.wrongcarbs_treatmentstab, true))
.option(Option(R.string.wrongcarbs_donothing, false))
.option(Option(R.string.wrongcarbs_bolus, false))
)
tasks.add(ExamTask(this, R.string.wronginsulin_label, R.string.wronginsulin_whattodo, "wronginsulin")
.option(Option(R.string.wronginsulin_careportal, false))
.option(Option(R.string.wronginsulin_compare, true))
.option(Option(R.string.wronginsulin_prime, true))
.option(Option(R.string.wrongcarbs_donothing, false))
)
tasks.add(ExamTask(this, R.string.iob_label, R.string.blank, "iob")
.option(Option(R.string.iob_value, true))
.option(Option(R.string.iob_hightemp, false))
.option(Option(R.string.iob_negiob, true))
.option(Option(R.string.iob_posiob, true))
)
tasks.add(ExamTask(this, R.string.breadgrams_label, R.string.blank, "breadgrams")
.option(Option(R.string.breadgrams_grams, true))
.option(Option(R.string.breadgrams_exchange, false))
.option(Option(R.string.breadgrams_decay, true))
.option(Option(R.string.breadgrams_calc, true))
.hint(Hint(R.string.breadgrams_hint1))
)
tasks.add(ExamTask(this, R.string.extendedcarbs_label, R.string.extendedcarbs_handling, "extendedcarbs")
.option(Option(R.string.extendedcarbs_future, true))
.option(Option(R.string.extendedcarbs_free, false))
.option(Option(R.string.extendedcarbs_fat, true))
.option(Option(R.string.extendedcarbs_rescue, false))
.hint(Hint(R.string.extendedcarbs_hint1))
)
tasks.add(ExamTask(this, R.string.nsclient_label, R.string.nsclient_howcanyou, "nsclient")
.option(Option(R.string.nsclient_nightscout, true))
.option(Option(R.string.nsclient_dexcomfollow, true))
.option(Option(R.string.nsclient_data, true))
.option(Option(R.string.nsclient_fullcontrol, false))
.hint(Hint(R.string.nsclient_hint1))
)
tasks.add(ExamTask(this, R.string.other_medication_label, R.string.other_medication_text, "otherMedicationWarning")
.option(Option(R.string.yes, true))
.option(Option(R.string.no, false))
)
for (task in tasks) (task as ExamTask).options.shuffle()
for (task in tasks) {
if (!task.isCompleted()) accomplishedOn = 0
}
}
}

View file

@ -1,61 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import androidx.fragment.app.FragmentActivity;
import java.util.List;
import javax.inject.Inject;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin;
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin;
import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService;
import info.nightscout.androidaps.utils.T;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
public class Objective3 extends Objective {
@Inject SP sp;
@Inject ObjectivesPlugin objectivesPlugin;
@Inject ResourceHelper resourceHelper;
@Inject NSClientPlugin nsClientPlugin;
private final int MANUAL_ENACTS_NEEDED = 20;
@Inject
public Objective3(HasAndroidInjector injector) {
super(injector, "openloop", R.string.objectives_openloop_objective, R.string.objectives_openloop_gate);
// disable option for skipping objectives for now
// hasSpecialInput = true;
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new MinimumDurationTask(T.days(7).msecs()));
tasks.add(new Task(R.string.objectives_manualenacts) {
@Override
public boolean isCompleted() {
return sp.getInt(R.string.key_ObjectivesmanualEnacts, 0) >= MANUAL_ENACTS_NEEDED;
}
@Override
public String getProgress() {
if (sp.getInt(R.string.key_ObjectivesmanualEnacts, 0) >= MANUAL_ENACTS_NEEDED)
return resourceHelper.gs(R.string.completed_well_done);
else
return sp.getInt(R.string.key_ObjectivesmanualEnacts, 0) + " / " + MANUAL_ENACTS_NEEDED;
}
});
}
@Override
public boolean specialActionEnabled() {
return NSClientService.isConnected && NSClientService.hasWriteAuth;
}
@Override
public void specialAction(FragmentActivity activity, String input) {
objectivesPlugin.completeObjectives(activity, input);
}
}

View file

@ -0,0 +1,41 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import androidx.fragment.app.FragmentActivity
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesPlugin
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin
import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService
import info.nightscout.androidaps.utils.T
import javax.inject.Inject
@Suppress("SpellCheckingInspection")
class Objective3 @Inject constructor(injector: HasAndroidInjector) : Objective(injector, "openloop", R.string.objectives_openloop_objective, R.string.objectives_openloop_gate) {
@Inject lateinit var objectivesPlugin: ObjectivesPlugin
@Inject lateinit var nsClientPlugin: NSClientPlugin
init {
tasks.add(MinimumDurationTask(this, T.days(7).msecs()))
tasks.add(object : Task(this, R.string.objectives_manualenacts) {
override fun isCompleted(): Boolean {
return sp.getInt(R.string.key_ObjectivesmanualEnacts, 0) >= MANUAL_ENACTS_NEEDED
}
override val progress: String
get() = if (sp.getInt(R.string.key_ObjectivesmanualEnacts, 0) >= MANUAL_ENACTS_NEEDED) resourceHelper.gs(R.string.completed_well_done) else sp.getInt(R.string.key_ObjectivesmanualEnacts, 0).toString() + " / " + MANUAL_ENACTS_NEEDED
})
}
override fun specialActionEnabled(): Boolean =
NSClientService.isConnected && NSClientService.hasWriteAuth
override fun specialAction(activity: FragmentActivity, input: String) {
objectivesPlugin.completeObjectives(activity, input)
}
companion object {
private const val MANUAL_ENACTS_NEEDED = 20
}
}

View file

@ -1,11 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
public class Objective4 extends Objective {
public Objective4(HasAndroidInjector injector) {
super(injector, "maxbasal", R.string.objectives_maxbasal_objective, R.string.objectives_maxbasal_gate);
}
}

View file

@ -0,0 +1,7 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
@Suppress("SpellCheckingInspection")
class Objective4(injector: HasAndroidInjector) : Objective(injector, "maxbasal", R.string.objectives_maxbasal_objective, R.string.objectives_maxbasal_gate)

View file

@ -1,32 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import java.util.List;
import javax.inject.Inject;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.interfaces.Constraint;
import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin;
import info.nightscout.androidaps.utils.T;
public class Objective5 extends Objective {
@Inject SafetyPlugin safetyPlugin;
public Objective5(HasAndroidInjector injector) {
super(injector, "maxiobzero", R.string.objectives_maxiobzero_objective, R.string.objectives_maxiobzero_gate);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new MinimumDurationTask(T.days(5).msecs()));
tasks.add(new Task(R.string.closedmodeenabled) {
@Override
public boolean isCompleted() {
Constraint<Boolean> closedLoopEnabled = new Constraint<>(true);
safetyPlugin.isClosedLoopAllowed(closedLoopEnabled);
return closedLoopEnabled.value();
}
});
}
}

View file

@ -0,0 +1,25 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.Constraint
import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin
import info.nightscout.androidaps.utils.T
import javax.inject.Inject
@Suppress("SpellCheckingInspection")
class Objective5(injector: HasAndroidInjector) : Objective(injector, "maxiobzero", R.string.objectives_maxiobzero_objective, R.string.objectives_maxiobzero_gate) {
@Inject lateinit var safetyPlugin: SafetyPlugin
init {
tasks.add(MinimumDurationTask(this, T.days(5).msecs()))
tasks.add(object : Task(this, R.string.closedmodeenabled) {
override fun isCompleted(): Boolean {
val closedLoopEnabled = Constraint(true)
safetyPlugin.isClosedLoopAllowed(closedLoopEnabled)
return closedLoopEnabled.value()
}
})
}
}

View file

@ -1,30 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import java.util.List;
import javax.inject.Inject;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker;
import info.nightscout.androidaps.utils.T;
public class Objective6 extends Objective {
@Inject ConstraintChecker constraintChecker;
public Objective6(HasAndroidInjector injector) {
super(injector, "maxiob", R.string.objectives_maxiob_objective, R.string.objectives_maxiob_gate);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new MinimumDurationTask(T.days(1).msecs()));
tasks.add(new Task(R.string.maxiobset) {
@Override
public boolean isCompleted() {
double maxIOB = constraintChecker.getMaxIOBAllowed().value();
return maxIOB > 0;
}
});
}
}

View file

@ -0,0 +1,23 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.utils.T
import javax.inject.Inject
@Suppress("SpellCheckingInspection")
class Objective6(injector: HasAndroidInjector) : Objective(injector, "maxiob", R.string.objectives_maxiob_objective, R.string.objectives_maxiob_gate) {
@Inject lateinit var constraintChecker: ConstraintChecker
init {
tasks.add(MinimumDurationTask(this, T.days(1).msecs()))
tasks.add(object : Task(this, R.string.maxiobset) {
override fun isCompleted(): Boolean {
val maxIOB = constraintChecker.getMaxIOBAllowed().value()
return maxIOB > 0
}
})
}
}

View file

@ -1,19 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import java.util.List;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.utils.T;
public class Objective7 extends Objective {
public Objective7(HasAndroidInjector injector) {
super(injector, "autosens", R.string.objectives_autosens_objective, R.string.objectives_autosens_gate);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new MinimumDurationTask(T.days(7).msecs()));
}
}

View file

@ -0,0 +1,12 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.utils.T
class Objective7(injector: HasAndroidInjector) : Objective(injector, "autosens", R.string.objectives_autosens_objective, R.string.objectives_autosens_gate) {
init {
tasks.add(MinimumDurationTask(this, T.days(7).msecs()))
}
}

View file

@ -1,19 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import java.util.List;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.utils.T;
public class Objective8 extends Objective {
public Objective8(HasAndroidInjector injector) {
super(injector, "ama", R.string.objectives_ama_objective, 0);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new MinimumDurationTask(T.days(28).msecs()));
}
}

View file

@ -0,0 +1,12 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.utils.T
class Objective8(injector: HasAndroidInjector) : Objective(injector, "ama", R.string.objectives_ama_objective, 0) {
init {
tasks.add(MinimumDurationTask(this, T.days(28).msecs()))
}
}

View file

@ -1,19 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives;
import java.util.List;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.utils.T;
public class Objective9 extends Objective {
public Objective9(HasAndroidInjector injector) {
super(injector, "smb", R.string.objectives_smb_objective, R.string.objectives_smb_gate);
}
@Override
protected void setupTasks(List<Task> tasks) {
tasks.add(new MinimumDurationTask(T.days(28).msecs()));
}
}

View file

@ -0,0 +1,12 @@
package info.nightscout.androidaps.plugins.constraints.objectives.objectives
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.utils.T
class Objective9(injector: HasAndroidInjector) : Objective(injector, "smb", R.string.objectives_smb_objective, R.string.objectives_smb_gate) {
init {
tasks.add(MinimumDurationTask(this, T.days(28).msecs()))
}
}

View file

@ -1,289 +0,0 @@
package info.nightscout.androidaps.plugins.constraints.safety;
import androidx.annotation.NonNull;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.Config;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.interfaces.ActivePluginProvider;
import info.nightscout.androidaps.interfaces.BgSourceInterface;
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.PumpDescription;
import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.plugins.aps.openAPSAMA.OpenAPSAMAPlugin;
import info.nightscout.androidaps.plugins.aps.openAPSSMB.OpenAPSSMBPlugin;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker;
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification;
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification;
import info.nightscout.androidaps.plugins.sensitivity.SensitivityOref1Plugin;
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
import info.nightscout.androidaps.utils.DecimalFormatter;
import info.nightscout.androidaps.utils.HardLimits;
import info.nightscout.androidaps.utils.Round;
import info.nightscout.androidaps.utils.buildHelper.BuildHelper;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
@Singleton
public class SafetyPlugin extends PluginBase implements ConstraintsInterface {
private final SP sp;
private final RxBusWrapper rxBus;
private final ConstraintChecker constraintChecker;
private final OpenAPSAMAPlugin openAPSAMAPlugin;
private final OpenAPSSMBPlugin openAPSSMBPlugin;
private final SensitivityOref1Plugin sensitivityOref1Plugin;
private final ActivePluginProvider activePlugin;
private final HardLimits hardLimits;
private final BuildHelper buildHelper;
private final TreatmentsPlugin treatmentsPlugin;
private final Config config;
@Inject
public SafetyPlugin(
HasAndroidInjector injector,
AAPSLogger aapsLogger,
ResourceHelper resourceHelper,
SP sp,
RxBusWrapper rxBus,
ConstraintChecker constraintChecker,
OpenAPSAMAPlugin openAPSAMAPlugin,
OpenAPSSMBPlugin openAPSSMBPlugin,
SensitivityOref1Plugin sensitivityOref1Plugin,
ActivePluginProvider activePlugin,
HardLimits hardLimits,
BuildHelper buildHelper,
TreatmentsPlugin treatmentsPlugin,
Config config
) {
super(new PluginDescription()
.mainType(PluginType.CONSTRAINTS)
.neverVisible(true)
.alwaysEnabled(true)
.showInList(false)
.pluginName(R.string.safety)
.preferencesId(R.xml.pref_safety),
aapsLogger, resourceHelper, injector
);
this.sp = sp;
this.rxBus = rxBus;
this.constraintChecker = constraintChecker;
this.openAPSAMAPlugin = openAPSAMAPlugin;
this.openAPSSMBPlugin = openAPSSMBPlugin;
this.sensitivityOref1Plugin = sensitivityOref1Plugin;
this.activePlugin = activePlugin;
this.hardLimits = hardLimits;
this.buildHelper = buildHelper;
this.treatmentsPlugin = treatmentsPlugin;
this.config = config;
}
/**
* Constraints interface
**/
@NonNull @Override
public Constraint<Boolean> isLoopInvocationAllowed(@NonNull Constraint<Boolean> value) {
if (!activePlugin.getActivePump().getPumpDescription().isTempBasalCapable)
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.pumpisnottempbasalcapable), this);
return value;
}
@NonNull @Override
public Constraint<Boolean> isClosedLoopAllowed(@NonNull Constraint<Boolean> value) {
String mode = sp.getString(R.string.key_aps_mode, "open");
if ((mode.equals("open")))
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.closedmodedisabledinpreferences), this);
if (!buildHelper.isEngineeringModeOrRelease()) {
if (value.value()) {
Notification n = new Notification(Notification.TOAST_ALARM, getResourceHelper().gs(R.string.closed_loop_disabled_on_dev_branch), Notification.NORMAL);
rxBus.send(new EventNewNotification(n));
}
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.closed_loop_disabled_on_dev_branch), this);
}
PumpInterface pump = activePlugin.getActivePump();
if (!pump.isFakingTempsByExtendedBoluses() && treatmentsPlugin.isInHistoryExtendedBoluslInProgress()) {
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.closed_loop_disabled_with_eb), this);
}
return value;
}
@NonNull @Override
public Constraint<Boolean> isAutosensModeEnabled(@NonNull Constraint<Boolean> value) {
boolean enabled = sp.getBoolean(R.string.key_openapsama_useautosens, false);
if (!enabled)
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.autosensdisabledinpreferences), this);
return value;
}
@NonNull @Override
public Constraint<Boolean> isSMBModeEnabled(@NonNull Constraint<Boolean> value) {
boolean enabled = sp.getBoolean(R.string.key_use_smb, false);
if (!enabled)
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.smbdisabledinpreferences), this);
Constraint<Boolean> closedLoop = constraintChecker.isClosedLoopAllowed();
if (!closedLoop.value())
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.smbnotallowedinopenloopmode), this);
return value;
}
@NonNull @Override
public Constraint<Boolean> isUAMEnabled(@NonNull Constraint<Boolean> value) {
boolean enabled = sp.getBoolean(R.string.key_use_uam, false);
if (!enabled)
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.uamdisabledinpreferences), this);
boolean oref1Enabled = sensitivityOref1Plugin.isEnabled(PluginType.SENSITIVITY);
if (!oref1Enabled)
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.uamdisabledoref1notselected), this);
return value;
}
@NonNull @Override
public Constraint<Boolean> isAdvancedFilteringEnabled(@NonNull Constraint<Boolean> value) {
BgSourceInterface bgSource = activePlugin.getActiveBgSource();
if (!bgSource.advancedFilteringSupported())
value.set(getAapsLogger(), false, getResourceHelper().gs(R.string.smbalwaysdisabled), this);
return value;
}
@NonNull @Override
public Constraint<Double> applyBasalConstraints(Constraint<Double> absoluteRate, @NonNull Profile profile) {
absoluteRate.setIfGreater(getAapsLogger(), 0d, String.format(getResourceHelper().gs(R.string.limitingbasalratio), 0d, getResourceHelper().gs(R.string.itmustbepositivevalue)), this);
if (config.getAPS()) {
double maxBasal = sp.getDouble(R.string.key_openapsma_max_basal, 1d);
if (maxBasal < profile.getMaxDailyBasal()) {
maxBasal = profile.getMaxDailyBasal();
absoluteRate.addReason(getResourceHelper().gs(R.string.increasingmaxbasal), this);
}
absoluteRate.setIfSmaller(getAapsLogger(), maxBasal, String.format(getResourceHelper().gs(R.string.limitingbasalratio), maxBasal, getResourceHelper().gs(R.string.maxvalueinpreferences)), this);
// Check percentRate but absolute rate too, because we know real current basal in pump
double maxBasalMult = sp.getDouble(R.string.key_openapsama_current_basal_safety_multiplier, 4d);
double maxFromBasalMult = Math.floor(maxBasalMult * profile.getBasal() * 100) / 100;
absoluteRate.setIfSmaller(getAapsLogger(), maxFromBasalMult, String.format(getResourceHelper().gs(R.string.limitingbasalratio), maxFromBasalMult, getResourceHelper().gs(R.string.maxbasalmultiplier)), this);
double maxBasalFromDaily = sp.getDouble(R.string.key_openapsama_max_daily_safety_multiplier, 3d);
double maxFromDaily = Math.floor(profile.getMaxDailyBasal() * maxBasalFromDaily * 100) / 100;
absoluteRate.setIfSmaller(getAapsLogger(), maxFromDaily, String.format(getResourceHelper().gs(R.string.limitingbasalratio), maxFromDaily, getResourceHelper().gs(R.string.maxdailybasalmultiplier)), this);
}
absoluteRate.setIfSmaller(getAapsLogger(), hardLimits.maxBasal(), String.format(getResourceHelper().gs(R.string.limitingbasalratio), hardLimits.maxBasal(), getResourceHelper().gs(R.string.hardlimit)), this);
PumpInterface pump = activePlugin.getActivePump();
// check for pump max
if (pump.getPumpDescription().tempBasalStyle == PumpDescription.ABSOLUTE) {
double pumpLimit = pump.getPumpDescription().pumpType.getTbrSettings().getMaxDose();
absoluteRate.setIfSmaller(getAapsLogger(), pumpLimit, String.format(getResourceHelper().gs(R.string.limitingbasalratio), pumpLimit, getResourceHelper().gs(R.string.pumplimit)), this);
}
// do rounding
if (pump.getPumpDescription().tempBasalStyle == PumpDescription.ABSOLUTE) {
absoluteRate.set(getAapsLogger(), Round.roundTo(absoluteRate.value(), pump.getPumpDescription().tempAbsoluteStep));
}
return absoluteRate;
}
@NonNull @Override
public Constraint<Integer> applyBasalPercentConstraints(Constraint<Integer> percentRate, Profile profile) {
Double currentBasal = profile.getBasal();
double absoluteRate = currentBasal * ((double) percentRate.originalValue() / 100);
percentRate.addReason("Percent rate " + percentRate.originalValue() + "% recalculated to " + DecimalFormatter.to2Decimal(absoluteRate) + " U/h with current basal " + DecimalFormatter.to2Decimal(currentBasal) + " U/h", this);
Constraint<Double> absoluteConstraint = new Constraint<>(absoluteRate);
applyBasalConstraints(absoluteConstraint, profile);
percentRate.copyReasons(absoluteConstraint);
PumpInterface pump = activePlugin.getActivePump();
int percentRateAfterConst = Double.valueOf(absoluteConstraint.value() / currentBasal * 100).intValue();
if (percentRateAfterConst < 100)
percentRateAfterConst = Round.ceilTo((double) percentRateAfterConst, (double) pump.getPumpDescription().tempPercentStep).intValue();
else
percentRateAfterConst = Round.floorTo((double) percentRateAfterConst, (double) pump.getPumpDescription().tempPercentStep).intValue();
percentRate.set(getAapsLogger(), percentRateAfterConst, String.format(getResourceHelper().gs(R.string.limitingpercentrate), percentRateAfterConst, getResourceHelper().gs(R.string.pumplimit)), this);
if (pump.getPumpDescription().tempBasalStyle == PumpDescription.PERCENT) {
double pumpLimit = pump.getPumpDescription().pumpType.getTbrSettings().getMaxDose();
percentRate.setIfSmaller(getAapsLogger(), (int) pumpLimit, String.format(getResourceHelper().gs(R.string.limitingbasalratio), pumpLimit, getResourceHelper().gs(R.string.pumplimit)), this);
}
return percentRate;
}
@NonNull @Override
public Constraint<Double> applyBolusConstraints(Constraint<Double> insulin) {
insulin.setIfGreater(getAapsLogger(), 0d, String.format(getResourceHelper().gs(R.string.limitingbolus), 0d, getResourceHelper().gs(R.string.itmustbepositivevalue)), this);
Double maxBolus = sp.getDouble(R.string.key_treatmentssafety_maxbolus, 3d);
insulin.setIfSmaller(getAapsLogger(), maxBolus, String.format(getResourceHelper().gs(R.string.limitingbolus), maxBolus, getResourceHelper().gs(R.string.maxvalueinpreferences)), this);
insulin.setIfSmaller(getAapsLogger(), hardLimits.maxBolus(), String.format(getResourceHelper().gs(R.string.limitingbolus), hardLimits.maxBolus(), getResourceHelper().gs(R.string.hardlimit)), this);
PumpInterface pump = activePlugin.getActivePump();
double rounded = pump.getPumpDescription().pumpType.determineCorrectBolusSize(insulin.value());
insulin.setIfDifferent(getAapsLogger(), rounded, getResourceHelper().gs(R.string.pumplimit), this);
return insulin;
}
@NonNull @Override
public Constraint<Double> applyExtendedBolusConstraints(Constraint<Double> insulin) {
insulin.setIfGreater(getAapsLogger(), 0d, String.format(getResourceHelper().gs(R.string.limitingextendedbolus), 0d, getResourceHelper().gs(R.string.itmustbepositivevalue)), this);
Double maxBolus = sp.getDouble(R.string.key_treatmentssafety_maxbolus, 3d);
insulin.setIfSmaller(getAapsLogger(), maxBolus, String.format(getResourceHelper().gs(R.string.limitingextendedbolus), maxBolus, getResourceHelper().gs(R.string.maxvalueinpreferences)), this);
insulin.setIfSmaller(getAapsLogger(), hardLimits.maxBolus(), String.format(getResourceHelper().gs(R.string.limitingextendedbolus), hardLimits.maxBolus(), getResourceHelper().gs(R.string.hardlimit)), this);
PumpInterface pump = activePlugin.getActivePump();
double rounded = pump.getPumpDescription().pumpType.determineCorrectExtendedBolusSize(insulin.value());
insulin.setIfDifferent(getAapsLogger(), rounded, getResourceHelper().gs(R.string.pumplimit), this);
return insulin;
}
@NonNull @Override
public Constraint<Integer> applyCarbsConstraints(Constraint<Integer> carbs) {
carbs.setIfGreater(getAapsLogger(), 0, String.format(getResourceHelper().gs(R.string.limitingcarbs), 0, getResourceHelper().gs(R.string.itmustbepositivevalue)), this);
Integer maxCarbs = sp.getInt(R.string.key_treatmentssafety_maxcarbs, 48);
carbs.setIfSmaller(getAapsLogger(), maxCarbs, String.format(getResourceHelper().gs(R.string.limitingcarbs), maxCarbs, getResourceHelper().gs(R.string.maxvalueinpreferences)), this);
return carbs;
}
@NonNull @Override
public Constraint<Double> applyMaxIOBConstraints(@NonNull Constraint<Double> maxIob) {
double maxIobPref;
String apsmode = sp.getString(R.string.key_aps_mode, "open");
if (openAPSSMBPlugin.isEnabled(PluginType.APS))
maxIobPref = sp.getDouble(R.string.key_openapssmb_max_iob, 3d);
else
maxIobPref = sp.getDouble(R.string.key_openapsma_max_iob, 1.5d);
maxIob.setIfSmaller(getAapsLogger(), maxIobPref, String.format(getResourceHelper().gs(R.string.limitingiob), maxIobPref, getResourceHelper().gs(R.string.maxvalueinpreferences)), this);
if (openAPSAMAPlugin.isEnabled(PluginType.APS))
maxIob.setIfSmaller(getAapsLogger(), hardLimits.maxIobAMA(), String.format(getResourceHelper().gs(R.string.limitingiob), hardLimits.maxIobAMA(), getResourceHelper().gs(R.string.hardlimit)), this);
if (openAPSSMBPlugin.isEnabled(PluginType.APS))
maxIob.setIfSmaller(getAapsLogger(), hardLimits.maxIobSMB(), String.format(getResourceHelper().gs(R.string.limitingiob), hardLimits.maxIobSMB(), getResourceHelper().gs(R.string.hardlimit)), this);
if ((apsmode.equals("lgs")))
maxIob.setIfSmaller(getAapsLogger(), hardLimits.getMAXIOB_LGS(), String.format(getResourceHelper().gs(R.string.limitingiob), hardLimits.getMAXIOB_LGS(), getResourceHelper().gs(R.string.lowglucosesuspend)), this);
return maxIob;
}
}

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