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
@ -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

@ -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)
} }
@ -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,7 +55,8 @@ 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(
PluginDescription()
.mainType(PluginType.GENERAL) .mainType(PluginType.GENERAL)
.fragmentClass(AutomationFragment::class.qualifiedName) .fragmentClass(AutomationFragment::class.qualifiedName)
.pluginIcon(R.drawable.ic_automation) .pluginIcon(R.drawable.ic_automation)
@ -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 {
@ -201,7 +203,20 @@ 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())
processEvent(event, userEventsEnabled)
}
// we cannot detect connected BT devices
// so let's collect all connection/disconnections between 2 runs of processActions()
// TriggerBTDevice can pick up and process these events
// after processing clear events to prevent repeated actions
btConnects.clear()
storeToSP() // save last run time
}
fun processEvent(event: AutomationEvent, userEventsEnabled: Boolean) {
if (event.trigger.shouldRun() && event.getPreconditions().shouldRun()) {
if (event.systemAction || userEventsEnabled) { if (event.systemAction || userEventsEnabled) {
val actions = event.actions val actions = event.actions
for (action in actions) { for (action in actions) {
@ -236,14 +251,6 @@ class AutomationPlugin @Inject constructor(
} }
} }
} }
// we cannot detect connected BT devices
// so let's collect all connection/disconnections between 2 runs of processActions()
// TriggerBTDevice can pick up and process these events
// after processing clear events to prevent repeated actions
btConnects.clear()
storeToSP() // save last run time
}
@Synchronized @Synchronized
fun add(event: AutomationEvent) { fun add(event: AutomationEvent) {
@ -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()

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
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 = Compare.values()[position] 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
@ -21,6 +22,7 @@ class ComparatorConnect(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()) 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 layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT).apply {
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { setMargins(0, resourceHelper.dpToPx(4), 0, resourceHelper.dpToPx(4))
}
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 = Compare.values()[position] 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
})
} }
} }

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
@ -21,6 +22,7 @@ class ComparatorExists(private val resourceHelper: ResourceHelper, var value: Co
} }
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 { }
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 = Compare.values()[position] 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
})
} }
} }

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,10 +27,12 @@ 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 {
@ -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,17 +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.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() {
@ -46,6 +44,7 @@ class InputCarePortalMenu(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 (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 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 = EventType.values()[position] value = EventType.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(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 px = resourceHelper.dpToPx(10)
val dateButton = TextView(root.context)
val timeButton = TextView(root.context)
dateButton.text = dateUtil.dateString(value)
timeButton.text = dateUtil.timeString(value)
// create an OnDateSetListener root.addView(
val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth -> LinearLayout(root.context).apply {
val cal = Calendar.getInstance() orientation = LinearLayout.HORIZONTAL
cal.timeInMillis = value layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
cal.set(Calendar.YEAR, year) addView(
cal.set(Calendar.MONTH, monthOfYear) TextView(root.context).apply {
cal.set(Calendar.DAY_OF_MONTH, dayOfMonth) text = resourceHelper.gs(R.string.atspecifiedtime, "")
value = cal.timeInMillis setTypeface(typeface, Typeface.BOLD)
dateButton.text = dateUtil.dateString(value) setPadding(px, px, px, px)
} })
addView(
dateButton.setOnClickListener { TextView(root.context).apply {
text = dateUtil.dateString(value)
setPadding(px, px, px, px)
setOnClickListener {
root.context?.let { root.context?.let {
val cal = Calendar.getInstance() val cal = Calendar.getInstance()
cal.timeInMillis = value cal.timeInMillis = value
DatePickerDialog(it, dateSetListener, 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.YEAR),
cal.get(Calendar.MONTH), cal.get(Calendar.MONTH),
cal.get(Calendar.DAY_OF_MONTH) cal.get(Calendar.DAY_OF_MONTH)
).show() ).show()
} }
} }
})
// create an OnTimeSetListener addView(
val timeSetListener = TimePickerDialog.OnTimeSetListener { _, hour, minute -> TextView(root.context).apply {
val cal = Calendar.getInstance() text = dateUtil.timeString(value)
cal.timeInMillis = value setPadding(px, px, px, px)
cal.set(Calendar.HOUR_OF_DAY, hour) setOnClickListener {
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 { root.context?.let {
val cal = Calendar.getInstance() val cal = Calendar.getInstance()
cal.timeInMillis = value cal.timeInMillis = value
TimePickerDialog(it, timeSetListener, 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.HOUR_OF_DAY),
cal.get(Calendar.MINUTE), cal.get(Calendar.MINUTE),
DateFormat.is24HourFormat(it) DateFormat.is24HourFormat(it)
).show() ).show()
} }
} }
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)
} }
)
})
}
} }

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() {
@ -25,6 +25,7 @@ class InputDelta(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 (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
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) {
deltaType = DeltaType.values()[position] 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,30 +25,24 @@ 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)
}) })
} }

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) root.addView(
val spinner = Spinner(root.context) Spinner(root.context).apply {
spinner.adapter = adapter adapter = ArrayAdapter(root.context, R.layout.spinner_centered, profileList).apply {
val spinnerParams = LinearLayout.LayoutParams( setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
LinearLayout.LayoutParams.WRAP_CONTENT, }
LinearLayout.LayoutParams.WRAP_CONTENT layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT).apply {
) setMargins(0, resourceHelper.dpToPx(4), 0, resourceHelper.dpToPx(4))
spinnerParams.setMargins(0, resourceHelper.dpToPx(4), 0, resourceHelper.dpToPx(4)) }
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 = profileList[position].toString() value = profileList[position].toString()
} }
override fun onNothingSelected(parent: AdapterView<*>?) {} override fun onNothingSelected(parent: AdapterView<*>?) {}
} }
for (i in 0 until profileList.size) if (profileList[i] == value) spinner.setSelection(i) for (i in 0 until profileList.size) if (profileList[i] == value) setSelection(i)
val l = LinearLayout(root.context) gravity = Gravity.CENTER_HORIZONTAL
l.orientation = LinearLayout.VERTICAL })
l.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
l.addView(spinner)
root.addView(l)
} }
} }

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(
TextView(root.context).apply {
text = dateUtil.timeString(toMills(value))
val px = resourceHelper.dpToPx(10)
setPadding(px, px, px, px)
setOnClickListener {
root.context?.let { root.context?.let {
val cal = Calendar.getInstance() val cal = Calendar.getInstance()
cal.timeInMillis = toMills(value) cal.timeInMillis = toMills(value)
TimePickerDialog(it, startTimeSetListener, TimePickerDialog(
it,
{ _, hour, minute ->
value = 60 * hour + minute
text = dateUtil.timeString(toMills(value))
},
cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.HOUR_OF_DAY),
cal.get(Calendar.MINUTE), cal.get(Calendar.MINUTE),
DateFormat.is24HourFormat(it) DateFormat.is24HourFormat(it)
).show() ).show()
} }
} }
})
val px = resourceHelper.dpToPx(10) })
label.text = resourceHelper.gs(R.string.atspecifiedtime, "")
label.setTypeface(label.typeface, Typeface.BOLD)
startButton.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(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 px = resourceHelper.dpToPx(10)
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 -> root.addView(
start = 60 * hour + minute TextView(root.context).apply {
startButton.text = dateUtil.timeString(toMills(start)) text = resourceHelper.gs(R.string.between)
} setTypeface(typeface, Typeface.BOLD)
gravity = Gravity.CENTER_HORIZONTAL
startButton.setOnClickListener { })
root.addView(
LinearLayout(root.context).apply {
orientation = LinearLayout.HORIZONTAL
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 { root.context?.let {
val cal = Calendar.getInstance() val cal = Calendar.getInstance()
cal.timeInMillis = toMills(start) cal.timeInMillis = toMills(start)
TimePickerDialog(it, startTimeSetListener, TimePickerDialog(
it,
{ _, hour, minute ->
start = 60 * hour + minute
text = dateUtil.timeString(toMills(start))
},
cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.HOUR_OF_DAY),
cal.get(Calendar.MINUTE), cal.get(Calendar.MINUTE),
DateFormat.is24HourFormat(it) DateFormat.is24HourFormat(it)
).show() ).show()
} }
} }
})
val endTimeSetListener = TimePickerDialog.OnTimeSetListener { _, hour, minute -> addView(TextView(root.context).apply {
end = 60 * hour + minute @Suppress("SetTextI18n")
endButton.text = dateUtil.timeString(toMills(end)) text = resourceHelper.gs(R.string.and) + " " + dateUtil.timeString(toMills(end))
} setPadding(px, px, px, px)
setOnClickListener {
endButton.setOnClickListener {
root.context?.let { root.context?.let {
val cal = Calendar.getInstance() val cal = Calendar.getInstance()
cal.timeInMillis = toMills(end) cal.timeInMillis = toMills(end)
TimePickerDialog(it, endTimeSetListener, TimePickerDialog(
it,
{ _, hour, minute ->
end = 60 * hour + minute
text = dateUtil.timeString(toMills(end))
},
cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.HOUR_OF_DAY),
cal.get(Calendar.MINUTE), cal.get(Calendar.MINUTE),
DateFormat.is24HourFormat(it) DateFormat.is24HourFormat(it)
).show() ).show()
} }
} }
})
val px = resourceHelper.dpToPx(10) })
label.text = resourceHelper.gs(R.string.between)
label.setTypeface(label.typeface, Typeface.BOLD)
startButton.setPadding(px, px, px, px)
endButton.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(startButton)
l.addView(endButton)
root.addView(l)
} }
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) {
root.addView(
WeekdayPicker(root.context).apply { WeekdayPicker(root.context).apply {
setSelectedDays(getSelectedDays()) setSelectedDays(getSelectedDays())
setOnWeekdaysChangeListener { i: Int, selected: Boolean -> set(DayOfWeek.fromCalendarInt(i), selected) } setOnWeekdaysChangeListener { i: Int, selected: Boolean -> set(DayOfWeek.fromCalendarInt(i), selected) }
root.addView(this) }
} )
} }
} }

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) {
val elementLayout = LinearLayout(layout.context).apply {
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) { for (e in mElements) {
e.addToLayout(layout) 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
layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT).apply {
weight = 1.0f
}
setPadding(px, px, px, px)
setTypeface(typeface, Typeface.BOLD)
})
trigger?.let { trigger?.let {
headerLayout.addView(it.createDeleteButton(root.context, it)) addView(it.createDeleteButton(root.context, it))
headerLayout.addView(it.createCloneButton(root.context, it)) addView(it.createCloneButton(root.context, it))
} }
root.addView(headerLayout) })
} }
} }

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,55 +104,36 @@ 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)
buttonAdd.contentDescription = resourceHelper.gs(R.string.add_short)
buttonAdd.setOnClickListener {
scanForActivity(context)?.supportFragmentManager?.let { scanForActivity(context)?.supportFragmentManager?.let {
val dialog = ChooseTriggerDialog() val dialog = ChooseTriggerDialog()
dialog.show(it, "ChooseTriggerDialog") dialog.show(it, "ChooseTriggerDialog")
@ -172,40 +145,32 @@ abstract class Trigger(val injector: HasAndroidInjector) {
}) })
} }
} }
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)
buttonRemove.contentDescription = resourceHelper.gs(R.string.delete_short)
buttonRemove.setOnClickListener {
rxBus.send(EventTriggerRemove(trigger)) 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)
buttonClone.setOnClickListener {
rxBus.send(EventTriggerClone(trigger)) 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.*
@ -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) val padding = resourceHelper.dpToPx(3)
headerLayout.orientation = LinearLayout.HORIZONTAL mainLayout.setPadding(padding, padding, padding, padding)
headerLayout.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) mainLayout.setBackgroundResource(R.drawable.border_automation_unit)
headerLayout.addView(createSpinner(root.context))
headerLayout.addView(createAddButton(root.context, this)) val buttonLayout = LinearLayout(root.context).also {
headerLayout.addView(createDeleteButton(root.context, this)) it.orientation = LinearLayout.HORIZONTAL
root.addView(headerLayout) 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 // Child triggers
val listLayout = LinearLayout(root.context) val listLayout = LinearLayout(root.context).also {
listLayout.orientation = LinearLayout.VERTICAL it.orientation = LinearLayout.VERTICAL
listLayout.setBackgroundColor(resourceHelper.gc(R.color.mdtp_line_dark)) it.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT).also { params ->
//listLayout.setPadding(resourceHelper.dpToPx(5), resourceHelper.dpToPx(5), resourceHelper.dpToPx(5), 0) params.setMargins(resourceHelper.dpToPx(1), 0, resourceHelper.dpToPx(1), resourceHelper.dpToPx(2))
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) {
for (t in list) t.generateDialog(listLayout) t.generateDialog(listLayout)
root.addView(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 createSpinner(context: Context): Spinner { private fun createVerticalView(context: Context): VerticalTextView =
val initialPosition = connectorType.ordinal VerticalTextView(context).apply {
val spinner = Spinner(context) text = resourceHelper.gs(connectorType.stringRes)
val spinnerArrayAdapter = ArrayAdapter(context, R.layout.spinner_centered, Type.labels(resourceHelper)) gravity = gravity or Gravity.CENTER_VERTICAL
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) setTypeface(typeface, Typeface.BOLD)
spinner.adapter = spinnerArrayAdapter setBackgroundColor(resourceHelper.gc(R.color.black_overlay))
spinner.setSelection(initialPosition) layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT).also { ll ->
spinner.setBackgroundColor(resourceHelper.gc(R.color.black_overlay)) ll.setMargins(resourceHelper.dpToPx(3), resourceHelper.dpToPx(3), resourceHelper.dpToPx(3), resourceHelper.dpToPx(3))
val params = LinearLayout.LayoutParams( }
LinearLayout.LayoutParams.WRAP_CONTENT, setOnClickListener {
LinearLayout.LayoutParams.WRAP_CONTENT scanForActivity(context)?.supportFragmentManager?.let {
) ChooseOperationDialog().also { dialog ->
params.setMargins(0, resourceHelper.dpToPx(8), 0, resourceHelper.dpToPx(8)) dialog.setCallback(object : ChooseOperationDialog.Callback() {
params.weight = 1.0f override fun run() {
spinner.layoutParams = params result?.let { result ->
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { setType(Type.values()[result])
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { text = resourceHelper.gs(connectorType.stringRes)
setType(Type.values()[position]) }
}
})
dialog.setCheckedIndex(connectorType.ordinal)
dialog.show(it, "TriggerConnector")
}
}
} }
override fun onNothingSelected(parent: AdapterView<*>?) {}
}
return spinner
} }
} }

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
`when`(repository.getLastBolusRecordOfTypeWrapped(Bolus.Type.NORMAL)).thenReturn(
Single.just(
ValueWrapper.Existing(
Bolus( Bolus(
timestamp = now, timestamp = now,
amount = 0.0, amount = 0.0,
type = Bolus.Type.NORMAL 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
`when`(repository.getLastBolusRecordOfTypeWrapped(Bolus.Type.NORMAL)).thenReturn(
Single.just(
ValueWrapper.Existing(
Bolus( Bolus(
timestamp = 0L, timestamp = 0,
amount = 0.0, amount = 0.0,
type = Bolus.Type.NORMAL 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

@ -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 dpToPx(dp: Float): Int {
val scale = context.resources.displayMetrics.density
return (dp * scale + 0.5f).toInt()
}
override fun shortTextMode(): Boolean = !gb(R.bool.isTablet) 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?