clean time related triggers

This commit is contained in:
Milos Kozak 2020-01-05 19:23:20 +01:00
parent fa4d4c9fa9
commit ec84c3fa70
6 changed files with 192 additions and 154 deletions

View file

@ -90,6 +90,8 @@ interface AppComponent : AndroidInjector<MainApp> {
fun injectElement(inputString: InputString)
fun injectElement(inputTempTarget: InputTempTarget)
fun injectElement(inputTimeRange: InputTimeRange)
fun injectElement(inputTime: InputTime)
fun injectElement(inputWeekDay: InputWeekDay)
fun injectElement(labelWithElement: LabelWithElement)
fun injectElement(staticLabel: StaticLabel)

View file

@ -112,7 +112,7 @@ open class AppModule {
@ContributesAndroidInjector fun inputButtonInjector(): InputButton
@ContributesAndroidInjector fun comparatorInjector(): Comparator
@ContributesAndroidInjector fun comparatorExistsInjector(): ComparatorExists
@ContributesAndroidInjector fun inputTimeInjector(): InputDateTime
@ContributesAndroidInjector fun inputDateTimeInjector(): InputDateTime
@ContributesAndroidInjector fun inputDeltaInjector(): InputDelta
@ContributesAndroidInjector fun inputDoubleInjector(): InputDouble
@ContributesAndroidInjector fun inputDurationInjector(): InputDuration
@ -123,6 +123,8 @@ open class AppModule {
@ContributesAndroidInjector fun inputStringInjector(): InputString
@ContributesAndroidInjector fun inputTempTargetInjector(): InputTempTarget
@ContributesAndroidInjector fun inputTimeRangeInjector(): InputTimeRange
@ContributesAndroidInjector fun inputTimeInjector(): InputTime
@ContributesAndroidInjector fun inputWeekDayInjector(): InputWeekDay
@ContributesAndroidInjector fun labelWithElementInjector(): LabelWithElement
@ContributesAndroidInjector fun staticLabelInjector(): StaticLabel

View file

@ -21,10 +21,10 @@ class InputTempTarget(mainApp: MainApp) : Element(mainApp) {
}
override fun addToLayout(root: LinearLayout) {
var minValue = 0.0
var maxValue = 0.0
var step = 0.0
var decimalFormat: DecimalFormat? = null
var minValue: Double
var maxValue: Double
var step: Double
var decimalFormat: DecimalFormat?
if (units == Constants.MMOL) { // mmol
minValue = Constants.MIN_TT_MMOL
maxValue = Constants.MAX_TT_MMOL

View file

@ -0,0 +1,57 @@
package info.nightscout.androidaps.plugins.general.automation.elements
import android.app.TimePickerDialog
import android.graphics.Typeface
import android.text.format.DateFormat
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.MidnightTime
import info.nightscout.androidaps.utils.T
import java.util.*
class InputTime(mainApp: MainApp) : Element(mainApp) {
var value: Int = getMinSinceMidnight(DateUtil.now())
override fun addToLayout(root: LinearLayout) {
val label = TextView(root.context)
val startButton = TextView(root.context)
startButton.text = DateUtil.timeString(toMills(value))
val startTimeSetListener = TimePickerDialog.OnTimeSetListener { _, hour, minute ->
value = 60 * hour + minute
startButton.text = DateUtil.timeString(toMills(value))
}
startButton.setOnClickListener {
root.context?.let {
val cal = Calendar.getInstance()
cal.timeInMillis = toMills(value)
TimePickerDialog(it, startTimeSetListener,
cal.get(Calendar.HOUR_OF_DAY),
cal.get(Calendar.MINUTE),
DateFormat.is24HourFormat(mainApp)
).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.calc() + T.mins(minutesSinceMidnight.toLong()).msecs()
private fun getMinSinceMidnight(time: Long): Int = Profile.secondsFromMidnight(time) / 60
}

View file

@ -0,0 +1,86 @@
package info.nightscout.androidaps.plugins.general.automation.elements
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.annotation.StringRes
import com.dpro.widgets.WeekdaysPicker
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import java.util.*
class InputWeekDay(mainApp: MainApp) : Element(mainApp) {
enum class DayOfWeek {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
fun toCalendarInt(): Int {
return calendarInts[ordinal]
}
@get:StringRes val shortName: Int
get() = shortNames[ordinal]
companion object {
private val calendarInts = intArrayOf(
Calendar.MONDAY,
Calendar.TUESDAY,
Calendar.WEDNESDAY,
Calendar.THURSDAY,
Calendar.FRIDAY,
Calendar.SATURDAY,
Calendar.SUNDAY
)
private val shortNames = intArrayOf(
R.string.weekday_monday_short,
R.string.weekday_tuesday_short,
R.string.weekday_wednesday_short,
R.string.weekday_thursday_short,
R.string.weekday_friday_short,
R.string.weekday_saturday_short,
R.string.weekday_sunday_short
)
fun fromCalendarInt(day: Int): DayOfWeek {
for (i in calendarInts.indices) {
if (calendarInts[i] == day) return values()[i]
}
throw IllegalStateException("Invalid day")
}
}
}
val weekdays = BooleanArray(DayOfWeek.values().size)
init {
for (day in DayOfWeek.values()) set(day, false)
}
operator fun set(day: DayOfWeek, value: Boolean): InputWeekDay {
weekdays[day.ordinal] = value
return this
}
fun isSet(day: DayOfWeek): Boolean = weekdays[day.ordinal]
fun getSelectedDays(): List<Int> {
val selectedDays: MutableList<Int> = ArrayList()
for (i in weekdays.indices) {
val day = DayOfWeek.values()[i]
val selected = weekdays[i]
if (selected) selectedDays.add(day.toCalendarInt())
}
return selectedDays
}
override fun addToLayout(root: LinearLayout) {
val weekdaysPicker = WeekdaysPicker(root.context)
weekdaysPicker.setEditable(true)
weekdaysPicker.selectedDays = getSelectedDays()
weekdaysPicker.setOnWeekdaysChangeListener { _: View?, i: Int, list: List<Int?> -> set(DayOfWeek.fromCalendarInt(i), list.contains(i)) }
weekdaysPicker.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
weekdaysPicker.sundayFirstDay = Calendar.getInstance().firstDayOfWeek == Calendar.SUNDAY
weekdaysPicker.redrawDays()
root.addView(weekdaysPicker)
}
}

View file

@ -1,99 +1,38 @@
package info.nightscout.androidaps.plugins.general.automation.triggers
import android.app.TimePickerDialog
import android.graphics.Typeface
import android.text.format.DateFormat
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import androidx.annotation.StringRes
import com.dpro.widgets.WeekdaysPicker
import com.google.common.base.Optional
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Profile
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.general.automation.elements.InputTime
import info.nightscout.androidaps.plugins.general.automation.elements.InputWeekDay
import info.nightscout.androidaps.plugins.general.automation.elements.LayoutBuilder
import info.nightscout.androidaps.plugins.general.automation.elements.StaticLabel
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.JsonHelper
import info.nightscout.androidaps.utils.MidnightTime
import info.nightscout.androidaps.utils.T
import org.json.JSONObject
import java.util.*
class TriggerRecurringTime(mainApp: MainApp) : Trigger(mainApp) {
enum class DayOfWeek {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
fun toCalendarInt(): Int {
return calendarInts[ordinal]
}
@get:StringRes val shortName: Int
get() = shortNames[ordinal]
companion object {
private val calendarInts = intArrayOf(
Calendar.MONDAY,
Calendar.TUESDAY,
Calendar.WEDNESDAY,
Calendar.THURSDAY,
Calendar.FRIDAY,
Calendar.SATURDAY,
Calendar.SUNDAY
)
private val shortNames = intArrayOf(
R.string.weekday_monday_short,
R.string.weekday_tuesday_short,
R.string.weekday_wednesday_short,
R.string.weekday_thursday_short,
R.string.weekday_friday_short,
R.string.weekday_saturday_short,
R.string.weekday_sunday_short
)
fun fromCalendarInt(day: Int): DayOfWeek {
for (i in calendarInts.indices) {
if (calendarInts[i] == day) return values()[i]
}
throw IllegalStateException("Invalid day")
}
}
}
private val weekdays = BooleanArray(DayOfWeek.values().size)
private var hour = 0
private var minute = 0
private var validTo: Long = 0
private val days = InputWeekDay(mainApp)
private val time = InputTime(mainApp)
constructor(mainApp: MainApp, triggerRecurringTime: TriggerRecurringTime) : this(mainApp) {
this.hour = triggerRecurringTime.hour
this.minute = triggerRecurringTime.minute
this.validTo = triggerRecurringTime.validTo
if (weekdays.size >= 0)
System.arraycopy(triggerRecurringTime.weekdays, 0, weekdays, 0, triggerRecurringTime.weekdays.size)
this.time.value = triggerRecurringTime.time.value
if (days.weekdays.size >= 0)
System.arraycopy(triggerRecurringTime.days.weekdays, 0, days.weekdays, 0, triggerRecurringTime.days.weekdays.size)
}
init {
for (day in DayOfWeek.values()) set(day, false)
}
operator fun set(day: DayOfWeek, value: Boolean): TriggerRecurringTime {
weekdays[day.ordinal] = value
return this
}
private fun isSet(day: DayOfWeek): Boolean = weekdays[day.ordinal]
override fun shouldRun(): Boolean {
if (validTo != 0L && DateUtil.now() > validTo) return false
val c = Calendar.getInstance()
val scheduledDayOfWeek = c[Calendar.DAY_OF_WEEK]
val scheduledCal: Calendar = DateUtil.gregorianCalendar()
scheduledCal[Calendar.HOUR_OF_DAY] = hour
scheduledCal[Calendar.MINUTE] = minute
scheduledCal[Calendar.SECOND] = 0
val scheduled = scheduledCal.timeInMillis
if (isSet(Objects.requireNonNull(DayOfWeek.fromCalendarInt(scheduledDayOfWeek)))) {
if (DateUtil.now() >= scheduled && DateUtil.now() - scheduled < T.mins(5).msecs()) {
val currentMinSinceMidnight = getMinSinceMidnight(DateUtil.now())
val scheduledDayOfWeek = Calendar.getInstance()[Calendar.DAY_OF_WEEK]
val scheduled = getMinSinceMidnight(time.value.toLong())
if (days.isSet(Objects.requireNonNull(InputWeekDay.DayOfWeek.fromCalendarInt(scheduledDayOfWeek)))) {
if (currentMinSinceMidnight >= scheduled && currentMinSinceMidnight - scheduled < 5) {
aapsLogger.debug(LTag.AUTOMATION, "Ready for execution: " + friendlyDescription())
return true
}
@ -104,11 +43,9 @@ class TriggerRecurringTime(mainApp: MainApp) : Trigger(mainApp) {
override fun toJSON(): String {
val data = JSONObject()
.put("hour", hour)
.put("minute", minute)
.put("validTo", validTo)
for (i in weekdays.indices) {
data.put(DayOfWeek.values()[i].name, weekdays[i])
.put("time", time.value)
for (i in days.weekdays.indices) {
data.put(InputWeekDay.DayOfWeek.values()[i].name, days.weekdays[i])
}
return JSONObject()
.put("type", TriggerRecurringTime::class.java.name)
@ -118,11 +55,16 @@ class TriggerRecurringTime(mainApp: MainApp) : Trigger(mainApp) {
override fun fromJSON(data: String): Trigger {
val o = JSONObject(data)
for (i in weekdays.indices)
weekdays[i] = JsonHelper.safeGetBoolean(o, DayOfWeek.values()[i].name)
hour = JsonHelper.safeGetInt(o, "hour")
minute = JsonHelper.safeGetInt(o, "minute")
validTo = JsonHelper.safeGetLong(o, "validTo")
for (i in days.weekdays.indices)
days.weekdays[i] = JsonHelper.safeGetBoolean(o, InputWeekDay.DayOfWeek.values()[i].name)
if (o.has("hour")) {
// do conversion from 2.5.1 format
val hour = JsonHelper.safeGetInt(o, "hour")
val minute = JsonHelper.safeGetInt(o, "minute")
time.value = 60 * hour + minute
} else {
time.value = JsonHelper.safeGetInt(o, "time")
}
return this
}
@ -133,17 +75,12 @@ class TriggerRecurringTime(mainApp: MainApp) : Trigger(mainApp) {
sb.append(resourceHelper.gs(R.string.every))
sb.append(" ")
var counter = 0
for (i in getSelectedDays()) {
for (i in days.getSelectedDays()) {
if (counter++ > 0) sb.append(",")
sb.append(resourceHelper.gs(Objects.requireNonNull(DayOfWeek.fromCalendarInt(i)).shortName))
sb.append(resourceHelper.gs(Objects.requireNonNull(InputWeekDay.DayOfWeek.fromCalendarInt(i)).shortName))
}
sb.append(" ")
val scheduledCal: Calendar = DateUtil.gregorianCalendar()
scheduledCal[Calendar.HOUR_OF_DAY] = hour
scheduledCal[Calendar.MINUTE] = minute
scheduledCal[Calendar.SECOND] = 0
val scheduled = scheduledCal.timeInMillis
sb.append(DateUtil.timeString(scheduled))
sb.append(DateUtil.timeString(toMills(time.value)))
return if (counter == 0) resourceHelper.gs(R.string.never) else sb.toString()
}
@ -151,61 +88,15 @@ class TriggerRecurringTime(mainApp: MainApp) : Trigger(mainApp) {
override fun duplicate(): Trigger = TriggerRecurringTime(mainApp, this)
private fun getSelectedDays(): List<Int> {
val selectedDays: MutableList<Int> = ArrayList()
for (i in weekdays.indices) {
val day = DayOfWeek.values()[i]
val selected = weekdays[i]
if (selected) selectedDays.add(day.toCalendarInt())
}
return selectedDays
}
private fun toMills(minutesSinceMidnight: Int): Long = MidnightTime.calc() + T.mins(minutesSinceMidnight.toLong()).msecs()
private fun getMinSinceMidnight(time: Long): Int = Profile.secondsFromMidnight(time) / 60
override fun generateDialog(root: LinearLayout) {
val label = TextView(root.context)
// TODO: Replace external tool WeekdaysPicker with a self-made GUI element
val weekdaysPicker = WeekdaysPicker(root.context)
weekdaysPicker.setEditable(true)
weekdaysPicker.selectedDays = getSelectedDays()
weekdaysPicker.setOnWeekdaysChangeListener { _: View?, i: Int, list: List<Int?> -> set(DayOfWeek.fromCalendarInt(i), list.contains(i)) }
weekdaysPicker.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
weekdaysPicker.sundayFirstDay = Calendar.getInstance().firstDayOfWeek == Calendar.SUNDAY
weekdaysPicker.redrawDays()
root.addView(weekdaysPicker)
val timeButton = TextView(root.context)
val runAt = GregorianCalendar()
runAt[Calendar.HOUR_OF_DAY] = hour
runAt[Calendar.MINUTE] = minute
timeButton.text = DateUtil.timeString(runAt.timeInMillis)
val timeSetListener = TimePickerDialog.OnTimeSetListener { _, h, m ->
val cal = Calendar.getInstance()
hour = h
minute = m
cal.set(Calendar.HOUR_OF_DAY, hour)
cal.set(Calendar.MINUTE, minute)
cal.set(Calendar.SECOND, 0)
timeButton.text = DateUtil.timeString(cal.timeInMillis)
}
timeButton.setOnClickListener {
root.context?.let {
TimePickerDialog(it, timeSetListener, hour, minute, DateFormat.is24HourFormat(mainApp))
.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)
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(timeButton)
root.addView(l)
LayoutBuilder()
.add(StaticLabel(mainApp, R.string.recurringTime))
.add(days)
.add(time)
.build(root)
}
}