diff --git a/README.md b/README.md index bb32ee30d0..a656d723e1 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,16 @@ [![Support Server](https://img.shields.io/discord/629952586895851530.svg?label=Discord&logo=Discord&colorB=7289da&style=for-the-badge)](https://discord.gg/4fQUWHZ4Mw) -[![Build status](https://travis-ci.org/nightscout/AndroidAPS.svg?branch=master)](https://travis-ci.org/nightscout/AndroidAPS) +[![CircleCI](https://circleci.com/gh/nightscout/AndroidAPS/tree/master.svg?style=svg)](https://circleci.com/gh/nightscout/AndroidAPS/tree/master) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/androidaps/localized.svg)](https://translations.androidaps.org/project/androidaps) [![Documentation Status](https://readthedocs.org/projects/androidaps/badge/?version=latest)](https://androidaps.readthedocs.io/en/latest/?badge=latest) -[![codecov](https://codecov.io/gh/MilosKozak/AndroidAPS/branch/master/graph/badge.svg)](https://codecov.io/gh/MilosKozak/AndroidAPS) -dev: [![codecov](https://codecov.io/gh/MilosKozak/AndroidAPS/branch/dev/graph/badge.svg)](https://codecov.io/gh/MilosKozak/AndroidAPS) +[![codecov](https://codecov.io/gh/nightscout/AndroidAPS/branch/master/graph/badge.svg)](https://codecov.io/gh/MilosKozak/AndroidAPS) + +DEV: +[![CircleCI](https://circleci.com/gh/nightscout/AndroidAPS/tree/dev.svg?style=svg)](https://circleci.com/gh/nightscout/AndroidAPS/tree/dev) +[![codecov](https://codecov.io/gh/nightscout/AndroidAPS/branch/dev/graph/badge.svg)](https://codecov.io/gh/MilosKozak/AndroidAPS) -![BTC](https://bitit.io/assets/coins/icon-btc-1e5a37bc0eb730ac83130d7aa859052bd4b53ac3f86f99966627801f7b0410be.svg) 3KawK8aQe48478s6fxJ8Ms6VTWkwjgr9f2 +Bitcoin Icon + +3KawK8aQe48478s6fxJ8Ms6VTWkwjgr9f2 diff --git a/app/src/main/java/info/nightscout/androidaps/activities/ProfileHelperActivity.kt b/app/src/main/java/info/nightscout/androidaps/activities/ProfileHelperActivity.kt index 1f45202ab0..25fc86cf2e 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/ProfileHelperActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/ProfileHelperActivity.kt @@ -239,6 +239,10 @@ class ProfileHelperActivity : NoSplashAppCompatActivity() { } ToastUtils.showToastInUiThread(this, R.string.invalidinput) } + binding.age.editText?.id?.let { binding.ageLabel.labelFor = it } + binding.tdd.editText?.id?.let { binding.tddLabel.labelFor = it } + binding.weight.editText?.id?.let { binding.weightLabel.labelFor = it } + binding.basalpctfromtdd.editText?.id?.let { binding.basalpctfromtddLabel.labelFor = it } switchTab(0, typeSelected[0], false) } diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsBolusCarbsFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsBolusCarbsFragment.kt index e979e09104..da98848fcd 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsBolusCarbsFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsBolusCarbsFragment.kt @@ -271,6 +271,8 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { _binding = null } + private fun timestamp(ml: MealLink): Long = ml.bolusCalculatorResult?.let { it.timestamp } ?: ml.bolus?.let { it.timestamp } ?: ml.carbs?.let { it.timestamp } ?: 0L + inner class RecyclerViewAdapter internal constructor(var mealLinks: List) : RecyclerView.Adapter() { override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): MealLinkLoadedViewHolder = @@ -280,16 +282,20 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { val profile = profileFunction.getProfile() ?: return val ml = mealLinks[position] + val sameDayPrevious = position > 0 && dateUtil.isSameDay(timestamp(ml), timestamp(mealLinks[position - 1])) + holder.binding.date.visibility = sameDayPrevious.not().toVisibility() + holder.binding.date.text = dateUtil.dateString(timestamp(ml)) + // Metadata holder.binding.metadataLayout.visibility = (ml.bolusCalculatorResult != null && (ml.bolusCalculatorResult.isValid || binding.showInvalidated.isChecked)).toVisibility() ml.bolusCalculatorResult?.let { bolusCalculatorResult -> - holder.binding.date.text = dateUtil.dateAndTimeString(bolusCalculatorResult.timestamp) + holder.binding.calcTime.text = dateUtil.timeString(bolusCalculatorResult.timestamp) } // Bolus holder.binding.bolusLayout.visibility = (ml.bolus != null && (ml.bolus.isValid || binding.showInvalidated.isChecked)).toVisibility() ml.bolus?.let { bolus -> - holder.binding.bolusDate.text = dateUtil.timeString(bolus.timestamp) + holder.binding.bolusTime.text = dateUtil.timeString(bolus.timestamp) holder.binding.insulin.text = rh.gs(R.string.formatinsulinunits, bolus.amount) holder.binding.bolusNs.visibility = (bolus.interfaceIDs.nightscoutId != null).toVisibility() holder.binding.bolusPump.visibility = (bolus.interfaceIDs.pumpId != null).toVisibility() @@ -317,7 +323,7 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { // Carbs holder.binding.carbsLayout.visibility = (ml.carbs != null && (ml.carbs.isValid || binding.showInvalidated.isChecked)).toVisibility() ml.carbs?.let { carbs -> - holder.binding.carbsDate.text = dateUtil.timeString(carbs.timestamp) + holder.binding.carbsTime.text = dateUtil.timeString(carbs.timestamp) holder.binding.carbs.text = rh.gs(R.string.format_carbs, carbs.amount.toInt()) holder.binding.carbsDuration.text = if (carbs.duration > 0) rh.gs(R.string.format_mins, T.msecs(carbs.duration).mins().toInt()) else "" holder.binding.carbsNs.visibility = (carbs.interfaceIDs.nightscoutId != null).toVisibility() @@ -330,6 +336,8 @@ class TreatmentsBolusCarbsFragment : DaggerFragment() { holder.binding.bolusRemove.tag = ml holder.binding.carbsRemove.tag = ml holder.binding.calculation.tag = ml + val nextTimestamp = if (mealLinks.size != position + 1) timestamp(mealLinks[position + 1]) else 0L + holder.binding.delimiter.visibility = dateUtil.isSameDay(timestamp(ml), nextTimestamp).toVisibility() } override fun getItemCount(): Int { diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsCareportalFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsCareportalFragment.kt index ac4c2da1e2..db151a6e2e 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsCareportalFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsCareportalFragment.kt @@ -152,7 +152,7 @@ class TreatmentsCareportalFragment : DaggerFragment() { _binding = null } - inner class RecyclerViewAdapter internal constructor(private var list: List) : RecyclerView.Adapter() { + inner class RecyclerViewAdapter internal constructor(private var therapyList: List) : RecyclerView.Adapter() { override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): TherapyEventsViewHolder { val v = LayoutInflater.from(viewGroup.context).inflate(R.layout.treatments_careportal_item, viewGroup, false) @@ -160,18 +160,23 @@ class TreatmentsCareportalFragment : DaggerFragment() { } override fun onBindViewHolder(holder: TherapyEventsViewHolder, position: Int) { - val therapyEvent = list[position] + val therapyEvent = therapyList[position] holder.binding.ns.visibility = (therapyEvent.interfaceIDs.nightscoutId != null).toVisibility() holder.binding.invalid.visibility = therapyEvent.isValid.not().toVisibility() - holder.binding.date.text = dateUtil.dateAndTimeString(therapyEvent.timestamp) + val sameDayPrevious = position > 0 && dateUtil.isSameDay(therapyEvent.timestamp, therapyList[position - 1].timestamp) + holder.binding.date.visibility = sameDayPrevious.not().toVisibility() + holder.binding.date.text = dateUtil.dateString(therapyEvent.timestamp) + holder.binding.time.text = dateUtil.timeString(therapyEvent.timestamp) holder.binding.duration.text = if (therapyEvent.duration == 0L) "" else dateUtil.niceTimeScalar(therapyEvent.duration, rh) holder.binding.note.text = therapyEvent.note holder.binding.type.text = translator.translate(therapyEvent.type) holder.binding.remove.tag = therapyEvent + val nextTimestamp = if (therapyList.size != position + 1) therapyList[position + 1].timestamp else 0L + holder.binding.delimiter.visibility = dateUtil.isSameDay(therapyEvent.timestamp, nextTimestamp).toVisibility() } override fun getItemCount(): Int { - return list.size + return therapyList.size } inner class TherapyEventsViewHolder(view: View) : RecyclerView.ViewHolder(view) { diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsExtendedBolusesFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsExtendedBolusesFragment.kt index 59e6622b4c..af8a39fe16 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsExtendedBolusesFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsExtendedBolusesFragment.kt @@ -125,13 +125,16 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() { holder.binding.ns.visibility = (extendedBolus.interfaceIDs.nightscoutId != null).toVisibility() holder.binding.ph.visibility = (extendedBolus.interfaceIDs.pumpId != null).toVisibility() holder.binding.invalid.visibility = extendedBolus.isValid.not().toVisibility() + val sameDayPrevious = position > 0 && dateUtil.isSameDay(extendedBolus.timestamp, extendedBolusList[position-1].timestamp) + holder.binding.date.visibility = sameDayPrevious.not().toVisibility() + holder.binding.date.text = dateUtil.dateString(extendedBolus.timestamp) @SuppressLint("SetTextI18n") if (extendedBolus.isInProgress(dateUtil)) { - holder.binding.date.text = dateUtil.dateAndTimeString(extendedBolus.timestamp) - holder.binding.date.setTextColor(rh.gc(R.color.colorActive)) + holder.binding.time.text = dateUtil.timeString(extendedBolus.timestamp) + holder.binding.time.setTextColor(rh.gc(R.color.colorActive)) } else { - holder.binding.date.text = dateUtil.dateAndTimeString(extendedBolus.timestamp) + " - " + dateUtil.timeString(extendedBolus.end) - holder.binding.date.setTextColor(holder.binding.insulin.currentTextColor) + holder.binding.time.text = dateUtil.timeRangeString(extendedBolus.timestamp, extendedBolus.end) + holder.binding.time.setTextColor(holder.binding.insulin.currentTextColor) } val profile = profileFunction.getProfile(extendedBolus.timestamp) ?: return holder.binding.duration.text = rh.gs(R.string.format_mins, T.msecs(extendedBolus.duration).mins()) @@ -141,6 +144,8 @@ class TreatmentsExtendedBolusesFragment : DaggerFragment() { holder.binding.ratio.text = rh.gs(R.string.pump_basebasalrate, extendedBolus.rate) if (iob.iob != 0.0) holder.binding.iob.setTextColor(rh.gc(R.color.colorActive)) else holder.binding.iob.setTextColor(holder.binding.insulin.currentTextColor) holder.binding.remove.tag = extendedBolus + val nextTimestamp = if (extendedBolusList.size != position + 1) extendedBolusList[position + 1].timestamp else 0L + holder.binding.delimiter.visibility = dateUtil.isSameDay(extendedBolus.timestamp, nextTimestamp).toVisibility() } override fun getItemCount(): Int = extendedBolusList.size diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsProfileSwitchFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsProfileSwitchFragment.kt index 20b8070583..67a2cc512a 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsProfileSwitchFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsProfileSwitchFragment.kt @@ -181,7 +181,10 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { val profileSwitch = profileSwitchList[position] holder.binding.ph.visibility = (profileSwitch is ProfileSealed.EPS).toVisibility() holder.binding.ns.visibility = (profileSwitch.interfaceIDs_backing?.nightscoutId != null).toVisibility() - holder.binding.date.text = dateUtil.dateAndTimeString(profileSwitch.timestamp) + val sameDayPrevious = position > 0 && dateUtil.isSameDay(profileSwitch.timestamp, profileSwitchList[position - 1].timestamp) + holder.binding.date.visibility = sameDayPrevious.not().toVisibility() + holder.binding.date.text = dateUtil.dateString(profileSwitch.timestamp) + holder.binding.time.text = dateUtil.timeString(profileSwitch.timestamp) holder.binding.duration.text = rh.gs(R.string.format_mins, T.msecs(profileSwitch.duration ?: 0L).mins()) holder.binding.name.text = if (profileSwitch is ProfileSealed.PS) profileSwitch.value.getCustomizedName() else if (profileSwitch is ProfileSealed.EPS) profileSwitch.value.originalCustomizedName else "" if (profileSwitch.isInProgress(dateUtil)) holder.binding.date.setTextColor(rh.gc(R.color.colorActive)) @@ -196,6 +199,8 @@ class TreatmentsProfileSwitchFragment : DaggerFragment() { holder.binding.clone.visibility = (profileSwitch is ProfileSealed.PS).toVisibility() holder.binding.spacer.visibility = (profileSwitch is ProfileSealed.PS).toVisibility() holder.binding.root.setBackgroundColor(rh.gc(if (profileSwitch is ProfileSealed.PS) R.color.defaultbackground else R.color.list_delimiter)) + val nextTimestamp = if (profileSwitchList.size != position + 1) profileSwitchList[position + 1].timestamp else 0L + holder.binding.delimiter.visibility = dateUtil.isSameDay(profileSwitch.timestamp, nextTimestamp).toVisibility() } override fun getItemCount(): Int { diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTempTargetFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTempTargetFragment.kt index b227f667df..ec01cc7b3a 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTempTargetFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTempTargetFragment.kt @@ -164,18 +164,23 @@ class TreatmentsTempTargetFragment : DaggerFragment() { holder.binding.ns.visibility = (tempTarget.interfaceIDs.nightscoutId != null).toVisibility() holder.binding.invalid.visibility = tempTarget.isValid.not().toVisibility() holder.binding.remove.visibility = tempTarget.isValid.toVisibility() - holder.binding.date.text = dateUtil.dateAndTimeString(tempTarget.timestamp) + " - " + dateUtil.timeString(tempTarget.end) + val sameDayPrevious = position > 0 && dateUtil.isSameDay(tempTarget.timestamp, tempTargetList[position-1].timestamp) + holder.binding.date.visibility = sameDayPrevious.not().toVisibility() + holder.binding.date.text = dateUtil.dateString(tempTarget.timestamp) + holder.binding.time.text = dateUtil.timeRangeString(tempTarget.timestamp, tempTarget.end) holder.binding.duration.text = rh.gs(R.string.format_mins, T.msecs(tempTarget.duration).mins()) holder.binding.low.text = tempTarget.lowValueToUnitsToString(units) holder.binding.high.text = tempTarget.highValueToUnitsToString(units) holder.binding.reason.text = translator.translate(tempTarget.reason) - holder.binding.date.setTextColor( + holder.binding.time.setTextColor( when { tempTarget.id == currentlyActiveTarget?.id -> rh.gc(R.color.colorActive) tempTarget.timestamp > dateUtil.now() -> rh.gc(R.color.colorScheduled) else -> holder.binding.reasonColon.currentTextColor }) holder.binding.remove.tag = tempTarget + val nextTimestamp = if (tempTargetList.size != position + 1) tempTargetList[position + 1].timestamp else 0L + holder.binding.delimiter.visibility = dateUtil.isSameDay(tempTarget.timestamp, nextTimestamp).toVisibility() } override fun getItemCount(): Int = tempTargetList.size diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTemporaryBasalsFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTemporaryBasalsFragment.kt index 4cb88c4290..75c0b3cc19 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTemporaryBasalsFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsTemporaryBasalsFragment.kt @@ -15,7 +15,6 @@ import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.ValueWrapper import info.nightscout.androidaps.database.entities.ExtendedBolus import info.nightscout.androidaps.database.entities.TemporaryBasal -import info.nightscout.androidaps.database.entities.UserEntry.* import info.nightscout.androidaps.database.entities.UserEntry.Action import info.nightscout.androidaps.database.entities.UserEntry.Sources import info.nightscout.androidaps.database.entities.ValueWithUnit @@ -165,12 +164,15 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { holder.binding.ns.visibility = (tempBasal.interfaceIDs.nightscoutId != null).toVisibility() holder.binding.invalid.visibility = tempBasal.isValid.not().toVisibility() holder.binding.ph.visibility = (tempBasal.interfaceIDs.pumpId != null).toVisibility() + val sameDayPrevious = position > 0 && dateUtil.isSameDay(tempBasal.timestamp, tempBasalList[position-1].timestamp) + holder.binding.date.visibility = sameDayPrevious.not().toVisibility() + holder.binding.date.text = dateUtil.dateString(tempBasal.timestamp) if (tempBasal.isInProgress) { - holder.binding.date.text = dateUtil.dateAndTimeString(tempBasal.timestamp) - holder.binding.date.setTextColor(rh.gc(R.color.colorActive)) + holder.binding.time.text = dateUtil.timeString(tempBasal.timestamp) + holder.binding.time.setTextColor(rh.gc(R.color.colorActive)) } else { - holder.binding.date.text = dateUtil.dateAndTimeRangeString(tempBasal.timestamp, tempBasal.end) - holder.binding.date.setTextColor(holder.binding.duration.currentTextColor) + holder.binding.time.text = dateUtil.timeRangeString(tempBasal.timestamp, tempBasal.end) + holder.binding.time.setTextColor(holder.binding.duration.currentTextColor) } holder.binding.duration.text = rh.gs(R.string.format_mins, T.msecs(tempBasal.duration).mins()) if (tempBasal.isAbsolute) holder.binding.rate.text = rh.gs(R.string.pump_basebasalrate, tempBasal.rate) @@ -186,6 +188,9 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() { holder.binding.superBolusFlag.visibility = (tempBasal.type == TemporaryBasal.Type.SUPERBOLUS).toVisibility() if (abs(iob.basaliob) > 0.01) holder.binding.iob.setTextColor(rh.gc(R.color.colorActive)) else holder.binding.iob.setTextColor(holder.binding.duration.currentTextColor) holder.binding.remove.tag = tempBasal + + val nextTimestamp = if (tempBasalList.size != position + 1) tempBasalList[position + 1].timestamp else 0L + holder.binding.delimiter.visibility = dateUtil.isSameDay(tempBasal.timestamp, nextTimestamp).toVisibility() } override fun getItemCount(): Int = tempBasalList.size diff --git a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsUserEntryFragment.kt b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsUserEntryFragment.kt index ad3d7bde20..f0fcf32180 100644 --- a/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsUserEntryFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/activities/fragments/TreatmentsUserEntryFragment.kt @@ -20,6 +20,7 @@ import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.events.EventTreatmentUpdateGui +import info.nightscout.androidaps.extensions.toVisibility import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.T @@ -131,14 +132,19 @@ class TreatmentsUserEntryFragment : DaggerFragment() { override fun onBindViewHolder(holder: UserEntryViewHolder, position: Int) { val current = entries[position] - holder.binding.date.text = dateUtil.dateAndTimeAndSecondsString(current.timestamp) + val sameDayPrevious = position > 0 && dateUtil.isSameDay(current.timestamp, entries[position-1].timestamp) + holder.binding.date.visibility = sameDayPrevious.not().toVisibility() + holder.binding.date.text = dateUtil.dateString(current.timestamp) + holder.binding.time.text = dateUtil.timeStringWithSeconds(current.timestamp) holder.binding.action.text = userEntryPresentationHelper.actionToColoredString(current.action) - holder.binding.s.text = current.note - holder.binding.s.visibility = if (current.note != "") View.VISIBLE else View.GONE + holder.binding.notes.text = current.note + holder.binding.notes.visibility = if (current.note != "") View.VISIBLE else View.GONE holder.binding.iconSource.setImageResource(userEntryPresentationHelper.iconId(current.source)) holder.binding.iconSource.visibility = View.VISIBLE holder.binding.values.text = userEntryPresentationHelper.listToPresentationString(current.values) holder.binding.values.visibility = if (holder.binding.values.text != "") View.VISIBLE else View.GONE + val nextTimestamp = if (entries.size != position + 1) entries[position + 1].timestamp else 0L + holder.binding.delimiter.visibility = dateUtil.isSameDay(current.timestamp, nextTimestamp).toVisibility() } inner class UserEntryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/CalibrationDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/CalibrationDialog.kt index 35f387144c..bd5daaf965 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/CalibrationDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/CalibrationDialog.kt @@ -64,6 +64,7 @@ class CalibrationDialog : DialogFragmentWithDate() { binding.bg.setParams(savedInstanceState?.getDouble("bg") ?: bg, 36.0, 500.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok) binding.units.text = if (units == GlucoseUnit.MMOL) rh.gs(R.string.mmol) else rh.gs(R.string.mgdl) + binding.bg.editText?.id?.let { binding.bgLabel.labelFor = it } } override fun onDestroyView() { diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt index aeb5fc52a5..f073e122f0 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/CarbsDialog.kt @@ -137,32 +137,39 @@ class CarbsDialog : DialogFragmentWithDate() { savedInstanceState?.getDouble("carbs") ?: 0.0, 0.0, maxCarbs, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher ) - - binding.plus1.text = toSignedString(sp.getInt(R.string.key_carbs_button_increment_1, FAV1_DEFAULT)) + val plus1text = toSignedString(sp.getInt(R.string.key_carbs_button_increment_1, FAV1_DEFAULT)) + binding.plus1.text = plus1text + binding.plus1.contentDescription = rh.gs(R.string.treatments_wizard_carbs_label) + " " + plus1text binding.plus1.setOnClickListener { binding.carbs.value = max( 0.0, binding.carbs.value + sp.getInt(R.string.key_carbs_button_increment_1, FAV1_DEFAULT) ) validateInputs() + binding.carbs.announceValue() } - binding.plus2.text = toSignedString(sp.getInt(R.string.key_carbs_button_increment_2, FAV2_DEFAULT)) + val plus2text = toSignedString(sp.getInt(R.string.key_carbs_button_increment_2, FAV2_DEFAULT)) + binding.plus2.text = plus2text + binding.plus2.contentDescription = rh.gs(R.string.treatments_wizard_carbs_label) + " " + plus2text binding.plus2.setOnClickListener { binding.carbs.value = max( 0.0, binding.carbs.value + sp.getInt(R.string.key_carbs_button_increment_2, FAV2_DEFAULT) ) validateInputs() + binding.carbs.announceValue() } - - binding.plus3.text = toSignedString(sp.getInt(R.string.key_carbs_button_increment_3, FAV3_DEFAULT)) + val plus3text = toSignedString(sp.getInt(R.string.key_carbs_button_increment_3, FAV3_DEFAULT)) + binding.plus3.text = plus3text + binding.plus2.contentDescription = rh.gs(R.string.treatments_wizard_carbs_label) + " " + plus3text binding.plus3.setOnClickListener { binding.carbs.value = max( 0.0, binding.carbs.value + sp.getInt(R.string.key_carbs_button_increment_3, FAV3_DEFAULT) ) validateInputs() + binding.carbs.announceValue() } setOnValueChangedListener { eventTime: Long -> @@ -188,6 +195,9 @@ class CarbsDialog : DialogFragmentWithDate() { binding.hypoTt.isChecked = false binding.activityTt.isChecked = false } + binding.duration.editText?.id?.let { binding.durationLabel.labelFor = it } + binding.time.editText?.id?.let { binding.timeLabel.labelFor = it } + binding.carbs.editText?.id?.let { binding.carbsLabel.labelFor = it } } override fun onDestroyView() { diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/CareDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/CareDialog.kt index 0006ce0cbe..03cb215a7f 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/CareDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/CareDialog.kt @@ -165,6 +165,8 @@ class CareDialog : DialogFragmentWithDate() { ?: 0.0, 0.0, Constants.MAX_PROFILE_SWITCH_DURATION, 10.0, DecimalFormat("0"), false, binding.okcancel.ok) if (options == EventType.NOTE || options == EventType.QUESTION || options == EventType.ANNOUNCEMENT || options == EventType.EXERCISE) binding.notesLayout.root.visibility = View.VISIBLE // independent to preferences + binding.bg.editText?.id?.let { binding.bgLabel.labelFor = it } + binding.duration.editText?.id?.let { binding.durationLabel.labelFor = it } } override fun onDestroyView() { diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/ExtendedBolusDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/ExtendedBolusDialog.kt index 4c86819465..7205a36939 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/ExtendedBolusDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/ExtendedBolusDialog.kt @@ -70,6 +70,8 @@ class ExtendedBolusDialog : DialogFragmentWithDate() { val extendedMaxDuration = pumpDescription.extendedBolusMaxDuration binding.duration.setParams(savedInstanceState?.getDouble("duration") ?: extendedDurationStep, extendedDurationStep, extendedMaxDuration, extendedDurationStep, DecimalFormat("0"), false, binding.okcancel.ok) + binding.insulin.editText?.id?.let { binding.insulinLabel.labelFor = it } + binding.duration.editText?.id?.let { binding.durationLabel.labelFor = it } } override fun onDestroyView() { diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/FillDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/FillDialog.kt index b7423dc564..54b222ce2c 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/FillDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/FillDialog.kt @@ -96,7 +96,7 @@ class FillDialog : DialogFragmentWithDate() { } else { binding.fillPresetButton3.visibility = View.GONE } - + binding.fillInsulinamount.editText?.id?.let { binding.fillLabel.labelFor = it } } override fun onDestroyView() { diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt index d35446e818..21eb81ebce 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/InsulinDialog.kt @@ -116,29 +116,40 @@ class InsulinDialog : DialogFragmentWithDate() { binding.amount.setParams(savedInstanceState?.getDouble("amount") ?: 0.0, 0.0, maxInsulin, activePlugin.activePump.pumpDescription.bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, binding.okcancel.ok, textWatcher) - binding.plus05.text = sp.getDouble(rh.gs(R.string.key_insulin_button_increment_1), PLUS1_DEFAULT).toSignedString(activePlugin.activePump) + val plus05Text = sp.getDouble(rh.gs(R.string.key_insulin_button_increment_1), PLUS1_DEFAULT).toSignedString(activePlugin.activePump) + binding.plus05.text = plus05Text + binding.plus05.contentDescription = rh.gs(R.string.overview_insulin_label) + " " + plus05Text binding.plus05.setOnClickListener { binding.amount.value = max(0.0, binding.amount.value + sp.getDouble(rh.gs(R.string.key_insulin_button_increment_1), PLUS1_DEFAULT)) validateInputs() + binding.amount.announceValue() } - binding.plus10.text = sp.getDouble(rh.gs(R.string.key_insulin_button_increment_2), PLUS2_DEFAULT).toSignedString(activePlugin.activePump) + val plus10Text = sp.getDouble(rh.gs(R.string.key_insulin_button_increment_2), PLUS2_DEFAULT).toSignedString(activePlugin.activePump) + binding.plus10.text = plus10Text + binding.plus10.contentDescription = rh.gs(R.string.overview_insulin_label) + " " + plus10Text binding.plus10.setOnClickListener { binding.amount.value = max(0.0, binding.amount.value + sp.getDouble(rh.gs(R.string.key_insulin_button_increment_2), PLUS2_DEFAULT)) validateInputs() + binding.amount.announceValue() } - binding.plus20.text = sp.getDouble(rh.gs(R.string.key_insulin_button_increment_3), PLUS3_DEFAULT).toSignedString(activePlugin.activePump) + val plus20Text = sp.getDouble(rh.gs(R.string.key_insulin_button_increment_3), PLUS3_DEFAULT).toSignedString(activePlugin.activePump) + binding.plus20.text = plus20Text + binding.plus20.contentDescription = rh.gs(R.string.overview_insulin_label) + " " + plus20Text binding.plus20.setOnClickListener { binding.amount.value = max(0.0, binding.amount.value + sp.getDouble(rh.gs(R.string.key_insulin_button_increment_3), PLUS3_DEFAULT)) validateInputs() + binding.amount.announceValue() } binding.timeLayout.visibility = View.GONE binding.recordOnly.setOnCheckedChangeListener { _, isChecked: Boolean -> binding.timeLayout.visibility = isChecked.toVisibility() } + binding.amount.editText?.id?.let { binding.insulinLabel.labelFor = it } + binding.time.editText?.id?.let { binding.timeLabel.labelFor = it } } override fun onDestroyView() { diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt index c869c22e6d..c64539af14 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/ProfileSwitchDialog.kt @@ -148,6 +148,9 @@ class ProfileSwitchDialog : DialogFragmentWithDate() { } } binding.ttLayout.visibility = View.GONE + binding.duration.editText?.id?.let { binding.durationLabel.labelFor = it } + binding.percentage.editText?.id?.let { binding.percentageLabel.labelFor = it } + binding.timeshift.editText?.id?.let { binding.timeshiftLabel.labelFor = it } } override fun onDestroyView() { diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/TempBasalDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/TempBasalDialog.kt index 65ee820eeb..d458055b9d 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/TempBasalDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/TempBasalDialog.kt @@ -86,6 +86,9 @@ class TempBasalDialog : DialogFragmentWithDate() { binding.percentLayout.visibility = View.GONE binding.absoluteLayout.visibility = View.VISIBLE } + binding.basalPercentInput.editText?.id?.let { binding.basalPercentLabel.labelFor = it } + binding.basalAbsoluteInput.editText?.id?.let { binding.basalAbsoluteLabel.labelFor = it } + binding.duration.editText?.id?.let { binding.durationLabel.labelFor = it } } override fun onDestroyView() { diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/TempTargetDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/TempTargetDialog.kt index 54801c723d..c1ed8faf5b 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/TempTargetDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/TempTargetDialog.kt @@ -120,6 +120,8 @@ class TempTargetDialog : DialogFragmentWithDate() { longClick(it) return@setOnLongClickListener true } + binding.duration.editText?.id?.let { binding.durationLabel.labelFor = it } + binding.temptarget.editText?.id?.let { binding.temptargetLabel.labelFor = it } } } diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/TreatmentDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/TreatmentDialog.kt index ac88f7e074..0e8a509517 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/TreatmentDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/TreatmentDialog.kt @@ -106,6 +106,8 @@ class TreatmentDialog : DialogFragmentWithDate() { binding.insulin.setParams(savedInstanceState?.getDouble("insulin") ?: 0.0, 0.0, maxInsulin, pumpDescription.bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, binding.okcancel.ok, textWatcher) binding.recordOnlyLayout.visibility = View.GONE + binding.insulin.editText?.id?.let { binding.insulinLabel.labelFor = it } + binding.carbs.editText?.id?.let { binding.carbsLabel.labelFor = it } } override fun onDestroyView() { diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt index 3fce025b61..b7f943a313 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/WizardDialog.kt @@ -4,11 +4,7 @@ import android.content.Context import android.os.Bundle import android.text.Editable import android.text.TextWatcher -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.view.Window -import android.view.WindowManager +import android.view.* import android.widget.AdapterView import android.widget.AdapterView.OnItemSelectedListener import android.widget.ArrayAdapter @@ -136,33 +132,36 @@ class WizardDialog : DaggerDialogFragment() { val maxCorrection = constraintChecker.getMaxBolusAllowed().value() bolusStep = activePlugin.activePump.pumpDescription.bolusStep - if (profileFunction.getUnits() == GlucoseUnit.MGDL) - binding.bgInput.setParams(savedInstanceState?.getDouble("bg_input") - ?: 0.0, 0.0, 500.0, 1.0, DecimalFormat("0"), false, binding.ok, timeTextWatcher) - else - binding.bgInput.setParams(savedInstanceState?.getDouble("bg_input") - ?: 0.0, 0.0, 30.0, 0.1, DecimalFormat("0.0"), false, binding.ok, textWatcher) + if (profileFunction.getUnits() == GlucoseUnit.MGDL) { + binding.bgInput.setParams( + savedInstanceState?.getDouble("bg_input") + ?: 0.0, 0.0, 500.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, timeTextWatcher) + } else { + binding.bgInput.setParams( + savedInstanceState?.getDouble("bg_input") + ?: 0.0, 0.0, 30.0, 0.1, DecimalFormat("0.0"), false, binding.okcancel.ok, textWatcher) + } binding.carbsInput.setParams(savedInstanceState?.getDouble("carbs_input") - ?: 0.0, 0.0, maxCarbs.toDouble(), 1.0, DecimalFormat("0"), false, binding.ok, textWatcher) + ?: 0.0, 0.0, maxCarbs.toDouble(), 1.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher) if (correctionPercent) { calculatedPercentage = sp.getInt(R.string.key_boluswizard_percentage, 100).toDouble() - binding.correctionInput.setParams(calculatedPercentage, 10.0, 200.0, 1.0, DecimalFormat("0"), false, binding.ok, textWatcher) + binding.correctionInput.setParams(calculatedPercentage, 10.0, 200.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher) binding.correctionInput.value = calculatedPercentage binding.correctionUnit.text = "%" } else { binding.correctionInput.setParams( savedInstanceState?.getDouble("correction_input") - ?: 0.0, -maxCorrection, maxCorrection, bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, binding.ok, textWatcher) + ?: 0.0, -maxCorrection, maxCorrection, bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, binding.okcancel.ok, textWatcher) binding.correctionUnit.text = rh.gs(R.string.insulin_unit_shortname) } binding.carbTimeInput.setParams(savedInstanceState?.getDouble("carb_time_input") - ?: 0.0, -60.0, 60.0, 5.0, DecimalFormat("0"), false, binding.ok, timeTextWatcher) + ?: 0.0, -60.0, 60.0, 5.0, DecimalFormat("0"), false, binding.okcancel.ok, timeTextWatcher) initDialog() calculatedPercentage = sp.getInt(R.string.key_boluswizard_percentage, 100).toDouble() binding.percentUsed.text = rh.gs(R.string.format_percent, sp.getInt(R.string.key_boluswizard_percentage, 100)) // ok button - binding.ok.setOnClickListener { + binding.okcancel.ok.setOnClickListener { if (okClicked) { aapsLogger.debug(LTag.UI, "guarding: ok already clicked") } else { @@ -175,12 +174,12 @@ class WizardDialog : DaggerDialogFragment() { } dismiss() } - binding.bgEnabledIcon.setOnClickListener { binding.bgCheckbox.isChecked = !binding.bgCheckbox.isChecked } - binding.trendEnabledIcon.setOnClickListener { binding.bgTrendCheckbox.isChecked = !binding.bgTrendCheckbox.isChecked } - binding.cobEnabledIcon.setOnClickListener { binding.cobCheckbox.isChecked = !binding.cobCheckbox.isChecked; processCobCheckBox(); } - binding.iobEnabledIcon.setOnClickListener { if (!binding.cobCheckbox.isChecked) binding.iobCheckbox.isChecked = !binding.iobCheckbox.isChecked } + binding.bgCheckboxIcon.setOnClickListener { binding.bgCheckbox.isChecked = !binding.bgCheckbox.isChecked } + binding.trendCheckboxIcon.setOnClickListener { binding.bgTrendCheckbox.isChecked = !binding.bgTrendCheckbox.isChecked } + binding.cobCheckboxIcon.setOnClickListener { binding.cobCheckbox.isChecked = !binding.cobCheckbox.isChecked; processCobCheckBox(); } + binding.iobCheckboxIcon.setOnClickListener { if (!binding.cobCheckbox.isChecked) binding.iobCheckbox.isChecked = !binding.iobCheckbox.isChecked } // cancel button - binding.cancel.setOnClickListener { + binding.okcancel.cancel.setOnClickListener { aapsLogger.debug(LTag.APS, "Dialog canceled: ${this.javaClass.name}") dismiss() } @@ -212,11 +211,17 @@ class WizardDialog : DaggerDialogFragment() { sp.putBoolean(rh.gs(R.string.key_wizard_correction_percent), isChecked) binding.correctionUnit.text = if (isChecked) "%" else rh.gs(R.string.insulin_unit_shortname) correctionPercent = binding.correctionPercent.isChecked - if (correctionPercent) - binding.correctionInput.setParams(calculatedPercentage, 10.0, 200.0, 1.0, DecimalFormat("0"), false, binding.ok, textWatcher) - else - binding.correctionInput.setParams(savedInstanceState?.getDouble("correction_input") - ?: 0.0, -maxCorrection, maxCorrection, bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, binding.ok, textWatcher) + if (correctionPercent) { + binding.correctionInput.setParams(calculatedPercentage, 10.0, 200.0, 1.0, DecimalFormat("0"), false, binding.okcancel.ok, textWatcher) + binding.correctionInput.customContentDescription = rh.gs(R.string.a11_correction_percentage) + } else { + binding.correctionInput.setParams( + savedInstanceState?.getDouble("correction_input") + ?: 0.0, -maxCorrection, maxCorrection, bolusStep, DecimalFormatter.pumpSupportedBolusFormat(activePlugin.activePump), false, binding.okcancel.ok, textWatcher + ) + binding.correctionInput.customContentDescription = rh.gs(R.string.a11_correction_units) + } + binding.correctionInput.updateA11yDescription() binding.correctionInput.value = if (correctionPercent) calculatedPercentage else Round.roundTo(calculatedCorrection, bolusStep) } } @@ -224,12 +229,12 @@ class WizardDialog : DaggerDialogFragment() { binding.profile.onItemSelectedListener = object : OnItemSelectedListener { override fun onNothingSelected(parent: AdapterView<*>?) { ToastUtils.showToastInUiThread(ctx, rh.gs(R.string.noprofileset)) - binding.ok.visibility = View.GONE + binding.okcancel.ok.visibility = View.GONE } override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { calculateInsulin() - binding.ok.visibility = View.VISIBLE + binding.okcancel.ok.visibility = View.VISIBLE } } // bus @@ -241,6 +246,14 @@ class WizardDialog : DaggerDialogFragment() { }, fabricPrivacy::logException) ) + setA11yLabels() + } + + private fun setA11yLabels() { + binding.bgInput.editText?.id?.let { binding.bgInputLabel.labelFor = it } + binding.carbsInput.editText?.id?.let { binding.carbsInputLabel.labelFor = it } + binding.correctionInput.editText?.id?.let { binding.correctionInputLabel.labelFor = it } + binding.carbTimeInput.editText?.id?.let { binding.carbTimeInputLabel.labelFor = it } } override fun onDestroyView() { @@ -261,21 +274,29 @@ class WizardDialog : DaggerDialogFragment() { private fun processCobCheckBox() { if (binding.cobCheckbox.isChecked) { binding.iobCheckbox.isEnabled = false + binding.iobCheckboxIcon.isEnabled = false binding.iobCheckbox.isChecked = true } else { binding.iobCheckbox.isEnabled = true + binding.iobCheckboxIcon.isEnabled = true } } private fun processEnabledIcons() { - binding.bgEnabledIcon.alpha = if (binding.bgCheckbox.isChecked) 1.0f else 0.2f - binding.trendEnabledIcon.alpha = if (binding.bgTrendCheckbox.isChecked) 1.0f else 0.2f - binding.iobEnabledIcon.alpha = if (binding.iobCheckbox.isChecked) 1.0f else 0.2f - binding.cobEnabledIcon.alpha = if (binding.cobCheckbox.isChecked) 1.0f else 0.2f - binding.bgEnabledIcon.visibility = binding.calculationCheckbox.isChecked.not().toVisibility() - binding.trendEnabledIcon.visibility = binding.calculationCheckbox.isChecked.not().toVisibility() - binding.iobEnabledIcon.visibility = binding.calculationCheckbox.isChecked.not().toVisibility() - binding.cobEnabledIcon.visibility = binding.calculationCheckbox.isChecked.not().toVisibility() + binding.bgCheckboxIcon.isChecked = binding.bgCheckbox.isChecked + binding.trendCheckboxIcon.isChecked = binding.bgTrendCheckbox.isChecked + binding.iobCheckboxIcon.isChecked = binding.iobCheckbox.isChecked + binding.cobCheckboxIcon.isChecked = binding.cobCheckbox.isChecked + + binding.bgCheckboxIcon.alpha = if (binding.bgCheckbox.isChecked) 1.0f else 0.2f + binding.trendCheckboxIcon.alpha = if (binding.bgTrendCheckbox.isChecked) 1.0f else 0.2f + binding.iobCheckboxIcon.alpha = if (binding.iobCheckbox.isChecked) 1.0f else 0.2f + binding.cobCheckboxIcon.alpha = if (binding.cobCheckbox.isChecked) 1.0f else 0.2f + + binding.bgCheckboxIcon.visibility = binding.calculationCheckbox.isChecked.not().toVisibility() + binding.trendCheckboxIcon.visibility = binding.calculationCheckbox.isChecked.not().toVisibility() + binding.iobCheckboxIcon.visibility = binding.calculationCheckbox.isChecked.not().toVisibility() + binding.cobCheckboxIcon.visibility = binding.calculationCheckbox.isChecked.not().toVisibility() } private fun saveCheckedStates() { @@ -442,10 +463,10 @@ class WizardDialog : DaggerDialogFragment() { val insulinText = if (wizard.calculatedTotalInsulin > 0.0) rh.gs(R.string.formatinsulinunits, wizard.calculatedTotalInsulin).formatColor(rh, R.color.bolus) else "" val carbsText = if (carbsAfterConstraint > 0.0) rh.gs(R.string.format_carbs, carbsAfterConstraint).formatColor(rh, R.color.carbs) else "" binding.total.text = HtmlHelper.fromHtml(rh.gs(R.string.result_insulin_carbs, insulinText, carbsText)) - binding.ok.visibility = View.VISIBLE + binding.okcancel.ok.visibility = View.VISIBLE } else { binding.total.text = HtmlHelper.fromHtml(rh.gs(R.string.missing_carbs, wizard.carbsEquivalent.toInt()).formatColor(rh, R.color.carbs)) - binding.ok.visibility = View.INVISIBLE + binding.okcancel.ok.visibility = View.INVISIBLE } binding.percentUsed.text = rh.gs(R.string.format_percent, wizard.percentageCorrection) calculatedPercentage = wizard.calculatedPercentage diff --git a/app/src/main/java/info/nightscout/androidaps/dialogs/WizardInfoDialog.kt b/app/src/main/java/info/nightscout/androidaps/dialogs/WizardInfoDialog.kt index 6a6aaf32fa..37a653f5e5 100644 --- a/app/src/main/java/info/nightscout/androidaps/dialogs/WizardInfoDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/dialogs/WizardInfoDialog.kt @@ -69,13 +69,15 @@ class WizardInfoDialog : DaggerDialogFragment() { binding.close.setOnClickListener { dismiss() } val units = profileFunction.getUnits() val bgString = Profile.toUnitsString(data.glucoseValue, data.glucoseValue * Constants.MGDL_TO_MMOLL, units) + val isf = Profile.toUnits(data.isf, data.isf * Constants.MGDL_TO_MMOLL, units) + val trend = Profile.toUnitsString(data.glucoseTrend * 3, data.glucoseTrend * 3 * Constants.MGDL_TO_MMOLL, units) // BG - binding.bg.text = rh.gs(R.string.format_bg_isf, bgString, data.isf) + binding.bg.text = rh.gs(R.string.format_bg_isf, bgString, isf) binding.bgInsulin.text = rh.gs(R.string.formatinsulinunits, data.glucoseInsulin) binding.bgCheckbox.isChecked = data.wasGlucoseUsed binding.ttCheckbox.isChecked = data.wasTempTargetUsed // Trend - binding.bgTrend.text = DecimalFormatter.to1Decimal(data.glucoseTrend) + binding.bgTrend.text = trend binding.bgTrendInsulin.text = rh.gs(R.string.formatinsulinunits, data.trendInsulin) binding.bgTrendCheckbox.isChecked = data.wasTrendUsed // COB diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/bgQualityCheck/BgQualityCheckPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/bgQualityCheck/BgQualityCheckPlugin.kt index 73e42b78d7..800994fe03 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/constraints/bgQualityCheck/BgQualityCheckPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/constraints/bgQualityCheck/BgQualityCheckPlugin.kt @@ -102,4 +102,11 @@ class BgQualityCheckPlugin @Inject constructor( State.RECALCULATED -> R.drawable.ic_baseline_warning_24_yellow State.DOUBLED -> R.drawable.ic_baseline_warning_24_red } + + fun stateDescription(): String = + when (state) { + State.RECALCULATED -> rh.gs(R.string.a11y_bg_quality_recalculated) + State.DOUBLED -> rh.gs(R.string.a11y_bg_quality_doubles) + else -> "" + } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsFragment.kt index 60434c77ae..dc5805cfd1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/actions/ActionsFragment.kt @@ -107,6 +107,7 @@ class ActionsFragment : DaggerFragment() { private var sensorLevelLabel: TextView? = null private var insulinLevelLabel: TextView? = null private var pbLevelLabel: TextView? = null + private var cannulaOrPatch: TextView? = null override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -153,6 +154,7 @@ class ActionsFragment : DaggerFragment() { sensorLevelLabel = view.findViewById(R.id.sensor_level_label) insulinLevelLabel = view.findViewById(R.id.insulin_level_label) pbLevelLabel = view.findViewById(R.id.pb_level_label) + cannulaOrPatch = view.findViewById(R.id.cannula_or_patch) profileSwitch?.setOnClickListener { ProfileSwitchDialog().show(childFragmentManager, "ProfileSwitchDialog") @@ -319,6 +321,10 @@ class ActionsFragment : DaggerFragment() { tempTarget?.visibility = (profile != null && !loop.isDisconnected).toVisibility() tddStats?.visibility = pump.pumpDescription.supportsTDDs.toVisibility() + cannulaOrPatch?.text = if (pump.pumpDescription.isPatchPump) rh.gs(R.string.patch_pump) else rh.gs(R.string.cannula) + val imageResource = if (pump.pumpDescription.isPatchPump) R.drawable.ic_patch_pump_outline else R.drawable.ic_cp_age_cannula + cannulaOrPatch?.setCompoundDrawablesWithIntrinsicBounds(imageResource, 0, 0, 0) + if (!config.NSCLIENT) { statusLightHandler.updateStatusLights(cannulaAge, insulinAge, reservoirLevel, sensorAge, sensorLevel, pbAge, batteryLevel) sensorLevelLabel?.text = if (activeBgSource.sensorBatteryLevel == -1) "" else rh.gs(R.string.careportal_level_label) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewData.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewData.kt index 8437c500fa..8eaf29a08c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewData.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewData.kt @@ -128,14 +128,29 @@ class OverviewData @Inject constructor( var lastBg: GlucoseValue? = null - val lastBgColor: Int + val isLow: Boolean get() = lastBg?.let { lastBg -> - when { - lastBg.valueToUnits(profileFunction.getUnits()) < defaultValueHelper.determineLowLine() -> rh.gc(R.color.low) - lastBg.valueToUnits(profileFunction.getUnits()) > defaultValueHelper.determineHighLine() -> rh.gc(R.color.high) - else -> rh.gc(R.color.inrange) - } - } ?: rh.gc(R.color.inrange) + lastBg.valueToUnits(profileFunction.getUnits()) < defaultValueHelper.determineLowLine() + } ?: false + + val isHigh: Boolean + get() = lastBg?.let { lastBg -> + lastBg.valueToUnits(profileFunction.getUnits()) > defaultValueHelper.determineHighLine() + } ?: false + + val lastBgColor: Int + get() = when { + isLow -> rh.gc(R.color.low) + isHigh -> rh.gc(R.color.high) + else -> rh.gc(R.color.inrange) + } + + val lastBgDescription: String + get() = when { + isLow -> rh.gs(R.string.a11y_low) + isHigh -> rh.gs(R.string.a11y_high) + else -> rh.gs(R.string.a11y_inrange) + } val isActualBg: Boolean get() = diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt index 5ec2a64b52..962f29d2e1 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt @@ -8,6 +8,7 @@ import android.content.Intent import android.graphics.Color import android.graphics.Paint import android.graphics.drawable.AnimationDrawable +import android.os.Build import android.os.Bundle import android.os.Handler import android.os.HandlerThread @@ -416,7 +417,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList OKDialog.showConfirmation(activity, rh.gs(R.string.tempbasal_label), lastRun.constraintsProcessed?.toSpanned() ?: "".toSpanned(), { uel.log(Action.ACCEPTS_TEMP_BASAL, Sources.Overview) - (context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).cancel(Constants.notificationID) + (context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager?)?.cancel(Constants.notificationID) rxBus.send(EventWearInitiateAction("cancelChangeRequest")) Thread { loop.acceptChangeRequest() }.run() binding.buttonsLayout.acceptTempButton.visibility = View.GONE @@ -597,24 +598,36 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList // aps mode val closedLoopEnabled = constraintChecker.isClosedLoopAllowed() + + fun apsModeSetA11yLabel(stringRes: Int) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + binding.infoLayout.apsMode.stateDescription = rh.gs(stringRes) + } else { + binding.infoLayout.apsMode.contentDescription = rh.gs(R.string.apsmode_title) + " " + rh.gs(stringRes) + } + } + if (config.APS && pump.pumpDescription.isTempBasalCapable) { binding.infoLayout.apsMode.visibility = View.VISIBLE binding.infoLayout.timeLayout.visibility = View.GONE when { (loop as PluginBase).isEnabled() && loop.isSuperBolus -> { binding.infoLayout.apsMode.setImageResource(R.drawable.ic_loop_superbolus) + apsModeSetA11yLabel(R.string.superbolus) binding.infoLayout.apsModeText.text = dateUtil.age(loop.minutesToEndOfSuspend() * 60000L, true, rh) binding.infoLayout.apsModeText.visibility = View.VISIBLE } loop.isDisconnected -> { binding.infoLayout.apsMode.setImageResource(R.drawable.ic_loop_disconnected) + apsModeSetA11yLabel(R.string.disconnected) binding.infoLayout.apsModeText.text = dateUtil.age(loop.minutesToEndOfSuspend() * 60000L, true, rh) binding.infoLayout.apsModeText.visibility = View.VISIBLE } (loop as PluginBase).isEnabled() && loop.isSuspended -> { binding.infoLayout.apsMode.setImageResource(R.drawable.ic_loop_paused) + apsModeSetA11yLabel(R.string.suspendloop_label) binding.infoLayout.apsModeText.text = dateUtil.age(loop.minutesToEndOfSuspend() * 60000L, true, rh) binding.infoLayout.apsModeText.visibility = View.VISIBLE } @@ -624,8 +637,10 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList if (pump.model() == PumpType.OMNIPOD_EROS || pump.model() == PumpType.OMNIPOD_DASH) { // For Omnipod, indicate the pump as disconnected when it's suspended. // The only way to 'reconnect' it, is through the Omnipod tab + apsModeSetA11yLabel(R.string.disconnected) R.drawable.ic_loop_disconnected } else { + apsModeSetA11yLabel(R.string.pump_paused) R.drawable.ic_loop_paused } ) @@ -634,21 +649,25 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList (loop as PluginBase).isEnabled() && closedLoopEnabled.value() && loop.isLGS -> { binding.infoLayout.apsMode.setImageResource(R.drawable.ic_loop_lgs) + apsModeSetA11yLabel(R.string.uel_lgs_loop_mode) binding.infoLayout.apsModeText.visibility = View.GONE } (loop as PluginBase).isEnabled() && closedLoopEnabled.value() -> { binding.infoLayout.apsMode.setImageResource(R.drawable.ic_loop_closed) + apsModeSetA11yLabel(R.string.closedloop) binding.infoLayout.apsModeText.visibility = View.GONE } (loop as PluginBase).isEnabled() && !closedLoopEnabled.value() -> { binding.infoLayout.apsMode.setImageResource(R.drawable.ic_loop_open) + apsModeSetA11yLabel(R.string.openloop) binding.infoLayout.apsModeText.visibility = View.GONE } else -> { binding.infoLayout.apsMode.setImageResource(R.drawable.ic_loop_disabled) + apsModeSetA11yLabel(R.string.disabledloop) binding.infoLayout.apsModeText.visibility = View.GONE } } @@ -732,6 +751,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList binding.infoLayout.bg.setTextColor(overviewData.lastBgColor) binding.infoLayout.arrow.setImageResource(trendCalculator.getTrendArrow(overviewData.lastBg).directionToIcon()) binding.infoLayout.arrow.setColorFilter(overviewData.lastBgColor) + binding.infoLayout.arrow.contentDescription = overviewData.lastBgDescription + " " + rh.gs(R.string.and) + " " + trendCalculator.getTrendDescription(overviewData.lastBg) val glucoseStatus = glucoseStatusProvider.glucoseStatusData if (glucoseStatus != null) { @@ -751,13 +771,20 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList binding.infoLayout.bg.paintFlags = if (!overviewData.isActualBg) binding.infoLayout.bg.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG else binding.infoLayout.bg.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv() + + val outDate = (if (!overviewData.isActualBg) rh.gs(R.string.a11y_bg_outdated) else "") + binding.infoLayout.bg.contentDescription = + rh.gs(R.string.a11y_blood_glucose) + " " + binding.infoLayout.bg.text.toString() + " " + overviewData.lastBgDescription + " " + outDate + binding.infoLayout.timeAgo.text = dateUtil.minAgo(rh, overviewData.lastBg?.timestamp) + binding.infoLayout.timeAgo.contentDescription = dateUtil.minAgoLong(rh, overviewData.lastBg?.timestamp) binding.infoLayout.timeAgoShort.text = "(" + dateUtil.minAgoShort(overviewData.lastBg?.timestamp) + ")" val qualityIcon = bgQualityCheckPlugin.icon() if (qualityIcon != 0) { binding.infoLayout.bgQuality.visibility = View.VISIBLE binding.infoLayout.bgQuality.setImageResource(qualityIcon) + binding.infoLayout.bgQuality.contentDescription = rh.gs(R.string.a11y_bg_quality) + " " + bgQualityCheckPlugin.stateDescription() binding.infoLayout.bgQuality.setOnClickListener { context?.let { context -> OKDialog.show(context, rh.gs(R.string.data_status), bgQualityCheckPlugin.message) } } @@ -823,7 +850,15 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList fun updateTime(from: String) { binding.infoLayout.time.text = dateUtil.timeString(dateUtil.now()) // Status lights - binding.statusLightsLayout.statusLights.visibility = (sp.getBoolean(R.string.key_show_statuslights, true) || config.NSCLIENT).toVisibility() + val isPatchPump = activePlugin.activePump.pumpDescription.isPatchPump + binding.statusLightsLayout.apply { + cannulaOrPatch.setImageResource(if (isPatchPump) R.drawable.ic_patch_pump_outline else R.drawable.ic_cp_age_cannula) + cannulaOrPatch.contentDescription = rh.gs(if (isPatchPump) R.string.statuslights_patch_pump_age else R.string.statuslights_cannula_age) + cannulaOrPatch.scaleX = if (isPatchPump) 1.4f else 2f + cannulaOrPatch.scaleY = cannulaOrPatch.scaleX + insulinAge.visibility = isPatchPump.not().toVisibility() + statusLights.visibility = (sp.getBoolean(R.string.key_show_statuslights, true) || config.NSCLIENT).toVisibility() + } statusLightHandler.updateStatusLights( binding.statusLightsLayout.cannulaAge, binding.statusLightsLayout.insulinAge, diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/StatusLightHandler.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/StatusLightHandler.kt index 61e632306b..ba73fab429 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/StatusLightHandler.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/StatusLightHandler.kt @@ -40,12 +40,7 @@ class StatusLightHandler @Inject constructor( val pump = activePlugin.activePump val bgSource = activePlugin.activeBgSource handleAge(careportal_cannula_age, TherapyEvent.Type.CANNULA_CHANGE, R.string.key_statuslights_cage_warning, 48.0, R.string.key_statuslights_cage_critical, 72.0) - if (pump.model() == PumpType.OMNIPOD_EROS || pump.model() == PumpType.OMNIPOD_DASH) { - careportal_insulin_age?.visibility = View.GONE - } else { - careportal_insulin_age?.visibility = View.VISIBLE - handleAge(careportal_insulin_age, TherapyEvent.Type.INSULIN_CHANGE, R.string.key_statuslights_iage_warning, 72.0, R.string.key_statuslights_iage_critical, 144.0) - } + handleAge(careportal_insulin_age, TherapyEvent.Type.INSULIN_CHANGE, R.string.key_statuslights_iage_warning, 72.0, R.string.key_statuslights_iage_critical, 144.0) handleAge(careportal_sensor_age, TherapyEvent.Type.SENSOR_CHANGE, R.string.key_statuslights_sage_warning, 216.0, R.string.key_statuslights_sage_critical, 240.0) if (pump.pumpDescription.isBatteryReplaceable || (pump is OmnipodErosPumpPlugin && pump.isUseRileyLinkBatteryLevel && pump.isBatteryChangeLoggingEnabled)) { handleAge(careportal_pb_age, TherapyEvent.Type.PUMP_BATTERY_CHANGE, R.string.key_statuslights_bage_warning, 216.0, R.string.key_statuslights_bage_critical, 240.0) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/activities/QuickWizardListActivity.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/activities/QuickWizardListActivity.kt index 8a76c606bf..9c770263fb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/activities/QuickWizardListActivity.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/activities/QuickWizardListActivity.kt @@ -1,12 +1,18 @@ package info.nightscout.androidaps.plugins.general.overview.activities +import android.annotation.SuppressLint import android.os.Bundle +import android.util.Log import android.view.LayoutInflater +import android.view.MotionEvent import android.view.View import android.view.ViewGroup import android.widget.Button +import android.widget.ImageView import android.widget.TextView import androidx.fragment.app.FragmentManager +import androidx.recyclerview.widget.ItemTouchHelper +import androidx.recyclerview.widget.ItemTouchHelper.* import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import info.nightscout.androidaps.R @@ -17,10 +23,12 @@ import info.nightscout.androidaps.plugins.general.overview.dialogs.EditQuickWiza import info.nightscout.androidaps.plugins.general.overview.events.EventQuickWizardChange import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy +import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.utils.wizard.QuickWizard -import io.reactivex.rxjava3.disposables.CompositeDisposable +import info.nightscout.androidaps.utils.wizard.QuickWizardEntry +import info.nightscout.shared.sharedPreferences.SP import javax.inject.Inject class QuickWizardListActivity : NoSplashAppCompatActivity() { @@ -30,20 +38,95 @@ class QuickWizardListActivity : NoSplashAppCompatActivity() { @Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var quickWizard: QuickWizard @Inject lateinit var dateUtil: DateUtil + @Inject lateinit var sp: SP private var disposable: CompositeDisposable = CompositeDisposable() private lateinit var binding: OverviewQuickwizardlistActivityBinding + private val itemTouchHelper by lazy { + val simpleItemTouchCallback = object : ItemTouchHelper.SimpleCallback(UP or DOWN or START or END, 0) { + + override fun onMove( + recyclerView: RecyclerView, + viewHolder: RecyclerView.ViewHolder, + target: RecyclerView.ViewHolder + ): Boolean { + val adapter = recyclerView.adapter as RecyclerViewAdapter + val from = viewHolder.layoutPosition + val to = target.layoutPosition + adapter.moveItem(from, to) + adapter.notifyItemMoved(from, to) + + return true + } + + override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { + } + + override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) { + super.onSelectedChanged(viewHolder, actionState) + + if (actionState == ACTION_STATE_DRAG) { + viewHolder?.itemView?.alpha = 0.5f + } + } + + override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) { + super.clearView(recyclerView, viewHolder) + + viewHolder.itemView.alpha = 1.0f + + val adapter = recyclerView.adapter as RecyclerViewAdapter + adapter.onDrop() + } + } + + ItemTouchHelper(simpleItemTouchCallback) + } + + fun startDragging(viewHolder: RecyclerView.ViewHolder) { + itemTouchHelper.startDrag(viewHolder) + } + private inner class RecyclerViewAdapter(var fragmentManager: FragmentManager) : RecyclerView.Adapter() { + @SuppressLint("ClickableViewAccessibility") override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QuickWizardEntryViewHolder { - return QuickWizardEntryViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.overview_quickwizardlist_item, parent, false), fragmentManager) + val itemView = LayoutInflater.from(parent.context).inflate(R.layout.overview_quickwizardlist_item, parent, false) + val viewHolder = QuickWizardEntryViewHolder(itemView, fragmentManager) + + viewHolder.handleView.setOnTouchListener { _, event -> + if (event.actionMasked == MotionEvent.ACTION_DOWN) { + startDragging(viewHolder) + } + return@setOnTouchListener true + } + + return viewHolder } override fun onBindViewHolder(holder: QuickWizardEntryViewHolder, position: Int) { holder.from.text = dateUtil.timeString(quickWizard[position].validFromDate()) holder.to.text = dateUtil.timeString(quickWizard[position].validToDate()) + val wearControl = sp.getBoolean(R.string.key_wear_control, false) + + if (wearControl) { + holder.handleView.visibility = View.VISIBLE + } else { + holder.handleView.visibility = View.GONE + } + if (quickWizard[position].device() == QuickWizardEntry.DEVICE_ALL) { + holder.device.visibility = View.GONE + } else { + holder.device.visibility = View.VISIBLE + holder.device.setImageResource( + when (quickWizard[position].device()) { + QuickWizardEntry.DEVICE_WATCH -> R.drawable.ic_watch + else -> R.drawable.ic_smartphone + } + ) + } holder.buttonText.text = quickWizard[position].buttonText() holder.carbs.text = rh.gs(R.string.format_carbs, quickWizard[position].carbs()) } @@ -55,6 +138,8 @@ class QuickWizardListActivity : NoSplashAppCompatActivity() { val buttonText: TextView = itemView.findViewById(R.id.overview_quickwizard_item_buttonText) val carbs: TextView = itemView.findViewById(R.id.overview_quickwizard_item_carbs) val from: TextView = itemView.findViewById(R.id.overview_quickwizard_item_from) + val handleView: ImageView = itemView.findViewById(R.id.handleView) + val device: ImageView = itemView.findViewById(R.id.overview_quickwizard_item_device) val to: TextView = itemView.findViewById(R.id.overview_quickwizard_item_to) private val editButton: Button = itemView.findViewById(R.id.overview_quickwizard_item_edit_button) private val removeButton: Button = itemView.findViewById(R.id.overview_quickwizard_item_remove_button) @@ -74,6 +159,16 @@ class QuickWizardListActivity : NoSplashAppCompatActivity() { } } } + + fun moveItem(from: Int, to: Int) { + Log.i("QuickWizard", "moveItem") + quickWizard.move(from, to) + } + + fun onDrop() { + Log.i("QuickWizard", "onDrop") + rxBus.send(EventQuickWizardChange()) + } } override fun onCreate(savedInstanceState: Bundle?) { @@ -84,6 +179,7 @@ class QuickWizardListActivity : NoSplashAppCompatActivity() { binding.recyclerview.setHasFixedSize(true) binding.recyclerview.layoutManager = LinearLayoutManager(this) binding.recyclerview.adapter = RecyclerViewAdapter(supportFragmentManager) + itemTouchHelper.attachToRecyclerView(binding.recyclerview) binding.addButton.setOnClickListener { val manager = supportFragmentManager @@ -98,13 +194,13 @@ class QuickWizardListActivity : NoSplashAppCompatActivity() { .toObservable(EventQuickWizardChange::class.java) .observeOn(aapsSchedulers.main) .subscribe({ - val adapter = RecyclerViewAdapter(supportFragmentManager) - binding.recyclerview.swapAdapter(adapter, false) - }, fabricPrivacy::logException) + val adapter = RecyclerViewAdapter(supportFragmentManager) + binding.recyclerview.swapAdapter(adapter, false) + }, fabricPrivacy::logException) } override fun onPause() { disposable.clear() super.onPause() } -} \ No newline at end of file +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/EditQuickWizardDialog.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/EditQuickWizardDialog.kt index bfe47b2bdf..e7abb4690d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/EditQuickWizardDialog.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/dialogs/EditQuickWizardDialog.kt @@ -9,6 +9,7 @@ import android.view.ViewGroup import android.view.Window import android.view.WindowManager import dagger.android.support.DaggerDialogFragment +import info.nightscout.androidaps.R import info.nightscout.androidaps.databinding.OverviewEditquickwizardDialogBinding import info.nightscout.shared.logging.AAPSLogger import info.nightscout.androidaps.plugins.bus.RxBus @@ -21,6 +22,7 @@ import info.nightscout.androidaps.utils.extensions.setEnableForChildren import info.nightscout.androidaps.utils.extensions.setSelection import info.nightscout.androidaps.utils.wizard.QuickWizard import info.nightscout.androidaps.utils.wizard.QuickWizardEntry +import info.nightscout.shared.sharedPreferences.SP import org.json.JSONException import javax.inject.Inject @@ -30,9 +32,9 @@ class EditQuickWizardDialog : DaggerDialogFragment(), View.OnClickListener { @Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var quickWizard: QuickWizard @Inject lateinit var dateUtil: DateUtil + @Inject lateinit var sp: SP var position = -1 - var fromSeconds: Int = 0 var toSeconds: Int = 0 @@ -42,8 +44,10 @@ class EditQuickWizardDialog : DaggerDialogFragment(), View.OnClickListener { // onDestroyView. private val binding get() = _binding!! - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View { + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { dialog?.window?.requestFeature(Window.FEATURE_NO_TITLE) dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) isCancelable = true @@ -57,6 +61,14 @@ class EditQuickWizardDialog : DaggerDialogFragment(), View.OnClickListener { position = bundle.getInt("position", -1) } val entry = if (position == -1) quickWizard.newEmptyItem() else quickWizard[position] + if (sp.getBoolean(R.string.key_wear_control, false)) { + binding.deviceLabel.visibility = View.VISIBLE + binding.device.visibility = View.VISIBLE + } else { + binding.deviceLabel.visibility = View.GONE + binding.device.visibility = View.GONE + } + binding.okcancel.ok.setOnClickListener { try { entry.storage.put("buttonText", binding.buttonEdit.text.toString()) @@ -66,10 +78,14 @@ class EditQuickWizardDialog : DaggerDialogFragment(), View.OnClickListener { entry.storage.put("useBG", binding.useBg.selectedItemPosition) entry.storage.put("useCOB", binding.useCob.selectedItemPosition) entry.storage.put("useBolusIOB", binding.useBolusIob.selectedItemPosition) + entry.storage.put("device", binding.device.selectedItemPosition) entry.storage.put("useBasalIOB", binding.useBasalIob.selectedItemPosition) entry.storage.put("useTrend", binding.useTrend.selectedItemPosition) entry.storage.put("useSuperBolus", binding.useSuperBolus.selectedItemPosition) entry.storage.put("useTempTarget", binding.useTempTarget.selectedItemPosition) + entry.storage.put("usePercentage", binding.usePercentage.selectedItemPosition) + val percentage = SafeParse.stringToInt(binding.percentage.text.toString()) + entry.storage.put("percentage", percentage) } catch (e: JSONException) { aapsLogger.error("Unhandled exception", e) } @@ -88,7 +104,8 @@ class EditQuickWizardDialog : DaggerDialogFragment(), View.OnClickListener { binding.from.setOnClickListener { context?.let { - TimePickerDialog(it, fromTimeSetListener, + TimePickerDialog( + it, fromTimeSetListener, T.secs(fromSeconds.toLong()).hours().toInt(), T.secs((fromSeconds % 3600).toLong()).mins().toInt(), DateFormat.is24HourFormat(context) @@ -105,13 +122,29 @@ class EditQuickWizardDialog : DaggerDialogFragment(), View.OnClickListener { binding.to.setOnClickListener { context?.let { - TimePickerDialog(it, toTimeSetListener, + TimePickerDialog( + it, toTimeSetListener, T.secs(toSeconds.toLong()).hours().toInt(), T.secs((toSeconds % 3600).toLong()).mins().toInt(), DateFormat.is24HourFormat(context) ).show() } } + + fun usePercentage(custom: Boolean) { + if (custom) { + binding.percentageLabel.visibility = View.VISIBLE + binding.percentage.visibility = View.VISIBLE + } else { + binding.percentageLabel.visibility = View.GONE + binding.percentage.visibility = View.GONE + } + } + + binding.usePercentage.setOnCheckedChangeListener { _, checkedId -> + usePercentage(checkedId == R.id.use_percentage_custom) + } + toSeconds = entry.validTo() binding.to.text = dateUtil.timeString(dateUtil.secondsOfTheDayToMilliseconds(toSeconds)) @@ -122,10 +155,13 @@ class EditQuickWizardDialog : DaggerDialogFragment(), View.OnClickListener { binding.useCob.setSelection(entry.useCOB()) binding.useBolusIob.setSelection(entry.useBolusIOB()) binding.useBasalIob.setSelection(entry.useBasalIOB()) + binding.device.setSelection(entry.device()) binding.useTrend.setSelection(entry.useTrend()) binding.useSuperBolus.setSelection(entry.useSuperBolus()) binding.useTempTarget.setSelection(entry.useTempTarget()) - + binding.usePercentage.setSelection(entry.usePercentage()) + usePercentage(entry.usePercentage() == QuickWizardEntry.CUSTOM) + binding.percentage.setText(entry.percentage().toString()) binding.useCobYes.setOnClickListener(this) binding.useCobNo.setOnClickListener(this) processCob() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt index 3fc78dbf3d..481e904868 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt @@ -2,6 +2,7 @@ package info.nightscout.androidaps.plugins.general.wear import android.app.NotificationManager import android.content.Context +import android.util.Log import dagger.android.HasAndroidInjector import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R @@ -39,11 +40,11 @@ import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.sharedPreferences.SP import info.nightscout.androidaps.utils.wizard.BolusWizard +import info.nightscout.androidaps.utils.wizard.QuickWizard import info.nightscout.shared.SafeParse import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign import java.text.DateFormat -import java.text.DecimalFormat import java.text.SimpleDateFormat import java.util.* import java.util.concurrent.TimeUnit @@ -71,6 +72,7 @@ class ActionStringHandler @Inject constructor( private val activePlugin: ActivePlugin, private val iobCobCalculator: IobCobCalculator, private val localInsightPlugin: LocalInsightPlugin, + private val quickWizard: QuickWizard, private val danaRPlugin: DanaRPlugin, private val danaRKoreanPlugin: DanaRKoreanPlugin, private val danaRv2Plugin: DanaRv2Plugin, @@ -79,7 +81,8 @@ class ActionStringHandler @Inject constructor( private val dateUtil: DateUtil, private val config: Config, private val repository: AppRepository, - private val uel: UserEntryLogger + private val uel: UserEntryLogger, + private val defaultValueHelper: DefaultValueHelper ) { private val timeout = 65 * 1000 @@ -107,9 +110,11 @@ class ActionStringHandler @Inject constructor( @Synchronized private fun handleInitiate(actionString: String) { + //TODO: i18n + Log.i("ActionStringHandler", "handleInitiate actionString=" + actionString) if (!sp.getBoolean(R.string.key_wear_control, false)) return lastBolusWizard = null - var rTitle = "CONFIRM" //TODO: i18n + var rTitle = rh.gs(R.string.confirm).uppercase() var rMessage = "" var rAction = "" // do the parsing and check constraints @@ -136,6 +141,11 @@ class ActionStringHandler @Inject constructor( val carbs = SafeParse.stringToInt(act[2]) val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(insulin)).value() val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value() + val pump = activePlugin.activePump + if (insulinAfterConstraints > 0 && (!pump.isInitialized() || pump.isSuspended() || loop.isDisconnected)) { + sendError(rh.gs(R.string.wizard_pump_not_available)) + return + } rMessage += rh.gs(R.string.bolus) + ": " + insulinAfterConstraints + "U\n" rMessage += rh.gs(R.string.carbs) + ": " + carbsAfterConstraints + "g" if (insulinAfterConstraints - insulin != 0.0 || carbsAfterConstraints - carbs != 0) { @@ -143,32 +153,72 @@ class ActionStringHandler @Inject constructor( } rAction += "bolus $insulinAfterConstraints $carbsAfterConstraints" } else if ("temptarget" == act[0]) { ///////////////////////////////////////////////////////// TEMPTARGET - val isMGDL = java.lang.Boolean.parseBoolean(act[1]) - if (profileFunction.getUnits() == GlucoseUnit.MGDL != isMGDL) { - sendError("Different units used on watch and phone!") - return - } - val duration = SafeParse.stringToInt(act[2]) - if (duration == 0) { - rMessage += "Zero-Temp-Target - cancelling running Temp-Targets?" + aapsLogger.info(LTag.WEAR, "temptarget received: $act") + if ("cancel" == act[1]) { + rMessage += rh.gs(R.string.wear_action_tempt_cancel_message) rAction = "temptarget true 0 0 0" + } else if ("preset" == act[1]) { + val presetIsMGDL = profileFunction.getUnits() == GlucoseUnit.MGDL + val preset = act[2] + when (preset) { + "activity" -> { + val activityTTDuration = defaultValueHelper.determineActivityTTDuration() + val activityTT = defaultValueHelper.determineActivityTT() + val reason = rh.gs(R.string.activity) + rMessage += rh.gs(R.string.wear_action_tempt_preset_message, reason, activityTT, activityTTDuration) + rAction = "temptarget $presetIsMGDL $activityTTDuration $activityTT $activityTT" + } + + "hypo" -> { + val hypoTTDuration = defaultValueHelper.determineHypoTTDuration() + val hypoTT = defaultValueHelper.determineHypoTT() + val reason = rh.gs(R.string.hypo) + rMessage += rh.gs(R.string.wear_action_tempt_preset_message, reason, hypoTT, hypoTTDuration) + rAction = "temptarget $presetIsMGDL $hypoTTDuration $hypoTT $hypoTT" + } + + "eating" -> { + val eatingSoonTTDuration = defaultValueHelper.determineEatingSoonTTDuration() + val eatingSoonTT = defaultValueHelper.determineEatingSoonTT() + val reason = rh.gs(R.string.eatingsoon) + rMessage += rh.gs(R.string.wear_action_tempt_preset_message, reason, eatingSoonTT, eatingSoonTTDuration) + rAction = "temptarget $presetIsMGDL $eatingSoonTTDuration $eatingSoonTT $eatingSoonTT" + } + + else -> { + sendError(rh.gs(R.string.wear_action_tempt_preset_error, preset)) + return + } + } } else { - var low = SafeParse.stringToDouble(act[3]) - var high = SafeParse.stringToDouble(act[4]) - if (!isMGDL) { - low *= Constants.MMOLL_TO_MGDL - high *= Constants.MMOLL_TO_MGDL - } - if (low < HardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[0] || low > HardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[1]) { - sendError("Min-BG out of range!") + val isMGDL = java.lang.Boolean.parseBoolean(act[1]) + if (profileFunction.getUnits() == GlucoseUnit.MGDL != isMGDL) { + sendError(rh.gs(R.string.wear_action_tempt_unit_error)) return } - if (high < HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[0] || high > HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[1]) { - sendError("Max-BG out of range!") - return + val duration = SafeParse.stringToInt(act[2]) + if (duration == 0) { + rMessage += rh.gs(R.string.wear_action_tempt_zero_message) + rAction = "temptarget true 0 0 0" + } else { + var low = SafeParse.stringToDouble(act[3]) + var high = SafeParse.stringToDouble(act[4]) + if (!isMGDL) { + low *= Constants.MMOLL_TO_MGDL + high *= Constants.MMOLL_TO_MGDL + } + if (low < HardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[0] || low > HardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[1]) { + sendError(rh.gs(R.string.wear_action_tempt_min_bg_error)) + return + } + if (high < HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[0] || high > HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[1]) { + sendError(rh.gs(R.string.wear_action_tempt_max_bg_error)) + return + } + rMessage += if (act[3] === act[4]) rh.gs(R.string.wear_action_tempt_manual_message, act[3], act[2]) + else rh.gs(R.string.wear_action_tempt_manual_range_message, act[3], act[4], act[2]) + rAction = actionString } - rMessage += "Temptarget:\nMin: " + act[3] + "\nMax: " + act[4] + "\nDuration: " + act[2] - rAction = actionString } } else if ("status" == act[0]) { ////////////////////////////////////////////// STATUS rTitle = "STATUS" @@ -186,10 +236,15 @@ class ActionStringHandler @Inject constructor( sendError("Update APP on Watch!") return } else if ("wizard2" == act[0]) { ////////////////////////////////////////////// WIZARD + val pump = activePlugin.activePump + if (!pump.isInitialized() || pump.isSuspended() || loop.isDisconnected) { + sendError(rh.gs(R.string.wizard_pump_not_available)) + return + } val carbsBeforeConstraints = SafeParse.stringToInt(act[1]) val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbsBeforeConstraints)).value() if (carbsAfterConstraints - carbsBeforeConstraints != 0) { - sendError("Carb constraint violation!") + sendError(rh.gs(R.string.wizard_carbs_constraint)) return } val useBG = sp.getBoolean(R.string.key_wearwizard_bg, true) @@ -202,52 +257,94 @@ class ActionStringHandler @Inject constructor( val profile = profileFunction.getProfile() val profileName = profileFunction.getProfileName() if (profile == null) { - sendError("No profile found!") + sendError(rh.gs(R.string.wizard_no_active_profile)) return } val bgReading = iobCobCalculator.ads.actualBg() if (bgReading == null) { - sendError("No recent BG to base calculation on!") + sendError(rh.gs(R.string.wizard_no_actual_bg)) return } val cobInfo = iobCobCalculator.getCobInfo(false, "Wizard wear") if (cobInfo.displayCob == null) { - sendError("Unknown COB! BG reading missing or recent app restart?") + sendError(rh.gs(R.string.wizard_no_cob)) return } - val format = DecimalFormat("0.00") - val formatInt = DecimalFormat("0") val dbRecord = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() val tempTarget = if (dbRecord is ValueWrapper.Existing) dbRecord.value else null - val bolusWizard = BolusWizard(injector).doCalc(profile, profileName, tempTarget, - carbsAfterConstraints, if (cobInfo.displayCob != null) cobInfo.displayCob!! else 0.0, bgReading.valueToUnits(profileFunction.getUnits()), - 0.0, percentage, useBG, useCOB, useBolusIOB, useBasalIOB, false, useTT, useTrend, false) - if (abs(bolusWizard.insulinAfterConstraints - bolusWizard.calculatedTotalInsulin) >= 0.01) { - sendError("Insulin constraint violation!" + - "\nCannot deliver " + format.format(bolusWizard.calculatedTotalInsulin) + "!") + val bolusWizard = BolusWizard(injector).doCalc( + profile, profileName, tempTarget, + carbsAfterConstraints, cobInfo.displayCob!!, bgReading.valueToUnits(profileFunction.getUnits()), + 0.0, percentage, useBG, useCOB, useBolusIOB, useBasalIOB, false, useTT, useTrend, false + ) + val insulinAfterConstraints = bolusWizard.insulinAfterConstraints + val minStep = pump.pumpDescription.pumpType.determineCorrectBolusStepSize(insulinAfterConstraints) + if (abs(insulinAfterConstraints - bolusWizard.calculatedTotalInsulin) >= minStep) { + sendError(rh.gs(R.string.wizard_constraint_bolus_size, bolusWizard.calculatedTotalInsulin)) return } if (bolusWizard.calculatedTotalInsulin <= 0 && bolusWizard.carbs <= 0) { rAction = "info" - rTitle = "INFO" + rTitle = rh.gs(R.string.info) } else { rAction = actionString } - rMessage += "Carbs: " + bolusWizard.carbs + "g" - rMessage += "\nBolus: " + format.format(bolusWizard.calculatedTotalInsulin) + "U" + rMessage += rh.gs(R.string.wizard_result, bolusWizard.calculatedTotalInsulin, bolusWizard.carbs) rMessage += "\n_____________" - rMessage += "\nCalc (IC:" + DecimalFormatter.to1Decimal(bolusWizard.ic) + ", " + "ISF:" + DecimalFormatter.to1Decimal(bolusWizard.sens) + "): " - rMessage += "\nFrom Carbs: " + format.format(bolusWizard.insulinFromCarbs) + "U" - if (useCOB) rMessage += "\nFrom" + formatInt.format(cobInfo.displayCob) + "g COB : " + format.format(bolusWizard.insulinFromCOB) + "U" - if (useBG) rMessage += "\nFrom BG: " + format.format(bolusWizard.insulinFromBG) + "U" - if (useBolusIOB) rMessage += "\nBolus IOB: " + format.format(bolusWizard.insulinFromBolusIOB) + "U" - if (useBasalIOB) rMessage += "\nBasal IOB: " + format.format(bolusWizard.insulinFromBasalIOB) + "U" - if (useTrend) rMessage += "\nFrom 15' trend: " + format.format(bolusWizard.insulinFromTrend) + "U" - if (percentage != 100) { - rMessage += "\nPercentage: " + format.format(bolusWizard.totalBeforePercentageAdjustment) + "U * " + percentage + "% -> ~" + format.format(bolusWizard.calculatedTotalInsulin) + "U" - } + rMessage += "\n" + bolusWizard.explainShort() lastBolusWizard = bolusWizard + } else if ("quick_wizard" == act[0]) { + val guid = act[1] + val actualBg = iobCobCalculator.ads.actualBg() + val profile = profileFunction.getProfile() + val profileName = profileFunction.getProfileName() + val quickWizardEntry = quickWizard.get(guid) + Log.i("QuickWizard", "handleInitiate: quick_wizard " + quickWizardEntry?.buttonText() + " c " + quickWizardEntry?.carbs()) + if (quickWizardEntry == null) { + sendError(rh.gs(R.string.quick_wizard_not_available)) + return + } + if (actualBg == null) { + sendError(rh.gs(R.string.wizard_no_actual_bg)) + return + } + if (profile == null) { + sendError(rh.gs(R.string.wizard_no_active_profile)) + return + } + val cobInfo = iobCobCalculator.getCobInfo(false, "QuickWizard wear") + if (cobInfo.displayCob == null) { + sendError(rh.gs(R.string.wizard_no_cob)) + return + } + val pump = activePlugin.activePump + if (!pump.isInitialized() || pump.isSuspended() || loop.isDisconnected) { + sendError(rh.gs(R.string.wizard_pump_not_available)) + return + } + + val wizard = quickWizardEntry.doCalc(profile, profileName, actualBg, true) + + val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(quickWizardEntry.carbs())).value() + if (carbsAfterConstraints != quickWizardEntry.carbs()) { + sendError(rh.gs(R.string.wizard_carbs_constraint)) + return + } + val insulinAfterConstraints = wizard.insulinAfterConstraints + val minStep = pump.pumpDescription.pumpType.determineCorrectBolusStepSize(insulinAfterConstraints) + if (abs(insulinAfterConstraints - wizard.calculatedTotalInsulin) >= minStep) { + sendError(rh.gs(R.string.wizard_constraint_bolus_size, wizard.calculatedTotalInsulin)) + return + } + + rMessage = rh.gs(R.string.quick_wizard_message, quickWizardEntry.buttonText(), wizard.calculatedTotalInsulin, quickWizardEntry.carbs()) + rAction = "bolus $insulinAfterConstraints $carbsAfterConstraints" + Log.i("QuickWizard", "handleInitiate: quick_wizard action=$rAction") + + rMessage += "\n_____________" + rMessage += "\n" + wizard.explainShort() + } else if ("opencpp" == act[0]) { val activeProfileSwitch = repository.getEffectiveProfileSwitchActiveAt(dateUtil.now()).blockingGet() if (activeProfileSwitch is ValueWrapper.Existing) { // read CPP values @@ -331,7 +428,10 @@ class ActionStringHandler @Inject constructor( rAction = "cancelChangeRequest" wearPlugin.requestNotificationCancel(rAction) return - } else return + } else { + sendError(rh.gs(R.string.wear_unknown_action_string) + act[0]) + return + } // send result wearPlugin.requestActionConfirmation(rTitle, rMessage, rAction) lastSentTimestamp = System.currentTimeMillis() @@ -560,39 +660,45 @@ class ActionStringHandler @Inject constructor( } //send profile to pump uel.log(Action.PROFILE_SWITCH, Sources.Wear, - ValueWithUnit.Percent(percentage), - ValueWithUnit.Hour(timeshift).takeIf { timeshift != 0 }) + ValueWithUnit.Percent(percentage), + ValueWithUnit.Hour(timeshift).takeIf { timeshift != 0 }) profileFunction.createProfileSwitch(0, percentage, timeshift) } private fun generateTempTarget(duration: Int, low: Double, high: Double) { if (duration != 0) { - disposable += repository.runTransactionForResult(InsertAndCancelCurrentTemporaryTargetTransaction( - timestamp = System.currentTimeMillis(), - duration = TimeUnit.MINUTES.toMillis(duration.toLong()), - reason = TemporaryTarget.Reason.WEAR, - lowTarget = Profile.toMgdl(low, profileFunction.getUnits()), - highTarget = Profile.toMgdl(high, profileFunction.getUnits()) - )).subscribe({ result -> - result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted temp target $it") } - result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") } - }, { - aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) - }) - uel.log(Action.TT, Sources.Wear, + disposable += repository.runTransactionForResult( + InsertAndCancelCurrentTemporaryTargetTransaction( + timestamp = System.currentTimeMillis(), + duration = TimeUnit.MINUTES.toMillis(duration.toLong()), + reason = TemporaryTarget.Reason.WEAR, + lowTarget = Profile.toMgdl(low, profileFunction.getUnits()), + highTarget = Profile.toMgdl(high, profileFunction.getUnits()) + ) + ).subscribe({ result -> + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted temp target $it") } + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") } + }, { + aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) + }) + uel.log( + Action.TT, Sources.Wear, ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.WEAR), ValueWithUnit.fromGlucoseUnit(low, profileFunction.getUnits().asText), ValueWithUnit.fromGlucoseUnit(high, profileFunction.getUnits().asText).takeIf { low != high }, - ValueWithUnit.Minute(duration)) + ValueWithUnit.Minute(duration) + ) } else { disposable += repository.runTransactionForResult(CancelCurrentTemporaryTargetIfAnyTransaction(System.currentTimeMillis())) .subscribe({ result -> - result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") } - }, { - aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) - }) - uel.log(Action.CANCEL_TT, Sources.Wear, - ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.WEAR)) + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") } + }, { + aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) + }) + uel.log( + Action.CANCEL_TT, Sources.Wear, + ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.WEAR) + ) } } @@ -601,13 +707,15 @@ class ActionStringHandler @Inject constructor( detailedBolusInfo.insulin = amount detailedBolusInfo.bolusType = DetailedBolusInfo.BolusType.PRIMING uel.log(Action.PRIME_BOLUS, Sources.Wear, - ValueWithUnit.Insulin(amount).takeIf { amount != 0.0 }) + ValueWithUnit.Insulin(amount).takeIf { amount != 0.0 }) commandQueue.bolus(detailedBolusInfo, object : Callback() { override fun run() { if (!result.success) { - sendError(rh.gs(R.string.treatmentdeliveryerror) + - "\n" + - result.comment) + sendError( + rh.gs(R.string.treatmentdeliveryerror) + + "\n" + + result.comment + ) } } }) @@ -615,9 +723,9 @@ class ActionStringHandler @Inject constructor( private fun doECarbs(carbs: Int, time: Long, duration: Int) { uel.log(if (duration == 0) Action.CARBS else Action.EXTENDED_CARBS, Sources.Wear, - ValueWithUnit.Timestamp(time), - ValueWithUnit.Gram(carbs), - ValueWithUnit.Hour(duration).takeIf { duration != 0 }) + ValueWithUnit.Timestamp(time), + ValueWithUnit.Gram(carbs), + ValueWithUnit.Hour(duration).takeIf { duration != 0 }) doBolus(0.0, carbs, time, duration) } @@ -636,15 +744,17 @@ class ActionStringHandler @Inject constructor( else -> Action.TREATMENT } uel.log(action, Sources.Wear, - ValueWithUnit.Insulin(amount).takeIf { amount != 0.0 }, - ValueWithUnit.Gram(carbs).takeIf { carbs != 0 }, - ValueWithUnit.Hour(carbsDuration).takeIf { carbsDuration != 0 }) + ValueWithUnit.Insulin(amount).takeIf { amount != 0.0 }, + ValueWithUnit.Gram(carbs).takeIf { carbs != 0 }, + ValueWithUnit.Hour(carbsDuration).takeIf { carbsDuration != 0 }) commandQueue.bolus(detailedBolusInfo, object : Callback() { override fun run() { if (!result.success) { - sendError(rh.gs(R.string.treatmentdeliveryerror) + - "\n" + - result.comment) + sendError( + rh.gs(R.string.treatmentdeliveryerror) + + "\n" + + result.comment + ) } } }) @@ -665,4 +775,4 @@ class ActionStringHandler @Inject constructor( lastConfirmActionString = null lastBolusWizard = null } -} \ No newline at end of file +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.java index 1ce5c509bb..95cf6128a2 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.java @@ -46,6 +46,7 @@ import info.nightscout.androidaps.interfaces.Loop; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.Profile; import info.nightscout.androidaps.interfaces.ProfileFunction; +import info.nightscout.androidaps.utils.wizard.QuickWizardEntry; import info.nightscout.shared.logging.AAPSLogger; import info.nightscout.shared.logging.LTag; import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; @@ -62,6 +63,7 @@ import info.nightscout.androidaps.utils.DecimalFormatter; import info.nightscout.androidaps.utils.DefaultValueHelper; import info.nightscout.androidaps.utils.TrendCalculator; import info.nightscout.androidaps.utils.resources.ResourceHelper; +import info.nightscout.androidaps.utils.wizard.QuickWizard; import info.nightscout.shared.sharedPreferences.SP; public class WatchUpdaterService extends WearableListenerService implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { @@ -81,6 +83,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog @Inject ReceiverStatusStore receiverStatusStore; @Inject Config config; @Inject public TrendCalculator trendCalculator; + @Inject public QuickWizard quickWizard; public static final String ACTION_RESEND = WatchUpdaterService.class.getName().concat(".Resend"); public static final String ACTION_OPEN_SETTINGS = WatchUpdaterService.class.getName().concat(".OpenSettings"); @@ -101,12 +104,14 @@ public class WatchUpdaterService extends WearableListenerService implements Goog private static final String OPEN_SETTINGS_PATH = "/openwearsettings"; private static final String NEW_STATUS_PATH = "/sendstatustowear"; private static final String NEW_PREFERENCES_PATH = "/sendpreferencestowear"; + private static final String QUICK_WIZARD_PATH = "/send_quick_wizard"; public static final String BASAL_DATA_PATH = "/nightscout_watch_basal"; public static final String BOLUS_PROGRESS_PATH = "/nightscout_watch_bolusprogress"; public static final String ACTION_CONFIRMATION_REQUEST_PATH = "/nightscout_watch_actionconfirmationrequest"; public static final String ACTION_CHANGECONFIRMATION_REQUEST_PATH = "/nightscout_watch_changeconfirmationrequest"; public static final String ACTION_CANCELNOTIFICATION_REQUEST_PATH = "/nightscout_watch_cancelnotificationrequest"; + String TAG = "WatchUpdateService"; private static boolean lastLoopStatus; @@ -156,7 +161,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog public int onStartCommand(Intent intent, int flags, int startId) { String action = intent != null ? intent.getAction() : null; - // Log.d(TAG, logPrefix + "onStartCommand: " + action); + // Log.d(TAG, "onStartCommand: " + action); if (wearIntegration()) { handler.post(() -> { @@ -235,7 +240,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog super.onPeerConnected(peer); String id = peer.getId(); String name = peer.getDisplayName(); - // Log.d(TAG, logPrefix + "onPeerConnected peer name & ID: " + name + "|" + id); + Log.d(TAG, "onPeerConnected peer name & ID: " + name + "|" + id); } @@ -244,14 +249,14 @@ public class WatchUpdaterService extends WearableListenerService implements Goog super.onPeerDisconnected(peer); String id = peer.getId(); String name = peer.getDisplayName(); - // Log.d(TAG, logPrefix + "onPeerDisconnected peer name & ID: " + name + "|" + id); + Log.d(TAG, "onPeerDisconnected peer name & ID: " + name + "|" + id); } @Override public void onMessageReceived(MessageEvent event) { - // Log.d(TAG, logPrefix + "onMessageRecieved: " + event); + // Log.d(TAG, "onMessageRecieved: " + event); if (wearIntegration()) { if (event != null && event.getPath().equals(WEARABLE_RESEND_PATH)) { @@ -283,7 +288,7 @@ public class WatchUpdaterService extends WearableListenerService implements Goog private void sendData() { GlucoseValue lastBG = iobCobCalculator.getAds().lastBg(); - // Log.d(TAG, logPrefix + "LastBg=" + lastBG); + // Log.d(TAG, "LastBg=" + lastBG); if (lastBG != null) { GlucoseStatus glucoseStatus = glucoseStatusProvider.getGlucoseStatusData(); @@ -364,6 +369,10 @@ public class WatchUpdaterService extends WearableListenerService implements Goog if (googleApiClient != null && !googleApiClient.isConnected() && !googleApiClient.isConnecting()) { googleApiConnect(); } + + sendPreferences(); + sendQuickWizard(); + long startTime = System.currentTimeMillis() - (long) (60000 * 60 * 5.5); GlucoseValue last_bg = iobCobCalculator.getAds().lastBg(); @@ -382,7 +391,6 @@ public class WatchUpdaterService extends WearableListenerService implements Goog entries.putDataMapArrayList("entries", dataMaps); (new SendToDataLayerThread(WEARABLE_DATA_PATH, googleApiClient)).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, entries); } - sendPreferences(); sendBasals(); sendStatus(); } @@ -720,19 +728,62 @@ public class WatchUpdaterService extends WearableListenerService implements Goog private void sendPreferences() { if (googleApiClient != null && googleApiClient.isConnected()) { + GlucoseUnit units = profileFunction.getUnits(); boolean wearcontrol = sp.getBoolean(R.string.key_wear_control, false); - + boolean mgdl = units.equals(GlucoseUnit.MGDL); + int percentage = sp.getInt(R.string.key_boluswizard_percentage, 100); + int maxCarbs = sp.getInt(R.string.key_treatmentssafety_maxcarbs, 48); + double maxBolus = sp.getDouble(R.string.key_treatmentssafety_maxbolus, 3.0); PutDataMapRequest dataMapRequest = PutDataMapRequest.create(NEW_PREFERENCES_PATH); //unique content dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis()); dataMapRequest.getDataMap().putBoolean(rh.gs(R.string.key_wear_control), wearcontrol); + dataMapRequest.getDataMap().putBoolean(rh.gs(R.string.key_units_mgdl), mgdl); + dataMapRequest.getDataMap().putInt(rh.gs(R.string.key_boluswizard_percentage), percentage); + dataMapRequest.getDataMap().putInt(rh.gs(R.string.key_treatmentssafety_maxcarbs), maxCarbs); + dataMapRequest.getDataMap().putDouble(rh.gs(R.string.key_treatmentssafety_maxbolus),maxBolus); PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest(); Wearable.DataApi.putDataItem(googleApiClient, putDataRequest); } else { - Log.e("SendStatus", "No connection to wearable available!"); + Log.e("SendPreferences", "No connection to wearable available!"); } } + private void sendQuickWizard() { + if (googleApiClient != null && googleApiClient.isConnected()) { + int size = quickWizard.size(); + ArrayList entities = new ArrayList<>(); + for(int i=0; i < size; i++) { + QuickWizardEntry q = quickWizard.get(i); + if (q.forDevice(QuickWizardEntry.DEVICE_WATCH)) { + entities.add(quickMap(q)); + } + } + + PutDataMapRequest dataMapRequest = PutDataMapRequest.create(QUICK_WIZARD_PATH); + + DataMap dm = dataMapRequest.getDataMap(); + dm.putLong("timestamp", System.currentTimeMillis()); + dm.putDataMapArrayList("quick_wizard", entities); + + PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest(); + Log.i(TAG, "sendQuickWizard: " + putDataRequest); + Wearable.DataApi.putDataItem(googleApiClient, putDataRequest); + } else { + Log.e("sendQuickWizard", "No connection to wearable available!"); + } + } + + private DataMap quickMap(QuickWizardEntry q) { + DataMap dm = new DataMap(); + dm.putString("guid", q.guid()); + dm.putString("button_text", q.buttonText()); + dm.putInt("carbs", q.carbs()); + dm.putInt("from", q.validFrom()); + dm.putInt("to", q.validTo()); + return dm; + } + @NonNull private String generateStatusString(Profile profile, String currentBasal, String iobSum, String iobDetail, String bgiString) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/insulin/ActivityGraph.kt b/app/src/main/java/info/nightscout/androidaps/plugins/insulin/ActivityGraph.kt index 93932d5633..c7f66ef4eb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/insulin/ActivityGraph.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/insulin/ActivityGraph.kt @@ -18,10 +18,11 @@ class ActivityGraph : GraphView { constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) - fun show(insulin: Insulin) { + fun show(insulin: Insulin, diaSample: Double? = null) { removeAllSeries() + val dia = diaSample ?: insulin.dia mSecondScale = null - val hours = floor(insulin.dia + 1).toLong() + val hours = floor(dia + 1).toLong() val bolus = Bolus( timestamp = 0, amount = 1.0, @@ -31,7 +32,7 @@ class ActivityGraph : GraphView { val iobArray: MutableList = ArrayList() var time: Long = 0 while (time <= T.hours(hours).msecs()) { - val iob = insulin.iobCalcForTreatment(bolus, time, insulin.dia) + val iob = insulin.iobCalcForTreatment(bolus, time, dia) activityArray.add(DataPoint(T.msecs(time).mins().toDouble(), iob.activityContrib)) iobArray.add(DataPoint(T.msecs(time).mins().toDouble(), iob.iobContrib)) time += T.mins(5).msecs() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfileFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfileFragment.kt index 0d37bb7c83..0b52087e66 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfileFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/profile/local/LocalProfileFragment.kt @@ -3,6 +3,7 @@ package info.nightscout.androidaps.plugins.profile.local import android.os.Bundle import android.text.Editable import android.text.TextWatcher +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -20,7 +21,6 @@ import info.nightscout.androidaps.dialogs.ProfileSwitchDialog import info.nightscout.androidaps.interfaces.ActivePlugin import info.nightscout.androidaps.interfaces.GlucoseUnit import info.nightscout.androidaps.interfaces.Profile -import info.nightscout.shared.logging.AAPSLogger import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.profile.local.events.EventLocalProfileChanged @@ -33,6 +33,8 @@ import info.nightscout.androidaps.utils.ui.TimeListEdit import info.nightscout.shared.SafeParse import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign +import info.nightscout.shared.logging.AAPSLogger +import java.math.RoundingMode import java.text.DecimalFormat import javax.inject.Inject @@ -61,6 +63,8 @@ class LocalProfileFragment : DaggerFragment() { binding.basalGraph.show(ProfileSealed.Pure(it)) binding.icGraph.show(ProfileSealed.Pure(it)) binding.isfGraph.show(ProfileSealed.Pure(it)) + binding.targetGraph.show(ProfileSealed.Pure(it)) + binding.insulinGraph.show(activePlugin.activeInsulin, SafeParse.stringToDouble(binding.dia.text)) } } @@ -117,6 +121,7 @@ class LocalProfileFragment : DaggerFragment() { processVisibilityOnClick(it) binding.target.visibility = View.VISIBLE } + binding.dia.editText?.id?.let { binding.diaLabel.labelFor = it } } fun build() { @@ -130,14 +135,22 @@ class LocalProfileFragment : DaggerFragment() { binding.name.addTextChangedListener(textWatch) binding.dia.setParams(currentProfile.dia, hardLimits.minDia(), hardLimits.maxDia(), 0.1, DecimalFormat("0.0"), false, null, textWatch) binding.dia.tag = "LP_DIA" - TimeListEdit(context, aapsLogger, dateUtil, view, R.id.ic_holder, "IC", rh.gs(R.string.ic_label), currentProfile.ic, null, hardLimits.minIC(), hardLimits.maxIC(), 0.1, DecimalFormat("0.0"), save) - basalView = TimeListEdit(context, aapsLogger, dateUtil, view, R.id.basal_holder, "BASAL", rh.gs(R.string.basal_label) + ": " + sumLabel(), currentProfile.basal, null, pumpDescription.basalMinimumRate, pumpDescription.basalMaximumRate, 0.01, DecimalFormat("0.00"), save) + TimeListEdit(context, aapsLogger, dateUtil, view, R.id.ic_holder, "IC", rh.gs(R.string.ic_long_label), currentProfile.ic, null, doubleArrayOf(hardLimits.minIC(), hardLimits.maxIC()), null, 0.1, DecimalFormat ("0.0"), save) + basalView = TimeListEdit(context, aapsLogger, dateUtil, view, R.id.basal_holder, "BASAL", rh.gs(R.string.basal_long_label) + ": " + sumLabel(), currentProfile.basal, null, doubleArrayOf(pumpDescription.basalMinimumRate, pumpDescription.basalMaximumRate), null, 0.01, DecimalFormat("0.00"), save) if (units == Constants.MGDL) { - TimeListEdit(context, aapsLogger, dateUtil, view, R.id.isf_holder, "ISF", rh.gs(R.string.isf_label), currentProfile.isf, null, HardLimits.MIN_ISF, HardLimits.MAX_ISF, 1.0, DecimalFormat("0"), save) - TimeListEdit(context, aapsLogger, dateUtil, view, R.id.target, "TARGET", rh.gs(R.string.target_label), currentProfile.targetLow, currentProfile.targetHigh, HardLimits.VERY_HARD_LIMIT_TARGET_BG[0], HardLimits.VERY_HARD_LIMIT_TARGET_BG[1], 1.0, DecimalFormat("0"), save) + val isfRange = doubleArrayOf(HardLimits.MIN_ISF, HardLimits.MAX_ISF) + TimeListEdit(context, aapsLogger, dateUtil, view, R.id.isf_holder, "ISF", rh.gs(R.string.isf_long_label), currentProfile.isf, null, isfRange , null, 1.0, DecimalFormat("0"), save) + TimeListEdit(context, aapsLogger, dateUtil, view, R.id.target_holder, "TARGET", rh.gs(R.string.target_long_label), currentProfile.targetLow, currentProfile.targetHigh, HardLimits.VERY_HARD_LIMIT_MIN_BG, HardLimits.VERY_HARD_LIMIT_TARGET_BG, 1.0, DecimalFormat("0"), save) } else { - TimeListEdit(context, aapsLogger, dateUtil, view, R.id.isf_holder, "ISF", rh.gs(R.string.isf_label), currentProfile.isf, null, Profile.fromMgdlToUnits(HardLimits.MIN_ISF, GlucoseUnit.MMOL), Profile.fromMgdlToUnits(HardLimits.MAX_ISF, GlucoseUnit.MMOL), 0.1, DecimalFormat("0.0"), save) - TimeListEdit(context, aapsLogger, dateUtil, view, R.id.target, "TARGET", rh.gs(R.string.target_label), currentProfile.targetLow, currentProfile.targetHigh, Profile.fromMgdlToUnits(HardLimits.VERY_HARD_LIMIT_TARGET_BG[0], GlucoseUnit.MMOL), Profile.fromMgdlToUnits(HardLimits.VERY_HARD_LIMIT_TARGET_BG[1], GlucoseUnit.MMOL), 0.1, DecimalFormat("0.0"), save) + val isfRange = doubleArrayOf(roundUp(Profile.fromMgdlToUnits(HardLimits.MIN_ISF, GlucoseUnit.MMOL)), + roundDown(Profile.fromMgdlToUnits(HardLimits.MAX_ISF, GlucoseUnit.MMOL))) + TimeListEdit(context, aapsLogger, dateUtil, view, R.id.isf_holder, "ISF", rh.gs(R.string.isf_long_label), currentProfile.isf, null,isfRange , null, 0.1, DecimalFormat("0.0"), save) + val range1 = doubleArrayOf(roundUp(Profile.fromMgdlToUnits(HardLimits.VERY_HARD_LIMIT_MIN_BG[0], GlucoseUnit.MMOL)), + roundDown(Profile.fromMgdlToUnits(HardLimits.VERY_HARD_LIMIT_MIN_BG[1], GlucoseUnit.MMOL))) + val range2 = doubleArrayOf(roundUp(Profile.fromMgdlToUnits(HardLimits.VERY_HARD_LIMIT_MAX_BG[0], GlucoseUnit.MMOL)), + roundDown(Profile.fromMgdlToUnits(HardLimits.VERY_HARD_LIMIT_MAX_BG[1], GlucoseUnit.MMOL))) + Log.i("TimeListEdit", "build: range1" + range1[0] + " " + range1[1] + " range2" + range2[0] + " " + range2[1]) + TimeListEdit(context, aapsLogger, dateUtil, view, R.id.target_holder, "TARGET", rh.gs(R.string.target_long_label), currentProfile.targetLow, currentProfile.targetHigh, range1 , range2, 0.1, DecimalFormat("0.0"), save) } // Spinner @@ -175,6 +188,8 @@ class LocalProfileFragment : DaggerFragment() { binding.basalGraph.show(ProfileSealed.Pure(it)) binding.icGraph.show(ProfileSealed.Pure(it)) binding.isfGraph.show(ProfileSealed.Pure(it)) + binding.targetGraph.show(ProfileSealed.Pure(it)) + binding.insulinGraph.show(activePlugin.activeInsulin, SafeParse.stringToDouble(binding.dia.text)) } binding.profileAdd.setOnClickListener { @@ -265,6 +280,14 @@ class LocalProfileFragment : DaggerFragment() { updateGUI() } + private fun roundUp(number: Double): Double { + return number.toBigDecimal().setScale(1, RoundingMode.UP).toDouble() + } + + private fun roundDown(number: Double): Double { + return number.toBigDecimal().setScale(1, RoundingMode.DOWN).toDouble() + } + private fun updateGUI() { if (_binding == null) return val isValid = localProfilePlugin.isValidEditState(activity) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/source/BGSourceFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/source/BGSourceFragment.kt index 7920902eda..d285db5f0a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/source/BGSourceFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/source/BGSourceFragment.kt @@ -7,8 +7,6 @@ import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import androidx.work.ListenableWorker -import androidx.work.workDataOf import dagger.android.support.DaggerFragment import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository @@ -118,7 +116,10 @@ class BGSourceFragment : DaggerFragment() { val glucoseValue = glucoseValues[position] holder.binding.ns.visibility = (glucoseValue.interfaceIDs.nightscoutId != null).toVisibility() holder.binding.invalid.visibility = (!glucoseValue.isValid).toVisibility() - holder.binding.date.text = dateUtil.dateAndTimeString(glucoseValue.timestamp) + val sameDayPrevious = position > 0 && dateUtil.isSameDay(glucoseValue.timestamp, glucoseValues[position-1].timestamp) + holder.binding.date.visibility = sameDayPrevious.not().toVisibility() + holder.binding.date.text = dateUtil.dateString(glucoseValue.timestamp) + holder.binding.time.text = dateUtil.timeString(glucoseValue.timestamp) holder.binding.value.text = glucoseValue.valueToUnitsString(profileFunction.getUnits()) holder.binding.direction.setImageResource(glucoseValue.trendArrow.directionToIcon()) holder.binding.remove.tag = glucoseValue diff --git a/app/src/main/java/info/nightscout/androidaps/utils/TrendCalculator.kt b/app/src/main/java/info/nightscout/androidaps/utils/TrendCalculator.kt index a5f2dc2dcb..869feb2fe8 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/TrendCalculator.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/TrendCalculator.kt @@ -1,20 +1,37 @@ package info.nightscout.androidaps.utils +import info.nightscout.androidaps.R import info.nightscout.androidaps.database.AppRepository import info.nightscout.androidaps.database.entities.GlucoseValue +import info.nightscout.androidaps.database.entities.GlucoseValue.TrendArrow.* +import info.nightscout.androidaps.utils.resources.ResourceHelper import javax.inject.Inject import javax.inject.Singleton @Singleton class TrendCalculator @Inject constructor( - private val repository: AppRepository + private val repository: AppRepository, + private val rh: ResourceHelper ) { fun getTrendArrow(glucoseValue: GlucoseValue?): GlucoseValue.TrendArrow = when { - glucoseValue?.trendArrow == null -> GlucoseValue.TrendArrow.NONE - glucoseValue.trendArrow != GlucoseValue.TrendArrow.NONE -> glucoseValue.trendArrow - else -> calculateDirection(glucoseValue) + glucoseValue?.trendArrow == null -> NONE + glucoseValue.trendArrow != NONE -> glucoseValue.trendArrow + else -> calculateDirection(glucoseValue) + } + + fun getTrendDescription(glucoseValue: GlucoseValue?): String = + when (getTrendArrow(glucoseValue)) { + DOUBLE_DOWN -> rh.gs(R.string.a11y_arrow_double_down) + SINGLE_DOWN -> rh.gs(R.string.a11y_arrow_single_down) + FORTY_FIVE_DOWN -> rh.gs(R.string.a11y_arrow_forty_five_down) + FLAT -> rh.gs(R.string.a11y_arrow_flat) + FORTY_FIVE_UP -> rh.gs(R.string.a11y_arrow_forty_five_up) + SINGLE_UP -> rh.gs(R.string.a11y_arrow_single_up) + DOUBLE_UP -> rh.gs(R.string.a11y_arrow_double_up) + NONE -> rh.gs(R.string.a11y_arrow_none) + else -> rh.gs(R.string.a11y_arrow_unknown) } private fun calculateDirection(glucoseValue: GlucoseValue): GlucoseValue.TrendArrow { @@ -23,7 +40,7 @@ class TrendCalculator @Inject constructor( val readings = repository.compatGetBgReadingsDataFromTime(toTime - T.mins(10).msecs(), toTime, false).blockingGet() if (readings.size < 2) - return GlucoseValue.TrendArrow.NONE + return NONE val current = readings[0] val previous = readings[1] @@ -35,14 +52,14 @@ class TrendCalculator @Inject constructor( val slopeByMinute = slope * 60000 return when { - slopeByMinute <= -3.5 -> GlucoseValue.TrendArrow.DOUBLE_DOWN - slopeByMinute <= -2 -> GlucoseValue.TrendArrow.SINGLE_DOWN - slopeByMinute <= -1 -> GlucoseValue.TrendArrow.FORTY_FIVE_DOWN - slopeByMinute <= 1 -> GlucoseValue.TrendArrow.FLAT - slopeByMinute <= 2 -> GlucoseValue.TrendArrow.FORTY_FIVE_UP - slopeByMinute <= 3.5 -> GlucoseValue.TrendArrow.SINGLE_UP - slopeByMinute <= 40 -> GlucoseValue.TrendArrow.DOUBLE_UP - else -> GlucoseValue.TrendArrow.NONE + slopeByMinute <= -3.5 -> DOUBLE_DOWN + slopeByMinute <= -2 -> SINGLE_DOWN + slopeByMinute <= -1 -> FORTY_FIVE_DOWN + slopeByMinute <= 1 -> FLAT + slopeByMinute <= 2 -> FORTY_FIVE_UP + slopeByMinute <= 3.5 -> SINGLE_UP + slopeByMinute <= 40 -> DOUBLE_UP + else -> NONE } } -} \ No newline at end of file +} diff --git a/app/src/main/java/info/nightscout/androidaps/utils/ui/TimeListEdit.java b/app/src/main/java/info/nightscout/androidaps/utils/ui/TimeListEdit.java index 5f435b91d3..1d79a9205e 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/ui/TimeListEdit.java +++ b/app/src/main/java/info/nightscout/androidaps/utils/ui/TimeListEdit.java @@ -57,6 +57,8 @@ public class TimeListEdit { private final double step; private final double min; private final double max; + private final double min2; + private final double max2; private final NumberFormat formatter; private final Runnable save; private LinearLayout layout; @@ -68,7 +70,7 @@ public class TimeListEdit { Context context, AAPSLogger aapsLogger, DateUtil dateUtil, - View view, int resLayoutId, String tagPrefix, String label, JSONArray data1, JSONArray data2, double min, double max, double step, NumberFormat formatter, Runnable save) { + View view, int resLayoutId, String tagPrefix, String label, JSONArray data1, JSONArray data2, double[] range1, double[] range2, double step, NumberFormat formatter, Runnable save) { this.context = context; this.aapsLogger = aapsLogger; this.dateUtil = dateUtil; @@ -79,8 +81,10 @@ public class TimeListEdit { this.data1 = data1; this.data2 = data2; this.step = step; - this.min = min; - this.max = max; + this.min = range1[0]; + this.max = range1[1]; + this.min2 = range2 != null ? range2[0] : 0; + this.max2 = range2 != null ? range2[1] : 0; this.formatter = formatter; this.save = save; buildView(); @@ -108,6 +112,7 @@ public class TimeListEdit { float factor = layout.getContext().getResources().getDisplayMetrics().density; finalAdd = new ImageView(context); finalAdd.setImageResource(R.drawable.ic_add); + finalAdd.setContentDescription(layout.getContext().getResources().getString(R.string.a11y_add_new_to_list)); LinearLayout.LayoutParams illp = new LinearLayout.LayoutParams((int) (35d * factor), (int) (35 * factor)); illp.setMargins(0, 25, 0, 25); // llp.setMargins(left, top, right, bottom); illp.gravity = Gravity.CENTER; @@ -177,7 +182,13 @@ public class TimeListEdit { numberPickers1[position].setTextWatcher(new TextWatcher() { @Override public void afterTextChanged(Editable s) { - editItem(position, secondFromMidnight(position), SafeParse.stringToDouble(numberPickers1[position].getText()), value2(position)); + Double value1 = SafeParse.stringToDouble(numberPickers1[position].getText()); + Double value2 = value2(position); + if (data2 != null && value1 > value2) { + value2 = value1; + numberPickers2[position].setValue(value2); + } + editItem(position, secondFromMidnight(position), value1, value2); callSave(); log(); } @@ -197,7 +208,13 @@ public class TimeListEdit { numberPickers2[position].setTextWatcher(new TextWatcher() { @Override public void afterTextChanged(Editable s) { - editItem(position, secondFromMidnight(position), value1(position), SafeParse.stringToDouble(numberPickers2[position].getText())); + Double value1 = value1(position); + Double value2 = SafeParse.stringToDouble(numberPickers2[position].getText()); + if (data2 != null && value2 < value1) { + value1 = value2; + numberPickers1[position].setValue(value1); + } + editItem(position, secondFromMidnight(position), value1, value2); callSave(); log(); } @@ -246,7 +263,7 @@ public class TimeListEdit { fillSpinner(timeSpinner, secondFromMidnight(i), previous, next); editText1.setParams(value1(i), min, max, step, formatter, false, null); - editText2.setParams(value2(i), min, max, step, formatter, false, null); + editText2.setParams(value2(i), min2, max2, step, formatter, false, null); if (data2 == null) { editText2.setVisibility(View.GONE); diff --git a/app/src/main/java/info/nightscout/androidaps/utils/wizard/BolusWizard.kt b/app/src/main/java/info/nightscout/androidaps/utils/wizard/BolusWizard.kt index 7dd5687e92..dca32f246a 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/wizard/BolusWizard.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/wizard/BolusWizard.kt @@ -18,6 +18,8 @@ import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.transactions.InsertOrUpdateBolusCalculatorResultTransaction import info.nightscout.androidaps.events.EventRefreshOverview import info.nightscout.androidaps.extensions.formatColor +import info.nightscout.androidaps.extensions.highValueToUnitsToString +import info.nightscout.androidaps.extensions.lowValueToUnitsToString import info.nightscout.androidaps.interfaces.* import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.logging.LTag @@ -135,27 +137,28 @@ class BolusWizard @Inject constructor( private var quickWizard: Boolean = true var usePercentage: Boolean = false - fun doCalc(profile: Profile, - profileName: String, - tempTarget: TemporaryTarget?, - carbs: Int, - cob: Double, - bg: Double, - correction: Double, - percentageCorrection: Int = 100, - useBg: Boolean, - useCob: Boolean, - includeBolusIOB: Boolean, - includeBasalIOB: Boolean, - useSuperBolus: Boolean, - useTT: Boolean, - useTrend: Boolean, - useAlarm: Boolean, - notes: String = "", - carbTime: Int = 0, - usePercentage: Boolean = false, - totalPercentage: Double = 100.0, - quickWizard: Boolean = false + fun doCalc( + profile: Profile, + profileName: String, + tempTarget: TemporaryTarget?, + carbs: Int, + cob: Double, + bg: Double, + correction: Double, + percentageCorrection: Int = 100, + useBg: Boolean, + useCob: Boolean, + includeBolusIOB: Boolean, + includeBasalIOB: Boolean, + useSuperBolus: Boolean, + useTT: Boolean, + useTrend: Boolean, + useAlarm: Boolean, + notes: String = "", + carbTime: Int = 0, + usePercentage: Boolean = false, + totalPercentage: Double = 100.0, + quickWizard: Boolean = false ): BolusWizard { this.profile = profile @@ -261,22 +264,23 @@ class BolusWizard @Inject constructor( return this } - private fun createBolusCalculatorResult(): BolusCalculatorResult = - BolusCalculatorResult( + private fun createBolusCalculatorResult(): BolusCalculatorResult { + val unit = profileFunction.getUnits() + return BolusCalculatorResult( timestamp = dateUtil.now(), - targetBGLow = targetBGLow, - targetBGHigh = targetBGHigh, - isf = sens, + targetBGLow = Profile.toMgdl(targetBGLow, unit), + targetBGHigh = Profile.toMgdl(targetBGHigh, unit), + isf = Profile.toMgdl(sens, unit), ic = ic, bolusIOB = insulinFromBolusIOB, wasBolusIOBUsed = includeBolusIOB, basalIOB = insulinFromBasalIOB, wasBasalIOBUsed = includeBasalIOB, - glucoseValue = bg, + glucoseValue = Profile.toMgdl(bg, unit), wasGlucoseUsed = useBg && bg > 0, glucoseDifference = bgDiff, glucoseInsulin = insulinFromBG, - glucoseTrend = trend, + glucoseTrend = Profile.fromMgdlToUnits(trend, unit), wasTrendUsed = useTrend, trendInsulin = insulinFromTrend, cob = cob, @@ -294,6 +298,7 @@ class BolusWizard @Inject constructor( profileName = profileName, note = notes ) + } private fun confirmMessageAfterConstraints(advisor: Boolean): Spanned { @@ -312,7 +317,9 @@ class BolusWizard @Inject constructor( actions.add(rh.gs(R.string.carbs) + ": " + rh.gs(R.string.format_carbs, carbs).formatColor(rh, R.color.carbs) + timeShift) } if (insulinFromCOB > 0) { - actions.add(rh.gs(R.string.cobvsiob) + ": " + rh.gs(R.string.formatsignedinsulinunits, insulinFromBolusIOB + insulinFromBasalIOB + insulinFromCOB + insulinFromBG).formatColor(rh, R.color.cobAlert)) + actions.add( + rh.gs(R.string.cobvsiob) + ": " + rh.gs(R.string.formatsignedinsulinunits, insulinFromBolusIOB + insulinFromBasalIOB + insulinFromCOB + insulinFromBG).formatColor(rh, R.color.cobAlert) + ) val absorptionRate = iobCobCalculator.ads.slowAbsorptionPercentage(60) if (absorptionRate > .25) actions.add(rh.gs(R.string.slowabsorptiondetected, rh.gc(R.color.cobAlert), (absorptionRate * 100).toInt())) @@ -342,11 +349,13 @@ class BolusWizard @Inject constructor( carbTimer.removeEatReminder() if (sp.getBoolean(R.string.key_usebolusadvisor, false) && Profile.toMgdl(bg, profile.units) > 180 && carbs > 0 && carbTime >= 0) OKDialog.showYesNoCancel(ctx, rh.gs(R.string.bolusadvisor), rh.gs(R.string.bolusadvisormessage), - { bolusAdvisorProcessing(ctx) }, - { commonProcessing(ctx) } + { bolusAdvisorProcessing(ctx) }, + { commonProcessing(ctx) } ) else commonProcessing(ctx) + } else { + OKDialog.show(ctx, rh.gs(R.string.boluswizard), rh.gs(R.string.no_action_selected)) } } @@ -363,10 +372,13 @@ class BolusWizard @Inject constructor( carbTime = 0 bolusCalculatorResult = createBolusCalculatorResult() notes = this@BolusWizard.notes - uel.log(Action.BOLUS_ADVISOR, if (quickWizard) Sources.QuickWizard else Sources.WizardDialog, + uel.log( + Action.BOLUS_ADVISOR, + if (quickWizard) Sources.QuickWizard else Sources.WizardDialog, notes, ValueWithUnit.TherapyEventType(eventType.toDBbEventType()), - ValueWithUnit.Insulin(insulinAfterConstraints)) + ValueWithUnit.Insulin(insulinAfterConstraints) + ) if (insulin > 0) { commandQueue.bolus(this, object : Callback() { override fun run() { @@ -381,6 +393,26 @@ class BolusWizard @Inject constructor( }) } + fun explainShort(): String { + var message = rh.gs(R.string.wizard_explain_calc, ic, sens) + message += "\n" + rh.gs(R.string.wizard_explain_carbs, insulinFromCarbs) + if (useTT && tempTarget != null) { + val tt = if (tempTarget?.lowTarget == tempTarget?.highTarget) tempTarget?.lowValueToUnitsToString(profile.units) + else rh.gs(R.string.wizard_explain_tt_to, tempTarget?.lowValueToUnitsToString(profile.units), tempTarget?.highValueToUnitsToString(profile.units)) + message += "\n" + rh.gs(R.string.wizard_explain_tt, tt) + } + if (useCob) message += "\n" + rh.gs(R.string.wizard_explain_cob, cob, insulinFromCOB) + if (useBg) message += "\n" + rh.gs(R.string.wizard_explain_bg, insulinFromBG) + if (includeBolusIOB) message += "\n" + rh.gs(R.string.wizard_explain_bolus_iob, insulinFromBolusIOB) + if (includeBasalIOB) message += "\n" + rh.gs(R.string.wizard_explain_basal_iob, insulinFromBasalIOB) + if (useTrend) message += "\n" + rh.gs(R.string.wizard_explain_trend, insulinFromTrend) + if (useSuperBolus) message += "\n" + rh.gs(R.string.wizard_explain_superbolus, insulinFromSuperBolus) + if (percentageCorrection != 100) { + message += "\n" + rh.gs(R.string.wizard_explain_percent, totalBeforePercentageAdjustment, percentageCorrection, calculatedTotalInsulin) + } + return message + } + private fun commonProcessing(ctx: Context) { val profile = profileFunction.getProfile() ?: return val pump = activePlugin.activePump @@ -429,17 +461,17 @@ class BolusWizard @Inject constructor( bolusCalculatorResult = createBolusCalculatorResult() notes = this@BolusWizard.notes if (insulin > 0 || carbs > 0) { - val action = when { + val action = when { insulinAfterConstraints.equals(0.0) -> Action.CARBS carbs.equals(0.0) -> Action.BOLUS else -> Action.TREATMENT } uel.log(action, if (quickWizard) Sources.QuickWizard else Sources.WizardDialog, - notes, - ValueWithUnit.TherapyEventType(eventType.toDBbEventType()), - ValueWithUnit.Insulin(insulinAfterConstraints).takeIf { insulinAfterConstraints != 0.0 }, - ValueWithUnit.Gram(this@BolusWizard.carbs).takeIf { this@BolusWizard.carbs != 0 }, - ValueWithUnit.Minute(carbTime).takeIf { carbTime != 0 }) + notes, + ValueWithUnit.TherapyEventType(eventType.toDBbEventType()), + ValueWithUnit.Insulin(insulinAfterConstraints).takeIf { insulinAfterConstraints != 0.0 }, + ValueWithUnit.Gram(this@BolusWizard.carbs).takeIf { this@BolusWizard.carbs != 0 }, + ValueWithUnit.Minute(carbTime).takeIf { carbTime != 0 }) commandQueue.bolus(this, object : Callback() { override fun run() { if (!result.success) { @@ -465,9 +497,9 @@ class BolusWizard @Inject constructor( private fun calcPercentageWithConstraints() { calculatedPercentage = 100.0 if (totalBeforePercentageAdjustment != insulinFromCorrection) - calculatedPercentage = calculatedTotalInsulin/(totalBeforePercentageAdjustment-insulinFromCorrection) * 100 + calculatedPercentage = calculatedTotalInsulin / (totalBeforePercentageAdjustment - insulinFromCorrection) * 100 calculatedPercentage = max(calculatedPercentage, 10.0) - calculatedPercentage = min(calculatedPercentage,250.0) + calculatedPercentage = min(calculatedPercentage, 250.0) } private fun calcCorrectionWithConstraints() { @@ -477,4 +509,4 @@ class BolusWizard @Inject constructor( calculatedCorrection = max(-constraintChecker.getMaxBolusAllowed().value(), calculatedCorrection) } -} \ No newline at end of file +} diff --git a/app/src/main/java/info/nightscout/androidaps/utils/wizard/QuickWizard.kt b/app/src/main/java/info/nightscout/androidaps/utils/wizard/QuickWizard.kt index a254831690..b48d44c9cc 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/wizard/QuickWizard.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/wizard/QuickWizard.kt @@ -1,10 +1,12 @@ package info.nightscout.androidaps.utils.wizard +import android.util.Log import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R import info.nightscout.shared.sharedPreferences.SP import org.json.JSONArray import org.json.JSONObject +import java.util.* import javax.inject.Inject import javax.inject.Singleton @@ -18,6 +20,18 @@ class QuickWizard @Inject constructor( init { setData(JSONArray(sp.getString(R.string.key_quickwizard, "[]"))) + setGuidsForOldEntries() + } + + private fun setGuidsForOldEntries() { + // for migration purposes; guid is a new required property + for (i in 0 until storage.length()) { + val entry = QuickWizardEntry(injector).from(storage.get(i) as JSONObject, i) + if (entry.guid() == "") { + val guid = UUID.randomUUID().toString() + entry.storage.put("guid", guid) + } + } } fun getActive(): QuickWizardEntry? { @@ -41,6 +55,38 @@ class QuickWizard @Inject constructor( operator fun get(position: Int): QuickWizardEntry = QuickWizardEntry(injector).from(storage.get(position) as JSONObject, position) + fun get(guid: String): QuickWizardEntry? { + for (i in 0 until storage.length()) { + val entry = QuickWizardEntry(injector).from(storage.get(i) as JSONObject, i) + if (entry.guid() == guid) { + return entry + } + } + return null + } + + fun move(from: Int, to: Int) { + Log.i("QuickWizard", "moveItem: $from $to") + val fromEntry = storage[from] as JSONObject + storage.remove(from) + addToPos(to, fromEntry, storage) + save() + } + + fun removePos(pos: Int, jsonObj: JSONObject?, jsonArr: JSONArray) { + for (i in jsonArr.length() downTo pos + 1) { + jsonArr.put(i, jsonArr[i - 1]) + } + jsonArr.put(pos, jsonObj) + } + + private fun addToPos(pos: Int, jsonObj: JSONObject?, jsonArr: JSONArray) { + for (i in jsonArr.length() downTo pos + 1) { + jsonArr.put(i, jsonArr[i - 1]) + } + jsonArr.put(pos, jsonObj) + } + fun newEmptyItem(): QuickWizardEntry { return QuickWizardEntry(injector) } @@ -57,4 +103,5 @@ class QuickWizard @Inject constructor( storage.remove(position) save() } + } diff --git a/app/src/main/java/info/nightscout/androidaps/utils/wizard/QuickWizardEntry.kt b/app/src/main/java/info/nightscout/androidaps/utils/wizard/QuickWizardEntry.kt index 79ff43bf66..c6dee8a660 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/wizard/QuickWizardEntry.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/wizard/QuickWizardEntry.kt @@ -19,6 +19,7 @@ import info.nightscout.androidaps.utils.JsonHelper.safeGetString import info.nightscout.shared.sharedPreferences.SP import org.json.JSONException import org.json.JSONObject +import java.util.* import javax.inject.Inject class QuickWizardEntry @Inject constructor(private val injector: HasAndroidInjector) { @@ -41,11 +42,26 @@ class QuickWizardEntry @Inject constructor(private val injector: HasAndroidInjec const val NO = 1 private const val POSITIVE_ONLY = 2 private const val NEGATIVE_ONLY = 3 + const val DEVICE_ALL = 0 + const val DEVICE_PHONE = 1 + const val DEVICE_WATCH = 2 + const val DEFAULT = 0 + const val CUSTOM = 1 } init { injector.androidInjector().inject(this) - val emptyData = "{\"buttonText\":\"\",\"carbs\":0,\"validFrom\":0,\"validTo\":86340}" + val guid = UUID.randomUUID().toString() + val emptyData = """{ + "guid": "$guid", + "buttonText": "", + "carbs": 0, + "validFrom": 0, + "validTo": 86340, + "device": "all", + "usePercentage": "default", + "percentage": 100 + }""".trimMargin() try { storage = JSONObject(emptyData) } catch (e: JSONException) { @@ -55,6 +71,8 @@ class QuickWizardEntry @Inject constructor(private val injector: HasAndroidInjec /* { + guid: string, + device: string, // (phone, watch, all) buttonText: "Meal", carbs: 36, validFrom: 8 * 60 * 60, // seconds from midnight @@ -66,15 +84,18 @@ class QuickWizardEntry @Inject constructor(private val injector: HasAndroidInjec useTrend: 0, useSuperBolus: 0, useTemptarget: 0 + usePercentage: string, // default, custom + percentage: int, } */ fun from(entry: JSONObject, position: Int): QuickWizardEntry { + // TODO set guid if missing for migration storage = entry this.position = position return this } - fun isActive(): Boolean = profileFunction.secondsFromMidnight() >= validFrom() && profileFunction.secondsFromMidnight() <= validTo() + fun isActive(): Boolean = profileFunction.secondsFromMidnight() >= validFrom() && profileFunction.secondsFromMidnight() <= validTo() && forDevice(DEVICE_PHONE) fun doCalc(profile: Profile, profileName: String, lastBG: GlucoseValue, _synchronized: Boolean): BolusWizard { val dbRecord = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() @@ -119,10 +140,16 @@ class QuickWizardEntry @Inject constructor(private val injector: HasAndroidInjec } else if (useTrend() == NEGATIVE_ONLY && glucoseStatus != null && glucoseStatus.shortAvgDelta < 0) { trend = true } - val percentage = sp.getInt(R.string.key_boluswizard_percentage, 100) + val percentage = if (usePercentage() == DEFAULT) sp.getInt(R.string.key_boluswizard_percentage, 100) else percentage() return BolusWizard(injector).doCalc(profile, profileName, tempTarget, carbs(), cob, bg, 0.0, percentage, true, useCOB() == YES, bolusIOB, basalIOB, superBolus, useTempTarget() == YES, trend, false, buttonText(), quickWizard = true) //tbc, ok if only quickwizard, but if other sources elsewhere use Sources.QuickWizard } + fun guid(): String = safeGetString(storage, "guid", "") + + fun device(): Int = safeGetInt(storage, "device", DEVICE_ALL) + + fun forDevice(device: Int) = device() == device || device() == DEVICE_ALL + fun buttonText(): String = safeGetString(storage, "buttonText", "") fun carbs(): Int = safeGetInt(storage, "carbs") @@ -148,4 +175,8 @@ class QuickWizardEntry @Inject constructor(private val injector: HasAndroidInjec fun useSuperBolus(): Int = safeGetInt(storage, "useSuperBolus", NO) fun useTempTarget(): Int = safeGetInt(storage, "useTempTarget", NO) -} \ No newline at end of file + + fun usePercentage(): Int = safeGetInt(storage, "usePercentage", DEFAULT) + + fun percentage(): Int = safeGetInt(storage, "percentage", 100) +} diff --git a/app/src/main/res/drawable/cb_background_bg.xml b/app/src/main/res/drawable/cb_background_bg.xml new file mode 100644 index 0000000000..cf493051a9 --- /dev/null +++ b/app/src/main/res/drawable/cb_background_bg.xml @@ -0,0 +1,7 @@ + + + + diff --git a/app/src/main/res/drawable/cb_background_cob.xml b/app/src/main/res/drawable/cb_background_cob.xml new file mode 100644 index 0000000000..b73f1f3b39 --- /dev/null +++ b/app/src/main/res/drawable/cb_background_cob.xml @@ -0,0 +1,7 @@ + + + + diff --git a/app/src/main/res/drawable/cb_background_iob.xml b/app/src/main/res/drawable/cb_background_iob.xml new file mode 100644 index 0000000000..bce5594f2a --- /dev/null +++ b/app/src/main/res/drawable/cb_background_iob.xml @@ -0,0 +1,7 @@ + + + + diff --git a/app/src/main/res/drawable/cb_background_trend.xml b/app/src/main/res/drawable/cb_background_trend.xml new file mode 100644 index 0000000000..74e90acb2f --- /dev/null +++ b/app/src/main/res/drawable/cb_background_trend.xml @@ -0,0 +1,7 @@ + + + + diff --git a/app/src/main/res/drawable/checkbox_bg_icon.xml b/app/src/main/res/drawable/checkbox_bg_icon.xml new file mode 100644 index 0000000000..f22b42b4d9 --- /dev/null +++ b/app/src/main/res/drawable/checkbox_bg_icon.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/res/drawable/checkbox_cob_icon.xml b/app/src/main/res/drawable/checkbox_cob_icon.xml new file mode 100644 index 0000000000..391e1bf226 --- /dev/null +++ b/app/src/main/res/drawable/checkbox_cob_icon.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/res/drawable/checkbox_iob_icon.xml b/app/src/main/res/drawable/checkbox_iob_icon.xml new file mode 100644 index 0000000000..f9bcb46d52 --- /dev/null +++ b/app/src/main/res/drawable/checkbox_iob_icon.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/res/drawable/checkbox_trend_icon.xml b/app/src/main/res/drawable/checkbox_trend_icon.xml new file mode 100644 index 0000000000..025544c3fd --- /dev/null +++ b/app/src/main/res/drawable/checkbox_trend_icon.xml @@ -0,0 +1,4 @@ + + + + diff --git a/app/src/main/res/drawable/ic_patch_pump_outline.xml b/app/src/main/res/drawable/ic_patch_pump_outline.xml new file mode 100644 index 0000000000..6e95a27545 --- /dev/null +++ b/app/src/main/res/drawable/ic_patch_pump_outline.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/app/src/main/res/layout/activity_logsetting.xml b/app/src/main/res/layout/activity_logsetting.xml index 50e1354cf1..4011279d11 100644 --- a/app/src/main/res/layout/activity_logsetting.xml +++ b/app/src/main/res/layout/activity_logsetting.xml @@ -23,7 +23,7 @@ + android:layout_height="40dp" + app:customContentDescription="@string/age" /> @@ -105,14 +105,14 @@ android:layout_width="150dp" android:layout_height="wrap_content" android:layout_marginStart="10dp" - android:labelFor="@+id/weight" android:text="@string/tdd_total" android:textAppearance="@style/TextAppearance.AppCompat.Medium" /> + android:layout_height="40dp" + app:customContentDescription="@string/tdd_total" /> @@ -123,18 +123,18 @@ android:gravity="center_vertical"> + android:layout_height="40dp" + app:customContentDescription="@string/weight_label" /> @@ -149,14 +149,14 @@ android:layout_width="150dp" android:layout_height="wrap_content" android:layout_marginStart="10dp" - android:labelFor="@+id/basalpctfromtdd" android:text="@string/basalpctfromtdd_label" android:textAppearance="@style/TextAppearance.AppCompat.Medium" /> + android:layout_height="40dp" + app:customContentDescription="@string/basalpctfromtdd_label" /> diff --git a/app/src/main/res/layout/activity_stats.xml b/app/src/main/res/layout/activity_stats.xml index 6335214899..cbadce20e9 100644 --- a/app/src/main/res/layout/activity_stats.xml +++ b/app/src/main/res/layout/activity_stats.xml @@ -23,7 +23,7 @@ + card_view:cardUseCompatPadding="true"> + android:orientation="vertical"> + android:background="@color/list_delimiter" + android:gravity="center" + android:text="1.1.2000" + android:textAppearance="?android:attr/textAppearanceMedium" /> - + android:gravity="center" + android:orientation="horizontal" + android:padding="6dp"> - + - + - + - + + + + + + + diff --git a/app/src/main/res/layout/careportal_stats_fragment.xml b/app/src/main/res/layout/careportal_stats_fragment.xml index b42553aa31..24e86b19f1 100644 --- a/app/src/main/res/layout/careportal_stats_fragment.xml +++ b/app/src/main/res/layout/careportal_stats_fragment.xml @@ -8,7 +8,8 @@ + android:layout_height="wrap_content" + android:focusable="true"> - + android:layout_height="wrap_content" + android:focusable="true"> + android:layout_height="wrap_content" + android:focusable="true"> + android:layout_height="wrap_content" + android:focusable="true"> + android:layout_height="wrap_content" + android:focusable="true"> + android:layout_height="wrap_content" + android:focusable="true"> + android:layout_height="wrap_content" + android:focusable="true"> + android:layout_height="wrap_content" + android:focusable="true"> + android:layout_height="40dp" + app:customContentDescription="@string/treatments_wizard_bg_label" /> - - + android:padding="2dp" + android:layoutDirection="rtl" + android:contentDescription="@string/a11y_carb_reminder" + android:drawableEnd="@drawable/ic_access_alarm_24dp" /> + + android:layout_height="40dp" + app:customContentDescription="@string/time_offset" /> + android:layout_gravity="center_horizontal" + app:customContentDescription="@string/careportal_newnstreatment_duration_label" /> + android:layout_height="40dp" + app:customContentDescription="@string/treatments_wizard_carbs_label" /> + android:layout_height="40dp" + app:customContentDescription="@string/treatments_wizard_bg_label" /> + android:layout_height="40dp" + app:customContentDescription="@string/careportal_newnstreatment_duration_label" /> + android:layout_height="40dp" + app:customContentDescription="@string/overview_insulin_label" /> + android:layout_height="40dp" + app:customContentDescription="@string/careportal_newnstreatment_duration_label" /> + android:paddingRight="5dp" + app:customContentDescription="@string/overview_insulin_label" /> + android:layout_height="40dp" + app:customContentDescription="@string/time_offset"/> + android:layout_height="40dp" + app:customContentDescription="@string/overview_insulin_label"/>