AndroidAPS/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt

358 lines
18 KiB
Kotlin
Raw Normal View History

2019-12-20 18:55:54 +01:00
package info.nightscout.androidaps.dialogs
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
2019-12-20 23:05:35 +01:00
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.view.WindowManager
import android.widget.AdapterView
2019-10-10 23:02:35 +02:00
import android.widget.AdapterView.OnItemSelectedListener
import android.widget.ArrayAdapter
import android.widget.CompoundButton
2020-04-24 12:00:31 +02:00
import dagger.android.support.DaggerDialogFragment
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile
2019-10-10 23:02:35 +02:00
import info.nightscout.androidaps.db.BgReading
2020-04-24 12:00:31 +02:00
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.Constraint
2020-04-24 12:00:31 +02:00
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunction
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
2020-04-24 12:00:31 +02:00
import info.nightscout.androidaps.utils.DecimalFormatter
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.SafeParse
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.utils.wizard.BolusWizard
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
2019-12-20 18:55:54 +01:00
import kotlinx.android.synthetic.main.dialog_wizard.*
import java.text.DecimalFormat
import java.util.*
2020-04-24 12:00:31 +02:00
import javax.inject.Inject
import kotlin.math.abs
2020-04-24 12:00:31 +02:00
class WizardDialog : DaggerDialogFragment() {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var mainApp: MainApp
@Inject lateinit var sp: SP
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var treatmentsPlugin: TreatmentsPlugin
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin
private var wizard: BolusWizard? = null
//one shot guards
private var okClicked: Boolean = false
private val textWatcher = 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()
}
}
private var disposable: CompositeDisposable = CompositeDisposable()
2019-10-10 23:02:35 +02:00
override fun onStart() {
super.onStart()
dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
}
override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState)
savedInstanceState.putDouble("treatments_wizard_bg_input", treatments_wizard_bg_input.value)
savedInstanceState.putDouble("treatments_wizard_carbs_input", treatments_wizard_carbs_input.value)
savedInstanceState.putDouble("treatments_wizard_correction_input", treatments_wizard_correction_input.value)
savedInstanceState.putDouble("treatments_wizard_carb_time_input", treatments_wizard_carb_time_input.value)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
2019-09-15 18:56:54 +02:00
dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE)
dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
isCancelable = true
2019-09-15 18:56:54 +02:00
dialog?.setCanceledOnTouchOutside(false)
2019-12-20 18:55:54 +01:00
return inflater.inflate(R.layout.dialog_wizard, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
loadCheckedStates()
processCobCheckBox()
2020-04-24 12:00:31 +02:00
treatments_wizard_sbcheckbox.visibility = sp.getBoolean(R.string.key_usesuperbolus, false).toVisibility()
treatments_wizard_notes_layout.visibility = sp.getBoolean(R.string.key_show_notes_entry_dialogs, false).toVisibility()
2020-04-24 12:00:31 +02:00
val maxCarbs = constraintChecker.getMaxCarbsAllowed().value()
val maxCorrection = constraintChecker.getMaxBolusAllowed().value()
treatments_wizard_bg_input.setParams(savedInstanceState?.getDouble("treatments_wizard_bg_input")
2019-12-20 23:05:35 +01:00
?: 0.0, 0.0, 500.0, 0.1, DecimalFormat("0.0"), false, ok, textWatcher)
treatments_wizard_carbs_input.setParams(savedInstanceState?.getDouble("treatments_wizard_carbs_input")
2019-12-20 23:05:35 +01:00
?: 0.0, 0.0, maxCarbs.toDouble(), 1.0, DecimalFormat("0"), false, ok, textWatcher)
2020-04-24 12:00:31 +02:00
val bolusStep = activePlugin.activePump.pumpDescription.bolusStep
treatments_wizard_correction_input.setParams(savedInstanceState?.getDouble("treatments_wizard_correction_input")
2020-04-24 12:00:31 +02:00
?: 0.0, -maxCorrection, maxCorrection, bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, ok, textWatcher)
treatments_wizard_carb_time_input.setParams(savedInstanceState?.getDouble("treatments_wizard_carb_time_input")
2019-12-20 23:05:35 +01:00
?: 0.0, -60.0, 60.0, 5.0, DecimalFormat("0"), false, ok, textWatcher)
initDialog()
2020-04-24 12:00:31 +02:00
treatments_wizard_percent_used.text = resourceHelper.gs(R.string.format_percent, sp.getInt(R.string.key_boluswizard_percentage, 100))
// ok button
ok.setOnClickListener {
if (okClicked) {
2020-04-24 12:00:31 +02:00
aapsLogger.debug(LTag.UI, "guarding: ok already clicked")
} else {
okClicked = true
calculateInsulin()
2019-12-20 18:55:54 +01:00
context?.let { context ->
wizard?.confirmAndExecute(context)
}
}
dismiss()
}
// cancel button
cancel.setOnClickListener { dismiss() }
// checkboxes
2019-12-20 23:05:35 +01:00
treatments_wizard_bgcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
treatments_wizard_ttcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
treatments_wizard_cobcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
treatments_wizard_basaliobcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
treatments_wizard_bolusiobcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
treatments_wizard_bgtrendcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
treatments_wizard_sbcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
2019-10-10 23:02:35 +02:00
2020-04-24 12:00:31 +02:00
val showCalc = sp.getBoolean(resourceHelper.gs(R.string.key_wizard_calculation_visible), false)
2019-12-20 23:05:35 +01:00
treatments_wizard_delimiter.visibility = showCalc.toVisibility()
treatments_wizard_resulttable.visibility = showCalc.toVisibility()
treatments_wizard_calculationcheckbox.isChecked = showCalc
2019-10-10 23:02:35 +02:00
treatments_wizard_calculationcheckbox.setOnCheckedChangeListener { _, isChecked ->
run {
2020-04-24 12:00:31 +02:00
sp.putBoolean(resourceHelper.gs(R.string.key_wizard_calculation_visible), isChecked)
2019-12-20 23:05:35 +01:00
treatments_wizard_delimiter.visibility = isChecked.toVisibility()
treatments_wizard_resulttable.visibility = isChecked.toVisibility()
2019-10-10 23:02:35 +02:00
}
}
// profile spinner
treatments_wizard_profile.onItemSelectedListener = object : OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {
2020-04-24 12:00:31 +02:00
ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.noprofileselected))
ok.visibility = View.GONE
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
calculateInsulin()
ok.visibility = View.VISIBLE
}
}
// bus
2020-04-24 12:00:31 +02:00
disposable.add(rxBus
2019-12-20 23:05:35 +01:00
.toObservable(EventAutosensCalculationFinished::class.java)
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
activity?.runOnUiThread { calculateInsulin() }
2020-04-24 12:00:31 +02:00
}, { fabricPrivacy.logException(it) })
)
}
override fun onDestroyView() {
super.onDestroyView()
disposable.clear()
}
2019-12-21 14:30:14 +01:00
private fun onCheckedChanged(buttonView: CompoundButton, @Suppress("UNUSED_PARAMETER") state: Boolean) {
saveCheckedStates()
2020-04-24 12:00:31 +02:00
treatments_wizard_ttcheckbox.isEnabled = treatments_wizard_bgcheckbox.isChecked && treatmentsPlugin.tempTargetFromHistory != null
if (buttonView.id == treatments_wizard_cobcheckbox.id)
processCobCheckBox()
calculateInsulin()
}
private fun processCobCheckBox() {
if (treatments_wizard_cobcheckbox.isChecked) {
treatments_wizard_bolusiobcheckbox.isEnabled = false
treatments_wizard_basaliobcheckbox.isEnabled = false
treatments_wizard_bolusiobcheckbox.isChecked = true
treatments_wizard_basaliobcheckbox.isChecked = true
} else {
treatments_wizard_bolusiobcheckbox.isEnabled = true
treatments_wizard_basaliobcheckbox.isEnabled = true
}
}
private fun saveCheckedStates() {
2020-04-24 12:00:31 +02:00
sp.putBoolean(resourceHelper.gs(R.string.key_wizard_include_cob), treatments_wizard_cobcheckbox.isChecked)
sp.putBoolean(resourceHelper.gs(R.string.key_wizard_include_trend_bg), treatments_wizard_bgtrendcheckbox.isChecked)
}
private fun loadCheckedStates() {
2020-04-24 12:00:31 +02:00
treatments_wizard_bgtrendcheckbox.isChecked = sp.getBoolean(resourceHelper.gs(R.string.key_wizard_include_trend_bg), false)
treatments_wizard_cobcheckbox.isChecked = sp.getBoolean(resourceHelper.gs(R.string.key_wizard_include_cob), false)
}
private fun initDialog() {
2020-04-24 12:00:31 +02:00
val profile = profileFunction.getProfile()
val profileStore = activePlugin.activeProfileInterface.profile
if (profile == null || profileStore == null) {
2020-04-24 12:00:31 +02:00
ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.noprofile))
dismiss()
return
}
val profileList: ArrayList<CharSequence>
2019-11-19 23:17:14 +01:00
profileList = profileStore.getProfileList()
2020-04-24 12:00:31 +02:00
profileList.add(0, resourceHelper.gs(R.string.active))
context?.let { context ->
val adapter = ArrayAdapter(context, R.layout.spinner_centered, profileList)
treatments_wizard_profile.adapter = adapter
} ?: return
2020-04-24 12:00:31 +02:00
val units = profileFunction.getUnits()
treatments_wizard_bgunits.text = units
if (units == Constants.MGDL)
treatments_wizard_bg_input.setStep(1.0)
else
treatments_wizard_bg_input.setStep(0.1)
// Set BG if not old
2020-04-24 12:00:31 +02:00
val lastBg = iobCobCalculatorPlugin.actualBg()
if (lastBg != null) {
treatments_wizard_bg_input.value = lastBg.valueToUnits(units)
} else {
treatments_wizard_bg_input.value = 0.0
}
2020-04-24 12:00:31 +02:00
treatments_wizard_ttcheckbox.isEnabled = treatmentsPlugin.tempTargetFromHistory != null
// IOB calculation
2020-04-24 12:00:31 +02:00
treatmentsPlugin.updateTotalIOBTreatments()
val bolusIob = treatmentsPlugin.lastCalculationTreatments.round()
treatmentsPlugin.updateTotalIOBTempBasals()
val basalIob = treatmentsPlugin.lastCalculationTempBasals.round()
2020-04-24 12:00:31 +02:00
treatments_wizard_bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, -bolusIob.iob)
treatments_wizard_basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, -basalIob.basaliob)
calculateInsulin()
2020-04-24 12:00:31 +02:00
treatments_wizard_percent_used.visibility = (sp.getInt(R.string.key_boluswizard_percentage, 100) != 100).toVisibility()
}
private fun calculateInsulin() {
2020-04-24 12:00:31 +02:00
val profileStore = activePlugin.activeProfileInterface.profile
if (treatments_wizard_profile.selectedItem == null || profileStore == null)
return // not initialized yet
var profileName = treatments_wizard_profile.selectedItem.toString()
val specificProfile: Profile?
2020-04-24 12:00:31 +02:00
if (profileName == resourceHelper.gs(R.string.active)) {
specificProfile = profileFunction.getProfile()
profileName = profileFunction.getProfileName()
} else
specificProfile = profileStore.getSpecificProfile(profileName)
if (specificProfile == null) return
// Entered values
var bg = SafeParse.stringToDouble(treatments_wizard_bg_input.text)
val carbs = SafeParse.stringToInt(treatments_wizard_carbs_input.text)
val correction = SafeParse.stringToDouble(treatments_wizard_correction_input.text)
2020-04-24 12:00:31 +02:00
val carbsAfterConstraint = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value()
if (abs(carbs - carbsAfterConstraint) > 0.01) {
treatments_wizard_carbs_input.value = 0.0
2020-04-24 12:00:31 +02:00
ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.carbsconstraintapplied))
return
}
bg = if (treatments_wizard_bgcheckbox.isChecked) bg else 0.0
2020-04-24 12:00:31 +02:00
val tempTarget = if (treatments_wizard_ttcheckbox.isChecked) treatmentsPlugin.tempTargetFromHistory else null
// COB
var cob = 0.0
if (treatments_wizard_cobcheckbox.isChecked) {
2020-04-24 12:00:31 +02:00
val cobInfo = iobCobCalculatorPlugin.getCobInfo(false, "Wizard COB")
cobInfo.displayCob?.let { cob = it }
}
val carbTime = SafeParse.stringToInt(treatments_wizard_carb_time_input.text)
2020-04-24 12:00:31 +02:00
wizard = BolusWizard(mainApp).doCalc(specificProfile, profileName, tempTarget, carbsAfterConstraint, cob, bg, correction,
sp.getInt(R.string.key_boluswizard_percentage, 100).toDouble(),
2019-12-20 23:05:35 +01:00
treatments_wizard_bgcheckbox.isChecked,
treatments_wizard_cobcheckbox.isChecked,
treatments_wizard_bolusiobcheckbox.isChecked,
treatments_wizard_basaliobcheckbox.isChecked,
treatments_wizard_sbcheckbox.isChecked,
treatments_wizard_ttcheckbox.isChecked,
treatments_wizard_bgtrendcheckbox.isChecked,
treatment_wizard_notes.text.toString(), carbTime)
wizard?.let { wizard ->
2020-04-24 12:00:31 +02:00
treatments_wizard_bg.text = String.format(resourceHelper.gs(R.string.format_bg_isf), BgReading().value(Profile.toMgdl(bg, profileFunction.getUnits())).valueToUnitsToString(profileFunction.getUnits()), wizard.sens)
treatments_wizard_bginsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromBG)
2020-04-24 12:00:31 +02:00
treatments_wizard_carbs.text = String.format(resourceHelper.gs(R.string.format_carbs_ic), carbs.toDouble(), wizard.ic)
treatments_wizard_carbsinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromCarbs)
2020-04-24 12:00:31 +02:00
treatments_wizard_bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromBolusIOB)
treatments_wizard_basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromBasalsIOB)
2020-04-24 12:00:31 +02:00
treatments_wizard_correctioninsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromCorrection)
// Superbolus
2020-04-24 12:00:31 +02:00
treatments_wizard_sb.text = if (treatments_wizard_sbcheckbox.isChecked) resourceHelper.gs(R.string.twohours) else ""
treatments_wizard_sbinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromSuperBolus)
// Trend
if (treatments_wizard_bgtrendcheckbox.isChecked && wizard.glucoseStatus != null) {
treatments_wizard_bgtrend.text = ((if (wizard.trend > 0) "+" else "")
2020-04-24 12:00:31 +02:00
+ Profile.toUnitsString(wizard.trend * 3, wizard.trend * 3 / Constants.MMOLL_TO_MGDL, profileFunction.getUnits())
+ " " + profileFunction.getUnits())
} else {
treatments_wizard_bgtrend.text = ""
}
2020-04-24 12:00:31 +02:00
treatments_wizard_bgtrendinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromTrend)
// COB
if (treatments_wizard_cobcheckbox.isChecked) {
2020-04-24 12:00:31 +02:00
treatments_wizard_cob.text = String.format(resourceHelper.gs(R.string.format_cob_ic), cob, wizard.ic)
treatments_wizard_cobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromCOB)
} else {
treatments_wizard_cob.text = ""
treatments_wizard_cobinsulin.text = ""
}
if (wizard.calculatedTotalInsulin > 0.0 || carbsAfterConstraint > 0.0) {
2020-04-24 12:00:31 +02:00
val insulinText = if (wizard.calculatedTotalInsulin > 0.0) resourceHelper.gs(R.string.formatinsulinunits, wizard.calculatedTotalInsulin) else ""
val carbsText = if (carbsAfterConstraint > 0.0) resourceHelper.gs(R.string.format_carbs, carbsAfterConstraint) else ""
treatments_wizard_total.text = resourceHelper.gs(R.string.result_insulin_carbs, insulinText, carbsText)
ok.visibility = View.VISIBLE
} else {
2020-04-24 12:00:31 +02:00
treatments_wizard_total.text = resourceHelper.gs(R.string.missing_carbs, wizard.carbsEquivalent.toInt())
ok.visibility = View.INVISIBLE
}
}
}
}