InsulinDialog -> ui
This commit is contained in:
parent
228167820c
commit
18b434b2e2
|
@ -47,7 +47,7 @@ import info.nightscout.rx.logging.LTag
|
|||
import info.nightscout.shared.interfaces.ResourceHelper
|
||||
import info.nightscout.shared.sharedPreferences.SP
|
||||
import info.nightscout.shared.utils.DateUtil
|
||||
import info.nightscout.ui.utils.ActivityMonitor
|
||||
import info.nightscout.ui.activityMonitor.ActivityMonitor
|
||||
import info.nightscout.ui.widget.Widget
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import io.reactivex.rxjava3.exceptions.UndeliverableException
|
||||
|
|
|
@ -3,7 +3,6 @@ package info.nightscout.androidaps.di
|
|||
import dagger.Module
|
||||
import dagger.android.ContributesAndroidInjector
|
||||
import info.nightscout.androidaps.activities.MyPreferenceFragment
|
||||
import info.nightscout.androidaps.dialogs.InsulinDialog
|
||||
import info.nightscout.androidaps.dialogs.LoopDialog
|
||||
import info.nightscout.androidaps.dialogs.NtpProgressDialog
|
||||
import info.nightscout.androidaps.dialogs.ProfileSwitchDialog
|
||||
|
@ -40,7 +39,6 @@ abstract class FragmentsModule {
|
|||
|
||||
@ContributesAndroidInjector abstract fun contributesEditQuickWizardDialog(): EditQuickWizardDialog
|
||||
|
||||
@ContributesAndroidInjector abstract fun contributesInsulinDialog(): InsulinDialog
|
||||
@ContributesAndroidInjector abstract fun contributesLoopDialog(): LoopDialog
|
||||
@ContributesAndroidInjector abstract fun contributesObjectivesExamDialog(): ObjectivesExamDialog
|
||||
@ContributesAndroidInjector abstract fun contributesProfileSwitchDialog(): ProfileSwitchDialog
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
package info.nightscout.androidaps.dialogs
|
||||
|
|
@ -1,311 +1,2 @@
|
|||
package info.nightscout.androidaps.dialogs
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.google.common.base.Joiner
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.databinding.DialogInsulinBinding
|
||||
import info.nightscout.androidaps.extensions.formatColor
|
||||
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||
import info.nightscout.androidaps.interfaces.CommandQueue
|
||||
import info.nightscout.androidaps.interfaces.Constraints
|
||||
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||
import info.nightscout.androidaps.logging.UserEntryLogger
|
||||
import info.nightscout.androidaps.utils.DecimalFormatter
|
||||
import info.nightscout.androidaps.utils.DefaultValueHelper
|
||||
import info.nightscout.androidaps.utils.ToastUtils
|
||||
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
|
||||
import info.nightscout.androidaps.utils.extensions.toSignedString
|
||||
import info.nightscout.androidaps.utils.protection.ProtectionCheck
|
||||
import info.nightscout.androidaps.utils.protection.ProtectionCheck.Protection.BOLUS
|
||||
import info.nightscout.core.profile.toMgdl
|
||||
import info.nightscout.core.pumpExtensions.insertBolusTransaction
|
||||
import info.nightscout.database.entities.TemporaryTarget
|
||||
import info.nightscout.database.entities.UserEntry.Action
|
||||
import info.nightscout.database.entities.UserEntry.Sources
|
||||
import info.nightscout.database.entities.ValueWithUnit
|
||||
import info.nightscout.database.impl.AppRepository
|
||||
import info.nightscout.database.impl.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
|
||||
import info.nightscout.interfaces.BolusTimer
|
||||
import info.nightscout.interfaces.Config
|
||||
import info.nightscout.interfaces.GlucoseUnit
|
||||
import info.nightscout.interfaces.constraints.Constraint
|
||||
import info.nightscout.interfaces.profile.Profile
|
||||
import info.nightscout.interfaces.pump.DetailedBolusInfo
|
||||
import info.nightscout.interfaces.queue.Callback
|
||||
import info.nightscout.interfaces.ui.ActivityNames
|
||||
import info.nightscout.interfaces.utils.HtmlHelper
|
||||
import info.nightscout.rx.logging.LTag
|
||||
import info.nightscout.shared.SafeParse
|
||||
import info.nightscout.shared.extensions.toVisibility
|
||||
import info.nightscout.shared.interfaces.ResourceHelper
|
||||
import info.nightscout.shared.utils.T
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import io.reactivex.rxjava3.kotlin.plusAssign
|
||||
import java.text.DecimalFormat
|
||||
import java.util.LinkedList
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
|
||||
class InsulinDialog : DialogFragmentWithDate() {
|
||||
|
||||
@Inject lateinit var constraintChecker: Constraints
|
||||
@Inject lateinit var rh: ResourceHelper
|
||||
@Inject lateinit var defaultValueHelper: DefaultValueHelper
|
||||
@Inject lateinit var profileFunction: ProfileFunction
|
||||
@Inject lateinit var commandQueue: CommandQueue
|
||||
@Inject lateinit var activePlugin: ActivePlugin
|
||||
@Inject lateinit var ctx: Context
|
||||
@Inject lateinit var repository: AppRepository
|
||||
@Inject lateinit var config: Config
|
||||
@Inject lateinit var bolusTimer: BolusTimer
|
||||
@Inject lateinit var uel: UserEntryLogger
|
||||
@Inject lateinit var protectionCheck: ProtectionCheck
|
||||
@Inject lateinit var activityNames: ActivityNames
|
||||
|
||||
companion object {
|
||||
|
||||
const val PLUS1_DEFAULT = 0.5
|
||||
const val PLUS2_DEFAULT = 1.0
|
||||
const val PLUS3_DEFAULT = 2.0
|
||||
}
|
||||
|
||||
private var queryingProtection = false
|
||||
private val disposable = CompositeDisposable()
|
||||
private var _binding: DialogInsulinBinding? = null
|
||||
|
||||
// This property is only valid between onCreateView and onDestroyView.
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private val textWatcher: TextWatcher = object : TextWatcher {
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
validateInputs()
|
||||
}
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
|
||||
}
|
||||
|
||||
private fun validateInputs() {
|
||||
val maxInsulin = constraintChecker.getMaxBolusAllowed().value()
|
||||
if (abs(binding.time.value.toInt()) > 12 * 60) {
|
||||
binding.time.value = 0.0
|
||||
ToastUtils.warnToast(context, R.string.constraint_applied)
|
||||
}
|
||||
if (binding.amount.value > maxInsulin) {
|
||||
binding.amount.value = 0.0
|
||||
ToastUtils.warnToast(context, R.string.bolus_constraint_applied)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(savedInstanceState: Bundle) {
|
||||
super.onSaveInstanceState(savedInstanceState)
|
||||
savedInstanceState.putDouble("time", binding.time.value)
|
||||
savedInstanceState.putDouble("amount", binding.amount.value)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
onCreateViewGeneral()
|
||||
_binding = DialogInsulinBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
if (config.NSCLIENT) {
|
||||
binding.recordOnly.isChecked = true
|
||||
binding.recordOnly.isEnabled = false
|
||||
}
|
||||
val maxInsulin = constraintChecker.getMaxBolusAllowed().value()
|
||||
|
||||
binding.time.setParams(
|
||||
savedInstanceState?.getDouble("time")
|
||||
?: 0.0, -12 * 60.0, 12 * 60.0, 5.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher
|
||||
)
|
||||
binding.amount.setParams(
|
||||
savedInstanceState?.getDouble("amount")
|
||||
?: 0.0, 0.0, maxInsulin, activePlugin.activePump.pumpDescription.bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, binding.okcancel.ok, textWatcher
|
||||
)
|
||||
|
||||
val plus05Text = sp.getDouble(rh.gs(R.string.key_insulin_button_increment_1), PLUS1_DEFAULT).toSignedString(activePlugin.activePump)
|
||||
binding.plus05.text = plus05Text
|
||||
binding.plus05.contentDescription = rh.gs(R.string.overview_insulin_label) + " " + plus05Text
|
||||
binding.plus05.setOnClickListener {
|
||||
binding.amount.value = max(
|
||||
0.0, binding.amount.value
|
||||
+ sp.getDouble(rh.gs(R.string.key_insulin_button_increment_1), PLUS1_DEFAULT)
|
||||
)
|
||||
validateInputs()
|
||||
binding.amount.announceValue()
|
||||
}
|
||||
val plus10Text = sp.getDouble(rh.gs(R.string.key_insulin_button_increment_2), PLUS2_DEFAULT).toSignedString(activePlugin.activePump)
|
||||
binding.plus10.text = plus10Text
|
||||
binding.plus10.contentDescription = rh.gs(R.string.overview_insulin_label) + " " + plus10Text
|
||||
binding.plus10.setOnClickListener {
|
||||
binding.amount.value = max(
|
||||
0.0, binding.amount.value
|
||||
+ sp.getDouble(rh.gs(R.string.key_insulin_button_increment_2), PLUS2_DEFAULT)
|
||||
)
|
||||
validateInputs()
|
||||
binding.amount.announceValue()
|
||||
}
|
||||
val plus20Text = sp.getDouble(rh.gs(R.string.key_insulin_button_increment_3), PLUS3_DEFAULT).toSignedString(activePlugin.activePump)
|
||||
binding.plus20.text = plus20Text
|
||||
binding.plus20.contentDescription = rh.gs(R.string.overview_insulin_label) + " " + plus20Text
|
||||
binding.plus20.setOnClickListener {
|
||||
binding.amount.value = max(
|
||||
0.0, binding.amount.value
|
||||
+ sp.getDouble(rh.gs(R.string.key_insulin_button_increment_3), PLUS3_DEFAULT)
|
||||
)
|
||||
validateInputs()
|
||||
binding.amount.announceValue()
|
||||
}
|
||||
|
||||
binding.timeLayout.visibility = View.GONE
|
||||
binding.recordOnly.setOnCheckedChangeListener { _, isChecked: Boolean ->
|
||||
binding.timeLayout.visibility = isChecked.toVisibility()
|
||||
}
|
||||
binding.insulinLabel.labelFor = binding.amount.editTextId
|
||||
binding.timeLabel.labelFor = binding.time.editTextId
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
disposable.clear()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
override fun submit(): Boolean {
|
||||
if (_binding == null) return false
|
||||
val pumpDescription = activePlugin.activePump.pumpDescription
|
||||
val insulin = SafeParse.stringToDouble(binding.amount.text)
|
||||
val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(insulin)).value()
|
||||
val actions: LinkedList<String?> = LinkedList()
|
||||
val units = profileFunction.getUnits()
|
||||
val unitLabel = if (units == GlucoseUnit.MMOL) rh.gs(R.string.mmol) else rh.gs(R.string.mgdl)
|
||||
val recordOnlyChecked = binding.recordOnly.isChecked
|
||||
val eatingSoonChecked = binding.startEatingSoonTt.isChecked
|
||||
|
||||
if (insulinAfterConstraints > 0) {
|
||||
actions.add(rh.gs(R.string.bolus) + ": " + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints, activePlugin.activePump, rh).formatColor(context, rh, R.attr.bolusColor))
|
||||
if (recordOnlyChecked)
|
||||
actions.add(rh.gs(R.string.bolusrecordedonly).formatColor(context, rh, R.attr.warningColor))
|
||||
if (abs(insulinAfterConstraints - insulin) > pumpDescription.pumpType.determineCorrectBolusStepSize(insulinAfterConstraints))
|
||||
actions.add(rh.gs(R.string.bolusconstraintappliedwarn, insulin, insulinAfterConstraints).formatColor(context, rh, R.attr.warningColor))
|
||||
}
|
||||
val eatingSoonTTDuration = defaultValueHelper.determineEatingSoonTTDuration()
|
||||
val eatingSoonTT = defaultValueHelper.determineEatingSoonTT()
|
||||
if (eatingSoonChecked)
|
||||
actions.add(
|
||||
rh.gs(R.string.temp_target_short) + ": " + (DecimalFormatter.to1Decimal(eatingSoonTT) + " " + unitLabel + " (" + rh.gs(R.string.format_mins, eatingSoonTTDuration) + ")")
|
||||
.formatColor(context, rh, R.attr.tempTargetConfirmation)
|
||||
)
|
||||
|
||||
val timeOffset = binding.time.value.toInt()
|
||||
val time = dateUtil.now() + T.mins(timeOffset.toLong()).msecs()
|
||||
if (timeOffset != 0)
|
||||
actions.add(rh.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(time))
|
||||
|
||||
val notes = binding.notesLayout.notes.text.toString()
|
||||
if (notes.isNotEmpty())
|
||||
actions.add(rh.gs(R.string.notes_label) + ": " + notes)
|
||||
|
||||
if (insulinAfterConstraints > 0 || eatingSoonChecked) {
|
||||
activity?.let { activity ->
|
||||
OKDialog.showConfirmation(activity, rh.gs(R.string.bolus), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
|
||||
if (eatingSoonChecked) {
|
||||
uel.log(
|
||||
Action.TT, Sources.InsulinDialog,
|
||||
notes,
|
||||
ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.EATING_SOON),
|
||||
ValueWithUnit.fromGlucoseUnit(eatingSoonTT, units.asText),
|
||||
ValueWithUnit.Minute(eatingSoonTTDuration)
|
||||
)
|
||||
disposable += repository.runTransactionForResult(
|
||||
InsertAndCancelCurrentTemporaryTargetTransaction(
|
||||
timestamp = System.currentTimeMillis(),
|
||||
duration = TimeUnit.MINUTES.toMillis(eatingSoonTTDuration.toLong()),
|
||||
reason = TemporaryTarget.Reason.EATING_SOON,
|
||||
lowTarget = Profile.toMgdl(eatingSoonTT, profileFunction.getUnits()),
|
||||
highTarget = Profile.toMgdl(eatingSoonTT, profileFunction.getUnits())
|
||||
)
|
||||
).subscribe({ result ->
|
||||
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted temp target $it") }
|
||||
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") }
|
||||
}, {
|
||||
aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it)
|
||||
})
|
||||
}
|
||||
if (insulinAfterConstraints > 0) {
|
||||
val detailedBolusInfo = DetailedBolusInfo()
|
||||
detailedBolusInfo.eventType = DetailedBolusInfo.EventType.CORRECTION_BOLUS
|
||||
detailedBolusInfo.insulin = insulinAfterConstraints
|
||||
detailedBolusInfo.context = context
|
||||
detailedBolusInfo.notes = notes
|
||||
detailedBolusInfo.timestamp = time
|
||||
if (recordOnlyChecked) {
|
||||
uel.log(Action.BOLUS, Sources.InsulinDialog,
|
||||
rh.gs(R.string.record) + if (notes.isNotEmpty()) ": $notes" else "",
|
||||
ValueWithUnit.SimpleString(rh.gsNotLocalised(R.string.record)),
|
||||
ValueWithUnit.Insulin(insulinAfterConstraints),
|
||||
ValueWithUnit.Minute(timeOffset).takeIf { timeOffset != 0 })
|
||||
disposable += repository.runTransactionForResult(detailedBolusInfo.insertBolusTransaction())
|
||||
.subscribe(
|
||||
{ result -> result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted bolus $it") } },
|
||||
{ aapsLogger.error(LTag.DATABASE, "Error while saving bolus", it) }
|
||||
)
|
||||
if (timeOffset == 0)
|
||||
bolusTimer.removeAutomationEventBolusReminder()
|
||||
} else {
|
||||
uel.log(
|
||||
Action.BOLUS, Sources.InsulinDialog,
|
||||
notes,
|
||||
ValueWithUnit.Insulin(insulinAfterConstraints)
|
||||
)
|
||||
commandQueue.bolus(detailedBolusInfo, object : Callback() {
|
||||
override fun run() {
|
||||
if (!result.success) {
|
||||
activityNames.runAlarm(ctx, result.comment, rh.gs(R.string.treatmentdeliveryerror), R.raw.boluserror)
|
||||
} else {
|
||||
bolusTimer.removeAutomationEventBolusReminder()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
} else
|
||||
activity?.let { activity ->
|
||||
OKDialog.show(activity, rh.gs(R.string.bolus), rh.gs(R.string.no_action_selected))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (!queryingProtection) {
|
||||
queryingProtection = true
|
||||
activity?.let { activity ->
|
||||
val cancelFail = {
|
||||
queryingProtection = false
|
||||
aapsLogger.debug(LTag.APS, "Dialog canceled on resume protection: ${this.javaClass.simpleName}")
|
||||
ToastUtils.warnToast(ctx, R.string.dialog_canceled)
|
||||
dismiss()
|
||||
}
|
||||
protectionCheck.queryProtection(activity, BOLUS, { queryingProtection = false }, cancelFail, cancelFail)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ import dagger.android.support.DaggerFragment
|
|||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.data.ProfileSealed
|
||||
import info.nightscout.androidaps.databinding.OverviewFragmentBinding
|
||||
import info.nightscout.androidaps.dialogs.InsulinDialog
|
||||
import info.nightscout.ui.dialogs.InsulinDialog
|
||||
import info.nightscout.androidaps.dialogs.LoopDialog
|
||||
import info.nightscout.androidaps.dialogs.ProfileSwitchDialog
|
||||
import info.nightscout.androidaps.dialogs.TempTargetDialog
|
||||
|
|
|
@ -4,7 +4,7 @@ import android.app.NotificationManager
|
|||
import android.content.Context
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.dialogs.InsulinDialog
|
||||
import info.nightscout.ui.dialogs.InsulinDialog
|
||||
import info.nightscout.androidaps.extensions.convertedToAbsolute
|
||||
import info.nightscout.androidaps.extensions.toStringShort
|
||||
import info.nightscout.androidaps.extensions.total
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:focusableInTouchMode="true"
|
||||
tools:context=".dialogs.InsulinDialog">
|
||||
tools:context="info.nightscout.ui.dialogs.InsulinDialog">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -361,9 +361,6 @@
|
|||
<string name="key_show_treatment_button" translatable="false">show_treatment_button</string>
|
||||
<string name="show_calibration_button_summary">Sends a calibration to xDrip+ or open BYODA calibration dialog</string>
|
||||
<string name="show_cgm_button_summary">Opens xDrip+ or BYODA, back buttons returns to AAPS</string>
|
||||
<string name="key_insulin_button_increment_1" translatable="false">insulin_button_increment_1</string>
|
||||
<string name="key_insulin_button_increment_2" translatable="false">insulin_button_increment_2</string>
|
||||
<string name="key_insulin_button_increment_3" translatable="false">insulin_button_increment_3</string>
|
||||
<string name="carb_increment_button_message">Number of carbs to add when button is pressed</string>
|
||||
<string name="insulin_increment_button_message">Amount of insulin to add when button is pressed</string>
|
||||
<string name="error_starting_cgm">Could not launch CGM application. Make sure it is installed.</string>
|
||||
|
@ -381,8 +378,6 @@
|
|||
<string name="allow_automated_crash_reporting">Allow automated crash reporting and feature usage data to be sent to the developers via the fabric.io service.</string>
|
||||
<string name="g5appnotdetected">Please update your Dexcom app to supported version</string>
|
||||
<string name="dexcom_app_not_installed">Dexcom app is not installed.</string>
|
||||
<string name="do_not_bolus_record_only">Do not bolus, record only</string>
|
||||
<string name="bolusrecordedonly">Bolus will be recorded only (not delivered by pump)</string>
|
||||
<string name="loop_smbsetbypump_label">SMB set by pump</string>
|
||||
<string name="overview_show_activity">Activity</string>
|
||||
<string name="overview_show_bgi">Blood Glucose Impact</string>
|
||||
|
|
|
@ -15,7 +15,7 @@ import info.nightscout.database.entities.UserEntry.Sources
|
|||
import info.nightscout.rx.AapsSchedulers
|
||||
import info.nightscout.ui.R
|
||||
import info.nightscout.ui.databinding.ActivityStatsBinding
|
||||
import info.nightscout.ui.utils.ActivityMonitor
|
||||
import info.nightscout.ui.activityMonitor.ActivityMonitor
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import io.reactivex.rxjava3.kotlin.plusAssign
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package info.nightscout.ui.utils
|
||||
package info.nightscout.ui.activityMonitor
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Application
|
|
@ -21,6 +21,7 @@ import info.nightscout.ui.dialogs.CarbsDialog
|
|||
import info.nightscout.ui.dialogs.CareDialog
|
||||
import info.nightscout.ui.dialogs.ExtendedBolusDialog
|
||||
import info.nightscout.ui.dialogs.FillDialog
|
||||
import info.nightscout.ui.dialogs.InsulinDialog
|
||||
import info.nightscout.ui.dialogs.ProfileViewerDialog
|
||||
import info.nightscout.ui.dialogs.WizardInfoDialog
|
||||
|
||||
|
@ -35,6 +36,7 @@ abstract class UiModule {
|
|||
@ContributesAndroidInjector abstract fun contributesProfileViewerDialog(): ProfileViewerDialog
|
||||
@ContributesAndroidInjector abstract fun contributesExtendedBolusDialog(): ExtendedBolusDialog
|
||||
@ContributesAndroidInjector abstract fun contributesFillDialog(): FillDialog
|
||||
@ContributesAndroidInjector abstract fun contributesInsulinDialog(): InsulinDialog
|
||||
|
||||
@ContributesAndroidInjector abstract fun contributesTDDStatsActivity(): TDDStatsActivity
|
||||
@ContributesAndroidInjector abstract fun contributeBolusProgressHelperActivity(): BolusProgressHelperActivity
|
||||
|
|
310
ui/src/main/java/info/nightscout/ui/dialogs/InsulinDialog.kt
Normal file
310
ui/src/main/java/info/nightscout/ui/dialogs/InsulinDialog.kt
Normal file
|
@ -0,0 +1,310 @@
|
|||
package info.nightscout.ui.dialogs
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.google.common.base.Joiner
|
||||
import info.nightscout.androidaps.dialogs.DialogFragmentWithDate
|
||||
import info.nightscout.androidaps.extensions.formatColor
|
||||
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||
import info.nightscout.androidaps.interfaces.CommandQueue
|
||||
import info.nightscout.androidaps.interfaces.Constraints
|
||||
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||
import info.nightscout.androidaps.logging.UserEntryLogger
|
||||
import info.nightscout.androidaps.utils.DecimalFormatter
|
||||
import info.nightscout.androidaps.utils.DefaultValueHelper
|
||||
import info.nightscout.androidaps.utils.ToastUtils
|
||||
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
|
||||
import info.nightscout.androidaps.utils.extensions.toSignedString
|
||||
import info.nightscout.androidaps.utils.protection.ProtectionCheck
|
||||
import info.nightscout.core.profile.toMgdl
|
||||
import info.nightscout.core.pumpExtensions.insertBolusTransaction
|
||||
import info.nightscout.database.entities.TemporaryTarget
|
||||
import info.nightscout.database.entities.UserEntry
|
||||
import info.nightscout.database.entities.ValueWithUnit
|
||||
import info.nightscout.database.impl.AppRepository
|
||||
import info.nightscout.database.impl.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
|
||||
import info.nightscout.interfaces.BolusTimer
|
||||
import info.nightscout.interfaces.Config
|
||||
import info.nightscout.interfaces.GlucoseUnit
|
||||
import info.nightscout.interfaces.constraints.Constraint
|
||||
import info.nightscout.interfaces.profile.Profile
|
||||
import info.nightscout.interfaces.pump.DetailedBolusInfo
|
||||
import info.nightscout.interfaces.queue.Callback
|
||||
import info.nightscout.interfaces.ui.ActivityNames
|
||||
import info.nightscout.interfaces.utils.HtmlHelper
|
||||
import info.nightscout.rx.logging.LTag
|
||||
import info.nightscout.shared.SafeParse
|
||||
import info.nightscout.shared.extensions.toVisibility
|
||||
import info.nightscout.shared.interfaces.ResourceHelper
|
||||
import info.nightscout.shared.utils.T
|
||||
import info.nightscout.ui.R
|
||||
import info.nightscout.ui.databinding.DialogInsulinBinding
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import io.reactivex.rxjava3.kotlin.plusAssign
|
||||
import java.text.DecimalFormat
|
||||
import java.util.LinkedList
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
|
||||
class InsulinDialog : DialogFragmentWithDate() {
|
||||
|
||||
@Inject lateinit var constraintChecker: Constraints
|
||||
@Inject lateinit var rh: ResourceHelper
|
||||
@Inject lateinit var defaultValueHelper: DefaultValueHelper
|
||||
@Inject lateinit var profileFunction: ProfileFunction
|
||||
@Inject lateinit var commandQueue: CommandQueue
|
||||
@Inject lateinit var activePlugin: ActivePlugin
|
||||
@Inject lateinit var ctx: Context
|
||||
@Inject lateinit var repository: AppRepository
|
||||
@Inject lateinit var config: Config
|
||||
@Inject lateinit var bolusTimer: BolusTimer
|
||||
@Inject lateinit var uel: UserEntryLogger
|
||||
@Inject lateinit var protectionCheck: ProtectionCheck
|
||||
@Inject lateinit var activityNames: ActivityNames
|
||||
|
||||
companion object {
|
||||
|
||||
const val PLUS1_DEFAULT = 0.5
|
||||
const val PLUS2_DEFAULT = 1.0
|
||||
const val PLUS3_DEFAULT = 2.0
|
||||
}
|
||||
|
||||
private var queryingProtection = false
|
||||
private val disposable = CompositeDisposable()
|
||||
private var _binding: DialogInsulinBinding? = null
|
||||
|
||||
// This property is only valid between onCreateView and onDestroyView.
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private val textWatcher: TextWatcher = object : TextWatcher {
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
validateInputs()
|
||||
}
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
|
||||
}
|
||||
|
||||
private fun validateInputs() {
|
||||
val maxInsulin = constraintChecker.getMaxBolusAllowed().value()
|
||||
if (abs(binding.time.value.toInt()) > 12 * 60) {
|
||||
binding.time.value = 0.0
|
||||
ToastUtils.warnToast(context, R.string.constraint_applied)
|
||||
}
|
||||
if (binding.amount.value > maxInsulin) {
|
||||
binding.amount.value = 0.0
|
||||
ToastUtils.warnToast(context, R.string.bolus_constraint_applied)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(savedInstanceState: Bundle) {
|
||||
super.onSaveInstanceState(savedInstanceState)
|
||||
savedInstanceState.putDouble("time", binding.time.value)
|
||||
savedInstanceState.putDouble("amount", binding.amount.value)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
onCreateViewGeneral()
|
||||
_binding = DialogInsulinBinding.inflate(inflater, container, false)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
if (config.NSCLIENT) {
|
||||
binding.recordOnly.isChecked = true
|
||||
binding.recordOnly.isEnabled = false
|
||||
}
|
||||
val maxInsulin = constraintChecker.getMaxBolusAllowed().value()
|
||||
|
||||
binding.time.setParams(
|
||||
savedInstanceState?.getDouble("time")
|
||||
?: 0.0, -12 * 60.0, 12 * 60.0, 5.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher
|
||||
)
|
||||
binding.amount.setParams(
|
||||
savedInstanceState?.getDouble("amount")
|
||||
?: 0.0, 0.0, maxInsulin, activePlugin.activePump.pumpDescription.bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, binding.okcancel.ok, textWatcher
|
||||
)
|
||||
|
||||
val plus05Text = sp.getDouble(rh.gs(R.string.key_insulin_button_increment_1), PLUS1_DEFAULT).toSignedString(activePlugin.activePump)
|
||||
binding.plus05.text = plus05Text
|
||||
binding.plus05.contentDescription = rh.gs(R.string.overview_insulin_label) + " " + plus05Text
|
||||
binding.plus05.setOnClickListener {
|
||||
binding.amount.value = max(
|
||||
0.0, binding.amount.value
|
||||
+ sp.getDouble(rh.gs(R.string.key_insulin_button_increment_1), PLUS1_DEFAULT)
|
||||
)
|
||||
validateInputs()
|
||||
binding.amount.announceValue()
|
||||
}
|
||||
val plus10Text = sp.getDouble(rh.gs(R.string.key_insulin_button_increment_2), PLUS2_DEFAULT).toSignedString(activePlugin.activePump)
|
||||
binding.plus10.text = plus10Text
|
||||
binding.plus10.contentDescription = rh.gs(R.string.overview_insulin_label) + " " + plus10Text
|
||||
binding.plus10.setOnClickListener {
|
||||
binding.amount.value = max(
|
||||
0.0, binding.amount.value
|
||||
+ sp.getDouble(rh.gs(R.string.key_insulin_button_increment_2), PLUS2_DEFAULT)
|
||||
)
|
||||
validateInputs()
|
||||
binding.amount.announceValue()
|
||||
}
|
||||
val plus20Text = sp.getDouble(rh.gs(R.string.key_insulin_button_increment_3), PLUS3_DEFAULT).toSignedString(activePlugin.activePump)
|
||||
binding.plus20.text = plus20Text
|
||||
binding.plus20.contentDescription = rh.gs(R.string.overview_insulin_label) + " " + plus20Text
|
||||
binding.plus20.setOnClickListener {
|
||||
binding.amount.value = max(
|
||||
0.0, binding.amount.value
|
||||
+ sp.getDouble(rh.gs(R.string.key_insulin_button_increment_3), PLUS3_DEFAULT)
|
||||
)
|
||||
validateInputs()
|
||||
binding.amount.announceValue()
|
||||
}
|
||||
|
||||
binding.timeLayout.visibility = View.GONE
|
||||
binding.recordOnly.setOnCheckedChangeListener { _, isChecked: Boolean ->
|
||||
binding.timeLayout.visibility = isChecked.toVisibility()
|
||||
}
|
||||
binding.insulinLabel.labelFor = binding.amount.editTextId
|
||||
binding.timeLabel.labelFor = binding.time.editTextId
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
disposable.clear()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
override fun submit(): Boolean {
|
||||
if (_binding == null) return false
|
||||
val pumpDescription = activePlugin.activePump.pumpDescription
|
||||
val insulin = SafeParse.stringToDouble(binding.amount.text)
|
||||
val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(insulin)).value()
|
||||
val actions: LinkedList<String?> = LinkedList()
|
||||
val units = profileFunction.getUnits()
|
||||
val unitLabel = if (units == GlucoseUnit.MMOL) rh.gs(R.string.mmol) else rh.gs(R.string.mgdl)
|
||||
val recordOnlyChecked = binding.recordOnly.isChecked
|
||||
val eatingSoonChecked = binding.startEatingSoonTt.isChecked
|
||||
|
||||
if (insulinAfterConstraints > 0) {
|
||||
actions.add(rh.gs(R.string.bolus) + ": " + DecimalFormatter.toPumpSupportedBolus(insulinAfterConstraints, activePlugin.activePump, rh).formatColor(context, rh, R.attr.bolusColor))
|
||||
if (recordOnlyChecked)
|
||||
actions.add(rh.gs(R.string.bolusrecordedonly).formatColor(context, rh, R.attr.warningColor))
|
||||
if (abs(insulinAfterConstraints - insulin) > pumpDescription.pumpType.determineCorrectBolusStepSize(insulinAfterConstraints))
|
||||
actions.add(rh.gs(R.string.bolusconstraintappliedwarn, insulin, insulinAfterConstraints).formatColor(context, rh, R.attr.warningColor))
|
||||
}
|
||||
val eatingSoonTTDuration = defaultValueHelper.determineEatingSoonTTDuration()
|
||||
val eatingSoonTT = defaultValueHelper.determineEatingSoonTT()
|
||||
if (eatingSoonChecked)
|
||||
actions.add(
|
||||
rh.gs(R.string.temp_target_short) + ": " + (DecimalFormatter.to1Decimal(eatingSoonTT) + " " + unitLabel + " (" + rh.gs(R.string.format_mins, eatingSoonTTDuration) + ")")
|
||||
.formatColor(context, rh, R.attr.tempTargetConfirmation)
|
||||
)
|
||||
|
||||
val timeOffset = binding.time.value.toInt()
|
||||
val time = dateUtil.now() + T.mins(timeOffset.toLong()).msecs()
|
||||
if (timeOffset != 0)
|
||||
actions.add(rh.gs(R.string.time) + ": " + dateUtil.dateAndTimeString(time))
|
||||
|
||||
val notes = binding.notesLayout.notes.text.toString()
|
||||
if (notes.isNotEmpty())
|
||||
actions.add(rh.gs(R.string.notes_label) + ": " + notes)
|
||||
|
||||
if (insulinAfterConstraints > 0 || eatingSoonChecked) {
|
||||
activity?.let { activity ->
|
||||
OKDialog.showConfirmation(activity, rh.gs(R.string.bolus), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
|
||||
if (eatingSoonChecked) {
|
||||
uel.log(
|
||||
UserEntry.Action.TT, UserEntry.Sources.InsulinDialog,
|
||||
notes,
|
||||
ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.EATING_SOON),
|
||||
ValueWithUnit.fromGlucoseUnit(eatingSoonTT, units.asText),
|
||||
ValueWithUnit.Minute(eatingSoonTTDuration)
|
||||
)
|
||||
disposable += repository.runTransactionForResult(
|
||||
InsertAndCancelCurrentTemporaryTargetTransaction(
|
||||
timestamp = System.currentTimeMillis(),
|
||||
duration = TimeUnit.MINUTES.toMillis(eatingSoonTTDuration.toLong()),
|
||||
reason = TemporaryTarget.Reason.EATING_SOON,
|
||||
lowTarget = Profile.toMgdl(eatingSoonTT, profileFunction.getUnits()),
|
||||
highTarget = Profile.toMgdl(eatingSoonTT, profileFunction.getUnits())
|
||||
)
|
||||
).subscribe({ result ->
|
||||
result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted temp target $it") }
|
||||
result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") }
|
||||
}, {
|
||||
aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it)
|
||||
})
|
||||
}
|
||||
if (insulinAfterConstraints > 0) {
|
||||
val detailedBolusInfo = DetailedBolusInfo()
|
||||
detailedBolusInfo.eventType = DetailedBolusInfo.EventType.CORRECTION_BOLUS
|
||||
detailedBolusInfo.insulin = insulinAfterConstraints
|
||||
detailedBolusInfo.context = context
|
||||
detailedBolusInfo.notes = notes
|
||||
detailedBolusInfo.timestamp = time
|
||||
if (recordOnlyChecked) {
|
||||
uel.log(UserEntry.Action.BOLUS, UserEntry.Sources.InsulinDialog,
|
||||
rh.gs(R.string.record) + if (notes.isNotEmpty()) ": $notes" else "",
|
||||
ValueWithUnit.SimpleString(rh.gsNotLocalised(R.string.record)),
|
||||
ValueWithUnit.Insulin(insulinAfterConstraints),
|
||||
ValueWithUnit.Minute(timeOffset).takeIf { timeOffset != 0 })
|
||||
disposable += repository.runTransactionForResult(detailedBolusInfo.insertBolusTransaction())
|
||||
.subscribe(
|
||||
{ result -> result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted bolus $it") } },
|
||||
{ aapsLogger.error(LTag.DATABASE, "Error while saving bolus", it) }
|
||||
)
|
||||
if (timeOffset == 0)
|
||||
bolusTimer.removeAutomationEventBolusReminder()
|
||||
} else {
|
||||
uel.log(
|
||||
UserEntry.Action.BOLUS, UserEntry.Sources.InsulinDialog,
|
||||
notes,
|
||||
ValueWithUnit.Insulin(insulinAfterConstraints)
|
||||
)
|
||||
commandQueue.bolus(detailedBolusInfo, object : Callback() {
|
||||
override fun run() {
|
||||
if (!result.success) {
|
||||
activityNames.runAlarm(ctx, result.comment, rh.gs(R.string.treatmentdeliveryerror), R.raw.boluserror)
|
||||
} else {
|
||||
bolusTimer.removeAutomationEventBolusReminder()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
} else
|
||||
activity?.let { activity ->
|
||||
OKDialog.show(activity, rh.gs(R.string.bolus), rh.gs(R.string.no_action_selected))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (!queryingProtection) {
|
||||
queryingProtection = true
|
||||
activity?.let { activity ->
|
||||
val cancelFail = {
|
||||
queryingProtection = false
|
||||
aapsLogger.debug(LTag.APS, "Dialog canceled on resume protection: ${this.javaClass.simpleName}")
|
||||
ToastUtils.warnToast(ctx, R.string.dialog_canceled)
|
||||
dismiss()
|
||||
}
|
||||
protectionCheck.queryProtection(activity, ProtectionCheck.Protection.BOLUS, { queryingProtection = false }, cancelFail, cancelFail)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -58,6 +58,13 @@
|
|||
<string name="record_pump_site_change">Record pump site change</string>
|
||||
<string name="record_insulin_cartridge_change">Record insulin cartridge change</string>
|
||||
|
||||
<!-- InsulinDialog -->
|
||||
<string name="key_insulin_button_increment_1" translatable="false">insulin_button_increment_1</string>
|
||||
<string name="key_insulin_button_increment_2" translatable="false">insulin_button_increment_2</string>
|
||||
<string name="key_insulin_button_increment_3" translatable="false">insulin_button_increment_3</string>
|
||||
<string name="do_not_bolus_record_only">Do not bolus, record only</string>
|
||||
<string name="bolusrecordedonly">Bolus will be recorded only (not delivered by pump)</string>
|
||||
|
||||
|
||||
<!-- Treatments -->
|
||||
<string name="no_records_available">No records available</string>
|
||||
|
|
|
@ -206,8 +206,6 @@
|
|||
<string name="key_dark" translatable="false">dark</string>
|
||||
<string name="key_input_design" translatable="false">input_design</string>
|
||||
<string name="key_complication_tap_action" translatable="false">complication_tap_action</string>
|
||||
<string name="key_insulin_button_increment_1" translatable="false">insulin_button_increment_1</string>
|
||||
<string name="key_insulin_button_increment_2" translatable="false">insulin_button_increment_2</string>
|
||||
<string name="key_carbs_button_increment_1" translatable="false">carbs_button_increment_1</string>
|
||||
<string name="key_carbs_button_increment_2" translatable="false">carbs_button_increment_2</string>
|
||||
<string name="increment">increment</string>
|
||||
|
|
Loading…
Reference in a new issue