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

397 lines
20 KiB
Kotlin

package info.nightscout.androidaps.dialogs
import android.content.Context
import android.os.Bundle
import android.os.Handler
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.view.WindowManager
import androidx.fragment.app.FragmentManager
import dagger.android.support.DaggerDialogFragment
import info.nightscout.androidaps.R
import info.nightscout.androidaps.activities.ErrorHelperActivity
import info.nightscout.androidaps.database.entities.XXXValueWithUnit
import info.nightscout.androidaps.database.entities.UserEntry.Action
import info.nightscout.androidaps.database.entities.UserEntry.Sources
import info.nightscout.androidaps.database.entities.UserEntry.Units
import info.nightscout.androidaps.database.entities.UserEntry.ValueWithUnit
import info.nightscout.androidaps.databinding.DialogLoopBinding
import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.events.EventRefreshOverview
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.ToastUtils
import info.nightscout.androidaps.utils.alertDialogs.OKDialog
import info.nightscout.androidaps.utils.extensions.toVisibility
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import javax.inject.Inject
class LoopDialog : DaggerDialogFragment() {
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var ctx: Context
@Inject lateinit var sp: SP
@Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var loopPlugin: LoopPlugin
@Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var constraintChecker: ConstraintChecker
@Inject lateinit var commandQueue: CommandQueueProvider
@Inject lateinit var configBuilderPlugin: ConfigBuilderPlugin
@Inject lateinit var uel: UserEntryLogger
private var showOkCancel: Boolean = true
private var _binding: DialogLoopBinding? = null
private var loopHandler = Handler()
private var refreshDialog: Runnable? = 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)
}
override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState)
savedInstanceState.putInt("showOkCancel", if (showOkCancel) 1 else 0)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
// load data from bundle
(savedInstanceState ?: arguments)?.let { bundle ->
showOkCancel = bundle.getInt("showOkCancel", 1) == 1
}
dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE)
dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN)
isCancelable = true
dialog?.setCanceledOnTouchOutside(false)
_binding = DialogLoopBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
updateGUI("LoopDialogOnViewCreated")
binding.overviewCloseloop.setOnClickListener { if (showOkCancel) onClickOkCancelEnabled(it) else onClick(it); dismiss() }
binding.overviewLgsloop.setOnClickListener { if (showOkCancel) onClickOkCancelEnabled(it) else onClick(it); dismiss() }
binding.overviewOpenloop.setOnClickListener { if (showOkCancel) onClickOkCancelEnabled(it) else onClick(it); dismiss() }
binding.overviewDisable.setOnClickListener { if (showOkCancel) onClickOkCancelEnabled(it) else onClick(it); dismiss() }
binding.overviewEnable.setOnClickListener { if (showOkCancel) onClickOkCancelEnabled(it) else onClick(it); dismiss() }
binding.overviewResume.setOnClickListener { if (showOkCancel) onClickOkCancelEnabled(it) else onClick(it); dismiss() }
binding.overviewReconnect.setOnClickListener { if (showOkCancel) onClickOkCancelEnabled(it) else onClick(it); dismiss() }
binding.overviewSuspend1h.setOnClickListener { if (showOkCancel) onClickOkCancelEnabled(it) else onClick(it); dismiss() }
binding.overviewSuspend2h.setOnClickListener { if (showOkCancel) onClickOkCancelEnabled(it) else onClick(it); dismiss() }
binding.overviewSuspend3h.setOnClickListener { if (showOkCancel) onClickOkCancelEnabled(it) else onClick(it); dismiss() }
binding.overviewSuspend10h.setOnClickListener { if (showOkCancel) onClickOkCancelEnabled(it) else onClick(it); dismiss() }
binding.overviewDisconnect15m.setOnClickListener { if (showOkCancel) onClickOkCancelEnabled(it) else onClick(it); dismiss() }
binding.overviewDisconnect30m.setOnClickListener { if (showOkCancel) onClickOkCancelEnabled(it) else onClick(it); dismiss() }
binding.overviewDisconnect1h.setOnClickListener { if (showOkCancel) onClickOkCancelEnabled(it) else onClick(it); dismiss() }
binding.overviewDisconnect2h.setOnClickListener { if (showOkCancel) onClickOkCancelEnabled(it) else onClick(it); dismiss() }
binding.overviewDisconnect3h.setOnClickListener { if (showOkCancel) onClickOkCancelEnabled(it) else onClick(it); dismiss() }
// cancel button
binding.cancel.setOnClickListener { dismiss() }
refreshDialog = Runnable {
scheduleUpdateGUI()
loopHandler.postDelayed(refreshDialog, 15 * 1000L)
}
loopHandler.postDelayed(refreshDialog, 15 * 1000L)
}
@Synchronized
override fun onDestroyView() {
super.onDestroyView()
_binding = null
loopHandler.removeCallbacksAndMessages(null)
}
var task: Runnable? = null
private fun scheduleUpdateGUI() {
class UpdateRunnable : Runnable {
override fun run() {
updateGUI("refreshDialog")
task = null
}
}
view?.removeCallbacks(task)
task = UpdateRunnable()
view?.postDelayed(task, 500)
}
@Synchronized
fun updateGUI(from: String) {
if (_binding == null) return
aapsLogger.debug("UpdateGUI from $from")
val pumpDescription: PumpDescription = activePlugin.activePump.pumpDescription
val closedLoopAllowed = constraintChecker.isClosedLoopAllowed(Constraint(true))
val lgsEnabled = constraintChecker.isLgsAllowed(Constraint(true))
val apsMode = sp.getString(R.string.key_aps_mode, "open")
if (profileFunction.isProfileValid("LoopDialogUpdateGUI")) {
if (loopPlugin.isEnabled(PluginType.LOOP)) {
when {
closedLoopAllowed.value() -> {
binding.overviewCloseloop.visibility = (apsMode != "closed").toVisibility()
binding.overviewLgsloop.visibility = (apsMode != "lgs").toVisibility()
binding.overviewOpenloop.visibility = (apsMode != "open").toVisibility()
}
lgsEnabled.value() -> {
binding.overviewCloseloop.visibility = View.GONE
binding.overviewLgsloop.visibility = (apsMode != "lgs").toVisibility()
binding.overviewOpenloop.visibility = (apsMode != "open").toVisibility()
}
else -> {
binding.overviewCloseloop.visibility = View.GONE
binding.overviewLgsloop.visibility = View.GONE
binding.overviewOpenloop.visibility = View.GONE
}
}
binding.overviewEnable.visibility = View.GONE
binding.overviewDisable.visibility = View.VISIBLE
if (!loopPlugin.isSuspended) {
binding.overviewSuspendHeader.text = resourceHelper.gs(R.string.suspendloop)
binding.overviewResume.visibility = View.GONE
binding.overviewSuspendButtons.visibility = View.VISIBLE
binding.overviewSuspend.visibility = View.VISIBLE
} else {
if (!loopPlugin.isDisconnected) {
binding.overviewSuspendHeader.text = resourceHelper.gs(R.string.resumeloop)
binding.overviewResume.visibility = View.VISIBLE
binding.overviewSuspendButtons.visibility = View.GONE
binding.overviewSuspend.visibility = View.VISIBLE
} else
binding.overviewSuspend.visibility = View.GONE
}
} else {
binding.overviewEnable.visibility = View.VISIBLE
binding.overviewDisable.visibility = View.GONE
binding.overviewSuspend.visibility = View.GONE
}
if (!loopPlugin.isDisconnected) {
binding.overviewPumpHeader.text = resourceHelper.gs(R.string.disconnectpump)
binding.overviewDisconnect15m.visibility = pumpDescription.tempDurationStep15mAllowed.toVisibility()
binding.overviewDisconnect30m.visibility = pumpDescription.tempDurationStep30mAllowed.toVisibility()
binding.overviewDisconnectButtons.visibility = View.VISIBLE
binding.overviewReconnect.visibility = View.GONE
} else {
binding.overviewPumpHeader.text = resourceHelper.gs(R.string.reconnect)
binding.overviewDisconnectButtons.visibility = View.GONE
binding.overviewReconnect.visibility = View.VISIBLE
}
}
val profile = profileFunction.getProfile()
val profileStore = activePlugin.activeProfileInterface.profile
if (profile == null || profileStore == null) {
ToastUtils.showToastInUiThread(ctx, resourceHelper.gs(R.string.noprofile))
dismiss()
return
}
}
private fun onClickOkCancelEnabled(v: View): Boolean {
var description = ""
when (v.id) {
R.id.overview_closeloop -> description = resourceHelper.gs(R.string.closedloop)
R.id.overview_lgsloop -> description = resourceHelper.gs(R.string.lowglucosesuspend)
R.id.overview_openloop -> description = resourceHelper.gs(R.string.openloop)
R.id.overview_disable -> description = resourceHelper.gs(R.string.disableloop)
R.id.overview_enable -> description = resourceHelper.gs(R.string.enableloop)
R.id.overview_resume -> description = resourceHelper.gs(R.string.resume)
R.id.overview_reconnect -> description = resourceHelper.gs(R.string.reconnect)
R.id.overview_suspend_1h -> description = resourceHelper.gs(R.string.suspendloopfor1h)
R.id.overview_suspend_2h -> description = resourceHelper.gs(R.string.suspendloopfor2h)
R.id.overview_suspend_3h -> description = resourceHelper.gs(R.string.suspendloopfor3h)
R.id.overview_suspend_10h -> description = resourceHelper.gs(R.string.suspendloopfor10h)
R.id.overview_disconnect_15m -> description = resourceHelper.gs(R.string.disconnectpumpfor15m)
R.id.overview_disconnect_30m -> description = resourceHelper.gs(R.string.disconnectpumpfor30m)
R.id.overview_disconnect_1h -> description = resourceHelper.gs(R.string.disconnectpumpfor1h)
R.id.overview_disconnect_2h -> description = resourceHelper.gs(R.string.disconnectpumpfor2h)
R.id.overview_disconnect_3h -> description = resourceHelper.gs(R.string.disconnectpumpfor3h)
}
activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.confirm), description, Runnable {
onClick(v)
})
}
return true
}
fun onClick(v: View): Boolean {
val profile = profileFunction.getProfile() ?: return true
when (v.id) {
R.id.overview_closeloop -> {
uel.log(Action.CLOSED_LOOP_MODE, ValueWithUnit(Sources.LoopDialog))
sp.putString(R.string.key_aps_mode, "closed")
rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.closedloop)))
return true
}
R.id.overview_lgsloop -> {
uel.log(Action.LGS_LOOP_MODE, ValueWithUnit(Sources.LoopDialog))
sp.putString(R.string.key_aps_mode, "lgs")
rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.lowglucosesuspend)))
return true
}
R.id.overview_openloop -> {
uel.log(Action.OPEN_LOOP_MODE, ValueWithUnit(Sources.LoopDialog))
sp.putString(R.string.key_aps_mode, "open")
rxBus.send(EventPreferenceChange(resourceHelper.gs(R.string.lowglucosesuspend)))
return true
}
R.id.overview_disable -> {
uel.log(Action.LOOP_DISABLED, ValueWithUnit(Sources.LoopDialog))
loopPlugin.setPluginEnabled(PluginType.LOOP, false)
loopPlugin.setFragmentVisible(PluginType.LOOP, false)
configBuilderPlugin.storeSettings("DisablingLoop")
rxBus.send(EventRefreshOverview("suspendmenu"))
commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() {
if (!result.success) {
ToastUtils.showToastInUiThread(ctx, resourceHelper.gs(R.string.tempbasaldeliveryerror))
}
}
})
loopPlugin.createOfflineEvent(24 * 60) // upload 24h, we don't know real duration
return true
}
R.id.overview_enable -> {
uel.log(Action.LOOP_ENABLED, ValueWithUnit(Sources.LoopDialog))
loopPlugin.setPluginEnabled(PluginType.LOOP, true)
loopPlugin.setFragmentVisible(PluginType.LOOP, true)
configBuilderPlugin.storeSettings("EnablingLoop")
rxBus.send(EventRefreshOverview("suspendmenu"))
loopPlugin.createOfflineEvent(0)
return true
}
R.id.overview_resume, R.id.overview_reconnect -> {
//uel.log(if (v.id == R.id.overview_resume) Action.RESUME else Action.RECONNECT )
uel.log(if (v.id==R.id.overview_resume) Action.RESUME else Action.RECONNECT, ValueWithUnit(Sources.LoopDialog))
loopPlugin.suspendTo(0L)
rxBus.send(EventRefreshOverview("suspendmenu"))
commandQueue.cancelTempBasal(true, object : Callback() {
override fun run() {
if (!result.success) {
ErrorHelperActivity.runAlarm(ctx, result.comment, resourceHelper.gs(R.string.tempbasaldeliveryerror), info.nightscout.androidaps.dana.R.raw.boluserror)
}
}
})
sp.putBoolean(R.string.key_objectiveusereconnect, true)
loopPlugin.createOfflineEvent(0)
return true
}
R.id.overview_suspend_1h -> {
//uel.log(Action.SUSPEND, XXXValueWithUnit.Hour(1))
uel.log(Action.SUSPEND, ValueWithUnit(Sources.LoopDialog), ValueWithUnit(1, Units.H))
loopPlugin.suspendLoop(60)
rxBus.send(EventRefreshOverview("suspendmenu"))
return true
}
R.id.overview_suspend_2h -> {
//uel.log(Action.SUSPEND, XXXValueWithUnit.Hour(2))
uel.log(Action.SUSPEND, ValueWithUnit(Sources.LoopDialog), ValueWithUnit(2, Units.H))
loopPlugin.suspendLoop(120)
rxBus.send(EventRefreshOverview("suspendmenu"))
return true
}
R.id.overview_suspend_3h -> {
//uel.log(Action.SUSPEND, XXXValueWithUnit.Hour(3))
uel.log(Action.SUSPEND, ValueWithUnit(Sources.LoopDialog), ValueWithUnit(3, Units.H))
loopPlugin.suspendLoop(180)
rxBus.send(EventRefreshOverview("suspendmenu"))
return true
}
R.id.overview_suspend_10h -> {
//uel.log(Action.SUSPEND, XXXValueWithUnit.Hour(10))
uel.log(Action.SUSPEND, ValueWithUnit(Sources.LoopDialog), ValueWithUnit(10, Units.H))
loopPlugin.suspendLoop(600)
rxBus.send(EventRefreshOverview("suspendmenu"))
return true
}
R.id.overview_disconnect_15m -> {
//Muel.log(Action.DISCONNECT, XXXValueWithUnit.Minute(15))
uel.log(Action.DISCONNECT, ValueWithUnit(Sources.LoopDialog), ValueWithUnit(15, Units.M))
loopPlugin.disconnectPump(15, profile)
rxBus.send(EventRefreshOverview("suspendmenu"))
return true
}
R.id.overview_disconnect_30m -> {
//uel.log(Action.DISCONNECT, XXXValueWithUnit.Minute(30))
uel.log(Action.DISCONNECT, ValueWithUnit(Sources.LoopDialog), ValueWithUnit(30, Units.M))
loopPlugin.disconnectPump(30, profile)
rxBus.send(EventRefreshOverview("suspendmenu"))
return true
}
R.id.overview_disconnect_1h -> {
//uel.log(Action.DISCONNECT, XXXValueWithUnit.Hour(1))
uel.log(Action.DISCONNECT, ValueWithUnit(Sources.LoopDialog), ValueWithUnit(1, Units.H))
loopPlugin.disconnectPump(60, profile)
sp.putBoolean(R.string.key_objectiveusedisconnect, true)
rxBus.send(EventRefreshOverview("suspendmenu"))
return true
}
R.id.overview_disconnect_2h -> {
//uel.log(Action.DISCONNECT, XXXValueWithUnit.Hour(2))
uel.log(Action.DISCONNECT, ValueWithUnit(Sources.LoopDialog), ValueWithUnit(2, Units.H))
loopPlugin.disconnectPump(120, profile)
rxBus.send(EventRefreshOverview("suspendmenu"))
return true
}
R.id.overview_disconnect_3h -> {
//uel.log(Action.DISCONNECT, XXXValueWithUnit.Hour(3))
uel.log(Action.DISCONNECT, ValueWithUnit(Sources.LoopDialog), ValueWithUnit(3, Units.H))
loopPlugin.disconnectPump(180, profile)
rxBus.send(EventRefreshOverview("suspendmenu"))
return true
}
}
return false
}
override fun show(manager: FragmentManager, tag: String?) {
try {
manager.beginTransaction().let {
it.add(this, tag)
it.commitAllowingStateLoss()
}
} catch (e: IllegalStateException) {
aapsLogger.debug(e.localizedMessage)
}
}
}