From 18b434b2e24f06f54433d11f0a00102fee9626b8 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Fri, 11 Nov 2022 10:41:15 +0100 Subject: [PATCH] InsulinDialog -> ui --- .../info/nightscout/androidaps/MainApp.kt | 2 +- .../androidaps/di/FragmentsModule.kt | 2 - .../androidaps/dialogs/FillDialog.kt | 2 - .../androidaps/dialogs/InsulinDialog.kt | 309 ----------------- .../general/overview/OverviewFragment.kt | 2 +- .../wear/wearintegration/DataHandlerMobile.kt | 2 +- app/src/main/res/layout/dialog_wizard.xml | 2 +- app/src/main/res/values/strings.xml | 5 - .../nightscout/ui/activities/StatsActivity.kt | 2 +- .../ActivityMonitor.kt | 2 +- .../java/info/nightscout/ui/di/UiModule.kt | 2 + .../nightscout/ui/dialogs/InsulinDialog.kt | 310 ++++++++++++++++++ .../ui}/extensions/DoubleToSignedString.kt | 0 .../src/main/res/layout/dialog_insulin.xml | 0 ui/src/main/res/values/strings.xml | 7 + wear/src/main/res/values/strings.xml | 2 - 16 files changed, 325 insertions(+), 326 deletions(-) delete mode 100644 app/src/main/java/info/nightscout/androidaps/dialogs/FillDialog.kt rename ui/src/main/java/info/nightscout/ui/{utils => activityMonitor}/ActivityMonitor.kt (99%) create mode 100644 ui/src/main/java/info/nightscout/ui/dialogs/InsulinDialog.kt rename {app/src/main/java/info/nightscout/androidaps/utils => ui/src/main/java/info/nightscout/ui}/extensions/DoubleToSignedString.kt (100%) rename {app => ui}/src/main/res/layout/dialog_insulin.xml (100%) diff --git a/app/src/main/java/info/nightscout/androidaps/MainApp.kt b/app/src/main/java/info/nightscout/androidaps/MainApp.kt index 5be051f1d0..0f17bfecad 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainApp.kt +++ b/app/src/main/java/info/nightscout/androidaps/MainApp.kt @@ -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 diff --git a/app/src/main/java/info/nightscout/androidaps/di/FragmentsModule.kt b/app/src/main/java/info/nightscout/androidaps/di/FragmentsModule.kt index acd0f1877e..9a2a9dda6e 100644 --- a/app/src/main/java/info/nightscout/androidaps/di/FragmentsModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/di/FragmentsModule.kt @@ -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 diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/FillDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/FillDialog.kt deleted file mode 100644 index 2e4bef4f6d..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/FillDialog.kt +++ /dev/null @@ -1,2 +0,0 @@ -package info.nightscout.androidaps.dialogs - diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt index ccb5ca7e61..2e4bef4f6d 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt @@ -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 = 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("
").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) - } - } - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt index f6f780966e..c99e239d16 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt @@ -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 diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataHandlerMobile.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataHandlerMobile.kt index 6a52c89dac..c555df697d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataHandlerMobile.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataHandlerMobile.kt @@ -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 diff --git a/app/src/main/res/layout/dialog_wizard.xml b/app/src/main/res/layout/dialog_wizard.xml index 419fbde9f8..7a2df77232 100644 --- a/app/src/main/res/layout/dialog_wizard.xml +++ b/app/src/main/res/layout/dialog_wizard.xml @@ -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"> show_treatment_button Sends a calibration to xDrip+ or open BYODA calibration dialog Opens xDrip+ or BYODA, back buttons returns to AAPS - insulin_button_increment_1 - insulin_button_increment_2 - insulin_button_increment_3 Number of carbs to add when button is pressed Amount of insulin to add when button is pressed Could not launch CGM application. Make sure it is installed. @@ -381,8 +378,6 @@ Allow automated crash reporting and feature usage data to be sent to the developers via the fabric.io service. Please update your Dexcom app to supported version Dexcom app is not installed. - Do not bolus, record only - Bolus will be recorded only (not delivered by pump) SMB set by pump Activity Blood Glucose Impact diff --git a/ui/src/main/java/info/nightscout/ui/activities/StatsActivity.kt b/ui/src/main/java/info/nightscout/ui/activities/StatsActivity.kt index 14fe7b7a77..2b1c996454 100644 --- a/ui/src/main/java/info/nightscout/ui/activities/StatsActivity.kt +++ b/ui/src/main/java/info/nightscout/ui/activities/StatsActivity.kt @@ -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 diff --git a/ui/src/main/java/info/nightscout/ui/utils/ActivityMonitor.kt b/ui/src/main/java/info/nightscout/ui/activityMonitor/ActivityMonitor.kt similarity index 99% rename from ui/src/main/java/info/nightscout/ui/utils/ActivityMonitor.kt rename to ui/src/main/java/info/nightscout/ui/activityMonitor/ActivityMonitor.kt index e755c1dd59..ef7c10413a 100644 --- a/ui/src/main/java/info/nightscout/ui/utils/ActivityMonitor.kt +++ b/ui/src/main/java/info/nightscout/ui/activityMonitor/ActivityMonitor.kt @@ -1,4 +1,4 @@ -package info.nightscout.ui.utils +package info.nightscout.ui.activityMonitor import android.app.Activity import android.app.Application diff --git a/ui/src/main/java/info/nightscout/ui/di/UiModule.kt b/ui/src/main/java/info/nightscout/ui/di/UiModule.kt index f58bc19144..0968c35b24 100644 --- a/ui/src/main/java/info/nightscout/ui/di/UiModule.kt +++ b/ui/src/main/java/info/nightscout/ui/di/UiModule.kt @@ -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 diff --git a/ui/src/main/java/info/nightscout/ui/dialogs/InsulinDialog.kt b/ui/src/main/java/info/nightscout/ui/dialogs/InsulinDialog.kt new file mode 100644 index 0000000000..8d5c53a3be --- /dev/null +++ b/ui/src/main/java/info/nightscout/ui/dialogs/InsulinDialog.kt @@ -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 = 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("
").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) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/utils/extensions/DoubleToSignedString.kt b/ui/src/main/java/info/nightscout/ui/extensions/DoubleToSignedString.kt similarity index 100% rename from app/src/main/java/info/nightscout/androidaps/utils/extensions/DoubleToSignedString.kt rename to ui/src/main/java/info/nightscout/ui/extensions/DoubleToSignedString.kt diff --git a/app/src/main/res/layout/dialog_insulin.xml b/ui/src/main/res/layout/dialog_insulin.xml similarity index 100% rename from app/src/main/res/layout/dialog_insulin.xml rename to ui/src/main/res/layout/dialog_insulin.xml diff --git a/ui/src/main/res/values/strings.xml b/ui/src/main/res/values/strings.xml index c578abadc1..14a6f5ecfd 100644 --- a/ui/src/main/res/values/strings.xml +++ b/ui/src/main/res/values/strings.xml @@ -58,6 +58,13 @@ Record pump site change Record insulin cartridge change + + insulin_button_increment_1 + insulin_button_increment_2 + insulin_button_increment_3 + Do not bolus, record only + Bolus will be recorded only (not delivered by pump) + No records available diff --git a/wear/src/main/res/values/strings.xml b/wear/src/main/res/values/strings.xml index d97cab5974..2c05f1d147 100644 --- a/wear/src/main/res/values/strings.xml +++ b/wear/src/main/res/values/strings.xml @@ -206,8 +206,6 @@ dark input_design complication_tap_action - insulin_button_increment_1 - insulin_button_increment_2 carbs_button_increment_1 carbs_button_increment_2 increment