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