diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d41d81ef4c..c29ed184a8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -130,7 +130,6 @@ Overview Treatments Virtual Pump - Careportal Pump diff --git a/automation/src/main/java/info/nightscout/androidaps/automation/di/AutomationModule.kt b/automation/src/main/java/info/nightscout/androidaps/automation/di/AutomationModule.kt index 977cc208d5..b5bf1ccaf1 100644 --- a/automation/src/main/java/info/nightscout/androidaps/automation/di/AutomationModule.kt +++ b/automation/src/main/java/info/nightscout/androidaps/automation/di/AutomationModule.kt @@ -39,6 +39,7 @@ abstract class AutomationModule { @ContributesAndroidInjector abstract fun actionLoopSuspendInjector(): ActionLoopSuspend @ContributesAndroidInjector abstract fun actionNotificationInjector(): ActionNotification @ContributesAndroidInjector abstract fun actionAlarmInjector(): ActionAlarm + @ContributesAndroidInjector abstract fun actionCarePortalEventInjector(): ActionCarePortalEvent @ContributesAndroidInjector abstract fun actionProfileSwitchInjector(): ActionProfileSwitch @ContributesAndroidInjector abstract fun actionProfileSwitchPercentInjector(): ActionProfileSwitchPercent @ContributesAndroidInjector abstract fun actionSendSMSInjector(): ActionSendSMS diff --git a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationPlugin.kt b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationPlugin.kt index 404df4f4bb..cba9df8617 100644 --- a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationPlugin.kt +++ b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/AutomationPlugin.kt @@ -293,6 +293,7 @@ class AutomationPlugin @Inject constructor( ActionStopTempTarget(injector), ActionNotification(injector), ActionAlarm(injector), + ActionCarePortalEvent(injector), ActionProfileSwitchPercent(injector), ActionProfileSwitch(injector), ActionSendSMS(injector) diff --git a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/Action.kt b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/Action.kt index 184b082cf5..69b3dc8346 100644 --- a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/Action.kt +++ b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/Action.kt @@ -51,6 +51,8 @@ abstract class Action(val injector: HasAndroidInjector) { return when (type) { ActionAlarm::class.java.name, // backward compatibility ActionAlarm::class.java.simpleName -> ActionAlarm(injector).fromJSON(data.toString()) + ActionCarePortalEvent::class.java.name, + ActionCarePortalEvent::class.java.simpleName -> ActionCarePortalEvent(injector).fromJSON(data.toString()) ActionDummy::class.java.name, ActionDummy::class.java.simpleName -> ActionDummy(injector).fromJSON(data.toString()) ActionLoopDisable::class.java.name, diff --git a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionCarePortalEvent.kt b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionCarePortalEvent.kt new file mode 100644 index 0000000000..617d56c121 --- /dev/null +++ b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionCarePortalEvent.kt @@ -0,0 +1,119 @@ +package info.nightscout.androidaps.plugins.general.automation.actions + +import android.widget.LinearLayout +import androidx.annotation.DrawableRes +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.automation.R +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.entities.TherapyEvent +import info.nightscout.androidaps.database.entities.UserEntry +import info.nightscout.androidaps.database.entities.ValueWithUnit +import info.nightscout.androidaps.database.transactions.InsertIfNewByTimestampTherapyEventTransaction +import info.nightscout.androidaps.extensions.fromConstant +import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.androidaps.logging.LTag +import info.nightscout.androidaps.logging.UserEntryLogger +import info.nightscout.androidaps.plugins.general.automation.elements.* +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider +import info.nightscout.androidaps.queue.Callback +import info.nightscout.androidaps.utils.DateUtil +import info.nightscout.androidaps.utils.JsonHelper +import info.nightscout.androidaps.utils.T +import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.androidaps.utils.sharedPreferences.SP +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.rxkotlin.plusAssign +import org.json.JSONObject +import javax.inject.Inject + +class ActionCarePortalEvent(injector: HasAndroidInjector) : Action(injector) { + + @Inject lateinit var resourceHelper: ResourceHelper + @Inject lateinit var repository: AppRepository + @Inject lateinit var profileFunction: ProfileFunction + @Inject lateinit var dateUtil: DateUtil + @Inject lateinit var sp: SP + @Inject lateinit var glucoseStatusProvider: GlucoseStatusProvider + @Inject lateinit var uel: UserEntryLogger + + + private val disposable = CompositeDisposable() + + var note = InputString() + var duration = InputDuration(0, InputDuration.TimeUnit.MINUTES) + var cpEvent = InputCarePortalMenu(resourceHelper) + private var valuesWithUnit = mutableListOf() + + private constructor(injector: HasAndroidInjector, actionCPEvent: ActionCarePortalEvent) : this(injector) { + cpEvent = InputCarePortalMenu(resourceHelper, actionCPEvent.cpEvent.value) + } + + override fun friendlyName(): Int = R.string.careportal + override fun shortDescription(): String = resourceHelper.gs(cpEvent.value.stringRes, note.value) + + @DrawableRes override fun icon(): Int = cpEvent.value.drawableRes + + override fun doAction(callback: Callback) { + val enteredBy = sp.getString("careportal_enteredby", "AndroidAPS") + val eventTime = dateUtil.now() + val therapyEvent = TherapyEvent( + timestamp = eventTime, + type = cpEvent.value.therapyEventType, + glucoseUnit = TherapyEvent.GlucoseUnit.fromConstant(profileFunction.getUnits()) + ) + valuesWithUnit.add(ValueWithUnit.TherapyEventType(therapyEvent.type)) + + therapyEvent.enteredBy = enteredBy + if ( therapyEvent.type == TherapyEvent.Type.QUESTION || therapyEvent.type == TherapyEvent.Type.ANNOUNCEMENT) { + val glucoseStatus = glucoseStatusProvider.glucoseStatusData + if (glucoseStatus != null) { + therapyEvent.glucose = glucoseStatus.glucose + therapyEvent.glucoseType = TherapyEvent.MeterType.SENSOR + valuesWithUnit.add(ValueWithUnit.fromGlucoseUnit(glucoseStatus.glucose, profileFunction.getUnits().asText)) + valuesWithUnit.add(ValueWithUnit.TherapyEventMeterType(TherapyEvent.MeterType.SENSOR)) + } + } else { + therapyEvent.duration = T.mins(duration.value.toLong()).msecs() + valuesWithUnit.add(ValueWithUnit.Minute(duration.value).takeIf { !duration.value.equals(0) } ) + } + therapyEvent.note = note.value + valuesWithUnit.add(ValueWithUnit.SimpleString(note.value).takeIf { note.value.isNotBlank() } ) + disposable += repository.runTransactionForResult(InsertIfNewByTimestampTherapyEventTransaction(therapyEvent)) + .subscribe( + { result -> result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted therapy event $it") } }, + { aapsLogger.error(LTag.DATABASE, "Error while saving therapy event", it) } + ) + uel.log(UserEntry.Action.CAREPORTAL, UserEntry.Sources.Automation, title, valuesWithUnit) + } + + override fun toJSON(): String { + val data = JSONObject() + .put("cpEvent", cpEvent.value) + .put("note", note.value) + .put("durationInMinutes", duration.value) + return JSONObject() + .put("type", this.javaClass.name) + .put("data", data) + .toString() + } + + override fun fromJSON(data: String): Action { + val o = JSONObject(data) + cpEvent.value = InputCarePortalMenu.EventType.valueOf(JsonHelper.safeGetString(o, "cpEvent")!!) + note.value = JsonHelper.safeGetString(o, "note", "") + duration.value = JsonHelper.safeGetInt(o, "durationInMinutes") + return this + } + + override fun hasDialog(): Boolean = true + + override fun generateDialog(root: LinearLayout) { + LayoutBuilder() + .add(cpEvent) + .add(LabelWithElement(resourceHelper, resourceHelper.gs(R.string.duration_min_label), "", duration)) + .add(LabelWithElement(resourceHelper, resourceHelper.gs(R.string.notes_label), "", note)) + .build(root) + } + + override fun isValid(): Boolean = true +} \ No newline at end of file diff --git a/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputCarePortalMenu.kt b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputCarePortalMenu.kt new file mode 100644 index 0000000000..42b3437f34 --- /dev/null +++ b/automation/src/main/java/info/nightscout/androidaps/plugins/general/automation/elements/InputCarePortalMenu.kt @@ -0,0 +1,84 @@ +package info.nightscout.androidaps.plugins.general.automation.elements + +import android.view.View +import android.widget.AdapterView +import android.widget.ArrayAdapter +import android.widget.LinearLayout +import android.widget.Spinner +import androidx.annotation.DrawableRes +import androidx.annotation.LayoutRes +import androidx.annotation.StringRes +import androidx.core.graphics.drawable.IconCompat +import info.nightscout.androidaps.automation.R +import info.nightscout.androidaps.database.entities.TherapyEvent +import info.nightscout.androidaps.database.entities.UserEntry +import info.nightscout.androidaps.utils.resources.ResourceHelper + +class InputCarePortalMenu(private val resourceHelper: ResourceHelper) : Element() { + + enum class EventType (val therapyEventType: TherapyEvent.Type) { + NOTE (TherapyEvent.Type.NOTE), + EXERCISE (TherapyEvent.Type.EXERCISE), + QUESTION (TherapyEvent.Type.QUESTION), + ANNOUNCEMENT (TherapyEvent.Type.ANNOUNCEMENT); + + @get:StringRes val stringRes: Int + get() = when (this) { + NOTE -> R.string.careportal_note_message + EXERCISE -> R.string.careportal_exercise_message + QUESTION -> R.string.careportal_question_message + ANNOUNCEMENT -> R.string.careportal_announcement_message + } + + @get:DrawableRes val drawableRes: Int + get() = when (this) { + NOTE -> R.drawable.ic_cp_note + EXERCISE -> R.drawable.ic_cp_exercise + QUESTION -> R.drawable.ic_cp_question + ANNOUNCEMENT -> R.drawable.ic_cp_announcement + } + + companion object { + fun labels(resourceHelper: ResourceHelper): List { + val list: MutableList = ArrayList() + for (e in values()) { + list.add(resourceHelper.gs(e.stringRes)) + } + return list + } + } + } + + constructor(resourceHelper: ResourceHelper, value: EventType) : this(resourceHelper) { + this.value = value + } + + var value = EventType.NOTE + + override fun addToLayout(root: LinearLayout) { + val spinner = Spinner(root.context) + val spinnerArrayAdapter = ArrayAdapter(root.context, R.layout.spinner_centered, EventType.labels(resourceHelper)) + spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) + spinner.adapter = spinnerArrayAdapter + val spinnerParams = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT + ) + spinnerParams.setMargins(0, resourceHelper.dpToPx(4), 0, resourceHelper.dpToPx(4)) + spinner.layoutParams = spinnerParams + spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { + value = EventType.values()[position] + } + + override fun onNothingSelected(parent: AdapterView<*>?) {} + } + spinner.setSelection(value.ordinal) + root.addView(spinner) + } + + fun setValue(eventType: EventType): InputCarePortalMenu { + value = eventType + return this + } +} \ No newline at end of file diff --git a/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionCarePortalEventTest.kt b/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionCarePortalEventTest.kt new file mode 100644 index 0000000000..2507c1cb85 --- /dev/null +++ b/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionCarePortalEventTest.kt @@ -0,0 +1,81 @@ +package info.nightscout.androidaps.plugins.general.automation.actions + +import info.nightscout.androidaps.automation.R +import info.nightscout.androidaps.database.transactions.InsertIfNewByTimestampTherapyEventTransaction +import info.nightscout.androidaps.database.transactions.Transaction +import info.nightscout.androidaps.interfaces.GlucoseUnit +import info.nightscout.androidaps.plugins.general.automation.elements.InputCarePortalMenu +import info.nightscout.androidaps.plugins.general.automation.elements.InputDuration +import info.nightscout.androidaps.plugins.general.automation.elements.InputString +import info.nightscout.androidaps.queue.Callback +import io.reactivex.Single +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.mockito.ArgumentMatchers +import org.mockito.Mockito.`when` + +class ActionCarePortalEventTest : ActionsTestBase() { + + private lateinit var sut: ActionCarePortalEvent + + @Before + fun setup() { + `when`(sp.getString(ArgumentMatchers.anyString(), ArgumentMatchers.anyString())).thenReturn("AndroidAPS") + `when`( + resourceHelper.gs( + ArgumentMatchers.eq(R.string.careportal_note_message), + ArgumentMatchers.anyString() + ) + ).thenReturn("Note : %s") + `when`(dateUtil.now()).thenReturn(0) + `when`(profileFunction.getUnits()).thenReturn(GlucoseUnit.MGDL) + `when`(repository.runTransactionForResult(anyObject>())) + .thenReturn(Single.just(InsertIfNewByTimestampTherapyEventTransaction.TransactionResult().apply { + })) + sut = ActionCarePortalEvent(injector) + sut.cpEvent = InputCarePortalMenu(resourceHelper) + sut.cpEvent.value = InputCarePortalMenu.EventType.NOTE + sut.note = InputString("Asd") + sut.duration = InputDuration(5, InputDuration.TimeUnit.MINUTES) + } + + @Test fun friendlyNameTest() { + Assert.assertEquals(R.string.careportal, sut.friendlyName()) + } + + @Test fun shortDescriptionTest() { + Assert.assertEquals("Note : %s", sut.shortDescription()) + } + + @Test fun iconTest() { + Assert.assertEquals(R.drawable.ic_cp_note, sut.icon()) + } + + @Test fun doActionTest() { + sut.doAction(object : Callback() { + override fun run() { + Assert.assertTrue(result.success) + } + }) + } + + @Test fun hasDialogTest() { + Assert.assertTrue(sut.hasDialog()) + } + + @Test fun toJSONTest() { + Assert.assertEquals( + "{\"data\":{\"note\":\"Asd\",\"cpEvent\":\"NOTE\",\"durationInMinutes\":5},\"type\":\"info.nightscout.androidaps.plugins.general.automation.actions.ActionCarePortalEvent\"}", + sut.toJSON() + ) + } + + @Test fun fromJSONTest() { + sut.note = InputString("Asd") + sut.fromJSON("{\"note\":\"Asd\",\"cpEvent\":\"NOTE\",\"durationInMinutes\":5}") + Assert.assertEquals("Asd", sut.note.value) + Assert.assertEquals(5, sut.duration.value) + Assert.assertEquals(InputCarePortalMenu.EventType.NOTE, sut.cpEvent.value) + } +} \ No newline at end of file diff --git a/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionsTestBase.kt b/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionsTestBase.kt index 38a2d884eb..06237a3b89 100644 --- a/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionsTestBase.kt +++ b/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/actions/ActionsTestBase.kt @@ -131,6 +131,14 @@ open class ActionsTestBase : TestBaseWithProfile() { it.rxBus = rxBus it.uel = uel } + if (it is ActionCarePortalEvent) { + it.resourceHelper = resourceHelper + it.repository = repository + it.sp = sp + it.dateUtil = dateUtil + it.profileFunction = profileFunction + it.uel = uel + } if (it is PumpEnactResult) { it.resourceHelper = resourceHelper } diff --git a/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/elements/InputCarePortalEventTest.kt b/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/elements/InputCarePortalEventTest.kt new file mode 100644 index 0000000000..3d23d84d96 --- /dev/null +++ b/automation/src/test/java/info/nightscout/androidaps/plugins/general/automation/elements/InputCarePortalEventTest.kt @@ -0,0 +1,19 @@ +package info.nightscout.androidaps.plugins.general.automation.elements + +import info.nightscout.androidaps.plugins.general.automation.triggers.TriggerTestBase +import org.junit.Assert +import org.junit.Test + +class InputCarePortalEventTest : TriggerTestBase() { + + @Test + fun labelsTest() { + Assert.assertEquals(4, InputCarePortalMenu.EventType.labels(resourceHelper).size) + } + + @Test + fun setValueTest() { + val cp = InputCarePortalMenu(resourceHelper, InputCarePortalMenu.EventType.EXERCISE) + Assert.assertEquals(InputCarePortalMenu.EventType.EXERCISE, cp.value) + } +} \ No newline at end of file diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index 539426f867..8e66f84755 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -250,12 +250,17 @@ %1$s: %2$s in %3$d days
]]>
+ Careportal BG Check Manual BG or Calibration Announcement Note Question Exercise + Announcement : %1$s + Note : %1$s + Question : %1$s + Exercise : %1$s Pump Site Change CGM Sensor Insert CGM Sensor Start