AUTO: user actions & new layout

This commit is contained in:
Milos Kozak 2021-10-28 20:21:15 +02:00
parent 04a104a9b0
commit a56bdcc7c8
54 changed files with 1063 additions and 709 deletions

View file

@ -12,11 +12,6 @@ import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesFragm
import info.nightscout.androidaps.plugins.constraints.objectives.activities.ObjectivesExamDialog import info.nightscout.androidaps.plugins.constraints.objectives.activities.ObjectivesExamDialog
import info.nightscout.androidaps.plugins.general.actions.ActionsFragment import info.nightscout.androidaps.plugins.general.actions.ActionsFragment
import info.nightscout.androidaps.plugins.general.automation.AutomationFragment import info.nightscout.androidaps.plugins.general.automation.AutomationFragment
import info.nightscout.androidaps.plugins.general.automation.dialogs.ChooseActionDialog
import info.nightscout.androidaps.plugins.general.automation.dialogs.ChooseTriggerDialog
import info.nightscout.androidaps.plugins.general.automation.dialogs.EditActionDialog
import info.nightscout.androidaps.plugins.general.automation.dialogs.EditEventDialog
import info.nightscout.androidaps.plugins.general.automation.dialogs.EditTriggerDialog
import info.nightscout.androidaps.plugins.general.food.FoodFragment import info.nightscout.androidaps.plugins.general.food.FoodFragment
import info.nightscout.androidaps.plugins.general.maintenance.MaintenanceFragment import info.nightscout.androidaps.plugins.general.maintenance.MaintenanceFragment
import info.nightscout.androidaps.plugins.general.nsclient.NSClientFragment import info.nightscout.androidaps.plugins.general.nsclient.NSClientFragment
@ -30,6 +25,7 @@ import info.nightscout.androidaps.plugins.profile.local.LocalProfileFragment
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpFragment import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpFragment
import info.nightscout.androidaps.plugins.source.BGSourceFragment import info.nightscout.androidaps.plugins.source.BGSourceFragment
import info.nightscout.androidaps.activities.fragments.* import info.nightscout.androidaps.activities.fragments.*
import info.nightscout.androidaps.plugins.general.automation.dialogs.*
import info.nightscout.androidaps.utils.protection.PasswordCheck import info.nightscout.androidaps.utils.protection.PasswordCheck
@Module @Module
@ -81,6 +77,7 @@ abstract class FragmentsModule {
@ContributesAndroidInjector abstract fun contributesFillDialog(): FillDialog @ContributesAndroidInjector abstract fun contributesFillDialog(): FillDialog
@ContributesAndroidInjector abstract fun contributesChooseActionDialog(): ChooseActionDialog @ContributesAndroidInjector abstract fun contributesChooseActionDialog(): ChooseActionDialog
@ContributesAndroidInjector abstract fun contributesChooseTriggerDialog(): ChooseTriggerDialog @ContributesAndroidInjector abstract fun contributesChooseTriggerDialog(): ChooseTriggerDialog
@ContributesAndroidInjector abstract fun contributesChooseOperationDialog(): ChooseOperationDialog
@ContributesAndroidInjector abstract fun contributesInsulinDialog(): InsulinDialog @ContributesAndroidInjector abstract fun contributesInsulinDialog(): InsulinDialog
@ContributesAndroidInjector abstract fun contributesLoopDialog(): LoopDialog @ContributesAndroidInjector abstract fun contributesLoopDialog(): LoopDialog
@ContributesAndroidInjector abstract fun contributesObjectivesExamDialog(): ObjectivesExamDialog @ContributesAndroidInjector abstract fun contributesObjectivesExamDialog(): ObjectivesExamDialog

View file

@ -12,6 +12,7 @@ import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.HandlerThread import android.os.HandlerThread
import android.util.DisplayMetrics import android.util.DisplayMetrics
import android.util.TypedValue
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.View.OnLongClickListener import android.view.View.OnLongClickListener
@ -49,6 +50,7 @@ import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin
import info.nightscout.androidaps.plugins.aps.loop.events.EventNewOpenLoopNotification import info.nightscout.androidaps.plugins.aps.loop.events.EventNewOpenLoopNotification
import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin
import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus
import info.nightscout.androidaps.plugins.general.overview.activities.QuickWizardListActivity import info.nightscout.androidaps.plugins.general.overview.activities.QuickWizardListActivity
import info.nightscout.androidaps.plugins.general.overview.events.EventUpdateOverview import info.nightscout.androidaps.plugins.general.overview.events.EventUpdateOverview
@ -72,6 +74,7 @@ import info.nightscout.androidaps.utils.protection.ProtectionCheck
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.androidaps.utils.sharedPreferences.SP import info.nightscout.androidaps.utils.sharedPreferences.SP
import info.nightscout.androidaps.utils.ui.SingleClickButton
import info.nightscout.androidaps.utils.ui.UIRunnable import info.nightscout.androidaps.utils.ui.UIRunnable
import info.nightscout.androidaps.utils.wizard.QuickWizard import info.nightscout.androidaps.utils.wizard.QuickWizard
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
@ -118,6 +121,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
@Inject lateinit var glucoseStatusProvider: GlucoseStatusProvider @Inject lateinit var glucoseStatusProvider: GlucoseStatusProvider
@Inject lateinit var overviewData: OverviewData @Inject lateinit var overviewData: OverviewData
@Inject lateinit var overviewPlugin: OverviewPlugin @Inject lateinit var overviewPlugin: OverviewPlugin
@Inject lateinit var automationPlugin: AutomationPlugin
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
@ -459,6 +463,32 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
binding.buttonsLayout.calibrationButton.visibility = ((xDripIsBgSource || dexcomIsSource) && actualBG != null && sp.getBoolean(R.string.key_show_calibration_button, true)).toVisibility() binding.buttonsLayout.calibrationButton.visibility = ((xDripIsBgSource || dexcomIsSource) && actualBG != null && sp.getBoolean(R.string.key_show_calibration_button, true)).toVisibility()
binding.buttonsLayout.cgmButton.visibility = (sp.getBoolean(R.string.key_show_cgm_button, false) && (xDripIsBgSource || dexcomIsSource)).toVisibility() binding.buttonsLayout.cgmButton.visibility = (sp.getBoolean(R.string.key_show_cgm_button, false) && (xDripIsBgSource || dexcomIsSource)).toVisibility()
// Automation buttons
binding.buttonsLayout.userButtonsLayout.removeAllViews()
val events = automationPlugin.userEvents()
for (event in events)
if (event.isEnabled && event.trigger.shouldRun())
context?.let { context ->
SingleClickButton(context).also {
it.setTextColor(resourceHelper.gc(R.color.colorTreatmentButton))
it.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10f)
it.layoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 0.5f).also { l ->
l.setMargins(0, 0, resourceHelper.dpToPx(-4), 0)
}
it.setCompoundDrawablesWithIntrinsicBounds(null, resourceHelper.gd(R.drawable.ic_danar_useropt), null, null)
it.text = event.title
it.setOnClickListener {
OKDialog.showConfirmation(
context,
resourceHelper.gs(R.string.run_question, event.title),
{ handler.post { automationPlugin.processEvent(event, true) } }
)
}
binding.buttonsLayout.userButtonsLayout.addView(it)
}
}
binding.buttonsLayout.userButtonsLayout.visibility = events.isNotEmpty().toVisibility()
} }
private fun processAps() { private fun processAps() {

View file

@ -18,6 +18,16 @@
android:textColor="@color/colorAcceptTempButton" android:textColor="@color/colorAcceptTempButton"
android:visibility="gone" /> android:visibility="gone" />
<LinearLayout
android:id="@+id/user_buttons_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingStart="0dp"
android:paddingEnd="5dp" >
</LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/buttons_layout" android:id="@+id/buttons_layout"
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -20,9 +20,6 @@
<color name="error_background">#66FC0000</color> <color name="error_background">#66FC0000</color>
<color name="ok_background">#323232</color> <color name="ok_background">#323232</color>
<color name="defaultbackground">#424242</color>
<color name="defaulttextcolor">#B3FFFFFF</color>
<color name="tempTargetBackground">#77dd77</color> <color name="tempTargetBackground">#77dd77</color>
<color name="exercise">#67DFE8</color> <color name="exercise">#67DFE8</color>

View file

@ -1124,5 +1124,6 @@
<string name="error_in_basal_values">Error in basal values</string> <string name="error_in_basal_values">Error in basal values</string>
<string name="error_in_target_values">Error in target values</string> <string name="error_in_target_values">Error in target values</string>
<string name="error_in_isf_values">Error in ISF values</string> <string name="error_in_isf_values">Error in ISF values</string>
<string name="run_question">Run %s?</string>
</resources> </resources>

View file

@ -4,7 +4,6 @@ import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.plugins.general.automation.actions.Action import info.nightscout.androidaps.plugins.general.automation.actions.Action
import info.nightscout.androidaps.plugins.general.automation.actions.ActionDummy import info.nightscout.androidaps.plugins.general.automation.actions.ActionDummy
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerDummy import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerDummy
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
@ -24,8 +23,9 @@ class AutomationEvent(private val injector: HasAndroidInjector) {
var systemAction: Boolean = false // true = generated by AAPS, false = entered by user var systemAction: Boolean = false // true = generated by AAPS, false = entered by user
var readOnly: Boolean = false // removing, editing disabled var readOnly: Boolean = false // removing, editing disabled
var autoRemove: Boolean = false // auto-remove once used var autoRemove: Boolean = false // auto-remove once used
var userAction: Boolean = false // shows button on Overview
var trigger: Trigger = TriggerConnector(injector) var trigger: TriggerConnector = TriggerConnector(injector)
val actions: MutableList<Action> = ArrayList() val actions: MutableList<Action> = ArrayList()
var lastRun: Long = 0 var lastRun: Long = 0
@ -44,7 +44,7 @@ class AutomationEvent(private val injector: HasAndroidInjector) {
fun addAction(action: Action) = actions.add(action) fun addAction(action: Action) = actions.add(action)
fun areActionsValid() : Boolean { fun areActionsValid(): Boolean {
var result = true var result = true
for (action in actions) result = result && action.isValid() for (action in actions) result = result && action.isValid()
return result return result
@ -59,6 +59,7 @@ class AutomationEvent(private val injector: HasAndroidInjector) {
.put("systemAction", systemAction) .put("systemAction", systemAction)
.put("readOnly", readOnly) .put("readOnly", readOnly)
.put("autoRemove", autoRemove) .put("autoRemove", autoRemove)
.put("userAction", userAction)
.put("trigger", trigger.toJSON()) .put("trigger", trigger.toJSON())
.put("actions", array) .put("actions", array)
.toString() .toString()
@ -71,7 +72,8 @@ class AutomationEvent(private val injector: HasAndroidInjector) {
systemAction = d.optBoolean("systemAction", false) systemAction = d.optBoolean("systemAction", false)
readOnly = d.optBoolean("readOnly", false) readOnly = d.optBoolean("readOnly", false)
autoRemove = d.optBoolean("autoRemove", false) autoRemove = d.optBoolean("autoRemove", false)
trigger = TriggerDummy(injector).instantiate(JSONObject(d.getString("trigger"))) userAction = d.optBoolean("userAction", false)
trigger = TriggerDummy(injector).instantiate(JSONObject(d.getString("trigger"))) as TriggerConnector
val array = d.getJSONArray("actions") val array = d.getJSONArray("actions")
actions.clear() actions.clear()
for (i in 0 until array.length()) { for (i in 0 until array.length()) {

View file

@ -100,14 +100,14 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener {
.toObservable(EventAutomationUpdateGui::class.java) .toObservable(EventAutomationUpdateGui::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ .subscribe({
updateGui() updateGui()
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventAutomationDataChanged::class.java) .toObservable(EventAutomationDataChanged::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ .subscribe({
eventListAdapter.notifyDataSetChanged() eventListAdapter.notifyDataSetChanged()
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
updateGui() updateGui()
} }
@ -167,14 +167,19 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener {
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val event = automationPlugin.at(position) val event = automationPlugin.at(position)
holder.binding.rootLayout.setBackgroundColor(resourceHelper.gc(if (event.areActionsValid()) R.color.ribbonDefault else R.color.errorAlertBackground)) holder.binding.rootLayout.setBackgroundColor(resourceHelper.gc(
if (event.userAction) R.color.mdtp_line_dark
else if (event.areActionsValid()) R.color.ribbonDefault
else R.color.errorAlertBackground)
)
holder.binding.eventTitle.text = event.title holder.binding.eventTitle.text = event.title
holder.binding.enabled.isChecked = event.isEnabled holder.binding.enabled.isChecked = event.isEnabled
holder.binding.enabled.isEnabled = !event.readOnly holder.binding.enabled.isEnabled = !event.readOnly
holder.binding.iconLayout.removeAllViews() holder.binding.iconLayout.removeAllViews()
// trigger icons // trigger icons
val triggerIcons = HashSet<Int>() val triggerIcons = HashSet<Int>()
fillIconSet(event.trigger as TriggerConnector, triggerIcons) if (event.userAction) triggerIcons.add(R.drawable.ic_danar_useropt)
fillIconSet(event.trigger, triggerIcons)
for (res in triggerIcons) { for (res in triggerIcons) {
addImage(res, holder.context, holder.binding.iconLayout) addImage(res, holder.context, holder.binding.iconLayout)
} }
@ -217,13 +222,13 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener {
// remove event // remove event
holder.binding.iconTrash.setOnClickListener { holder.binding.iconTrash.setOnClickListener {
OKDialog.showConfirmation(requireContext(), resourceHelper.gs(R.string.removerecord) + " " + automationPlugin.at(position).title, OKDialog.showConfirmation(requireContext(), resourceHelper.gs(R.string.removerecord) + " " + automationPlugin.at(position).title,
{ {
uel.log(Action.AUTOMATION_REMOVED, Sources.Automation, automationPlugin.at(position).title) uel.log(Action.AUTOMATION_REMOVED, Sources.Automation, automationPlugin.at(position).title)
automationPlugin.removeAt(position) automationPlugin.removeAt(position)
notifyItemRemoved(position) notifyItemRemoved(position)
}, { }, {
rxBus.send(EventAutomationUpdateGui()) rxBus.send(EventAutomationUpdateGui())
}) })
} }
holder.binding.iconTrash.visibility = (!event.readOnly).toVisibility() holder.binding.iconTrash.visibility = (!event.readOnly).toVisibility()
holder.binding.aapsLogo.visibility = (event.systemAction).toVisibility() holder.binding.aapsLogo.visibility = (event.systemAction).toVisibility()
@ -239,13 +244,15 @@ class AutomationFragment : DaggerFragment(), OnStartDragListener {
override fun onItemDismiss(position: Int) { override fun onItemDismiss(position: Int) {
activity?.let { activity -> activity?.let { activity ->
OKDialog.showConfirmation(activity, resourceHelper.gs(R.string.removerecord) + " " + automationPlugin.at(position).title, OKDialog.showConfirmation(
Runnable { activity,
resourceHelper.gs(R.string.removerecord) + " " + automationPlugin.at(position).title,
{
uel.log(Action.AUTOMATION_REMOVED, Sources.Automation, automationPlugin.at(position).title) uel.log(Action.AUTOMATION_REMOVED, Sources.Automation, automationPlugin.at(position).title)
automationPlugin.removeAt(position) automationPlugin.removeAt(position)
notifyItemRemoved(position) notifyItemRemoved(position)
rxBus.send(EventAutomationDataChanged()) rxBus.send(EventAutomationDataChanged())
}, Runnable { rxBus.send(EventAutomationUpdateGui()) }) }, { rxBus.send(EventAutomationUpdateGui()) })
} }
} }

View file

@ -55,17 +55,18 @@ class AutomationPlugin @Inject constructor(
private val config: Config, private val config: Config,
private val locationServiceHelper: LocationServiceHelper, private val locationServiceHelper: LocationServiceHelper,
private val dateUtil: DateUtil private val dateUtil: DateUtil
) : PluginBase(PluginDescription() ) : PluginBase(
.mainType(PluginType.GENERAL) PluginDescription()
.fragmentClass(AutomationFragment::class.qualifiedName) .mainType(PluginType.GENERAL)
.pluginIcon(R.drawable.ic_automation) .fragmentClass(AutomationFragment::class.qualifiedName)
.pluginName(R.string.automation) .pluginIcon(R.drawable.ic_automation)
.shortName(R.string.automation_short) .pluginName(R.string.automation)
.showInList(config.APS) .shortName(R.string.automation_short)
.neverVisible(!config.APS) .showInList(config.APS)
.alwaysEnabled(!config.APS) .neverVisible(!config.APS)
.preferencesId(R.xml.pref_automation) .alwaysEnabled(!config.APS)
.description(R.string.automation_description), .preferencesId(R.xml.pref_automation)
.description(R.string.automation_description),
aapsLogger, resourceHelper, injector aapsLogger, resourceHelper, injector
) { ) {
@ -82,7 +83,8 @@ class AutomationPlugin @Inject constructor(
companion object { 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}}\"]}" 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}}\"]}"
} }
init { init {
@ -103,11 +105,11 @@ class AutomationPlugin @Inject constructor(
.toObservable(EventPreferenceChange::class.java) .toObservable(EventPreferenceChange::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ e -> .subscribe({ e ->
if (e.isChanged(resourceHelper, R.string.key_location)) { if (e.isChanged(resourceHelper, R.string.key_location)) {
locationServiceHelper.stopService(context) locationServiceHelper.stopService(context)
locationServiceHelper.startService(context) locationServiceHelper.startService(context)
} }
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventAutomationDataChanged::class.java) .toObservable(EventAutomationDataChanged::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
@ -116,11 +118,11 @@ class AutomationPlugin @Inject constructor(
.toObservable(EventLocationChange::class.java) .toObservable(EventLocationChange::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ e -> .subscribe({ e ->
e?.let { e?.let {
aapsLogger.debug(LTag.AUTOMATION, "Grabbed location: $it.location.latitude $it.location.longitude Provider: $it.location.provider") aapsLogger.debug(LTag.AUTOMATION, "Grabbed location: $it.location.latitude $it.location.longitude Provider: $it.location.provider")
processActions() processActions()
} }
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventChargingState::class.java) .toObservable(EventChargingState::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
@ -137,10 +139,10 @@ class AutomationPlugin @Inject constructor(
.toObservable(EventBTChange::class.java) .toObservable(EventBTChange::class.java)
.observeOn(aapsSchedulers.io) .observeOn(aapsSchedulers.io)
.subscribe({ .subscribe({
aapsLogger.debug(LTag.AUTOMATION, "Grabbed new BT event: $it") aapsLogger.debug(LTag.AUTOMATION, "Grabbed new BT event: $it")
btConnects.add(it) btConnects.add(it)
processActions() processActions()
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
} }
override fun onStop() { override fun onStop() {
@ -201,40 +203,8 @@ class AutomationPlugin @Inject constructor(
val iterator: MutableIterator<AutomationEvent> = automationEvents.iterator() val iterator: MutableIterator<AutomationEvent> = automationEvents.iterator()
while (iterator.hasNext()) { while (iterator.hasNext()) {
val event = iterator.next() val event = iterator.next()
if (event.isEnabled && event.shouldRun() && event.trigger.shouldRun() && event.getPreconditions().shouldRun()) { if (event.isEnabled && !event.userAction && event.shouldRun())
if (event.systemAction || userEventsEnabled) { processEvent(event, userEventsEnabled)
val actions = event.actions
for (action in actions) {
action.title = event.title
if (action.isValid())
action.doAction(object : Callback() {
override fun run() {
val sb = StringBuilder()
sb.append(dateUtil.timeString(dateUtil.now()))
sb.append(" ")
sb.append(if (result.success) "" else "")
sb.append(" <b>")
sb.append(event.title)
sb.append(":</b> ")
sb.append(action.shortDescription())
sb.append(": ")
sb.append(result.comment)
executionLog.add(sb.toString())
aapsLogger.debug(LTag.AUTOMATION, "Executed: $sb")
rxBus.send(EventAutomationUpdateGui())
}
})
else {
executionLog.add("Invalid action: ${action.shortDescription()}")
aapsLogger.debug(LTag.AUTOMATION, "Invalid action: ${action.shortDescription()}")
rxBus.send(EventAutomationUpdateGui())
}
}
SystemClock.sleep(1100)
event.lastRun = dateUtil.now()
if (event.autoRemove) automationEvents.remove(event)
}
}
} }
// we cannot detect connected BT devices // we cannot detect connected BT devices
// so let's collect all connection/disconnections between 2 runs of processActions() // so let's collect all connection/disconnections between 2 runs of processActions()
@ -245,6 +215,43 @@ class AutomationPlugin @Inject constructor(
storeToSP() // save last run time storeToSP() // save last run time
} }
fun processEvent(event: AutomationEvent, userEventsEnabled: Boolean) {
if (event.trigger.shouldRun() && event.getPreconditions().shouldRun()) {
if (event.systemAction || userEventsEnabled) {
val actions = event.actions
for (action in actions) {
action.title = event.title
if (action.isValid())
action.doAction(object : Callback() {
override fun run() {
val sb = StringBuilder()
sb.append(dateUtil.timeString(dateUtil.now()))
sb.append(" ")
sb.append(if (result.success) "" else "")
sb.append(" <b>")
sb.append(event.title)
sb.append(":</b> ")
sb.append(action.shortDescription())
sb.append(": ")
sb.append(result.comment)
executionLog.add(sb.toString())
aapsLogger.debug(LTag.AUTOMATION, "Executed: $sb")
rxBus.send(EventAutomationUpdateGui())
}
})
else {
executionLog.add("Invalid action: ${action.shortDescription()}")
aapsLogger.debug(LTag.AUTOMATION, "Invalid action: ${action.shortDescription()}")
rxBus.send(EventAutomationUpdateGui())
}
}
SystemClock.sleep(1100)
event.lastRun = dateUtil.now()
if (event.autoRemove) automationEvents.remove(event)
}
}
}
@Synchronized @Synchronized
fun add(event: AutomationEvent) { fun add(event: AutomationEvent) {
automationEvents.add(event) automationEvents.add(event)
@ -283,6 +290,16 @@ class AutomationPlugin @Inject constructor(
rxBus.send(EventAutomationDataChanged()) rxBus.send(EventAutomationDataChanged())
} }
fun userEvents(): List<AutomationEvent> {
val list = mutableListOf<AutomationEvent>()
val iterator: MutableIterator<AutomationEvent> = automationEvents.iterator()
while (iterator.hasNext()) {
val event = iterator.next()
if (event.userAction) list.add(event)
}
return list
}
fun getActionDummyObjects(): List<Action> { fun getActionDummyObjects(): List<Action> {
return listOf( return listOf(
//ActionLoopDisable(injector), //ActionLoopDisable(injector),
@ -318,7 +335,7 @@ class AutomationPlugin @Inject constructor(
TriggerAutosensValue(injector), TriggerAutosensValue(injector),
TriggerBolusAgo(injector), TriggerBolusAgo(injector),
TriggerPumpLastConnection(injector), TriggerPumpLastConnection(injector),
TriggerBTDevice(injector) TriggerBTDevice(injector),
) )
} }
} }

View file

@ -0,0 +1,96 @@
package info.nightscout.androidaps.plugins.general.automation.dialogs
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.RadioButton
import info.nightscout.androidaps.automation.databinding.AutomationDialogChooseOperationBinding
import info.nightscout.androidaps.dialogs.DialogFragmentWithDate
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector
import info.nightscout.androidaps.utils.resources.ResourceHelper
import javax.inject.Inject
class ChooseOperationDialog : DialogFragmentWithDate() {
@Inject lateinit var resourceHelper: ResourceHelper
private var checkedIndex = -1
private var _binding: AutomationDialogChooseOperationBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
abstract class Callback : Runnable {
var result: Int? = null
fun result(result: Int?): Callback {
this.result = result
return this
}
}
private var callback: Callback? = null
fun setCallback(callback: Callback): ChooseOperationDialog {
this.callback = callback
return this
}
fun setCheckedIndex(checkedIndex: Int): ChooseOperationDialog {
this.checkedIndex = checkedIndex
return this
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// restore checked radio button
savedInstanceState?.let { bundle ->
checkedIndex = bundle.getInt("checkedIndex")
}
onCreateViewGeneral()
_binding = AutomationDialogChooseOperationBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
for (t in TriggerConnector.Type.labels(resourceHelper)) {
val radioButton = RadioButton(context)
radioButton.text = t
binding.chooseOperationRadioGroup.addView(radioButton)
}
if (checkedIndex != -1)
(binding.chooseOperationRadioGroup.getChildAt(checkedIndex) as RadioButton).isChecked = true
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
override fun submit(): Boolean {
callback?.result(determineCheckedIndex())?.run()
return true
}
override fun onSaveInstanceState(savedInstanceState: Bundle) {
super.onSaveInstanceState(savedInstanceState)
savedInstanceState.putInt("checkedIndex", determineCheckedIndex())
}
private fun determineCheckedIndex(): Int {
for (i in 0 until binding.chooseOperationRadioGroup.childCount) {
if ((binding.chooseOperationRadioGroup.getChildAt(i) as RadioButton).isChecked)
return i
}
return -1
}
}

View file

@ -13,6 +13,7 @@ import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.automation.R import info.nightscout.androidaps.automation.R
import info.nightscout.androidaps.automation.databinding.AutomationDialogEventBinding import info.nightscout.androidaps.automation.databinding.AutomationDialogEventBinding
import info.nightscout.androidaps.dialogs.DialogFragmentWithDate import info.nightscout.androidaps.dialogs.DialogFragmentWithDate
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.automation.AutomationEvent import info.nightscout.androidaps.plugins.general.automation.AutomationEvent
import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin import info.nightscout.androidaps.plugins.general.automation.AutomationPlugin
@ -22,13 +23,11 @@ import info.nightscout.androidaps.plugins.general.automation.events.EventAutomat
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateAction import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateAction
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateGui import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateGui
import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateTrigger import info.nightscout.androidaps.plugins.general.automation.events.EventAutomationUpdateTrigger
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.ToastUtils import info.nightscout.androidaps.utils.ToastUtils
import io.reactivex.rxkotlin.plusAssign
import info.nightscout.androidaps.extensions.toVisibility
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import javax.inject.Inject import javax.inject.Inject
class EditEventDialog : DialogFragmentWithDate() { class EditEventDialog : DialogFragmentWithDate() {
@ -73,6 +72,8 @@ class EditEventDialog : DialogFragmentWithDate() {
binding.inputEventTitle.setText(event.title) binding.inputEventTitle.setText(event.title)
binding.inputEventTitle.isFocusable = !event.readOnly binding.inputEventTitle.isFocusable = !event.readOnly
binding.triggerDescription.text = event.trigger.friendlyDescription() binding.triggerDescription.text = event.trigger.friendlyDescription()
binding.userAction.isChecked = event.userAction
binding.enabled.isChecked = event.isEnabled
binding.editTrigger.visibility = (!event.readOnly).toVisibility() binding.editTrigger.visibility = (!event.readOnly).toVisibility()
binding.editTrigger.setOnClickListener { binding.editTrigger.setOnClickListener {
@ -131,9 +132,11 @@ class EditEventDialog : DialogFragmentWithDate() {
return false return false
} }
event.title = title event.title = title
event.userAction = binding.userAction.isChecked
event.isEnabled = binding.enabled.isChecked
// check for at least one trigger // check for at least one trigger
val con = event.trigger as TriggerConnector val con = event.trigger
if (con.size() == 0) { if (con.size() == 0 && !event.userAction) {
ToastUtils.showToastInUiThread(context, R.string.automation_missing_trigger) ToastUtils.showToastInUiThread(context, R.string.automation_missing_trigger)
return false return false
} }

View file

@ -16,9 +16,9 @@ import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerDummy import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerDummy
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import io.reactivex.rxkotlin.plusAssign
import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.rx.AapsSchedulers
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.plusAssign
import org.json.JSONObject import org.json.JSONObject
import javax.inject.Inject import javax.inject.Inject
@ -31,7 +31,7 @@ class EditTriggerDialog : DialogFragmentWithDate() {
private var disposable: CompositeDisposable = CompositeDisposable() private var disposable: CompositeDisposable = CompositeDisposable()
private var triggers: Trigger? = null private var triggers: TriggerConnector? = null
private var _binding: AutomationDialogEditTriggerBinding? = null private var _binding: AutomationDialogEditTriggerBinding? = null
@ -39,11 +39,13 @@ class EditTriggerDialog : DialogFragmentWithDate() {
// onDestroyView. // onDestroyView.
private val binding get() = _binding!! private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(
savedInstanceState: Bundle?): View { inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// load data from bundle // load data from bundle
(savedInstanceState ?: arguments)?.let { bundle -> (savedInstanceState ?: arguments)?.let { bundle ->
bundle.getString("trigger")?.let { triggers = TriggerDummy(injector).instantiate(JSONObject(it)) } bundle.getString("trigger")?.let { triggers = TriggerDummy(injector).instantiate(JSONObject(it)) as TriggerConnector }
} }
onCreateViewGeneral() onCreateViewGeneral()
@ -58,26 +60,26 @@ class EditTriggerDialog : DialogFragmentWithDate() {
.toObservable(EventTriggerChanged::class.java) .toObservable(EventTriggerChanged::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ .subscribe({
binding.layoutTrigger.removeAllViews() binding.layoutTrigger.removeAllViews()
triggers?.generateDialog(binding.layoutTrigger) triggers?.generateDialog(binding.layoutTrigger)
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventTriggerRemove::class.java) .toObservable(EventTriggerRemove::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ .subscribe({
findParent(triggers, it.trigger)?.list?.remove(it.trigger) findParent(triggers, it.trigger)?.list?.remove(it.trigger)
binding.layoutTrigger.removeAllViews() binding.layoutTrigger.removeAllViews()
triggers?.generateDialog(binding.layoutTrigger) triggers?.generateDialog(binding.layoutTrigger)
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
disposable += rxBus disposable += rxBus
.toObservable(EventTriggerClone::class.java) .toObservable(EventTriggerClone::class.java)
.observeOn(aapsSchedulers.main) .observeOn(aapsSchedulers.main)
.subscribe({ .subscribe({
findParent(triggers, it.trigger)?.list?.add(it.trigger.duplicate()) findParent(triggers, it.trigger)?.list?.add(it.trigger.duplicate())
binding.layoutTrigger.removeAllViews() binding.layoutTrigger.removeAllViews()
triggers?.generateDialog(binding.layoutTrigger) triggers?.generateDialog(binding.layoutTrigger)
}, fabricPrivacy::logException) }, fabricPrivacy::logException)
// display root trigger // display root trigger
triggers?.generateDialog(binding.layoutTrigger) triggers?.generateDialog(binding.layoutTrigger)

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.plugins.general.automation.elements package info.nightscout.androidaps.plugins.general.automation.elements
import android.view.Gravity
import android.view.View import android.view.View
import android.widget.AdapterView import android.widget.AdapterView
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
@ -42,6 +43,7 @@ class Comparator(private val resourceHelper: ResourceHelper) : Element() {
} }
companion object { companion object {
fun labels(resourceHelper: ResourceHelper): List<String> { fun labels(resourceHelper: ResourceHelper): List<String> {
val list: MutableList<String> = ArrayList() val list: MutableList<String> = ArrayList()
for (c in values()) { for (c in values()) {
@ -59,25 +61,24 @@ class Comparator(private val resourceHelper: ResourceHelper) : Element() {
var value = Compare.IS_EQUAL var value = Compare.IS_EQUAL
override fun addToLayout(root: LinearLayout) { override fun addToLayout(root: LinearLayout) {
val spinner = Spinner(root.context) root.addView(
val spinnerArrayAdapter = ArrayAdapter(root.context, R.layout.spinner_centered, Compare.labels(resourceHelper)) Spinner(root.context).apply {
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) adapter = ArrayAdapter(root.context, R.layout.spinner_centered, Compare.labels(resourceHelper)).apply {
spinner.adapter = spinnerArrayAdapter setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
val spinnerParams = LinearLayout.LayoutParams( }
LinearLayout.LayoutParams.MATCH_PARENT, layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT).apply {
LinearLayout.LayoutParams.WRAP_CONTENT setMargins(0, resourceHelper.dpToPx(1), 0, resourceHelper.dpToPx(1))
) }
spinnerParams.setMargins(0, resourceHelper.dpToPx(4), 0, resourceHelper.dpToPx(4)) onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
spinner.layoutParams = spinnerParams override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { value = Compare.values()[position]
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { }
value = Compare.values()[position]
}
override fun onNothingSelected(parent: AdapterView<*>?) {} override fun onNothingSelected(parent: AdapterView<*>?) {}
} }
spinner.setSelection(value.ordinal) setSelection(value.ordinal)
root.addView(spinner) gravity = Gravity.CENTER_HORIZONTAL
})
} }
fun setValue(compare: Compare): Comparator { fun setValue(compare: Compare): Comparator {

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.plugins.general.automation.elements package info.nightscout.androidaps.plugins.general.automation.elements
import android.view.Gravity
import android.view.View import android.view.View
import android.widget.AdapterView import android.widget.AdapterView
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
@ -16,11 +17,12 @@ class ComparatorConnect(private val resourceHelper: ResourceHelper) : Element()
@get:StringRes val stringRes: Int @get:StringRes val stringRes: Int
get() = when (this) { get() = when (this) {
ON_CONNECT -> R.string.onconnect ON_CONNECT -> R.string.onconnect
ON_DISCONNECT -> R.string.ondisconnect ON_DISCONNECT -> R.string.ondisconnect
} }
companion object { companion object {
fun labels(resourceHelper: ResourceHelper): List<String> { fun labels(resourceHelper: ResourceHelper): List<String> {
val list: MutableList<String> = ArrayList() val list: MutableList<String> = ArrayList()
for (c in values()) list.add(resourceHelper.gs(c.stringRes)) for (c in values()) list.add(resourceHelper.gs(c.stringRes))
@ -36,21 +38,24 @@ class ComparatorConnect(private val resourceHelper: ResourceHelper) : Element()
var value = Compare.ON_CONNECT var value = Compare.ON_CONNECT
override fun addToLayout(root: LinearLayout) { override fun addToLayout(root: LinearLayout) {
val spinner = Spinner(root.context) root.addView(
val spinnerArrayAdapter = ArrayAdapter(root.context, R.layout.spinner_centered, Compare.labels(resourceHelper)) Spinner(root.context).apply {
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) adapter = ArrayAdapter(root.context, R.layout.spinner_centered, Compare.labels(resourceHelper)).apply {
spinner.adapter = spinnerArrayAdapter setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
val spinnerParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT) }
spinnerParams.setMargins(0, resourceHelper.dpToPx(4), 0, resourceHelper.dpToPx(4))
spinner.layoutParams = spinnerParams
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
value = Compare.values()[position]
}
override fun onNothingSelected(parent: AdapterView<*>?) {} layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT).apply {
} setMargins(0, resourceHelper.dpToPx(4), 0, resourceHelper.dpToPx(4))
spinner.setSelection(value.ordinal) }
root.addView(spinner) onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
value = Compare.values()[position]
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
}
setSelection(value.ordinal)
gravity = Gravity.CENTER_HORIZONTAL
})
} }
} }

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.plugins.general.automation.elements package info.nightscout.androidaps.plugins.general.automation.elements
import android.view.Gravity
import android.view.View import android.view.View
import android.widget.AdapterView import android.widget.AdapterView
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
@ -16,11 +17,12 @@ class ComparatorExists(private val resourceHelper: ResourceHelper, var value: Co
@get:StringRes val stringRes: Int @get:StringRes val stringRes: Int
get() = when (this) { get() = when (this) {
EXISTS -> R.string.exists EXISTS -> R.string.exists
NOT_EXISTS -> R.string.notexists NOT_EXISTS -> R.string.notexists
} }
companion object { companion object {
fun labels(resourceHelper: ResourceHelper): List<String> { fun labels(resourceHelper: ResourceHelper): List<String> {
val list: MutableList<String> = ArrayList() val list: MutableList<String> = ArrayList()
for (c in values()) list.add(resourceHelper.gs(c.stringRes)) for (c in values()) list.add(resourceHelper.gs(c.stringRes))
@ -30,21 +32,24 @@ class ComparatorExists(private val resourceHelper: ResourceHelper, var value: Co
} }
override fun addToLayout(root: LinearLayout) { override fun addToLayout(root: LinearLayout) {
val spinner = Spinner(root.context) root.addView(
val spinnerArrayAdapter = ArrayAdapter(root.context, R.layout.spinner_centered, Compare.labels(resourceHelper)) Spinner(root.context).apply {
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) adapter = ArrayAdapter(root.context, R.layout.spinner_centered, Compare.labels(resourceHelper)).apply {
spinner.adapter = spinnerArrayAdapter setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
val spinnerParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT) }
spinnerParams.setMargins(0, resourceHelper.dpToPx(4), 0, resourceHelper.dpToPx(4)) layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT).apply {
spinner.layoutParams = spinnerParams setMargins(0, resourceHelper.dpToPx(4), 0, resourceHelper.dpToPx(4))
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { }
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
value = Compare.values()[position]
}
override fun onNothingSelected(parent: AdapterView<*>?) {} onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
} override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
spinner.setSelection(value.ordinal) value = Compare.values()[position]
root.addView(spinner) }
override fun onNothingSelected(parent: AdapterView<*>?) {}
}
setSelection(value.ordinal)
gravity = Gravity.CENTER_HORIZONTAL
})
} }
} }

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.plugins.general.automation.elements package info.nightscout.androidaps.plugins.general.automation.elements
import android.view.Gravity
import android.widget.LinearLayout import android.widget.LinearLayout
import info.nightscout.androidaps.automation.R import info.nightscout.androidaps.automation.R
import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.interfaces.GlucoseUnit
@ -26,13 +27,15 @@ class InputBg(profileFunction: ProfileFunction) : Element() {
} }
override fun addToLayout(root: LinearLayout) { override fun addToLayout(root: LinearLayout) {
val numberPicker = NumberPicker(root.context, null) root.addView(
numberPicker.setParams(value, minValue, maxValue, step, decimalFormat, false, root.findViewById(R.id.ok)) NumberPicker(root.context, null).apply {
numberPicker.setOnValueChangedListener { value: Double -> this.value = value } setParams(value, minValue, maxValue, step, decimalFormat, false, root.findViewById(R.id.ok))
root.addView(numberPicker) setOnValueChangedListener { value: Double -> this.value = value }
gravity = Gravity.CENTER_HORIZONTAL
})
} }
fun setValue(value: Double) : InputBg { fun setValue(value: Double): InputBg {
this.value = value this.value = value
return this return this
} }
@ -54,6 +57,7 @@ class InputBg(profileFunction: ProfileFunction) : Element() {
} }
companion object { companion object {
const val MMOL_MIN = 3.0 const val MMOL_MIN = 3.0
const val MMOL_MAX = 20.0 const val MMOL_MAX = 20.0
const val MGDL_MIN = 54.0 const val MGDL_MIN = 54.0

View file

@ -1,9 +1,11 @@
package info.nightscout.androidaps.plugins.general.automation.elements package info.nightscout.androidaps.plugins.general.automation.elements
import android.view.Gravity
import android.widget.Button import android.widget.Button
import android.widget.LinearLayout import android.widget.LinearLayout
class InputButton() : Element() { class InputButton() : Element() {
var text: String? = null var text: String? = null
var runnable: Runnable? = null var runnable: Runnable? = null
@ -13,9 +15,11 @@ class InputButton() : Element() {
} }
override fun addToLayout(root: LinearLayout) { override fun addToLayout(root: LinearLayout) {
val button = Button(root.context) root.addView(
button.text = text Button(root.context).apply {
button.setOnClickListener { runnable?.run() } text = text
root.addView(button) setOnClickListener { runnable?.run() }
gravity = Gravity.CENTER_HORIZONTAL
})
} }
} }

View file

@ -1,51 +1,50 @@
package info.nightscout.androidaps.plugins.general.automation.elements package info.nightscout.androidaps.plugins.general.automation.elements
import android.view.Gravity
import android.view.View import android.view.View
import android.widget.AdapterView import android.widget.AdapterView
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.Spinner import android.widget.Spinner
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.annotation.LayoutRes
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.core.graphics.drawable.IconCompat
import info.nightscout.androidaps.automation.R import info.nightscout.androidaps.automation.R
import info.nightscout.androidaps.database.entities.TherapyEvent import info.nightscout.androidaps.database.entities.TherapyEvent
import info.nightscout.androidaps.database.entities.UserEntry
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
class InputCarePortalMenu(private val resourceHelper: ResourceHelper) : Element() { class InputCarePortalMenu(private val resourceHelper: ResourceHelper) : Element() {
enum class EventType (val therapyEventType: TherapyEvent.Type) { enum class EventType(val therapyEventType: TherapyEvent.Type) {
NOTE (TherapyEvent.Type.NOTE), NOTE(TherapyEvent.Type.NOTE),
EXERCISE (TherapyEvent.Type.EXERCISE), EXERCISE(TherapyEvent.Type.EXERCISE),
QUESTION (TherapyEvent.Type.QUESTION), QUESTION(TherapyEvent.Type.QUESTION),
ANNOUNCEMENT (TherapyEvent.Type.ANNOUNCEMENT); ANNOUNCEMENT(TherapyEvent.Type.ANNOUNCEMENT);
@get:StringRes val stringResWithValue: Int @get:StringRes val stringResWithValue: Int
get() = when (this) { get() = when (this) {
NOTE -> R.string.careportal_note_message NOTE -> R.string.careportal_note_message
EXERCISE -> R.string.careportal_exercise_message EXERCISE -> R.string.careportal_exercise_message
QUESTION -> R.string.careportal_question_message QUESTION -> R.string.careportal_question_message
ANNOUNCEMENT -> R.string.careportal_announcement_message ANNOUNCEMENT -> R.string.careportal_announcement_message
} }
@get:StringRes val stringRes: Int @get:StringRes val stringRes: Int
get() = when (this) { get() = when (this) {
NOTE -> R.string.careportal_note NOTE -> R.string.careportal_note
EXERCISE -> R.string.careportal_exercise EXERCISE -> R.string.careportal_exercise
QUESTION -> R.string.careportal_question QUESTION -> R.string.careportal_question
ANNOUNCEMENT -> R.string.careportal_announcement ANNOUNCEMENT -> R.string.careportal_announcement
} }
@get:DrawableRes val drawableRes: Int @get:DrawableRes val drawableRes: Int
get() = when (this) { get() = when (this) {
NOTE -> R.drawable.ic_cp_note NOTE -> R.drawable.ic_cp_note
EXERCISE -> R.drawable.ic_cp_exercise EXERCISE -> R.drawable.ic_cp_exercise
QUESTION -> R.drawable.ic_cp_question QUESTION -> R.drawable.ic_cp_question
ANNOUNCEMENT -> R.drawable.ic_cp_announcement ANNOUNCEMENT -> R.drawable.ic_cp_announcement
} }
companion object { companion object {
fun labels(resourceHelper: ResourceHelper): List<String> { fun labels(resourceHelper: ResourceHelper): List<String> {
val list: MutableList<String> = ArrayList() val list: MutableList<String> = ArrayList()
for (e in values()) { for (e in values()) {
@ -63,25 +62,25 @@ class InputCarePortalMenu(private val resourceHelper: ResourceHelper) : Element(
var value = EventType.NOTE var value = EventType.NOTE
override fun addToLayout(root: LinearLayout) { override fun addToLayout(root: LinearLayout) {
val spinner = Spinner(root.context) root.addView(
val spinnerArrayAdapter = ArrayAdapter(root.context, R.layout.spinner_centered, EventType.labels(resourceHelper)) Spinner(root.context).apply {
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) adapter = ArrayAdapter(root.context, R.layout.spinner_centered, EventType.labels(resourceHelper)).apply {
spinner.adapter = spinnerArrayAdapter setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
val spinnerParams = LinearLayout.LayoutParams( }
LinearLayout.LayoutParams.MATCH_PARENT, layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT).apply {
LinearLayout.LayoutParams.WRAP_CONTENT setMargins(0, resourceHelper.dpToPx(4), 0, resourceHelper.dpToPx(4))
) }
spinnerParams.setMargins(0, resourceHelper.dpToPx(4), 0, resourceHelper.dpToPx(4))
spinner.layoutParams = spinnerParams
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
value = EventType.values()[position]
}
override fun onNothingSelected(parent: AdapterView<*>?) {} onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
} override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
spinner.setSelection(value.ordinal) value = EventType.values()[position]
root.addView(spinner) }
override fun onNothingSelected(parent: AdapterView<*>?) {}
}
setSelection(value.ordinal)
gravity = Gravity.CENTER_HORIZONTAL
})
} }
fun setValue(eventType: EventType): InputCarePortalMenu { fun setValue(eventType: EventType): InputCarePortalMenu {

View file

@ -15,71 +15,71 @@ import java.util.*
class InputDateTime(private val resourceHelper: ResourceHelper, private val dateUtil: DateUtil, var value: Long = dateUtil.now()) : Element() { class InputDateTime(private val resourceHelper: ResourceHelper, private val dateUtil: DateUtil, var value: Long = dateUtil.now()) : Element() {
override fun addToLayout(root: LinearLayout) { override fun addToLayout(root: LinearLayout) {
val label = TextView(root.context)
val dateButton = TextView(root.context)
val timeButton = TextView(root.context)
dateButton.text = dateUtil.dateString(value)
timeButton.text = dateUtil.timeString(value)
// create an OnDateSetListener
val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth ->
val cal = Calendar.getInstance()
cal.timeInMillis = value
cal.set(Calendar.YEAR, year)
cal.set(Calendar.MONTH, monthOfYear)
cal.set(Calendar.DAY_OF_MONTH, dayOfMonth)
value = cal.timeInMillis
dateButton.text = dateUtil.dateString(value)
}
dateButton.setOnClickListener {
root.context?.let {
val cal = Calendar.getInstance()
cal.timeInMillis = value
DatePickerDialog(it, dateSetListener,
cal.get(Calendar.YEAR),
cal.get(Calendar.MONTH),
cal.get(Calendar.DAY_OF_MONTH)
).show()
}
}
// create an OnTimeSetListener
val timeSetListener = TimePickerDialog.OnTimeSetListener { _, hour, minute ->
val cal = Calendar.getInstance()
cal.timeInMillis = value
cal.set(Calendar.HOUR_OF_DAY, hour)
cal.set(Calendar.MINUTE, minute)
cal.set(Calendar.SECOND, 0) // randomize seconds to prevent creating record of the same time, if user choose time manually
value = cal.timeInMillis
timeButton.text = dateUtil.timeString(value)
}
timeButton.setOnClickListener {
root.context?.let {
val cal = Calendar.getInstance()
cal.timeInMillis = value
TimePickerDialog(it, timeSetListener,
cal.get(Calendar.HOUR_OF_DAY),
cal.get(Calendar.MINUTE),
DateFormat.is24HourFormat(it)
).show()
}
}
val px = resourceHelper.dpToPx(10) val px = resourceHelper.dpToPx(10)
label.text = resourceHelper.gs(R.string.atspecifiedtime, "")
label.setTypeface(label.typeface, Typeface.BOLD)
label.setPadding(px, px, px, px)
dateButton.setPadding(px, px, px, px)
timeButton.setPadding(px, px, px, px)
val l = LinearLayout(root.context)
l.orientation = LinearLayout.HORIZONTAL
l.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
l.addView(label)
l.addView(dateButton)
l.addView(timeButton)
root.addView(l)
}
root.addView(
LinearLayout(root.context).apply {
orientation = LinearLayout.HORIZONTAL
layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
addView(
TextView(root.context).apply {
text = resourceHelper.gs(R.string.atspecifiedtime, "")
setTypeface(typeface, Typeface.BOLD)
setPadding(px, px, px, px)
})
addView(
TextView(root.context).apply {
text = dateUtil.dateString(value)
setPadding(px, px, px, px)
setOnClickListener {
root.context?.let {
val cal = Calendar.getInstance()
cal.timeInMillis = value
DatePickerDialog(
it,
{ _, year, monthOfYear, dayOfMonth ->
value = Calendar.getInstance().apply {
timeInMillis = value
set(Calendar.YEAR, year)
set(Calendar.MONTH, monthOfYear)
set(Calendar.DAY_OF_MONTH, dayOfMonth)
}.timeInMillis
text = dateUtil.dateString(value)
},
cal.get(Calendar.YEAR),
cal.get(Calendar.MONTH),
cal.get(Calendar.DAY_OF_MONTH)
).show()
}
}
})
addView(
TextView(root.context).apply {
text = dateUtil.timeString(value)
setPadding(px, px, px, px)
setOnClickListener {
root.context?.let {
val cal = Calendar.getInstance()
cal.timeInMillis = value
TimePickerDialog(
it,
{ _, hour, minute ->
value = Calendar.getInstance().apply {
timeInMillis = value
set(Calendar.HOUR_OF_DAY, hour)
set(Calendar.MINUTE, minute)
set(Calendar.SECOND, 0) // randomize seconds to prevent creating record of the same time, if user choose time manually
}.timeInMillis
text = dateUtil.timeString(value)
},
cal.get(Calendar.HOUR_OF_DAY),
cal.get(Calendar.MINUTE),
DateFormat.is24HourFormat(it)
).show()
}
}
}
)
})
}
} }

View file

@ -1,15 +1,15 @@
package info.nightscout.androidaps.plugins.general.automation.elements package info.nightscout.androidaps.plugins.general.automation.elements
import android.view.Gravity
import android.view.View import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView import android.widget.AdapterView
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.Spinner import android.widget.Spinner
import androidx.annotation.StringRes import androidx.annotation.StringRes
import info.nightscout.androidaps.automation.R import info.nightscout.androidaps.automation.R
import info.nightscout.androidaps.utils.ui.NumberPicker
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.ui.NumberPicker
import java.text.DecimalFormat import java.text.DecimalFormat
class InputDelta(private val resourceHelper: ResourceHelper) : Element() { class InputDelta(private val resourceHelper: ResourceHelper) : Element() {
@ -19,12 +19,13 @@ class InputDelta(private val resourceHelper: ResourceHelper) : Element() {
@get:StringRes val stringRes: Int @get:StringRes val stringRes: Int
get() = when (this) { get() = when (this) {
DELTA -> R.string.delta DELTA -> R.string.delta
SHORT_AVERAGE -> R.string.short_avgdelta SHORT_AVERAGE -> R.string.short_avgdelta
LONG_AVERAGE -> R.string.long_avgdelta LONG_AVERAGE -> R.string.long_avgdelta
} }
companion object { companion object {
fun labels(resourceHelper: ResourceHelper): List<String> { fun labels(resourceHelper: ResourceHelper): List<String> {
val list: MutableList<String> = ArrayList() val list: MutableList<String> = ArrayList()
for (d in values()) { for (d in values()) {
@ -61,32 +62,29 @@ class InputDelta(private val resourceHelper: ResourceHelper) : Element() {
} }
override fun addToLayout(root: LinearLayout) { override fun addToLayout(root: LinearLayout) {
val spinner = Spinner(root.context) root.addView(
val spinnerArrayAdapter = ArrayAdapter(root.context, R.layout.spinner_centered, DeltaType.labels(resourceHelper)) Spinner(root.context).apply {
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) adapter = ArrayAdapter(root.context, R.layout.spinner_centered, DeltaType.labels(resourceHelper)).apply {
spinner.adapter = spinnerArrayAdapter setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
val spinnerParams = LinearLayout.LayoutParams( }
LinearLayout.LayoutParams.WRAP_CONTENT, layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT).apply {
LinearLayout.LayoutParams.WRAP_CONTENT setMargins(0, resourceHelper.dpToPx(4), 0, resourceHelper.dpToPx(4))
) }
spinnerParams.setMargins(0, resourceHelper.dpToPx(4), 0, resourceHelper.dpToPx(4)) onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
spinner.layoutParams = spinnerParams override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { deltaType = DeltaType.values()[position]
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { }
deltaType = DeltaType.values()[position]
}
override fun onNothingSelected(parent: AdapterView<*>?) {} override fun onNothingSelected(parent: AdapterView<*>?) {}
} }
spinner.setSelection(deltaType.ordinal) setSelection(deltaType.ordinal)
val numberPicker = NumberPicker(root.context, null) gravity = Gravity.CENTER_HORIZONTAL
numberPicker.setParams(value, minValue, maxValue, step, decimalFormat, true, null, null) })
numberPicker.setOnValueChangedListener { value: Double -> this.value = value } root.addView(
val l = LinearLayout(root.context) NumberPicker(root.context, null).apply {
l.orientation = LinearLayout.VERTICAL setParams(value, minValue, maxValue, step, decimalFormat, true, null, null)
l.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) setOnValueChangedListener { value: Double -> this.value = value }
l.addView(spinner) gravity = Gravity.CENTER_HORIZONTAL
l.addView(numberPicker) })
root.addView(l)
} }
} }

View file

@ -1,11 +1,13 @@
package info.nightscout.androidaps.plugins.general.automation.elements package info.nightscout.androidaps.plugins.general.automation.elements
import android.view.Gravity
import android.widget.LinearLayout import android.widget.LinearLayout
import info.nightscout.androidaps.automation.R import info.nightscout.androidaps.automation.R
import info.nightscout.androidaps.utils.ui.NumberPicker import info.nightscout.androidaps.utils.ui.NumberPicker
import java.text.DecimalFormat import java.text.DecimalFormat
class InputDouble() : Element() { class InputDouble() : Element() {
var value = 0.0 var value = 0.0
private var minValue = 0.0 private var minValue = 0.0
private var maxValue = 0.0 private var maxValue = 0.0
@ -30,9 +32,11 @@ class InputDouble() : Element() {
} }
override fun addToLayout(root: LinearLayout) { override fun addToLayout(root: LinearLayout) {
numberPicker = NumberPicker(root.context, null) numberPicker = NumberPicker(root.context, null).apply {
numberPicker?.setParams(value, minValue, maxValue, step, decimalFormat, true, root.findViewById(R.id.ok)) setParams(value, minValue, maxValue, step, decimalFormat, true, root.findViewById(R.id.ok))
numberPicker?.setOnValueChangedListener { value: Double -> this.value = value } setOnValueChangedListener { value: Double -> this.value = value }
gravity = Gravity.CENTER_HORIZONTAL
}
root.addView(numberPicker) root.addView(numberPicker)
} }

View file

@ -1,7 +1,7 @@
package info.nightscout.androidaps.plugins.general.automation.elements package info.nightscout.androidaps.plugins.general.automation.elements
import android.view.Gravity
import android.view.View import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView import android.widget.AdapterView
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.LinearLayout import android.widget.LinearLayout
@ -25,31 +25,25 @@ class InputDropdownMenu(private val resourceHelper: ResourceHelper) : Element()
} }
override fun addToLayout(root: LinearLayout) { override fun addToLayout(root: LinearLayout) {
val spinner = Spinner(root.context) root.addView(
spinner.adapter = ArrayAdapter( Spinner(root.context).apply {
root.context, adapter = ArrayAdapter(root.context, R.layout.spinner_centered, itemList).apply {
R.layout.spinner_centered, itemList setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
).also { }
it.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT).also {
} it.setMargins(0, resourceHelper.dpToPx(4), 0, resourceHelper.dpToPx(4))
spinner.layoutParams = LinearLayout.LayoutParams( }
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
).also { it.setMargins(0, resourceHelper.dpToPx(4), 0, resourceHelper.dpToPx(4)) }
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
setValue(itemList[position].toString()) setValue(itemList[position].toString())
} }
override fun onNothingSelected(parent: AdapterView<*>?) {} override fun onNothingSelected(parent: AdapterView<*>?) {}
} }
for (i in 0 until itemList.size) if (itemList[i] == value) spinner.setSelection(i) gravity = Gravity.CENTER_HORIZONTAL
root.addView(LinearLayout(root.context).also { for (i in 0 until itemList.size) if (itemList[i] == value) setSelection(i)
it.orientation = LinearLayout.VERTICAL })
it.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
it.addView(spinner)
})
} }
fun setValue(name: String): InputDropdownMenu { fun setValue(name: String): InputDropdownMenu {

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.plugins.general.automation.elements package info.nightscout.androidaps.plugins.general.automation.elements
import android.view.Gravity
import android.widget.LinearLayout import android.widget.LinearLayout
import info.nightscout.androidaps.automation.R import info.nightscout.androidaps.automation.R
import info.nightscout.androidaps.utils.ui.MinutesNumberPicker import info.nightscout.androidaps.utils.ui.MinutesNumberPicker
@ -25,6 +26,7 @@ class InputDuration(
numberPicker.setParams(value.toDouble(), 1.0, 24.0, 1.0, DecimalFormat("0"), false, root.findViewById(R.id.ok)) numberPicker.setParams(value.toDouble(), 1.0, 24.0, 1.0, DecimalFormat("0"), false, root.findViewById(R.id.ok))
} }
numberPicker.setOnValueChangedListener { value: Double -> this.value = value.toInt() } numberPicker.setOnValueChangedListener { value: Double -> this.value = value.toInt() }
numberPicker.gravity = Gravity.CENTER_HORIZONTAL
root.addView(numberPicker) root.addView(numberPicker)
} }

View file

@ -1,11 +1,13 @@
package info.nightscout.androidaps.plugins.general.automation.elements package info.nightscout.androidaps.plugins.general.automation.elements
import android.view.Gravity
import android.widget.LinearLayout import android.widget.LinearLayout
import info.nightscout.androidaps.automation.R import info.nightscout.androidaps.automation.R
import info.nightscout.androidaps.utils.ui.NumberPicker import info.nightscout.androidaps.utils.ui.NumberPicker
import java.text.DecimalFormat import java.text.DecimalFormat
class InputInsulin() : Element() { class InputInsulin() : Element() {
var value = 0.0 var value = 0.0
constructor(another: InputInsulin) : this() { constructor(another: InputInsulin) : this() {
@ -13,10 +15,12 @@ class InputInsulin() : Element() {
} }
override fun addToLayout(root: LinearLayout) { override fun addToLayout(root: LinearLayout) {
val numberPicker = NumberPicker(root.context, null) root.addView(
numberPicker.setParams(0.0, -20.0, 20.0, 0.1, DecimalFormat("0.0"), true, root.findViewById(R.id.ok)) NumberPicker(root.context, null).apply {
numberPicker.value = value setParams(0.0, -20.0, 20.0, 0.1, DecimalFormat("0.0"), true, root.findViewById(R.id.ok))
numberPicker.setOnValueChangedListener { value: Double -> this.value = value } value = value
root.addView(numberPicker) setOnValueChangedListener { value: Double -> this.value = value }
gravity = Gravity.CENTER_HORIZONTAL
})
} }
} }

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.plugins.general.automation.elements package info.nightscout.androidaps.plugins.general.automation.elements
import android.view.Gravity
import android.view.View import android.view.View
import android.widget.AdapterView import android.widget.AdapterView
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
@ -30,6 +31,7 @@ class InputLocationMode(private val resourceHelper: ResourceHelper) : Element()
} }
companion object { companion object {
fun labels(resourceHelper: ResourceHelper): List<String> { fun labels(resourceHelper: ResourceHelper): List<String> {
val list: MutableList<String> = ArrayList() val list: MutableList<String> = ArrayList()
for (c in values()) { for (c in values()) {
@ -47,24 +49,24 @@ class InputLocationMode(private val resourceHelper: ResourceHelper) : Element()
} }
override fun addToLayout(root: LinearLayout) { override fun addToLayout(root: LinearLayout) {
val adapter = ArrayAdapter(root.context, R.layout.spinner_centered, Mode.labels(resourceHelper)) root.addView(
val spinner = Spinner(root.context) Spinner(root.context).apply {
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) adapter = ArrayAdapter(root.context, R.layout.spinner_centered, Mode.labels(resourceHelper)).apply {
spinner.adapter = adapter setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
val spinnerParams = LinearLayout.LayoutParams( }
LinearLayout.LayoutParams.WRAP_CONTENT, val spinnerParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT).apply {
LinearLayout.LayoutParams.WRAP_CONTENT setMargins(0, resourceHelper.dpToPx(4), 0, resourceHelper.dpToPx(4))
) }
spinnerParams.setMargins(0, resourceHelper.dpToPx(4), 0, resourceHelper.dpToPx(4)) layoutParams = spinnerParams
spinner.layoutParams = spinnerParams onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { value = Mode.values()[position]
value = Mode.values()[position] }
}
override fun onNothingSelected(parent: AdapterView<*>?) {} override fun onNothingSelected(parent: AdapterView<*>?) {}
} }
spinner.setSelection(value.ordinal) setSelection(value.ordinal)
root.addView(spinner) gravity = Gravity.CENTER_HORIZONTAL
})
} }
} }

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.plugins.general.automation.elements package info.nightscout.androidaps.plugins.general.automation.elements
import android.view.Gravity
import android.widget.LinearLayout import android.widget.LinearLayout
import info.nightscout.androidaps.automation.R import info.nightscout.androidaps.automation.R
import info.nightscout.androidaps.utils.ui.NumberPicker import info.nightscout.androidaps.utils.ui.NumberPicker
@ -14,11 +15,13 @@ class InputPercent() : Element() {
} }
override fun addToLayout(root: LinearLayout) { override fun addToLayout(root: LinearLayout) {
val numberPicker = NumberPicker(root.context, null) root.addView(
numberPicker.setParams(100.0, MIN, MAX, 5.0, DecimalFormat("0"), true, root.findViewById(R.id.ok)) NumberPicker(root.context, null).apply {
numberPicker.value = value setParams(100.0, MIN, MAX, 5.0, DecimalFormat("0"), true, root.findViewById(R.id.ok))
numberPicker.setOnValueChangedListener { value: Double -> this.value = value } value = value
root.addView(numberPicker) setOnValueChangedListener { value: Double -> this.value = value }
gravity = Gravity.CENTER_HORIZONTAL
})
} }
companion object { companion object {

View file

@ -1,7 +1,7 @@
package info.nightscout.androidaps.plugins.general.automation.elements package info.nightscout.androidaps.plugins.general.automation.elements
import android.view.Gravity
import android.view.View import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView import android.widget.AdapterView
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.LinearLayout import android.widget.LinearLayout
@ -17,28 +17,24 @@ class InputProfileName(private val resourceHelper: ResourceHelper, private val a
override fun addToLayout(root: LinearLayout) { override fun addToLayout(root: LinearLayout) {
val profileStore = activePlugin.activeProfileSource.profile ?: return val profileStore = activePlugin.activeProfileSource.profile ?: return
val profileList = profileStore.getProfileList() val profileList = profileStore.getProfileList()
val adapter = ArrayAdapter(root.context, R.layout.spinner_centered, profileList)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
val spinner = Spinner(root.context)
spinner.adapter = adapter
val spinnerParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
spinnerParams.setMargins(0, resourceHelper.dpToPx(4), 0, resourceHelper.dpToPx(4))
spinner.layoutParams = spinnerParams
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
value = profileList[position].toString()
}
override fun onNothingSelected(parent: AdapterView<*>?) {} root.addView(
} Spinner(root.context).apply {
for (i in 0 until profileList.size) if (profileList[i] == value) spinner.setSelection(i) adapter = ArrayAdapter(root.context, R.layout.spinner_centered, profileList).apply {
val l = LinearLayout(root.context) setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
l.orientation = LinearLayout.VERTICAL }
l.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT).apply {
l.addView(spinner) setMargins(0, resourceHelper.dpToPx(4), 0, resourceHelper.dpToPx(4))
root.addView(l) }
onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
value = profileList[position].toString()
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
}
for (i in 0 until profileList.size) if (profileList[i] == value) setSelection(i)
gravity = Gravity.CENTER_HORIZONTAL
})
} }
} }

View file

@ -2,11 +2,13 @@ package info.nightscout.androidaps.plugins.general.automation.elements
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
import android.view.Gravity
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.EditText import android.widget.EditText
import android.widget.LinearLayout import android.widget.LinearLayout
class InputString(var value: String = "") : Element() { class InputString(var value: String = "") : Element() {
private val textWatcher: TextWatcher = object : TextWatcher { private val textWatcher: TextWatcher = object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {} override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
@ -16,10 +18,12 @@ class InputString(var value: String = "") : Element() {
} }
override fun addToLayout(root: LinearLayout) { override fun addToLayout(root: LinearLayout) {
val editText = EditText(root.context) root.addView(
editText.setText(value) EditText(root.context).apply {
editText.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) setText(value)
editText.addTextChangedListener(textWatcher) layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
root.addView(editText) addTextChangedListener(textWatcher)
gravity = Gravity.CENTER_HORIZONTAL
})
} }
} }

View file

@ -1,5 +1,6 @@
package info.nightscout.androidaps.plugins.general.automation.elements package info.nightscout.androidaps.plugins.general.automation.elements
import android.view.Gravity
import android.widget.LinearLayout import android.widget.LinearLayout
import info.nightscout.androidaps.Constants import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.automation.R import info.nightscout.androidaps.automation.R
@ -9,6 +10,7 @@ import info.nightscout.androidaps.utils.ui.NumberPicker
import java.text.DecimalFormat import java.text.DecimalFormat
class InputTempTarget(profileFunction: ProfileFunction) : Element() { class InputTempTarget(profileFunction: ProfileFunction) : Element() {
var units: GlucoseUnit = GlucoseUnit.MGDL var units: GlucoseUnit = GlucoseUnit.MGDL
var value = 0.0 var value = 0.0
@ -38,9 +40,12 @@ class InputTempTarget(profileFunction: ProfileFunction) : Element() {
step = 1.0 step = 1.0
decimalFormat = DecimalFormat("0") decimalFormat = DecimalFormat("0")
} }
val numberPicker = NumberPicker(root.context, null) root.addView(
numberPicker.setParams(value, minValue, maxValue, step, decimalFormat, true, root.findViewById(R.id.ok)) NumberPicker(root.context, null).apply {
numberPicker.setOnValueChangedListener { value: Double -> this.value = value } setParams(value, minValue, maxValue, step, decimalFormat, true, root.findViewById(R.id.ok))
root.addView(numberPicker) setOnValueChangedListener { value: Double -> this.value = value }
gravity = Gravity.CENTER_HORIZONTAL
})
} }
} }

View file

@ -3,6 +3,7 @@ package info.nightscout.androidaps.plugins.general.automation.elements
import android.app.TimePickerDialog import android.app.TimePickerDialog
import android.graphics.Typeface import android.graphics.Typeface
import android.text.format.DateFormat import android.text.format.DateFormat
import android.view.Gravity
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
@ -18,37 +19,38 @@ class InputTime(private val resourceHelper: ResourceHelper, private val dateUtil
var value: Int = getMinSinceMidnight(dateUtil.now()) var value: Int = getMinSinceMidnight(dateUtil.now())
override fun addToLayout(root: LinearLayout) { override fun addToLayout(root: LinearLayout) {
val label = TextView(root.context) root.addView(
val startButton = TextView(root.context) LinearLayout(root.context).apply {
startButton.text = dateUtil.timeString(toMills(value)) orientation = LinearLayout.HORIZONTAL
layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
val startTimeSetListener = TimePickerDialog.OnTimeSetListener { _, hour, minute -> addView(
value = 60 * hour + minute TextView(root.context).apply {
startButton.text = dateUtil.timeString(toMills(value)) text = resourceHelper.gs(R.string.atspecifiedtime, "")
} setTypeface(typeface, Typeface.BOLD)
})
startButton.setOnClickListener { addView(
root.context?.let { TextView(root.context).apply {
val cal = Calendar.getInstance() text = dateUtil.timeString(toMills(value))
cal.timeInMillis = toMills(value) val px = resourceHelper.dpToPx(10)
TimePickerDialog(it, startTimeSetListener, setPadding(px, px, px, px)
cal.get(Calendar.HOUR_OF_DAY), setOnClickListener {
cal.get(Calendar.MINUTE), root.context?.let {
DateFormat.is24HourFormat(it) val cal = Calendar.getInstance()
).show() cal.timeInMillis = toMills(value)
} TimePickerDialog(
} it,
{ _, hour, minute ->
val px = resourceHelper.dpToPx(10) value = 60 * hour + minute
label.text = resourceHelper.gs(R.string.atspecifiedtime, "") text = dateUtil.timeString(toMills(value))
label.setTypeface(label.typeface, Typeface.BOLD) },
startButton.setPadding(px, px, px, px) cal.get(Calendar.HOUR_OF_DAY),
val l = LinearLayout(root.context) cal.get(Calendar.MINUTE),
l.orientation = LinearLayout.HORIZONTAL DateFormat.is24HourFormat(it)
l.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) ).show()
l.addView(label) }
l.addView(startButton) }
root.addView(l) })
})
} }
private fun toMills(minutesSinceMidnight: Int): Long = MidnightTime.calcPlusMinutes(minutesSinceMidnight) private fun toMills(minutesSinceMidnight: Int): Long = MidnightTime.calcPlusMinutes(minutesSinceMidnight)

View file

@ -3,6 +3,7 @@ package info.nightscout.androidaps.plugins.general.automation.elements
import android.app.TimePickerDialog import android.app.TimePickerDialog
import android.graphics.Typeface import android.graphics.Typeface
import android.text.format.DateFormat import android.text.format.DateFormat
import android.view.Gravity
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
@ -19,59 +20,62 @@ class InputTimeRange(private val resourceHelper: ResourceHelper, private val dat
var end: Int = getMinSinceMidnight(dateUtil.now()) var end: Int = getMinSinceMidnight(dateUtil.now())
override fun addToLayout(root: LinearLayout) { override fun addToLayout(root: LinearLayout) {
val label = TextView(root.context)
val startButton = TextView(root.context)
val endButton = TextView(root.context)
startButton.text = dateUtil.timeString(toMills(start))
@Suppress("SetTextI18n")
endButton.text = resourceHelper.gs(R.string.and) + " " + dateUtil.timeString(toMills(end))
val startTimeSetListener = TimePickerDialog.OnTimeSetListener { _, hour, minute ->
start = 60 * hour + minute
startButton.text = dateUtil.timeString(toMills(start))
}
startButton.setOnClickListener {
root.context?.let {
val cal = Calendar.getInstance()
cal.timeInMillis = toMills(start)
TimePickerDialog(it, startTimeSetListener,
cal.get(Calendar.HOUR_OF_DAY),
cal.get(Calendar.MINUTE),
DateFormat.is24HourFormat(it)
).show()
}
}
val endTimeSetListener = TimePickerDialog.OnTimeSetListener { _, hour, minute ->
end = 60 * hour + minute
endButton.text = dateUtil.timeString(toMills(end))
}
endButton.setOnClickListener {
root.context?.let {
val cal = Calendar.getInstance()
cal.timeInMillis = toMills(end)
TimePickerDialog(it, endTimeSetListener,
cal.get(Calendar.HOUR_OF_DAY),
cal.get(Calendar.MINUTE),
DateFormat.is24HourFormat(it)
).show()
}
}
val px = resourceHelper.dpToPx(10) val px = resourceHelper.dpToPx(10)
label.text = resourceHelper.gs(R.string.between)
label.setTypeface(label.typeface, Typeface.BOLD) root.addView(
startButton.setPadding(px, px, px, px) TextView(root.context).apply {
endButton.setPadding(px, px, px, px) text = resourceHelper.gs(R.string.between)
val l = LinearLayout(root.context) setTypeface(typeface, Typeface.BOLD)
l.orientation = LinearLayout.HORIZONTAL gravity = Gravity.CENTER_HORIZONTAL
l.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) })
l.addView(label) root.addView(
l.addView(startButton) LinearLayout(root.context).apply {
l.addView(endButton) orientation = LinearLayout.HORIZONTAL
root.addView(l) layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
gravity = Gravity.CENTER_HORIZONTAL
addView(
TextView(root.context).apply {
text = dateUtil.timeString(toMills(start))
setPadding(px, px, px, px)
setOnClickListener {
root.context?.let {
val cal = Calendar.getInstance()
cal.timeInMillis = toMills(start)
TimePickerDialog(
it,
{ _, hour, minute ->
start = 60 * hour + minute
text = dateUtil.timeString(toMills(start))
},
cal.get(Calendar.HOUR_OF_DAY),
cal.get(Calendar.MINUTE),
DateFormat.is24HourFormat(it)
).show()
}
}
})
addView(TextView(root.context).apply {
@Suppress("SetTextI18n")
text = resourceHelper.gs(R.string.and) + " " + dateUtil.timeString(toMills(end))
setPadding(px, px, px, px)
setOnClickListener {
root.context?.let {
val cal = Calendar.getInstance()
cal.timeInMillis = toMills(end)
TimePickerDialog(
it,
{ _, hour, minute ->
end = 60 * hour + minute
text = dateUtil.timeString(toMills(end))
},
cal.get(Calendar.HOUR_OF_DAY),
cal.get(Calendar.MINUTE),
DateFormat.is24HourFormat(it)
).show()
}
}
})
})
} }
private fun toMills(minutesSinceMidnight: Int): Long = MidnightTime.calcPlusMinutes(minutesSinceMidnight) private fun toMills(minutesSinceMidnight: Int): Long = MidnightTime.calcPlusMinutes(minutesSinceMidnight)

View file

@ -2,7 +2,6 @@ package info.nightscout.androidaps.plugins.general.automation.elements
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.annotation.StringRes import androidx.annotation.StringRes
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.automation.R import info.nightscout.androidaps.automation.R
import info.nightscout.androidaps.utils.ui.WeekdayPicker import info.nightscout.androidaps.utils.ui.WeekdayPicker
import java.util.* import java.util.*
@ -20,6 +19,7 @@ class InputWeekDay : Element() {
get() = shortNames[ordinal] get() = shortNames[ordinal]
companion object { companion object {
private val calendarInts = intArrayOf( private val calendarInts = intArrayOf(
Calendar.MONDAY, Calendar.MONDAY,
Calendar.TUESDAY, Calendar.TUESDAY,
@ -76,10 +76,11 @@ class InputWeekDay : Element() {
} }
override fun addToLayout(root: LinearLayout) { override fun addToLayout(root: LinearLayout) {
WeekdayPicker(root.context).apply { root.addView(
setSelectedDays(getSelectedDays()) WeekdayPicker(root.context).apply {
setOnWeekdaysChangeListener { i: Int, selected: Boolean -> set(DayOfWeek.fromCalendarInt(i), selected) } setSelectedDays(getSelectedDays())
root.addView(this) setOnWeekdaysChangeListener { i: Int, selected: Boolean -> set(DayOfWeek.fromCalendarInt(i), selected) }
} }
)
} }
} }

View file

@ -1,9 +1,8 @@
package info.nightscout.androidaps.plugins.general.automation.elements package info.nightscout.androidaps.plugins.general.automation.elements
import android.graphics.Typeface import android.graphics.Typeface
import android.view.ViewGroup import android.view.Gravity
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TableLayout
import android.widget.TextView import android.widget.TextView
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
@ -15,35 +14,27 @@ class LabelWithElement(
) : Element() { ) : Element() {
override fun addToLayout(root: LinearLayout) { // container layout override fun addToLayout(root: LinearLayout) { // container layout
val layout = LinearLayout(root.context)
layout.orientation = LinearLayout.HORIZONTAL
layout.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT)
// text view pre element // text view pre element
var px = resourceHelper.dpToPx(10) val px = resourceHelper.dpToPx(1)
val textViewPre = TextView(root.context)
textViewPre.text = textPre
textViewPre.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT)
textViewPre.setPadding(px, px, px, px)
textViewPre.setTypeface(textViewPre.typeface, Typeface.BOLD)
layout.addView(textViewPre)
val spacer = TextView(root.context)
spacer.layoutParams = TableLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, 1f)
layout.addView(spacer)
// add element to layout
element?.addToLayout(layout)
// text view post element
px = resourceHelper.dpToPx(5)
val textViewPost = TextView(root.context)
textViewPost.text = textPost
textViewPost.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT)
textViewPost.setPadding(px, px, px, px)
textViewPost.setTypeface(textViewPost.typeface, Typeface.BOLD)
layout.addView(textViewPost)
// add layout to root layout
root.addView(layout)
}
root.addView(
TextView(root.context).apply {
text = textPre
setPadding(px, px, px, px)
setTypeface(typeface, Typeface.BOLD)
gravity = Gravity.CENTER
}
)
// add element to layout
element?.addToLayout(root)
// text view post element
root.addView(
TextView(root.context).apply {
text = textPost
setPadding(px, px, px, px)
setTypeface(typeface, Typeface.BOLD)
gravity = Gravity.CENTER
})
}
} }

View file

@ -1,6 +1,10 @@
package info.nightscout.androidaps.plugins.general.automation.elements package info.nightscout.androidaps.plugins.general.automation.elements
import android.content.Context
import android.view.Gravity
import android.view.ViewGroup
import android.widget.LinearLayout import android.widget.LinearLayout
import info.nightscout.androidaps.automation.R
class LayoutBuilder { class LayoutBuilder {
@ -16,8 +20,21 @@ class LayoutBuilder {
} }
fun build(layout: LinearLayout) { fun build(layout: LinearLayout) {
for (e in mElements) { val elementLayout = LinearLayout(layout.context).apply {
e.addToLayout(layout) orientation = LinearLayout.VERTICAL
layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT).apply {
setMargins(0, 0, dpToPx(layout.context, 2), dpToPx(layout.context, 2))
}
setBackgroundColor(layout.context.getColor(R.color.mdtp_line_dark))
} }
for (e in mElements) {
e.addToLayout(elementLayout)
}
layout.addView(elementLayout)
}
fun dpToPx(context: Context, dp: Int): Int {
val scale = context.resources.displayMetrics.density
return (dp * scale + 0.5f).toInt()
} }
} }

View file

@ -4,10 +4,8 @@ import android.graphics.Typeface
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import javax.inject.Inject
class StaticLabel(private val resourceHelper: ResourceHelper) : Element() { class StaticLabel(private val resourceHelper: ResourceHelper) : Element() {
@ -25,24 +23,25 @@ class StaticLabel(private val resourceHelper: ResourceHelper) : Element() {
} }
override fun addToLayout(root: LinearLayout) { override fun addToLayout(root: LinearLayout) {
val headerLayout = LinearLayout(root.context)
headerLayout.orientation = LinearLayout.HORIZONTAL
headerLayout.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
headerLayout.setBackgroundColor(resourceHelper.gc(android.R.color.black))
// text
val px = resourceHelper.dpToPx(10) val px = resourceHelper.dpToPx(10)
val textView = TextView(root.context) root.addView(
textView.text = label LinearLayout(root.context).apply {
val params = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) orientation = LinearLayout.HORIZONTAL
params.weight = 1.0f layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
textView.layoutParams = params setBackgroundColor(resourceHelper.gc(android.R.color.black))
textView.setPadding(px, px, px, px) addView(
textView.setTypeface(textView.typeface, Typeface.BOLD) TextView(root.context).apply {
headerLayout.addView(textView) text = label
trigger?.let { layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).apply {
headerLayout.addView(it.createDeleteButton(root.context, it)) weight = 1.0f
headerLayout.addView(it.createCloneButton(root.context, it)) }
} setPadding(px, px, px, px)
root.addView(headerLayout) setTypeface(typeface, Typeface.BOLD)
})
trigger?.let {
addView(it.createDeleteButton(root.context, it))
addView(it.createCloneButton(root.context, it))
}
})
} }
} }

View file

@ -1,6 +1,6 @@
package info.nightscout.androidaps.plugins.general.automation.events package info.nightscout.androidaps.plugins.general.automation.events
import info.nightscout.androidaps.events.Event import info.nightscout.androidaps.events.Event
import info.nightscout.androidaps.plugins.general.automation.triggers.Trigger import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector
class EventAutomationUpdateTrigger(val trigger: Trigger) : Event() class EventAutomationUpdateTrigger(val trigger: TriggerConnector) : Event()

View file

@ -56,7 +56,7 @@ abstract class Trigger(val injector: HasAndroidInjector) {
abstract fun icon(): Optional<Int?> abstract fun icon(): Optional<Int?>
abstract fun duplicate(): Trigger abstract fun duplicate(): Trigger
private fun scanForActivity(cont: Context?): AppCompatActivity? { fun scanForActivity(cont: Context?): AppCompatActivity? {
return when (cont) { return when (cont) {
null -> null null -> null
is AppCompatActivity -> cont is AppCompatActivity -> cont
@ -84,27 +84,19 @@ abstract class Trigger(val injector: HasAndroidInjector) {
//return (clazz.primaryConstructor?.call(injector) as Trigger).fromJSON(data?.toString() ?: "") //return (clazz.primaryConstructor?.call(injector) as Trigger).fromJSON(data?.toString() ?: "")
return when (type) { return when (type) {
TriggerAutosensValue::class.java.name, // backward compatibility TriggerAutosensValue::class.java.name, // backward compatibility
TriggerAutosensValue::class.java.simpleName -> TriggerAutosensValue(injector).fromJSON( TriggerAutosensValue::class.java.simpleName -> TriggerAutosensValue(injector).fromJSON(data.toString())
data.toString()
)
TriggerBg::class.java.name, TriggerBg::class.java.name,
TriggerBg::class.java.simpleName -> TriggerBg(injector).fromJSON(data.toString()) TriggerBg::class.java.simpleName -> TriggerBg(injector).fromJSON(data.toString())
TriggerBolusAgo::class.java.name, TriggerBolusAgo::class.java.name,
TriggerBolusAgo::class.java.simpleName -> TriggerBolusAgo(injector).fromJSON( TriggerBolusAgo::class.java.simpleName -> TriggerBolusAgo(injector).fromJSON(data.toString())
data.toString()
)
TriggerBTDevice::class.java.name, TriggerBTDevice::class.java.name,
TriggerBTDevice::class.java.simpleName -> TriggerBTDevice(injector).fromJSON( TriggerBTDevice::class.java.simpleName -> TriggerBTDevice(injector).fromJSON(data.toString())
data.toString()
)
TriggerIob::class.java.name, TriggerIob::class.java.name,
TriggerIob::class.java.simpleName -> TriggerIob(injector).fromJSON(data.toString()) TriggerIob::class.java.simpleName -> TriggerIob(injector).fromJSON(data.toString())
TriggerCOB::class.java.name, TriggerCOB::class.java.name,
TriggerCOB::class.java.simpleName -> TriggerCOB(injector).fromJSON(data.toString()) TriggerCOB::class.java.simpleName -> TriggerCOB(injector).fromJSON(data.toString())
TriggerConnector::class.java.name, TriggerConnector::class.java.name,
TriggerConnector::class.java.simpleName -> TriggerConnector(injector).fromJSON( TriggerConnector::class.java.simpleName -> TriggerConnector(injector).fromJSON(data.toString())
data.toString()
)
TriggerDelta::class.java.name, TriggerDelta::class.java.name,
TriggerDelta::class.java.simpleName -> TriggerDelta(injector).fromJSON(data.toString()) TriggerDelta::class.java.simpleName -> TriggerDelta(injector).fromJSON(data.toString())
TriggerDummy::class.java.name, TriggerDummy::class.java.name,
@ -112,100 +104,73 @@ abstract class Trigger(val injector: HasAndroidInjector) {
TriggerIob::class.java.name, TriggerIob::class.java.name,
TriggerIob::class.java.simpleName -> TriggerIob(injector).fromJSON(data.toString()) TriggerIob::class.java.simpleName -> TriggerIob(injector).fromJSON(data.toString())
TriggerLocation::class.java.name, TriggerLocation::class.java.name,
TriggerLocation::class.java.simpleName -> TriggerLocation(injector).fromJSON( TriggerLocation::class.java.simpleName -> TriggerLocation(injector).fromJSON(data.toString())
data.toString()
)
TriggerProfilePercent::class.java.name, TriggerProfilePercent::class.java.name,
TriggerProfilePercent::class.java.simpleName -> TriggerProfilePercent(injector).fromJSON( TriggerProfilePercent::class.java.simpleName -> TriggerProfilePercent(injector).fromJSON(data.toString())
data.toString()
)
TriggerPumpLastConnection::class.java.name, TriggerPumpLastConnection::class.java.name,
TriggerPumpLastConnection::class.java.simpleName -> TriggerPumpLastConnection(injector).fromJSON( TriggerPumpLastConnection::class.java.simpleName -> TriggerPumpLastConnection(injector).fromJSON(data.toString())
data.toString()
)
TriggerRecurringTime::class.java.name, TriggerRecurringTime::class.java.name,
TriggerRecurringTime::class.java.simpleName -> TriggerRecurringTime(injector).fromJSON( TriggerRecurringTime::class.java.simpleName -> TriggerRecurringTime(injector).fromJSON(data.toString())
data.toString()
)
TriggerTempTarget::class.java.name, TriggerTempTarget::class.java.name,
TriggerTempTarget::class.java.simpleName -> TriggerTempTarget(injector).fromJSON( TriggerTempTarget::class.java.simpleName -> TriggerTempTarget(injector).fromJSON(data.toString())
data.toString()
)
TriggerTempTargetValue::class.java.name, TriggerTempTargetValue::class.java.name,
TriggerTempTargetValue::class.java.simpleName -> TriggerTempTargetValue(injector).fromJSON( TriggerTempTargetValue::class.java.simpleName -> TriggerTempTargetValue(injector).fromJSON(data.toString())
data.toString()
)
TriggerTime::class.java.name, TriggerTime::class.java.name,
TriggerTime::class.java.simpleName -> TriggerTime(injector).fromJSON(data.toString()) TriggerTime::class.java.simpleName -> TriggerTime(injector).fromJSON(data.toString())
TriggerTimeRange::class.java.name, TriggerTimeRange::class.java.name,
TriggerTimeRange::class.java.simpleName -> TriggerTimeRange(injector).fromJSON( TriggerTimeRange::class.java.simpleName -> TriggerTimeRange(injector).fromJSON(data.toString())
data.toString()
)
TriggerWifiSsid::class.java.name, TriggerWifiSsid::class.java.name,
TriggerWifiSsid::class.java.simpleName -> TriggerWifiSsid(injector).fromJSON( TriggerWifiSsid::class.java.simpleName -> TriggerWifiSsid(injector).fromJSON(data.toString())
data.toString()
)
else -> TriggerConnector(injector) else -> TriggerConnector(injector)
} }
} }
fun createAddButton(context: Context, trigger: TriggerConnector): ImageButton { fun createAddButton(context: Context, trigger: TriggerConnector): ImageButton =
// Button [+] // Button [+]
val buttonAdd = ImageButton(context) ImageButton(context).apply {
val params = LinearLayout.LayoutParams( layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT).apply {
LinearLayout.LayoutParams.WRAP_CONTENT, gravity = Gravity.CENTER
LinearLayout.LayoutParams.WRAP_CONTENT }
) setImageResource(R.drawable.ic_add)
params.gravity = Gravity.CENTER contentDescription = resourceHelper.gs(R.string.add_short)
buttonAdd.layoutParams = params setOnClickListener {
buttonAdd.setImageResource(R.drawable.ic_add) scanForActivity(context)?.supportFragmentManager?.let {
buttonAdd.contentDescription = resourceHelper.gs(R.string.add_short) val dialog = ChooseTriggerDialog()
buttonAdd.setOnClickListener { dialog.show(it, "ChooseTriggerDialog")
scanForActivity(context)?.supportFragmentManager?.let { dialog.setOnClickListener(object : ChooseTriggerDialog.OnClickListener {
val dialog = ChooseTriggerDialog() override fun onClick(newTriggerObject: Trigger) {
dialog.show(it, "ChooseTriggerDialog") trigger.list.add(newTriggerObject)
dialog.setOnClickListener(object : ChooseTriggerDialog.OnClickListener { rxBus.send(EventTriggerChanged())
override fun onClick(newTriggerObject: Trigger) { }
trigger.list.add(newTriggerObject) })
rxBus.send(EventTriggerChanged()) }
}
})
} }
} }
return buttonAdd
}
fun createDeleteButton(context: Context, trigger: Trigger): ImageButton { fun createDeleteButton(context: Context, trigger: Trigger): ImageButton =
// Button [-] // Button [-]
val buttonRemove = ImageButton(context) ImageButton(context).apply {
val params = LinearLayout.LayoutParams( layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT).apply {
LinearLayout.LayoutParams.WRAP_CONTENT, gravity = Gravity.CENTER
LinearLayout.LayoutParams.WRAP_CONTENT }
) setImageResource(R.drawable.ic_remove)
params.gravity = Gravity.CENTER contentDescription = resourceHelper.gs(R.string.delete_short)
buttonRemove.layoutParams = params setOnClickListener {
buttonRemove.setImageResource(R.drawable.ic_remove) rxBus.send(EventTriggerRemove(trigger))
buttonRemove.contentDescription = resourceHelper.gs(R.string.delete_short) }
buttonRemove.setOnClickListener {
rxBus.send(EventTriggerRemove(trigger))
} }
return buttonRemove
}
fun createCloneButton(context: Context, trigger: Trigger): ImageButton { fun createCloneButton(context: Context, trigger: Trigger): ImageButton =
// Button [*] // Button [*]
val buttonClone = ImageButton(context) ImageButton(context).apply {
val params = LinearLayout.LayoutParams( val params = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT).apply {
LinearLayout.LayoutParams.WRAP_CONTENT, gravity = Gravity.CENTER
LinearLayout.LayoutParams.WRAP_CONTENT }
) layoutParams = params
params.gravity = Gravity.CENTER setImageResource(R.drawable.ic_clone)
buttonClone.layoutParams = params contentDescription = resourceHelper.gs(R.string.copy_short)
buttonClone.setImageResource(R.drawable.ic_clone) setOnClickListener {
buttonClone.contentDescription = resourceHelper.gs(R.string.copy_short) rxBus.send(EventTriggerClone(trigger))
buttonClone.setOnClickListener { }
rxBus.send(EventTriggerClone(trigger))
} }
return buttonClone
}
} }

View file

@ -4,6 +4,7 @@ import android.widget.LinearLayout
import com.google.common.base.Optional import com.google.common.base.Optional
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.automation.R import info.nightscout.androidaps.automation.R
import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.database.entities.Bolus import info.nightscout.androidaps.database.entities.Bolus
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.general.automation.elements.Comparator import info.nightscout.androidaps.plugins.general.automation.elements.Comparator
@ -36,7 +37,8 @@ class TriggerBolusAgo(injector: HasAndroidInjector) : Trigger(injector) {
} }
override fun shouldRun(): Boolean { override fun shouldRun(): Boolean {
val lastBolusTime = repository.getLastBolusRecordOfType(Bolus.Type.NORMAL)?.timestamp ?: 0L val lastBolus = repository.getLastBolusRecordOfTypeWrapped(Bolus.Type.NORMAL).blockingGet()
val lastBolusTime = if (lastBolus is ValueWrapper.Existing) lastBolus.value.timestamp else 0L
if (lastBolusTime == 0L) if (lastBolusTime == 0L)
return if (comparator.value == Comparator.Compare.IS_NOT_AVAILABLE) { return if (comparator.value == Comparator.Compare.IS_NOT_AVAILABLE) {
aapsLogger.debug(LTag.AUTOMATION, "Ready for execution: " + friendlyDescription()) aapsLogger.debug(LTag.AUTOMATION, "Ready for execution: " + friendlyDescription())
@ -81,7 +83,7 @@ class TriggerBolusAgo(injector: HasAndroidInjector) : Trigger(injector) {
LayoutBuilder() LayoutBuilder()
.add(StaticLabel(resourceHelper, R.string.lastboluslabel, this)) .add(StaticLabel(resourceHelper, R.string.lastboluslabel, this))
.add(comparator) .add(comparator)
.add(LabelWithElement(resourceHelper, resourceHelper.gs(R.string.lastboluslabel) + ": ", "", minutesAgo)) .add(LabelWithElement(resourceHelper, resourceHelper.gs(R.string.lastboluslabel) + ": ", resourceHelper.gs(R.string.unit_minutes), minutesAgo))
.build(root) .build(root)
} }
} }

View file

@ -1,19 +1,20 @@
package info.nightscout.androidaps.plugins.general.automation.triggers package info.nightscout.androidaps.plugins.general.automation.triggers
import android.content.Context import android.content.Context
import android.view.View import android.graphics.Typeface
import android.view.Gravity
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.Spinner import android.widget.TextView
import androidx.annotation.StringRes import androidx.annotation.StringRes
import com.google.common.base.Optional import com.google.common.base.Optional
import dagger.android.HasAndroidInjector import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.automation.R import info.nightscout.androidaps.automation.R
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.general.automation.dialogs.ChooseOperationDialog
import info.nightscout.androidaps.utils.JsonHelper.safeGetString import info.nightscout.androidaps.utils.JsonHelper.safeGetString
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.ui.VerticalTextView
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONObject import org.json.JSONObject
import java.util.* import java.util.*
@ -35,7 +36,7 @@ class TriggerConnector(injector: HasAndroidInjector) : Trigger(injector) {
@get:StringRes val stringRes: Int @get:StringRes val stringRes: Int
get() = when (this) { get() = when (this) {
OR -> R.string.or OR -> R.string.or
XOR -> R.string.xor XOR -> R.string.xor
AND -> R.string.and AND -> R.string.and
} }
@ -119,51 +120,75 @@ class TriggerConnector(injector: HasAndroidInjector) : Trigger(injector) {
override fun duplicate(): Trigger = TriggerConnector(injector, connectorType) override fun duplicate(): Trigger = TriggerConnector(injector, connectorType)
override fun generateDialog(root: LinearLayout) { override fun generateDialog(root: LinearLayout) {
val padding = resourceHelper.dpToPx(5) val mainLayout = LinearLayout(root.context).also {
root.setPadding(padding, padding, padding, padding) it.orientation = LinearLayout.HORIZONTAL
root.setBackgroundResource(R.drawable.border_automation_unit) it.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
// Header with spinner
val headerLayout = LinearLayout(root.context)
headerLayout.orientation = LinearLayout.HORIZONTAL
headerLayout.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
headerLayout.addView(createSpinner(root.context))
headerLayout.addView(createAddButton(root.context, this))
headerLayout.addView(createDeleteButton(root.context, this))
root.addView(headerLayout)
// Child triggers
val listLayout = LinearLayout(root.context)
listLayout.orientation = LinearLayout.VERTICAL
listLayout.setBackgroundColor(resourceHelper.gc(R.color.mdtp_line_dark))
//listLayout.setPadding(resourceHelper.dpToPx(5), resourceHelper.dpToPx(5), resourceHelper.dpToPx(5), 0)
val params = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
params.setMargins(resourceHelper.dpToPx(15), 0, resourceHelper.dpToPx(5), resourceHelper.dpToPx(4))
listLayout.layoutParams = params
for (t in list) t.generateDialog(listLayout)
root.addView(listLayout)
}
private fun createSpinner(context: Context): Spinner {
val initialPosition = connectorType.ordinal
val spinner = Spinner(context)
val spinnerArrayAdapter = ArrayAdapter(context, R.layout.spinner_centered, Type.labels(resourceHelper))
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spinner.adapter = spinnerArrayAdapter
spinner.setSelection(initialPosition)
spinner.setBackgroundColor(resourceHelper.gc(R.color.black_overlay))
val params = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
params.setMargins(0, resourceHelper.dpToPx(8), 0, resourceHelper.dpToPx(8))
params.weight = 1.0f
spinner.layoutParams = params
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
setType(Type.values()[position])
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
} }
return spinner val padding = resourceHelper.dpToPx(3)
mainLayout.setPadding(padding, padding, padding, padding)
mainLayout.setBackgroundResource(R.drawable.border_automation_unit)
val buttonLayout = LinearLayout(root.context).also {
it.orientation = LinearLayout.HORIZONTAL
it.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
}
buttonLayout.addView(createAddButton(root.context, this))
buttonLayout.addView(createDeleteButton(root.context, this))
val rightSideLayout = LinearLayout(root.context).also {
it.orientation = LinearLayout.VERTICAL
it.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, 1f)
}
rightSideLayout.addView(buttonLayout)
// Child triggers
val listLayout = LinearLayout(root.context).also {
it.orientation = LinearLayout.VERTICAL
it.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT).also { params ->
params.setMargins(resourceHelper.dpToPx(1), 0, resourceHelper.dpToPx(1), resourceHelper.dpToPx(2))
}
}
for (t in list) {
t.generateDialog(listLayout)
listLayout.addView(
TextView(root.context).also {
it.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
it.setPadding(0, resourceHelper.dpToPx(0.3f), 0, 0)
})
}
rightSideLayout.addView(listLayout)
// Header with spinner
mainLayout.addView(createVerticalView(root.context))
mainLayout.addView(rightSideLayout)
root.addView(mainLayout)
} }
private fun createVerticalView(context: Context): VerticalTextView =
VerticalTextView(context).apply {
text = resourceHelper.gs(connectorType.stringRes)
gravity = gravity or Gravity.CENTER_VERTICAL
setTypeface(typeface, Typeface.BOLD)
setBackgroundColor(resourceHelper.gc(R.color.black_overlay))
layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT).also { ll ->
ll.setMargins(resourceHelper.dpToPx(3), resourceHelper.dpToPx(3), resourceHelper.dpToPx(3), resourceHelper.dpToPx(3))
}
setOnClickListener {
scanForActivity(context)?.supportFragmentManager?.let {
ChooseOperationDialog().also { dialog ->
dialog.setCallback(object : ChooseOperationDialog.Callback() {
override fun run() {
result?.let { result ->
setType(Type.values()[result])
text = resourceHelper.gs(connectorType.stringRes)
}
}
})
dialog.setCheckedIndex(connectorType.ordinal)
dialog.show(it, "TriggerConnector")
}
}
}
}
} }

View file

@ -55,6 +55,12 @@ class TriggerProfilePercent(injector: HasAndroidInjector) : Trigger(injector) {
return true return true
} }
} }
if (profile is ProfileSealed.Pure) {
if (comparator.value.check(100.0, pct.value)) {
aapsLogger.debug(LTag.AUTOMATION, "Ready for execution: " + friendlyDescription())
return true
}
}
aapsLogger.debug(LTag.AUTOMATION, "NOT ready for execution: " + friendlyDescription()) aapsLogger.debug(LTag.AUTOMATION, "NOT ready for execution: " + friendlyDescription())
return false return false
} }

View file

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusableInTouchMode="true"
android:minWidth="300dp"
android:orientation="vertical"
tools:context="info.nightscout.androidaps.plugins.general.automation.dialogs.ChooseOperationDialog">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@color/dialog_title_background"
android:orientation="horizontal"
android:padding="5dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/please_choose_a_operation_type"
app:srcCompat="@drawable/ic_trigger_green_48dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:text="@string/please_choose_a_operation_type"
android:textAlignment="center"
android:textAppearance="?android:attr/textAppearanceLarge" />
</RelativeLayout>
<RadioGroup
android:id="@+id/chooseOperationRadioGroup"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"/>
<include layout="@layout/okcancel" />
</LinearLayout>
</ScrollView>

View file

@ -7,7 +7,7 @@
android:focusableInTouchMode="true" android:focusableInTouchMode="true"
android:minWidth="300dp" android:minWidth="300dp"
android:orientation="vertical" android:orientation="vertical"
tools:context=".plugins.general.automation.dialogs.EditEventDialog"> tools:context="info.nightscout.androidaps.plugins.general.automation.dialogs.EditEventDialog">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -45,7 +45,7 @@
android:id="@+id/layoutTrigger" android:id="@+id/layoutTrigger"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="10dp" android:padding="0dp"
android:orientation="vertical" /> android:orientation="vertical" />
<include layout="@layout/okcancel" /> <include layout="@layout/okcancel" />

View file

@ -7,14 +7,14 @@
android:focusableInTouchMode="true" android:focusableInTouchMode="true"
android:minWidth="300dp" android:minWidth="300dp"
android:orientation="vertical" android:orientation="vertical"
tools:context=".plugins.general.automation.dialogs.EditEventDialog"> tools:context="info.nightscout.androidaps.plugins.general.automation.dialogs.EditEventDialog">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<RelativeLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
@ -29,17 +29,23 @@
app:srcCompat="@drawable/ic_action_orange_48dp" /> app:srcCompat="@drawable/ic_action_orange_48dp" />
<TextView <TextView
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_gravity="center" android:layout_gravity="center"
android:layout_marginLeft="10dp" android:layout_marginLeft="10dp"
android:layout_marginRight="10dp" android:layout_marginRight="10dp"
android:text="@string/automation_event" android:text="@string/automation_event"
android:textAlignment="center" android:textAlignment="center"
android:layout_weight="1"
android:textAppearance="?android:attr/textAppearanceLarge" /> android:textAppearance="?android:attr/textAppearanceLarge" />
</RelativeLayout> <CheckBox
android:id="@+id/enabled"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical" />
</LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/spacer" android:id="@+id/spacer"
@ -54,6 +60,27 @@
android:orientation="vertical" android:orientation="vertical"
android:padding="10dp"> android:padding="10dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageButton
android:id="@+id/imageButton"
android:layout_width="27dp"
android:layout_height="match_parent"
android:scaleX="0.7"
android:scaleY="0.7"
android:src="@drawable/ic_danar_useropt" />
<CheckBox
android:id="@+id/user_action"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/user_action" />
</LinearLayout>
<com.google.android.material.textfield.TextInputEditText <com.google.android.material.textfield.TextInputEditText
android:id="@+id/inputEventTitle" android:id="@+id/inputEventTitle"
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -107,12 +107,13 @@
<string name="edit_short">EDIT</string> <string name="edit_short">EDIT</string>
<string name="please_choose_an_action_type">Choose an action type</string> <string name="please_choose_an_action_type">Choose an action type</string>
<string name="please_choose_a_trigger_type">Choose a trigger type</string> <string name="please_choose_a_trigger_type">Choose a trigger type</string>
<string name="please_choose_a_operation_type">Choose a operation type</string>
<string name="triggers">Triggers:</string> <string name="triggers">Triggers:</string>
<string name="remove_label">REMOVE</string> <string name="remove_label">REMOVE</string>
<string name="preconditions">Preconditions:</string> <string name="preconditions">Preconditions:</string>
<string name="automation_event">Automation event</string> <string name="automation_event">Automation event</string>
<string name="reorder_label">Reorder</string> <string name="reorder_label">Reorder</string>
<string name="key_automation_settings" translatable="false">automation_settings</string> <string name="key_automation_settings" translatable="false">automation_settings</string>
<string name="user_action">User action</string>
</resources> </resources>

View file

@ -8,6 +8,7 @@ import info.nightscout.androidaps.interfaces.Loop
import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.automation.actions.Action import info.nightscout.androidaps.plugins.general.automation.actions.Action
import info.nightscout.androidaps.plugins.general.automation.actions.ActionLoopEnable import info.nightscout.androidaps.plugins.general.automation.actions.ActionLoopEnable
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnector
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnectorTest import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerConnectorTest
import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerDummy import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerDummy
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
@ -44,11 +45,12 @@ class AutomationEventTest : TestBase() {
// create test object // create test object
val event = AutomationEvent(injector) val event = AutomationEvent(injector)
event.title = "Test" event.title = "Test"
event.trigger = TriggerDummy(injector).instantiate(JSONObject(TriggerConnectorTest.oneItem)) event.trigger = TriggerDummy(injector).instantiate(JSONObject(TriggerConnectorTest.oneItem)) as TriggerConnector
event.addAction(ActionLoopEnable(injector)) event.addAction(ActionLoopEnable(injector))
// export to json // export to json
val eventJsonExpected = "{\"autoRemove\":false,\"readOnly\":false,\"trigger\":\"{\\\"data\\\":{\\\"connectorType\\\":\\\"AND\\\",\\\"triggerList\\\":[\\\"{\\\\\\\"data\\\\\\\":{\\\\\\\"connectorType\\\\\\\":\\\\\\\"AND\\\\\\\",\\\\\\\"triggerList\\\\\\\":[]},\\\\\\\"type\\\\\\\":\\\\\\\"TriggerConnector\\\\\\\"}\\\"]},\\\"type\\\":\\\"TriggerConnector\\\"}\",\"title\":\"Test\",\"systemAction\":false,\"actions\":[\"{\\\"type\\\":\\\"ActionLoopEnable\\\"}\"],\"enabled\":true}" val eventJsonExpected =
"{\"userAction\":false,\"autoRemove\":false,\"readOnly\":false,\"trigger\":\"{\\\"data\\\":{\\\"connectorType\\\":\\\"AND\\\",\\\"triggerList\\\":[\\\"{\\\\\\\"data\\\\\\\":{\\\\\\\"connectorType\\\\\\\":\\\\\\\"AND\\\\\\\",\\\\\\\"triggerList\\\\\\\":[]},\\\\\\\"type\\\\\\\":\\\\\\\"TriggerConnector\\\\\\\"}\\\"]},\\\"type\\\":\\\"TriggerConnector\\\"}\",\"title\":\"Test\",\"systemAction\":false,\"actions\":[\"{\\\"type\\\":\\\"ActionLoopEnable\\\"}\"],\"enabled\":true}"
Assert.assertEquals(eventJsonExpected, event.toJSON()) Assert.assertEquals(eventJsonExpected, event.toJSON())
// clone // clone

View file

@ -2,8 +2,10 @@ package info.nightscout.androidaps.plugins.general.automation.triggers
import com.google.common.base.Optional import com.google.common.base.Optional
import info.nightscout.androidaps.automation.R import info.nightscout.androidaps.automation.R
import info.nightscout.androidaps.database.ValueWrapper
import info.nightscout.androidaps.database.entities.Bolus import info.nightscout.androidaps.database.entities.Bolus
import info.nightscout.androidaps.plugins.general.automation.elements.Comparator import info.nightscout.androidaps.plugins.general.automation.elements.Comparator
import io.reactivex.Single
import org.json.JSONException import org.json.JSONException
import org.json.JSONObject import org.json.JSONObject
import org.junit.Assert import org.junit.Assert
@ -22,13 +24,18 @@ class TriggerBolusAgoTest : TriggerTestBase() {
@Test @Test
fun shouldRunTest() { fun shouldRunTest() {
`when`(repository.getLastBolusRecordOfType(Bolus.Type.NORMAL)).thenReturn( // Set last bolus time to now
Bolus( `when`(repository.getLastBolusRecordOfTypeWrapped(Bolus.Type.NORMAL)).thenReturn(
timestamp = now, Single.just(
amount = 0.0, ValueWrapper.Existing(
type = Bolus.Type.NORMAL Bolus(
timestamp = now,
amount = 0.0,
type = Bolus.Type.NORMAL
)
)
) )
) // Set last bolus time to now )
`when`(dateUtil.now()).thenReturn(now + 10 * 60 * 1000) // set current time to now + 10 min `when`(dateUtil.now()).thenReturn(now + 10 * 60 * 1000) // set current time to now + 10 min
var t = TriggerBolusAgo(injector).setValue(110).comparator(Comparator.Compare.IS_EQUAL) var t = TriggerBolusAgo(injector).setValue(110).comparator(Comparator.Compare.IS_EQUAL)
Assert.assertEquals(110, t.minutesAgo.value) Assert.assertEquals(110, t.minutesAgo.value)
@ -51,13 +58,18 @@ class TriggerBolusAgoTest : TriggerTestBase() {
Assert.assertTrue(t.shouldRun()) Assert.assertTrue(t.shouldRun())
t = TriggerBolusAgo(injector).setValue(390).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER) t = TriggerBolusAgo(injector).setValue(390).comparator(Comparator.Compare.IS_EQUAL_OR_LESSER)
Assert.assertTrue(t.shouldRun()) Assert.assertTrue(t.shouldRun())
`when`(repository.getLastBolusRecordOfType(Bolus.Type.NORMAL)).thenReturn( // Set last bolus time to 0
Bolus( `when`(repository.getLastBolusRecordOfTypeWrapped(Bolus.Type.NORMAL)).thenReturn(
timestamp = 0L, Single.just(
amount = 0.0, ValueWrapper.Existing(
type = Bolus.Type.NORMAL Bolus(
timestamp = 0,
amount = 0.0,
type = Bolus.Type.NORMAL
)
)
) )
) // Set last bolus time to 0 )
t = TriggerBolusAgo(injector).comparator(Comparator.Compare.IS_NOT_AVAILABLE) t = TriggerBolusAgo(injector).comparator(Comparator.Compare.IS_NOT_AVAILABLE)
Assert.assertTrue(t.shouldRun()) Assert.assertTrue(t.shouldRun())
} }

View file

@ -53,7 +53,7 @@ class TriggerProfilePercentTest : TriggerTestBase() {
Assert.assertEquals(bgJson, t.toJSON()) Assert.assertEquals(bgJson, t.toJSON())
} }
@Test @Throws(JSONException::class) fun fromJSONTest() { @Test fun fromJSONTest() {
val t: TriggerProfilePercent = TriggerProfilePercent(injector).setValue(120.0).comparator(Comparator.Compare.IS_EQUAL) val t: TriggerProfilePercent = TriggerProfilePercent(injector).setValue(120.0).comparator(Comparator.Compare.IS_EQUAL)
val t2 = TriggerDummy(injector).instantiate(JSONObject(t.toJSON())) as TriggerProfilePercent val t2 = TriggerDummy(injector).instantiate(JSONObject(t.toJSON())) as TriggerProfilePercent
Assert.assertEquals(Comparator.Compare.IS_EQUAL, t2.comparator.value) Assert.assertEquals(Comparator.Compare.IS_EQUAL, t2.comparator.value)

View file

@ -14,11 +14,11 @@ object OKDialog {
@SuppressLint("InflateParams") @SuppressLint("InflateParams")
fun show(context: Context, title: String, message: String, runnable: Runnable? = null) { fun show(context: Context, title: String, message: String, runnable: Runnable? = null) {
var okClicked = false var okClicked = false
var notEmptytitle = title var notEmptyTitle = title
if (notEmptytitle.isEmpty()) notEmptytitle = context.getString(R.string.message) if (notEmptyTitle.isEmpty()) notEmptyTitle = context.getString(R.string.message)
AlertDialogHelper.Builder(context) AlertDialogHelper.Builder(context)
.setCustomTitle(AlertDialogHelper.buildCustomTitle(context, notEmptytitle)) .setCustomTitle(AlertDialogHelper.buildCustomTitle(context, notEmptyTitle))
.setMessage(message) .setMessage(message)
.setPositiveButton(context.getString(R.string.ok)) { dialog: DialogInterface, _: Int -> .setPositiveButton(context.getString(R.string.ok)) { dialog: DialogInterface, _: Int ->
if (okClicked) return@setPositiveButton if (okClicked) return@setPositiveButton
@ -36,11 +36,11 @@ object OKDialog {
@SuppressLint("InflateParams") @SuppressLint("InflateParams")
fun show(activity: FragmentActivity, title: String, message: Spanned, runnable: Runnable? = null) { fun show(activity: FragmentActivity, title: String, message: Spanned, runnable: Runnable? = null) {
var okClicked = false var okClicked = false
var notEmptytitle = title var notEmptyTitle = title
if (notEmptytitle.isEmpty()) notEmptytitle = activity.getString(R.string.message) if (notEmptyTitle.isEmpty()) notEmptyTitle = activity.getString(R.string.message)
AlertDialogHelper.Builder(activity) AlertDialogHelper.Builder(activity)
.setCustomTitle(AlertDialogHelper.buildCustomTitle(activity, notEmptytitle)) .setCustomTitle(AlertDialogHelper.buildCustomTitle(activity, notEmptyTitle))
.setMessage(message) .setMessage(message)
.setPositiveButton(activity.getString(R.string.ok)) { dialog: DialogInterface, _: Int -> .setPositiveButton(activity.getString(R.string.ok)) { dialog: DialogInterface, _: Int ->
if (okClicked) return@setPositiveButton if (okClicked) return@setPositiveButton

View file

@ -21,5 +21,6 @@ interface ResourceHelper {
fun decodeResource(id : Int) : Bitmap fun decodeResource(id : Int) : Bitmap
fun getDisplayMetrics(): DisplayMetrics fun getDisplayMetrics(): DisplayMetrics
fun dpToPx(dp: Int): Int fun dpToPx(dp: Int): Int
fun dpToPx(dp: Float): Int
fun shortTextMode(): Boolean fun shortTextMode(): Boolean
} }

View file

@ -26,7 +26,7 @@ class ResourceHelperImplementation @Inject constructor(private val context: Cont
override fun gq(@PluralsRes id: Int, quantity: Int, vararg args: Any?): String = override fun gq(@PluralsRes id: Int, quantity: Int, vararg args: Any?): String =
context.resources.getQuantityString(id, quantity, *args) context.resources.getQuantityString(id, quantity, *args)
override fun gsNotLocalised(@StringRes id: Int, vararg args: Any?) : String = override fun gsNotLocalised(@StringRes id: Int, vararg args: Any?): String =
with(Configuration(context.resources.configuration)) { with(Configuration(context.resources.configuration)) {
setLocale(Locale.ENGLISH) setLocale(Locale.ENGLISH)
context.createConfigurationContext(this).getString(id, args) context.createConfigurationContext(this).getString(id, args)
@ -51,7 +51,7 @@ class ResourceHelperImplementation @Inject constructor(private val context: Cont
override fun decodeResource(id: Int): Bitmap = override fun decodeResource(id: Int): Bitmap =
BitmapFactory.decodeResource(context.resources, id) BitmapFactory.decodeResource(context.resources, id)
override fun getDisplayMetrics():DisplayMetrics = override fun getDisplayMetrics(): DisplayMetrics =
context.resources.displayMetrics context.resources.displayMetrics
override fun dpToPx(dp: Int): Int { override fun dpToPx(dp: Int): Int {
@ -59,5 +59,10 @@ class ResourceHelperImplementation @Inject constructor(private val context: Cont
return (dp * scale + 0.5f).toInt() return (dp * scale + 0.5f).toInt()
} }
override fun shortTextMode() : Boolean = !gb(R.bool.isTablet) override fun dpToPx(dp: Float): Int {
val scale = context.resources.displayMetrics.density
return (dp * scale + 0.5f).toInt()
}
override fun shortTextMode(): Boolean = !gb(R.bool.isTablet)
} }

View file

@ -0,0 +1,44 @@
package info.nightscout.androidaps.utils.ui
import android.content.Context
import android.graphics.Canvas
import android.util.AttributeSet
import android.view.Gravity
import androidx.appcompat.widget.AppCompatTextView
class VerticalTextView(context: Context, attrs: AttributeSet? = null) : AppCompatTextView(context, attrs) {
private var topDown = false
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(heightMeasureSpec, widthMeasureSpec)
setMeasuredDimension(measuredHeight, measuredWidth)
}
override fun onDraw(canvas: Canvas) {
val textPaint = paint
textPaint.color = currentTextColor
textPaint.drawableState = drawableState
canvas.save()
if (topDown) {
canvas.translate(width.toFloat(), 0f)
canvas.rotate(90f)
} else {
canvas.translate(0f, height.toFloat())
canvas.rotate(-90f)
}
canvas.translate(compoundPaddingLeft.toFloat(), extendedPaddingTop.toFloat())
layout.draw(canvas)
canvas.restore()
}
init {
val gravity = gravity
topDown = if (Gravity.isVertical(gravity) && gravity and Gravity.VERTICAL_GRAVITY_MASK == Gravity.BOTTOM) {
setGravity(gravity and Gravity.HORIZONTAL_GRAVITY_MASK or Gravity.TOP)
false
} else {
true
}
}
}

View file

@ -3,6 +3,7 @@
Please use AlertDialogHelper or wrap inflater context with ContextThemeWrapper(context, R.style.AppTheme) Please use AlertDialogHelper or wrap inflater context with ContextThemeWrapper(context, R.style.AppTheme)
--> -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
@ -19,7 +20,7 @@
android:id="@+id/alertdialog_icon" android:id="@+id/alertdialog_icon"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:tint="?dialogTitleIconTint" /> app:tint="?dialogTitleIconTint" />
<TextView <TextView
android:id="@+id/alertdialog_title" android:id="@+id/alertdialog_title"

View file

@ -11,6 +11,9 @@
<color name="cardColorBackground">#121212</color> <color name="cardColorBackground">#121212</color>
<color name="black_overlay">#66000000</color> <color name="black_overlay">#66000000</color>
<color name="defaultbackground">#424242</color>
<color name="defaulttextcolor">#B3FFFFFF</color>
<!-- Fragments--> <!-- Fragments-->
<color name="pumpStatusBackground">#505050</color> <color name="pumpStatusBackground">#505050</color>

View file

@ -436,8 +436,10 @@ import kotlin.math.roundToInt
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.toWrappedSingle() .toWrappedSingle()
fun getLastBolusRecordOfType(type: Bolus.Type): Bolus? = fun getLastBolusRecordOfTypeWrapped(type: Bolus.Type): Single<ValueWrapper<Bolus>> =
database.bolusDao.getLastBolusRecordOfType(type) database.bolusDao.getLastBolusRecordOfType(type)
.subscribeOn(Schedulers.io())
.toWrappedSingle()
fun getOldestBolusRecord(): Bolus? = fun getOldestBolusRecord(): Bolus? =
database.bolusDao.getOldestBolusRecord() database.bolusDao.getOldestBolusRecord()

View file

@ -3,10 +3,8 @@ package info.nightscout.androidaps.database.daos
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Query import androidx.room.Query
import info.nightscout.androidaps.database.TABLE_BOLUSES import info.nightscout.androidaps.database.TABLE_BOLUSES
import info.nightscout.androidaps.database.TABLE_BOLUS_CALCULATOR_RESULTS
import info.nightscout.androidaps.database.embedments.InterfaceIDs import info.nightscout.androidaps.database.embedments.InterfaceIDs
import info.nightscout.androidaps.database.entities.Bolus import info.nightscout.androidaps.database.entities.Bolus
import info.nightscout.androidaps.database.entities.BolusCalculatorResult
import io.reactivex.Maybe import io.reactivex.Maybe
import io.reactivex.Single import io.reactivex.Single
@ -42,7 +40,7 @@ internal interface BolusDao : TraceableDao<Bolus> {
fun getLastBolusRecordMaybe(exclude: Bolus.Type = Bolus.Type.PRIMING): Maybe<Bolus> fun getLastBolusRecordMaybe(exclude: Bolus.Type = Bolus.Type.PRIMING): Maybe<Bolus>
@Query("SELECT * FROM $TABLE_BOLUSES WHERE isValid = 1 AND type == :only AND referenceId IS NULL ORDER BY timestamp DESC LIMIT 1") @Query("SELECT * FROM $TABLE_BOLUSES WHERE isValid = 1 AND type == :only AND referenceId IS NULL ORDER BY timestamp DESC LIMIT 1")
fun getLastBolusRecordOfType(only: Bolus.Type): Bolus? fun getLastBolusRecordOfType(only: Bolus.Type): Maybe<Bolus>
@Query("SELECT * FROM $TABLE_BOLUSES WHERE isValid = 1 AND type <> :exclude AND referenceId IS NULL ORDER BY timestamp ASC LIMIT 1") @Query("SELECT * FROM $TABLE_BOLUSES WHERE isValid = 1 AND type <> :exclude AND referenceId IS NULL ORDER BY timestamp ASC LIMIT 1")
fun getOldestBolusRecord(exclude: Bolus.Type = Bolus.Type.PRIMING): Bolus? fun getOldestBolusRecord(exclude: Bolus.Type = Bolus.Type.PRIMING): Bolus?