Merge branch 'dev' of https://github.com/nightscout/AndroidAPS into dev
This commit is contained in:
commit
a00dea8c0d
|
@ -1,5 +1,6 @@
|
|||
package info.nightscout.androidaps.activities
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.res.ColorStateList
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
|
@ -168,7 +169,8 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() {
|
|||
|
||||
binding.basalpctfromtdd.setParams(32.0, 32.0, 37.0, 1.0, DecimalFormat("0"), false, null)
|
||||
|
||||
binding.tdds.text = getString(R.string.tdd) + ": " + rh.gs(R.string.calculation_in_progress);
|
||||
@SuppressLint("SetTextI18n")
|
||||
binding.tdds.text = getString(R.string.tdd) + ": " + rh.gs(R.string.calculation_in_progress)
|
||||
Thread {
|
||||
val tdds = tddCalculator.stats()
|
||||
runOnUiThread { binding.tdds.text = tdds }
|
||||
|
|
|
@ -76,7 +76,7 @@ class CalibrationDialog : DialogFragmentWithDate() {
|
|||
val units = profileFunction.getUnits()
|
||||
val unitLabel = if (units == GlucoseUnit.MMOL) rh.gs(R.string.mmol) else rh.gs(R.string.mgdl)
|
||||
val actions: LinkedList<String?> = LinkedList()
|
||||
val bg = binding.bg.value ?: return false
|
||||
val bg = binding.bg.value
|
||||
actions.add(rh.gs(R.string.treatments_wizard_bg_label) + ": " + Profile.toCurrentUnitsString(profileFunction, bg) + " " + unitLabel)
|
||||
if (bg > 0) {
|
||||
activity?.let { activity ->
|
||||
|
|
|
@ -189,7 +189,7 @@ class CarbsDialog : DialogFragmentWithDate() {
|
|||
|
||||
override fun submit(): Boolean {
|
||||
if (_binding == null) return false
|
||||
val carbs = binding.carbs.value?.toInt() ?: return false
|
||||
val carbs = binding.carbs.value.toInt()
|
||||
val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value()
|
||||
val units = profileFunction.getUnits()
|
||||
val activityTTDuration = defaultValueHelper.determineActivityTTDuration()
|
||||
|
|
|
@ -79,7 +79,7 @@ class ExtendedBolusDialog : DialogFragmentWithDate() {
|
|||
|
||||
override fun submit(): Boolean {
|
||||
if (_binding == null) return false
|
||||
val insulin = SafeParse.stringToDouble(binding.insulin.text ?: return false)
|
||||
val insulin = SafeParse.stringToDouble(binding.insulin.text)
|
||||
val durationInMinutes = binding.duration.value.toInt()
|
||||
val actions: LinkedList<String> = LinkedList()
|
||||
val insulinAfterConstraint = constraintChecker.applyExtendedBolusConstraints(Constraint(insulin)).value()
|
||||
|
|
|
@ -106,7 +106,7 @@ class FillDialog : DialogFragmentWithDate() {
|
|||
|
||||
override fun submit(): Boolean {
|
||||
if (_binding == null) return false
|
||||
val insulin = SafeParse.stringToDouble(binding.fillInsulinamount.text ?: return false)
|
||||
val insulin = SafeParse.stringToDouble(binding.fillInsulinamount.text)
|
||||
val actions: LinkedList<String?> = LinkedList()
|
||||
|
||||
val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(insulin)).value()
|
||||
|
|
|
@ -149,7 +149,7 @@ class InsulinDialog : DialogFragmentWithDate() {
|
|||
override fun submit(): Boolean {
|
||||
if (_binding == null) return false
|
||||
val pumpDescription = activePlugin.activePump.pumpDescription
|
||||
val insulin = SafeParse.stringToDouble(binding.amount.text ?: return false)
|
||||
val insulin = SafeParse.stringToDouble(binding.amount.text)
|
||||
val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(insulin)).value()
|
||||
val actions: LinkedList<String?> = LinkedList()
|
||||
val units = profileFunction.getUnits()
|
||||
|
|
|
@ -162,7 +162,7 @@ class ProfileSwitchDialog : DialogFragmentWithDate() {
|
|||
?: return false
|
||||
|
||||
val actions: LinkedList<String> = LinkedList()
|
||||
val duration = binding.duration.value?.toInt() ?: return false
|
||||
val duration = binding.duration.value.toInt()
|
||||
if (duration > 0L)
|
||||
actions.add(rh.gs(R.string.duration) + ": " + rh.gs(R.string.format_mins, duration))
|
||||
val profileName = binding.profile.selectedItem.toString()
|
||||
|
|
|
@ -47,8 +47,8 @@ class TempBasalDialog : DialogFragmentWithDate() {
|
|||
override fun onSaveInstanceState(savedInstanceState: Bundle) {
|
||||
super.onSaveInstanceState(savedInstanceState)
|
||||
savedInstanceState.putDouble("duration", binding.duration.value)
|
||||
savedInstanceState.putDouble("basalpercentinput", binding.basalpercentinput.value)
|
||||
savedInstanceState.putDouble("basalabsoluteinput", binding.basalabsoluteinput.value)
|
||||
savedInstanceState.putDouble("basalPercentInput", binding.basalPercentInput.value)
|
||||
savedInstanceState.putDouble("basalAbsoluteInput", binding.basalAbsoluteInput.value)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||
|
@ -67,10 +67,10 @@ class TempBasalDialog : DialogFragmentWithDate() {
|
|||
val maxTempPercent = pumpDescription.maxTempPercent.toDouble()
|
||||
val tempPercentStep = pumpDescription.tempPercentStep.toDouble()
|
||||
|
||||
binding.basalpercentinput.setParams(savedInstanceState?.getDouble("basalpercentinput")
|
||||
binding.basalPercentInput.setParams(savedInstanceState?.getDouble("basalPercentInput")
|
||||
?: 100.0, 0.0, maxTempPercent, tempPercentStep, DecimalFormat("0"), true, binding.okcancel.ok)
|
||||
|
||||
binding.basalabsoluteinput.setParams(savedInstanceState?.getDouble("basalabsoluteinput")
|
||||
binding.basalAbsoluteInput.setParams(savedInstanceState?.getDouble("basalAbsoluteInput")
|
||||
?: profile.getBasal(), 0.0, pumpDescription.maxTempAbsolute, pumpDescription.tempAbsoluteStep, DecimalFormat("0.00"), true, binding.okcancel.ok)
|
||||
|
||||
val tempDurationStep = pumpDescription.tempDurationStep.toDouble()
|
||||
|
@ -97,17 +97,17 @@ class TempBasalDialog : DialogFragmentWithDate() {
|
|||
if (_binding == null) return false
|
||||
var percent = 0
|
||||
var absolute = 0.0
|
||||
val durationInMinutes = binding.duration.value?.toInt() ?: return false
|
||||
val durationInMinutes = binding.duration.value.toInt()
|
||||
val profile = profileFunction.getProfile() ?: return false
|
||||
val actions: LinkedList<String> = LinkedList()
|
||||
if (isPercentPump) {
|
||||
val basalPercentInput = SafeParse.stringToInt(binding.basalpercentinput.text)
|
||||
val basalPercentInput = SafeParse.stringToInt(binding.basalPercentInput.text)
|
||||
percent = constraintChecker.applyBasalPercentConstraints(Constraint(basalPercentInput), profile).value()
|
||||
actions.add(rh.gs(R.string.tempbasal_label) + ": $percent%")
|
||||
actions.add(rh.gs(R.string.duration) + ": " + rh.gs(R.string.format_mins, durationInMinutes))
|
||||
if (percent != basalPercentInput) actions.add(rh.gs(R.string.constraintapllied))
|
||||
} else {
|
||||
val basalAbsoluteInput = SafeParse.stringToDouble(binding.basalabsoluteinput.text)
|
||||
val basalAbsoluteInput = SafeParse.stringToDouble(binding.basalAbsoluteInput.text)
|
||||
absolute = constraintChecker.applyBasalConstraints(Constraint(basalAbsoluteInput), profile).value()
|
||||
actions.add(rh.gs(R.string.tempbasal_label) + ": " + rh.gs(R.string.pump_basebasalrate, absolute))
|
||||
actions.add(rh.gs(R.string.duration) + ": " + rh.gs(R.string.format_mins, durationInMinutes))
|
||||
|
|
|
@ -115,7 +115,7 @@ class TreatmentDialog : DialogFragmentWithDate() {
|
|||
override fun submit(): Boolean {
|
||||
if (_binding == null) return false
|
||||
val pumpDescription = activePlugin.activePump.pumpDescription
|
||||
val insulin = SafeParse.stringToDouble(binding.insulin.text ?: return false)
|
||||
val insulin = SafeParse.stringToDouble(binding.insulin.text)
|
||||
val carbs = SafeParse.stringToInt(binding.carbs.text)
|
||||
val recordOnlyChecked = binding.recordOnly.isChecked
|
||||
val actions: LinkedList<String?> = LinkedList()
|
||||
|
@ -139,7 +139,7 @@ class TreatmentDialog : DialogFragmentWithDate() {
|
|||
OKDialog.showConfirmation(activity, rh.gs(R.string.overview_treatment_label), HtmlHelper.fromHtml(Joiner.on("<br/>").join(actions)), {
|
||||
val action = when {
|
||||
insulinAfterConstraints.equals(0.0) -> Action.CARBS
|
||||
carbsAfterConstraints.equals(0) -> Action.BOLUS
|
||||
carbsAfterConstraints == 0 -> Action.BOLUS
|
||||
else -> Action.TREATMENT
|
||||
}
|
||||
val detailedBolusInfo = DetailedBolusInfo()
|
||||
|
|
|
@ -258,10 +258,7 @@ class WizardDialog : DaggerDialogFragment() {
|
|||
|
||||
val units = profileFunction.getUnits()
|
||||
binding.bgunits.text = units.asText
|
||||
if (units == GlucoseUnit.MGDL)
|
||||
binding.bgInput.setStep(1.0)
|
||||
else
|
||||
binding.bgInput.setStep(0.1)
|
||||
binding.bgInput.step = if (units == GlucoseUnit.MGDL) 1.0 else 0.1
|
||||
|
||||
// Set BG if not old
|
||||
binding.bgInput.value = iobCobCalculator.ads.actualBg()?.valueToUnits(units) ?: 0.0
|
||||
|
|
|
@ -109,7 +109,7 @@ class ObjectivesExamDialog : DaggerDialogFragment() {
|
|||
task.answered = result
|
||||
if (!result) {
|
||||
task.disabledTo = dateUtil.now() + T.hours(1).msecs()
|
||||
ToastUtils.showToastInUiThread(context, R.string.wronganswer)
|
||||
context?.let { it1 -> ToastUtils.showToastInUiThread(it1, R.string.wronganswer) }
|
||||
} else task.disabledTo = 0
|
||||
updateGui()
|
||||
rxBus.send(EventObjectivesUpdateGui())
|
||||
|
|
|
@ -646,9 +646,9 @@ class OverviewData @Inject constructor(
|
|||
maxCobValueFound = max(maxCobValueFound, cob.toDouble())
|
||||
lastCob = cob
|
||||
}
|
||||
if (autosensData.failoverToMinAbsorbtionRate) {
|
||||
autosensData.setScale(cobScale)
|
||||
autosensData.setChartTime(time)
|
||||
if (autosensData.failOverToMinAbsorptionRate) {
|
||||
autosensData.scale = cobScale
|
||||
autosensData.chartTime = time
|
||||
minFailOverActiveList.add(autosensData)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,18 +20,21 @@ class BolusDataPoint @Inject constructor(
|
|||
|
||||
override fun getX(): Double = data.timestamp.toDouble()
|
||||
override fun getY(): Double = if (data.type == Bolus.Type.SMB) defaultValueHelper.determineLowLine() else yValue
|
||||
override fun getLabel(): String = DecimalFormatter.toPumpSupportedBolus(data.amount, activePlugin.activePump, rh)
|
||||
override fun getDuration(): Long = 0
|
||||
override fun getSize(): Float = 2f
|
||||
override val label
|
||||
get() = DecimalFormatter.toPumpSupportedBolus(data.amount, activePlugin.activePump, rh)
|
||||
override val duration = 0L
|
||||
override val size = 2f
|
||||
|
||||
override fun getShape(): PointsWithLabelGraphSeries.Shape =
|
||||
if (data.type == Bolus.Type.SMB) PointsWithLabelGraphSeries.Shape.SMB
|
||||
else PointsWithLabelGraphSeries.Shape.BOLUS
|
||||
override val shape
|
||||
get() = if (data.type == Bolus.Type.SMB) PointsWithLabelGraphSeries.Shape.SMB else PointsWithLabelGraphSeries.Shape.BOLUS
|
||||
|
||||
override fun getColor(): Int =
|
||||
if (data.type == Bolus.Type.SMB) rh.gc(R.color.tempbasal)
|
||||
else if (data.isValid) Color.CYAN
|
||||
else rh.gc(android.R.color.holo_red_light)
|
||||
override val color
|
||||
get() =
|
||||
when {
|
||||
data.type == Bolus.Type.SMB -> rh.gc(R.color.tempbasal)
|
||||
data.isValid -> Color.CYAN
|
||||
else -> rh.gc(android.R.color.holo_red_light)
|
||||
}
|
||||
|
||||
override fun setY(y: Double) {
|
||||
yValue = y
|
||||
|
|
|
@ -14,15 +14,11 @@ class CarbsDataPoint @Inject constructor(
|
|||
|
||||
override fun getX(): Double = data.timestamp.toDouble()
|
||||
override fun getY(): Double = yValue
|
||||
override fun getLabel(): String = rh.gs(R.string.format_carbs, data.amount.toInt())
|
||||
override fun getDuration(): Long = 0
|
||||
override fun getSize(): Float = 2f
|
||||
|
||||
override fun getShape(): PointsWithLabelGraphSeries.Shape = PointsWithLabelGraphSeries.Shape.CARBS
|
||||
|
||||
override fun getColor(): Int =
|
||||
if (data.isValid) rh.gc(R.color.carbs)
|
||||
else rh.gc(android.R.color.holo_red_light)
|
||||
override val label get() = rh.gs(R.string.format_carbs, data.amount.toInt())
|
||||
override val duration = 0L
|
||||
override val size = 2f
|
||||
override val shape = PointsWithLabelGraphSeries.Shape.CARBS
|
||||
override val color get() = if (data.isValid) rh.gc(R.color.carbs) else rh.gc(android.R.color.holo_red_light)
|
||||
|
||||
override fun setY(y: Double) {
|
||||
yValue = y
|
||||
|
|
|
@ -17,9 +17,9 @@ class EffectiveProfileSwitchDataPoint @Inject constructor(
|
|||
yValue = y
|
||||
}
|
||||
|
||||
override fun getLabel(): String = data.originalCustomizedName
|
||||
override fun getDuration(): Long = 0
|
||||
override fun getShape(): PointsWithLabelGraphSeries.Shape = PointsWithLabelGraphSeries.Shape.PROFILE
|
||||
override fun getSize(): Float = 10f
|
||||
override fun getColor(): Int = Color.CYAN
|
||||
override val label get() = data.originalCustomizedName
|
||||
override val duration = 0L
|
||||
override val shape = PointsWithLabelGraphSeries.Shape.PROFILE
|
||||
override val size = 10f
|
||||
override val color = Color.CYAN
|
||||
}
|
|
@ -13,11 +13,11 @@ class ExtendedBolusDataPoint @Inject constructor(
|
|||
|
||||
override fun getX(): Double = data.timestamp.toDouble()
|
||||
override fun getY(): Double = yValue
|
||||
override fun getLabel(): String = data.toStringTotal()
|
||||
override fun getDuration(): Long = data.duration
|
||||
override fun getSize(): Float = 10f
|
||||
override fun getShape(): PointsWithLabelGraphSeries.Shape = PointsWithLabelGraphSeries.Shape.EXTENDEDBOLUS
|
||||
override fun getColor(): Int = Color.CYAN
|
||||
override val label get() = data.toStringTotal()
|
||||
override val duration get() = data.duration
|
||||
override val size = 10f
|
||||
override val shape = PointsWithLabelGraphSeries.Shape.EXTENDEDBOLUS
|
||||
override val color = Color.CYAN
|
||||
|
||||
override fun setY(y: Double) {
|
||||
yValue = y
|
||||
|
|
|
@ -19,44 +19,36 @@ class GlucoseValueDataPoint @Inject constructor(
|
|||
fun valueToUnits(units: GlucoseUnit): Double =
|
||||
if (units == GlucoseUnit.MGDL) data.value else data.value * Constants.MGDL_TO_MMOLL
|
||||
|
||||
override fun getX(): Double {
|
||||
return data.timestamp.toDouble()
|
||||
}
|
||||
|
||||
override fun getY(): Double {
|
||||
return valueToUnits(profileFunction.getUnits())
|
||||
}
|
||||
override fun getX(): Double = data.timestamp.toDouble()
|
||||
override fun getY(): Double = valueToUnits(profileFunction.getUnits())
|
||||
|
||||
override fun setY(y: Double) {}
|
||||
override fun getLabel(): String? = null
|
||||
override fun getDuration(): Long = 0
|
||||
override fun getShape(): PointsWithLabelGraphSeries.Shape =
|
||||
if (isPrediction) PointsWithLabelGraphSeries.Shape.PREDICTION
|
||||
else PointsWithLabelGraphSeries.Shape.BG
|
||||
|
||||
override fun getSize(): Float = 1f
|
||||
|
||||
override fun getColor(): Int {
|
||||
val units = profileFunction.getUnits()
|
||||
val lowLine = defaultValueHelper.determineLowLine()
|
||||
val highLine = defaultValueHelper.determineHighLine()
|
||||
return when {
|
||||
isPrediction -> predictionColor
|
||||
valueToUnits(units) < lowLine -> rh.gc(R.color.low)
|
||||
valueToUnits(units) > highLine -> rh.gc(R.color.high)
|
||||
else -> rh.gc(R.color.inrange)
|
||||
override val label: String? = null
|
||||
override val duration = 0L
|
||||
override val shape get() = if (isPrediction) PointsWithLabelGraphSeries.Shape.PREDICTION else PointsWithLabelGraphSeries.Shape.BG
|
||||
override val size = 1f
|
||||
override val color: Int
|
||||
get() {
|
||||
val units = profileFunction.getUnits()
|
||||
val lowLine = defaultValueHelper.determineLowLine()
|
||||
val highLine = defaultValueHelper.determineHighLine()
|
||||
return when {
|
||||
isPrediction -> predictionColor
|
||||
valueToUnits(units) < lowLine -> rh.gc(R.color.low)
|
||||
valueToUnits(units) > highLine -> rh.gc(R.color.high)
|
||||
else -> rh.gc(R.color.inrange)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val predictionColor: Int
|
||||
get() {
|
||||
return when (data.sourceSensor) {
|
||||
GlucoseValue.SourceSensor.IOB_PREDICTION -> rh.gc(R.color.iob)
|
||||
GlucoseValue.SourceSensor.IOB_PREDICTION -> rh.gc(R.color.iob)
|
||||
GlucoseValue.SourceSensor.COB_PREDICTION -> rh.gc(R.color.cob)
|
||||
GlucoseValue.SourceSensor.A_COB_PREDICTION -> -0x7f000001 and rh.gc(R.color.cob)
|
||||
GlucoseValue.SourceSensor.UAM_PREDICTION -> rh.gc(R.color.uam)
|
||||
GlucoseValue.SourceSensor.ZT_PREDICTION -> rh.gc(R.color.zt)
|
||||
else -> R.color.white
|
||||
GlucoseValue.SourceSensor.ZT_PREDICTION -> rh.gc(R.color.zt)
|
||||
else -> R.color.white
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,9 +20,9 @@ class InMemoryGlucoseValueDataPoint @Inject constructor(
|
|||
override fun getX(): Double = data.timestamp.toDouble()
|
||||
override fun getY(): Double = valueToUnits(profileFunction.getUnits())
|
||||
override fun setY(y: Double) {}
|
||||
override fun getLabel(): String? = null
|
||||
override fun getDuration(): Long = 0
|
||||
override fun getShape(): PointsWithLabelGraphSeries.Shape = PointsWithLabelGraphSeries.Shape.BUCKETED_BG
|
||||
override fun getSize(): Float = 0.3f
|
||||
override fun getColor(): Int = rh.gc(R.color.white)
|
||||
override val label: String? = null
|
||||
override val duration = 0L
|
||||
override val shape = PointsWithLabelGraphSeries.Shape.BUCKETED_BG
|
||||
override val size = 0.3f
|
||||
override val color get() = rh.gc(R.color.white)
|
||||
}
|
|
@ -19,9 +19,7 @@ class TherapyEventDataPoint @Inject constructor(
|
|||
|
||||
private var yValue = 0.0
|
||||
|
||||
override fun getX(): Double {
|
||||
return data.timestamp.toDouble()
|
||||
}
|
||||
override fun getX(): Double = data.timestamp.toDouble()
|
||||
|
||||
override fun getY(): Double {
|
||||
val units = profileFunction.getUnits()
|
||||
|
@ -46,30 +44,29 @@ class TherapyEventDataPoint @Inject constructor(
|
|||
yValue = y
|
||||
}
|
||||
|
||||
override fun getLabel(): String? =
|
||||
if (data.note.isNullOrBlank().not()) data.note
|
||||
else translator.translate(data.type)
|
||||
override val label get() = if (data.note.isNullOrBlank().not()) data.note else translator.translate(data.type)
|
||||
override val duration get() = data.duration
|
||||
override val shape
|
||||
get() =
|
||||
when {
|
||||
data.type == TherapyEvent.Type.NS_MBG -> PointsWithLabelGraphSeries.Shape.MBG
|
||||
data.type == TherapyEvent.Type.FINGER_STICK_BG_VALUE -> PointsWithLabelGraphSeries.Shape.BGCHECK
|
||||
data.type == TherapyEvent.Type.ANNOUNCEMENT -> PointsWithLabelGraphSeries.Shape.ANNOUNCEMENT
|
||||
data.type == TherapyEvent.Type.APS_OFFLINE -> PointsWithLabelGraphSeries.Shape.OPENAPSOFFLINE
|
||||
data.type == TherapyEvent.Type.EXERCISE -> PointsWithLabelGraphSeries.Shape.EXERCISE
|
||||
duration > 0 -> PointsWithLabelGraphSeries.Shape.GENERALWITHDURATION
|
||||
else -> PointsWithLabelGraphSeries.Shape.GENERAL
|
||||
}
|
||||
|
||||
override fun getDuration(): Long = data.duration
|
||||
override fun getShape(): PointsWithLabelGraphSeries.Shape =
|
||||
when {
|
||||
data.type == TherapyEvent.Type.NS_MBG -> PointsWithLabelGraphSeries.Shape.MBG
|
||||
data.type == TherapyEvent.Type.FINGER_STICK_BG_VALUE -> PointsWithLabelGraphSeries.Shape.BGCHECK
|
||||
data.type == TherapyEvent.Type.ANNOUNCEMENT -> PointsWithLabelGraphSeries.Shape.ANNOUNCEMENT
|
||||
data.type == TherapyEvent.Type.APS_OFFLINE -> PointsWithLabelGraphSeries.Shape.OPENAPSOFFLINE
|
||||
data.type == TherapyEvent.Type.EXERCISE -> PointsWithLabelGraphSeries.Shape.EXERCISE
|
||||
duration > 0 -> PointsWithLabelGraphSeries.Shape.GENERALWITHDURATION
|
||||
else -> PointsWithLabelGraphSeries.Shape.GENERAL
|
||||
}
|
||||
|
||||
override fun getSize(): Float = if (rh.gb(R.bool.isTablet)) 12.0f else 10.0f
|
||||
override fun getColor(): Int =
|
||||
when (data.type) {
|
||||
TherapyEvent.Type.ANNOUNCEMENT -> rh.gc(R.color.notificationAnnouncement)
|
||||
TherapyEvent.Type.NS_MBG -> Color.RED
|
||||
TherapyEvent.Type.FINGER_STICK_BG_VALUE -> Color.RED
|
||||
TherapyEvent.Type.EXERCISE -> Color.BLUE
|
||||
TherapyEvent.Type.APS_OFFLINE -> Color.GRAY and -0x7f000001
|
||||
else -> Color.GRAY
|
||||
}
|
||||
override val size get() = if (rh.gb(R.bool.isTablet)) 12.0f else 10.0f
|
||||
override val color
|
||||
get() =
|
||||
when (data.type) {
|
||||
TherapyEvent.Type.ANNOUNCEMENT -> rh.gc(R.color.notificationAnnouncement)
|
||||
TherapyEvent.Type.NS_MBG -> Color.RED
|
||||
TherapyEvent.Type.FINGER_STICK_BG_VALUE -> Color.RED
|
||||
TherapyEvent.Type.EXERCISE -> Color.BLUE
|
||||
TherapyEvent.Type.APS_OFFLINE -> Color.GRAY and -0x7f000001
|
||||
else -> Color.GRAY
|
||||
}
|
||||
}
|
|
@ -664,13 +664,15 @@ public class WatchUpdaterService extends WearableListenerService implements Goog
|
|||
IobTotal bolusIob = iobCobCalculator.calculateIobFromBolus().round();
|
||||
IobTotal basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round();
|
||||
|
||||
iobSum = DecimalFormatter.INSTANCE.to2Decimal(bolusIob.iob + basalIob.basaliob);
|
||||
iobDetail = "(" + DecimalFormatter.INSTANCE.to2Decimal(bolusIob.iob) + "|" + DecimalFormatter.INSTANCE.to2Decimal(basalIob.basaliob) + ")";
|
||||
iobSum = DecimalFormatter.INSTANCE.to2Decimal(bolusIob.getIob() + basalIob.getBasaliob());
|
||||
iobDetail =
|
||||
"(" + DecimalFormatter.INSTANCE.to2Decimal(bolusIob.getIob()) + "|" + DecimalFormatter.INSTANCE.to2Decimal(basalIob.getBasaliob()) + ")";
|
||||
cobString = iobCobCalculator.getCobInfo(false, "WatcherUpdaterService").generateCOBString();
|
||||
currentBasal = generateBasalString();
|
||||
|
||||
//bgi
|
||||
double bgi = -(bolusIob.activity + basalIob.activity) * 5 * Profile.Companion.fromMgdlToUnits(profile.getIsfMgdl(), profileFunction.getUnits());
|
||||
double bgi =
|
||||
-(bolusIob.getActivity() + basalIob.getActivity()) * 5 * Profile.Companion.fromMgdlToUnits(profile.getIsfMgdl(), profileFunction.getUnits());
|
||||
bgiString = "" + ((bgi >= 0) ? "+" : "") + DecimalFormatter.INSTANCE.to1Decimal(bgi);
|
||||
|
||||
status = generateStatusString(profile, currentBasal, iobSum, iobDetail, bgiString);
|
||||
|
|
|
@ -209,12 +209,12 @@ class IobCobOref1Thread internal constructor(
|
|||
// figure out how many carbs that represents
|
||||
// but always assume at least 3mg/dL/5m (default) absorption per active treatment
|
||||
val ci = max(deviation, totalMinCarbsImpact)
|
||||
if (ci != deviation) autosensData.failoverToMinAbsorbtionRate = true
|
||||
if (ci != deviation) autosensData.failOverToMinAbsorptionRate = true
|
||||
autosensData.absorbed = ci * profile.getIc(bgTime) / sens
|
||||
// and add that to the running total carbsAbsorbed
|
||||
autosensData.cob = max(previous.cob - autosensData.absorbed, 0.0)
|
||||
autosensData.mealCarbs = previous.mealCarbs
|
||||
autosensData.substractAbosorbedCarbs()
|
||||
autosensData.deductAbsorbedCarbs()
|
||||
autosensData.usedMinCarbsImpact = totalMinCarbsImpact
|
||||
autosensData.absorbing = previous.absorbing
|
||||
autosensData.mealStartCounter = previous.mealStartCounter
|
||||
|
|
|
@ -10,7 +10,6 @@ import info.nightscout.androidaps.database.AppRepository
|
|||
import info.nightscout.androidaps.events.Event
|
||||
import info.nightscout.androidaps.events.EventAutosensCalculationFinished
|
||||
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||
import info.nightscout.androidaps.interfaces.PluginType
|
||||
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
|
@ -218,11 +217,11 @@ class IobCobThread @Inject internal constructor(
|
|||
// figure out how many carbs that represents
|
||||
// but always assume at least 3mg/dL/5m (default) absorption per active treatment
|
||||
val ci = max(deviation, totalMinCarbsImpact)
|
||||
if (ci != deviation) autosensData.failoverToMinAbsorbtionRate = true
|
||||
if (ci != deviation) autosensData.failOverToMinAbsorptionRate = true
|
||||
autosensData.absorbed = ci * profile.getIc(bgTime) / sens
|
||||
// and add that to the running total carbsAbsorbed
|
||||
autosensData.cob = max(previous.cob - autosensData.absorbed, 0.0)
|
||||
autosensData.substractAbosorbedCarbs()
|
||||
autosensData.deductAbsorbedCarbs()
|
||||
autosensData.usedMinCarbsImpact = totalMinCarbsImpact
|
||||
}
|
||||
val isAAPSOrWeighted = sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled()
|
||||
|
|
|
@ -65,7 +65,7 @@ class LocalProfileFragment : DaggerFragment() {
|
|||
override fun afterTextChanged(s: Editable) {}
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||
localProfilePlugin.currentProfile()?.dia = SafeParse.stringToDouble(binding.dia.text.toString())
|
||||
localProfilePlugin.currentProfile()?.dia = SafeParse.stringToDouble(binding.dia.text)
|
||||
localProfilePlugin.currentProfile()?.name = binding.name.text.toString()
|
||||
doEdit()
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import info.nightscout.androidaps.Constants
|
|||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.events.EventPumpStatusChanged
|
||||
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||
import info.nightscout.androidaps.interfaces.CommandQueue
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.bus.RxBus
|
||||
|
@ -18,7 +19,7 @@ import info.nightscout.androidaps.utils.resources.ResourceHelper
|
|||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
|
||||
class QueueThread internal constructor(
|
||||
private val queue: CommandQueueImplementation,
|
||||
private val queue: CommandQueue,
|
||||
context: Context,
|
||||
private val aapsLogger: AAPSLogger,
|
||||
private val rxBus: RxBus,
|
||||
|
|
|
@ -32,7 +32,7 @@ class CommandBolus(
|
|||
}
|
||||
|
||||
override fun status(): String {
|
||||
return (if (detailedBolusInfo.insulin > 0) "BOLUS " + rh.gs(R.string.formatinsulinunits, detailedBolusInfo.insulin) else "") +
|
||||
if (detailedBolusInfo.carbs > 0) "CARBS " + rh.gs(R.string.format_carbs, detailedBolusInfo.carbs.toInt()) else ""
|
||||
return (if (detailedBolusInfo.insulin > 0) rh.gs(R.string.bolus_u_min, detailedBolusInfo.insulin) else "") +
|
||||
if (detailedBolusInfo.carbs > 0) rh.gs(R.string.carbs_g, detailedBolusInfo.carbs.toInt()) else ""
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package info.nightscout.androidaps.queue.commands
|
||||
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.queue.Callback
|
||||
|
@ -19,5 +20,5 @@ class CommandCancelExtendedBolus constructor(
|
|||
callback?.result(r)?.run()
|
||||
}
|
||||
|
||||
override fun status(): String = "CANCEL EXTENDEDBOLUS"
|
||||
override fun status(): String = rh.gs(R.string.uel_cancel_extended_bolus)
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package info.nightscout.androidaps.queue.commands
|
||||
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.queue.Callback
|
||||
|
@ -20,5 +21,5 @@ class CommandCancelTempBasal(
|
|||
callback?.result(r)?.run()
|
||||
}
|
||||
|
||||
override fun status(): String = "CANCEL TEMPBASAL"
|
||||
override fun status(): String = rh.gs(R.string.uel_accepts_temp_basal)
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package info.nightscout.androidaps.queue.commands
|
||||
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.queue.Callback
|
||||
|
@ -21,5 +22,5 @@ class CommandExtendedBolus constructor(
|
|||
callback?.result(r)?.run()
|
||||
}
|
||||
|
||||
override fun status(): String = "EXTENDEDBOLUS $insulin U $durationInMinutes min"
|
||||
override fun status(): String = rh.gs(R.string.extended_bolus_u_min, insulin, durationInMinutes)
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package info.nightscout.androidaps.queue.commands
|
||||
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
|
||||
import info.nightscout.androidaps.queue.Callback
|
||||
|
@ -23,5 +24,5 @@ class CommandInsightSetTBROverNotification constructor(
|
|||
}
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
override fun status(): String = "INSIGHTSETTBROVERNOTIFICATION"
|
||||
override fun status(): String = rh.gs(R.string.insight_set_tbr_over_notification)
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package info.nightscout.androidaps.queue.commands
|
||||
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||
import info.nightscout.androidaps.interfaces.Dana
|
||||
import info.nightscout.androidaps.interfaces.Diaconn
|
||||
|
@ -32,5 +33,5 @@ class CommandLoadEvents(
|
|||
}
|
||||
}
|
||||
|
||||
override fun status(): String = "LOAD EVENTS"
|
||||
override fun status(): String = rh.gs(R.string.load_events)
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package info.nightscout.androidaps.queue.commands
|
||||
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||
import info.nightscout.androidaps.interfaces.Dana
|
||||
import info.nightscout.androidaps.interfaces.Diaconn
|
||||
|
@ -33,5 +34,5 @@ class CommandLoadHistory(
|
|||
}
|
||||
}
|
||||
|
||||
override fun status(): String = "LOAD HISTORY $type"
|
||||
override fun status(): String = rh.gs(R.string.load_history, type.toInt())
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package info.nightscout.androidaps.queue.commands
|
||||
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.queue.Callback
|
||||
|
@ -20,5 +21,5 @@ class CommandLoadTDDs(
|
|||
callback?.result(r)?.run()
|
||||
}
|
||||
|
||||
override fun status(): String = "LOAD TDDs"
|
||||
override fun status(): String = rh.gs(R.string.load_tdds)
|
||||
}
|
|
@ -37,5 +37,5 @@ class CommandSMBBolus(
|
|||
callback?.result(r)?.run()
|
||||
}
|
||||
|
||||
override fun status(): String = "SMB BOLUS ${rh.gs(R.string.formatinsulinunits, detailedBolusInfo.insulin)}"
|
||||
override fun status(): String = rh.gs(R.string.smb_bolus_u, detailedBolusInfo.insulin)
|
||||
}
|
|
@ -45,5 +45,5 @@ class CommandSetProfile constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun status(): String = "SET PROFILE"
|
||||
override fun status(): String = rh.gs(R.string.set_profile)
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package info.nightscout.androidaps.queue.commands
|
||||
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||
import info.nightscout.androidaps.interfaces.Dana
|
||||
import info.nightscout.androidaps.interfaces.Diaconn
|
||||
|
@ -30,5 +31,5 @@ class CommandSetUserSettings(
|
|||
}
|
||||
}
|
||||
|
||||
override fun status(): String = "SET USER SETTINGS"
|
||||
override fun status(): String = rh.gs(R.string.set_user_settings)
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package info.nightscout.androidaps.queue.commands
|
||||
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
|
||||
import info.nightscout.androidaps.queue.Callback
|
||||
|
@ -21,5 +22,5 @@ class CommandStartPump(
|
|||
}
|
||||
}
|
||||
|
||||
override fun status(): String = "START PUMP"
|
||||
override fun status(): String = rh.gs(R.string.start_pump)
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package info.nightscout.androidaps.queue.commands
|
||||
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
|
||||
import info.nightscout.androidaps.queue.Callback
|
||||
|
@ -21,5 +22,5 @@ class CommandStopPump(
|
|||
}
|
||||
}
|
||||
|
||||
override fun status(): String = "STOP PUMP"
|
||||
override fun status(): String = rh.gs(R.string.stop_pump)
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package info.nightscout.androidaps.queue.commands
|
||||
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.interfaces.Profile
|
||||
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||
import info.nightscout.androidaps.interfaces.PumpSync
|
||||
|
@ -26,5 +27,5 @@ class CommandTempBasalAbsolute(
|
|||
callback?.result(r)?.run()
|
||||
}
|
||||
|
||||
override fun status(): String = "TEMP BASAL $absoluteRate U/h $durationInMinutes min"
|
||||
override fun status(): String = rh.gs(R.string.temp_basal_absolute, absoluteRate, durationInMinutes)
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package info.nightscout.androidaps.queue.commands
|
||||
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.interfaces.Profile
|
||||
import info.nightscout.androidaps.interfaces.ActivePlugin
|
||||
import info.nightscout.androidaps.interfaces.PumpSync
|
||||
|
@ -30,5 +31,5 @@ class CommandTempBasalPercent(
|
|||
callback?.result(r)?.run()
|
||||
}
|
||||
|
||||
override fun status(): String = "TEMP BASAL $percent% $durationInMinutes min"
|
||||
override fun status(): String = rh.gs(R.string.temp_basal_percent, percent, durationInMinutes)
|
||||
}
|
|
@ -5,10 +5,7 @@ import android.util.AttributeSet
|
|||
import android.view.LayoutInflater
|
||||
import info.nightscout.androidaps.R
|
||||
|
||||
class NumberPickerVertical : NumberPicker {
|
||||
|
||||
constructor(context: Context?) : super(context)
|
||||
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
|
||||
class NumberPickerVertical(context: Context, attrs: AttributeSet? = null) : NumberPicker(context, attrs) {
|
||||
|
||||
override fun inflate(context: Context) {
|
||||
LayoutInflater.from(context).inflate(R.layout.number_picker_layout_vertical, this, true)
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
android:textStyle="bold" />
|
||||
|
||||
<info.nightscout.androidaps.utils.ui.NumberPicker
|
||||
android:id="@+id/basalpercentinput"
|
||||
android:id="@+id/basal_percent_input"
|
||||
android:layout_width="130dp"
|
||||
android:layout_height="40dp" />
|
||||
|
||||
|
@ -98,7 +98,7 @@
|
|||
android:textStyle="bold" />
|
||||
|
||||
<info.nightscout.androidaps.utils.ui.NumberPicker
|
||||
android:id="@+id/basalabsoluteinput"
|
||||
android:id="@+id/basal_absolute_input"
|
||||
android:layout_width="130dp"
|
||||
android:layout_height="40dp" />
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.content.Context
|
|||
import android.os.PowerManager
|
||||
import dagger.android.AndroidInjector
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.TestBaseWithProfile
|
||||
import info.nightscout.androidaps.TestPumpPlugin
|
||||
import info.nightscout.androidaps.database.AppRepository
|
||||
|
@ -21,6 +22,7 @@ import info.nightscout.androidaps.utils.sharedPreferences.SP
|
|||
import org.junit.Assert
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.ArgumentMatchers
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito
|
||||
|
||||
|
@ -41,6 +43,7 @@ class QueueThreadTest : TestBaseWithProfile() {
|
|||
}
|
||||
if (it is CommandTempBasalAbsolute) {
|
||||
it.activePlugin = activePlugin
|
||||
it.rh = rh
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +78,7 @@ class QueueThreadTest : TestBaseWithProfile() {
|
|||
val percentageConstraint = Constraint(0)
|
||||
Mockito.`when`(constraintChecker.applyBasalPercentConstraints(anyObject(), anyObject()))
|
||||
.thenReturn(percentageConstraint)
|
||||
Mockito.`when`(rh.gs(ArgumentMatchers.eq(R.string.temp_basal_absolute), anyObject(), anyObject())).thenReturn("TEMP BASAL %1\$.2f U/h %2\$d min")
|
||||
|
||||
sut = QueueThread(commandQueue, context, aapsLogger, rxBus, activePlugin, rh, sp)
|
||||
}
|
||||
|
|
|
@ -128,7 +128,7 @@ class EditEventDialog : DialogFragmentWithDate() {
|
|||
// check for title
|
||||
val title = binding.inputEventTitle.text?.toString() ?: return false
|
||||
if (title.isEmpty()) {
|
||||
ToastUtils.showToastInUiThread(context, R.string.automation_missing_task_name)
|
||||
context?.let { ToastUtils.showToastInUiThread(it, R.string.automation_missing_task_name) }
|
||||
return false
|
||||
}
|
||||
event.title = title
|
||||
|
@ -137,12 +137,12 @@ class EditEventDialog : DialogFragmentWithDate() {
|
|||
// check for at least one trigger
|
||||
val con = event.trigger
|
||||
if (con.size() == 0 && !event.userAction) {
|
||||
ToastUtils.showToastInUiThread(context, R.string.automation_missing_trigger)
|
||||
context?.let { ToastUtils.showToastInUiThread(it, R.string.automation_missing_trigger) }
|
||||
return false
|
||||
}
|
||||
// check for at least one action
|
||||
if (event.actions.isEmpty()) {
|
||||
ToastUtils.showToastInUiThread(context, R.string.automation_missing_action)
|
||||
context?.let { ToastUtils.showToastInUiThread(it, R.string.automation_missing_action) }
|
||||
return false
|
||||
}
|
||||
// store
|
||||
|
|
|
@ -9,7 +9,7 @@ buildscript {
|
|||
rxkotlin_version = '2.4.0'
|
||||
room_version = '2.3.0'
|
||||
lifecycle_version = '2.3.1'
|
||||
dagger_version = '2.40.1'
|
||||
dagger_version = '2.40.2'
|
||||
coroutinesVersion = '1.4.1'
|
||||
activityVersion = '1.3.1'
|
||||
fragmentktx_version = '1.3.6'
|
||||
|
|
|
@ -10,18 +10,18 @@ import org.json.JSONObject
|
|||
@Suppress("SpellCheckingInspection")
|
||||
class IobTotal(val time: Long) : DataPointWithLabelInterface {
|
||||
|
||||
@JvmField var iob = 0.0
|
||||
@JvmField var activity = 0.0
|
||||
@JvmField var bolussnooze = 0.0
|
||||
@JvmField var basaliob = 0.0
|
||||
@JvmField var netbasalinsulin = 0.0
|
||||
@JvmField var hightempinsulin = 0.0
|
||||
var iob = 0.0
|
||||
var activity = 0.0
|
||||
var bolussnooze = 0.0
|
||||
var basaliob = 0.0
|
||||
var netbasalinsulin = 0.0
|
||||
var hightempinsulin = 0.0
|
||||
|
||||
// oref1
|
||||
@JvmField var lastBolusTime: Long = 0
|
||||
var lastBolusTime: Long = 0
|
||||
var iobWithZeroTemp: IobTotal? = null
|
||||
@JvmField var netInsulin = 0.0 // for calculations from temp basals only
|
||||
@JvmField var extendedBolusInsulin = 0.0 // total insulin for extended bolus
|
||||
var netInsulin = 0.0 // for calculations from temp basals only
|
||||
var extendedBolusInsulin = 0.0 // total insulin for extended bolus
|
||||
fun copy(): IobTotal {
|
||||
val i = IobTotal(time)
|
||||
i.iob = iob
|
||||
|
@ -104,36 +104,14 @@ class IobTotal(val time: Long) : DataPointWithLabelInterface {
|
|||
}
|
||||
|
||||
// DataPoint interface
|
||||
private var color = 0
|
||||
override fun getX(): Double {
|
||||
return time.toDouble()
|
||||
}
|
||||
|
||||
override fun getY(): Double {
|
||||
return iob
|
||||
}
|
||||
|
||||
override var color = 0
|
||||
override fun getX(): Double = time.toDouble()
|
||||
override fun getY(): Double = iob
|
||||
override fun setY(y: Double) {}
|
||||
|
||||
override fun getLabel(): String {
|
||||
return ""
|
||||
}
|
||||
|
||||
override fun getDuration(): Long {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun getShape(): PointsWithLabelGraphSeries.Shape {
|
||||
return PointsWithLabelGraphSeries.Shape.IOBPREDICTION
|
||||
}
|
||||
|
||||
override fun getSize(): Float {
|
||||
return 0.5f
|
||||
}
|
||||
|
||||
override fun getColor(): Int {
|
||||
return color
|
||||
}
|
||||
override val label = ""
|
||||
override val duration = 0L
|
||||
override val shape = PointsWithLabelGraphSeries.Shape.IOBPREDICTION
|
||||
override val size = 0.5f
|
||||
|
||||
fun setColor(color: Int): IobTotal {
|
||||
this.color = color
|
||||
|
|
|
@ -164,7 +164,7 @@ class ProfileViewerDialog : DaggerDialogFragment() {
|
|||
binding.date.text = date
|
||||
binding.ic.text = it.getIcList(rh, dateUtil)
|
||||
binding.isf.text = it.getIsfList(rh, dateUtil)
|
||||
binding.basal.text = it.getBasalList(rh, dateUtil)
|
||||
binding.basal.text = "∑ " + rh.gs(R.string.formatinsulinunits, it.baseBasalSum()) + "\n" + it.getBasalList(rh, dateUtil)
|
||||
binding.target.text = it.getTargetList(rh, dateUtil)
|
||||
binding.basalGraph.show(it)
|
||||
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.aps.openAPSSMB;
|
||||
|
||||
/**
|
||||
* Created by mike on 10.12.2017.
|
||||
*/
|
||||
|
||||
public class SMBDefaults {
|
||||
// CALCULATED OR FROM PREFS
|
||||
|
||||
// max_iob: 0 // if max_iob is not provided, will default to zero
|
||||
// max_daily_safety_multiplier:3
|
||||
// current_basal_safety_multiplier:4
|
||||
// autosens_max:1.2
|
||||
// autosens_min:0.7
|
||||
|
||||
// USED IN AUTOSENS
|
||||
public final static boolean rewind_resets_autosens = true; // reset autosensitivity to neutral for awhile after each pump rewind
|
||||
|
||||
// USED IN TARGETS
|
||||
// by default the higher end of the target range is used only for avoiding bolus wizard overcorrections
|
||||
// use wide_bg_target_range: true to force neutral temps over a wider range of eventualBGs
|
||||
public final static boolean wide_bg_target_range = false; // by default use only the low end of the pump's BG target range as OpenAPS target
|
||||
|
||||
// USED IN AUTOTUNE
|
||||
public final static double autotune_isf_adjustmentFraction = 1.0; // keep autotune ISF closer to pump ISF via a weighted average of fullNewISF and pumpISF. 1.0 allows full adjustment, 0 is no adjustment from pump ISF.
|
||||
public final static double remainingCarbsFraction = 1.0; // fraction of carbs we'll assume will absorb over 4h if we don't yet see carb absorption
|
||||
|
||||
// USED IN DETERMINE_BASAL
|
||||
public final static boolean low_temptarget_lowers_sensitivity = false; // lower sensitivity for temptargets <= 99.
|
||||
public final static boolean high_temptarget_raises_sensitivity = false; // raise sensitivity for temptargets >= 111. synonym for exercise_mode
|
||||
public final static boolean sensitivity_raises_target = true; // raise BG target when autosens detects sensitivity
|
||||
public final static boolean resistance_lowers_target = false; // lower BG target when autosens detects resistance
|
||||
public final static boolean adv_target_adjustments = false; // lower target automatically when BG and eventualBG are high
|
||||
public final static boolean exercise_mode = false; // when true, > 105 mg/dL high temp target adjusts sensitivityRatio for exercise_mode. This majorly changes the behavior of high temp targets from before. synonmym for high_temptarget_raises_sensitivity
|
||||
public final static int half_basal_exercise_target = 160; // when temptarget is 160 mg/dL *and* exercise_mode=true, run 50% basal at this level (120 = 75%; 140 = 60%)
|
||||
// create maxCOB and default it to 120 because that's the most a typical body can absorb over 4 hours.
|
||||
// (If someone enters more carbs or stacks more; OpenAPS will just truncate dosing based on 120.
|
||||
// Essentially, this just limits AMA/SMB as a safety cap against excessive COB entry)
|
||||
public final static int maxCOB = 120;
|
||||
//public final static boolean skip_neutral_temps = true; // ***** default false in oref1 ***** if true, don't set neutral temps
|
||||
// unsuspend_if_no_temp:false // if true, pump will un-suspend after a zero temp finishes
|
||||
// bolussnooze_dia_divisor:2 // bolus snooze decays after 1/2 of DIA
|
||||
public final static double min_5m_carbimpact = 8d; // mg/dL per 5m (8 mg/dL/5m corresponds to 24g/hr at a CSF of 4 mg/dL/g (x/5*60/4))
|
||||
public final static int remainingCarbsCap = 90; // max carbs we'll assume will absorb over 4h if we don't yet see carb absorption
|
||||
// WARNING: use SMB with caution: it can and will automatically bolus up to max_iob worth of extra insulin
|
||||
// enableUAM:true // enable detection of unannounced meal carb absorption
|
||||
public final static boolean A52_risk_enable = false;
|
||||
//public final static boolean enableSMB_with_COB = true; // ***** default false in oref1 ***** enable supermicrobolus while COB is positive
|
||||
//public final static boolean enableSMB_with_temptarget = true; // ***** default false in oref1 ***** enable supermicrobolus for eating soon temp targets
|
||||
// *** WARNING *** DO NOT USE enableSMB_always or enableSMB_after_carbs with xDrip+, Libre, or similar
|
||||
// xDrip+, LimiTTer, etc. do not properly filter out high-noise SGVs
|
||||
// Using SMB overnight with such data sources risks causing a dangerous overdose of insulin
|
||||
// if the CGM sensor reads falsely high and doesn't come down as actual BG does
|
||||
// public final static boolean enableSMB_always = false; // always enable supermicrobolus (unless disabled by high temptarget)
|
||||
// *** WARNING *** DO NOT USE enableSMB_always or enableSMB_after_carbs with xDrip+, Libre, or similar
|
||||
//public final static boolean enableSMB_after_carbs = false; // enable supermicrobolus for 6h after carbs, even with 0 COB
|
||||
//public final static boolean allowSMB_with_high_temptarget = false; // allow supermicrobolus (if otherwise enabled) even with high temp targets
|
||||
public final static int SMBInterval = 3; // minimum interval between SMBs, in minutes. (limited between 1 and 10 min)
|
||||
public final static int maxSMBBasalMinutes = 30; // maximum minutes of basal that can be delivered as a single SMB with uncovered COB
|
||||
public final static int maxUAMSMBBasalMinutes = 30; // maximum minutes of basal that can be delivered as a single SMB when IOB exceeds COB
|
||||
// curve:"rapid-acting" // Supported curves: "bilinear", "rapid-acting" (Novolog, Novorapid, Humalog, Apidra) and "ultra-rapid" (Fiasp)
|
||||
// useCustomPeakTime:false // allows changing insulinPeakTime
|
||||
// insulinPeakTime:75 // number of minutes after a bolus activity peaks. defaults to 55m for Fiasp if useCustomPeakTime: false
|
||||
public final static int carbsReqThreshold = 1; // grams of carbsReq to trigger a pushover
|
||||
// offline_hotspot:false // enabled an offline-only local wifi hotspot if no Internet available
|
||||
public final static double bolus_increment = 0.1; // minimum bolus that can be delivered as an SMB
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package info.nightscout.androidaps.plugins.aps.openAPSSMB
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
object SMBDefaults {
|
||||
|
||||
// CALCULATED OR FROM PREFS
|
||||
// max_iob: 0 // if max_iob is not provided, will default to zero
|
||||
// max_daily_safety_multiplier:3
|
||||
// current_basal_safety_multiplier:4
|
||||
// autosens_max:1.2
|
||||
// autosens_min:0.7
|
||||
// USED IN AUTOSENS
|
||||
// const val rewind_resets_autosens = true // reset autosensitivity to neutral for awhile after each pump rewind
|
||||
|
||||
// USED IN TARGETS
|
||||
// by default the higher end of the target range is used only for avoiding bolus wizard overcorrections
|
||||
// use wide_bg_target_range: true to force neutral temps over a wider range of eventualBGs
|
||||
// const val wide_bg_target_range = false // by default use only the low end of the pump's BG target range as OpenAPS target
|
||||
|
||||
// USED IN AUTOTUNE
|
||||
// const val autotune_isf_adjustmentFraction = 1.0 // keep autotune ISF closer to pump ISF via a weighted average of fullNewISF and pumpISF. 1.0 allows full adjustment, 0 is no adjustment from pump ISF.
|
||||
// const val remainingCarbsFraction = 1.0 // fraction of carbs we'll assume will absorb over 4h if we don't yet see carb absorption
|
||||
|
||||
// USED IN DETERMINE_BASAL
|
||||
// const val low_temptarget_lowers_sensitivity = false // lower sensitivity for temptargets <= 99.
|
||||
const val high_temptarget_raises_sensitivity = false // raise sensitivity for temptargets >= 111. synonym for exercise_mode
|
||||
const val sensitivity_raises_target = true // raise BG target when autosens detects sensitivity
|
||||
const val resistance_lowers_target = false // lower BG target when autosens detects resistance
|
||||
const val adv_target_adjustments = false // lower target automatically when BG and eventualBG are high
|
||||
const val exercise_mode = false // when true, > 105 mg/dL high temp target adjusts sensitivityRatio for exercise_mode. This majorly changes the behavior of high temp targets from before. synonym for high_temptarget_raises_sensitivity
|
||||
const val half_basal_exercise_target = 160 // when temptarget is 160 mg/dL *and* exercise_mode=true, run 50% basal at this level (120 = 75%; 140 = 60%)
|
||||
|
||||
// create maxCOB and default it to 120 because that's the most a typical body can absorb over 4 hours.
|
||||
// (If someone enters more carbs or stacks more; OpenAPS will just truncate dosing based on 120.
|
||||
// Essentially, this just limits AMA/SMB as a safety cap against excessive COB entry)
|
||||
const val maxCOB = 120
|
||||
|
||||
//public final static boolean skip_neutral_temps = true; // ***** default false in oref1 ***** if true, don't set neutral temps
|
||||
// un-suspend_if_no_temp:false // if true, pump will un-suspend after a zero temp finishes
|
||||
// bolussnooze_dia_divisor:2 // bolus snooze decays after 1/2 of DIA
|
||||
const val min_5m_carbimpact = 8.0 // mg/dL per 5m (8 mg/dL/5m corresponds to 24g/hr at a CSF of 4 mg/dL/g (x/5*60/4))
|
||||
const val remainingCarbsCap = 90 // max carbs we'll assume will absorb over 4h if we don't yet see carb absorption
|
||||
|
||||
// WARNING: use SMB with caution: it can and will automatically bolus up to max_iob worth of extra insulin
|
||||
// enableUAM:true // enable detection of unannounced meal carb absorption
|
||||
const val A52_risk_enable = false
|
||||
|
||||
//public final static boolean enableSMB_with_COB = true; // ***** default false in oref1 ***** enable supermicrobolus while COB is positive
|
||||
//public final static boolean enableSMB_with_temptarget = true; // ***** default false in oref1 ***** enable supermicrobolus for eating soon temp targets
|
||||
// *** WARNING *** DO NOT USE enableSMB_always or enableSMB_after_carbs with xDrip+, Libre, or similar
|
||||
// xDrip+, LimiTTer, etc. do not properly filter out high-noise SGVs
|
||||
// Using SMB overnight with such data sources risks causing a dangerous overdose of insulin
|
||||
// if the CGM sensor reads falsely high and doesn't come down as actual BG does
|
||||
// public final static boolean enableSMB_always = false; // always enable supermicrobolus (unless disabled by high temptarget)
|
||||
// *** WARNING *** DO NOT USE enableSMB_always or enableSMB_after_carbs with xDrip+, Libre, or similar
|
||||
//public final static boolean enableSMB_after_carbs = false; // enable supermicrobolus for 6h after carbs, even with 0 COB
|
||||
//public final static boolean allowSMB_with_high_temptarget = false; // allow supermicrobolus (if otherwise enabled) even with high temp targets
|
||||
const val SMBInterval = 3 // minimum interval between SMBs, in minutes. (limited between 1 and 10 min)
|
||||
const val maxSMBBasalMinutes = 30 // maximum minutes of basal that can be delivered as a single SMB with uncovered COB
|
||||
const val maxUAMSMBBasalMinutes = 30 // maximum minutes of basal that can be delivered as a single SMB when IOB exceeds COB
|
||||
|
||||
// curve:"rapid-acting" // Supported curves: "bilinear", "rapid-acting" (Novolog, Novorapid, Humalog, Apidra) and "ultra-rapid" (Fiasp)
|
||||
// useCustomPeakTime:false // allows changing insulinPeakTime
|
||||
// insulinPeakTime:75 // number of minutes after a bolus activity peaks. defaults to 55m for Fiasp if useCustomPeakTime: false
|
||||
const val carbsReqThreshold = 1 // grams of carbsReq to trigger a pushover
|
||||
|
||||
// offline_hotspot:false // enabled an offline-only local wifi hotspot if no Internet available
|
||||
// const val bolus_increment = 0.1 // minimum bolus that can be delivered as an SMB
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.general.overview.graphExtensions;
|
||||
/**
|
||||
* GraphView
|
||||
* Copyright (C) 2014 Jonas Gehring
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License,
|
||||
* with the "Linking Exception", which can be found at the license.txt
|
||||
* file in this program.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* with the "Linking Exception" along with this program; if not,
|
||||
* write to the author Jonas Gehring <g.jjoe64@gmail.com>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Added by mike
|
||||
*/
|
||||
|
||||
import com.jjoe64.graphview.series.DataPointInterface;
|
||||
|
||||
/**
|
||||
* interface of data points. Implement this in order
|
||||
* to use your class in {@link com.jjoe64.graphview.series.Series}.
|
||||
*
|
||||
* You can also use the default implementation {@link com.jjoe64.graphview.series.DataPoint} so
|
||||
* you do not have to implement it for yourself.
|
||||
*
|
||||
* @author jjoe64
|
||||
*/
|
||||
public interface DataPointWithLabelInterface extends DataPointInterface{
|
||||
/**
|
||||
* @return the x value
|
||||
*/
|
||||
double getX();
|
||||
|
||||
/**
|
||||
* @return the y value
|
||||
*/
|
||||
double getY();
|
||||
void setY(double y);
|
||||
|
||||
/**
|
||||
* @return the label value
|
||||
*/
|
||||
String getLabel();
|
||||
|
||||
long getDuration();
|
||||
PointsWithLabelGraphSeries.Shape getShape();
|
||||
float getSize();
|
||||
int getColor();
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package info.nightscout.androidaps.plugins.general.overview.graphExtensions
|
||||
|
||||
import com.jjoe64.graphview.series.DataPointInterface
|
||||
|
||||
interface DataPointWithLabelInterface : DataPointInterface {
|
||||
|
||||
override fun getX(): Double
|
||||
override fun getY(): Double
|
||||
fun setY(y: Double)
|
||||
|
||||
val label: String?
|
||||
val duration: Long
|
||||
val shape: PointsWithLabelGraphSeries.Shape?
|
||||
val size: Float
|
||||
val color: Int
|
||||
}
|
|
@ -347,7 +347,7 @@ class AutosensDataStore {
|
|||
synchronized(dataLock) {
|
||||
var i = autosensDataTable.size() - 1
|
||||
while (i >= 0 && count < valuesToProcess) {
|
||||
if (autosensDataTable.valueAt(i).failoverToMinAbsorbtionRate) sum++
|
||||
if (autosensDataTable.valueAt(i).failOverToMinAbsorptionRate) sum++
|
||||
count++
|
||||
i--
|
||||
}
|
||||
|
|
|
@ -1,203 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.iob.iobCobCalculator.data;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.android.HasAndroidInjector;
|
||||
import info.nightscout.androidaps.Constants;
|
||||
import info.nightscout.androidaps.core.R;
|
||||
import info.nightscout.androidaps.interfaces.Profile;
|
||||
import info.nightscout.androidaps.database.entities.Carbs;
|
||||
import info.nightscout.androidaps.interfaces.ProfileFunction;
|
||||
import info.nightscout.androidaps.logging.AAPSLogger;
|
||||
import info.nightscout.androidaps.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults;
|
||||
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DataPointWithLabelInterface;
|
||||
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.PointsWithLabelGraphSeries;
|
||||
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.Scale;
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult;
|
||||
import info.nightscout.androidaps.utils.DateUtil;
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper;
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP;
|
||||
|
||||
public class AutosensData implements DataPointWithLabelInterface {
|
||||
|
||||
@Inject AAPSLogger aapsLogger;
|
||||
@Inject SP sp;
|
||||
@Inject ResourceHelper rh;
|
||||
@Inject ProfileFunction profileFunction;
|
||||
@Inject DateUtil dateUtil;
|
||||
|
||||
public AutosensData(HasAndroidInjector injector) {
|
||||
injector.androidInjector().inject(this);
|
||||
}
|
||||
|
||||
public void setChartTime(long chartTime) {
|
||||
this.chartTime = chartTime;
|
||||
}
|
||||
|
||||
public class CarbsInPast {
|
||||
long time;
|
||||
double carbs;
|
||||
public double min5minCarbImpact;
|
||||
double remaining;
|
||||
|
||||
public CarbsInPast(Carbs t, boolean isAAPSOrWeighted) {
|
||||
time = t.getTimestamp();
|
||||
carbs = t.getAmount();
|
||||
remaining = t.getAmount();
|
||||
Profile profile = profileFunction.getProfile(t.getTimestamp());
|
||||
if (isAAPSOrWeighted && profile != null) {
|
||||
double maxAbsorptionHours = sp.getDouble(R.string.key_absorption_maxtime, Constants.DEFAULT_MAX_ABSORPTION_TIME);
|
||||
double sens = profile.getIsfMgdl(t.getTimestamp());
|
||||
double ic = profile.getIc(t.getTimestamp());
|
||||
min5minCarbImpact = t.getAmount() / (maxAbsorptionHours * 60 / 5) * sens / ic;
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Min 5m carbs impact for " + carbs + "g @" + dateUtil.dateAndTimeString(t.getTimestamp()) + " for " + maxAbsorptionHours + "h calculated to " + min5minCarbImpact + " ISF: " + sens + " IC: " + ic);
|
||||
} else {
|
||||
min5minCarbImpact = sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact);
|
||||
}
|
||||
}
|
||||
|
||||
CarbsInPast(CarbsInPast other) {
|
||||
this.time = other.time;
|
||||
this.carbs = other.carbs;
|
||||
this.min5minCarbImpact = other.min5minCarbImpact;
|
||||
this.remaining = other.remaining;
|
||||
}
|
||||
|
||||
@NonNull @Override
|
||||
public String toString() {
|
||||
return String.format(Locale.ENGLISH, "CarbsInPast: time: %s carbs: %.02f min5minCI: %.02f remaining: %.2f", dateUtil.dateAndTimeString(time), carbs, min5minCarbImpact, remaining);
|
||||
}
|
||||
}
|
||||
|
||||
public long time = 0L;
|
||||
public double bg = 0; // mgdl
|
||||
private long chartTime;
|
||||
public String pastSensitivity = "";
|
||||
public double deviation = 0d;
|
||||
public boolean validDeviation = false;
|
||||
public List<CarbsInPast> activeCarbsList = new ArrayList<>();
|
||||
public double absorbed = 0d;
|
||||
public double carbsFromBolus = 0d;
|
||||
public double cob = 0;
|
||||
public double bgi = 0d;
|
||||
public double delta = 0d;
|
||||
public double avgDelta = 0d;
|
||||
public double avgDeviation = 0d;
|
||||
|
||||
public AutosensResult autosensResult = new AutosensResult();
|
||||
public double slopeFromMaxDeviation = 0;
|
||||
public double slopeFromMinDeviation = 999;
|
||||
public double usedMinCarbsImpact = 0d;
|
||||
public boolean failoverToMinAbsorbtionRate = false;
|
||||
|
||||
// Oref1
|
||||
public boolean absorbing = false;
|
||||
public double mealCarbs = 0;
|
||||
public int mealStartCounter = 999;
|
||||
public String type = "";
|
||||
public boolean uam = false;
|
||||
public List<Double> extraDeviation = new ArrayList<>();
|
||||
|
||||
@NonNull @Override
|
||||
public String toString() {
|
||||
return String.format(Locale.ENGLISH, "AutosensData: %s pastSensitivity=%s delta=%.02f avgDelta=%.02f bgi=%.02f deviation=%.02f avgDeviation=%.02f absorbed=%.02f carbsFromBolus=%.02f cob=%.02f autosensRatio=%.02f slopeFromMaxDeviation=%.02f slopeFromMinDeviation=%.02f activeCarbsList=%s",
|
||||
dateUtil.dateAndTimeString(time), pastSensitivity, delta, avgDelta, bgi, deviation, avgDeviation, absorbed, carbsFromBolus, cob, autosensResult.getRatio(), slopeFromMaxDeviation, slopeFromMinDeviation, activeCarbsList.toString());
|
||||
}
|
||||
|
||||
public List<CarbsInPast> cloneCarbsList() {
|
||||
List<CarbsInPast> newActiveCarbsList = new ArrayList<>();
|
||||
|
||||
for (CarbsInPast c : activeCarbsList) {
|
||||
newActiveCarbsList.add(new CarbsInPast(c));
|
||||
}
|
||||
|
||||
return newActiveCarbsList;
|
||||
}
|
||||
|
||||
// remove carbs older than timeframe
|
||||
public void removeOldCarbs(long toTime, boolean isAAPSOrWeighted) {
|
||||
double maxAbsorptionHours;
|
||||
if (isAAPSOrWeighted) {
|
||||
maxAbsorptionHours = sp.getDouble(R.string.key_absorption_maxtime, Constants.DEFAULT_MAX_ABSORPTION_TIME);
|
||||
} else {
|
||||
maxAbsorptionHours = sp.getDouble(R.string.key_absorption_cutoff, Constants.DEFAULT_MAX_ABSORPTION_TIME);
|
||||
}
|
||||
for (int i = 0; i < activeCarbsList.size(); i++) {
|
||||
CarbsInPast c = activeCarbsList.get(i);
|
||||
if (c.time + maxAbsorptionHours * 60 * 60 * 1000L < toTime) {
|
||||
activeCarbsList.remove(i--);
|
||||
if (c.remaining > 0)
|
||||
cob -= c.remaining;
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Removing carbs at " + dateUtil.dateAndTimeString(toTime) + " after " + maxAbsorptionHours + "h > " + c.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void substractAbosorbedCarbs() {
|
||||
double ac = absorbed;
|
||||
for (int i = 0; i < activeCarbsList.size() && ac > 0; i++) {
|
||||
CarbsInPast c = activeCarbsList.get(i);
|
||||
if (c.remaining > 0) {
|
||||
double sub = Math.min(ac, c.remaining);
|
||||
c.remaining -= sub;
|
||||
ac -= sub;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------- DataPointWithLabelInterface ------
|
||||
|
||||
private Scale scale;
|
||||
|
||||
public void setScale(Scale scale) {
|
||||
this.scale = scale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getX() {
|
||||
return chartTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getY() {
|
||||
return scale.transform(cob);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setY(double y) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDuration() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PointsWithLabelGraphSeries.Shape getShape() {
|
||||
return PointsWithLabelGraphSeries.Shape.COBFAILOVER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getSize() {
|
||||
return 0.5f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColor() {
|
||||
return rh.gc(R.color.cob);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
package info.nightscout.androidaps.plugins.iob.iobCobCalculator.data
|
||||
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.Constants
|
||||
import info.nightscout.androidaps.core.R
|
||||
import info.nightscout.androidaps.database.entities.Carbs
|
||||
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults
|
||||
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.DataPointWithLabelInterface
|
||||
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.PointsWithLabelGraphSeries
|
||||
import info.nightscout.androidaps.plugins.general.overview.graphExtensions.Scale
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult
|
||||
import info.nightscout.androidaps.utils.DateUtil
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.min
|
||||
|
||||
class AutosensData(injector: HasAndroidInjector) : DataPointWithLabelInterface {
|
||||
|
||||
@Inject lateinit var aapsLogger: AAPSLogger
|
||||
@Inject lateinit var sp: SP
|
||||
@Inject lateinit var rh: ResourceHelper
|
||||
@Inject lateinit var profileFunction: ProfileFunction
|
||||
@Inject lateinit var dateUtil: DateUtil
|
||||
|
||||
inner class CarbsInPast {
|
||||
|
||||
var time: Long
|
||||
var carbs: Double
|
||||
var min5minCarbImpact = 0.0
|
||||
var remaining: Double
|
||||
|
||||
constructor(t: Carbs, isAAPSOrWeighted: Boolean) {
|
||||
time = t.timestamp
|
||||
carbs = t.amount
|
||||
remaining = t.amount
|
||||
val profile = profileFunction.getProfile(t.timestamp)
|
||||
if (isAAPSOrWeighted && profile != null) {
|
||||
val maxAbsorptionHours = sp.getDouble(R.string.key_absorption_maxtime, Constants.DEFAULT_MAX_ABSORPTION_TIME)
|
||||
val sens = profile.getIsfMgdl(t.timestamp)
|
||||
val ic = profile.getIc(t.timestamp)
|
||||
min5minCarbImpact = t.amount / (maxAbsorptionHours * 60 / 5) * sens / ic
|
||||
aapsLogger.debug(
|
||||
LTag.AUTOSENS,
|
||||
"""Min 5m carbs impact for ${carbs}g @${dateUtil.dateAndTimeString(t.timestamp)} for ${maxAbsorptionHours}h calculated to $min5minCarbImpact ISF: $sens IC: $ic"""
|
||||
)
|
||||
} else {
|
||||
min5minCarbImpact = sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact)
|
||||
}
|
||||
}
|
||||
|
||||
internal constructor(other: CarbsInPast) {
|
||||
time = other.time
|
||||
carbs = other.carbs
|
||||
min5minCarbImpact = other.min5minCarbImpact
|
||||
remaining = other.remaining
|
||||
}
|
||||
|
||||
override fun toString(): String =
|
||||
String.format(Locale.ENGLISH, "CarbsInPast: time: %s carbs: %.02f min5minCI: %.02f remaining: %.2f", dateUtil.dateAndTimeString(time), carbs, min5minCarbImpact, remaining)
|
||||
}
|
||||
|
||||
var time = 0L
|
||||
var bg = 0.0 // mgdl
|
||||
var chartTime: Long = 0
|
||||
var pastSensitivity = ""
|
||||
var deviation = 0.0
|
||||
var validDeviation = false
|
||||
var activeCarbsList: MutableList<CarbsInPast> = ArrayList()
|
||||
var absorbed = 0.0
|
||||
var carbsFromBolus = 0.0
|
||||
var cob = 0.0
|
||||
var bgi = 0.0
|
||||
var delta = 0.0
|
||||
var avgDelta = 0.0
|
||||
var avgDeviation = 0.0
|
||||
var autosensResult = AutosensResult()
|
||||
var slopeFromMaxDeviation = 0.0
|
||||
var slopeFromMinDeviation = 999.0
|
||||
var usedMinCarbsImpact = 0.0
|
||||
var failOverToMinAbsorptionRate = false
|
||||
|
||||
// Oref1
|
||||
var absorbing = false
|
||||
var mealCarbs = 0.0
|
||||
var mealStartCounter = 999
|
||||
var type = ""
|
||||
var uam = false
|
||||
var extraDeviation: MutableList<Double> = ArrayList()
|
||||
override fun toString(): String {
|
||||
return String.format(
|
||||
Locale.ENGLISH,
|
||||
"AutosensData: %s pastSensitivity=%s delta=%.02f avgDelta=%.02f bgi=%.02f deviation=%.02f avgDeviation=%.02f absorbed=%.02f carbsFromBolus=%.02f cob=%.02f autosensRatio=%.02f slopeFromMaxDeviation=%.02f slopeFromMinDeviation=%.02f activeCarbsList=%s",
|
||||
dateUtil.dateAndTimeString(time),
|
||||
pastSensitivity,
|
||||
delta,
|
||||
avgDelta,
|
||||
bgi,
|
||||
deviation,
|
||||
avgDeviation,
|
||||
absorbed,
|
||||
carbsFromBolus,
|
||||
cob,
|
||||
autosensResult.ratio,
|
||||
slopeFromMaxDeviation,
|
||||
slopeFromMinDeviation,
|
||||
activeCarbsList.toString()
|
||||
)
|
||||
}
|
||||
|
||||
fun cloneCarbsList(): MutableList<CarbsInPast> {
|
||||
val newActiveCarbsList: MutableList<CarbsInPast> = ArrayList()
|
||||
for (c in activeCarbsList) {
|
||||
newActiveCarbsList.add(CarbsInPast(c))
|
||||
}
|
||||
return newActiveCarbsList
|
||||
}
|
||||
|
||||
// remove carbs older than timeframe
|
||||
fun removeOldCarbs(toTime: Long, isAAPSOrWeighted: Boolean) {
|
||||
val maxAbsorptionHours: Double =
|
||||
if (isAAPSOrWeighted) sp.getDouble(R.string.key_absorption_maxtime, Constants.DEFAULT_MAX_ABSORPTION_TIME)
|
||||
else sp.getDouble(R.string.key_absorption_cutoff, Constants.DEFAULT_MAX_ABSORPTION_TIME)
|
||||
var i = 0
|
||||
while (i < activeCarbsList.size) {
|
||||
val c = activeCarbsList[i]
|
||||
if (c.time + maxAbsorptionHours * 60 * 60 * 1000L < toTime) {
|
||||
activeCarbsList.removeAt(i--)
|
||||
if (c.remaining > 0) cob -= c.remaining
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Removing carbs at " + dateUtil.dateAndTimeString(toTime) + " after " + maxAbsorptionHours + "h > " + c.toString())
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
fun deductAbsorbedCarbs() {
|
||||
var ac = absorbed
|
||||
var i = 0
|
||||
while (i < activeCarbsList.size && ac > 0) {
|
||||
val c = activeCarbsList[i]
|
||||
if (c.remaining > 0) {
|
||||
val sub = min(ac, c.remaining)
|
||||
c.remaining -= sub
|
||||
ac -= sub
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// ------- DataPointWithLabelInterface ------
|
||||
var scale: Scale? = null
|
||||
|
||||
override fun getX(): Double = chartTime.toDouble()
|
||||
override fun getY(): Double = scale!!.transform(cob)
|
||||
|
||||
override fun setY(y: Double) {}
|
||||
override val label: String? = null
|
||||
override val duration = 0L
|
||||
override val shape = PointsWithLabelGraphSeries.Shape.COBFAILOVER
|
||||
override val size = 0.5f
|
||||
override val color get() = rh.gc(R.color.cob)
|
||||
|
||||
init {
|
||||
injector.androidInjector().inject(this)
|
||||
}
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
package info.nightscout.androidaps.utils;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.media.MediaPlayer;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.appcompat.view.ContextThemeWrapper;
|
||||
|
||||
import info.nightscout.androidaps.core.R;
|
||||
import info.nightscout.androidaps.plugins.bus.RxBus;
|
||||
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification;
|
||||
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification;
|
||||
|
||||
|
||||
public class ToastUtils {
|
||||
|
||||
private static Toast lastToast = null;
|
||||
|
||||
public static class Long {
|
||||
|
||||
public static void warnToast(final Context ctx, final String string) {
|
||||
graphicalToast(ctx, string, R.drawable.ic_toast_warn, false);
|
||||
}
|
||||
|
||||
public static void infoToast(final Context ctx, final String string) {
|
||||
graphicalToast(ctx, string, R.drawable.ic_toast_info, false);
|
||||
}
|
||||
|
||||
public static void okToast(final Context ctx, final String string) {
|
||||
graphicalToast(ctx, string, R.drawable.ic_toast_check, false);
|
||||
}
|
||||
|
||||
public static void errorToast(final Context ctx, final String string) {
|
||||
graphicalToast(ctx, string, R.drawable.ic_toast_error, false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void showToastInUiThread(final Context ctx, final int stringId) {
|
||||
showToastInUiThread(ctx, ctx.getString(stringId));
|
||||
}
|
||||
|
||||
public static void warnToast(final Context ctx, final String string) {
|
||||
graphicalToast(ctx, string, R.drawable.ic_toast_warn, true);
|
||||
}
|
||||
|
||||
public static void infoToast(final Context ctx, final String string) {
|
||||
graphicalToast(ctx, string, R.drawable.ic_toast_info, true);
|
||||
}
|
||||
|
||||
public static void okToast(final Context ctx, final String string) {
|
||||
graphicalToast(ctx, string, R.drawable.ic_toast_check, true);
|
||||
}
|
||||
|
||||
public static void errorToast(final Context ctx, final String string) {
|
||||
graphicalToast(ctx, string, R.drawable.ic_toast_error, true);
|
||||
}
|
||||
|
||||
public static void graphicalToast(final Context ctx, final String string, @DrawableRes int iconId) {
|
||||
graphicalToast(ctx, string, iconId, true);
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
public static void graphicalToast(final Context ctx, final String string, @DrawableRes int iconId, boolean isShort) {
|
||||
Handler mainThread = new Handler(Looper.getMainLooper());
|
||||
mainThread.post(() -> {
|
||||
View toastRoot = LayoutInflater.from(new ContextThemeWrapper(ctx, R.style.AppTheme)).inflate(R.layout.toast, null);
|
||||
TextView toastMessage = toastRoot.findViewById(android.R.id.message);
|
||||
toastMessage.setText(string);
|
||||
|
||||
ImageView toastIcon = toastRoot.findViewById(android.R.id.icon);
|
||||
toastIcon.setImageResource(iconId);
|
||||
|
||||
if (lastToast != null) lastToast.cancel();
|
||||
lastToast = new Toast(ctx);
|
||||
lastToast.setDuration(isShort ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG);
|
||||
lastToast.setView(toastRoot);
|
||||
lastToast.show();
|
||||
});
|
||||
}
|
||||
|
||||
public static void showToastInUiThread(final Context ctx, final String string) {
|
||||
Handler mainThread = new Handler(Looper.getMainLooper());
|
||||
mainThread.post(() -> Toast.makeText(ctx, string, Toast.LENGTH_SHORT).show());
|
||||
}
|
||||
|
||||
public static void showToastInUiThread(final Context ctx, final RxBus rxBus,
|
||||
final String string, int soundID) {
|
||||
|
||||
showToastInUiThread(ctx, string);
|
||||
playSound(ctx, soundID);
|
||||
Notification notification = new Notification(Notification.TOAST_ALARM, string, Notification.URGENT);
|
||||
rxBus.send(new EventNewNotification(notification));
|
||||
}
|
||||
|
||||
private static void playSound(final Context ctx, final int soundID) {
|
||||
final MediaPlayer soundMP = MediaPlayer.create(ctx, soundID);
|
||||
soundMP.start();
|
||||
soundMP.setOnCompletionListener(MediaPlayer::release);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
package info.nightscout.androidaps.utils
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.media.MediaPlayer
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.appcompat.view.ContextThemeWrapper
|
||||
import info.nightscout.androidaps.core.R
|
||||
import info.nightscout.androidaps.plugins.bus.RxBus
|
||||
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
|
||||
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
|
||||
|
||||
object ToastUtils {
|
||||
|
||||
private var lastToast: Toast? = null
|
||||
fun showToastInUiThread(ctx: Context, stringId: Int) {
|
||||
showToastInUiThread(ctx, ctx.getString(stringId))
|
||||
}
|
||||
|
||||
fun warnToast(ctx: Context?, string: String?) {
|
||||
graphicalToast(ctx, string, R.drawable.ic_toast_warn, true)
|
||||
}
|
||||
|
||||
fun infoToast(ctx: Context?, string: String?) {
|
||||
graphicalToast(ctx, string, R.drawable.ic_toast_info, true)
|
||||
}
|
||||
|
||||
fun okToast(ctx: Context?, string: String?) {
|
||||
graphicalToast(ctx, string, R.drawable.ic_toast_check, true)
|
||||
}
|
||||
|
||||
fun errorToast(ctx: Context?, string: String?) {
|
||||
graphicalToast(ctx, string, R.drawable.ic_toast_error, true)
|
||||
}
|
||||
|
||||
fun graphicalToast(ctx: Context?, string: String?, @DrawableRes iconId: Int) {
|
||||
graphicalToast(ctx, string, iconId, true)
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
fun graphicalToast(ctx: Context?, string: String?, @DrawableRes iconId: Int, isShort: Boolean) {
|
||||
val mainThread = Handler(Looper.getMainLooper())
|
||||
mainThread.post {
|
||||
val toastRoot = LayoutInflater.from(ContextThemeWrapper(ctx, R.style.AppTheme)).inflate(R.layout.toast, null)
|
||||
val toastMessage = toastRoot.findViewById<TextView>(android.R.id.message)
|
||||
toastMessage.text = string
|
||||
val toastIcon = toastRoot.findViewById<ImageView>(android.R.id.icon)
|
||||
toastIcon.setImageResource(iconId)
|
||||
lastToast?.cancel()
|
||||
lastToast = Toast(ctx)
|
||||
lastToast?.duration = if (isShort) Toast.LENGTH_SHORT else Toast.LENGTH_LONG
|
||||
lastToast?.view = toastRoot
|
||||
lastToast?.show()
|
||||
}
|
||||
}
|
||||
|
||||
fun showToastInUiThread(ctx: Context?, string: String?) {
|
||||
val mainThread = Handler(Looper.getMainLooper())
|
||||
mainThread.post { Toast.makeText(ctx, string, Toast.LENGTH_SHORT).show() }
|
||||
}
|
||||
|
||||
fun showToastInUiThread(
|
||||
ctx: Context?, rxBus: RxBus,
|
||||
string: String?, soundID: Int
|
||||
) {
|
||||
showToastInUiThread(ctx, string)
|
||||
playSound(ctx, soundID)
|
||||
val notification = Notification(Notification.TOAST_ALARM, string!!, Notification.URGENT)
|
||||
rxBus.send(EventNewNotification(notification))
|
||||
}
|
||||
|
||||
private fun playSound(ctx: Context?, soundID: Int) {
|
||||
val soundMP = MediaPlayer.create(ctx, soundID)
|
||||
soundMP.start()
|
||||
soundMP.setOnCompletionListener { obj: MediaPlayer -> obj.release() }
|
||||
}
|
||||
|
||||
object Long {
|
||||
|
||||
fun warnToast(ctx: Context?, string: String) {
|
||||
graphicalToast(ctx, string, R.drawable.ic_toast_warn, false)
|
||||
}
|
||||
|
||||
fun infoToast(ctx: Context?, string: String) {
|
||||
graphicalToast(ctx, string, R.drawable.ic_toast_info, false)
|
||||
}
|
||||
|
||||
fun okToast(ctx: Context?, string: String) {
|
||||
graphicalToast(ctx, string, R.drawable.ic_toast_check, false)
|
||||
}
|
||||
|
||||
fun errorToast(ctx: Context?, string: String) {
|
||||
graphicalToast(ctx, string, R.drawable.ic_toast_error, false)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,16 +13,16 @@ class MinutesNumberPicker constructor(context: Context, attrs: AttributeSet? = n
|
|||
}
|
||||
|
||||
override fun updateEditText() {
|
||||
if (value == 0.0 && !allowZero) editText.setText("")
|
||||
if (currentValue == 0.0 && !allowZero) editText?.setText("")
|
||||
else {
|
||||
if (focused) editText.setText(DecimalFormat("0").format(value))
|
||||
if (focused) editText?.setText(DecimalFormat("0").format(currentValue))
|
||||
else {
|
||||
val hours = (value / 60).toInt()
|
||||
val minutes = (value - hours * 60).toInt()
|
||||
val hours = (currentValue / 60).toInt()
|
||||
val minutes = (currentValue - hours * 60).toInt()
|
||||
val formatted =
|
||||
if (hours != 0) String.format(context.getString(R.string.format_hour_minute), hours, minutes)
|
||||
else DecimalFormat("0").format(value)
|
||||
editText.setText(formatted)
|
||||
else DecimalFormat("0").format(currentValue)
|
||||
editText?.setText(formatted)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,343 +0,0 @@
|
|||
package info.nightscout.androidaps.utils.ui;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import info.nightscout.androidaps.core.R;
|
||||
import info.nightscout.androidaps.utils.SafeParse;
|
||||
import info.nightscout.androidaps.utils.ToastUtils;
|
||||
|
||||
public class NumberPicker extends LinearLayout implements View.OnKeyListener,
|
||||
View.OnTouchListener, View.OnClickListener {
|
||||
|
||||
public interface OnValueChangedListener {
|
||||
void onValueChanged(double value);
|
||||
}
|
||||
|
||||
EditText editText;
|
||||
Button minusButton;
|
||||
Button plusButton;
|
||||
|
||||
double value = 0;
|
||||
double minValue = 0d;
|
||||
double maxValue = 1d;
|
||||
double step = 1d;
|
||||
NumberFormat formatter;
|
||||
boolean allowZero = false;
|
||||
TextWatcher textWatcher = null;
|
||||
|
||||
Button okButton = null;
|
||||
|
||||
protected Boolean focused = false;
|
||||
|
||||
private Handler mHandler;
|
||||
private ScheduledExecutorService mUpdater;
|
||||
private OnValueChangedListener mOnValueChangedListener;
|
||||
|
||||
private class UpdateCounterTask implements Runnable {
|
||||
private final boolean mInc;
|
||||
private int repeated = 0;
|
||||
private int multiplier = 1;
|
||||
|
||||
private final int doubleLimit = 5;
|
||||
|
||||
UpdateCounterTask(boolean inc) {
|
||||
mInc = inc;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
Message msg = new Message();
|
||||
if (repeated % doubleLimit == 0) multiplier *= 2;
|
||||
repeated++;
|
||||
msg.arg1 = multiplier;
|
||||
msg.arg2 = repeated;
|
||||
if (mInc) {
|
||||
msg.what = MSG_INC;
|
||||
} else {
|
||||
msg.what = MSG_DEC;
|
||||
}
|
||||
mHandler.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private static final int MSG_INC = 0;
|
||||
private static final int MSG_DEC = 1;
|
||||
|
||||
public NumberPicker(Context context) {
|
||||
super(context, null);
|
||||
this.initialize(context);
|
||||
}
|
||||
|
||||
public NumberPicker(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
this.initialize(context);
|
||||
}
|
||||
|
||||
protected void inflate(Context context) {
|
||||
LayoutInflater.from(context).inflate(R.layout.number_picker_layout, this, true);
|
||||
}
|
||||
|
||||
protected void initialize(Context context) {
|
||||
// set layout view
|
||||
inflate(context);
|
||||
|
||||
// init ui components
|
||||
minusButton = findViewById(R.id.decrement);
|
||||
minusButton.setId(View.generateViewId());
|
||||
plusButton = findViewById(R.id.increment);
|
||||
plusButton.setId(View.generateViewId());
|
||||
editText = findViewById(R.id.display);
|
||||
editText.setId(View.generateViewId());
|
||||
|
||||
mHandler = new Handler(msg -> {
|
||||
switch (msg.what) {
|
||||
case MSG_INC:
|
||||
inc(msg.arg1);
|
||||
return true;
|
||||
case MSG_DEC:
|
||||
dec(msg.arg1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
minusButton.setOnTouchListener(this);
|
||||
minusButton.setOnKeyListener(this);
|
||||
minusButton.setOnClickListener(this);
|
||||
plusButton.setOnTouchListener(this);
|
||||
plusButton.setOnKeyListener(this);
|
||||
plusButton.setOnClickListener(this);
|
||||
setTextWatcher(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
if (focused) value = SafeParse.stringToDouble(editText.getText().toString());
|
||||
callValueChangedListener();
|
||||
if (okButton != null) {
|
||||
if (value > maxValue || value < minValue)
|
||||
okButton.setVisibility(INVISIBLE);
|
||||
else
|
||||
okButton.setVisibility(VISIBLE);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
editText.setOnFocusChangeListener((v, hasFocus) -> {
|
||||
focused = hasFocus;
|
||||
if (!focused) getValue(); // check min/max
|
||||
updateEditText();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTag(Object tag) {
|
||||
editText.setTag(tag);
|
||||
}
|
||||
|
||||
public void setOnValueChangedListener(OnValueChangedListener onValueChangedListener) {
|
||||
mOnValueChangedListener = onValueChangedListener;
|
||||
}
|
||||
|
||||
public void setTextWatcher(TextWatcher textWatcher) {
|
||||
this.textWatcher = textWatcher;
|
||||
editText.addTextChangedListener(textWatcher);
|
||||
editText.setOnFocusChangeListener((v, hasFocus) -> {
|
||||
if (!hasFocus) {
|
||||
value = SafeParse.stringToDouble(editText.getText().toString());
|
||||
if (value > maxValue) {
|
||||
value = maxValue;
|
||||
ToastUtils.showToastInUiThread(getContext(), getContext().getString(R.string.youareonallowedlimit));
|
||||
updateEditText();
|
||||
if (okButton != null)
|
||||
okButton.setVisibility(VISIBLE);
|
||||
}
|
||||
if (value < minValue) {
|
||||
value = minValue;
|
||||
ToastUtils.showToastInUiThread(getContext(), getContext().getString(R.string.youareonallowedlimit));
|
||||
updateEditText();
|
||||
if (okButton != null)
|
||||
okButton.setVisibility(VISIBLE);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setParams(Double initValue, Double minValue, Double maxValue, Double step, NumberFormat formatter, boolean allowZero, Button okButton, TextWatcher textWatcher) {
|
||||
if (this.textWatcher != null) {
|
||||
editText.removeTextChangedListener(this.textWatcher);
|
||||
}
|
||||
setParams(initValue, minValue, maxValue, step, formatter, allowZero, okButton);
|
||||
this.textWatcher = textWatcher;
|
||||
if (textWatcher != null)
|
||||
editText.addTextChangedListener(textWatcher);
|
||||
}
|
||||
|
||||
public void setParams(Double initValue, Double minValue, Double maxValue, Double step, NumberFormat formatter, boolean allowZero, Button okButton) {
|
||||
this.value = initValue;
|
||||
this.minValue = minValue;
|
||||
this.maxValue = maxValue;
|
||||
this.step = step;
|
||||
this.formatter = formatter;
|
||||
this.allowZero = allowZero;
|
||||
callValueChangedListener();
|
||||
this.okButton = okButton;
|
||||
|
||||
editText.setKeyListener(DigitsKeyListenerWithComma.getInstance(minValue < 0, step != Math.rint(step)));
|
||||
|
||||
if (textWatcher != null)
|
||||
editText.removeTextChangedListener(textWatcher);
|
||||
updateEditText();
|
||||
if (textWatcher != null)
|
||||
editText.addTextChangedListener(textWatcher);
|
||||
}
|
||||
|
||||
public void setValue(Double value) {
|
||||
if (textWatcher != null)
|
||||
editText.removeTextChangedListener(textWatcher);
|
||||
this.value = value;
|
||||
callValueChangedListener();
|
||||
updateEditText();
|
||||
if (textWatcher != null)
|
||||
editText.addTextChangedListener(textWatcher);
|
||||
}
|
||||
|
||||
public Double getValue() {
|
||||
if (value > maxValue) {
|
||||
value = maxValue;
|
||||
ToastUtils.showToastInUiThread(getContext(), getContext().getString(R.string.youareonallowedlimit));
|
||||
}
|
||||
if (value < minValue) {
|
||||
value = minValue;
|
||||
ToastUtils.showToastInUiThread(getContext(), getContext().getString(R.string.youareonallowedlimit));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return editText.getText().toString();
|
||||
}
|
||||
|
||||
public void setStep(Double step) {
|
||||
this.step = step;
|
||||
}
|
||||
|
||||
private void inc(int multiplier) {
|
||||
value += step * multiplier;
|
||||
if (value > maxValue) {
|
||||
value = maxValue;
|
||||
callValueChangedListener();
|
||||
ToastUtils.showToastInUiThread(getContext(), getContext().getString(R.string.youareonallowedlimit));
|
||||
stopUpdating();
|
||||
}
|
||||
updateEditText();
|
||||
}
|
||||
|
||||
private void dec(int multiplier) {
|
||||
value -= step * multiplier;
|
||||
if (value < minValue) {
|
||||
value = minValue;
|
||||
callValueChangedListener();
|
||||
ToastUtils.showToastInUiThread(getContext(), getContext().getString(R.string.youareonallowedlimit));
|
||||
stopUpdating();
|
||||
}
|
||||
updateEditText();
|
||||
}
|
||||
|
||||
protected void updateEditText() {
|
||||
if (value == 0d && !allowZero)
|
||||
editText.setText("");
|
||||
else
|
||||
editText.setText(formatter.format(value));
|
||||
}
|
||||
|
||||
private void callValueChangedListener() {
|
||||
if (mOnValueChangedListener != null)
|
||||
mOnValueChangedListener.onValueChanged(value);
|
||||
}
|
||||
|
||||
private void startUpdating(boolean inc) {
|
||||
if (mUpdater != null) {
|
||||
//log.debug("Another executor is still active");
|
||||
return;
|
||||
}
|
||||
mUpdater = Executors.newSingleThreadScheduledExecutor();
|
||||
mUpdater.scheduleAtFixedRate(new UpdateCounterTask(inc), 200, 200,
|
||||
TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private void stopUpdating() {
|
||||
if (mUpdater != null) {
|
||||
mUpdater.shutdownNow();
|
||||
mUpdater = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mUpdater == null) {
|
||||
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Service.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
|
||||
editText.clearFocus();
|
||||
if (v == plusButton) {
|
||||
inc(1);
|
||||
} else {
|
||||
dec(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||
boolean isKeyOfInterest = keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER;
|
||||
boolean isReleased = event.getAction() == KeyEvent.ACTION_UP;
|
||||
boolean isPressed = event.getAction() == KeyEvent.ACTION_DOWN
|
||||
&& event.getAction() != KeyEvent.ACTION_MULTIPLE;
|
||||
|
||||
if (isKeyOfInterest && isReleased) {
|
||||
stopUpdating();
|
||||
} else if (isKeyOfInterest && isPressed) {
|
||||
startUpdating(v == plusButton);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
boolean isReleased = event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL;
|
||||
boolean isPressed = event.getAction() == MotionEvent.ACTION_DOWN;
|
||||
|
||||
if (isReleased) {
|
||||
stopUpdating();
|
||||
} else if (isPressed) {
|
||||
startUpdating(v == plusButton);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,295 @@
|
|||
package info.nightscout.androidaps.utils.ui
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.os.Message
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.util.AttributeSet
|
||||
import android.view.KeyEvent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.View.OnFocusChangeListener
|
||||
import android.view.View.OnTouchListener
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.Button
|
||||
import android.widget.EditText
|
||||
import android.widget.LinearLayout
|
||||
import info.nightscout.androidaps.core.R
|
||||
import info.nightscout.androidaps.utils.SafeParse
|
||||
import info.nightscout.androidaps.utils.ToastUtils
|
||||
import java.text.NumberFormat
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.ScheduledExecutorService
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.math.round
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
open class NumberPicker(context: Context, attrs: AttributeSet? = null) : LinearLayout(context, attrs), View.OnKeyListener, OnTouchListener, View.OnClickListener {
|
||||
|
||||
fun interface OnValueChangedListener {
|
||||
|
||||
fun onValueChanged(value: Double)
|
||||
}
|
||||
|
||||
var editText: EditText? = null
|
||||
private var minusButton: Button? = null
|
||||
private var plusButton: Button? = null
|
||||
var currentValue = 0.0
|
||||
var minValue = 0.0
|
||||
var maxValue = 1.0
|
||||
var step = 1.0
|
||||
var formatter: NumberFormat? = null
|
||||
var allowZero = false
|
||||
private var watcher: TextWatcher? = null
|
||||
var okButton: Button? = null
|
||||
protected var focused = false
|
||||
private var mUpdater: ScheduledExecutorService? = null
|
||||
private var mOnValueChangedListener: OnValueChangedListener? = null
|
||||
|
||||
private var mHandler: Handler = Handler(Looper.getMainLooper(), Handler.Callback { msg: Message ->
|
||||
when (msg.what) {
|
||||
MSG_INC -> {
|
||||
inc(msg.arg1)
|
||||
return@Callback true
|
||||
}
|
||||
|
||||
MSG_DEC -> {
|
||||
dec(msg.arg1)
|
||||
return@Callback true
|
||||
}
|
||||
}
|
||||
false
|
||||
})
|
||||
|
||||
private inner class UpdateCounterTask(private val mInc: Boolean) : Runnable {
|
||||
|
||||
private var repeated = 0
|
||||
private var multiplier = 1
|
||||
private val doubleLimit = 5
|
||||
override fun run() {
|
||||
val msg = Message()
|
||||
if (repeated % doubleLimit == 0) multiplier *= 2
|
||||
repeated++
|
||||
msg.arg1 = multiplier
|
||||
msg.arg2 = repeated
|
||||
if (mInc) {
|
||||
msg.what = MSG_INC
|
||||
} else {
|
||||
msg.what = MSG_DEC
|
||||
}
|
||||
mHandler.sendMessage(msg)
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun inflate(context: Context) {
|
||||
LayoutInflater.from(context).inflate(R.layout.number_picker_layout, this, true)
|
||||
}
|
||||
|
||||
protected fun initialize(context: Context) {
|
||||
// set layout view
|
||||
inflate(context)
|
||||
|
||||
// init ui components
|
||||
minusButton = findViewById(R.id.decrement)
|
||||
minusButton?.id = generateViewId()
|
||||
plusButton = findViewById(R.id.increment)
|
||||
plusButton?.id = generateViewId()
|
||||
editText = findViewById(R.id.display)
|
||||
editText?.id = generateViewId()
|
||||
minusButton?.setOnTouchListener(this)
|
||||
minusButton?.setOnKeyListener(this)
|
||||
minusButton?.setOnClickListener(this)
|
||||
plusButton?.setOnTouchListener(this)
|
||||
plusButton?.setOnKeyListener(this)
|
||||
plusButton?.setOnClickListener(this)
|
||||
setTextWatcher(object : TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
if (focused) currentValue = SafeParse.stringToDouble(editText?.text.toString())
|
||||
callValueChangedListener()
|
||||
okButton?.visibility = if (currentValue > maxValue || currentValue < minValue) INVISIBLE else VISIBLE
|
||||
}
|
||||
})
|
||||
editText?.setOnFocusChangeListener { _: View?, hasFocus: Boolean ->
|
||||
focused = hasFocus
|
||||
if (!focused) value // check min/max
|
||||
updateEditText()
|
||||
}
|
||||
}
|
||||
|
||||
override fun setTag(tag: Any) {
|
||||
editText?.tag = tag
|
||||
}
|
||||
|
||||
fun setOnValueChangedListener(onValueChangedListener: OnValueChangedListener?) {
|
||||
mOnValueChangedListener = onValueChangedListener
|
||||
}
|
||||
|
||||
fun setTextWatcher(textWatcher: TextWatcher) {
|
||||
watcher = textWatcher
|
||||
editText?.addTextChangedListener(textWatcher)
|
||||
editText?.onFocusChangeListener = OnFocusChangeListener { _: View?, hasFocus: Boolean ->
|
||||
if (!hasFocus) {
|
||||
currentValue = SafeParse.stringToDouble(editText?.text.toString())
|
||||
if (currentValue > maxValue) {
|
||||
currentValue = maxValue
|
||||
ToastUtils.showToastInUiThread(context, context.getString(R.string.youareonallowedlimit))
|
||||
updateEditText()
|
||||
okButton?.visibility = VISIBLE
|
||||
}
|
||||
if (currentValue < minValue) {
|
||||
currentValue = minValue
|
||||
ToastUtils.showToastInUiThread(context, context.getString(R.string.youareonallowedlimit))
|
||||
updateEditText()
|
||||
okButton?.visibility = VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setParams(initValue: Double, minValue: Double, maxValue: Double, step: Double, formatter: NumberFormat?, allowZero: Boolean, okButton: Button?, textWatcher: TextWatcher?) {
|
||||
if (watcher != null) {
|
||||
editText?.removeTextChangedListener(watcher)
|
||||
}
|
||||
setParams(initValue, minValue, maxValue, step, formatter, allowZero, okButton)
|
||||
watcher = textWatcher
|
||||
if (textWatcher != null) editText?.addTextChangedListener(textWatcher)
|
||||
}
|
||||
|
||||
fun setParams(initValue: Double, minValue: Double, maxValue: Double, step: Double, formatter: NumberFormat?, allowZero: Boolean, okButton: Button?) {
|
||||
currentValue = initValue
|
||||
this.minValue = minValue
|
||||
this.maxValue = maxValue
|
||||
this.step = step
|
||||
this.formatter = formatter
|
||||
this.allowZero = allowZero
|
||||
callValueChangedListener()
|
||||
this.okButton = okButton
|
||||
editText?.keyListener = DigitsKeyListenerWithComma.getInstance(minValue < 0, step != round(step))
|
||||
if (watcher != null) editText?.removeTextChangedListener(watcher)
|
||||
updateEditText()
|
||||
if (watcher != null) editText?.addTextChangedListener(watcher)
|
||||
}
|
||||
|
||||
var value: Double
|
||||
get() {
|
||||
if (currentValue > maxValue) {
|
||||
currentValue = maxValue
|
||||
ToastUtils.showToastInUiThread(context, context.getString(R.string.youareonallowedlimit))
|
||||
}
|
||||
if (currentValue < minValue) {
|
||||
currentValue = minValue
|
||||
ToastUtils.showToastInUiThread(context, context.getString(R.string.youareonallowedlimit))
|
||||
}
|
||||
return currentValue
|
||||
}
|
||||
set(value) {
|
||||
if (watcher != null) editText?.removeTextChangedListener(watcher)
|
||||
currentValue = value
|
||||
callValueChangedListener()
|
||||
updateEditText()
|
||||
if (watcher != null) editText?.addTextChangedListener(watcher)
|
||||
}
|
||||
|
||||
val text: String
|
||||
get() = editText?.text.toString()
|
||||
|
||||
private fun inc(multiplier: Int) {
|
||||
currentValue += step * multiplier
|
||||
if (currentValue > maxValue) {
|
||||
currentValue = maxValue
|
||||
callValueChangedListener()
|
||||
ToastUtils.showToastInUiThread(context, context.getString(R.string.youareonallowedlimit))
|
||||
stopUpdating()
|
||||
}
|
||||
updateEditText()
|
||||
}
|
||||
|
||||
private fun dec(multiplier: Int) {
|
||||
currentValue -= step * multiplier
|
||||
if (currentValue < minValue) {
|
||||
currentValue = minValue
|
||||
callValueChangedListener()
|
||||
ToastUtils.showToastInUiThread(context, context.getString(R.string.youareonallowedlimit))
|
||||
stopUpdating()
|
||||
}
|
||||
updateEditText()
|
||||
}
|
||||
|
||||
protected open fun updateEditText() {
|
||||
if (currentValue == 0.0 && !allowZero) editText?.setText("") else editText?.setText(formatter?.format(currentValue))
|
||||
}
|
||||
|
||||
private fun callValueChangedListener() {
|
||||
mOnValueChangedListener?.onValueChanged(currentValue)
|
||||
}
|
||||
|
||||
private fun startUpdating(inc: Boolean) {
|
||||
if (mUpdater != null) {
|
||||
//log.debug("Another executor is still active");
|
||||
return
|
||||
}
|
||||
mUpdater = Executors.newSingleThreadScheduledExecutor()
|
||||
mUpdater?.scheduleAtFixedRate(
|
||||
UpdateCounterTask(inc), 200, 200,
|
||||
TimeUnit.MILLISECONDS
|
||||
)
|
||||
}
|
||||
|
||||
private fun stopUpdating() {
|
||||
mUpdater?.shutdownNow()
|
||||
mUpdater = null
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
if (mUpdater == null) {
|
||||
val imm = context.getSystemService(Service.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
imm.hideSoftInputFromWindow(editText?.windowToken, 0)
|
||||
editText?.clearFocus()
|
||||
if (v === plusButton) {
|
||||
inc(1)
|
||||
} else {
|
||||
dec(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onKey(v: View, keyCode: Int, event: KeyEvent): Boolean {
|
||||
val isKeyOfInterest = keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER
|
||||
val isReleased = event.action == KeyEvent.ACTION_UP
|
||||
val isPressed = (event.action == KeyEvent.ACTION_DOWN)
|
||||
if (isKeyOfInterest && isReleased) {
|
||||
stopUpdating()
|
||||
} else if (isKeyOfInterest && isPressed) {
|
||||
startUpdating(v === plusButton)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onTouch(v: View, event: MotionEvent): Boolean {
|
||||
val isReleased = event.action == MotionEvent.ACTION_UP || event.action == MotionEvent.ACTION_CANCEL
|
||||
val isPressed = event.action == MotionEvent.ACTION_DOWN
|
||||
if (isReleased) {
|
||||
stopUpdating()
|
||||
} else if (isPressed) {
|
||||
startUpdating(v === plusButton)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val MSG_INC = 0
|
||||
private const val MSG_DEC = 1
|
||||
}
|
||||
|
||||
init {
|
||||
initialize(context)
|
||||
}
|
||||
}
|
|
@ -504,7 +504,22 @@
|
|||
<string name="basal_value">Basal value</string>
|
||||
<string name="nsclient_version_does_not_match">NSClient version doesn\'t match to AndroidAPS. Please update.</string>
|
||||
|
||||
<!-- readStatus reasons -->
|
||||
<!-- Command Queue + readStatus reasons -->
|
||||
<string name="bolus_u_min">BOLUS %1$.2f U</string>
|
||||
<string name="carbs_g">CARBS %1$d g</string>
|
||||
<string name="extended_bolus_u_min">EXTENDED BOLUS %1$.2f U %2$d min</string>
|
||||
<string name="load_events">LOAD EVENTS</string>
|
||||
<string name="load_history">LOAD HISTORY %1$d</string>
|
||||
<string name="load_tdds">LOAD TDDs</string>
|
||||
<string name="set_profile">SET PROFILE</string>
|
||||
<string name="set_user_settings">SET USER SETTINGS</string>
|
||||
<string name="smb_bolus_u">SMB BOLUS %1$.2f U</string>
|
||||
<string name="start_pump">START PUMP</string>
|
||||
<string name="stop_pump">STOP PUMP</string>
|
||||
<string name="temp_basal_absolute">TEMP BASAL %1$.2f U/h %2$d min</string>
|
||||
<string name="temp_basal_percent">TEMP BASAL %1$d%% %2$d min</string>
|
||||
<string name="insight_set_tbr_over_notification">INSIGHT SET TBR OVER NOTIFICATION</string>
|
||||
<string name="read_status" comment="10 characters max for READSTATUS translation">READSTATUS %1$s</string>
|
||||
<string name="keepalive_status_outdated" comment="26 characters max for translation">KeepAlive. Status outdated.</string>
|
||||
<string name="keepalive_basal_outdated" comment="26 characters max for translation">KeepAlive. Basal outdated.</string>
|
||||
<string name="sms" comment="26 characters max for translation">SMS</string>
|
||||
|
@ -520,7 +535,6 @@
|
|||
<string name="bolus_ok" comment="26 characters max for translation">Bolus OK</string>
|
||||
<string name="pump_paired" comment="26 characters max for translation">Pump paired</string>
|
||||
<string name="insight_refresh_button" comment="26 characters max for translation">Insight Refresh Button</string>
|
||||
<string name="read_status" comment="10 characters max for READSTATUS translation">READSTATUS %1$s</string>
|
||||
|
||||
<plurals name="days">
|
||||
<item quantity="one">%1$d day</item>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package info.nightscout.androidaps.danaRKorean.comm
|
||||
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.dana.DanaPump
|
||||
import info.nightscout.androidaps.danar.R
|
||||
import info.nightscout.androidaps.danar.comm.MessageBase
|
||||
import info.nightscout.androidaps.events.EventRebuildTabs
|
||||
|
|
|
@ -207,10 +207,12 @@ public abstract class AbstractDanaRExecutionService extends DaggerService {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
ToastUtils.showToastInUiThread(context.getApplicationContext(), rh.gs(R.string.nobtadapter));
|
||||
ToastUtils.INSTANCE.showToastInUiThread(context.getApplicationContext(),
|
||||
rh.gs(R.string.nobtadapter));
|
||||
}
|
||||
if (mBTDevice == null) {
|
||||
ToastUtils.showToastInUiThread(context.getApplicationContext(), rh.gs(R.string.devicenotfound));
|
||||
ToastUtils.INSTANCE.showToastInUiThread(context.getApplicationContext(),
|
||||
rh.gs(R.string.devicenotfound));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,12 +6,17 @@ import info.nightscout.androidaps.dana.DanaPump
|
|||
import info.nightscout.androidaps.danars.encryption.BleEncryption
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
class DanaRSPacketGeneralInitialScreenInformation(
|
||||
injector: HasAndroidInjector
|
||||
) : DanaRSPacket(injector) {
|
||||
|
||||
@Inject lateinit var danaPump: DanaPump
|
||||
|
||||
var isTempBasalInProgress = false
|
||||
var isExtendedInProgress = false
|
||||
var isDualBolusInProgress = false
|
||||
|
||||
init {
|
||||
opCode = BleEncryption.DANAR_PACKET__OPCODE_REVIEW__INITIAL_SCREEN_INFORMATION
|
||||
aapsLogger.debug(LTag.PUMPCOMM, "New message")
|
||||
|
@ -22,42 +27,22 @@ class DanaRSPacketGeneralInitialScreenInformation(
|
|||
failed = true
|
||||
return
|
||||
} else failed = false
|
||||
var dataIndex = DATA_START
|
||||
var dataSize = 1
|
||||
val status = byteArrayToInt(getBytes(data, dataIndex, dataSize))
|
||||
val status = intFromBuff(data, 0, 1)
|
||||
danaPump.pumpSuspended = status and 0x01 == 0x01
|
||||
val isTempBasalInProgress = status and 0x10 == 0x10
|
||||
val isExtendedInProgress = status and 0x04 == 0x04
|
||||
val isDualBolusInProgress = status and 0x08 == 0x08
|
||||
dataIndex += dataSize
|
||||
dataSize = 2
|
||||
danaPump.dailyTotalUnits = byteArrayToInt(getBytes(data, dataIndex, dataSize)) / 100.0
|
||||
dataIndex += dataSize
|
||||
dataSize = 2
|
||||
danaPump.maxDailyTotalUnits = (byteArrayToInt(getBytes(data, dataIndex, dataSize)) / 100.0).toInt()
|
||||
dataIndex += dataSize
|
||||
dataSize = 2
|
||||
danaPump.reservoirRemainingUnits = byteArrayToInt(getBytes(data, dataIndex, dataSize)) / 100.0
|
||||
dataIndex += dataSize
|
||||
dataSize = 2
|
||||
danaPump.currentBasal = byteArrayToInt(getBytes(data, dataIndex, dataSize)) / 100.0
|
||||
dataIndex += dataSize
|
||||
dataSize = 1
|
||||
val tempBasalPercent = byteArrayToInt(getBytes(data, dataIndex, dataSize))
|
||||
dataIndex += dataSize
|
||||
dataSize = 1
|
||||
danaPump.batteryRemaining = byteArrayToInt(getBytes(data, dataIndex, dataSize))
|
||||
dataIndex += dataSize
|
||||
dataSize = 2
|
||||
val extendedBolusAbsoluteRate = byteArrayToInt(getBytes(data, dataIndex, dataSize)) / 100.0
|
||||
dataIndex += dataSize
|
||||
dataSize = 2
|
||||
danaPump.iob = byteArrayToInt(getBytes(data, dataIndex, dataSize)) / 100.0
|
||||
isTempBasalInProgress = status and 0x10 == 0x10
|
||||
isExtendedInProgress = status and 0x04 == 0x04
|
||||
isDualBolusInProgress = status and 0x08 == 0x08
|
||||
danaPump.dailyTotalUnits = intFromBuff(data, 1, 2) / 100.0
|
||||
danaPump.maxDailyTotalUnits = intFromBuff(data, 3, 2) / 100
|
||||
danaPump.reservoirRemainingUnits = intFromBuff(data, 5, 2) / 100.0
|
||||
danaPump.currentBasal = intFromBuff(data, 7, 2) / 100.0
|
||||
val tempBasalPercent = intFromBuff(data, 9, 1)
|
||||
danaPump.batteryRemaining = intFromBuff(data, 10, 1)
|
||||
val extendedBolusAbsoluteRate = intFromBuff(data, 11, 2) / 100.0
|
||||
danaPump.iob = intFromBuff(data, 13, 2) / 100.0
|
||||
if (data.size >= 18) {
|
||||
//protocol 10+
|
||||
dataIndex += dataSize
|
||||
dataSize = 1
|
||||
danaPump.errorState = DanaPump.ErrorState[byteArrayToInt(getBytes(data, dataIndex, dataSize))]
|
||||
danaPump.errorState = DanaPump.ErrorState[intFromBuff(data, 15, 1)]
|
||||
?: DanaPump.ErrorState.NONE
|
||||
aapsLogger.debug(LTag.PUMPCOMM, "ErrorState: " + danaPump.errorState.name)
|
||||
}
|
||||
|
|
|
@ -338,7 +338,9 @@ class DanaRSService : DaggerService() {
|
|||
|
||||
fun tempBasal(percent: Int, durationInHours: Int): Boolean {
|
||||
if (!isConnected) return false
|
||||
if (danaPump.isTempBasalInProgress) {
|
||||
val status = DanaRSPacketGeneralInitialScreenInformation(injector)
|
||||
sendMessage(status)
|
||||
if (status.isTempBasalInProgress) {
|
||||
rxBus.send(EventPumpStatusChanged(rh.gs(R.string.stoppingtempbasal)))
|
||||
sendMessage(DanaRSPacketBasalSetCancelTemporaryBasal(injector))
|
||||
SystemClock.sleep(500)
|
||||
|
@ -348,6 +350,7 @@ class DanaRSService : DaggerService() {
|
|||
sendMessage(msgTBR)
|
||||
SystemClock.sleep(200)
|
||||
loadEvents()
|
||||
SystemClock.sleep(4500)
|
||||
val tbr = pumpSync.expectedPumpState().temporaryBasal
|
||||
danaPump.fromTemporaryBasal(tbr)
|
||||
rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING))
|
||||
|
@ -355,7 +358,9 @@ class DanaRSService : DaggerService() {
|
|||
}
|
||||
|
||||
fun highTempBasal(percent: Int): Boolean {
|
||||
if (danaPump.isTempBasalInProgress) {
|
||||
val status = DanaRSPacketGeneralInitialScreenInformation(injector)
|
||||
sendMessage(status)
|
||||
if (status.isTempBasalInProgress) {
|
||||
rxBus.send(EventPumpStatusChanged(rh.gs(R.string.stoppingtempbasal)))
|
||||
sendMessage(DanaRSPacketBasalSetCancelTemporaryBasal(injector))
|
||||
SystemClock.sleep(500)
|
||||
|
@ -364,6 +369,7 @@ class DanaRSService : DaggerService() {
|
|||
val msgTBR = DanaRSPacketAPSBasalSetTemporaryBasal(injector, percent)
|
||||
sendMessage(msgTBR)
|
||||
loadEvents()
|
||||
SystemClock.sleep(4500)
|
||||
val tbr = pumpSync.expectedPumpState().temporaryBasal
|
||||
danaPump.fromTemporaryBasal(tbr)
|
||||
rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING))
|
||||
|
@ -375,7 +381,9 @@ class DanaRSService : DaggerService() {
|
|||
aapsLogger.error(LTag.PUMPCOMM, "Wrong duration param")
|
||||
return false
|
||||
}
|
||||
if (danaPump.isTempBasalInProgress) {
|
||||
val status = DanaRSPacketGeneralInitialScreenInformation(injector)
|
||||
sendMessage(status)
|
||||
if (status.isTempBasalInProgress) {
|
||||
rxBus.send(EventPumpStatusChanged(rh.gs(R.string.stoppingtempbasal)))
|
||||
sendMessage(DanaRSPacketBasalSetCancelTemporaryBasal(injector))
|
||||
SystemClock.sleep(500)
|
||||
|
@ -384,6 +392,7 @@ class DanaRSService : DaggerService() {
|
|||
val msgTBR = DanaRSPacketAPSBasalSetTemporaryBasal(injector, percent)
|
||||
sendMessage(msgTBR)
|
||||
loadEvents()
|
||||
SystemClock.sleep(4500)
|
||||
val tbr = pumpSync.expectedPumpState().temporaryBasal
|
||||
aapsLogger.debug(LTag.PUMPCOMM, "Expected TBR found: $tbr")
|
||||
danaPump.fromTemporaryBasal(tbr)
|
||||
|
@ -397,6 +406,7 @@ class DanaRSService : DaggerService() {
|
|||
val msgCancel = DanaRSPacketBasalSetCancelTemporaryBasal(injector)
|
||||
sendMessage(msgCancel)
|
||||
loadEvents()
|
||||
SystemClock.sleep(4500)
|
||||
val tbr = pumpSync.expectedPumpState().temporaryBasal
|
||||
danaPump.fromTemporaryBasal(tbr)
|
||||
rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING))
|
||||
|
@ -410,6 +420,7 @@ class DanaRSService : DaggerService() {
|
|||
sendMessage(msgExtended)
|
||||
SystemClock.sleep(200)
|
||||
loadEvents()
|
||||
SystemClock.sleep(4500)
|
||||
val eb = pumpSync.expectedPumpState().extendedBolus
|
||||
danaPump.fromExtendedBolus(eb)
|
||||
rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING))
|
||||
|
@ -422,6 +433,7 @@ class DanaRSService : DaggerService() {
|
|||
val msgStop = DanaRSPacketBolusSetExtendedBolusCancel(injector)
|
||||
sendMessage(msgStop)
|
||||
loadEvents()
|
||||
SystemClock.sleep(4500)
|
||||
val eb = pumpSync.expectedPumpState().extendedBolus
|
||||
danaPump.fromExtendedBolus(eb)
|
||||
rxBus.send(EventPumpStatusChanged(EventPumpStatusChanged.Status.DISCONNECTING))
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.common.queue.command
|
||||
|
||||
import info.nightscout.androidaps.queue.commands.CustomCommand
|
||||
|
||||
class CommandDisableSuspendAlerts: CustomCommand {
|
||||
|
||||
override val statusDescription: String
|
||||
get() = "DISABLE SUSPEND ALERTS"
|
||||
}
|
|
@ -502,7 +502,10 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
|||
// prevent setBasal requests
|
||||
return true
|
||||
}
|
||||
// TODO: what do we have to answer here if delivery is suspended?
|
||||
if (podStateManager.isSuspended) {
|
||||
// set new basal profile failed midway
|
||||
return false
|
||||
}
|
||||
val running = podStateManager.basalProgram
|
||||
val equal = (mapProfileToBasalProgram(profile) == running)
|
||||
aapsLogger.info(LTag.PUMP, "set: $equal. profile=$profile, running=$running")
|
||||
|
@ -1089,8 +1092,6 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
|||
return when (customCommand) {
|
||||
is CommandSilenceAlerts ->
|
||||
silenceAlerts()
|
||||
is CommandSuspendDelivery ->
|
||||
suspendDelivery()
|
||||
is CommandResumeDelivery ->
|
||||
resumeDelivery()
|
||||
is CommandDeactivatePod ->
|
||||
|
@ -1101,7 +1102,8 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
|||
updateAlertConfiguration()
|
||||
is CommandPlayTestBeep ->
|
||||
playTestBeep()
|
||||
|
||||
is CommandDisableSuspendAlerts ->
|
||||
disableSuspendAlerts()
|
||||
else -> {
|
||||
aapsLogger.warn(LTag.PUMP, "Unsupported custom command: " + customCommand.javaClass.name)
|
||||
PumpEnactResult(injector).success(false).enacted(false).comment(
|
||||
|
@ -1124,27 +1126,28 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
|||
} ?: PumpEnactResult(injector).success(false).enacted(false).comment("No active alerts") // TODO i18n
|
||||
}
|
||||
|
||||
private fun suspendDelivery(): PumpEnactResult {
|
||||
return executeProgrammingCommand(
|
||||
historyEntry = history.createRecord(OmnipodCommandType.SUSPEND_DELIVERY),
|
||||
command = omnipodManager.suspendDelivery(hasBasalBeepEnabled())
|
||||
.filter { podEvent -> podEvent.isCommandSent() }
|
||||
.map {
|
||||
pumpSyncTempBasal(
|
||||
0.0,
|
||||
PodConstants.MAX_POD_LIFETIME.toMinutes(),
|
||||
PumpSync.TemporaryBasalType.PUMP_SUSPEND
|
||||
)
|
||||
}
|
||||
.ignoreElements(),
|
||||
pre = observeDeliveryActive(),
|
||||
).doFinally {
|
||||
notifyOnUnconfirmed(
|
||||
Notification.PUMP_ERROR,
|
||||
"Unconfirmed suspendDelivery command. Please refresh pod status",
|
||||
R.raw.boluserror
|
||||
)
|
||||
}.toPumpEnactResult()
|
||||
private fun disableSuspendAlerts(): PumpEnactResult {
|
||||
val alerts = listOf(
|
||||
AlertConfiguration(
|
||||
AlertType.SUSPEND_ENDED,
|
||||
enabled = false,
|
||||
durationInMinutes = 0,
|
||||
autoOff = false,
|
||||
AlertTrigger.TimerTrigger(
|
||||
0
|
||||
),
|
||||
BeepType.FOUR_TIMES_BIP_BEEP,
|
||||
BeepRepetitionType.EVERY_MINUTE_AND_EVERY_15_MIN
|
||||
),
|
||||
)
|
||||
val ret = executeProgrammingCommand(
|
||||
historyEntry = history.createRecord(OmnipodCommandType.CONFIGURE_ALERTS),
|
||||
command = omnipodManager.programAlerts(alerts).ignoreElements(),
|
||||
).toPumpEnactResult()
|
||||
if (ret.success && ret.enacted) {
|
||||
podStateManager.suspendAlertsEnabled = false
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
private fun observeDeliveryActive(): Completable = Completable.defer {
|
||||
|
@ -1249,7 +1252,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
|||
expiryAlertDelay.toMinutes().toShort()
|
||||
),
|
||||
BeepType.FOUR_TIMES_BIP_BEEP,
|
||||
BeepRepetitionType.XXX2
|
||||
BeepRepetitionType.EVERY_MINUTE_AND_EVERY_15_MIN
|
||||
)
|
||||
)
|
||||
return executeProgrammingCommand(
|
||||
|
@ -1369,9 +1372,11 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
|||
)
|
||||
podStateManager.tempBasal = null
|
||||
rxBus.send(EventDismissNotification(Notification.OMNIPOD_POD_SUSPENDED))
|
||||
rxBus.send(EventDismissNotification(Notification.FAILED_UPDATE_PROFILE))
|
||||
rxBus.send(EventDismissNotification(Notification.OMNIPOD_TBR_ALERTS))
|
||||
rxBus.send(EventDismissNotification(Notification.OMNIPOD_TIME_OUT_OF_SYNC))
|
||||
commandQueue.customCommand(CommandDisableSuspendAlerts(), null)
|
||||
}
|
||||
rxBus.send(EventDismissNotification(Notification.OMNIPOD_TBR_ALERTS))
|
||||
rxBus.send(EventDismissNotification(Notification.OMNIPOD_TIME_OUT_OF_SYNC))
|
||||
}
|
||||
|
||||
OmnipodCommandType.SET_BASAL_PROFILE -> {
|
||||
|
@ -1394,6 +1399,7 @@ class OmnipodDashPumpPlugin @Inject constructor(
|
|||
rxBus.send(EventDismissNotification(Notification.FAILED_UPDATE_PROFILE))
|
||||
rxBus.send(EventDismissNotification(Notification.OMNIPOD_TBR_ALERTS))
|
||||
rxBus.send(EventDismissNotification(Notification.OMNIPOD_TIME_OUT_OF_SYNC))
|
||||
commandQueue.customCommand(CommandDisableSuspendAlerts(), null)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,8 +12,10 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definitio
|
|||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodConstants.Companion.POD_EXPIRATION_ALERT_HOURS
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodConstants.Companion.POD_EXPIRATION_ALERT_HOURS_DURATION
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodConstants.Companion.POD_EXPIRATION_IMMINENT_ALERT_HOURS
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.PodConstants.Companion.POD_PULSE_BOLUS_UNITS
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.*
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.state.OmnipodDashPodStateManager
|
||||
import info.nightscout.androidaps.utils.Round
|
||||
import info.nightscout.androidaps.utils.rx.AapsSchedulers
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.functions.Action
|
||||
|
@ -281,7 +283,7 @@ class OmnipodDashManagerImpl @Inject constructor(
|
|||
.setUniqueId(podStateManager.uniqueId!!.toInt())
|
||||
.setSequenceNumber(podStateManager.messageSequenceNumber)
|
||||
.setNonce(NONCE)
|
||||
.setNumberOfUnits(podStateManager.firstPrimeBolusVolume!! * PodConstants.POD_PULSE_BOLUS_UNITS)
|
||||
.setNumberOfUnits(Round.roundTo(podStateManager.firstPrimeBolusVolume!! * PodConstants.POD_PULSE_BOLUS_UNITS, POD_PULSE_BOLUS_UNITS))
|
||||
.setDelayBetweenPulsesInEighthSeconds(podStateManager.primePulseRate!!.toByte())
|
||||
.setProgramReminder(ProgramReminder(atStart = false, atEnd = false, atInterval = 0))
|
||||
.build(),
|
||||
|
@ -383,7 +385,7 @@ class OmnipodDashManagerImpl @Inject constructor(
|
|||
)
|
||||
observables.add(
|
||||
observeSendProgramBolusCommand(
|
||||
podStateManager.secondPrimeBolusVolume!! * PodConstants.POD_PULSE_BOLUS_UNITS,
|
||||
Round.roundTo(podStateManager.secondPrimeBolusVolume!! * PodConstants.POD_PULSE_BOLUS_UNITS, PodConstants.POD_PULSE_BOLUS_UNITS),
|
||||
podStateManager.primePulseRate!!.toByte(),
|
||||
confirmationBeeps = false,
|
||||
completionBeeps = false
|
||||
|
@ -437,7 +439,7 @@ class OmnipodDashManagerImpl @Inject constructor(
|
|||
userExpiryAlertDelay.toMinutes().toShort()
|
||||
),
|
||||
BeepType.FOUR_TIMES_BIP_BEEP,
|
||||
BeepRepetitionType.XXX2
|
||||
BeepRepetitionType.EVERY_MINUTE_AND_EVERY_15_MIN
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -502,8 +504,29 @@ class OmnipodDashManagerImpl @Inject constructor(
|
|||
return Observable.concat(
|
||||
observePodRunning,
|
||||
observeConnectToPod,
|
||||
observeSendStopDeliveryCommand(StopDeliveryCommand.DeliveryType.ALL, hasBasalBeepEnabled)
|
||||
).interceptPodEvents()
|
||||
observeSuspendDeliveryCommand(hasBasalBeepEnabled)
|
||||
).doOnComplete {
|
||||
podStateManager.suspendAlertsEnabled = true
|
||||
}.interceptPodEvents()
|
||||
}
|
||||
|
||||
private fun observeSuspendDeliveryCommand(hasBasalBeepEnabled: Boolean): Observable<PodEvent> {
|
||||
return Observable.defer {
|
||||
val beepType = if (!hasBasalBeepEnabled)
|
||||
BeepType.SILENT
|
||||
else
|
||||
BeepType.LONG_SINGLE_BEEP
|
||||
|
||||
bleManager.sendCommand(
|
||||
SuspendDeliveryCommand.Builder()
|
||||
.setSequenceNumber(podStateManager.messageSequenceNumber)
|
||||
.setUniqueId(podStateManager.uniqueId!!.toInt())
|
||||
.setNonce(NONCE)
|
||||
.setBeepType(beepType)
|
||||
.build(),
|
||||
DefaultStatusResponse::class
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeSendProgramTempBasalCommand(rate: Double, durationInMinutes: Short, tempBasalBeeps: Boolean): Observable<PodEvent> {
|
||||
|
|
|
@ -38,6 +38,18 @@ class ProgramAlertsCommand private constructor(
|
|||
return appendCrc(byteBuffer.array())
|
||||
}
|
||||
|
||||
val encodedWithoutHeaderAndCRC32: ByteArray
|
||||
get() {
|
||||
val byteBuffer: ByteBuffer = ByteBuffer.allocate(getLength().toInt())
|
||||
.put(commandType.value)
|
||||
.put(getBodyLength())
|
||||
.putInt(nonce)
|
||||
for (configuration in alertConfigurations) {
|
||||
byteBuffer.put(configuration.encoded)
|
||||
}
|
||||
return byteBuffer.array()
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "ProgramAlertsCommand{" +
|
||||
"alertConfigurations=" + alertConfigurations +
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.CommandType
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.NonceEnabledCommand
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command.base.builder.NonceEnabledCommandBuilder
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.*
|
||||
import java.nio.ByteBuffer
|
||||
import java.util.*
|
||||
|
||||
// StopDelivery.ALL followed by ProgramAlerts
|
||||
class SuspendDeliveryCommand private constructor(
|
||||
uniqueId: Int,
|
||||
sequenceNumber: Short,
|
||||
multiCommandFlag: Boolean,
|
||||
private val beepType: BeepType,
|
||||
nonce: Int
|
||||
) : NonceEnabledCommand(CommandType.STOP_DELIVERY, uniqueId, sequenceNumber, multiCommandFlag, nonce) {
|
||||
|
||||
override val encoded: ByteArray
|
||||
get() {
|
||||
val alerts = listOf(
|
||||
AlertConfiguration(
|
||||
AlertType.SUSPEND_ENDED,
|
||||
enabled = true,
|
||||
durationInMinutes = 0,
|
||||
autoOff = false,
|
||||
AlertTrigger.TimerTrigger(
|
||||
20
|
||||
),
|
||||
BeepType.FOUR_TIMES_BIP_BEEP,
|
||||
BeepRepetitionType.EVERY_MINUTE_AND_EVERY_15_MIN
|
||||
),
|
||||
)
|
||||
val programAlerts = ProgramAlertsCommand.Builder()
|
||||
.setNonce(nonce)
|
||||
.setUniqueId(uniqueId)
|
||||
.setAlertConfigurations(alerts)
|
||||
.setSequenceNumber(sequenceNumber)
|
||||
.setMultiCommandFlag(false)
|
||||
.build()
|
||||
.encodedWithoutHeaderAndCRC32
|
||||
|
||||
val byteBuffer = ByteBuffer.allocate(LENGTH + HEADER_LENGTH + programAlerts.size)
|
||||
.put(encodeHeader(uniqueId, sequenceNumber, (LENGTH + programAlerts.size).toShort(), multiCommandFlag))
|
||||
.put(commandType.value)
|
||||
.put(BODY_LENGTH)
|
||||
.putInt(nonce)
|
||||
.put((beepType.value.toInt() shl 4 or DeliveryType.ALL.encoded[0].toInt()).toByte())
|
||||
.put(programAlerts)
|
||||
.array()
|
||||
|
||||
return appendCrc(
|
||||
byteBuffer
|
||||
)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "SuspendDeliveryCommand{" +
|
||||
"deliveryType=" + DeliveryType.ALL +
|
||||
", beepType=" + beepType +
|
||||
", nonce=" + nonce +
|
||||
", commandType=" + commandType +
|
||||
", uniqueId=" + uniqueId +
|
||||
", sequenceNumber=" + sequenceNumber +
|
||||
", multiCommandFlag=" + multiCommandFlag +
|
||||
'}'
|
||||
}
|
||||
|
||||
enum class DeliveryType(
|
||||
private val basal: Boolean,
|
||||
private val tempBasal: Boolean,
|
||||
private val bolus: Boolean
|
||||
) : Encodable {
|
||||
|
||||
BASAL(true, false, false), TEMP_BASAL(false, true, false), BOLUS(false, false, true), ALL(true, true, true);
|
||||
|
||||
override val encoded: ByteArray
|
||||
get() {
|
||||
val bitSet = BitSet(8)
|
||||
bitSet[0] = basal
|
||||
bitSet[1] = tempBasal
|
||||
bitSet[2] = bolus
|
||||
return bitSet.toByteArray()
|
||||
}
|
||||
}
|
||||
|
||||
class Builder : NonceEnabledCommandBuilder<Builder, SuspendDeliveryCommand>() {
|
||||
private var beepType: BeepType? = BeepType.LONG_SINGLE_BEEP
|
||||
|
||||
fun setBeepType(beepType: BeepType): Builder {
|
||||
this.beepType = beepType
|
||||
return this
|
||||
}
|
||||
|
||||
override fun buildCommand(): SuspendDeliveryCommand {
|
||||
requireNotNull(beepType) { "beepType can not be null" }
|
||||
|
||||
return SuspendDeliveryCommand(
|
||||
uniqueId!!,
|
||||
sequenceNumber!!,
|
||||
multiCommandFlag,
|
||||
beepType!!,
|
||||
nonce!!
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val LENGTH: Short = 7
|
||||
private const val BODY_LENGTH: Byte = 5
|
||||
}
|
||||
}
|
|
@ -6,8 +6,8 @@ enum class BeepRepetitionType(
|
|||
) {
|
||||
|
||||
XXX(0x01.toByte()), // Used in lump of coal alert, LOW_RESERVOIR
|
||||
XXX2(0x03.toByte()), // Used in USER_SET_EXPIRATION
|
||||
EVERY_MINUTE_AND_EVERY_15_MIN(0x03.toByte()), // Used in USER_SET_EXPIRATION, suspend delivery
|
||||
XXX3(0x05.toByte()), // published system expiration alert
|
||||
XXX4(0x06.toByte()), // Used in imminent pod expiration alert, suspend in progress
|
||||
XXX4(0x06.toByte()), // Used in imminent pod expiration alert, suspend in progress. No repeat?
|
||||
XXX5(0x08.toByte()); // Lump of coal alert
|
||||
}
|
||||
|
|
|
@ -77,6 +77,7 @@ interface OmnipodDashPodStateManager {
|
|||
var basalProgram: BasalProgram?
|
||||
val activeCommand: ActiveCommand?
|
||||
val lastBolus: LastBolus?
|
||||
var suspendAlertsEnabled: Boolean
|
||||
|
||||
fun increaseMessageSequenceNumber()
|
||||
fun increaseEapAkaSequenceNumber(): ByteArray
|
||||
|
|
|
@ -16,6 +16,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.
|
|||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.DefaultStatusResponse
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.SetUniqueIdResponse
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.response.VersionResponse
|
||||
import info.nightscout.androidaps.utils.Round
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
import io.reactivex.Completable
|
||||
import io.reactivex.Maybe
|
||||
|
@ -224,6 +225,13 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
|||
store()
|
||||
}
|
||||
|
||||
override var suspendAlertsEnabled: Boolean
|
||||
get() = podState.suspendAlertsEnabled
|
||||
set(enabled) {
|
||||
podState.suspendAlertsEnabled = enabled
|
||||
store()
|
||||
}
|
||||
|
||||
override val lastStatusResponseReceived: Long
|
||||
get() = podState.lastStatusResponseReceived
|
||||
|
||||
|
@ -340,7 +348,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
|||
|
||||
private fun updateLastBolusFromResponse(bolusPulsesRemaining: Short) {
|
||||
podState.lastBolus?.run {
|
||||
val remainingUnits = bolusPulsesRemaining.toDouble() * PodConstants.POD_PULSE_BOLUS_UNITS
|
||||
val remainingUnits = Round.roundTo(bolusPulsesRemaining * PodConstants.POD_PULSE_BOLUS_UNITS, PodConstants.POD_PULSE_BOLUS_UNITS)
|
||||
this.bolusUnitsRemaining = remainingUnits
|
||||
if (remainingUnits == 0.0) {
|
||||
this.deliveryComplete = true
|
||||
|
@ -713,6 +721,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor(
|
|||
var timeZoneOffset: Int? = null
|
||||
var timeZoneUpdated: Long? = null
|
||||
var alarmSynced: Boolean = false
|
||||
var suspendAlertsEnabled: Boolean = false
|
||||
|
||||
var bleVersion: SoftwareVersion? = null
|
||||
var firmwareVersion: SoftwareVersion? = null
|
||||
|
|
|
@ -439,6 +439,10 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
|||
R.string.omnipod_common_alert_expiration_advisory
|
||||
AlertType.AUTO_OFF ->
|
||||
R.string.omnipod_common_alert_shutdown_imminent
|
||||
AlertType.SUSPEND_IN_PROGRESS ->
|
||||
R.string.omnipod_common_alert_delivery_suspended
|
||||
AlertType.SUSPEND_ENDED ->
|
||||
R.string.omnipod_common_alert_delivery_suspended
|
||||
else ->
|
||||
R.string.omnipod_common_alert_unknown_alert
|
||||
}
|
||||
|
@ -629,16 +633,8 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
|||
|
||||
private fun updateSuspendDeliveryButton() {
|
||||
// If the Pod is currently suspended, we show the Resume delivery button instead.
|
||||
if (isSuspendDeliveryButtonEnabled() &&
|
||||
podStateManager.isPodRunning &&
|
||||
(!podStateManager.isSuspended || commandQueue.isCustomCommandInQueue(CommandSuspendDelivery::class.java))
|
||||
) {
|
||||
buttonBinding.buttonSuspendDelivery.visibility = View.VISIBLE
|
||||
buttonBinding.buttonSuspendDelivery.isEnabled =
|
||||
podStateManager.isPodRunning && !podStateManager.isSuspended && isQueueEmpty()
|
||||
} else {
|
||||
buttonBinding.buttonSuspendDelivery.visibility = View.GONE
|
||||
}
|
||||
// disable the 'suspendDelivery' button.
|
||||
buttonBinding.buttonSuspendDelivery.visibility = View.GONE
|
||||
}
|
||||
|
||||
private fun updateSetTimeButton() {
|
||||
|
@ -654,10 +650,6 @@ class OmnipodDashOverviewFragment : DaggerFragment() {
|
|||
return sp.getBoolean(R.string.omnipod_common_preferences_automatically_silence_alerts, false)
|
||||
}
|
||||
|
||||
private fun isSuspendDeliveryButtonEnabled(): Boolean {
|
||||
return sp.getBoolean(R.string.key_omnipod_common_suspend_delivery_button_enabled, false)
|
||||
}
|
||||
|
||||
private fun displayErrorDialog(title: String, message: String, withSound: Boolean) {
|
||||
context?.let {
|
||||
ErrorHelperActivity.runAlarm(it, message, title, if (withSound) R.raw.boluserror else 0)
|
||||
|
|
|
@ -47,4 +47,5 @@
|
|||
<string name="omnipod_common_history_tbr_value">Rate: %1$.2f U, duration: %2$d minutes</string>
|
||||
<string name="omnipod_common_history_bolus_value">%1$.2f U</string>
|
||||
<string name="dash_bolusdelivering">Delivering %1$.2f U</string>
|
||||
<string name="omnipod_common_alert_delivery_suspended">Insulin delivery is suspended</string>
|
||||
</resources>
|
||||
|
|
|
@ -104,12 +104,7 @@
|
|||
android:key="@string/key_common_preferences_category_other_settings"
|
||||
android:title="@string/omnipod_common_preferences_category_other"
|
||||
app:initialExpandedChildrenCount="0">
|
||||
|
||||
<SwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="@string/key_omnipod_common_suspend_delivery_button_enabled"
|
||||
android:title="@string/omnipod_common_preferences_suspend_delivery_button_enabled" />
|
||||
|
||||
|
||||
<SwitchPreference
|
||||
android:defaultValue="true"
|
||||
android:key="@string/key_omnipod_common_time_change_event_enabled"
|
||||
|
|
|
@ -85,7 +85,7 @@ class ProgramAlertsCommandTest {
|
|||
false,
|
||||
AlertTrigger.TimerTrigger(4079.toShort()),
|
||||
BeepType.FOUR_TIMES_BIP_BEEP,
|
||||
BeepRepetitionType.XXX2
|
||||
BeepRepetitionType.EVERY_MINUTE_AND_EVERY_15_MIN
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.command
|
||||
|
||||
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BeepType
|
||||
import org.apache.commons.codec.DecoderException
|
||||
import org.apache.commons.codec.binary.Hex
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
class SuspendDeliveryCommandTest {
|
||||
@Test @Throws(DecoderException::class) fun testSuspendDelivery() {
|
||||
val encoded = SuspendDeliveryCommand.Builder()
|
||||
.setUniqueId(37879811)
|
||||
.setSequenceNumber(0.toShort())
|
||||
.setNonce(1229869870)
|
||||
.setBeepType(BeepType.LONG_SINGLE_BEEP)
|
||||
.build()
|
||||
.encoded
|
||||
Assert.assertArrayEquals(Hex.decodeHex("0242000300131f05494e532e67190a494e532e680000140302811f"), encoded)
|
||||
}
|
||||
}
|
|
@ -85,10 +85,11 @@ public class RileyLinkBLE {
|
|||
aapsLogger.debug(LTag.PUMPBTCOMM, "Response Count is " + ByteUtil.shortHexString(characteristic.getValue()));
|
||||
}
|
||||
}
|
||||
if (radioResponseCountNotified != null) {
|
||||
radioResponseCountNotified.run();
|
||||
if (characteristic.getUuid().equals(UUID.fromString(GattAttributes.CHARA_RADIO_RESPONSE_COUNT))) {
|
||||
if (radioResponseCountNotified != null) {
|
||||
radioResponseCountNotified.run();
|
||||
}
|
||||
}
|
||||
|
||||
orangeLink.onCharacteristicChanged(characteristic);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue