Bolus advisor

This commit is contained in:
Milos Kozak 2020-12-28 16:42:00 +01:00
parent 4238e9cab3
commit c8f7dae2c8
17 changed files with 602 additions and 310 deletions

View file

@ -18,6 +18,7 @@ import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.databinding.DialogWizardBinding
import info.nightscout.androidaps.db.BgReading
import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.Constraint
@ -39,7 +40,6 @@ import info.nightscout.androidaps.utils.sharedPreferences.SP
import info.nightscout.androidaps.utils.wizard.BolusWizard
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.dialog_wizard.*
import java.text.DecimalFormat
import java.util.*
import javax.inject.Inject
@ -72,8 +72,23 @@ class WizardDialog : DaggerDialogFragment() {
}
}
private val timeTextWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
calculateInsulin()
binding.alarm.isChecked = binding.carbTimeInput.value > 0
}
}
private var disposable: CompositeDisposable = CompositeDisposable()
private var _binding: DialogWizardBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onStart() {
super.onStart()
dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
@ -81,49 +96,50 @@ class WizardDialog : DaggerDialogFragment() {
override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState)
savedInstanceState.putDouble("treatments_wizard_bg_input", treatments_wizard_bg_input.value)
savedInstanceState.putDouble("treatments_wizard_carbs_input", treatments_wizard_carbs_input.value)
savedInstanceState.putDouble("treatments_wizard_correction_input", treatments_wizard_correction_input.value)
savedInstanceState.putDouble("treatments_wizard_carb_time_input", treatments_wizard_carb_time_input.value)
savedInstanceState.putDouble("bg_input", binding.bgInput.value)
savedInstanceState.putDouble("carbs_input", binding.carbsInput.value)
savedInstanceState.putDouble("correction_input", binding.correctionInput.value)
savedInstanceState.putDouble("carb_time_input", binding.carbTimeInput.value)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
savedInstanceState: Bundle?): View {
dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE)
dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
isCancelable = true
dialog?.setCanceledOnTouchOutside(false)
return inflater.inflate(R.layout.dialog_wizard, container, false)
_binding = DialogWizardBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
loadCheckedStates()
processCobCheckBox()
treatments_wizard_sbcheckbox.visibility = sp.getBoolean(R.string.key_usesuperbolus, false).toVisibility()
treatments_wizard_notes_layout.visibility = sp.getBoolean(R.string.key_show_notes_entry_dialogs, false).toVisibility()
binding.sbcheckbox.visibility = sp.getBoolean(R.string.key_usesuperbolus, false).toVisibility()
binding.notesLayout.visibility = sp.getBoolean(R.string.key_show_notes_entry_dialogs, false).toVisibility()
val maxCarbs = constraintChecker.getMaxCarbsAllowed().value()
val maxCorrection = constraintChecker.getMaxBolusAllowed().value()
if (profileFunction.getUnits() == Constants.MGDL)
treatments_wizard_bg_input.setParams(savedInstanceState?.getDouble("treatments_wizard_bg_input")
?: 0.0, 0.0, 500.0, 1.0, DecimalFormat("0"), false, ok, textWatcher)
binding.bgInput.setParams(savedInstanceState?.getDouble("bg_input")
?: 0.0, 0.0, 500.0, 1.0, DecimalFormat("0"), false, binding.ok, timeTextWatcher)
else
treatments_wizard_bg_input.setParams(savedInstanceState?.getDouble("treatments_wizard_bg_input")
?: 0.0, 0.0, 30.0, 0.1, DecimalFormat("0.0"), false, ok, textWatcher)
treatments_wizard_carbs_input.setParams(savedInstanceState?.getDouble("treatments_wizard_carbs_input")
?: 0.0, 0.0, maxCarbs.toDouble(), 1.0, DecimalFormat("0"), false, ok, textWatcher)
binding.bgInput.setParams(savedInstanceState?.getDouble("bg_input")
?: 0.0, 0.0, 30.0, 0.1, DecimalFormat("0.0"), false, binding.ok, textWatcher)
binding.carbsInput.setParams(savedInstanceState?.getDouble("carbs_input")
?: 0.0, 0.0, maxCarbs.toDouble(), 1.0, DecimalFormat("0"), false, binding.ok, textWatcher)
val bolusStep = activePlugin.activePump.pumpDescription.bolusStep
treatments_wizard_correction_input.setParams(savedInstanceState?.getDouble("treatments_wizard_correction_input")
?: 0.0, -maxCorrection, maxCorrection, bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, ok, textWatcher)
treatments_wizard_carb_time_input.setParams(savedInstanceState?.getDouble("treatments_wizard_carb_time_input")
?: 0.0, -60.0, 60.0, 5.0, DecimalFormat("0"), false, ok, textWatcher)
binding.correctionInput.setParams(savedInstanceState?.getDouble("correction_input")
?: 0.0, -maxCorrection, maxCorrection, bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, binding.ok, textWatcher)
binding.carbTimeInput.setParams(savedInstanceState?.getDouble("carb_time_input")
?: 0.0, -60.0, 60.0, 5.0, DecimalFormat("0"), false, binding.ok, timeTextWatcher)
initDialog()
treatments_wizard_percent_used.text = resourceHelper.gs(R.string.format_percent, sp.getInt(R.string.key_boluswizard_percentage, 100))
binding.percentUsed.text = resourceHelper.gs(R.string.format_percent, sp.getInt(R.string.key_boluswizard_percentage, 100))
// ok button
ok.setOnClickListener {
binding.ok.setOnClickListener {
if (okClicked) {
aapsLogger.debug(LTag.UI, "guarding: ok already clicked")
} else {
@ -136,37 +152,37 @@ class WizardDialog : DaggerDialogFragment() {
dismiss()
}
// cancel button
cancel.setOnClickListener { dismiss() }
binding.cancel.setOnClickListener { dismiss() }
// checkboxes
treatments_wizard_bgcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
treatments_wizard_ttcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
treatments_wizard_cobcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
treatments_wizard_basaliobcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
treatments_wizard_bolusiobcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
treatments_wizard_bgtrendcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
treatments_wizard_sbcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
binding.bgcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
binding.ttcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
binding.cobcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
binding.basaliobcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
binding.bolusiobcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
binding.bgtrendcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
binding.sbcheckbox.setOnCheckedChangeListener(::onCheckedChanged)
val showCalc = sp.getBoolean(R.string.key_wizard_calculation_visible, false)
treatments_wizard_delimiter.visibility = showCalc.toVisibility()
treatments_wizard_resulttable.visibility = showCalc.toVisibility()
treatments_wizard_calculationcheckbox.isChecked = showCalc
treatments_wizard_calculationcheckbox.setOnCheckedChangeListener { _, isChecked ->
binding.delimiter.visibility = showCalc.toVisibility()
binding.resulttable.visibility = showCalc.toVisibility()
binding.calculationcheckbox.isChecked = showCalc
binding.calculationcheckbox.setOnCheckedChangeListener { _, isChecked ->
run {
sp.putBoolean(resourceHelper.gs(R.string.key_wizard_calculation_visible), isChecked)
treatments_wizard_delimiter.visibility = isChecked.toVisibility()
treatments_wizard_resulttable.visibility = isChecked.toVisibility()
binding.delimiter.visibility = isChecked.toVisibility()
binding.resulttable.visibility = isChecked.toVisibility()
}
}
// profile spinner
treatments_wizard_profile.onItemSelectedListener = object : OnItemSelectedListener {
binding.profile.onItemSelectedListener = object : OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {
ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.noprofileselected))
ok.visibility = View.GONE
binding.ok.visibility = View.GONE
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
calculateInsulin()
ok.visibility = View.VISIBLE
binding.ok.visibility = View.VISIBLE
}
}
// bus
@ -183,36 +199,37 @@ class WizardDialog : DaggerDialogFragment() {
override fun onDestroyView() {
super.onDestroyView()
disposable.clear()
_binding = null
}
private fun onCheckedChanged(buttonView: CompoundButton, @Suppress("UNUSED_PARAMETER") state: Boolean) {
saveCheckedStates()
treatments_wizard_ttcheckbox.isEnabled = treatments_wizard_bgcheckbox.isChecked && treatmentsPlugin.tempTargetFromHistory != null
if (buttonView.id == treatments_wizard_cobcheckbox.id)
binding.ttcheckbox.isEnabled = binding.bgcheckbox.isChecked && treatmentsPlugin.tempTargetFromHistory != null
if (buttonView.id == binding.cobcheckbox.id)
processCobCheckBox()
calculateInsulin()
}
private fun processCobCheckBox() {
if (treatments_wizard_cobcheckbox.isChecked) {
treatments_wizard_bolusiobcheckbox.isEnabled = false
treatments_wizard_basaliobcheckbox.isEnabled = false
treatments_wizard_bolusiobcheckbox.isChecked = true
treatments_wizard_basaliobcheckbox.isChecked = true
if (binding.cobcheckbox.isChecked) {
binding.bolusiobcheckbox.isEnabled = false
binding.basaliobcheckbox.isEnabled = false
binding.bolusiobcheckbox.isChecked = true
binding.basaliobcheckbox.isChecked = true
} else {
treatments_wizard_bolusiobcheckbox.isEnabled = true
treatments_wizard_basaliobcheckbox.isEnabled = true
binding.bolusiobcheckbox.isEnabled = true
binding.basaliobcheckbox.isEnabled = true
}
}
private fun saveCheckedStates() {
sp.putBoolean(resourceHelper.gs(R.string.key_wizard_include_cob), treatments_wizard_cobcheckbox.isChecked)
sp.putBoolean(resourceHelper.gs(R.string.key_wizard_include_trend_bg), treatments_wizard_bgtrendcheckbox.isChecked)
sp.putBoolean(R.string.key_wizard_include_cob, binding.cobcheckbox.isChecked)
sp.putBoolean(R.string.key_wizard_include_trend_bg, binding.bgtrendcheckbox.isChecked)
}
private fun loadCheckedStates() {
treatments_wizard_bgtrendcheckbox.isChecked = sp.getBoolean(R.string.key_wizard_include_trend_bg, false)
treatments_wizard_cobcheckbox.isChecked = sp.getBoolean(R.string.key_wizard_include_cob, false)
binding.bgtrendcheckbox.isChecked = sp.getBoolean(R.string.key_wizard_include_trend_bg, false)
binding.cobcheckbox.isChecked = sp.getBoolean(R.string.key_wizard_include_cob, false)
}
private fun initDialog() {
@ -230,25 +247,25 @@ class WizardDialog : DaggerDialogFragment() {
profileList.add(0, resourceHelper.gs(R.string.active))
context?.let { context ->
val adapter = ArrayAdapter(context, R.layout.spinner_centered, profileList)
treatments_wizard_profile.adapter = adapter
binding.profile.adapter = adapter
} ?: return
val units = profileFunction.getUnits()
treatments_wizard_bgunits.text = units
binding.bgunits.text = units
if (units == Constants.MGDL)
treatments_wizard_bg_input.setStep(1.0)
binding.bgInput.setStep(1.0)
else
treatments_wizard_bg_input.setStep(0.1)
binding.bgInput.setStep(0.1)
// Set BG if not old
val lastBg = iobCobCalculatorPlugin.actualBg()
if (lastBg != null) {
treatments_wizard_bg_input.value = lastBg.valueToUnits(units)
binding.bgInput.value = lastBg.valueToUnits(units)
} else {
treatments_wizard_bg_input.value = 0.0
binding.bgInput.value = 0.0
}
treatments_wizard_ttcheckbox.isEnabled = treatmentsPlugin.tempTargetFromHistory != null
binding.ttcheckbox.isEnabled = treatmentsPlugin.tempTargetFromHistory != null
// IOB calculation
treatmentsPlugin.updateTotalIOBTreatments()
@ -256,19 +273,19 @@ class WizardDialog : DaggerDialogFragment() {
treatmentsPlugin.updateTotalIOBTempBasals()
val basalIob = treatmentsPlugin.lastCalculationTempBasals.round()
treatments_wizard_bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, -bolusIob.iob)
treatments_wizard_basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, -basalIob.basaliob)
binding.bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, -bolusIob.iob)
binding.basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, -basalIob.basaliob)
calculateInsulin()
treatments_wizard_percent_used.visibility = (sp.getInt(R.string.key_boluswizard_percentage, 100) != 100).toVisibility()
binding.percentUsed.visibility = (sp.getInt(R.string.key_boluswizard_percentage, 100) != 100).toVisibility()
}
private fun calculateInsulin() {
val profileStore = activePlugin.activeProfileInterface.profile
if (treatments_wizard_profile?.selectedItem == null || profileStore == null)
if (binding.profile.selectedItem == null || profileStore == null)
return // not initialized yet
var profileName = treatments_wizard_profile.selectedItem.toString()
var profileName = binding.profile.selectedItem.toString()
val specificProfile: Profile?
if (profileName == resourceHelper.gs(R.string.active)) {
specificProfile = profileFunction.getProfile()
@ -279,82 +296,83 @@ class WizardDialog : DaggerDialogFragment() {
if (specificProfile == null) return
// Entered values
var bg = SafeParse.stringToDouble(treatments_wizard_bg_input.text)
val carbs = SafeParse.stringToInt(treatments_wizard_carbs_input.text)
val correction = SafeParse.stringToDouble(treatments_wizard_correction_input.text)
var bg = SafeParse.stringToDouble(binding.bgInput.text)
val carbs = SafeParse.stringToInt(binding.carbsInput.text)
val correction = SafeParse.stringToDouble(binding.correctionInput.text)
val carbsAfterConstraint = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value()
if (abs(carbs - carbsAfterConstraint) > 0.01) {
treatments_wizard_carbs_input.value = 0.0
binding.carbsInput.value = 0.0
ToastUtils.showToastInUiThread(mainApp, resourceHelper.gs(R.string.carbsconstraintapplied))
return
}
bg = if (treatments_wizard_bgcheckbox.isChecked) bg else 0.0
val tempTarget = if (treatments_wizard_ttcheckbox.isChecked) treatmentsPlugin.tempTargetFromHistory else null
bg = if (binding.bgcheckbox.isChecked) bg else 0.0
val tempTarget = if (binding.ttcheckbox.isChecked) treatmentsPlugin.tempTargetFromHistory else null
// COB
var cob = 0.0
if (treatments_wizard_cobcheckbox.isChecked) {
if (binding.cobcheckbox.isChecked) {
val cobInfo = iobCobCalculatorPlugin.getCobInfo(false, "Wizard COB")
cobInfo.displayCob?.let { cob = it }
}
val carbTime = SafeParse.stringToInt(treatments_wizard_carb_time_input.text)
val carbTime = SafeParse.stringToInt(binding.carbTimeInput.text)
wizard = BolusWizard(mainApp).doCalc(specificProfile, profileName, tempTarget, carbsAfterConstraint, cob, bg, correction,
sp.getInt(R.string.key_boluswizard_percentage, 100).toDouble(),
treatments_wizard_bgcheckbox.isChecked,
treatments_wizard_cobcheckbox.isChecked,
treatments_wizard_bolusiobcheckbox.isChecked,
treatments_wizard_basaliobcheckbox.isChecked,
treatments_wizard_sbcheckbox.isChecked,
treatments_wizard_ttcheckbox.isChecked,
treatments_wizard_bgtrendcheckbox.isChecked,
treatment_wizard_notes.text.toString(), carbTime)
binding.bgcheckbox.isChecked,
binding.cobcheckbox.isChecked,
binding.bolusiobcheckbox.isChecked,
binding.basaliobcheckbox.isChecked,
binding.sbcheckbox.isChecked,
binding.ttcheckbox.isChecked,
binding.bgtrendcheckbox.isChecked,
binding.alarm.isChecked,
binding.notes.text.toString(), carbTime)
wizard?.let { wizard ->
treatments_wizard_bg.text = String.format(resourceHelper.gs(R.string.format_bg_isf), BgReading().value(Profile.toMgdl(bg, profileFunction.getUnits())).valueToUnitsToString(profileFunction.getUnits()), wizard.sens)
treatments_wizard_bginsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromBG)
binding.bg.text = String.format(resourceHelper.gs(R.string.format_bg_isf), BgReading().value(Profile.toMgdl(bg, profileFunction.getUnits())).valueToUnitsToString(profileFunction.getUnits()), wizard.sens)
binding.bginsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromBG)
treatments_wizard_carbs.text = String.format(resourceHelper.gs(R.string.format_carbs_ic), carbs.toDouble(), wizard.ic)
treatments_wizard_carbsinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromCarbs)
binding.carbs.text = String.format(resourceHelper.gs(R.string.format_carbs_ic), carbs.toDouble(), wizard.ic)
binding.carbsinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromCarbs)
treatments_wizard_bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromBolusIOB)
treatments_wizard_basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromBasalsIOB)
binding.bolusiobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromBolusIOB)
binding.basaliobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromBasalIOB)
treatments_wizard_correctioninsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromCorrection)
binding.correctioninsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromCorrection)
// Superbolus
treatments_wizard_sb.text = if (treatments_wizard_sbcheckbox.isChecked) resourceHelper.gs(R.string.twohours) else ""
treatments_wizard_sbinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromSuperBolus)
binding.sb.text = if (binding.sbcheckbox.isChecked) resourceHelper.gs(R.string.twohours) else ""
binding.sbinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromSuperBolus)
// Trend
if (treatments_wizard_bgtrendcheckbox.isChecked && wizard.glucoseStatus != null) {
treatments_wizard_bgtrend.text = ((if (wizard.trend > 0) "+" else "")
if (binding.bgtrendcheckbox.isChecked && wizard.glucoseStatus != null) {
binding.bgtrend.text = ((if (wizard.trend > 0) "+" else "")
+ Profile.toUnitsString(wizard.trend * 3, wizard.trend * 3 / Constants.MMOLL_TO_MGDL, profileFunction.getUnits())
+ " " + profileFunction.getUnits())
} else {
treatments_wizard_bgtrend.text = ""
binding.bgtrend.text = ""
}
treatments_wizard_bgtrendinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromTrend)
binding.bgtrendinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromTrend)
// COB
if (treatments_wizard_cobcheckbox.isChecked) {
treatments_wizard_cob.text = String.format(resourceHelper.gs(R.string.format_cob_ic), cob, wizard.ic)
treatments_wizard_cobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromCOB)
if (binding.cobcheckbox.isChecked) {
binding.cob.text = String.format(resourceHelper.gs(R.string.format_cob_ic), cob, wizard.ic)
binding.cobinsulin.text = resourceHelper.gs(R.string.formatinsulinunits, wizard.insulinFromCOB)
} else {
treatments_wizard_cob.text = ""
treatments_wizard_cobinsulin.text = ""
binding.cob.text = ""
binding.cobinsulin.text = ""
}
if (wizard.calculatedTotalInsulin > 0.0 || carbsAfterConstraint > 0.0) {
val insulinText = if (wizard.calculatedTotalInsulin > 0.0) resourceHelper.gs(R.string.formatinsulinunits, wizard.calculatedTotalInsulin) else ""
val carbsText = if (carbsAfterConstraint > 0.0) resourceHelper.gs(R.string.format_carbs, carbsAfterConstraint) else ""
treatments_wizard_total.text = resourceHelper.gs(R.string.result_insulin_carbs, insulinText, carbsText)
ok.visibility = View.VISIBLE
binding.total.text = resourceHelper.gs(R.string.result_insulin_carbs, insulinText, carbsText)
binding.ok.visibility = View.VISIBLE
} else {
treatments_wizard_total.text = resourceHelper.gs(R.string.missing_carbs, wizard.carbsEquivalent.toInt())
ok.visibility = View.INVISIBLE
binding.total.text = resourceHelper.gs(R.string.missing_carbs, wizard.carbsEquivalent.toInt())
binding.ok.visibility = View.INVISIBLE
}
}

View file

@ -15,8 +15,8 @@ import androidx.annotation.DrawableRes
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import dagger.android.HasAndroidInjector
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.databinding.AutomationEventItemBinding
import info.nightscout.androidaps.databinding.AutomationFragmentBinding
@ -46,7 +46,7 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener {
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var automationPlugin: AutomationPlugin
@Inject lateinit var mainApp: MainApp
@Inject lateinit var injector: HasAndroidInjector
private var disposable: CompositeDisposable = CompositeDisposable()
private lateinit var eventListAdapter: EventListAdapter
@ -76,7 +76,7 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener {
binding.fabAddEvent.setOnClickListener {
val dialog = EditEventDialog()
val args = Bundle()
args.putString("event", AutomationEvent(mainApp).toJSON())
args.putString("event", AutomationEvent(injector).toJSON())
args.putInt("position", -1) // New event
dialog.arguments = args
dialog.show(childFragmentManager, "EditEventDialog")
@ -159,7 +159,7 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener {
@SuppressLint("ClickableViewAccessibility")
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val event = automationPlugin.automationEvents[position]
val event = automationPlugin.at(position)
holder.binding.eventTitle.text = event.title
holder.binding.enabled.isChecked = event.isEnabled
holder.binding.enabled.isEnabled = !event.readOnly
@ -190,7 +190,6 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener {
rxBus.send(EventAutomationDataChanged())
}
// edit event
if (!event.readOnly)
holder.binding.rootLayout.setOnClickListener {
val dialog = EditEventDialog()
val args = Bundle()
@ -209,36 +208,33 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener {
}
// remove event
holder.binding.iconTrash.setOnClickListener {
showConfirmation(requireContext(), resourceHelper.gs(R.string.removerecord) + " " + automationPlugin.automationEvents[position].title,
showConfirmation(requireContext(), resourceHelper.gs(R.string.removerecord) + " " + automationPlugin.at(position).title,
Runnable {
automationPlugin.automationEvents.removeAt(position)
automationPlugin.removeAt(position)
notifyItemRemoved(position)
rxBus.send(EventAutomationDataChanged())
rxBus.send(EventAutomationUpdateGui())
}, Runnable {
rxBus.send(EventAutomationUpdateGui())
})
}
holder.binding.iconTrash.visibility = (!event.readOnly).toVisibility()
holder.binding.aapsLogo.visibility = (event.systemAction).toVisibility()
}
override fun getItemCount(): Int = automationPlugin.automationEvents.size
override fun getItemCount(): Int = automationPlugin.size()
override fun onItemMove(fromPosition: Int, toPosition: Int): Boolean {
Collections.swap(automationPlugin.automationEvents, fromPosition, toPosition)
automationPlugin.swap(fromPosition, toPosition)
notifyItemMoved(fromPosition, toPosition)
rxBus.send(EventAutomationDataChanged())
return true
}
override fun onItemDismiss(position: Int) {
activity?.let { activity ->
showConfirmation(activity, resourceHelper.gs(R.string.removerecord) + " " + automationPlugin.automationEvents[position].title,
showConfirmation(activity, resourceHelper.gs(R.string.removerecord) + " " + automationPlugin.at(position).title,
Runnable {
automationPlugin.automationEvents.removeAt(position)
automationPlugin.removeAt(position)
notifyItemRemoved(position)
rxBus.send(EventAutomationDataChanged())
rxBus.send(EventAutomationUpdateGui())
}, Runnable { rxBus.send(EventAutomationUpdateGui()) })
}
}

View file

@ -39,8 +39,10 @@ import io.reactivex.schedulers.Schedulers
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import java.util.*
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.collections.ArrayList
@Singleton
class AutomationPlugin @Inject constructor(
@ -69,7 +71,7 @@ class AutomationPlugin @Inject constructor(
private val keyAutomationEvents = "AUTOMATION_EVENTS"
val automationEvents = ArrayList<AutomationEvent>()
private val automationEvents = ArrayList<AutomationEvent>()
var executionLog: MutableList<String> = ArrayList()
var btConnects: MutableList<EventBTChange> = ArrayList()
@ -77,6 +79,7 @@ class AutomationPlugin @Inject constructor(
private lateinit var refreshLoop: Runnable
companion object {
const val event = "{\"title\":\"Low\",\"enabled\":true,\"trigger\":\"{\\\"type\\\":\\\"info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector\\\",\\\"data\\\":{\\\"connectorType\\\":\\\"AND\\\",\\\"triggerList\\\":[\\\"{\\\\\\\"type\\\\\\\":\\\\\\\"info.nightscout.androidaps.plugins.general.automation.triggers.TriggerBg\\\\\\\",\\\\\\\"data\\\\\\\":{\\\\\\\"bg\\\\\\\":4,\\\\\\\"comparator\\\\\\\":\\\\\\\"IS_LESSER\\\\\\\",\\\\\\\"units\\\\\\\":\\\\\\\"mmol\\\\\\\"}}\\\",\\\"{\\\\\\\"type\\\\\\\":\\\\\\\"info.nightscout.androidaps.plugins.general.automation.triggers.TriggerDelta\\\\\\\",\\\\\\\"data\\\\\\\":{\\\\\\\"value\\\\\\\":-0.1,\\\\\\\"units\\\\\\\":\\\\\\\"mmol\\\\\\\",\\\\\\\"deltaType\\\\\\\":\\\\\\\"DELTA\\\\\\\",\\\\\\\"comparator\\\\\\\":\\\\\\\"IS_LESSER\\\\\\\"}}\\\"]}}\",\"actions\":[\"{\\\"type\\\":\\\"info.nightscout.androidaps.plugins.general.automation.actions.ActionStartTempTarget\\\",\\\"data\\\":{\\\"value\\\":8,\\\"units\\\":\\\"mmol\\\",\\\"durationInMinutes\\\":60}}\"]}"
}
@ -184,20 +187,22 @@ class AutomationPlugin @Inject constructor(
@Synchronized
private fun processActions() {
var userEventsEnabled = true
if (loopPlugin.isSuspended || !loopPlugin.isEnabled()) {
aapsLogger.debug(LTag.AUTOMATION, "Loop deactivated")
executionLog.add(resourceHelper.gs(R.string.smscommunicator_loopisdisabled))
return
userEventsEnabled = false
}
val enabled = constraintChecker.isAutomationEnabled()
if (!enabled.value()) {
executionLog.add(enabled.getMostLimitedReasons(aapsLogger))
return
userEventsEnabled = false
}
aapsLogger.debug(LTag.AUTOMATION, "processActions")
for (event in automationEvents) {
if (event.isEnabled && event.shouldRun() && event.trigger.shouldRun() && event.getPreconditions().shouldRun()) {
if (event.systemAction || userEventsEnabled) {
val actions = event.actions
for (action in actions) {
action.doAction(object : Callback() {
@ -220,6 +225,8 @@ class AutomationPlugin @Inject constructor(
}
SystemClock.sleep(1100)
event.lastRun = DateUtil.now()
if (event.autoRemove) automationEvents.remove(event)
}
}
}
// we cannot detect connected BT devices
@ -231,6 +238,38 @@ class AutomationPlugin @Inject constructor(
storeToSP() // save last run time
}
fun add(event: AutomationEvent) {
automationEvents.add(event)
rxBus.send(EventAutomationDataChanged())
}
fun addIfNotExists(event: AutomationEvent) {
for (e in automationEvents) {
if (event.title == e.title) return
}
automationEvents.add(event)
rxBus.send(EventAutomationDataChanged())
}
fun set(event: AutomationEvent, index: Int) {
automationEvents[index] = event
rxBus.send(EventAutomationDataChanged())
}
fun removeAt(index: Int) {
automationEvents.removeAt(index)
rxBus.send(EventAutomationDataChanged())
}
fun at(index: Int) = automationEvents[index]
fun size() = automationEvents.size
fun swap(fromPosition: Int, toPosition: Int) {
Collections.swap(automationEvents, fromPosition, toPosition)
rxBus.send(EventAutomationDataChanged())
}
fun getActionDummyObjects(): List<Action> {
return listOf(
//ActionLoopDisable(injector),

View file

@ -33,6 +33,9 @@ class ActionAlarm(injector: HasAndroidInjector) : Action(injector) {
var text = InputString(injector)
constructor(injector: HasAndroidInjector, text: String) : this(injector) {
this.text = InputString(injector, text)
}
override fun friendlyName(): Int = R.string.alarm
override fun shortDescription(): String = resourceHelper.gs(R.string.alarm_message, text.value)
@DrawableRes override fun icon(): Int = R.drawable.ic_access_alarm_24dp

View file

@ -26,6 +26,7 @@ import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerCon
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.extensions.plusAssign
import info.nightscout.androidaps.utils.extensions.toVisibility
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import javax.inject.Inject
@ -66,9 +67,13 @@ class EditEventDialog : DialogFragmentWithDate() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.okcancel.ok.visibility = (!event.readOnly).toVisibility()
binding.inputEventTitle.setText(event.title)
binding.inputEventTitle.isFocusable = false
binding.triggerDescription.text = event.trigger.friendlyDescription()
binding.editTrigger.visibility = (!event.readOnly).toVisibility()
binding.editTrigger.setOnClickListener {
val args = Bundle()
args.putString("trigger", event.trigger.toJSON())
@ -82,6 +87,7 @@ class EditEventDialog : DialogFragmentWithDate() {
binding.actionListView.layoutManager = LinearLayoutManager(context)
binding.actionListView.adapter = actionListAdapter
binding.addAction.visibility = (!event.readOnly).toVisibility()
binding.addAction.setOnClickListener { ChooseActionDialog().show(childFragmentManager, "ChooseActionDialog") }
showPreconditions()
@ -140,9 +146,9 @@ class EditEventDialog : DialogFragmentWithDate() {
}
// store
if (position == -1)
automationPlugin.automationEvents.add(event)
automationPlugin.add(event)
else
automationPlugin.automationEvents[position] = event
automationPlugin.set(event, position)
rxBus.send(EventAutomationDataChanged())
return true
@ -189,6 +195,7 @@ class EditEventDialog : DialogFragmentWithDate() {
inner class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
fun bind(action: Action, recyclerView: RecyclerView.Adapter<ViewHolder>, position: Int) {
if (!event.readOnly)
view.findViewById<LinearLayout>(R.id.automation_layoutText).setOnClickListener {
if (action.hasDialog()) {
val args = Bundle()
@ -199,11 +206,14 @@ class EditEventDialog : DialogFragmentWithDate() {
dialog.show(childFragmentManager, "EditActionDialog")
}
}
view.findViewById<ImageView>(R.id.automation_iconTrash).setOnClickListener {
view.findViewById<ImageView>(R.id.automation_iconTrash).run {
visibility = (!event.readOnly).toVisibility()
setOnClickListener {
event.actions.remove(action)
recyclerView.notifyDataSetChanged()
rxBus.send(EventAutomationUpdateGui())
}
}
view.findViewById<ImageView>(R.id.automation_action_image).setImageResource(action.icon())
view.findViewById<TextView>(R.id.automation_viewActionTitle).text = action.shortDescription()
}

View file

@ -35,6 +35,12 @@ class TriggerDelta(injector: HasAndroidInjector) : Trigger(injector) {
else InputDelta(injector, 0.0, (-MGDL_MAX), MGDL_MAX, 1.0, DecimalFormat("1"), DeltaType.DELTA)
}
constructor(injector: HasAndroidInjector, inputDelta: InputDelta, units: String, comparator: Comparator.Compare) : this(injector) {
this.units = units
this.delta = inputDelta
this.comparator.value = comparator
}
private constructor(injector: HasAndroidInjector, triggerDelta: TriggerDelta) : this(injector) {
units = triggerDelta.units
delta = InputDelta(injector, triggerDelta.delta)

View file

@ -195,7 +195,7 @@ class ActionStringHandler @Inject constructor(
val formatInt = DecimalFormat("0")
val bolusWizard = BolusWizard(injector).doCalc(profile, profileName, activePlugin.activeTreatments.tempTargetFromHistory,
carbsAfterConstraints, cobInfo.displayCob!!, bgReading!!.valueToUnits(profileFunction.getUnits()),
0.0, percentage.toDouble(), useBG, useCOB, useBolusIOB, useBasalIOB, false, useTT, useTrend)
0.0, percentage.toDouble(), useBG, useCOB, useBolusIOB, useBasalIOB, false, useTT, useTrend, false)
if (Math.abs(bolusWizard.insulinAfterConstraints - bolusWizard.calculatedTotalInsulin) >= 0.01) {
sendError("Insulin constraint violation!" +
"\nCannot deliver " + format.format(bolusWizard.calculatedTotalInsulin) + "!")
@ -215,7 +215,7 @@ class ActionStringHandler @Inject constructor(
if (useCOB) rMessage += "\nFrom" + formatInt.format(cobInfo.displayCob) + "g COB : " + format.format(bolusWizard.insulinFromCOB) + "U"
if (useBG) rMessage += "\nFrom BG: " + format.format(bolusWizard.insulinFromBG) + "U"
if (useBolusIOB) rMessage += "\nBolus IOB: " + format.format(bolusWizard.insulinFromBolusIOB) + "U"
if (useBasalIOB) rMessage += "\nBasal IOB: " + format.format(bolusWizard.insulinFromBasalsIOB) + "U"
if (useBasalIOB) rMessage += "\nBasal IOB: " + format.format(bolusWizard.insulinFromBasalIOB) + "U"
if (useTrend) rMessage += "\nFrom 15' trend: " + format.format(bolusWizard.insulinFromTrend) + "U"
if (percentage != 100) {
rMessage += "\nPercentage: " + format.format(bolusWizard.totalBeforePercentageAdjustment) + "U * " + percentage + "% -> ~" + format.format(bolusWizard.calculatedTotalInsulin) + "U"

View file

@ -6,6 +6,7 @@ import android.text.Spanned
import com.google.common.base.Joiner
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.Config
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.data.DetailedBolusInfo
@ -20,6 +21,15 @@ import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.general.automation.AutomationEvent
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin
import info.nightscout.androidaps.plugins.general.automation.actions.ActionAlarm
import info.nightscout.androidaps.plugins.general.automation.elements.Comparator
import info.nightscout.androidaps.plugins.general.automation.elements.InputDelta
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerBg
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerDelta
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerTime
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.queue.Callback
@ -30,8 +40,10 @@ import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.formatColor
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import org.json.JSONException
import org.json.JSONObject
import java.text.DecimalFormat
import java.util.*
import javax.inject.Inject
import kotlin.math.abs
@ -43,13 +55,15 @@ class BolusWizard @Inject constructor(
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var sp: SP
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var context: Context
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var loopPlugin: LoopPlugin
@Inject lateinit var iobCobCalculatorPlugin: IobCobCalculatorPlugin
@Inject lateinit var automationPlugin: AutomationPlugin
@Inject lateinit var dateUtil: DateUtil
@Inject lateinit var config: Config
init {
@ -72,7 +86,7 @@ class BolusWizard @Inject constructor(
private set
var insulinFromBolusIOB = 0.0
private set
var insulinFromBasalsIOB = 0.0
var insulinFromBasalIOB = 0.0
private set
var insulinFromCorrection = 0.0
private set
@ -104,7 +118,7 @@ class BolusWizard @Inject constructor(
var carbs: Int = 0
var cob: Double = 0.0
var bg: Double = 0.0
var correction: Double = 0.0
private var correction: Double = 0.0
private var percentageCorrection: Double = 0.0
private var useBg: Boolean = false
private var useCob: Boolean = false
@ -113,8 +127,9 @@ class BolusWizard @Inject constructor(
private var useSuperBolus: Boolean = false
private var useTT: Boolean = false
private var useTrend: Boolean = false
private var useAlarm = false
var notes: String = ""
var carbTime: Int = 0
private var carbTime: Int = 0
@JvmOverloads
fun doCalc(profile: Profile,
@ -132,6 +147,7 @@ class BolusWizard @Inject constructor(
useSuperBolus: Boolean,
useTT: Boolean,
useTrend: Boolean,
useAlarm: Boolean,
notes: String = "",
carbTime: Int = 0
): BolusWizard {
@ -151,6 +167,7 @@ class BolusWizard @Inject constructor(
this.useSuperBolus = useSuperBolus
this.useTT = useTT
this.useTrend = useTrend
this.useAlarm = useAlarm
this.notes = notes
this.carbTime = carbTime
@ -193,7 +210,7 @@ class BolusWizard @Inject constructor(
val basalIob = activePlugin.activeTreatments.lastCalculationTempBasals.round()
insulinFromBolusIOB = if (includeBolusIOB) -bolusIob.iob else 0.0
insulinFromBasalsIOB = if (includeBasalIOB) -basalIob.basaliob else 0.0
insulinFromBasalIOB = if (includeBasalIOB) -basalIob.basaliob else 0.0
// Insulin from correction
insulinFromCorrection = correction
@ -207,7 +224,7 @@ class BolusWizard @Inject constructor(
}
// Total
calculatedTotalInsulin = insulinFromBG + insulinFromTrend + insulinFromCarbs + insulinFromBolusIOB + insulinFromBasalsIOB + insulinFromCorrection + insulinFromSuperBolus + insulinFromCOB
calculatedTotalInsulin = insulinFromBG + insulinFromTrend + insulinFromCarbs + insulinFromBolusIOB + insulinFromBasalIOB + insulinFromCorrection + insulinFromSuperBolus + insulinFromCOB
// Percentage adjustment
totalBeforePercentageAdjustment = calculatedTotalInsulin
@ -229,6 +246,7 @@ class BolusWizard @Inject constructor(
return this
}
@Suppress("SpellCheckingInspection")
private fun nsJSON(): JSONObject {
val bolusCalcJSON = JSONObject()
try {
@ -239,9 +257,9 @@ class BolusWizard @Inject constructor(
bolusCalcJSON.put("targetBGHigh", targetBGHigh)
bolusCalcJSON.put("isf", sens)
bolusCalcJSON.put("ic", ic)
bolusCalcJSON.put("iob", -(insulinFromBolusIOB + insulinFromBasalsIOB))
bolusCalcJSON.put("iob", -(insulinFromBolusIOB + insulinFromBasalIOB))
bolusCalcJSON.put("bolusiob", insulinFromBolusIOB)
bolusCalcJSON.put("basaliob", insulinFromBasalsIOB)
bolusCalcJSON.put("basaliob", insulinFromBasalIOB)
bolusCalcJSON.put("bolusiobused", includeBolusIOB)
bolusCalcJSON.put("basaliobused", includeBasalIOB)
bolusCalcJSON.put("bg", bg)
@ -270,14 +288,14 @@ class BolusWizard @Inject constructor(
return bolusCalcJSON
}
private fun confirmMessageAfterConstraints(pump: PumpInterface): Spanned {
private fun confirmMessageAfterConstraints(advisor: Boolean): Spanned {
val actions: LinkedList<String> = LinkedList()
if (insulinAfterConstraints > 0) {
val pct = if (percentageCorrection != 100.0) " (" + percentageCorrection.toInt() + "%)" else ""
actions.add(resourceHelper.gs(R.string.bolus) + ": " + resourceHelper.gs(R.string.formatinsulinunits, insulinAfterConstraints).formatColor(resourceHelper, R.color.bolus) + pct)
}
if (carbs > 0) {
if (carbs > 0 && !advisor) {
var timeShift = ""
if (carbTime > 0) {
timeShift += " (+" + resourceHelper.gs(R.string.mins, carbTime) + ")"
@ -287,23 +305,24 @@ class BolusWizard @Inject constructor(
actions.add(resourceHelper.gs(R.string.carbs) + ": " + resourceHelper.gs(R.string.format_carbs, carbs).formatColor(resourceHelper, R.color.carbs) + timeShift)
}
if (insulinFromCOB > 0) {
actions.add(resourceHelper.gs(R.string.cobvsiob) + ": " + resourceHelper.gs(R.string.formatsignedinsulinunits, insulinFromBolusIOB + insulinFromBasalsIOB + insulinFromCOB + insulinFromBG).formatColor(resourceHelper, R.color.cobAlert))
actions.add(resourceHelper.gs(R.string.cobvsiob) + ": " + resourceHelper.gs(R.string.formatsignedinsulinunits, insulinFromBolusIOB + insulinFromBasalIOB + insulinFromCOB + insulinFromBG).formatColor(resourceHelper, R.color.cobAlert))
val absorptionRate = iobCobCalculatorPlugin.slowAbsorptionPercentage(60)
if (absorptionRate > .25)
actions.add(resourceHelper.gs(R.string.slowabsorptiondetected, resourceHelper.gc(R.color.cobAlert), (absorptionRate * 100).toInt()))
}
if (abs(insulinAfterConstraints - calculatedTotalInsulin) > pump.pumpDescription.pumpType.determineCorrectBolusStepSize(insulinAfterConstraints))
if (abs(insulinAfterConstraints - calculatedTotalInsulin) > activePlugin.activePump.pumpDescription.pumpType.determineCorrectBolusStepSize(insulinAfterConstraints))
actions.add(resourceHelper.gs(R.string.bolusconstraintappliedwarn, calculatedTotalInsulin, insulinAfterConstraints).formatColor(resourceHelper, R.color.warning))
if (config.NSCLIENT)
if (config.NSCLIENT && insulinAfterConstraints > 0)
actions.add(resourceHelper.gs(R.string.bolusrecordedonly).formatColor(resourceHelper, R.color.warning))
if (useAlarm && !advisor && carbs > 0 && carbTime > 0)
actions.add(resourceHelper.gs(R.string.alarminxmin, carbTime).formatColor(resourceHelper, R.color.info))
if (advisor)
actions.add(resourceHelper.gs(R.string.advisoralarm).formatColor(resourceHelper, R.color.info))
return HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions))
}
fun confirmAndExecute(context: Context) {
val profile = profileFunction.getProfile() ?: return
val pump = activePlugin.activePump
fun confirmAndExecute(ctx: Context) {
if (calculatedTotalInsulin > 0.0 || carbs > 0.0) {
if (accepted) {
aapsLogger.debug(LTag.UI, "guarding: already accepted")
@ -311,9 +330,56 @@ class BolusWizard @Inject constructor(
}
accepted = true
val confirmMessage = confirmMessageAfterConstraints(pump)
if (sp.getBoolean(R.string.key_usebolusadvisor, false) && Profile.toMgdl(bg, profile.units) > 180 && carbs > 0 && carbTime >= 0)
OKDialog.showYesNoCancel(ctx, resourceHelper.gs(R.string.bolusadvisor), resourceHelper.gs(R.string.bolusadvisormessage),
{ bolusAdvisorProcessing(ctx) },
{ commonProcessing(ctx) }
)
else
commonProcessing(ctx)
}
}
OKDialog.showConfirmation(context, resourceHelper.gs(R.string.boluswizard), confirmMessage, Runnable {
private fun bolusAdvisorProcessing(ctx: Context) {
val confirmMessage = confirmMessageAfterConstraints(advisor = true)
OKDialog.showConfirmation(ctx, resourceHelper.gs(R.string.boluswizard), confirmMessage, {
DetailedBolusInfo().apply {
eventType = CareportalEvent.CORRECTIONBOLUS
insulin = insulinAfterConstraints
carbs = 0.0
context = ctx
glucose = bg
glucoseType = "Manual"
carbTime = 0
boluscalc = nsJSON()
source = Source.USER
notes = this@BolusWizard.notes
aapsLogger.debug("USER ENTRY: BOLUS ADVISOR insulin $insulinAfterConstraints")
if (insulin > 0) {
commandQueue.bolus(this, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(ctx, ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.treatmentdeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
ctx.startActivity(i)
} else
scheduleEatReminder()
}
})
}
}
})
}
private fun commonProcessing(ctx: Context) {
val profile = profileFunction.getProfile() ?: return
val pump = activePlugin.activePump
val confirmMessage = confirmMessageAfterConstraints(advisor = false)
OKDialog.showConfirmation(ctx, resourceHelper.gs(R.string.boluswizard), confirmMessage, {
if (insulinAfterConstraints > 0 || carbs > 0) {
if (useSuperBolus) {
aapsLogger.debug("USER ENTRY: SUPERBOLUS TBR")
@ -326,12 +392,12 @@ class BolusWizard @Inject constructor(
commandQueue.tempBasalAbsolute(0.0, 120, true, profile, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(context, ErrorHelperActivity::class.java)
val i = Intent(ctx, ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.tempbasaldeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(i)
ctx.startActivity(i)
}
}
})
@ -340,47 +406,98 @@ class BolusWizard @Inject constructor(
commandQueue.tempBasalPercent(0, 120, true, profile, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(context, ErrorHelperActivity::class.java)
val i = Intent(ctx, ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.tempbasaldeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(i)
ctx.startActivity(i)
}
}
})
}
}
val detailedBolusInfo = DetailedBolusInfo()
detailedBolusInfo.eventType = CareportalEvent.BOLUSWIZARD
detailedBolusInfo.insulin = insulinAfterConstraints
detailedBolusInfo.carbs = carbs.toDouble()
detailedBolusInfo.context = context
detailedBolusInfo.glucose = bg
detailedBolusInfo.glucoseType = "Manual"
detailedBolusInfo.carbTime = carbTime
detailedBolusInfo.boluscalc = nsJSON()
detailedBolusInfo.source = Source.USER
detailedBolusInfo.notes = notes
aapsLogger.debug("USER ENTRY: BOLUS insulin $insulinAfterConstraints carbs: $carbs")
if (detailedBolusInfo.insulin > 0 || pump.pumpDescription.storesCarbInfo) {
commandQueue.bolus(detailedBolusInfo, object : Callback() {
DetailedBolusInfo().apply {
eventType = CareportalEvent.BOLUSWIZARD
insulin = insulinAfterConstraints
carbs = this@BolusWizard.carbs.toDouble()
context = ctx
glucose = bg
glucoseType = "Manual"
carbTime = this@BolusWizard.carbTime
boluscalc = nsJSON()
source = Source.USER
notes = this@BolusWizard.notes
aapsLogger.debug("USER ENTRY: BOLUS WIZARD insulin $insulinAfterConstraints carbs: $carbs")
if (insulin > 0 || pump.pumpDescription.storesCarbInfo) {
commandQueue.bolus(this, object : Callback() {
override fun run() {
if (!result.success) {
val i = Intent(context, ErrorHelperActivity::class.java)
val i = Intent(ctx, ErrorHelperActivity::class.java)
i.putExtra("soundid", R.raw.boluserror)
i.putExtra("status", result.comment)
i.putExtra("title", resourceHelper.gs(R.string.treatmentdeliveryerror))
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(i)
ctx.startActivity(i)
}
}
})
} else {
activePlugin.activeTreatments.addToHistoryTreatment(detailedBolusInfo, false)
activePlugin.activeTreatments.addToHistoryTreatment(this, false)
}
}
if (useAlarm && carbs > 0 && carbTime > 0) {
scheduleReminder(dateUtil._now() + T.mins(carbTime.toLong()).msecs())
}
}
})
}
private fun scheduleEatReminder() {
val event = AutomationEvent(injector).apply {
title = resourceHelper.gs(R.string.bolusadvisor)
readOnly = true
systemAction = true
autoRemove = true
trigger = TriggerConnector(injector, TriggerConnector.Type.OR).apply {
// Bg under 180 mgdl and dropping by 15 mgdl
list.add(TriggerConnector(injector, TriggerConnector.Type.AND).apply {
list.add(TriggerBg(injector, 180.0, Constants.MGDL, Comparator.Compare.IS_LESSER))
list.add(TriggerDelta(injector, InputDelta(injector, -15.0, -360.0, 360.0, 1.0, DecimalFormat("0"), InputDelta.DeltaType.DELTA), Constants.MGDL, Comparator.Compare.IS_EQUAL_OR_LESSER))
list.add(TriggerDelta(injector, InputDelta(injector, -8.0, -360.0, 360.0, 1.0, DecimalFormat("0"), InputDelta.DeltaType.SHORT_AVERAGE), Constants.MGDL, Comparator.Compare.IS_EQUAL_OR_LESSER))
})
// Bg under 160 mgdl and dropping by 9 mgdl
list.add(TriggerConnector(injector, TriggerConnector.Type.AND).apply {
list.add(TriggerBg(injector, 160.0, Constants.MGDL, Comparator.Compare.IS_LESSER))
list.add(TriggerDelta(injector, InputDelta(injector, -9.0, -360.0, 360.0, 1.0, DecimalFormat("0"), InputDelta.DeltaType.DELTA), Constants.MGDL, Comparator.Compare.IS_EQUAL_OR_LESSER))
list.add(TriggerDelta(injector, InputDelta(injector, -5.0, -360.0, 360.0, 1.0, DecimalFormat("0"), InputDelta.DeltaType.SHORT_AVERAGE), Constants.MGDL, Comparator.Compare.IS_EQUAL_OR_LESSER))
})
// Bg under 145 mgdl and dropping
list.add(TriggerConnector(injector, TriggerConnector.Type.AND).apply {
list.add(TriggerBg(injector, 145.0, Constants.MGDL, Comparator.Compare.IS_LESSER))
list.add(TriggerDelta(injector, InputDelta(injector, 0.0, -360.0, 360.0, 1.0, DecimalFormat("0"), InputDelta.DeltaType.DELTA), Constants.MGDL, Comparator.Compare.IS_EQUAL_OR_LESSER))
list.add(TriggerDelta(injector, InputDelta(injector, 0.0, -360.0, 360.0, 1.0, DecimalFormat("0"), InputDelta.DeltaType.SHORT_AVERAGE), Constants.MGDL, Comparator.Compare.IS_EQUAL_OR_LESSER))
})
}
actions.add(ActionAlarm(injector, resourceHelper.gs(R.string.time_to_eat)))
}
automationPlugin.addIfNotExists(event)
}
private fun scheduleReminder(time: Long) {
val event = AutomationEvent(injector).apply {
title = resourceHelper.gs(R.string.timetoeat)
readOnly = true
systemAction = true
autoRemove = true
trigger = TriggerConnector(injector, TriggerConnector.Type.AND).apply {
list.add(TriggerTime(injector, time))
}
actions.add(ActionAlarm(injector, resourceHelper.gs(R.string.timetoeat)))
}
automationPlugin.addIfNotExists(event)
}
}

View file

@ -117,7 +117,7 @@ class QuickWizardEntry @Inject constructor(private val injector: HasAndroidInjec
trend = true
}
val percentage = sp.getDouble(R.string.key_boluswizard_percentage, 100.0)
return BolusWizard(injector).doCalc(profile, profileName, tempTarget, carbs(), cob, bg, 0.0, percentage, true, useCOB() == YES, bolusIOB, basalIOB, superBolus, useTempTarget() == YES, trend, "QuickWizard")
return BolusWizard(injector).doCalc(profile, profileName, tempTarget, carbs(), cob, bg, 0.0, percentage, true, useCOB() == YES, bolusIOB, basalIOB, superBolus, useTempTarget() == YES, trend, false, "QuickWizard")
}
fun buttonText(): String = safeGetString(storage, "buttonText", "")

View file

@ -51,8 +51,8 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:orientation="vertical">
android:orientation="vertical"
android:padding="10dp">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/inputEventTitle"
@ -163,7 +163,9 @@
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp" />
<include layout="@layout/okcancel" />
<include
android:id="@+id/okcancel"
layout="@layout/okcancel" />
</LinearLayout>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/rootLayout"
android:layout_width="match_parent"
@ -8,59 +8,79 @@
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:clickable="true"
android:focusable="true"
android:background="@color/ribbonDefault"
android:padding="8dp">
android:clickable="true"
android:focusable="true">
<CheckBox
android:id="@+id/enabled"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true" />
app:layout_constraintBottom_toTopOf="@+id/iconLayout"
app:layout_constraintEnd_toStartOf="@+id/aapsLogo"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/aapsLogo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:contentDescription="@string/remove_label"
android:scaleX="0.9"
android:scaleY="0.9"
android:src="@drawable/ic_notif_aaps"
app:layout_constraintBottom_toTopOf="@+id/iconLayout"
app:layout_constraintEnd_toStartOf="@+id/eventTitle"
app:layout_constraintStart_toEndOf="@+id/enabled"
app:layout_constraintTop_toTopOf="@+id/enabled" />
<TextView
android:id="@+id/eventTitle"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/enabled"
android:layout_alignBottom="@+id/enabled"
android:layout_centerVertical="true"
android:layout_marginTop="6dp"
android:layout_toStartOf="@+id/iconTrash"
android:layout_toEndOf="@id/enabled"
android:text="Title"
android:textAlignment="viewStart"
android:textStyle="bold" />
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@+id/iconLayout"
app:layout_constraintEnd_toStartOf="@+id/iconTrash"
app:layout_constraintStart_toEndOf="@+id/aapsLogo"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/iconTrash"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginEnd="20dp"
android:layout_toStartOf="@+id/iconSort"
android:contentDescription="@string/remove_label"
android:orientation="horizontal"
android:src="@drawable/ic_trash_outline" />
android:src="@drawable/ic_trash_outline"
app:layout_constraintBottom_toTopOf="@+id/iconLayout"
app:layout_constraintEnd_toStartOf="@+id/iconSort"
app:layout_constraintStart_toEndOf="@+id/eventTitle"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/iconSort"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:contentDescription="@string/reorder_label"
android:orientation="horizontal"
app:layout_constraintBottom_toTopOf="@+id/iconLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/iconTrash"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_reorder_gray_24dp" />
<LinearLayout
android:id="@+id/iconLayout"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_below="@id/enabled"
android:orientation="horizontal" />
android:layout_marginStart="16dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/iconSort" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -49,9 +49,9 @@
android:padding="5dp" />
<TableLayout
android:paddingEnd="5dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:paddingEnd="5dp">
<TableRow
android:layout_width="match_parent"
@ -67,12 +67,12 @@
android:textStyle="bold" />
<info.nightscout.androidaps.utils.ui.NumberPicker
android:id="@+id/treatments_wizard_bg_input"
android:id="@+id/bg_input"
android:layout_width="130dp"
android:layout_height="40dp" />
<TextView
android:id="@+id/treatments_wizard_bgunits"
android:id="@+id/bgunits"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
@ -97,7 +97,7 @@
android:textStyle="bold" />
<info.nightscout.androidaps.utils.ui.NumberPicker
android:id="@+id/treatments_wizard_carbs_input"
android:id="@+id/carbs_input"
android:layout_width="130dp"
android:layout_height="40dp"
android:layout_gravity="center_horizontal" />
@ -128,7 +128,7 @@
android:textStyle="bold" />
<info.nightscout.androidaps.utils.ui.NumberPicker
android:id="@+id/treatments_wizard_correction_input"
android:id="@+id/correction_input"
android:layout_width="130dp"
android:layout_height="40dp"
android:layout_gravity="center_horizontal" />
@ -148,17 +148,43 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="@string/careportal_newnstreatment_carbtime_label"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textStyle="bold" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/ic_access_alarm_24dp" />
<CheckBox
android:id="@+id/alarm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:checked="false"
android:padding="5dp" />
</LinearLayout>
<info.nightscout.androidaps.utils.ui.NumberPicker
android:id="@+id/treatments_wizard_carb_time_input"
android:id="@+id/carb_time_input"
android:layout_width="130dp"
android:layout_height="40dp"
android:layout_gravity="center_horizontal" />
@ -191,7 +217,7 @@
android:textStyle="bold" />
<Spinner
android:id="@+id/treatments_wizard_profile"
android:id="@+id/profile"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|center_horizontal"
@ -199,13 +225,13 @@
<CheckBox
android:id="@+id/treatments_wizard_sbcheckbox"
android:id="@+id/sbcheckbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="10dp"
android:padding="5dp"
android:checked="false"
android:padding="5dp"
android:text="@string/superbolus" />
</LinearLayout>
@ -217,7 +243,7 @@
android:orientation="horizontal">
<TextView
android:id="@+id/treatments_wizard_total"
android:id="@+id/total"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/background_darkgray"
@ -227,7 +253,7 @@
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="@+id/treatments_wizard_percent_used"
android:id="@+id/percent_used"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
@ -241,7 +267,7 @@
</LinearLayout>
<LinearLayout
android:id="@+id/treatments_wizard_notes_layout"
android:id="@+id/notes_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
@ -250,14 +276,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:labelFor="@+id/treatment_wizard_notes"
android:labelFor="@+id/notes"
android:padding="10dp"
android:text="@string/careportal_newnstreatment_notes_label"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textStyle="bold" />
<EditText
android:id="@+id/treatment_wizard_notes"
android:id="@+id/notes"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
@ -279,7 +305,7 @@
android:padding="5dp">
<CheckBox
android:id="@+id/treatments_wizard_calculationcheckbox"
android:id="@+id/calculationcheckbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
@ -288,8 +314,8 @@
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/show_calculation"
android:layout_gravity="center_vertical"
android:contentDescription="@string/show_calculation"
app:srcCompat="@drawable/ic_visibility" />
<Button
@ -312,7 +338,7 @@
</LinearLayout>
<View
android:id="@+id/treatments_wizard_delimiter"
android:id="@+id/delimiter"
android:layout_width="fill_parent"
android:layout_height="2dip"
android:layout_marginStart="20dp"
@ -321,7 +347,7 @@
android:background="@color/listdelimiter" />
<TableLayout
android:id="@+id/treatments_wizard_resulttable"
android:id="@+id/resulttable"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp">
@ -331,7 +357,7 @@
android:layout_height="match_parent">
<CheckBox
android:id="@+id/treatments_wizard_bgcheckbox"
android:id="@+id/bgcheckbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="32dp"
@ -350,7 +376,7 @@
android:textAppearance="?android:attr/textAppearanceSmall" />
<CheckBox
android:id="@+id/treatments_wizard_ttcheckbox"
android:id="@+id/ttcheckbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="32dp"
@ -366,7 +392,7 @@
</LinearLayout>
<TextView
android:id="@+id/treatments_wizard_bg"
android:id="@+id/bg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
@ -374,7 +400,7 @@
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/treatments_wizard_bginsulin"
android:id="@+id/bginsulin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="50dp"
@ -388,7 +414,7 @@
android:layout_height="match_parent">
<CheckBox
android:id="@+id/treatments_wizard_bgtrendcheckbox"
android:id="@+id/bgtrendcheckbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="32dp"
@ -402,7 +428,7 @@
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/treatments_wizard_bgtrend"
android:id="@+id/bgtrend"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
@ -410,7 +436,7 @@
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/treatments_wizard_bgtrendinsulin"
android:id="@+id/bgtrendinsulin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="50dp"
@ -424,7 +450,7 @@
android:layout_height="match_parent">
<CheckBox
android:id="@+id/treatments_wizard_cobcheckbox"
android:id="@+id/cobcheckbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="32dp"
@ -438,7 +464,7 @@
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/treatments_wizard_cob"
android:id="@+id/cob"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
@ -446,7 +472,7 @@
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/treatments_wizard_cobinsulin"
android:id="@+id/cobinsulin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="50dp"
@ -460,7 +486,7 @@
android:layout_height="match_parent">
<CheckBox
android:id="@+id/treatments_wizard_bolusiobcheckbox"
android:id="@+id/bolusiobcheckbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="32dp"
@ -481,7 +507,7 @@
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/treatments_wizard_bolusiobinsulin"
android:id="@+id/bolusiobinsulin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="50dp"
@ -495,7 +521,7 @@
android:layout_height="match_parent">
<CheckBox
android:id="@+id/treatments_wizard_basaliobcheckbox"
android:id="@+id/basaliobcheckbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="32dp"
@ -516,7 +542,7 @@
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/treatments_wizard_basaliobinsulin"
android:id="@+id/basaliobinsulin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="50dp"
@ -542,7 +568,7 @@
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/treatments_wizard_carbs"
android:id="@+id/carbs"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
@ -550,7 +576,7 @@
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/treatments_wizard_carbsinsulin"
android:id="@+id/carbsinsulin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="50dp"
@ -576,7 +602,7 @@
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/treatments_wizard_sb"
android:id="@+id/sb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
@ -584,7 +610,7 @@
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/treatments_wizard_sbinsulin"
android:id="@+id/sbinsulin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="50dp"
@ -617,7 +643,7 @@
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/treatments_wizard_correctioninsulin"
android:id="@+id/correctioninsulin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:width="50dp"

View file

@ -16,6 +16,7 @@
<color name="basebasal">#C83F51B5</color>
<color name="graphgrid">#757575</color>
<color name="warning">#ff1a1a</color>
<color name="info">#77dd77</color>
<color name="error_background">#66FC0000</color>
<color name="ok_background">#323232</color>

View file

@ -680,8 +680,6 @@
<string name="overview_editquickwizard_usebasaliob">Basal IOB calculation</string>
<string name="overview_editquickwizard_usetrend">Trend calculation</string>
<string name="overview_editquickwizard_usesuperbolus">Superbolus calculation</string>
<string name="yes">Yes</string>
<string name="no">No</string>
<string name="positiveonly">Positive only</string>
<string name="negativeonly">Negative only</string>
<string name="overview_editquickwizard_usecob">COB calculation</string>
@ -1425,5 +1423,14 @@
<string name="key_smscommunicator_report_pump_ureachable" translatable="false">smscommunicator_report_pump_ureachable</string>
<string name="smscommunicator_report_pump_ureachable_summary">Send SMS if unreachable pump event is triggered</string>
<string name="smscommunicator_pump_ureachable">Report pump ureachable</string>
<string name="advisoralarm">Run alarm when is time to eat</string>
<string name="alarminxmin">Run alarm in %1$d min</string>
<string name="bolusadvisor">Bolus advisor</string>
<string name="bolusadvisormessage">You have high glycemia. Instead of eating now it\'s recommended to wait for better glycemia. Do you want to do a correction bolus now and remind you when it\'s time to eat? In this case no carbs will be recorded and you must use wizard again when we remind you.</string>
<string name="key_usebolusadvisor" translatable="false">use_bolus_advisor</string>
<string name="enablebolusadvisor">Enable bolus advisor</string>
<string name="enablebolusadvisor_summary">Use reminder to start eating instead of wizard during high glycemia</string>
<string name="time_to_eat">Time to eat!\nRun Bolus wizard and do calculation again.</string>
<string name="timetoeat">Time to eat</string>
</resources>

View file

@ -489,6 +489,18 @@
validate:minNumber="10"
validate:testType="numericRange" />
<SwitchPreference
android:defaultValue="true"
android:key="@string/key_usebolusadvisor"
android:summary="@string/enablebolusadvisor_summary"
android:title="@string/enablebolusadvisor" />
<SwitchPreference
android:defaultValue="false"
android:key="@string/key_usebolusadvisor"
android:summary="@string/enablebolusadvisor_summary"
android:title="@string/enablebolusadvisor" />
<SwitchPreference
android:defaultValue="false"
android:key="@string/key_usesuperbolus"

View file

@ -10,6 +10,7 @@ import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.utils.extensions.runOnUiThread
object OKDialog {
@SuppressLint("InflateParams")
fun show(context: Context, title: String, message: String, runnable: Runnable? = null) {
var okClicked = false
@ -217,4 +218,36 @@ object OKDialog {
.show()
.setCanceledOnTouchOutside(false)
}
@SuppressLint("InflateParams")
fun showYesNoCancel(context: Context, title: String, message: String, yes: Runnable?, no: Runnable? = null) {
var okClicked = false
AlertDialogHelper.Builder(context)
.setMessage(message)
.setCustomTitle(AlertDialogHelper.buildCustomTitle(context, title))
.setPositiveButton(R.string.yes) { dialog: DialogInterface, _: Int ->
if (okClicked) return@setPositiveButton
else {
okClicked = true
dialog.dismiss()
SystemClock.sleep(100)
runOnUiThread(yes)
}
}
.setNegativeButton(R.string.no) { dialog: DialogInterface, _: Int ->
if (okClicked) return@setNegativeButton
else {
okClicked = true
dialog.dismiss()
SystemClock.sleep(100)
runOnUiThread(no)
}
}
.setNeutralButton(R.string.cancel) { dialog: DialogInterface, _: Int ->
dialog.dismiss()
}
.show()
.setCanceledOnTouchOutside(false)
}
}

View file

@ -95,6 +95,8 @@
<string name="btwatchdog_title">BT Watchdog</string>
<string name="btwatchdog_summary">Switches off the phone\'s bluetooth for one second if no connection to the pump is possible. This may help on some phones where the bluetooth stack freezes.</string>
<string name="pairing">Pairing</string>
<string name="yes">Yes</string>
<string name="no">No</string>
<!-- Constraints-->
<string name="limitingbasalratio">Limiting max basal rate to %1$.2f U/h because of %2$s</string>