diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 3069f7d830..668d69029d 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -167,7 +167,7 @@
@@ -185,23 +185,7 @@
-
-
-
-
diff --git a/app/src/main/java/info/nightscout/androidaps/di/ServicesModule.kt b/app/src/main/java/info/nightscout/androidaps/di/ServicesModule.kt
index b10b4b90e9..9fc54fa7cf 100644
--- a/app/src/main/java/info/nightscout/androidaps/di/ServicesModule.kt
+++ b/app/src/main/java/info/nightscout/androidaps/di/ServicesModule.kt
@@ -5,7 +5,7 @@ import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService
import info.nightscout.androidaps.plugins.general.overview.notifications.DismissNotificationService
import info.nightscout.androidaps.plugins.general.persistentNotification.DummyService
-import info.nightscout.androidaps.plugins.general.wear.wearintegration.WatchUpdaterService
+import info.nightscout.androidaps.plugins.general.wear.wearintegration.DataLayerListenerServiceMobile
import info.nightscout.androidaps.services.AlarmSoundService
import info.nightscout.androidaps.services.LocationService
@@ -18,5 +18,5 @@ abstract class ServicesModule {
@ContributesAndroidInjector abstract fun contributesDummyService(): DummyService
@ContributesAndroidInjector abstract fun contributesLocationService(): LocationService
@ContributesAndroidInjector abstract fun contributesNSClientService(): NSClientService
- @ContributesAndroidInjector abstract fun contributesWatchUpdaterService(): WatchUpdaterService
+ @ContributesAndroidInjector abstract fun contributesWatchUpdaterService(): DataLayerListenerServiceMobile
}
\ No newline at end of file
diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventBolusRequested.kt b/app/src/main/java/info/nightscout/androidaps/events/EventBolusRequested.kt
deleted file mode 100644
index a528ef1656..0000000000
--- a/app/src/main/java/info/nightscout/androidaps/events/EventBolusRequested.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package info.nightscout.androidaps.events
-
-class EventBolusRequested(var amount: Double) : Event()
\ No newline at end of file
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt
index fa8c085404..0cb9545d61 100644
--- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt
@@ -27,6 +27,7 @@ import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentOfflineEventTransaction
import info.nightscout.androidaps.database.transactions.InsertTherapyEventAnnouncementTransaction
import info.nightscout.androidaps.events.EventAcceptOpenLoopChange
+import info.nightscout.androidaps.events.EventMobileToWear
import info.nightscout.androidaps.events.EventTempTargetChange
import info.nightscout.androidaps.extensions.buildDeviceStatus
import info.nightscout.androidaps.extensions.convertedToAbsolute
@@ -44,8 +45,6 @@ import info.nightscout.androidaps.plugins.configBuilder.RunningConfiguration
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
-import info.nightscout.androidaps.plugins.general.wear.events.EventWearConfirmAction
-import info.nightscout.androidaps.plugins.general.wear.events.EventWearInitiateAction
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin
import info.nightscout.androidaps.queue.Callback
import info.nightscout.androidaps.receivers.ReceiverStatusStore
@@ -58,6 +57,7 @@ import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP
+import info.nightscout.shared.weardata.EventData
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
import javax.inject.Inject
@@ -349,7 +349,7 @@ class LoopPlugin @Inject constructor(
//only send to wear if Native notifications are turned off
if (!sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, true)) {
// Send to Wear
- rxBus.send(EventWearInitiateAction("changeRequest"))
+ sendToWear()
}
}
} else {
@@ -454,14 +454,28 @@ class LoopPlugin @Inject constructor(
rxBus.send(EventNewOpenLoopNotification())
// Send to Wear
- rxBus.send(EventWearInitiateAction("changeRequest"))
+ sendToWear()
}
private fun dismissSuggestion() {
// dismiss notifications
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.cancel(Constants.notificationID)
- rxBus.send(EventWearConfirmAction("cancelChangeRequest"))
+ rxBus.send(EventMobileToWear(EventData.CancelNotification(dateUtil.now())))
+ }
+
+ private fun sendToWear() {
+ lastRun?.let {
+ rxBus.send(
+ EventMobileToWear(
+ EventData.OpenLoopRequest(
+ rh.gs(R.string.openloop_newsuggestion),
+ it.constraintsProcessed.toString(),
+ EventData.OpenLoopRequestConfirmed(dateUtil.now())
+ )
+ )
+ )
+ }
}
override fun acceptChangeRequest() {
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 efcc0c154e..21a8ee9f95 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
@@ -54,7 +54,6 @@ import info.nightscout.androidaps.plugins.general.overview.activities.QuickWizar
import info.nightscout.androidaps.plugins.general.overview.events.*
import info.nightscout.androidaps.plugins.general.overview.graphData.GraphData
import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationStore
-import info.nightscout.androidaps.plugins.general.wear.events.EventWearInitiateAction
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType
import info.nightscout.androidaps.plugins.pump.omnipod.eros.OmnipodErosPumpPlugin
@@ -76,6 +75,7 @@ import info.nightscout.androidaps.utils.ui.UIRunnable
import info.nightscout.androidaps.utils.wizard.QuickWizard
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.sharedPreferences.SP
+import info.nightscout.shared.weardata.EventData
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
import java.util.*
@@ -408,7 +408,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList
?: "".toSpanned(), {
uel.log(Action.ACCEPTS_TEMP_BASAL, Sources.Overview)
(context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager?)?.cancel(Constants.notificationID)
- rxBus.send(EventWearInitiateAction("cancelChangeRequest"))
+ rxBus.send(EventMobileToWear(EventData.CancelNotification(dateUtil.now())))
Thread { loop.acceptChangeRequest() }.run()
binding.buttonsLayout.acceptTempButton.visibility = View.GONE
})
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
deleted file mode 100644
index 52a2a5ce27..0000000000
--- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt
+++ /dev/null
@@ -1,841 +0,0 @@
-package info.nightscout.androidaps.plugins.general.wear
-
-import android.app.NotificationManager
-import android.content.Context
-import dagger.android.HasAndroidInjector
-import info.nightscout.androidaps.Constants
-import info.nightscout.androidaps.R
-import info.nightscout.androidaps.dana.DanaPump
-import info.nightscout.androidaps.danaRKorean.DanaRKoreanPlugin
-import info.nightscout.androidaps.danaRv2.DanaRv2Plugin
-import info.nightscout.androidaps.danar.DanaRPlugin
-import info.nightscout.androidaps.danars.DanaRSPlugin
-import info.nightscout.androidaps.data.DetailedBolusInfo
-import info.nightscout.androidaps.database.AppRepository
-import info.nightscout.androidaps.database.ValueWrapper
-import info.nightscout.androidaps.database.entities.TemporaryTarget
-import info.nightscout.androidaps.database.entities.TotalDailyDose
-import info.nightscout.androidaps.database.entities.UserEntry.Action
-import info.nightscout.androidaps.database.entities.UserEntry.Sources
-import info.nightscout.androidaps.database.entities.ValueWithUnit
-import info.nightscout.androidaps.database.interfaces.end
-import info.nightscout.androidaps.database.transactions.CancelCurrentTemporaryTargetIfAnyTransaction
-import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
-import info.nightscout.androidaps.extensions.total
-import info.nightscout.androidaps.extensions.valueToUnits
-import info.nightscout.androidaps.interfaces.*
-import info.nightscout.androidaps.logging.UserEntryLogger
-import info.nightscout.androidaps.plugins.bus.RxBus
-import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
-import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification
-import info.nightscout.androidaps.plugins.general.wear.events.EventWearConfirmAction
-import info.nightscout.androidaps.plugins.general.wear.events.EventWearInitiateAction
-import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
-import info.nightscout.androidaps.queue.Callback
-import info.nightscout.androidaps.utils.*
-import info.nightscout.androidaps.utils.resources.ResourceHelper
-import info.nightscout.androidaps.utils.rx.AapsSchedulers
-import info.nightscout.androidaps.utils.wizard.BolusWizard
-import info.nightscout.androidaps.utils.wizard.QuickWizard
-import info.nightscout.shared.SafeParse
-import info.nightscout.shared.logging.AAPSLogger
-import info.nightscout.shared.logging.LTag
-import info.nightscout.shared.sharedPreferences.SP
-import info.nightscout.shared.weardata.ActionData
-import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_BOLUS
-import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_CANCEL_CHANGE_REQUEST
-import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_CHANGE_REQUEST
-import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_CPP_SET
-import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_DISMISS_OVERVIEW_NOTIF
-import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_E_CARBS
-import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_FILL
-import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_FILL_PRESET
-import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_OPEN_CPP
-import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_QUICK_WIZARD
-import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_STATUS
-import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_TDD_STATS
-import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_TEMPORARY_TARGET
-import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_WIZARD
-import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_WIZARD2
-import io.reactivex.rxjava3.disposables.CompositeDisposable
-import io.reactivex.rxjava3.kotlin.plusAssign
-import java.text.DateFormat
-import java.text.SimpleDateFormat
-import java.util.*
-import java.util.concurrent.TimeUnit
-import javax.inject.Inject
-import javax.inject.Singleton
-import kotlin.math.abs
-import kotlin.math.min
-
-@Suppress("SpellCheckingInspection")
-@Singleton
-class ActionStringHandler @Inject constructor(
- private val sp: SP,
- private val rxBus: RxBus,
- private val aapsLogger: AAPSLogger,
- private val aapsSchedulers: AapsSchedulers,
- private val rh: ResourceHelper,
- private val injector: HasAndroidInjector,
- private val context: Context,
- private val constraintChecker: ConstraintChecker,
- private val profileFunction: ProfileFunction,
- private val loop: Loop,
- private val wearPlugin: WearPlugin,
- private val fabricPrivacy: FabricPrivacy,
- private val commandQueue: CommandQueue,
- 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,
- private val danaRSPlugin: DanaRSPlugin,
- private val danaPump: DanaPump,
- private val dateUtil: DateUtil,
- private val config: Config,
- private val repository: AppRepository,
- private val uel: UserEntryLogger,
- private val defaultValueHelper: DefaultValueHelper
-) {
-
- private val timeout = 65 * 1000
- private var lastSentTimestamp: Long = 0
- private var lastConfirmActionString: String? = null
- private var lastBolusWizard: BolusWizard? = null
-
- private val disposable = CompositeDisposable()
-
- fun setup() {
- disposable += rxBus
- .toObservable(EventWearInitiateAction::class.java)
- .observeOn(aapsSchedulers.main)
- .subscribe({ handleInitiateActionOnPhone(it.action) }, fabricPrivacy::logException)
-
- disposable += rxBus
- .toObservable(EventWearConfirmAction::class.java)
- .observeOn(aapsSchedulers.main)
- .subscribe({ handleConfirmation(it.action) }, fabricPrivacy::logException)
- }
-
- fun tearDown() {
- disposable.clear()
- }
-
- @Synchronized
- private fun handleInitiateActionOnPhone(actionString: String) {
- //TODO: i18n
- aapsLogger.debug(LTag.WEAR, "handleInitiateActionOnPhone actionString=$actionString")
- if (!sp.getBoolean(R.string.key_wear_control, false)) return
- lastBolusWizard = null
- var rTitle = rh.gs(R.string.confirm).uppercase()
- var rMessage = ""
- var rAction = ""
-
- if (actionString.startsWith("{")) {
- when (val command = ActionData.deserialize(actionString)) {
- is ActionData.Bolus -> {
- val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(command.insulin)).value()
- val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(command.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 - command.insulin != 0.0 || carbsAfterConstraints - command.carbs != 0) {
- rMessage += "\n" + rh.gs(R.string.constraintapllied)
- }
- rAction += "bolus $insulinAfterConstraints $carbsAfterConstraints"
- }
- is ActionData.ProfileSwitch -> {
- val activeProfileSwitch = repository.getEffectiveProfileSwitchActiveAt(dateUtil.now()).blockingGet()
- if (activeProfileSwitch is ValueWrapper.Existing) {
- rMessage = "Profile:" + "\n\n" +
- "Timeshift: " + command.timeShift + "\n" +
- "Percentage: " + command.percentage + "%"
- rAction = actionString
- } else { // read CPP values
- sendError("No active profile switch!")
- return
- }
- }
- }
- } else {
- // do the parsing and check constraints
- val act = actionString.split("\\s+".toRegex()).toTypedArray()
- when (act[0]) {
- ACTION_FILL_PRESET -> { ///////////////////////////////////// PRIME/FILL
- val amount: Double = when {
- "1" == act[1] -> sp.getDouble("fill_button1", 0.3)
- "2" == act[1] -> sp.getDouble("fill_button2", 0.0)
- "3" == act[1] -> sp.getDouble("fill_button3", 0.0)
- else -> return
- }
- val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(amount)).value()
- rMessage += rh.gs(R.string.primefill) + ": " + insulinAfterConstraints + "U"
- if (insulinAfterConstraints - amount != 0.0) rMessage += "\n" + rh.gs(R.string.constraintapllied)
- rAction += "fill $insulinAfterConstraints"
- }
-
- ACTION_FILL -> {
- val amount = SafeParse.stringToDouble(act[1])
- val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(amount)).value()
- rMessage += rh.gs(R.string.primefill) + ": " + insulinAfterConstraints + "U"
- if (insulinAfterConstraints - amount != 0.0) rMessage += "\n" + rh.gs(R.string.constraintapllied)
- rAction += "fill $insulinAfterConstraints"
- }
-
- ACTION_TEMPORARY_TARGET -> {
- 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 {
- 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
- }
- 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
- }
- }
- }
-
- ACTION_STATUS -> {
- rTitle = "STATUS"
- rAction = "statusmessage"
- if ("pump" == act[1]) {
- rTitle += " PUMP"
- rMessage = pumpStatus
- } else if ("loop" == act[1]) {
- rTitle += " LOOP"
- rMessage = "TARGETS:\n$targetsStatus"
- rMessage += "\n\n" + loopStatus
- rMessage += "\n\nOAPS RESULT:\n$oAPSResultStatus"
- }
- }
-
- ACTION_WIZARD -> {
- sendError("Update APP on Watch!")
- return
- }
-
- ACTION_WIZARD2 -> {
- 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(rh.gs(R.string.wizard_carbs_constraint))
- return
- }
- val useBG = sp.getBoolean(R.string.key_wearwizard_bg, true)
- val useTT = sp.getBoolean(R.string.key_wearwizard_tt, false)
- val useBolusIOB = sp.getBoolean(R.string.key_wearwizard_bolusiob, true)
- val useBasalIOB = sp.getBoolean(R.string.key_wearwizard_basaliob, true)
- val useCOB = sp.getBoolean(R.string.key_wearwizard_cob, true)
- val useTrend = sp.getBoolean(R.string.key_wearwizard_trend, false)
- val percentage = act[2].toInt()
- val profile = profileFunction.getProfile()
- val profileName = profileFunction.getProfileName()
- if (profile == null) {
- sendError(rh.gs(R.string.wizard_no_active_profile))
- return
- }
- val bgReading = iobCobCalculator.ads.actualBg()
- if (bgReading == null) {
- sendError(rh.gs(R.string.wizard_no_actual_bg))
- return
- }
- val cobInfo = iobCobCalculator.getCobInfo(false, "Wizard wear")
- if (cobInfo.displayCob == null) {
- sendError(rh.gs(R.string.wizard_no_cob))
- return
- }
- 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, 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 = rh.gs(R.string.info)
- } else {
- rAction = actionString
- }
- rMessage += rh.gs(R.string.wizard_result, bolusWizard.calculatedTotalInsulin, bolusWizard.carbs)
- rMessage += "\n_____________"
- rMessage += "\n" + bolusWizard.explainShort()
- lastBolusWizard = bolusWizard
- }
-
- ACTION_QUICK_WIZARD -> {
- 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()
-
- }
-
- ACTION_OPEN_CPP -> {
- val activeProfileSwitch = repository.getEffectiveProfileSwitchActiveAt(dateUtil.now()).blockingGet()
- if (activeProfileSwitch is ValueWrapper.Existing) { // read CPP values
- rTitle = "opencpp"
- rMessage = "opencpp"
- rAction = "opencpp" + " " + activeProfileSwitch.value.originalPercentage + " " + activeProfileSwitch.value.originalTimeshift
- } else {
- sendError("No active profile switch!")
- return
- }
- }
-
- ACTION_TDD_STATS -> {
- val activePump = activePlugin.activePump
- // check if DB up to date
- val dummies: MutableList = LinkedList()
- val historyList = getTDDList(dummies)
- if (isOldData(historyList)) {
- rTitle = "TDD"
- rAction = "statusmessage"
- rMessage = "OLD DATA - "
- //if pump is not busy: try to fetch data
- if (activePump.isBusy()) {
- rMessage += rh.gs(R.string.pumpbusy)
- } else {
- rMessage += "trying to fetch data from pump."
- commandQueue.loadTDDs(object : Callback() {
- override fun run() {
- val dummies1: MutableList = LinkedList()
- val historyList1 = getTDDList(dummies1)
- if (isOldData(historyList1)) {
- sendStatusMessage("TDD: Still old data! Cannot load from pump.\n" + generateTDDMessage(historyList1, dummies1))
- } else {
- sendStatusMessage(generateTDDMessage(historyList1, dummies1))
- }
- }
- })
- }
- } else { // if up to date: prepare, send (check if CPP is activated -> add CPP stats)
- rTitle = "TDD"
- rAction = "statusmessage"
- rMessage = generateTDDMessage(historyList, dummies)
- }
- }
-
- ACTION_E_CARBS -> {
- val carbs = SafeParse.stringToInt(act[1])
- val starttime = SafeParse.stringToInt(act[2])
- val duration = SafeParse.stringToInt(act[3])
- val starttimestamp = System.currentTimeMillis() + starttime * 60 * 1000
- val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value()
- rMessage += rh.gs(R.string.carbs) + ": " + carbsAfterConstraints + "g"
- rMessage += "\n" + rh.gs(R.string.time) + ": " + dateUtil.timeString(starttimestamp)
- rMessage += "\n" + rh.gs(R.string.duration) + ": " + duration + "h"
- if (carbsAfterConstraints - carbs != 0) {
- rMessage += "\n" + rh.gs(R.string.constraintapllied)
- }
- if (carbsAfterConstraints <= 0) {
- sendError("Carbs = 0! No action taken!")
- return
- }
- rAction += "ecarbs $carbsAfterConstraints $starttimestamp $duration"
- }
-
- ACTION_CHANGE_REQUEST -> {
- rTitle = rh.gs(R.string.openloop_newsuggestion)
- rAction = "changeRequest"
- loop.lastRun?.let {
- rMessage += it.constraintsProcessed
- wearPlugin.requestChangeConfirmation(rTitle, rMessage, rAction)
- lastSentTimestamp = System.currentTimeMillis()
- lastConfirmActionString = rAction
- }
- return
- }
-
- ACTION_CANCEL_CHANGE_REQUEST -> {
- rAction = "cancelChangeRequest"
- wearPlugin.requestNotificationCancel(rAction)
- return
- }
-
- else -> {
- sendError(rh.gs(R.string.wear_unknown_action_string) + " " + act[0])
- return
- }
- }
- }
- // send result
- wearPlugin.requestActionConfirmation(rTitle, rMessage, rAction)
- lastSentTimestamp = System.currentTimeMillis()
- lastConfirmActionString = rAction
- }
-
- private fun generateTDDMessage(historyList: MutableList, dummies: MutableList): String {
- val profile = profileFunction.getProfile() ?: return "No profile loaded :("
- if (historyList.isEmpty()) {
- return "No history data!"
- }
- val df: DateFormat = SimpleDateFormat("dd.MM.", Locale.getDefault())
- var message = ""
- val refTDD = profile.baseBasalSum() * 2
- val pump = activePlugin.activePump
- if (df.format(Date(historyList[0].timestamp)) == df.format(Date())) {
- val tdd = historyList[0].total
- historyList.removeAt(0)
- message += "Today: " + DecimalFormatter.to2Decimal(tdd) + "U " + (DecimalFormatter.to0Decimal(100 * tdd / refTDD) + "%") + "\n"
- message += "\n"
- } else if (pump is DanaRPlugin) {
- val tdd = danaPump.dailyTotalUnits
- message += "Today: " + DecimalFormatter.to2Decimal(tdd) + "U " + (DecimalFormatter.to0Decimal(100 * tdd / refTDD) + "%") + "\n"
- message += "\n"
- }
- var weighted03 = 0.0
- var weighted05 = 0.0
- var weighted07 = 0.0
- historyList.reverse()
- for ((i, record) in historyList.withIndex()) {
- val tdd = record.total
- if (i == 0) {
- weighted03 = tdd
- weighted05 = tdd
- weighted07 = tdd
- } else {
- weighted07 = weighted07 * 0.3 + tdd * 0.7
- weighted05 = weighted05 * 0.5 + tdd * 0.5
- weighted03 = weighted03 * 0.7 + tdd * 0.3
- }
- }
- message += "weighted:\n"
- message += "0.3: " + DecimalFormatter.to2Decimal(weighted03) + "U " + (DecimalFormatter.to0Decimal(100 * weighted03 / refTDD) + "%") + "\n"
- message += "0.5: " + DecimalFormatter.to2Decimal(weighted05) + "U " + (DecimalFormatter.to0Decimal(100 * weighted05 / refTDD) + "%") + "\n"
- message += "0.7: " + DecimalFormatter.to2Decimal(weighted07) + "U " + (DecimalFormatter.to0Decimal(100 * weighted07 / refTDD) + "%") + "\n"
- message += "\n"
- historyList.reverse()
- //add TDDs:
- for (record in historyList) {
- val tdd = record.total
- message += df.format(Date(record.timestamp)) + " " + DecimalFormatter.to2Decimal(tdd) + "U " + (DecimalFormatter.to0Decimal(100 * tdd / refTDD) + "%") + (if (dummies.contains(record)) "x" else "") + "\n"
- }
- return message
- }
-
- private fun isOldData(historyList: List): Boolean {
- val activePump = activePlugin.activePump
- val startsYesterday = activePump === danaRPlugin || activePump === danaRSPlugin || activePump === danaRv2Plugin || activePump === danaRKoreanPlugin || activePump === localInsightPlugin
- val df: DateFormat = SimpleDateFormat("dd.MM.", Locale.getDefault())
- return historyList.size < 3 || df.format(Date(historyList[0].timestamp)) != df.format(Date(System.currentTimeMillis() - if (startsYesterday) 1000 * 60 * 60 * 24 else 0))
- }
-
- private fun getTDDList(returnDummies: MutableList): MutableList {
- var historyList = repository.getLastTotalDailyDoses(10, false).blockingGet().toMutableList()
- //var historyList = databaseHelper.getTDDs().toMutableList()
- historyList = historyList.subList(0, min(10, historyList.size))
- //fill single gaps - only needed for Dana*R data
- val dummies: MutableList = returnDummies
- val df: DateFormat = SimpleDateFormat("dd.MM.", Locale.getDefault())
- for (i in 0 until historyList.size - 1) {
- val elem1 = historyList[i]
- val elem2 = historyList[i + 1]
- if (df.format(Date(elem1.timestamp)) != df.format(Date(elem2.timestamp + 25 * 60 * 60 * 1000))) {
- val dummy = TotalDailyDose(timestamp = elem1.timestamp - T.hours(24).msecs(), bolusAmount = elem1.bolusAmount / 2, basalAmount = elem1.basalAmount / 2)
- dummies.add(dummy)
- elem1.basalAmount /= 2.0
- elem1.bolusAmount /= 2.0
- }
- }
- historyList.addAll(dummies)
- historyList.sortWith { lhs, rhs -> (rhs.timestamp - lhs.timestamp).toInt() }
- return historyList
- }
-
- private val pumpStatus: String
- get() = activePlugin.activePump.shortStatus(false)
-
- // decide if enabled/disabled closed/open; what Plugin as APS?
- private val loopStatus: String
- get() {
- var ret = ""
- // decide if enabled/disabled closed/open; what Plugin as APS?
- if ((loop as PluginBase).isEnabled()) {
- ret += if (constraintChecker.isClosedLoopAllowed().value()) {
- "CLOSED LOOP\n"
- } else {
- "OPEN LOOP\n"
- }
- val aps = activePlugin.activeAPS
- ret += "APS: " + (aps as PluginBase).name
- val lastRun = loop.lastRun
- if (lastRun != null) {
- ret += "\nLast Run: " + dateUtil.timeString(lastRun.lastAPSRun)
- if (lastRun.lastTBREnact != 0L) ret += "\nLast Enact: " + dateUtil.timeString(lastRun.lastTBREnact)
- }
- } else {
- ret += "LOOP DISABLED\n"
- }
- return ret
- }
-
- //Check for Temp-Target:
- private val targetsStatus: String
- get() {
- var ret = ""
- if (!config.APS) {
- return "Targets only apply in APS mode!"
- }
- val profile = profileFunction.getProfile() ?: return "No profile set :("
- //Check for Temp-Target:
- val tempTarget = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet()
- if (tempTarget is ValueWrapper.Existing) {
- ret += "Temp Target: " + Profile.toTargetRangeString(tempTarget.value.lowTarget, tempTarget.value.lowTarget, GlucoseUnit.MGDL, profileFunction.getUnits())
- ret += "\nuntil: " + dateUtil.timeString(tempTarget.value.end)
- ret += "\n\n"
- }
- ret += "DEFAULT RANGE: "
- ret += Profile.fromMgdlToUnits(profile.getTargetLowMgdl(), profileFunction.getUnits()).toString() + " - " + Profile.fromMgdlToUnits(profile.getTargetHighMgdl(), profileFunction.getUnits())
- ret += " target: " + Profile.fromMgdlToUnits(profile.getTargetMgdl(), profileFunction.getUnits())
- return ret
- }
-
- private val oAPSResultStatus: String
- get() {
- var ret = ""
- if (!config.APS)
- return "Only apply in APS mode!"
- val usedAPS = activePlugin.activeAPS
- val result = usedAPS.lastAPSResult ?: return "Last result not available!"
- ret += if (!result.isChangeRequested) {
- rh.gs(R.string.nochangerequested) + "\n"
- } else if (result.rate == 0.0 && result.duration == 0) {
- rh.gs(R.string.canceltemp) + "\n"
- } else {
- rh.gs(R.string.rate) + ": " + DecimalFormatter.to2Decimal(result.rate) + " U/h " +
- "(" + DecimalFormatter.to2Decimal(result.rate / activePlugin.activePump.baseBasalRate * 100) + "%)\n" +
- rh.gs(R.string.duration) + ": " + DecimalFormatter.to0Decimal(result.duration.toDouble()) + " min\n"
- }
- ret += "\n" + rh.gs(R.string.reason) + ": " + result.reason
- return ret
- }
-
- @Synchronized
- fun handleConfirmation(actionString: String) {
- if (!sp.getBoolean(R.string.key_wear_control, false)) return
- //Guard from old or duplicate confirmations
- if (lastConfirmActionString == null) return
- if (lastConfirmActionString != actionString) return
- if (System.currentTimeMillis() - lastSentTimestamp > timeout) return
- lastConfirmActionString = null
- // do the parsing, check constraints and enact!
- val act = actionString.split("\\s+".toRegex()).toTypedArray()
- when (act[0]) {
- ACTION_FILL -> {
- val amount = SafeParse.stringToDouble(act[1])
- val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(amount)).value()
- if (amount - insulinAfterConstraints != 0.0) {
- ToastUtils.showToastInUiThread(context, "aborting: previously applied constraint changed")
- sendError("aborting: previously applied constraint changed")
- return
- }
- doFillBolus(amount)
- }
-
- ACTION_TEMPORARY_TARGET -> {
- val duration = SafeParse.stringToInt(act[2])
- val low = SafeParse.stringToDouble(act[3])
- val high = SafeParse.stringToDouble(act[4])
- generateTempTarget(duration, low, high)
- }
-
- ACTION_WIZARD2 -> {
- if (lastBolusWizard != null) { //use last calculation as confirmed string matches
- doBolus(lastBolusWizard!!.calculatedTotalInsulin, lastBolusWizard!!.carbs, null, 0)
- lastBolusWizard = null
- }
- }
-
- ACTION_BOLUS -> {
- val insulin = SafeParse.stringToDouble(act[1])
- val carbs = SafeParse.stringToInt(act[2])
- doBolus(insulin, carbs, null, 0)
- }
-
- ACTION_CPP_SET -> {
- val timeshift = SafeParse.stringToInt(act[1])
- val percentage = SafeParse.stringToInt(act[2])
- setCPP(timeshift, percentage)
- }
-
- ACTION_E_CARBS -> {
- val carbs = SafeParse.stringToInt(act[1])
- val starttime = SafeParse.stringToLong(act[2])
- val duration = SafeParse.stringToInt(act[3])
- doECarbs(carbs, starttime, duration)
- }
-
- ACTION_DISMISS_OVERVIEW_NOTIF -> {
- rxBus.send(EventDismissNotification(SafeParse.stringToInt(act[1])))
- }
-
- ACTION_CHANGE_REQUEST -> {
- loop.acceptChangeRequest()
- val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
- notificationManager.cancel(Constants.notificationID)
- }
- }
- lastBolusWizard = null
- }
-
- private fun setCPP(timeshift: Int, percentage: Int) {
- var msg = ""
- //check for validity
- if (percentage < Constants.CPP_MIN_PERCENTAGE || percentage > Constants.CPP_MAX_PERCENTAGE) {
- msg += rh.gs(R.string.valueoutofrange, "Profile-Percentage") + "\n"
- }
- if (timeshift < 0 || timeshift > 23) {
- msg += rh.gs(R.string.valueoutofrange, "Profile-Timeshift") + "\n"
- }
- val profile = profileFunction.getProfile()
- if (profile == null) {
- msg += rh.gs(R.string.notloadedplugins) + "\n"
- }
- if ("" != msg) {
- msg += rh.gs(R.string.valuesnotstored)
- val rTitle = "STATUS"
- val rAction = "statusmessage"
- wearPlugin.requestActionConfirmation(rTitle, msg, rAction)
- lastSentTimestamp = System.currentTimeMillis()
- lastConfirmActionString = rAction
- return
- }
- //send profile to pump
- uel.log(Action.PROFILE_SWITCH, Sources.Wear,
- 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,
- ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.WEAR),
- ValueWithUnit.fromGlucoseUnit(low, profileFunction.getUnits().asText),
- ValueWithUnit.fromGlucoseUnit(high, profileFunction.getUnits().asText).takeIf { low != high },
- 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)
- )
- }
- }
-
- private fun doFillBolus(amount: Double) {
- val detailedBolusInfo = DetailedBolusInfo()
- detailedBolusInfo.insulin = amount
- detailedBolusInfo.bolusType = DetailedBolusInfo.BolusType.PRIMING
- uel.log(Action.PRIME_BOLUS, Sources.Wear,
- 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
- )
- }
- }
- })
- }
-
- 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 })
- doBolus(0.0, carbs, time, duration)
- }
-
- private fun doBolus(amount: Double, carbs: Int, carbsTime: Long?, carbsDuration: Int) {
- val detailedBolusInfo = DetailedBolusInfo()
- detailedBolusInfo.insulin = amount
- detailedBolusInfo.carbs = carbs.toDouble()
- detailedBolusInfo.bolusType = DetailedBolusInfo.BolusType.NORMAL
- detailedBolusInfo.carbsTimestamp = carbsTime
- detailedBolusInfo.carbsDuration = T.hours(carbsDuration.toLong()).msecs()
- if (detailedBolusInfo.insulin > 0 || detailedBolusInfo.carbs > 0) {
- val action = when {
- amount.equals(0.0) -> Action.CARBS
- carbs.equals(0) -> Action.BOLUS
- carbsDuration > 0 -> Action.EXTENDED_CARBS
- 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 })
- commandQueue.bolus(detailedBolusInfo, object : Callback() {
- override fun run() {
- if (!result.success) {
- sendError(
- rh.gs(R.string.treatmentdeliveryerror) +
- "\n" +
- result.comment
- )
- }
- }
- })
- }
- }
-
- @Synchronized private fun sendError(errorMessage: String) {
- wearPlugin.requestActionConfirmation("ERROR", errorMessage, "error")
- lastSentTimestamp = System.currentTimeMillis()
- lastConfirmActionString = null
- lastBolusWizard = null
- }
-
- @Synchronized
- private fun sendStatusMessage(message: String) {
- wearPlugin.requestActionConfirmation("TDD", message, "statusmessage")
- lastSentTimestamp = System.currentTimeMillis()
- lastConfirmActionString = null
- lastBolusWizard = null
- }
-}
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearFragment.kt
index 95e21f730d..0cad0dcd55 100644
--- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearFragment.kt
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearFragment.kt
@@ -6,10 +6,13 @@ import android.view.View
import android.view.ViewGroup
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.databinding.WearFragmentBinding
+import info.nightscout.androidaps.events.EventMobileToWear
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientUpdateGUI
+import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.rx.AapsSchedulers
+import info.nightscout.shared.weardata.EventData
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
import javax.inject.Inject
@@ -20,6 +23,7 @@ class WearFragment : DaggerFragment() {
@Inject lateinit var rxBus: RxBus
@Inject lateinit var aapsSchedulers: AapsSchedulers
@Inject lateinit var fabricPrivacy: FabricPrivacy
+ @Inject lateinit var dateUtil: DateUtil
private var _binding: WearFragmentBinding? = null
@@ -37,8 +41,8 @@ class WearFragment : DaggerFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- binding.resend.setOnClickListener { wearPlugin.resendDataToWatch() }
- binding.openSettings.setOnClickListener { wearPlugin.openSettings() }
+ binding.resend.setOnClickListener { rxBus.send(EventData.ActionResendData("WearFragment")) }
+ binding.openSettings.setOnClickListener { rxBus.send(EventMobileToWear(EventData.OpenSettings(dateUtil.now()))) }
}
override fun onResume() {
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearPlugin.kt
index ead6df3277..3ac1f5adbb 100644
--- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearPlugin.kt
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearPlugin.kt
@@ -2,24 +2,26 @@ package info.nightscout.androidaps.plugins.general.wear
import android.content.Context
import android.content.Intent
-import dagger.Lazy
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
-import info.nightscout.androidaps.events.*
+import info.nightscout.androidaps.events.EventAutosensCalculationFinished
+import info.nightscout.androidaps.events.EventMobileToWear
+import info.nightscout.androidaps.events.EventPreferenceChange
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
-import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui
import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopUpdateGui
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissBolusProgressIfRunning
import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress
-import info.nightscout.androidaps.plugins.general.wear.wearintegration.WatchUpdaterService
+import info.nightscout.androidaps.plugins.general.wear.wearintegration.DataHandlerMobile
+import info.nightscout.androidaps.plugins.general.wear.wearintegration.DataLayerListenerServiceMobile
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.sharedPreferences.SP
+import info.nightscout.shared.weardata.EventData
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
import javax.inject.Inject
@@ -32,10 +34,10 @@ class WearPlugin @Inject constructor(
rh: ResourceHelper,
private val aapsSchedulers: AapsSchedulers,
private val sp: SP,
- private val ctx: Context,
private val fabricPrivacy: FabricPrivacy,
private val rxBus: RxBus,
- private val actionStringHandler: Lazy
+ private val context: Context,
+ private val dataHandlerMobile: DataHandlerMobile
) : PluginBase(
PluginDescription()
@@ -55,136 +57,43 @@ class WearPlugin @Inject constructor(
override fun onStart() {
super.onStart()
- disposable += rxBus
- .toObservable(EventOpenAPSUpdateGui::class.java)
- .observeOn(aapsSchedulers.io)
- .subscribe({ sendDataToWatch(status = true, basals = true, bgValue = false) }, fabricPrivacy::logException)
-
- disposable += rxBus
- .toObservable(EventExtendedBolusChange::class.java)
- .observeOn(aapsSchedulers.io)
- .subscribe({ sendDataToWatch(status = true, basals = true, bgValue = false) }, fabricPrivacy::logException)
- disposable += rxBus
- .toObservable(EventTempBasalChange::class.java)
- .observeOn(aapsSchedulers.io)
- .subscribe({ sendDataToWatch(status = true, basals = true, bgValue = false) }, fabricPrivacy::logException)
- disposable += rxBus
- .toObservable(EventTreatmentChange::class.java)
- .observeOn(aapsSchedulers.io)
- .subscribe({ sendDataToWatch(status = true, basals = true, bgValue = false) }, fabricPrivacy::logException)
- disposable += rxBus
- .toObservable(EventEffectiveProfileSwitchChanged::class.java)
- .observeOn(aapsSchedulers.io)
- .subscribe({ sendDataToWatch(status = false, basals = true, bgValue = false) }, fabricPrivacy::logException)
- disposable += rxBus
- .toObservable(EventAutosensCalculationFinished::class.java)
- .observeOn(aapsSchedulers.io)
- .subscribe({ sendDataToWatch(status = true, basals = true, bgValue = true) }, fabricPrivacy::logException)
- disposable += rxBus
- .toObservable(EventPreferenceChange::class.java)
- .observeOn(aapsSchedulers.io)
- .subscribe({
- // possibly new high or low mark
- resendDataToWatch()
- // status may be formatted differently
- sendDataToWatch(status = true, basals = false, bgValue = false)
- }, fabricPrivacy::logException)
- disposable += rxBus
- .toObservable(EventLoopUpdateGui::class.java)
- .observeOn(aapsSchedulers.io)
- .subscribe({
- sendDataToWatch(status = true, basals = false, bgValue = false)
- }, fabricPrivacy::logException)
- disposable += rxBus
- .toObservable(EventBolusRequested::class.java)
- .observeOn(aapsSchedulers.io)
- .subscribe({ event: EventBolusRequested ->
- val status = rh.gs(R.string.bolusrequested, event.amount)
- val intent = Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_BOLUS_PROGRESS)
- intent.putExtra("progresspercent", 0)
- intent.putExtra("progressstatus", status)
- ctx.startService(intent)
- }, fabricPrivacy::logException)
+ context.startService(Intent(context, DataLayerListenerServiceMobile::class.java))
disposable += rxBus
.toObservable(EventDismissBolusProgressIfRunning::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ event: EventDismissBolusProgressIfRunning ->
- if (event.result == null) return@subscribe
- val status: String = if (event.result!!.success) {
- rh.gs(R.string.success)
- } else {
- rh.gs(R.string.nosuccess)
+ event.result?.let {
+ val status =
+ if (it.success) rh.gs(R.string.success)
+ else rh.gs(R.string.nosuccess)
+ if (isEnabled()) rxBus.send(EventMobileToWear(EventData.BolusProgress(percent = 100, status = status)))
}
- val intent = Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_BOLUS_PROGRESS)
- intent.putExtra("progresspercent", 100)
- intent.putExtra("progressstatus", status)
- ctx.startService(intent)
}, fabricPrivacy::logException)
disposable += rxBus
.toObservable(EventOverviewBolusProgress::class.java)
.observeOn(aapsSchedulers.io)
.subscribe({ event: EventOverviewBolusProgress ->
if (!event.isSMB() || sp.getBoolean("wear_notifySMB", true)) {
- val intent = Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_BOLUS_PROGRESS)
- intent.putExtra("progresspercent", event.percent)
- intent.putExtra("progressstatus", event.status)
- ctx.startService(intent)
+ if (isEnabled()) rxBus.send(EventMobileToWear(EventData.BolusProgress(percent = event.percent, status = event.status)))
}
}, fabricPrivacy::logException)
- actionStringHandler.get().setup()
+ disposable += rxBus
+ .toObservable(EventPreferenceChange::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe({ dataHandlerMobile.resendData("EventPreferenceChange") }, fabricPrivacy::logException)
+ disposable += rxBus
+ .toObservable(EventAutosensCalculationFinished::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe({ dataHandlerMobile.resendData("EventAutosensCalculationFinished") }, fabricPrivacy::logException)
+ disposable += rxBus
+ .toObservable(EventLoopUpdateGui::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe({ dataHandlerMobile.resendData("EventLoopUpdateGui") }, fabricPrivacy::logException)
}
override fun onStop() {
disposable.clear()
super.onStop()
- actionStringHandler.get().tearDown()
- }
-
- private fun sendDataToWatch(status: Boolean, basals: Boolean, bgValue: Boolean) {
- // only start service when this plugin is enabled
- if (isEnabled()) {
- if (bgValue) ctx.startService(Intent(ctx, WatchUpdaterService::class.java))
- if (basals) ctx.startService(Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_BASALS))
- if (status) ctx.startService(Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_STATUS))
- }
- }
-
- fun resendDataToWatch() {
- ctx.startService(Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_RESEND))
- }
-
- fun openSettings() {
- ctx.startService(Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_OPEN_SETTINGS))
- }
-
- fun requestNotificationCancel(actionString: String?) {
- ctx.startService(
- Intent(ctx, WatchUpdaterService::class.java)
- .setAction(WatchUpdaterService.ACTION_CANCEL_NOTIFICATION)
- .also {
- it.putExtra("actionstring", actionString)
- })
- }
-
- fun requestActionConfirmation(title: String, message: String, actionString: String) {
- ctx.startService(
- Intent(ctx, WatchUpdaterService::class.java)
- .setAction(WatchUpdaterService.ACTION_SEND_ACTION_CONFIRMATION_REQUEST)
- .also {
- it.putExtra("title", title)
- it.putExtra("message", message)
- it.putExtra("actionstring", actionString)
- })
- }
-
- fun requestChangeConfirmation(title: String, message: String, actionString: String) {
- ctx.startService(
- Intent(ctx, WatchUpdaterService::class.java)
- .setAction(WatchUpdaterService.ACTION_SEND_CHANGE_CONFIRMATION_REQUEST)
- .also {
- it.putExtra("title", title)
- it.putExtra("message", message)
- it.putExtra("actionstring", actionString)
- })
+ context.stopService(Intent(context, DataLayerListenerServiceMobile::class.java))
}
}
\ No newline at end of file
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/events/EventWearConfirmAction.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/events/EventWearConfirmAction.kt
deleted file mode 100644
index 211836d27d..0000000000
--- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/events/EventWearConfirmAction.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package info.nightscout.androidaps.plugins.general.wear.events
-
-import info.nightscout.androidaps.events.Event
-
-class EventWearConfirmAction(val action: String) : Event()
\ No newline at end of file
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/events/EventWearInitiateAction.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/events/EventWearInitiateAction.kt
deleted file mode 100644
index 9661c4af38..0000000000
--- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/events/EventWearInitiateAction.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package info.nightscout.androidaps.plugins.general.wear.events
-
-import info.nightscout.androidaps.events.Event
-
-class EventWearInitiateAction(val action: String) : Event()
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataHandlerMobile.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataHandlerMobile.kt
new file mode 100644
index 0000000000..16db4f7241
--- /dev/null
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataHandlerMobile.kt
@@ -0,0 +1,1173 @@
+package info.nightscout.androidaps.plugins.general.wear.wearintegration
+
+import android.app.NotificationManager
+import android.content.Context
+import dagger.android.HasAndroidInjector
+import info.nightscout.androidaps.Constants
+import info.nightscout.androidaps.R
+import info.nightscout.androidaps.data.DetailedBolusInfo
+import info.nightscout.androidaps.database.AppRepository
+import info.nightscout.androidaps.database.ValueWrapper
+import info.nightscout.androidaps.database.entities.*
+import info.nightscout.androidaps.database.interfaces.end
+import info.nightscout.androidaps.database.transactions.CancelCurrentTemporaryTargetIfAnyTransaction
+import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction
+import info.nightscout.androidaps.events.EventMobileToWear
+import info.nightscout.androidaps.extensions.convertedToAbsolute
+import info.nightscout.androidaps.extensions.toStringShort
+import info.nightscout.androidaps.extensions.total
+import info.nightscout.androidaps.extensions.valueToUnits
+import info.nightscout.androidaps.extensions.valueToUnitsString
+import info.nightscout.androidaps.interfaces.*
+import info.nightscout.androidaps.logging.UserEntryLogger
+import info.nightscout.androidaps.plugins.bus.RxBus
+import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
+import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus
+import info.nightscout.androidaps.plugins.general.overview.graphExtensions.GlucoseValueDataPoint
+import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider
+import info.nightscout.androidaps.queue.Callback
+import info.nightscout.androidaps.receivers.ReceiverStatusStore
+import info.nightscout.androidaps.utils.*
+import info.nightscout.androidaps.utils.resources.ResourceHelper
+import info.nightscout.androidaps.utils.rx.AapsSchedulers
+import info.nightscout.androidaps.utils.wizard.BolusWizard
+import info.nightscout.androidaps.utils.wizard.QuickWizard
+import info.nightscout.androidaps.utils.wizard.QuickWizardEntry
+import info.nightscout.shared.logging.AAPSLogger
+import info.nightscout.shared.logging.LTag
+import info.nightscout.shared.sharedPreferences.SP
+import info.nightscout.shared.weardata.EventData
+import io.reactivex.rxjava3.disposables.CompositeDisposable
+import io.reactivex.rxjava3.kotlin.plusAssign
+import java.text.DateFormat
+import java.text.SimpleDateFormat
+import java.util.*
+import java.util.concurrent.TimeUnit
+import java.util.stream.Collectors
+import javax.inject.Inject
+import javax.inject.Singleton
+import kotlin.collections.ArrayList
+import kotlin.math.abs
+import kotlin.math.min
+
+@Singleton
+class DataHandlerMobile @Inject constructor(
+ aapsSchedulers: AapsSchedulers,
+ private val injector: HasAndroidInjector,
+ private val context: Context,
+ private val rxBus: RxBus,
+ private val aapsLogger: AAPSLogger,
+ private val rh: ResourceHelper,
+ private val sp: SP,
+ private val config: Config,
+ private val iobCobCalculator: IobCobCalculator,
+ private val repository: AppRepository,
+ private val glucoseStatusProvider: GlucoseStatusProvider,
+ private val profileFunction: ProfileFunction,
+ private val loop: Loop,
+ private val nsDeviceStatus: NSDeviceStatus,
+ private val receiverStatusStore: ReceiverStatusStore,
+ private val quickWizard: QuickWizard,
+ private val defaultValueHelper: DefaultValueHelper,
+ private val trendCalculator: TrendCalculator,
+ private val dateUtil: DateUtil,
+ private val constraintChecker: ConstraintChecker,
+ private val uel: UserEntryLogger,
+ private val activePlugin: ActivePlugin,
+ private val commandQueue: CommandQueue,
+ private val fabricPrivacy: FabricPrivacy
+) {
+
+ private val disposable = CompositeDisposable()
+
+ private var lastBolusWizard: BolusWizard? = null
+
+ init {
+ // From Wear
+ disposable += rxBus
+ .toObservable(EventData.ActionPong::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe({
+ aapsLogger.debug(LTag.WEAR, "Pong received from ${it.sourceNodeId}")
+ }, fabricPrivacy::logException)
+ disposable += rxBus
+ .toObservable(EventData.CancelBolus::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe({
+ aapsLogger.debug(LTag.WEAR, "CancelBolus received from ${it.sourceNodeId}")
+ activePlugin.activePump.stopBolusDelivering()
+ }, fabricPrivacy::logException)
+ disposable += rxBus
+ .toObservable(EventData.OpenLoopRequestConfirmed::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe({
+ aapsLogger.debug(LTag.WEAR, "OpenLoopRequestConfirmed received from ${it.sourceNodeId}")
+ loop.acceptChangeRequest()
+ (context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).cancel(Constants.notificationID)
+ }, fabricPrivacy::logException)
+ disposable += rxBus
+ .toObservable(EventData.ActionResendData::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe({
+ aapsLogger.debug(LTag.WEAR, "ResendData received from ${it.sourceNodeId}")
+ resendData(it.from)
+ }, fabricPrivacy::logException)
+ disposable += rxBus
+ .toObservable(EventData.ActionPumpStatus::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe({
+ aapsLogger.debug(LTag.WEAR, "ActionPumpStatus received from ${it.sourceNodeId}")
+ rxBus.send(
+ EventMobileToWear(
+ EventData.ConfirmAction(
+ rh.gs(R.string.medtronic_pump_status).uppercase(),
+ activePlugin.activePump.shortStatus(false),
+ returnCommand = null
+ )
+ )
+ )
+ }, fabricPrivacy::logException)
+ disposable += rxBus
+ .toObservable(EventData.ActionLoopStatus::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe({
+ aapsLogger.debug(LTag.WEAR, "ActionLoopStatus received from ${it.sourceNodeId}")
+ rxBus.send(
+ EventMobileToWear(
+ EventData.ConfirmAction(
+ rh.gs(R.string.loop_status).uppercase(),
+ "TARGETS:\n$targetsStatus\n\n$loopStatus\n\nOAPS RESULT:\n$oAPSResultStatus",
+ returnCommand = null
+ )
+ )
+ )
+ }, fabricPrivacy::logException)
+ disposable += rxBus
+ .toObservable(EventData.ActionTddStatus::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe {
+ aapsLogger.debug(LTag.WEAR, "ActionTddStatus received from ${it.sourceNodeId}")
+ handleTddStatus()
+ }
+ disposable += rxBus
+ .toObservable(EventData.ActionProfileSwitchSendInitialData::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe({
+ aapsLogger.debug(LTag.WEAR, "ActionProfileSwitchSendInitialData received $it from ${it.sourceNodeId}")
+ handleProfileSwitchSendInitialData()
+ }, fabricPrivacy::logException)
+ disposable += rxBus
+ .toObservable(EventData.ActionProfileSwitchPreCheck::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe({
+ aapsLogger.debug(LTag.WEAR, "ActionProfileSwitchPreCheck received $it from ${it.sourceNodeId}")
+ handleProfileSwitchPreCheck(it)
+ }, fabricPrivacy::logException)
+ disposable += rxBus
+ .toObservable(EventData.ActionProfileSwitchConfirmed::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe({
+ aapsLogger.debug(LTag.WEAR, "ActionProfileSwitchConfirmed received $it from ${it.sourceNodeId}")
+ doProfileSwitch(it)
+ }, fabricPrivacy::logException)
+ disposable += rxBus
+ .toObservable(EventData.ActionTempTargetPreCheck::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe({
+ aapsLogger.debug(LTag.WEAR, "ActionTempTargetPreCheck received $it from ${it.sourceNodeId}")
+ handleTempTargetPreCheck(it)
+ }, fabricPrivacy::logException)
+ disposable += rxBus
+ .toObservable(EventData.ActionTempTargetConfirmed::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe({
+ aapsLogger.debug(LTag.WEAR, "ActionTempTargetConfirmed received $it from ${it.sourceNodeId}")
+ doTempTarget(it)
+ }, fabricPrivacy::logException)
+ disposable += rxBus
+ .toObservable(EventData.ActionBolusPreCheck::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe({
+ aapsLogger.debug(LTag.WEAR, "ActionBolusPreCheck received $it from ${it.sourceNodeId}")
+ handleBolusPreCheck(it)
+ }, fabricPrivacy::logException)
+ disposable += rxBus
+ .toObservable(EventData.ActionBolusConfirmed::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe({
+ aapsLogger.debug(LTag.WEAR, "ActionBolusConfirmed received $it from ${it.sourceNodeId}")
+ doBolus(it.insulin, it.carbs, null, 0)
+ }, fabricPrivacy::logException)
+ disposable += rxBus
+ .toObservable(EventData.ActionECarbsPreCheck::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe({
+ aapsLogger.debug(LTag.WEAR, "ActionECarbsPreCheck received $it from ${it.sourceNodeId}")
+ handleECarbsPreCheck(it)
+ }, fabricPrivacy::logException)
+ disposable += rxBus
+ .toObservable(EventData.ActionECarbsConfirmed::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe({
+ aapsLogger.debug(LTag.WEAR, "ActionECarbsConfirmed received $it from ${it.sourceNodeId}")
+ doECarbs(it.carbs, it.carbsTime, it.duration)
+ }, fabricPrivacy::logException)
+ disposable += rxBus
+ .toObservable(EventData.ActionFillPresetPreCheck::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe({
+ aapsLogger.debug(LTag.WEAR, "ActionFillPresetPreCheck received $it from ${it.sourceNodeId}")
+ handleFillPresetPreCheck(it)
+ }, fabricPrivacy::logException)
+ disposable += rxBus
+ .toObservable(EventData.ActionFillPreCheck::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe({
+ aapsLogger.debug(LTag.WEAR, "ActionFillPreCheck received $it from ${it.sourceNodeId}")
+ handleFillPreCheck(it)
+ }, fabricPrivacy::logException)
+ disposable += rxBus
+ .toObservable(EventData.ActionFillConfirmed::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe({
+ aapsLogger.debug(LTag.WEAR, "ActionFillConfirmed received $it from ${it.sourceNodeId}")
+ if (constraintChecker.applyBolusConstraints(Constraint(it.insulin)).value() - it.insulin != 0.0) {
+ ToastUtils.showToastInUiThread(context, "aborting: previously applied constraint changed")
+ sendError("aborting: previously applied constraint changed")
+ } else
+ doFillBolus(it.insulin)
+ }, fabricPrivacy::logException)
+ disposable += rxBus
+ .toObservable(EventData.ActionQuickWizardPreCheck::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe({
+ aapsLogger.debug(LTag.WEAR, "ActionWizardPreCheck received $it from ${it.sourceNodeId}")
+ handleQuickWizardPreCheck(it)
+ }, fabricPrivacy::logException)
+ disposable += rxBus
+ .toObservable(EventData.ActionWizardPreCheck::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe({
+ aapsLogger.debug(LTag.WEAR, "ActionWizardPreCheck received $it from ${it.sourceNodeId}")
+ handleWizardPreCheck(it)
+ }, fabricPrivacy::logException)
+ disposable += rxBus
+ .toObservable(EventData.ActionWizardConfirmed::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe({
+ aapsLogger.debug(LTag.WEAR, "ActionWizardConfirmed received $it from ${it.sourceNodeId}")
+ if (lastBolusWizard?.timeStamp == it.timeStamp) { //use last calculation as confirmed string matches
+ doBolus(lastBolusWizard!!.calculatedTotalInsulin, lastBolusWizard!!.carbs, null, 0)
+ }
+ lastBolusWizard = null
+ }, fabricPrivacy::logException)
+ }
+
+ private fun handleTddStatus() {
+ val activePump = activePlugin.activePump
+ var message: String
+ // check if DB up to date
+ val dummies: MutableList = LinkedList()
+ val historyList = getTDDList(dummies)
+ if (isOldData(historyList)) {
+ message = "OLD DATA - "
+ //if pump is not busy: try to fetch data
+ if (activePump.isBusy()) {
+ message += rh.gs(R.string.pumpbusy)
+ } else {
+ message += "trying to fetch data from pump."
+ commandQueue.loadTDDs(object : Callback() {
+ override fun run() {
+ val dummies1: MutableList = LinkedList()
+ val historyList1 = getTDDList(dummies1)
+ val reloadMessage =
+ if (isOldData(historyList1))
+ "TDD: Still old data! Cannot load from pump.\n" + generateTDDMessage(historyList1, dummies1)
+ else
+ generateTDDMessage(historyList1, dummies1)
+ rxBus.send(
+ EventMobileToWear(
+ EventData.ConfirmAction(
+ rh.gs(R.string.tdd),
+ reloadMessage,
+ returnCommand = null
+ )
+ )
+ )
+ }
+ })
+ }
+ } else { // if up to date: prepare, send (check if CPP is activated -> add CPP stats)
+ message = generateTDDMessage(historyList, dummies)
+ }
+ rxBus.send(
+ EventMobileToWear(
+ EventData.ConfirmAction(
+ rh.gs(R.string.tdd),
+ message,
+ returnCommand = null
+ )
+ )
+ )
+ }
+
+ private fun handleWizardPreCheck(command: EventData.ActionWizardPreCheck) {
+ val pump = activePlugin.activePump
+ if (!pump.isInitialized() || pump.isSuspended() || loop.isDisconnected) {
+ sendError(rh.gs(R.string.wizard_pump_not_available))
+ return
+ }
+ val carbsBeforeConstraints = command.carbs
+ val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbsBeforeConstraints)).value()
+ if (carbsAfterConstraints - carbsBeforeConstraints != 0) {
+ sendError(rh.gs(R.string.wizard_carbs_constraint))
+ return
+ }
+ val useBG = sp.getBoolean(R.string.key_wearwizard_bg, true)
+ val useTT = sp.getBoolean(R.string.key_wearwizard_tt, false)
+ val useBolusIOB = sp.getBoolean(R.string.key_wearwizard_bolusiob, true)
+ val useBasalIOB = sp.getBoolean(R.string.key_wearwizard_basaliob, true)
+ val useCOB = sp.getBoolean(R.string.key_wearwizard_cob, true)
+ val useTrend = sp.getBoolean(R.string.key_wearwizard_trend, false)
+ val percentage = command.percentage
+ val profile = profileFunction.getProfile()
+ val profileName = profileFunction.getProfileName()
+ if (profile == null) {
+ sendError(rh.gs(R.string.wizard_no_active_profile))
+ return
+ }
+ val bgReading = iobCobCalculator.ads.actualBg()
+ if (bgReading == null) {
+ sendError(rh.gs(R.string.wizard_no_actual_bg))
+ return
+ }
+ val cobInfo = iobCobCalculator.getCobInfo(false, "Wizard wear")
+ if (cobInfo.displayCob == null) {
+ sendError(rh.gs(R.string.wizard_no_cob))
+ return
+ }
+ 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, 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) {
+ sendError("No insulin required")
+ return
+ }
+ val message =
+ rh.gs(R.string.wizard_result, bolusWizard.calculatedTotalInsulin, bolusWizard.carbs) + "\n_____________\n" + bolusWizard.explainShort()
+ lastBolusWizard = bolusWizard
+ rxBus.send(
+ EventMobileToWear(
+ EventData.ConfirmAction(
+ rh.gs(R.string.confirm).uppercase(), message,
+ returnCommand = EventData.ActionWizardConfirmed(bolusWizard.timeStamp)
+ )
+ )
+ )
+ }
+
+ private fun handleQuickWizardPreCheck(command: EventData.ActionQuickWizardPreCheck) {
+ val actualBg = iobCobCalculator.ads.actualBg()
+ val profile = profileFunction.getProfile()
+ val profileName = profileFunction.getProfileName()
+ val quickWizardEntry = quickWizard.get(command.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
+ }
+
+ val message = rh.gs(R.string.quick_wizard_message, quickWizardEntry.buttonText(), wizard.calculatedTotalInsulin, quickWizardEntry.carbs()) +
+ "\n_____________\n" + wizard.explainShort()
+
+ rxBus.send(
+ EventMobileToWear(
+ EventData.ConfirmAction(
+ rh.gs(R.string.confirm).uppercase(), message,
+ returnCommand = EventData.ActionBolusConfirmed(insulinAfterConstraints, carbsAfterConstraints)
+ )
+ )
+ )
+ }
+
+ private fun handleBolusPreCheck(command: EventData.ActionBolusPreCheck) {
+ val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(command.insulin)).value()
+ val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(command.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
+ }
+ var message = ""
+ message += rh.gs(R.string.bolus) + ": " + insulinAfterConstraints + "U\n"
+ message += rh.gs(R.string.carbs) + ": " + carbsAfterConstraints + "g"
+ if (insulinAfterConstraints - command.insulin != 0.0 || carbsAfterConstraints - command.carbs != 0)
+ message += "\n" + rh.gs(R.string.constraintapllied)
+ rxBus.send(
+ EventMobileToWear(
+ EventData.ConfirmAction(
+ rh.gs(R.string.confirm).uppercase(), message,
+ returnCommand = EventData.ActionBolusConfirmed(insulinAfterConstraints, carbsAfterConstraints)
+ )
+ )
+ )
+ }
+
+ private fun handleECarbsPreCheck(command: EventData.ActionECarbsPreCheck) {
+ val startTimeStamp = System.currentTimeMillis() + T.hours(command.carbsTimeShift.toLong()).msecs()
+ val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(command.carbs)).value()
+ var message = rh.gs(R.string.carbs) + ": " + carbsAfterConstraints + "g" +
+ "\n" + rh.gs(R.string.time) + ": " + dateUtil.timeString(startTimeStamp) +
+ "\n" + rh.gs(R.string.duration) + ": " + command.duration + "h"
+ if (carbsAfterConstraints - command.carbs != 0) {
+ message += "\n" + rh.gs(R.string.constraintapllied)
+ }
+ if (carbsAfterConstraints <= 0) {
+ sendError("Carbs = 0! No action taken!")
+ return
+ }
+ rxBus.send(
+ EventMobileToWear(
+ EventData.ConfirmAction(
+ rh.gs(R.string.confirm).uppercase(), message,
+ returnCommand = EventData.ActionECarbsConfirmed(carbsAfterConstraints, startTimeStamp, command.duration)
+ )
+ )
+ )
+ }
+
+ private fun handleFillPresetPreCheck(command: EventData.ActionFillPresetPreCheck) {
+ val amount: Double = when (command.button) {
+ 1 -> sp.getDouble("fill_button1", 0.3)
+ 2 -> sp.getDouble("fill_button2", 0.0)
+ 3 -> sp.getDouble("fill_button3", 0.0)
+ else -> return
+ }
+ val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(amount)).value()
+ var message = rh.gs(R.string.primefill) + ": " + insulinAfterConstraints + "U"
+ if (insulinAfterConstraints - amount != 0.0) message += "\n" + rh.gs(R.string.constraintapllied)
+ rxBus.send(
+ EventMobileToWear(
+ EventData.ConfirmAction(
+ rh.gs(R.string.confirm).uppercase(), message,
+ returnCommand = EventData.ActionFillConfirmed(insulinAfterConstraints)
+ )
+ )
+ )
+ }
+
+ private fun handleFillPreCheck(command: EventData.ActionFillPreCheck) {
+ val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(command.insulin)).value()
+ var message = rh.gs(R.string.primefill) + ": " + insulinAfterConstraints + "U"
+ if (insulinAfterConstraints - command.insulin != 0.0) message += "\n" + rh.gs(R.string.constraintapllied)
+ rxBus.send(
+ EventMobileToWear(
+ EventData.ConfirmAction(
+ rh.gs(R.string.confirm).uppercase(), message,
+ returnCommand = EventData.ActionFillConfirmed(insulinAfterConstraints)
+ )
+ )
+ )
+ }
+
+ private fun handleProfileSwitchSendInitialData() {
+ val activeProfileSwitch = repository.getEffectiveProfileSwitchActiveAt(dateUtil.now()).blockingGet()
+ if (activeProfileSwitch is ValueWrapper.Existing) { // read CPP values
+ rxBus.send(
+ EventMobileToWear(EventData.ActionProfileSwitchOpenActivity(T.msecs(activeProfileSwitch.value.originalTimeshift).hours().toInt(), activeProfileSwitch.value.originalPercentage))
+ )
+ } else {
+ sendError("No active profile switch!")
+ return
+ }
+
+ }
+
+ private fun handleProfileSwitchPreCheck(command: EventData.ActionProfileSwitchPreCheck) {
+ val activeProfileSwitch = repository.getEffectiveProfileSwitchActiveAt(dateUtil.now()).blockingGet()
+ if (activeProfileSwitch is ValueWrapper.Absent) {
+ sendError("No active profile switch!")
+ }
+ if (command.percentage < Constants.CPP_MIN_PERCENTAGE || command.percentage > Constants.CPP_MAX_PERCENTAGE) {
+ sendError(rh.gs(R.string.valueoutofrange, "Profile-Percentage"))
+ }
+ if (command.timeShift < 0 || command.timeShift > 23) {
+ sendError(rh.gs(R.string.valueoutofrange, "Profile-Timeshift"))
+ }
+ val message = "Profile:" + "\n\n" +
+ "Timeshift: " + command.timeShift + "\n" +
+ "Percentage: " + command.percentage + "%"
+ rxBus.send(
+ EventMobileToWear(
+ EventData.ConfirmAction(
+ rh.gs(R.string.confirm).uppercase(), message,
+ returnCommand = EventData.ActionProfileSwitchConfirmed(command.timeShift, command.percentage)
+ )
+ )
+ )
+ }
+
+ private fun handleTempTargetPreCheck(action: EventData.ActionTempTargetPreCheck) {
+ val title = rh.gs(R.string.confirm).uppercase()
+ var message = ""
+ val presetIsMGDL = profileFunction.getUnits() == GlucoseUnit.MGDL
+ when (action.command) {
+ EventData.ActionTempTargetPreCheck.TempTargetCommand.PRESET_ACTIVITY -> {
+ val activityTTDuration = defaultValueHelper.determineActivityTTDuration()
+ val activityTT = defaultValueHelper.determineActivityTT()
+ val reason = rh.gs(R.string.activity)
+ message += rh.gs(R.string.wear_action_tempt_preset_message, reason, activityTT, activityTTDuration)
+ rxBus.send(
+ EventMobileToWear(
+ EventData.ConfirmAction(
+ title, message,
+ returnCommand = EventData.ActionTempTargetConfirmed(presetIsMGDL, activityTTDuration, activityTT, activityTT)
+ )
+ )
+ )
+ }
+
+ EventData.ActionTempTargetPreCheck.TempTargetCommand.PRESET_HYPO -> {
+ val hypoTTDuration = defaultValueHelper.determineHypoTTDuration()
+ val hypoTT = defaultValueHelper.determineHypoTT()
+ val reason = rh.gs(R.string.hypo)
+ message += rh.gs(R.string.wear_action_tempt_preset_message, reason, hypoTT, hypoTTDuration)
+ rxBus.send(
+ EventMobileToWear(
+ EventData.ConfirmAction(
+ title, message,
+ returnCommand = EventData.ActionTempTargetConfirmed(presetIsMGDL, hypoTTDuration, hypoTT, hypoTT)
+ )
+ )
+ )
+ }
+
+ EventData.ActionTempTargetPreCheck.TempTargetCommand.PRESET_EATING -> {
+ val eatingSoonTTDuration = defaultValueHelper.determineEatingSoonTTDuration()
+ val eatingSoonTT = defaultValueHelper.determineEatingSoonTT()
+ val reason = rh.gs(R.string.eatingsoon)
+ message += rh.gs(R.string.wear_action_tempt_preset_message, reason, eatingSoonTT, eatingSoonTTDuration)
+ rxBus.send(
+ EventMobileToWear(
+ EventData.ConfirmAction(
+ title, message,
+ returnCommand = EventData.ActionTempTargetConfirmed(presetIsMGDL, eatingSoonTTDuration, eatingSoonTT, eatingSoonTT)
+ )
+ )
+ )
+ }
+
+ EventData.ActionTempTargetPreCheck.TempTargetCommand.CANCEL -> {
+ message += rh.gs(R.string.wear_action_tempt_cancel_message)
+ rxBus.send(
+ EventMobileToWear(
+ EventData.ConfirmAction(
+ title, message,
+ returnCommand = EventData.ActionTempTargetConfirmed(true, 0, 0.0, 0.0)
+ )
+ )
+ )
+ }
+
+ EventData.ActionTempTargetPreCheck.TempTargetCommand.MANUAL -> {
+ if (profileFunction.getUnits() == GlucoseUnit.MGDL != action.isMgdl) {
+ sendError(rh.gs(R.string.wear_action_tempt_unit_error))
+ return
+ }
+ if (action.duration == 0) {
+ message += rh.gs(R.string.wear_action_tempt_zero_message)
+ rxBus.send(
+ EventMobileToWear(
+ EventData.ConfirmAction(
+ title, message,
+ returnCommand = EventData.ActionTempTargetConfirmed(true, 0, 0.0, 0.0)
+ )
+ )
+ )
+ } else {
+ var low = action.low
+ var high = action.high
+ if (!action.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
+ }
+ message += if (low == high) rh.gs(R.string.wear_action_tempt_manual_message, action.low, action.duration)
+ else rh.gs(R.string.wear_action_tempt_manual_range_message, action.low, action.high, action.duration)
+ rxBus.send(
+ EventMobileToWear(
+ EventData.ConfirmAction(
+ title, message,
+ returnCommand = EventData.ActionTempTargetConfirmed(presetIsMGDL, action.duration, action.low, action.high)
+ )
+ )
+ )
+ }
+ }
+ }
+ }
+
+ private fun QuickWizardEntry.toWear(): EventData.QuickWizard.QuickWizardEntry =
+ EventData.QuickWizard.QuickWizardEntry(
+ guid = guid(),
+ buttonText = buttonText(),
+ carbs = carbs(),
+ validFrom = validFrom(),
+ validTo = validTo()
+ )
+
+ fun resendData(from: String) {
+ aapsLogger.debug(LTag.WEAR, "Sending data to wear from $from")
+ // SingleBg
+ iobCobCalculator.ads.lastBg()?.let { rxBus.send(EventMobileToWear(getSingleBG(it))) }
+ // Preferences
+ rxBus.send(
+ EventMobileToWear(
+ EventData.Preferences(
+ timeStamp = System.currentTimeMillis(),
+ wearControl = sp.getBoolean(R.string.key_wear_control, false),
+ unitsMgdl = profileFunction.getUnits() == GlucoseUnit.MGDL,
+ bolusPercentage = sp.getInt(R.string.key_boluswizard_percentage, 100),
+ maxCarbs = sp.getInt(R.string.key_treatmentssafety_maxcarbs, 48),
+ maxBolus = sp.getDouble(R.string.key_treatmentssafety_maxbolus, 3.0)
+ )
+ )
+ )
+ // QuickWizard
+ rxBus.send(
+ EventMobileToWear(
+ EventData.QuickWizard(
+ ArrayList(quickWizard.list().filter { it.forDevice(QuickWizardEntry.DEVICE_WATCH) }.map { it.toWear() })
+ )
+ )
+ )
+ // GraphData
+ val startTime = System.currentTimeMillis() - (60000 * 60 * 5.5).toLong()
+ rxBus.send(EventMobileToWear(EventData.GraphData(ArrayList(repository.compatGetBgReadingsDataFromTime(startTime, true).blockingGet().map { getSingleBG(it) }))))
+ // Treatments
+ sendTreatments()
+ // Status
+ // Keep status last. Wear start refreshing after status received
+ sendStatus()
+ }
+
+ private fun sendTreatments() {
+ val now = System.currentTimeMillis()
+ val startTimeWindow = now - (60000 * 60 * 5.5).toLong()
+ val basals = arrayListOf()
+ val temps = arrayListOf()
+ val boluses = arrayListOf()
+ val predictions = arrayListOf()
+ val profile = profileFunction.getProfile() ?: return
+ var beginBasalSegmentTime = startTimeWindow
+ var runningTime = startTimeWindow
+ var beginBasalValue = profile.getBasal(beginBasalSegmentTime)
+ var endBasalValue = beginBasalValue
+ var tb1 = iobCobCalculator.getTempBasalIncludingConvertedExtended(runningTime)
+ var tb2: TemporaryBasal?
+ var tbBefore = beginBasalValue
+ var tbAmount = beginBasalValue
+ var tbStart = runningTime
+ if (tb1 != null) {
+ val profileTB = profileFunction.getProfile(runningTime)
+ if (profileTB != null) {
+ tbAmount = tb1.convertedToAbsolute(runningTime, profileTB)
+ tbStart = runningTime
+ }
+ }
+ while (runningTime < now) {
+ val profileTB = profileFunction.getProfile(runningTime) ?: return
+ //basal rate
+ endBasalValue = profile.getBasal(runningTime)
+ if (endBasalValue != beginBasalValue) {
+ //push the segment we recently left
+ basals.add(EventData.TreatmentData.Basal(beginBasalSegmentTime, runningTime, beginBasalValue))
+
+ //begin new Basal segment
+ beginBasalSegmentTime = runningTime
+ beginBasalValue = endBasalValue
+ }
+
+ //temps
+ tb2 = iobCobCalculator.getTempBasalIncludingConvertedExtended(runningTime)
+ if (tb1 == null && tb2 == null) {
+ //no temp stays no temp
+ } else if (tb1 != null && tb2 == null) {
+ //temp is over -> push it
+ temps.add(EventData.TreatmentData.TempBasal(tbStart, tbBefore, runningTime, endBasalValue, tbAmount))
+ tb1 = null
+ } else if (tb1 == null && tb2 != null) {
+ //temp begins
+ tb1 = tb2
+ tbStart = runningTime
+ tbBefore = endBasalValue
+ tbAmount = tb1.convertedToAbsolute(runningTime, profileTB)
+ } else if (tb1 != null && tb2 != null) {
+ val currentAmount = tb2.convertedToAbsolute(runningTime, profileTB)
+ if (currentAmount != tbAmount) {
+ temps.add(EventData.TreatmentData.TempBasal(tbStart, tbBefore, runningTime, currentAmount, tbAmount))
+ tbStart = runningTime
+ tbBefore = tbAmount
+ tbAmount = currentAmount
+ tb1 = tb2
+ }
+ }
+ runningTime += (5 * 60 * 1000).toLong()
+ }
+ if (beginBasalSegmentTime != runningTime) {
+ //push the remaining segment
+ basals.add(EventData.TreatmentData.Basal(beginBasalSegmentTime, runningTime, beginBasalValue))
+ }
+ if (tb1 != null) {
+ tb2 = iobCobCalculator.getTempBasalIncludingConvertedExtended(now) //use "now" to express current situation
+ if (tb2 == null) {
+ //express the cancelled temp by painting it down one minute early
+ temps.add(EventData.TreatmentData.TempBasal(tbStart, tbBefore, now - 60 * 1000, endBasalValue, tbAmount))
+ } else {
+ //express currently running temp by painting it a bit into the future
+ val profileNow = profileFunction.getProfile(now)
+ val currentAmount = tb2.convertedToAbsolute(now, profileNow!!)
+ if (currentAmount != tbAmount) {
+ temps.add(EventData.TreatmentData.TempBasal(tbStart, tbBefore, now, tbAmount, tbAmount))
+ temps.add(EventData.TreatmentData.TempBasal(now, tbAmount, runningTime + 5 * 60 * 1000, currentAmount, currentAmount))
+ } else {
+ temps.add(EventData.TreatmentData.TempBasal(tbStart, tbBefore, runningTime + 5 * 60 * 1000, tbAmount, tbAmount))
+ }
+ }
+ } else {
+ tb2 = iobCobCalculator.getTempBasalIncludingConvertedExtended(now) //use "now" to express current situation
+ if (tb2 != null) {
+ //onset at the end
+ val profileTB = profileFunction.getProfile(runningTime)
+ val currentAmount = tb2.convertedToAbsolute(runningTime, profileTB!!)
+ temps.add(EventData.TreatmentData.TempBasal(now - 60 * 1000, endBasalValue, runningTime + 5 * 60 * 1000, currentAmount, currentAmount))
+ }
+ }
+ repository.getBolusesIncludingInvalidFromTime(startTimeWindow, true).blockingGet()
+ .stream()
+ .filter { (_, _, _, _, _, _, _, _, _, type) -> type !== Bolus.Type.PRIMING }
+ .forEach { (_, _, _, isValid, _, _, timestamp, _, amount, type) -> boluses.add(EventData.TreatmentData.Treatment(timestamp, amount, 0.0, type === Bolus.Type.SMB, isValid)) }
+ repository.getCarbsDataFromTimeExpanded(startTimeWindow, true).blockingGet()
+ .forEach { (_, _, _, isValid, _, _, timestamp, _, _, amount) -> boluses.add(EventData.TreatmentData.Treatment(timestamp, 0.0, amount, false, isValid)) }
+ val finalLastRun = loop.lastRun
+ if (sp.getBoolean("wear_predictions", true) && finalLastRun?.request?.hasPredictions == true && finalLastRun.constraintsProcessed != null) {
+ val predArray = finalLastRun.constraintsProcessed!!.predictions
+ .stream().map { bg: GlucoseValue -> GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, rh) }
+ .collect(Collectors.toList())
+ if (predArray.isNotEmpty())
+ for (bg in predArray) if (bg.data.value > 39)
+ predictions.add(
+ EventData.SingleBg(
+ timeStamp = bg.data.timestamp,
+ glucoseUnits = Constants.MGDL,
+ sgv = bg.data.value,
+ high = 0.0,
+ low = 0.0,
+ color = bg.color(null)
+ )
+ )
+ }
+ rxBus.send(EventMobileToWear(EventData.TreatmentData(temps, basals, boluses, predictions)))
+ }
+
+ private fun sendStatus() {
+ val profile = profileFunction.getProfile()
+ var status = rh.gs(R.string.noprofile)
+ var iobSum = ""
+ var iobDetail = ""
+ var cobString = ""
+ var currentBasal = ""
+ var bgiString = ""
+ if (profile != null) {
+ val bolusIob = iobCobCalculator.calculateIobFromBolus().round()
+ val basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round()
+ iobSum = DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob)
+ iobDetail = "(${DecimalFormatter.to2Decimal(bolusIob.iob)}|${DecimalFormatter.to2Decimal(basalIob.basaliob)})"
+ cobString = iobCobCalculator.getCobInfo(false, "WatcherUpdaterService").generateCOBString()
+ currentBasal = iobCobCalculator.getTempBasalIncludingConvertedExtended(System.currentTimeMillis())?.toStringShort() ?: DecimalFormatter.to2Decimal(profile.getBasal()) + "U/h"
+
+ //bgi
+ val bgi = -(bolusIob.activity + basalIob.activity) * 5 * Profile.fromMgdlToUnits(profile.getIsfMgdl(), profileFunction.getUnits())
+ bgiString = "" + (if (bgi >= 0) "+" else "") + DecimalFormatter.to1Decimal(bgi)
+ status = generateStatusString(profile, currentBasal, iobSum, iobDetail, bgiString)
+ }
+
+ //batteries
+ val phoneBattery = receiverStatusStore.batteryLevel
+ val rigBattery = nsDeviceStatus.uploaderStatus.trim { it <= ' ' }
+ //OpenAPS status
+ val openApsStatus =
+ if (config.APS) loop.lastRun?.let { if (it.lastTBREnact != 0L) it.lastTBREnact else -1 } ?: -1
+ else nsDeviceStatus.openApsTimestamp
+
+ rxBus.send(
+ EventMobileToWear(
+ EventData.Status(
+ externalStatus = status,
+ iobSum = iobSum,
+ iobDetail = iobDetail,
+ detailedIob = sp.getBoolean(R.string.key_wear_detailediob, false),
+ cob = cobString,
+ currentBasal = currentBasal,
+ battery = phoneBattery.toString(),
+ rigBattery = rigBattery,
+ openApsStatus = openApsStatus,
+ bgi = bgiString,
+ showBgi = sp.getBoolean(R.string.key_wear_showbgi, false),
+ batteryLevel = if (phoneBattery >= 30) 1 else 0
+ )
+ )
+ )
+ }
+
+ private fun deltaString(deltaMGDL: Double, deltaMMOL: Double, units: GlucoseUnit): String {
+ val detailed = sp.getBoolean(R.string.key_wear_detailed_delta, false)
+ var deltaString = if (deltaMGDL >= 0) "+" else "-"
+ deltaString += if (units == GlucoseUnit.MGDL) {
+ if (detailed) DecimalFormatter.to1Decimal(abs(deltaMGDL)) else DecimalFormatter.to0Decimal(abs(deltaMGDL))
+ } else {
+ if (detailed) DecimalFormatter.to2Decimal(abs(deltaMMOL)) else DecimalFormatter.to1Decimal(abs(deltaMMOL))
+ }
+ return deltaString
+ }
+
+ private fun getSingleBG(glucoseValue: GlucoseValue): EventData.SingleBg {
+ val glucoseStatus = glucoseStatusProvider.getGlucoseStatusData(true)
+ val units = profileFunction.getUnits()
+ val lowLine = Profile.toMgdl(defaultValueHelper.determineLowLine(), units)
+ val highLine = Profile.toMgdl(defaultValueHelper.determineHighLine(), units)
+
+ return EventData.SingleBg(
+ timeStamp = glucoseValue.timestamp,
+ sgvString = glucoseValue.valueToUnitsString(units),
+ glucoseUnits = units.asText,
+ slopeArrow = trendCalculator.getTrendArrow(glucoseValue).symbol,
+ delta = glucoseStatus?.let { deltaString(it.delta, it.delta * Constants.MGDL_TO_MMOLL, units) } ?: "--",
+ avgDelta = glucoseStatus?.let { deltaString(it.shortAvgDelta, it.shortAvgDelta * Constants.MGDL_TO_MMOLL, units) } ?: "--",
+ sgvLevel = if (glucoseValue.value > highLine) 1L else if (glucoseValue.value < lowLine) -1L else 0L,
+ sgv = glucoseValue.value,
+ high = highLine,
+ low = lowLine,
+ color = 0
+ )
+ }
+
+ //Check for Temp-Target:
+ private
+ val targetsStatus: String
+ get() {
+ var ret = ""
+ if (!config.APS) {
+ return "Targets only apply in APS mode!"
+ }
+ val profile = profileFunction.getProfile() ?: return "No profile set :("
+ //Check for Temp-Target:
+ val tempTarget = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet()
+ if (tempTarget is ValueWrapper.Existing) {
+ ret += "Temp Target: " + Profile.toTargetRangeString(tempTarget.value.lowTarget, tempTarget.value.lowTarget, GlucoseUnit.MGDL, profileFunction.getUnits())
+ ret += "\nuntil: " + dateUtil.timeString(tempTarget.value.end)
+ ret += "\n\n"
+ }
+ ret += "DEFAULT RANGE: "
+ ret += Profile.fromMgdlToUnits(profile.getTargetLowMgdl(), profileFunction.getUnits()).toString() + " - " + Profile.fromMgdlToUnits(
+ profile.getTargetHighMgdl(),
+ profileFunction.getUnits()
+ )
+ ret += " target: " + Profile.fromMgdlToUnits(profile.getTargetMgdl(), profileFunction.getUnits())
+ return ret
+ }
+
+ private
+ val oAPSResultStatus: String
+ get() {
+ var ret = ""
+ if (!config.APS)
+ return "Only apply in APS mode!"
+ val usedAPS = activePlugin.activeAPS
+ val result = usedAPS.lastAPSResult ?: return "Last result not available!"
+ ret += if (!result.isChangeRequested) {
+ rh.gs(R.string.nochangerequested) + "\n"
+ } else if (result.rate == 0.0 && result.duration == 0) {
+ rh.gs(R.string.canceltemp) + "\n"
+ } else {
+ rh.gs(R.string.rate) + ": " + DecimalFormatter.to2Decimal(result.rate) + " U/h " +
+ "(" + DecimalFormatter.to2Decimal(result.rate / activePlugin.activePump.baseBasalRate * 100) + "%)\n" +
+ rh.gs(R.string.duration) + ": " + DecimalFormatter.to0Decimal(result.duration.toDouble()) + " min\n"
+ }
+ ret += "\n" + rh.gs(R.string.reason) + ": " + result.reason
+ return ret
+ }
+
+ // decide if enabled/disabled closed/open; what Plugin as APS?
+ private
+ val loopStatus: String
+ get() {
+ var ret = ""
+ // decide if enabled/disabled closed/open; what Plugin as APS?
+ if ((loop as PluginBase).isEnabled()) {
+ ret += if (constraintChecker.isClosedLoopAllowed().value()) {
+ "CLOSED LOOP\n"
+ } else {
+ "OPEN LOOP\n"
+ }
+ val aps = activePlugin.activeAPS
+ ret += "APS: " + (aps as PluginBase).name
+ val lastRun = loop.lastRun
+ if (lastRun != null) {
+ ret += "\nLast Run: " + dateUtil.timeString(lastRun.lastAPSRun)
+ if (lastRun.lastTBREnact != 0L) ret += "\nLast Enact: " + dateUtil.timeString(lastRun.lastTBREnact)
+ }
+ } else {
+ ret += "LOOP DISABLED\n"
+ }
+ return ret
+ }
+
+ private fun isOldData(historyList: List): Boolean {
+ val startsYesterday = activePlugin.activePump.pumpDescription.supportsTDDs
+ val df: DateFormat = SimpleDateFormat("dd.MM.", Locale.getDefault())
+ return historyList.size < 3 || df.format(Date(historyList[0].timestamp)) != df.format(Date(System.currentTimeMillis() - if (startsYesterday) 1000 * 60 * 60 * 24 else 0))
+ }
+
+ private fun getTDDList(returnDummies: MutableList): MutableList {
+ var historyList = repository.getLastTotalDailyDoses(10, false).blockingGet().toMutableList()
+ //var historyList = databaseHelper.getTDDs().toMutableList()
+ historyList = historyList.subList(0, min(10, historyList.size))
+ //fill single gaps - only needed for Dana*R data
+ val dummies: MutableList = returnDummies
+ val df: DateFormat = SimpleDateFormat("dd.MM.", Locale.getDefault())
+ for (i in 0 until historyList.size - 1) {
+ val elem1 = historyList[i]
+ val elem2 = historyList[i + 1]
+ if (df.format(Date(elem1.timestamp)) != df.format(Date(elem2.timestamp + 25 * 60 * 60 * 1000))) {
+ val dummy = TotalDailyDose(timestamp = elem1.timestamp - T.hours(24).msecs(), bolusAmount = elem1.bolusAmount / 2, basalAmount = elem1.basalAmount / 2)
+ dummies.add(dummy)
+ elem1.basalAmount /= 2.0
+ elem1.bolusAmount /= 2.0
+ }
+ }
+ historyList.addAll(dummies)
+ historyList.sortWith { lhs, rhs -> (rhs.timestamp - lhs.timestamp).toInt() }
+ return historyList
+ }
+
+ private fun generateTDDMessage(historyList: MutableList, dummies: MutableList): String {
+ val profile = profileFunction.getProfile() ?: return "No profile loaded :("
+ if (historyList.isEmpty()) {
+ return "No history data!"
+ }
+ val df: DateFormat = SimpleDateFormat("dd.MM.", Locale.getDefault())
+ var message = ""
+ val refTDD = profile.baseBasalSum() * 2
+ if (df.format(Date(historyList[0].timestamp)) == df.format(Date())) {
+ val tdd = historyList[0].total
+ historyList.removeAt(0)
+ message += "Today: " + DecimalFormatter.to2Decimal(tdd) + "U " + (DecimalFormatter.to0Decimal(100 * tdd / refTDD) + "%") + "\n"
+ message += "\n"
+ }
+ var weighted03 = 0.0
+ var weighted05 = 0.0
+ var weighted07 = 0.0
+ historyList.reverse()
+ for ((i, record) in historyList.withIndex()) {
+ val tdd = record.total
+ if (i == 0) {
+ weighted03 = tdd
+ weighted05 = tdd
+ weighted07 = tdd
+ } else {
+ weighted07 = weighted07 * 0.3 + tdd * 0.7
+ weighted05 = weighted05 * 0.5 + tdd * 0.5
+ weighted03 = weighted03 * 0.7 + tdd * 0.3
+ }
+ }
+ message += "weighted:\n"
+ message += "0.3: " + DecimalFormatter.to2Decimal(weighted03) + "U " + (DecimalFormatter.to0Decimal(100 * weighted03 / refTDD) + "%") + "\n"
+ message += "0.5: " + DecimalFormatter.to2Decimal(weighted05) + "U " + (DecimalFormatter.to0Decimal(100 * weighted05 / refTDD) + "%") + "\n"
+ message += "0.7: " + DecimalFormatter.to2Decimal(weighted07) + "U " + (DecimalFormatter.to0Decimal(100 * weighted07 / refTDD) + "%") + "\n"
+ message += "\n"
+ historyList.reverse()
+ //add TDDs:
+ for (record in historyList) {
+ val tdd = record.total
+ message += df.format(Date(record.timestamp)) + " " + DecimalFormatter.to2Decimal(tdd) + "U " + (DecimalFormatter.to0Decimal(100 * tdd / refTDD) + "%") + (if (dummies.contains(
+ record
+ )
+ ) "x" else "") + "\n"
+ }
+ return message
+ }
+
+ private fun generateStatusString(profile: Profile?, currentBasal: String, iobSum: String, iobDetail: String, bgiString: String): String {
+ var status = ""
+ profile ?: return rh.gs(R.string.noprofile)
+ if (!(loop as PluginBase).isEnabled()) status += rh.gs(R.string.disabledloop) + "\n"
+
+ val iobString =
+ if (sp.getBoolean(R.string.key_wear_detailediob, false)) "$iobSum $iobDetail"
+ else iobSum + "U"
+
+ status += "$currentBasal $iobString"
+
+ //add BGI if shown, otherwise return
+ if (sp.getBoolean(R.string.key_wear_showbgi, false)) status += " $bgiString"
+ return status
+ }
+
+ private fun doTempTarget(command: EventData.ActionTempTargetConfirmed) {
+ if (command.duration != 0) {
+ disposable += repository.runTransactionForResult(
+ InsertAndCancelCurrentTemporaryTargetTransaction(
+ timestamp = System.currentTimeMillis(),
+ duration = TimeUnit.MINUTES.toMillis(command.duration.toLong()),
+ reason = TemporaryTarget.Reason.WEAR,
+ lowTarget = Profile.toMgdl(command.low, profileFunction.getUnits()),
+ highTarget = Profile.toMgdl(command.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(
+ UserEntry.Action.TT, UserEntry.Sources.Wear,
+ ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.WEAR),
+ ValueWithUnit.fromGlucoseUnit(command.low, profileFunction.getUnits().asText),
+ ValueWithUnit.fromGlucoseUnit(command.high, profileFunction.getUnits().asText).takeIf { command.low != command.high },
+ ValueWithUnit.Minute(command.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(
+ UserEntry.Action.CANCEL_TT, UserEntry.Sources.Wear,
+ ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.WEAR)
+ )
+ }
+ }
+
+ private fun doBolus(amount: Double, carbs: Int, carbsTime: Long?, carbsDuration: Int) {
+ val detailedBolusInfo = DetailedBolusInfo()
+ detailedBolusInfo.insulin = amount
+ detailedBolusInfo.carbs = carbs.toDouble()
+ detailedBolusInfo.bolusType = DetailedBolusInfo.BolusType.NORMAL
+ detailedBolusInfo.carbsTimestamp = carbsTime
+ detailedBolusInfo.carbsDuration = T.hours(carbsDuration.toLong()).msecs()
+ if (detailedBolusInfo.insulin > 0 || detailedBolusInfo.carbs > 0) {
+ val action = when {
+ amount == 0.0 -> UserEntry.Action.CARBS
+ carbs == 0 -> UserEntry.Action.BOLUS
+ carbsDuration > 0 -> UserEntry.Action.EXTENDED_CARBS
+ else -> UserEntry.Action.TREATMENT
+ }
+ uel.log(action, UserEntry.Sources.Wear,
+ 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)
+ }
+ })
+ }
+ }
+
+ private fun doFillBolus(amount: Double) {
+ val detailedBolusInfo = DetailedBolusInfo()
+ detailedBolusInfo.insulin = amount
+ detailedBolusInfo.bolusType = DetailedBolusInfo.BolusType.PRIMING
+ uel.log(
+ UserEntry.Action.PRIME_BOLUS, UserEntry.Sources.Wear,
+ 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)
+ }
+ }
+ })
+ }
+
+ private fun doECarbs(carbs: Int, carbsTime: Long, duration: Int) {
+ uel.log(if (duration == 0) UserEntry.Action.CARBS else UserEntry.Action.EXTENDED_CARBS, UserEntry.Sources.Wear,
+ ValueWithUnit.Timestamp(carbsTime),
+ ValueWithUnit.Gram(carbs),
+ ValueWithUnit.Hour(duration).takeIf { duration != 0 })
+ doBolus(0.0, carbs, carbsTime, duration)
+ }
+
+ private fun doProfileSwitch(command: EventData.ActionProfileSwitchConfirmed) {
+ //check for validity
+ if (command.percentage < Constants.CPP_MIN_PERCENTAGE || command.percentage > Constants.CPP_MAX_PERCENTAGE)
+ return
+ if (command.timeShift < 0 || command.timeShift > 23)
+ return
+ profileFunction.getProfile() ?: return
+ //send profile to pump
+ uel.log(
+ UserEntry.Action.PROFILE_SWITCH, UserEntry.Sources.Wear,
+ ValueWithUnit.Percent(command.percentage),
+ ValueWithUnit.Hour(command.timeShift).takeIf { command.timeShift != 0 })
+ profileFunction.createProfileSwitch(0, command.percentage, command.timeShift)
+ }
+
+ @Synchronized private fun sendError(errorMessage: String) {
+ rxBus.send(EventData.ConfirmAction(rh.gs(R.string.error), errorMessage, returnCommand = EventData.Error(dateUtil.now()))) // ignore return path
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataLayerListenerServiceMobile.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataLayerListenerServiceMobile.kt
new file mode 100644
index 0000000000..b8305a981b
--- /dev/null
+++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataLayerListenerServiceMobile.kt
@@ -0,0 +1,200 @@
+package info.nightscout.androidaps.plugins.general.wear.wearintegration
+
+import android.os.Handler
+import android.os.HandlerThread
+import com.google.android.gms.tasks.Tasks
+import com.google.android.gms.wearable.*
+import dagger.android.AndroidInjection
+import info.nightscout.androidaps.R
+import info.nightscout.androidaps.database.AppRepository
+import info.nightscout.androidaps.events.EventMobileToWear
+import info.nightscout.androidaps.interfaces.ActivePlugin
+import info.nightscout.androidaps.interfaces.Config
+import info.nightscout.androidaps.interfaces.IobCobCalculator
+import info.nightscout.androidaps.interfaces.Loop
+import info.nightscout.androidaps.interfaces.ProfileFunction
+import info.nightscout.androidaps.plugins.bus.RxBus
+import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus
+import info.nightscout.androidaps.plugins.general.wear.WearPlugin
+import info.nightscout.androidaps.plugins.general.wear.events.EventWearUpdateGui
+import info.nightscout.androidaps.receivers.ReceiverStatusStore
+import info.nightscout.androidaps.utils.DefaultValueHelper
+import info.nightscout.androidaps.utils.resources.ResourceHelper
+import info.nightscout.androidaps.utils.rx.AapsSchedulers
+import info.nightscout.androidaps.utils.wizard.QuickWizard
+import info.nightscout.shared.logging.AAPSLogger
+import info.nightscout.shared.logging.LTag
+import info.nightscout.shared.sharedPreferences.SP
+import info.nightscout.shared.weardata.EventData
+import io.reactivex.rxjava3.disposables.CompositeDisposable
+import io.reactivex.rxjava3.kotlin.plusAssign
+import kotlinx.coroutines.*
+import kotlinx.coroutines.tasks.await
+import javax.inject.Inject
+
+class DataLayerListenerServiceMobile : WearableListenerService() {
+
+ @Inject lateinit var aapsLogger: AAPSLogger
+ @Inject lateinit var profileFunction: ProfileFunction
+ @Inject lateinit var iobCobCalculator: IobCobCalculator
+ @Inject lateinit var rh: ResourceHelper
+ @Inject lateinit var loop: Loop
+ @Inject lateinit var wearPlugin: WearPlugin
+ @Inject lateinit var sp: SP
+ @Inject lateinit var quickWizard: QuickWizard
+ @Inject lateinit var config: Config
+ @Inject lateinit var nsDeviceStatus: NSDeviceStatus
+ @Inject lateinit var receiverStatusStore: ReceiverStatusStore
+ @Inject lateinit var repository: AppRepository
+ @Inject lateinit var defaultValueHelper: DefaultValueHelper
+ @Inject lateinit var activePlugin: ActivePlugin
+ @Inject lateinit var rxBus: RxBus
+ @Inject lateinit var aapsSchedulers: AapsSchedulers
+
+ private val dataClient by lazy { Wearable.getDataClient(this) }
+ private val messageClient by lazy { Wearable.getMessageClient(this) }
+ private val capabilityClient by lazy { Wearable.getCapabilityClient(this) }
+ //private val nodeClient by lazy { Wearable.getNodeClient(this) }
+
+ private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
+ private var handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper)
+
+ private val disposable = CompositeDisposable()
+
+ private val rxPath get() = getString(R.string.path_rx_bridge)
+
+ override fun onCreate() {
+ AndroidInjection.inject(this)
+ super.onCreate()
+ aapsLogger.debug(LTag.WEAR, "onCreate")
+ handler.post { updateTranscriptionCapability() }
+ disposable += rxBus
+ .toObservable(EventMobileToWear::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe { sendMessage(rxPath, it.payload.serialize()) }
+ }
+
+ override fun onCapabilityChanged(p0: CapabilityInfo) {
+ super.onCapabilityChanged(p0)
+ handler.post { updateTranscriptionCapability() }
+ aapsLogger.debug(LTag.WEAR, "onCapabilityChanged: ${p0.name} ${p0.nodes.joinToString(", ") { it.displayName + "(" + it.id + ")" }}")
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ disposable.clear()
+ scope.cancel()
+ }
+
+ @Suppress("ControlFlowWithEmptyBody", "UNUSED_EXPRESSION")
+ override fun onDataChanged(dataEvents: DataEventBuffer) {
+ //aapsLogger.debug(LTag.WEAR, "onDataChanged")
+
+ if (wearPlugin.isEnabled()) {
+ dataEvents.forEach { event ->
+ if (event.type == DataEvent.TYPE_CHANGED) {
+ val path = event.dataItem.uri.path
+
+ aapsLogger.debug(LTag.WEAR, "onDataChanged: Path: $path, EventDataItem=${event.dataItem}")
+ try {
+ when (path) {
+ }
+ } catch (exception: Exception) {
+ aapsLogger.error(LTag.WEAR, "Message failed", exception)
+ }
+ }
+ }
+ }
+ super.onDataChanged(dataEvents)
+ }
+
+ override fun onMessageReceived(messageEvent: MessageEvent) {
+ super.onMessageReceived(messageEvent)
+
+ if (wearPlugin.isEnabled()) {
+ when (messageEvent.path) {
+ rxPath -> {
+ aapsLogger.debug(LTag.WEAR, "onMessageReceived: ${String(messageEvent.data)}")
+ val command = EventData.deserialize(String(messageEvent.data))
+ rxBus.send(command.also { it.sourceNodeId = messageEvent.sourceNodeId })
+ }
+ }
+ }
+ }
+
+ private var transcriptionNodeId: String? = null
+
+ private fun updateTranscriptionCapability() {
+ val capabilityInfo: CapabilityInfo = Tasks.await(
+ capabilityClient.getCapability(WEAR_CAPABILITY, CapabilityClient.FILTER_REACHABLE)
+ )
+ aapsLogger.debug(LTag.WEAR, "Nodes: ${capabilityInfo.nodes.joinToString(", ") { it.displayName + "(" + it.id + ")" }}")
+ val bestNode = pickBestNodeId(capabilityInfo.nodes)
+ transcriptionNodeId = bestNode?.id
+ wearPlugin.connectedDevice = bestNode?.displayName ?: "---"
+ rxBus.send(EventWearUpdateGui())
+ aapsLogger.debug(LTag.WEAR, "Selected node: ${bestNode?.displayName} $transcriptionNodeId")
+ rxBus.send(EventMobileToWear(EventData.ActionPing(System.currentTimeMillis())))
+ rxBus.send(EventData.ActionResendData("WatchUpdaterService"))
+ }
+
+ // Find a nearby node or pick one arbitrarily
+ private fun pickBestNodeId(nodes: Set): Node? =
+ nodes.firstOrNull { it.isNearby } ?: nodes.firstOrNull()
+
+ @Suppress("unused")
+ private fun sendData(path: String, vararg params: DataMap) {
+ if (wearPlugin.isEnabled()) {
+ scope.launch {
+ try {
+ for (dm in params) {
+ val request = PutDataMapRequest.create(path).apply {
+ dataMap.putAll(dm)
+ }
+ .asPutDataRequest()
+ .setUrgent()
+
+ val result = dataClient.putDataItem(request).await()
+ aapsLogger.debug(LTag.WEAR, "sendData: ${result.uri} ${params.joinToString()}")
+ }
+ } catch (cancellationException: CancellationException) {
+ throw cancellationException
+ } catch (exception: Exception) {
+ aapsLogger.error(LTag.WEAR, "DataItem failed: $exception")
+ }
+ }
+ }
+ }
+
+ private fun sendMessage(path: String, data: String?) {
+ aapsLogger.debug(LTag.WEAR, "sendMessage: $path $data")
+ transcriptionNodeId?.also { nodeId ->
+ messageClient
+ .sendMessage(nodeId, path, data?.toByteArray() ?: byteArrayOf()).apply {
+ addOnSuccessListener { }
+ addOnFailureListener {
+ aapsLogger.debug(LTag.WEAR, "sendMessage: $path failure")
+ }
+ }
+ }
+ }
+
+ @Suppress("unused")
+ private fun sendMessage(path: String, data: ByteArray) {
+ aapsLogger.debug(LTag.WEAR, "sendMessage: $path")
+ transcriptionNodeId?.also { nodeId ->
+ messageClient
+ .sendMessage(nodeId, path, data).apply {
+ addOnSuccessListener { }
+ addOnFailureListener {
+ aapsLogger.debug(LTag.WEAR, "sendMessage: $path failure")
+ }
+ }
+ }
+ }
+
+ companion object {
+
+ const val WEAR_CAPABILITY = "androidaps_wear"
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.kt
deleted file mode 100644
index c3a37d4f61..0000000000
--- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.kt
+++ /dev/null
@@ -1,633 +0,0 @@
-package info.nightscout.androidaps.plugins.general.wear.wearintegration
-
-import android.content.Intent
-import android.os.Handler
-import android.os.HandlerThread
-import com.google.android.gms.tasks.Tasks
-import com.google.android.gms.wearable.*
-import dagger.android.AndroidInjection
-import info.nightscout.androidaps.Constants
-import info.nightscout.androidaps.R
-import info.nightscout.androidaps.database.AppRepository
-import info.nightscout.androidaps.database.entities.Bolus
-import info.nightscout.androidaps.database.entities.GlucoseValue
-import info.nightscout.androidaps.database.entities.TemporaryBasal
-import info.nightscout.androidaps.extensions.convertedToAbsolute
-import info.nightscout.androidaps.extensions.toStringShort
-import info.nightscout.androidaps.extensions.valueToUnitsString
-import info.nightscout.androidaps.interfaces.*
-import info.nightscout.androidaps.interfaces.Profile.Companion.fromMgdlToUnits
-import info.nightscout.androidaps.plugins.bus.RxBus
-import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus
-import info.nightscout.androidaps.plugins.general.overview.graphExtensions.GlucoseValueDataPoint
-import info.nightscout.androidaps.plugins.general.wear.WearPlugin
-import info.nightscout.androidaps.plugins.general.wear.events.EventWearConfirmAction
-import info.nightscout.androidaps.plugins.general.wear.events.EventWearInitiateAction
-import info.nightscout.androidaps.plugins.general.wear.events.EventWearUpdateGui
-import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus
-import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider
-import info.nightscout.androidaps.receivers.ReceiverStatusStore
-import info.nightscout.androidaps.utils.DecimalFormatter.to0Decimal
-import info.nightscout.androidaps.utils.DecimalFormatter.to1Decimal
-import info.nightscout.androidaps.utils.DecimalFormatter.to2Decimal
-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.androidaps.utils.wizard.QuickWizardEntry
-import info.nightscout.shared.logging.AAPSLogger
-import info.nightscout.shared.logging.LTag
-import info.nightscout.shared.sharedPreferences.SP
-import info.nightscout.shared.weardata.WearConstants
-import io.reactivex.rxjava3.disposables.CompositeDisposable
-import kotlinx.coroutines.*
-import kotlinx.coroutines.tasks.await
-import java.util.function.Consumer
-import java.util.stream.Collectors
-import javax.inject.Inject
-import kotlin.math.abs
-
-class WatchUpdaterService : WearableListenerService() {
-
- @Inject lateinit var aapsLogger: AAPSLogger
- @Inject lateinit var profileFunction: ProfileFunction
- @Inject lateinit var iobCobCalculator: IobCobCalculator
- @Inject lateinit var rh: ResourceHelper
- @Inject lateinit var loop: Loop
- @Inject lateinit var wearPlugin: WearPlugin
- @Inject lateinit var sp: SP
- @Inject lateinit var quickWizard: QuickWizard
- @Inject lateinit var config: Config
- @Inject lateinit var nsDeviceStatus: NSDeviceStatus
- @Inject lateinit var receiverStatusStore: ReceiverStatusStore
- @Inject lateinit var repository: AppRepository
- @Inject lateinit var defaultValueHelper: DefaultValueHelper
- @Inject lateinit var glucoseStatusProvider: GlucoseStatusProvider
- @Inject lateinit var trendCalculator: TrendCalculator
- @Inject lateinit var activePlugin: ActivePlugin
- @Inject lateinit var rxBus: RxBus
- @Inject lateinit var wearConstants: WearConstants
-
- private val dataClient by lazy { Wearable.getDataClient(this) }
- private val messageClient by lazy { Wearable.getMessageClient(this) }
- private val capabilityClient by lazy { Wearable.getCapabilityClient(this) }
- //private val nodeClient by lazy { Wearable.getNodeClient(this) }
-
- private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
- private var handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper)
-
- private val disposable = CompositeDisposable()
-
- override fun onCreate() {
- AndroidInjection.inject(this)
- super.onCreate()
- aapsLogger.debug(LTag.WEAR, "onCreate")
- handler.post { updateTranscriptionCapability() }
- }
-
- override fun onCapabilityChanged(p0: CapabilityInfo) {
- super.onCapabilityChanged(p0)
- handler.post { updateTranscriptionCapability() }
- aapsLogger.debug(LTag.WEAR, "onCapabilityChanged: ${p0.name} ${p0.nodes.joinToString(", ") { it.displayName + "(" + it.id + ")" }}")
- }
-
- override fun onDestroy() {
- super.onDestroy()
- disposable.clear()
- scope.cancel()
- }
-
- override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
- aapsLogger.debug(LTag.WEAR, "onStartCommand ${intent?.action}")
- if (wearPlugin.isEnabled()) {
- handler.post {
- when (intent?.action) {
- ACTION_RESEND -> resendData()
- ACTION_OPEN_SETTINGS -> sendMessage(wearConstants.M_W_OPEN_SETTINGS, byteArrayOf())
- ACTION_SEND_STATUS -> sendStatus()
- ACTION_SEND_BASALS -> sendBasals()
- ACTION_SEND_BOLUS_PROGRESS -> sendBolusProgress(
- intent.getIntExtra("progresspercent", 0),
- intent.getStringExtra("progressstatus")
- )
- ACTION_SEND_ACTION_CONFIRMATION_REQUEST -> sendActionConfirmationRequest(
- intent.getStringExtra("title"),
- intent.getStringExtra("message"),
- intent.getStringExtra("actionstring")
- )
- ACTION_SEND_CHANGE_CONFIRMATION_REQUEST -> sendChangeConfirmationRequest(
- intent.getStringExtra("title"),
- intent.getStringExtra("message"),
- intent.getStringExtra("actionstring")
- )
- ACTION_CANCEL_NOTIFICATION -> sendCancelNotificationRequest(intent.getStringExtra("actionstring"))
-
- null -> {}
-
- else -> sendData()
- }
- }
- }
- return START_STICKY
- }
-
- @Suppress("ControlFlowWithEmptyBody", "UNUSED_EXPRESSION")
- override fun onDataChanged(dataEvents: DataEventBuffer) {
- //aapsLogger.debug(LTag.WEAR, "onDataChanged")
-
- if (wearPlugin.isEnabled()) {
- dataEvents.forEach { event ->
- if (event.type == DataEvent.TYPE_CHANGED) {
- val path = event.dataItem.uri.path
-
- aapsLogger.debug(LTag.WEAR, "onDataChanged: Path: $path, EventDataItem=${event.dataItem}")
- try {
- when (path) {
- }
- } catch (exception: Exception) {
- aapsLogger.error(LTag.WEAR, "Message failed", exception)
- }
- }
- }
- }
- super.onDataChanged(dataEvents)
- }
-
- override fun onMessageReceived(messageEvent: MessageEvent) {
- super.onMessageReceived(messageEvent)
- aapsLogger.debug(LTag.WEAR, "onMessageReceived: $messageEvent")
-
- if (wearPlugin.isEnabled()) {
- when (messageEvent.path) {
- wearConstants.W_M_RESEND_DATA -> resendData()
- wearConstants.W_M_CANCEL_BOLUS -> activePlugin.activePump.stopBolusDelivering()
-
- wearConstants.W_M_INITIATE_ACTION ->
- String(messageEvent.data).also { actionstring ->
- aapsLogger.debug(LTag.WEAR, "Initiate action: $actionstring")
- rxBus.send(EventWearInitiateAction(actionstring))
- }
-
- wearConstants.W_M_CONFIRM_ACTION ->
- String(messageEvent.data).also { actionstring ->
- aapsLogger.debug(LTag.WEAR, "Wear confirm action: $actionstring")
- rxBus.send(EventWearConfirmAction(actionstring))
- }
-
- wearConstants.W_M_PONG -> aapsLogger.debug(LTag.WEAR, "Pong response from ${messageEvent.sourceNodeId}")
- }
- }
- }
-
- private var transcriptionNodeId: String? = null
-
- private fun updateTranscriptionCapability() {
- val capabilityInfo: CapabilityInfo = Tasks.await(
- capabilityClient.getCapability(WEAR_CAPABILITY, CapabilityClient.FILTER_REACHABLE)
- )
- aapsLogger.debug(LTag.WEAR, "Nodes: ${capabilityInfo.nodes.joinToString(", ") { it.displayName + "(" + it.id + ")" }}")
- val bestNode = pickBestNodeId(capabilityInfo.nodes)
- transcriptionNodeId = bestNode?.id
- wearPlugin.connectedDevice = bestNode?.displayName ?: "---"
- rxBus.send(EventWearUpdateGui())
- aapsLogger.debug(LTag.WEAR, "Selected node: ${bestNode?.displayName} $transcriptionNodeId")
- sendMessage(wearConstants.M_W_PING, byteArrayOf())
- }
-
- // Find a nearby node or pick one arbitrarily
- private fun pickBestNodeId(nodes: Set): Node? =
- nodes.firstOrNull { it.isNearby } ?: nodes.firstOrNull()
-
- private fun sendData(path: String, vararg params: DataMap) {
- if (wearPlugin.isEnabled()) {
- scope.launch {
- try {
- for (dm in params) {
- val request = PutDataMapRequest.create(path).apply {
- dataMap.putAll(dm)
- }
- .asPutDataRequest()
- .setUrgent()
-
- val result = dataClient.putDataItem(request).await()
- aapsLogger.debug(LTag.WEAR, "sendData: ${result.uri} ${params.joinToString()}")
- }
- } catch (cancellationException: CancellationException) {
- throw cancellationException
- } catch (exception: Exception) {
- aapsLogger.error(LTag.WEAR, "DataItem failed: $exception")
- }
- }
- }
- }
-
- private fun sendMessage(path: String, data: ByteArray) {
- if (wearPlugin.isEnabled()) {
- aapsLogger.debug(LTag.WEAR, "sendMessage: $path")
- transcriptionNodeId?.also { nodeId ->
- messageClient
- .sendMessage(nodeId, path, data).apply {
- addOnSuccessListener { }
- addOnFailureListener {
- aapsLogger.debug(LTag.WEAR, "sendMessage: $path failure")
- }
- }
- }
- }
- }
-
- private fun sendData() {
- val lastBG = iobCobCalculator.ads.lastBg() ?: return
- val glucoseStatus = glucoseStatusProvider.glucoseStatusData
- aapsLogger.debug(LTag.WEAR, "Sending bg data to wear")
- sendData(
- wearConstants.M_W_DATA,
- dataMapSingleBG(lastBG, glucoseStatus)
- )
- }
-
- private fun resendData() {
- sendPreferences()
- sendQuickWizard()
- val startTime = System.currentTimeMillis() - (60000 * 60 * 5.5).toLong()
- val lastBg = iobCobCalculator.ads.lastBg() ?: return
- val graphBgs = repository.compatGetBgReadingsDataFromTime(startTime, true).blockingGet()
- val glucoseStatus = glucoseStatusProvider.getGlucoseStatusData(true)
- if (graphBgs.isNotEmpty()) {
- val entries = dataMapSingleBG(lastBg, glucoseStatus)
- val dataMaps = ArrayList(graphBgs.size)
- for (bg in graphBgs) {
- val dataMap: DataMap = dataMapSingleBG(bg, glucoseStatus)
- dataMaps.add(dataMap)
- }
- entries.putDataMapArrayList("entries", dataMaps)
- aapsLogger.debug(LTag.WEAR, "Sending graph bg data to wear")
- sendData(
- wearConstants.M_W_DATA,
- entries
- )
- }
- sendBasals()
- sendStatus()
- }
-
- private fun sendBasals() {
- val now = System.currentTimeMillis()
- val startTimeWindow = now - (60000 * 60 * 5.5).toLong()
- val basals = java.util.ArrayList()
- val temps = java.util.ArrayList()
- val boluses = java.util.ArrayList()
- val predictions = java.util.ArrayList()
- val profile = profileFunction.getProfile() ?: return
- var beginBasalSegmentTime = startTimeWindow
- var runningTime = startTimeWindow
- var beginBasalValue = profile.getBasal(beginBasalSegmentTime)
- var endBasalValue = beginBasalValue
- var tb1 = iobCobCalculator.getTempBasalIncludingConvertedExtended(runningTime)
- var tb2: TemporaryBasal?
- var tbBefore = beginBasalValue
- var tbAmount = beginBasalValue
- var tbStart = runningTime
- if (tb1 != null) {
- val profileTB = profileFunction.getProfile(runningTime)
- if (profileTB != null) {
- tbAmount = tb1.convertedToAbsolute(runningTime, profileTB)
- tbStart = runningTime
- }
- }
- while (runningTime < now) {
- val profileTB = profileFunction.getProfile(runningTime) ?: return
- //basal rate
- endBasalValue = profile.getBasal(runningTime)
- if (endBasalValue != beginBasalValue) {
- //push the segment we recently left
- basals.add(basalMap(beginBasalSegmentTime, runningTime, beginBasalValue))
-
- //begin new Basal segment
- beginBasalSegmentTime = runningTime
- beginBasalValue = endBasalValue
- }
-
- //temps
- tb2 = iobCobCalculator.getTempBasalIncludingConvertedExtended(runningTime)
- if (tb1 == null && tb2 == null) {
- //no temp stays no temp
- } else if (tb1 != null && tb2 == null) {
- //temp is over -> push it
- temps.add(tempMap(tbStart, tbBefore, runningTime, endBasalValue, tbAmount))
- tb1 = null
- } else if (tb1 == null && tb2 != null) {
- //temp begins
- tb1 = tb2
- tbStart = runningTime
- tbBefore = endBasalValue
- tbAmount = tb1.convertedToAbsolute(runningTime, profileTB)
- } else if (tb1 != null && tb2 != null) {
- val currentAmount = tb2.convertedToAbsolute(runningTime, profileTB)
- if (currentAmount != tbAmount) {
- temps.add(tempMap(tbStart, tbBefore, runningTime, currentAmount, tbAmount))
- tbStart = runningTime
- tbBefore = tbAmount
- tbAmount = currentAmount
- tb1 = tb2
- }
- }
- runningTime += (5 * 60 * 1000).toLong()
- }
- if (beginBasalSegmentTime != runningTime) {
- //push the remaining segment
- basals.add(basalMap(beginBasalSegmentTime, runningTime, beginBasalValue))
- }
- if (tb1 != null) {
- tb2 = iobCobCalculator.getTempBasalIncludingConvertedExtended(now) //use "now" to express current situation
- if (tb2 == null) {
- //express the cancelled temp by painting it down one minute early
- temps.add(tempMap(tbStart, tbBefore, now - 60 * 1000, endBasalValue, tbAmount))
- } else {
- //express currently running temp by painting it a bit into the future
- val profileNow = profileFunction.getProfile(now)
- val currentAmount = tb2.convertedToAbsolute(now, profileNow!!)
- if (currentAmount != tbAmount) {
- temps.add(tempMap(tbStart, tbBefore, now, tbAmount, tbAmount))
- temps.add(tempMap(now, tbAmount, runningTime + 5 * 60 * 1000, currentAmount, currentAmount))
- } else {
- temps.add(tempMap(tbStart, tbBefore, runningTime + 5 * 60 * 1000, tbAmount, tbAmount))
- }
- }
- } else {
- tb2 = iobCobCalculator.getTempBasalIncludingConvertedExtended(now) //use "now" to express current situation
- if (tb2 != null) {
- //onset at the end
- val profileTB = profileFunction.getProfile(runningTime)
- val currentAmount = tb2.convertedToAbsolute(runningTime, profileTB!!)
- temps.add(tempMap(now - 60 * 1000, endBasalValue, runningTime + 5 * 60 * 1000, currentAmount, currentAmount))
- }
- }
- repository.getBolusesIncludingInvalidFromTime(startTimeWindow, true).blockingGet()
- .stream()
- .filter { (_, _, _, _, _, _, _, _, _, type) -> type !== Bolus.Type.PRIMING }
- .forEach { (_, _, _, isValid, _, _, timestamp, _, amount, type) -> boluses.add(treatmentMap(timestamp, amount, 0.0, type === Bolus.Type.SMB, isValid)) }
- repository.getCarbsDataFromTimeExpanded(startTimeWindow, true).blockingGet()
- .forEach(Consumer { (_, _, _, isValid, _, _, timestamp, _, _, amount) -> boluses.add(treatmentMap(timestamp, 0.0, amount, false, isValid)) })
- val finalLastRun = loop.lastRun
- if (sp.getBoolean("wear_predictions", true) && finalLastRun?.request?.hasPredictions == true && finalLastRun.constraintsProcessed != null) {
- val predArray = finalLastRun.constraintsProcessed!!.predictions
- .stream().map { bg: GlucoseValue -> GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, rh) }
- .collect(Collectors.toList())
- if (predArray.isNotEmpty())
- for (bg in predArray) if (bg.data.value > 39) predictions.add(predictionMap(bg.data.timestamp, bg.data.value, bg.color(null)))
- }
- aapsLogger.debug(LTag.WEAR, "Sending basal data to wear")
- sendData(
- wearConstants.M_W_BASAL,
- DataMap().apply {
- putDataMapArrayList("basals", basals)
- putDataMapArrayList("temps", temps)
- putDataMapArrayList("boluses", boluses)
- putDataMapArrayList("predictions", predictions)
- })
- }
-
- private fun deltaString(deltaMGDL: Double, deltaMMOL: Double, units: GlucoseUnit): String {
- val detailed = sp.getBoolean(R.string.key_wear_detailed_delta, false)
- var deltaString = if (deltaMGDL >= 0) "+" else "-"
- deltaString += if (units == GlucoseUnit.MGDL) {
- if (detailed) to1Decimal(abs(deltaMGDL)) else to0Decimal(abs(deltaMGDL))
- } else {
- if (detailed) to2Decimal(abs(deltaMMOL)) else to1Decimal(abs(deltaMMOL))
- }
- return deltaString
- }
-
- private fun dataMapSingleBG(lastBG: GlucoseValue, glucoseStatus: GlucoseStatus?): DataMap {
- val units = profileFunction.getUnits()
- val lowLine = Profile.toMgdl(defaultValueHelper.determineLowLine(), units)
- val highLine = Profile.toMgdl(defaultValueHelper.determineHighLine(), units)
- val sgvLevel = if (lastBG.value > highLine) 1L else if (lastBG.value < lowLine) -1L else 0L
- val dataMap = DataMap()
- dataMap.putString("sgvString", lastBG.valueToUnitsString(units))
- dataMap.putString("glucoseUnits", units.asText)
- dataMap.putLong("timestamp", lastBG.timestamp)
- if (glucoseStatus == null) {
- dataMap.putString("slopeArrow", "")
- dataMap.putString("delta", "--")
- dataMap.putString("avgDelta", "--")
- } else {
- dataMap.putString("slopeArrow", trendCalculator.getTrendArrow(lastBG).symbol)
- dataMap.putString("delta", deltaString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units))
- dataMap.putString("avgDelta", deltaString(glucoseStatus.shortAvgDelta, glucoseStatus.shortAvgDelta * Constants.MGDL_TO_MMOLL, units))
- }
- dataMap.putLong("sgvLevel", sgvLevel)
- dataMap.putDouble("sgvDouble", lastBG.value)
- dataMap.putDouble("high", highLine)
- dataMap.putDouble("low", lowLine)
- return dataMap
- }
-
- private fun tempMap(startTime: Long, startBasal: Double, to: Long, toBasal: Double, amount: Double) =
- DataMap().apply {
- putLong("starttime", startTime)
- putDouble("startBasal", startBasal)
- putLong("endtime", to)
- putDouble("endbasal", toBasal)
- putDouble("amount", amount)
- }
-
- private fun basalMap(startTime: Long, endTime: Long, amount: Double) =
- DataMap().apply {
- putLong("starttime", startTime)
- putLong("endtime", endTime)
- putDouble("amount", amount)
- }
-
- private fun treatmentMap(date: Long, bolus: Double, carbs: Double, isSMB: Boolean, isValid: Boolean) =
- DataMap().apply {
- putLong("date", date)
- putDouble("bolus", bolus)
- putDouble("carbs", carbs)
- putBoolean("isSMB", isSMB)
- putBoolean("isValid", isValid)
- }
-
- private fun predictionMap(timestamp: Long, sgv: Double, color: Int) =
- DataMap().apply {
- putLong("timestamp", timestamp)
- putDouble("sgv", sgv)
- putInt("color", color)
- }
-
- private fun quickMap(q: QuickWizardEntry) =
- DataMap().apply {
- putString("guid", q.guid())
- putString("button_text", q.buttonText())
- putInt("carbs", q.carbs())
- putInt("from", q.validFrom())
- putInt("to", q.validTo())
- }
-
- private fun sendBolusProgress(progressPercent: Int?, status: String?) {
- progressPercent ?: return
- aapsLogger.debug(LTag.WEAR, "Sending bolus progress: $progressPercent $status")
- sendData(
- wearConstants.M_W_BOLUS_PROGRESS,
- DataMap().apply {
- putLong("timestamp", System.currentTimeMillis())
- putString("bolusProgress", "bolusProgress")
- putString("progressstatus", status ?: "")
- putInt("progresspercent", progressPercent)
- })
- }
-
- private fun sendActionConfirmationRequest(title: String?, message: String?, actionstring: String?) {
- title ?: message ?: actionstring ?: return
- aapsLogger.debug(LTag.WEAR, "Requesting confirmation from wear: $actionstring")
- sendData(
- wearConstants.M_W_ACTION_CONFIRMATION_REQUEST,
- DataMap().apply {
- putLong("timestamp", System.currentTimeMillis())
- putString("actionConfirmationRequest", "actionConfirmationRequest")
- putString("title", title)
- putString("message", message)
- putString("actionstring", actionstring)
- })
- }
-
- private fun sendChangeConfirmationRequest(title: String?, message: String?, actionstring: String?) {
- title ?: message ?: actionstring ?: return
- aapsLogger.debug(LTag.WEAR, "Requesting confirmation from wear: $actionstring")
- sendData(
- wearConstants.M_W_ACTION_CHANGE_CONFIRMATION_REQUEST,
- DataMap().apply {
- putLong("timestamp", System.currentTimeMillis())
- putString("changeConfirmationRequest", "changeConfirmationRequest")
- putString("title", title)
- putString("message", message)
- putString("actionstring", actionstring)
- })
- }
-
- private fun sendCancelNotificationRequest(actionstring: String?) {
- actionstring ?: return
- aapsLogger.debug(LTag.WEAR, "Canceling notification on wear: $actionstring")
- sendData(
- wearConstants.M_W_ACTION_CANCEL_NOTIFICATION_REQUEST,
- DataMap().apply {
- putLong("timestamp", System.currentTimeMillis())
- putString("cancelNotificationRequest", "cancelNotificationRequest")
- putString("actionstring", actionstring)
- })
- }
-
- private fun sendStatus() {
- aapsLogger.debug(LTag.WEAR, "Updating status on wear")
- val profile = profileFunction.getProfile()
- var status = rh.gs(R.string.noprofile)
- var iobSum = ""
- var iobDetail = ""
- var cobString = ""
- var currentBasal = ""
- var bgiString = ""
- if (profile != null) {
- val bolusIob = iobCobCalculator.calculateIobFromBolus().round()
- val basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round()
- iobSum = to2Decimal(bolusIob.iob + basalIob.basaliob)
- iobDetail = "(" + to2Decimal(bolusIob.iob) + "|" + to2Decimal(basalIob.basaliob) + ")"
- cobString = iobCobCalculator.getCobInfo(false, "WatcherUpdaterService").generateCOBString()
- currentBasal = generateBasalString()
-
- //bgi
- val bgi = -(bolusIob.activity + basalIob.activity) * 5 * fromMgdlToUnits(profile.getIsfMgdl(), profileFunction.getUnits())
- bgiString = "" + (if (bgi >= 0) "+" else "") + to1Decimal(bgi)
- status = generateStatusString(profile, currentBasal, iobSum, iobDetail, bgiString)
- }
-
- //batteries
- val phoneBattery = receiverStatusStore.batteryLevel
- val rigBattery = nsDeviceStatus.uploaderStatus.trim { it <= ' ' }
- //OpenAPS status
- val openApsStatus =
- if (config.APS) loop.lastRun?.let { if (it.lastTBREnact != 0L) it.lastTBREnact else -1 } ?: -1
- else nsDeviceStatus.openApsTimestamp
-
- sendData(
- wearConstants.M_W_STATUS,
- DataMap().apply {
- //unique content
- putString("externalStatusString", status)
- putString("iobSum", iobSum)
- putString("iobDetail", iobDetail)
- putBoolean("detailedIob", sp.getBoolean(R.string.key_wear_detailediob, false))
- putString("cob", cobString)
- putString("currentBasal", currentBasal)
- putString("battery", "" + phoneBattery)
- putString("rigBattery", rigBattery)
- putLong("openApsStatus", openApsStatus)
- putString("bgi", bgiString)
- putBoolean("showBgi", sp.getBoolean(R.string.key_wear_showbgi, false))
- putInt("batteryLevel", if (phoneBattery >= 30) 1 else 0)
- })
- }
-
- private fun sendPreferences() {
- sendData(
- wearConstants.M_W_PREFERENCES,
- DataMap().apply {
- putLong("timestamp", System.currentTimeMillis())
- putBoolean(rh.gs(R.string.key_wear_control), sp.getBoolean(R.string.key_wear_control, false))
- putBoolean(rh.gs(R.string.key_units_mgdl), profileFunction.getUnits() == GlucoseUnit.MGDL)
- putInt(rh.gs(R.string.key_boluswizard_percentage), sp.getInt(R.string.key_boluswizard_percentage, 100))
- putInt(rh.gs(R.string.key_treatmentssafety_maxcarbs), sp.getInt(R.string.key_treatmentssafety_maxcarbs, 48))
- putDouble(rh.gs(R.string.key_treatmentssafety_maxbolus), sp.getDouble(R.string.key_treatmentssafety_maxbolus, 3.0))
- })
- }
-
- private fun sendQuickWizard() {
- val entities = ArrayList()
- for (i in 0 until quickWizard.size()) {
- val q = quickWizard[i]
- if (q.forDevice(QuickWizardEntry.DEVICE_WATCH)) entities.add(quickMap(q))
- }
- sendData(
- wearConstants.M_W_QUICK_WIZARD,
- DataMap().apply {
- putLong("timestamp", System.currentTimeMillis())
- putDataMapArrayList("quick_wizard", entities)
- })
- }
-
- private fun generateStatusString(profile: Profile?, currentBasal: String, iobSum: String, iobDetail: String, bgiString: String): String {
- var status = ""
- if (profile == null) return rh.gs(R.string.noprofile)
- if (!(loop as PluginBase).isEnabled()) status += rh.gs(R.string.disabledloop) + "\n"
-
- val iobString =
- if (sp.getBoolean(R.string.key_wear_detailediob, false)) "$iobSum $iobDetail"
- else iobSum + "U"
-
- status += "$currentBasal $iobString"
-
- //add BGI if shown, otherwise return
- if (sp.getBoolean(R.string.key_wear_showbgi, false)) status += " $bgiString"
- return status
- }
-
- private fun generateBasalString(): String {
- val profile: Profile = profileFunction.getProfile() ?: return ""
- return iobCobCalculator.getTempBasalIncludingConvertedExtended(System.currentTimeMillis())?.toStringShort() ?: to2Decimal(profile.getBasal()) + "U/h"
- }
-
- companion object {
-
- const val WEAR_CAPABILITY = "androidaps_wear"
-
- val ACTION_RESEND = WatchUpdaterService::class.java.name + ".Resend"
- val ACTION_OPEN_SETTINGS = WatchUpdaterService::class.java.name + ".OpenSettings"
- val ACTION_SEND_STATUS = WatchUpdaterService::class.java.name + ".SendStatus"
- val ACTION_SEND_BASALS = WatchUpdaterService::class.java.name + ".SendBasals"
- val ACTION_SEND_BOLUS_PROGRESS = WatchUpdaterService::class.java.name + ".BolusProgress"
- val ACTION_SEND_ACTION_CONFIRMATION_REQUEST = WatchUpdaterService::class.java.name + ".ActionConfirmationRequest"
- val ACTION_SEND_CHANGE_CONFIRMATION_REQUEST = WatchUpdaterService::class.java.name + ".ChangeConfirmationRequest"
- val ACTION_CANCEL_NOTIFICATION = WatchUpdaterService::class.java.name + ".CancelNotification"
-
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService1.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService1.java
deleted file mode 100644
index 95a2419d4a..0000000000
--- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService1.java
+++ /dev/null
@@ -1,778 +0,0 @@
-package info.nightscout.androidaps.plugins.general.wear.wearintegration;
-
-/*
-public class WatchUpdaterService1 extends WearableListenerService implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
- @Inject public GlucoseStatusProvider glucoseStatusProvider;
- @Inject public AAPSLogger aapsLogger;
- @Inject public WearPlugin wearPlugin;
- @Inject public ResourceHelper rh;
- @Inject public SP sp;
- @Inject public RxBus rxBus;
- @Inject public ProfileFunction profileFunction;
- @Inject public DefaultValueHelper defaultValueHelper;
- @Inject public NSDeviceStatus nsDeviceStatus;
- @Inject public ActivePlugin activePlugin;
- @Inject public Loop loop;
- @Inject public IobCobCalculator iobCobCalculator;
- @Inject public AppRepository repository;
- @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");
- public static final String ACTION_SEND_STATUS = WatchUpdaterService.class.getName().concat(".SendStatus");
- public static final String ACTION_SEND_BASALS = WatchUpdaterService.class.getName().concat(".SendBasals");
- public static final String ACTION_SEND_BOLUSPROGRESS = WatchUpdaterService.class.getName().concat(".BolusProgress");
- public static final String ACTION_SEND_ACTIONCONFIRMATIONREQUEST = WatchUpdaterService.class.getName().concat(".ActionConfirmationRequest");
- public static final String ACTION_SEND_CHANGECONFIRMATIONREQUEST = WatchUpdaterService.class.getName().concat(".ChangeConfirmationRequest");
- public static final String ACTION_CANCEL_NOTIFICATION = WatchUpdaterService.class.getName().concat(".CancelNotification");
-
- private GoogleApiClient googleApiClient;
-
- String TAG = "WatchUpdateService";
-
- private static boolean lastLoopStatus;
-
- private Handler handler;
-
- // Phone
- private static final String CAPABILITY_PHONE_APP = "phone_app_sync_bgs";
- private static final String MESSAGE_PATH_PHONE = "/phone_message_path";
- // Wear
- private static final String CAPABILITY_WEAR_APP = "wear_app_sync_bgs";
- private static final String MESSAGE_PATH_WEAR = "/wear_message_path";
-
-
- @Override
- public void onCreate() {
- AndroidInjection.inject(this);
- if (wearIntegration()) {
- googleApiConnect();
- }
- if (handler == null) {
- HandlerThread handlerThread = new HandlerThread(this.getClass().getSimpleName() + "Handler");
- handlerThread.start();
- handler = new Handler(handlerThread.getLooper());
- }
- }
-
- private boolean wearIntegration() {
- return wearPlugin.isEnabled();
- }
-
- private void googleApiConnect() {
- if (googleApiClient != null && (googleApiClient.isConnected() || googleApiClient.isConnecting())) {
- googleApiClient.disconnect();
- }
- googleApiClient = new GoogleApiClient.Builder(this).addConnectionCallbacks(this)
- .addOnConnectionFailedListener(this).addApi(Wearable.API).build();
- Wearable.MessageApi.addListener(googleApiClient, this);
- if (googleApiClient.isConnected()) {
- aapsLogger.debug(LTag.WEAR, "API client is connected");
- } else {
- // Log.d("WatchUpdater", logPrefix + "API client is not connected and is trying to connect");
- googleApiClient.connect();
- }
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- String action = intent != null ? intent.getAction() : null;
-
- // Log.d(TAG, "onStartCommand: " + action);
-
- if (wearIntegration()) {
- handler.post(() -> {
- if (googleApiClient != null && googleApiClient.isConnected()) {
- if (ACTION_RESEND.equals(action)) {
- resendData();
- } else if (ACTION_OPEN_SETTINGS.equals(action)) {
- sendNotification();
- } else if (ACTION_SEND_STATUS.equals(action)) {
- sendStatus();
- } else if (ACTION_SEND_BASALS.equals(action)) {
- sendBasals();
- } else if (ACTION_SEND_BOLUSPROGRESS.equals(action)) {
- sendBolusProgress(intent.getIntExtra("progresspercent", 0), intent.hasExtra("progressstatus") ? intent.getStringExtra("progressstatus") : "");
- } else if (ACTION_SEND_ACTIONCONFIRMATIONREQUEST.equals(action)) {
- String title = intent.getStringExtra("title");
- String message = intent.getStringExtra("message");
- String actionstring = intent.getStringExtra("actionstring");
- sendActionConfirmationRequest(title, message, actionstring);
- } else if (ACTION_SEND_CHANGECONFIRMATIONREQUEST.equals(action)) {
- String title = intent.getStringExtra("title");
- String message = intent.getStringExtra("message");
- String actionstring = intent.getStringExtra("actionstring");
- sendChangeConfirmationRequest(title, message, actionstring);
- } else if (ACTION_CANCEL_NOTIFICATION.equals(action)) {
- String actionstring = intent.getStringExtra("actionstring");
- sendCancelNotificationRequest(actionstring);
- } else {
- sendData();
- }
- } else {
- if (googleApiClient != null) googleApiClient.connect();
- }
- });
- }
-
- return START_STICKY;
- }
-
-
- private void updateWearSyncBgsCapability(CapabilityInfo capabilityInfo) {
- Log.d("WatchUpdaterService", "CabilityInfo: " + capabilityInfo);
- Set connectedNodes = capabilityInfo.getNodes();
- String mWearNodeId = pickBestNodeId(connectedNodes);
- }
-
-
- private String pickBestNodeId(Set nodes) {
- String bestNodeId = null;
- // Find a nearby node or pick one arbitrarily
- for (Node node : nodes) {
- if (node.isNearby()) {
- return node.getId();
- }
- bestNodeId = node.getId();
- }
- return bestNodeId;
- }
-
-
- @Override
- public void onConnected(Bundle connectionHint) {
- CapabilityApi.CapabilityListener capabilityListener = capabilityInfo -> {
- updateWearSyncBgsCapability(capabilityInfo);
- // Log.d(TAG, logPrefix + "onConnected onCapabilityChanged mWearNodeID:" + mWearNodeId);
- // new CheckWearableConnected().execute();
- };
-
- Wearable.CapabilityApi.addCapabilityListener(googleApiClient, capabilityListener, CAPABILITY_WEAR_APP);
- sendData();
- }
-
-
- @Override
- public void onPeerConnected(com.google.android.gms.wearable.Node peer) {// KS
- super.onPeerConnected(peer);
- String id = peer.getId();
- String name = peer.getDisplayName();
- Log.d(TAG, "onPeerConnected peer name & ID: " + name + "|" + id);
- }
-
-
- @Override
- public void onPeerDisconnected(com.google.android.gms.wearable.Node peer) {// KS
- super.onPeerDisconnected(peer);
- String id = peer.getId();
- String name = peer.getDisplayName();
- Log.d(TAG, "onPeerDisconnected peer name & ID: " + name + "|" + id);
- }
-
-
- @Override
- public void onMessageReceived(MessageEvent event) {
-
- // Log.d(TAG, "onMessageRecieved: " + event);
-
- if (wearIntegration()) {
- if (event != null && event.getPath().equals(WearUris.WEARABLE_RESEND_PATH)) {
- resendData();
- }
-
- if (event != null && event.getPath().equals(WearUris.WEARABLE_CANCELBOLUS_PATH)) {
- cancelBolus();
- }
-
- if (event != null && event.getPath().equals(WearUris.WEARABLE_INITIATE_ACTIONSTRING_PATH)) {
- String actionstring = new String(event.getData());
- aapsLogger.debug(LTag.WEAR, "Wear: " + actionstring);
- rxBus.send(new EventWearInitiateAction(actionstring));
- }
-
- if (event != null && event.getPath().equals(WearUris.WEARABLE_CONFIRM_ACTIONSTRING_PATH)) {
- String actionstring = new String(event.getData());
- aapsLogger.debug(LTag.WEAR, "Wear Confirm: " + actionstring);
- rxBus.send(new EventWearConfirmAction(actionstring));
- }
- }
- }
-
- private void cancelBolus() {
- activePlugin.getActivePump().stopBolusDelivering();
- }
-
- private void sendData() {
-
- GlucoseValue lastBG = iobCobCalculator.getAds().lastBg();
- // Log.d(TAG, "LastBg=" + lastBG);
- if (lastBG != null) {
- GlucoseStatus glucoseStatus = glucoseStatusProvider.getGlucoseStatusData();
-
- if (googleApiClient != null && !googleApiClient.isConnected() && !googleApiClient.isConnecting()) {
- googleApiConnect();
- }
- if (wearIntegration()) {
-
- final DataMap dataMap = dataMapSingleBG(lastBG, glucoseStatus);
-
- (new SendToDataLayerThread(WearUris.WEARABLE_DATA_PATH, googleApiClient)).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, dataMap);
- }
- }
- }
-
-
- private DataMap dataMapSingleBG(GlucoseValue lastBG, GlucoseStatus glucoseStatus) {
- GlucoseUnit units = profileFunction.getUnits();
- double convert2MGDL = 1.0;
- if (units.equals(GlucoseUnit.MMOL))
- convert2MGDL = Constants.MMOLL_TO_MGDL;
- double lowLine = defaultValueHelper.determineLowLine() * convert2MGDL;
- double highLine = defaultValueHelper.determineHighLine() * convert2MGDL;
-
- long sgvLevel = 0L;
- if (lastBG.getValue() > highLine) {
- sgvLevel = 1;
- } else if (lastBG.getValue() < lowLine) {
- sgvLevel = -1;
- }
-
- DataMap dataMap = new DataMap();
- dataMap.putString("sgvString", GlucoseValueExtensionKt.valueToUnitsString(lastBG, units));
- dataMap.putString("glucoseUnits", units.getAsText());
- dataMap.putLong("timestamp", lastBG.getTimestamp());
- if (glucoseStatus == null) {
- dataMap.putString("slopeArrow", "");
- dataMap.putString("delta", "--");
- dataMap.putString("avgDelta", "--");
- } else {
- dataMap.putString("slopeArrow", trendCalculator.getTrendArrow(lastBG).getSymbol());
- dataMap.putString("delta", deltastring(glucoseStatus.getDelta(), glucoseStatus.getDelta() * Constants.MGDL_TO_MMOLL, units));
- dataMap.putString("avgDelta", deltastring(glucoseStatus.getShortAvgDelta(), glucoseStatus.getShortAvgDelta() * Constants.MGDL_TO_MMOLL, units));
- }
- dataMap.putLong("sgvLevel", sgvLevel);
- dataMap.putDouble("sgvDouble", lastBG.getValue());
- dataMap.putDouble("high", highLine);
- dataMap.putDouble("low", lowLine);
- return dataMap;
- }
-
- private String deltastring(double deltaMGDL, double deltaMMOL, GlucoseUnit units) {
- String deltastring = "";
- if (deltaMGDL >= 0) {
- deltastring += "+";
- } else {
- deltastring += "-";
- }
-
- boolean detailed = sp.getBoolean(R.string.key_wear_detailed_delta, false);
- if (units.equals(GlucoseUnit.MGDL)) {
- if (detailed) {
- deltastring += DecimalFormatter.INSTANCE.to1Decimal(Math.abs(deltaMGDL));
- } else {
- deltastring += DecimalFormatter.INSTANCE.to0Decimal(Math.abs(deltaMGDL));
- }
- } else {
- if (detailed) {
- deltastring += DecimalFormatter.INSTANCE.to2Decimal(Math.abs(deltaMMOL));
- } else {
- deltastring += DecimalFormatter.INSTANCE.to1Decimal(Math.abs(deltaMMOL));
- }
- }
- return deltastring;
- }
-
- private void resendData() {
- 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();
-
- if (last_bg == null) return;
-
- List graph_bgs = repository.compatGetBgReadingsDataFromTime(startTime, true).blockingGet();
- GlucoseStatus glucoseStatus = glucoseStatusProvider.getGlucoseStatusData(true);
-
- if (!graph_bgs.isEmpty()) {
- DataMap entries = dataMapSingleBG(last_bg, glucoseStatus);
- final ArrayList dataMaps = new ArrayList<>(graph_bgs.size());
- for (GlucoseValue bg : graph_bgs) {
- DataMap dataMap = dataMapSingleBG(bg, glucoseStatus);
- dataMaps.add(dataMap);
- }
- entries.putDataMapArrayList("entries", dataMaps);
- (new SendToDataLayerThread(WearUris.WEARABLE_DATA_PATH, googleApiClient)).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, entries);
- }
- sendBasals();
- sendStatus();
- }
-
- private void sendBasals() {
- if (googleApiClient != null && !googleApiClient.isConnected() && !googleApiClient.isConnecting()) {
- googleApiConnect();
- }
-
- long now = System.currentTimeMillis();
- final long startTimeWindow = now - (long) (60000 * 60 * 5.5);
-
-
- ArrayList basals = new ArrayList<>();
- ArrayList temps = new ArrayList<>();
- ArrayList boluses = new ArrayList<>();
- ArrayList predictions = new ArrayList<>();
-
-
- Profile profile = profileFunction.getProfile();
-
- if (profile == null) {
- return;
- }
-
- long beginBasalSegmentTime = startTimeWindow;
- long runningTime = startTimeWindow;
-
- double beginBasalValue = profile.getBasal(beginBasalSegmentTime);
- double endBasalValue = beginBasalValue;
-
- TemporaryBasal tb1 = iobCobCalculator.getTempBasalIncludingConvertedExtended(runningTime);
- TemporaryBasal tb2;
- double tb_before = beginBasalValue;
- double tb_amount = beginBasalValue;
- long tb_start = runningTime;
-
- if (tb1 != null) {
- Profile profileTB = profileFunction.getProfile(runningTime);
- if (profileTB != null) {
- tb_amount = TemporaryBasalExtensionKt.convertedToAbsolute(tb1, runningTime, profileTB);
- tb_start = runningTime;
- }
- }
-
-
- for (; runningTime < now; runningTime += 5 * 60 * 1000) {
- Profile profileTB = profileFunction.getProfile(runningTime);
- if (profileTB == null)
- return;
- //basal rate
- endBasalValue = profile.getBasal(runningTime);
- if (endBasalValue != beginBasalValue) {
- //push the segment we recently left
- basals.add(basalMap(beginBasalSegmentTime, runningTime, beginBasalValue));
-
- //begin new Basal segment
- beginBasalSegmentTime = runningTime;
- beginBasalValue = endBasalValue;
- }
-
- //temps
- tb2 = iobCobCalculator.getTempBasalIncludingConvertedExtended(runningTime);
-
- if (tb1 == null && tb2 == null) {
- ; //no temp stays no temp
-
- } else if (tb1 != null && tb2 == null) {
- //temp is over -> push it
- temps.add(tempDatamap(tb_start, tb_before, runningTime, endBasalValue, tb_amount));
- tb1 = null;
-
- } else if (tb1 == null && tb2 != null) {
- //temp begins
- tb1 = tb2;
- tb_start = runningTime;
- tb_before = endBasalValue;
- tb_amount = TemporaryBasalExtensionKt.convertedToAbsolute(tb1, runningTime, profileTB);
-
- } else if (tb1 != null && tb2 != null) {
- double currentAmount = TemporaryBasalExtensionKt.convertedToAbsolute(tb2, runningTime, profileTB);
- if (currentAmount != tb_amount) {
- temps.add(tempDatamap(tb_start, tb_before, runningTime, currentAmount, tb_amount));
- tb_start = runningTime;
- tb_before = tb_amount;
- tb_amount = currentAmount;
- tb1 = tb2;
- }
- }
- }
- if (beginBasalSegmentTime != runningTime) {
- //push the remaining segment
- basals.add(basalMap(beginBasalSegmentTime, runningTime, beginBasalValue));
- }
- if (tb1 != null) {
- tb2 = iobCobCalculator.getTempBasalIncludingConvertedExtended(now); //use "now" to express current situation
- if (tb2 == null) {
- //express the cancelled temp by painting it down one minute early
- temps.add(tempDatamap(tb_start, tb_before, now - 60 * 1000, endBasalValue, tb_amount));
- } else {
- //express currently running temp by painting it a bit into the future
- Profile profileNow = profileFunction.getProfile(now);
- double currentAmount = TemporaryBasalExtensionKt.convertedToAbsolute(tb2, now, profileNow);
- if (currentAmount != tb_amount) {
- temps.add(tempDatamap(tb_start, tb_before, now, tb_amount, tb_amount));
- temps.add(tempDatamap(now, tb_amount, runningTime + 5 * 60 * 1000, currentAmount, currentAmount));
- } else {
- temps.add(tempDatamap(tb_start, tb_before, runningTime + 5 * 60 * 1000, tb_amount, tb_amount));
- }
- }
- } else {
- tb2 = iobCobCalculator.getTempBasalIncludingConvertedExtended(now); //use "now" to express current situation
- if (tb2 != null) {
- //onset at the end
- Profile profileTB = profileFunction.getProfile(runningTime);
- double currentAmount = TemporaryBasalExtensionKt.convertedToAbsolute(tb2, runningTime, profileTB);
- temps.add(tempDatamap(now - 60 * 1000, endBasalValue, runningTime + 5 * 60 * 1000, currentAmount, currentAmount));
- }
- }
-
- repository.getBolusesIncludingInvalidFromTime(startTimeWindow, true).blockingGet()
- .stream()
- .filter(bolus -> bolus.getType() != Bolus.Type.PRIMING)
- .forEach(bolus -> boluses.add(treatmentMap(bolus.getTimestamp(), bolus.getAmount(), 0, bolus.getType() == Bolus.Type.SMB, bolus.isValid())));
- repository.getCarbsDataFromTimeExpanded(startTimeWindow, true).blockingGet()
- .forEach(carb -> boluses.add(treatmentMap(carb.getTimestamp(), 0, carb.getAmount(), false, carb.isValid())));
-
- final LoopPlugin.LastRun finalLastRun = loop.getLastRun();
- if (sp.getBoolean("wear_predictions", true) && finalLastRun != null && finalLastRun.getRequest().getHasPredictions() && finalLastRun.getConstraintsProcessed() != null) {
- List predArray =
- finalLastRun.getConstraintsProcessed().getPredictions()
- .stream().map(bg -> new GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, rh))
- .collect(Collectors.toList());
-
- if (!predArray.isEmpty()) {
- for (GlucoseValueDataPoint bg : predArray) {
- if (bg.getData().getValue() < 40) continue;
- predictions.add(predictionMap(bg.getData().getTimestamp(),
- bg.getData().getValue(), bg.color(null)));
- }
- }
- }
-
-
- DataMap dm = new DataMap();
- dm.putDataMapArrayList("basals", basals);
- dm.putDataMapArrayList("temps", temps);
- dm.putDataMapArrayList("boluses", boluses);
- dm.putDataMapArrayList("predictions", predictions);
- (new SendToDataLayerThread(WearUris.BASAL_DATA_PATH, googleApiClient)).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, dm);
- }
-
- private DataMap tempDatamap(long startTime, double startBasal, long to, double toBasal, double amount) {
- DataMap dm = new DataMap();
- dm.putLong("starttime", startTime);
- dm.putDouble("startBasal", startBasal);
- dm.putLong("endtime", to);
- dm.putDouble("endbasal", toBasal);
- dm.putDouble("amount", amount);
- return dm;
- }
-
- private DataMap basalMap(long startTime, long endTime, double amount) {
- DataMap dm = new DataMap();
- dm.putLong("starttime", startTime);
- dm.putLong("endtime", endTime);
- dm.putDouble("amount", amount);
- return dm;
- }
-
- private DataMap treatmentMap(long date, double bolus, double carbs, boolean isSMB, boolean isValid) {
- DataMap dm = new DataMap();
- dm.putLong("date", date);
- dm.putDouble("bolus", bolus);
- dm.putDouble("carbs", carbs);
- dm.putBoolean("isSMB", isSMB);
- dm.putBoolean("isValid", isValid);
- return dm;
- }
-
- private DataMap predictionMap(long timestamp, double sgv, int color) {
- DataMap dm = new DataMap();
- dm.putLong("timestamp", timestamp);
- dm.putDouble("sgv", sgv);
- dm.putInt("color", color);
- return dm;
- }
-
-
- private void sendNotification() {
- if (googleApiClient != null && googleApiClient.isConnected()) {
- PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.OPEN_SETTINGS_PATH);
- //unique content
- dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis());
- dataMapRequest.getDataMap().putString("openSettings", "openSettings");
- PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest();
- Wearable.DataApi.putDataItem(googleApiClient, putDataRequest);
- } else {
- Log.e("OpenSettings", "No connection to wearable available!");
- }
- }
-
- private void sendBolusProgress(int progresspercent, String status) {
- if (googleApiClient != null && googleApiClient.isConnected()) {
- PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.BOLUS_PROGRESS_PATH);
- //unique content
- dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis());
- dataMapRequest.getDataMap().putString("bolusProgress", "bolusProgress");
- dataMapRequest.getDataMap().putString("progressstatus", status);
- dataMapRequest.getDataMap().putInt("progresspercent", progresspercent);
- PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest();
- Wearable.DataApi.putDataItem(googleApiClient, putDataRequest);
- } else {
- Log.e("BolusProgress", "No connection to wearable available!");
- }
- }
-
- private void sendActionConfirmationRequest(String title, String message, String actionstring) {
- if (googleApiClient != null && googleApiClient.isConnected()) {
- PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.ACTION_CONFIRMATION_REQUEST_PATH);
- //unique content
- dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis());
- dataMapRequest.getDataMap().putString("actionConfirmationRequest", "actionConfirmationRequest");
- dataMapRequest.getDataMap().putString("title", title);
- dataMapRequest.getDataMap().putString("message", message);
- dataMapRequest.getDataMap().putString("actionstring", actionstring);
-
- aapsLogger.debug(LTag.WEAR, "Requesting confirmation from wear: " + actionstring);
-
- PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest();
- Wearable.DataApi.putDataItem(googleApiClient, putDataRequest);
- } else {
- Log.e("confirmationRequest", "No connection to wearable available!");
- }
- }
-
- private void sendChangeConfirmationRequest(String title, String message, String actionstring) {
- if (googleApiClient != null && googleApiClient.isConnected()) {
- PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.ACTION_CHANGECONFIRMATION_REQUEST_PATH);
- //unique content
- dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis());
- dataMapRequest.getDataMap().putString("changeConfirmationRequest", "changeConfirmationRequest");
- dataMapRequest.getDataMap().putString("title", title);
- dataMapRequest.getDataMap().putString("message", message);
- dataMapRequest.getDataMap().putString("actionstring", actionstring);
-
- aapsLogger.debug(LTag.WEAR, "Requesting confirmation from wear: " + actionstring);
-
- PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest();
- Wearable.DataApi.putDataItem(googleApiClient, putDataRequest);
- } else {
- Log.e("changeConfirmRequest", "No connection to wearable available!");
- }
- }
-
- private void sendCancelNotificationRequest(String actionstring) {
- if (googleApiClient != null && googleApiClient.isConnected()) {
- PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.ACTION_CANCELNOTIFICATION_REQUEST_PATH);
- //unique content
- dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis());
- dataMapRequest.getDataMap().putString("cancelNotificationRequest", "cancelNotificationRequest");
- dataMapRequest.getDataMap().putString("actionstring", actionstring);
-
- aapsLogger.debug(LTag.WEAR, "Canceling notification on wear: " + actionstring);
-
- PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest();
- Wearable.DataApi.putDataItem(googleApiClient, putDataRequest);
- } else {
- Log.e("cancelNotificationReq", "No connection to wearable available!");
- }
- }
-
- private void sendStatus() {
-
- if (googleApiClient != null && googleApiClient.isConnected()) {
- Profile profile = profileFunction.getProfile();
- String status = rh.gs(R.string.noprofile);
- String iobSum, iobDetail, cobString, currentBasal, bgiString;
- iobSum = iobDetail = cobString = currentBasal = bgiString = "";
- if (profile != null) {
- IobTotal bolusIob = iobCobCalculator.calculateIobFromBolus().round();
- IobTotal basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round();
-
- iobSum = DecimalFormatter.INSTANCE.to2Decimal(bolusIob.getIob() + basalIob.getBasaliob());
- iobDetail =
- "(" + DecimalFormatter.INSTANCE.to2Decimal(bolusIob.getIob()) + "|" + DecimalFormatter.INSTANCE.to2Decimal(basalIob.getBasaliob()) + ")";
- cobString = iobCobCalculator.getCobInfo(false, "WatcherUpdaterService").generateCOBString();
- currentBasal = generateBasalString();
-
- //bgi
- double bgi =
- -(bolusIob.getActivity() + basalIob.getActivity()) * 5 * Profile.Companion.fromMgdlToUnits(profile.getIsfMgdl(), profileFunction.getUnits());
- bgiString = "" + ((bgi >= 0) ? "+" : "") + DecimalFormatter.INSTANCE.to1Decimal(bgi);
-
- status = generateStatusString(profile, currentBasal, iobSum, iobDetail, bgiString);
- }
-
-
- //batteries
- int phoneBattery = receiverStatusStore.getBatteryLevel();
- String rigBattery = nsDeviceStatus.getUploaderStatus().trim();
-
-
- long openApsStatus;
- //OpenAPS status
- if (config.getAPS()) {
- //we are AndroidAPS
- openApsStatus = loop.getLastRun() != null && loop.getLastRun().getLastTBREnact() != 0 ? loop.getLastRun().getLastTBREnact() : -1;
- } else {
- //NSClient or remote
- openApsStatus = nsDeviceStatus.getOpenApsTimestamp();
- }
-
- PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.NEW_STATUS_PATH);
- //unique content
- dataMapRequest.getDataMap().putString("externalStatusString", status);
- dataMapRequest.getDataMap().putString("iobSum", iobSum);
- dataMapRequest.getDataMap().putString("iobDetail", iobDetail);
- dataMapRequest.getDataMap().putBoolean("detailedIob", sp.getBoolean(R.string.key_wear_detailediob, false));
- dataMapRequest.getDataMap().putString("cob", cobString);
- dataMapRequest.getDataMap().putString("currentBasal", currentBasal);
- dataMapRequest.getDataMap().putString("battery", "" + phoneBattery);
- dataMapRequest.getDataMap().putString("rigBattery", rigBattery);
- dataMapRequest.getDataMap().putLong("openApsStatus", openApsStatus);
- dataMapRequest.getDataMap().putString("bgi", bgiString);
- dataMapRequest.getDataMap().putBoolean("showBgi", sp.getBoolean(R.string.key_wear_showbgi, false));
- dataMapRequest.getDataMap().putInt("batteryLevel", (phoneBattery >= 30) ? 1 : 0);
- PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest();
- Wearable.DataApi.putDataItem(googleApiClient, putDataRequest);
- } else {
- Log.e("SendStatus", "No connection to wearable available!");
- }
- }
-
- 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(WearUris.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("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(WearUris.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) {
-
- String status = "";
-
- if (profile == null) {
- status = rh.gs(R.string.noprofile);
- return status;
- }
-
- if (!((PluginBase) loop).isEnabled()) {
- status += rh.gs(R.string.disabledloop) + "\n";
- lastLoopStatus = false;
- } else {
- lastLoopStatus = true;
- }
-
- String iobString;
- if (sp.getBoolean(R.string.key_wear_detailediob, false)) {
- iobString = iobSum + " " + iobDetail;
- } else {
- iobString = iobSum + "U";
- }
-
- status += currentBasal + " " + iobString;
-
- //add BGI if shown, otherwise return
- if (sp.getBoolean(R.string.key_wear_showbgi, false)) {
- status += " " + bgiString;
- }
-
- return status;
- }
-
- @NonNull
- private String generateBasalString() {
-
- String basalStringResult;
-
- Profile profile = profileFunction.getProfile();
- if (profile == null)
- return "";
-
- TemporaryBasal activeTemp = iobCobCalculator.getTempBasalIncludingConvertedExtended(System.currentTimeMillis());
- if (activeTemp != null) {
- basalStringResult = TemporaryBasalExtensionKt.toStringShort(activeTemp);
- } else {
- basalStringResult = DecimalFormatter.INSTANCE.to2Decimal(profile.getBasal()) + "U/h";
- }
- return basalStringResult;
- }
-
- @Override
- public void onDestroy() {
- if (googleApiClient != null && googleApiClient.isConnected()) {
- googleApiClient.disconnect();
- }
- }
-
- @Override
- public void onConnectionSuspended(int cause) {
- }
-
- @Override
- public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
- }
-
- public static boolean shouldReportLoopStatus(boolean enabled) {
- return (lastLoopStatus != enabled);
- }
-}
-*/
\ No newline at end of file
diff --git a/app/src/main/java/info/nightscout/androidaps/queue/CommandQueueImplementation.kt b/app/src/main/java/info/nightscout/androidaps/queue/CommandQueueImplementation.kt
index 1bf2891a81..4f9b16efcc 100644
--- a/app/src/main/java/info/nightscout/androidaps/queue/CommandQueueImplementation.kt
+++ b/app/src/main/java/info/nightscout/androidaps/queue/CommandQueueImplementation.kt
@@ -19,12 +19,10 @@ import info.nightscout.androidaps.database.entities.EffectiveProfileSwitch
import info.nightscout.androidaps.database.entities.ProfileSwitch
import info.nightscout.androidaps.database.interfaces.end
import info.nightscout.androidaps.dialogs.BolusProgressDialog
-import info.nightscout.androidaps.events.EventBolusRequested
+import info.nightscout.androidaps.events.EventMobileToWear
import info.nightscout.androidaps.events.EventProfileSwitchChanged
import info.nightscout.androidaps.extensions.getCustomizedName
import info.nightscout.androidaps.interfaces.*
-import info.nightscout.shared.logging.AAPSLogger
-import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissBolusProgressIfRunning
@@ -40,7 +38,10 @@ import info.nightscout.androidaps.utils.HtmlHelper
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.rx.AapsSchedulers
+import info.nightscout.shared.logging.AAPSLogger
+import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP
+import info.nightscout.shared.weardata.EventData
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
import io.reactivex.rxjava3.kotlin.subscribeBy
@@ -297,7 +298,7 @@ class CommandQueueImplementation @Inject constructor(
// not when the Bolus command is starting. The command closes the dialog upon completion).
showBolusProgressDialog(detailedBolusInfo)
// Notify Wear about upcoming bolus
- rxBus.send(EventBolusRequested(detailedBolusInfo.insulin))
+ rxBus.send(EventMobileToWear(EventData.BolusProgress(percent = 0, status = rh.gs(R.string.bolusrequested, detailedBolusInfo.insulin))))
}
}
notifyAboutNewCommand()
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 4b691ec932..581e201deb 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
@@ -65,10 +65,14 @@ class BolusWizard @Inject constructor(
private val disposable = CompositeDisposable()
+ var timeStamp : Long
+
init {
injector.androidInjector().inject(this)
+ timeStamp = dateUtil.now()
}
+
// Intermediate
var sens = 0.0
private set
@@ -236,7 +240,7 @@ class BolusWizard @Inject constructor(
// Total
calculatedTotalInsulin = insulinFromBG + insulinFromTrend + insulinFromCarbs + insulinFromBolusIOB + insulinFromBasalIOB + insulinFromCorrection + insulinFromSuperBolus + insulinFromCOB
- var percentage = if (usePercentage) totalPercentage else percentageCorrection.toDouble()
+ val percentage = if (usePercentage) totalPercentage else percentageCorrection.toDouble()
// Percentage adjustment
totalBeforePercentageAdjustment = calculatedTotalInsulin
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 b48d44c9cc..450ce43d46 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
@@ -9,6 +9,7 @@ import org.json.JSONObject
import java.util.*
import javax.inject.Inject
import javax.inject.Singleton
+import kotlin.collections.ArrayList
@Singleton
class QuickWizard @Inject constructor(
@@ -55,6 +56,11 @@ class QuickWizard @Inject constructor(
operator fun get(position: Int): QuickWizardEntry =
QuickWizardEntry(injector).from(storage.get(position) as JSONObject, position)
+ fun list(): ArrayList =
+ ArrayList().also {
+ for (i in 0 until size()) it.add(get(i))
+ }
+
fun get(guid: String): QuickWizardEntry? {
for (i in 0 until storage.length()) {
val entry = QuickWizardEntry(injector).from(storage.get(i) as JSONObject, i)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index a8f8bf0efd..316ef63054 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1212,4 +1212,5 @@
Hide loop records
AndroidAPS widget
Configure opacity
+ Loop status
diff --git a/app/src/test/java/info/nightscout/androidaps/queue/CommandQueueImplementationTest.kt b/app/src/test/java/info/nightscout/androidaps/queue/CommandQueueImplementationTest.kt
index 1f49ca02a5..c16f8a52bb 100644
--- a/app/src/test/java/info/nightscout/androidaps/queue/CommandQueueImplementationTest.kt
+++ b/app/src/test/java/info/nightscout/androidaps/queue/CommandQueueImplementationTest.kt
@@ -4,9 +4,9 @@ import android.content.Context
import android.os.PowerManager
import dagger.android.AndroidInjector
import dagger.android.HasAndroidInjector
+import info.nightscout.androidaps.R
import info.nightscout.androidaps.TestBaseWithProfile
import info.nightscout.androidaps.TestPumpPlugin
-import info.nightscout.androidaps.core.R
import info.nightscout.androidaps.data.DetailedBolusInfo
import info.nightscout.androidaps.data.PumpEnactResult
import info.nightscout.androidaps.database.AppRepository
@@ -138,6 +138,7 @@ class CommandQueueImplementationTest : TestBaseWithProfile() {
`when`(constraintChecker.applyBasalPercentConstraints(anyObject(), anyObject())).thenReturn(percentageConstraint)
`when`(rh.gs(R.string.connectiontimedout)).thenReturn("Connection timed out")
`when`(rh.gs(R.string.formatinsulinunits)).thenReturn("%1\$.2f U")
+ `when`(rh.gs(R.string.bolusrequested)).thenReturn("Going to deliver %1\$.2f U")
}
@Test
diff --git a/app/src/test/java/info/nightscout/androidaps/utils/wizard/BolusWizardTest.kt b/app/src/test/java/info/nightscout/androidaps/utils/wizard/BolusWizardTest.kt
index 6cec97fb42..420f062103 100644
--- a/app/src/test/java/info/nightscout/androidaps/utils/wizard/BolusWizardTest.kt
+++ b/app/src/test/java/info/nightscout/androidaps/utils/wizard/BolusWizardTest.kt
@@ -48,6 +48,7 @@ class BolusWizardTest : TestBase() {
it.activePlugin = activePlugin
it.commandQueue = commandQueue
it.loop = loop
+ it.dateUtil = dateUtil
it.iobCobCalculator = iobCobCalculator
it.glucoseStatusProvider = GlucoseStatusProvider(aapsLogger = aapsLogger, iobCobCalculator = iobCobCalculator, dateUtil = dateUtil)
}
diff --git a/shared/src/main/java/info/nightscout/androidaps/events/EventMobileToWear.kt b/shared/src/main/java/info/nightscout/androidaps/events/EventMobileToWear.kt
new file mode 100644
index 0000000000..c13bd0b2fe
--- /dev/null
+++ b/shared/src/main/java/info/nightscout/androidaps/events/EventMobileToWear.kt
@@ -0,0 +1,5 @@
+package info.nightscout.androidaps.events
+
+import info.nightscout.shared.weardata.EventData
+
+class EventMobileToWear(val payload: EventData) : Event()
\ No newline at end of file
diff --git a/shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobile.kt b/shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobile.kt
new file mode 100644
index 0000000000..3967cebaa9
--- /dev/null
+++ b/shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobile.kt
@@ -0,0 +1,5 @@
+package info.nightscout.androidaps.events
+
+import info.nightscout.shared.weardata.EventData
+
+class EventWearToMobile(val payload: EventData) : Event()
\ No newline at end of file
diff --git a/shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobileAction.kt b/shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobileAction.kt
deleted file mode 100644
index 1d5ca463e9..0000000000
--- a/shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobileAction.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package info.nightscout.androidaps.events
-
-import info.nightscout.shared.weardata.ActionData
-import kotlinx.serialization.Serializable
-import kotlinx.serialization.json.Json
-
-class EventWearToMobileAction(val actionData: ActionData) : Event()
\ No newline at end of file
diff --git a/shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobileChange.kt b/shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobileChange.kt
deleted file mode 100644
index 49ae662e82..0000000000
--- a/shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobileChange.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package info.nightscout.androidaps.events
-
-import info.nightscout.shared.weardata.ActionData
-import kotlinx.serialization.Serializable
-import kotlinx.serialization.json.Json
-
-class EventWearToMobileChange(val actionData: ActionData) : Event()
\ No newline at end of file
diff --git a/shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobileConfirm.kt b/shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobileConfirm.kt
deleted file mode 100644
index c999248cde..0000000000
--- a/shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobileConfirm.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package info.nightscout.androidaps.events
-
-import info.nightscout.shared.weardata.ActionData
-import kotlinx.serialization.Serializable
-import kotlinx.serialization.json.Json
-
-class EventWearToMobileConfirm(val actionData: ActionData) : Event()
\ No newline at end of file
diff --git a/shared/src/main/java/info/nightscout/shared/weardata/ActionData.kt b/shared/src/main/java/info/nightscout/shared/weardata/ActionData.kt
deleted file mode 100644
index 042edc0d6f..0000000000
--- a/shared/src/main/java/info/nightscout/shared/weardata/ActionData.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-package info.nightscout.shared.weardata
-
-import kotlinx.serialization.Serializable
-import kotlinx.serialization.json.Json
-
-@Serializable
-sealed class ActionData {
-
- fun serialize() = Json.encodeToString(serializer(), this)
-
- companion object {
-
- fun deserialize(json: String) = Json.decodeFromString(serializer(), json)
- }
- // Wear -> Mobile
- @Serializable
- data class Pong(val timeStamp: Long) : ActionData()
- @Serializable
- data class Bolus(val insulin: Double, val carbs: Int) : ActionData()
- @Serializable
- data class ProfileSwitch(val timeShift: Int, val percentage: Int) : ActionData()
-
- @Serializable
- data class OpenProfileSwitch(val timeShift: Int, val percentage: Int) : ActionData()
-
- // Mobile -> Wear
- @Serializable
- data class Ping(val timeStamp: Long) : ActionData()
- @Serializable
- data class ConfirmAction(val title: String, val message: String, val originalCommand: ActionData) : ActionData()
- @Serializable
- data class ChangeAction(val title: String, val message: String, val originalCommand: ActionData) : ActionData()
-}
\ No newline at end of file
diff --git a/shared/src/main/java/info/nightscout/shared/weardata/EventData.kt b/shared/src/main/java/info/nightscout/shared/weardata/EventData.kt
new file mode 100644
index 0000000000..45db4cd8fc
--- /dev/null
+++ b/shared/src/main/java/info/nightscout/shared/weardata/EventData.kt
@@ -0,0 +1,237 @@
+package info.nightscout.shared.weardata
+
+import info.nightscout.androidaps.events.Event
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.json.Json
+import java.util.*
+
+@Serializable
+sealed class EventData : Event() {
+
+ var sourceNodeId = ""
+
+ fun serialize() = Json.encodeToString(serializer(), this)
+
+ companion object {
+
+ fun deserialize(json: String) = Json.decodeFromString(serializer(), json)
+ }
+
+ // Mobile <- Wear
+ @Serializable
+ data class ActionPong(val timeStamp: Long) : EventData()
+
+ @Serializable
+ data class Error(val timeStamp: Long) : EventData() // ignored
+
+ @Serializable
+ data class CancelBolus(val timeStamp: Long) : EventData()
+
+ @Serializable
+ data class ActionResendData(val from: String) : EventData()
+
+ @Serializable
+ data class ActionPumpStatus(val timeStamp: Long) : EventData()
+
+ @Serializable
+ data class ActionLoopStatus(val timeStamp: Long) : EventData()
+
+ @Serializable
+ data class ActionTddStatus(val timeStamp: Long) : EventData()
+
+ @Serializable
+ data class ActionECarbsPreCheck(val carbs: Int, val carbsTimeShift: Int, val duration: Int) : EventData()
+
+ @Serializable
+ data class ActionBolusPreCheck(val insulin: Double, val carbs: Int) : EventData()
+
+ @Serializable
+ data class ActionFillPreCheck(val insulin: Double) : EventData()
+
+ @Serializable
+ data class ActionFillPresetPreCheck(val button: Int) : EventData()
+
+ @Serializable
+ data class ActionProfileSwitchSendInitialData(val timeStamp: Long) : EventData()
+
+ @Serializable
+ data class ActionProfileSwitchPreCheck(val timeShift: Int, val percentage: Int) : EventData()
+
+ @Serializable
+ data class ActionWizardPreCheck(val carbs: Int, val percentage: Int) : EventData()
+
+ @Serializable
+ data class ActionQuickWizardPreCheck(val guid: String) : EventData()
+
+ @Serializable
+ data class ActionTempTargetPreCheck(
+ val command: TempTargetCommand,
+ val isMgdl: Boolean = true, val duration: Int = 0, val low: Double = 0.0, val high: Double = 0.0 // manual
+ ) : EventData() {
+
+ @Serializable
+ enum class TempTargetCommand {
+
+ PRESET_ACTIVITY, PRESET_HYPO, PRESET_EATING, CANCEL, MANUAL
+ }
+
+ }
+
+ // Mobile <- Wear return
+
+ @Serializable
+ data class ActionWizardConfirmed(val timeStamp: Long) : EventData()
+
+ @Serializable
+ data class ActionTempTargetConfirmed(val isMgdl: Boolean = true, val duration: Int = 0, val low: Double = 0.0, val high: Double = 0.0) : EventData()
+
+ @Serializable
+ data class ActionBolusConfirmed(val insulin: Double, val carbs: Int) : EventData()
+
+ @Serializable
+ data class ActionECarbsConfirmed(val carbs: Int, val carbsTime: Long, val duration: Int) : EventData()
+
+ @Serializable
+ data class ActionFillConfirmed(val insulin: Double) : EventData()
+
+ @Serializable
+ data class ActionProfileSwitchConfirmed(val timeShift: Int, val percentage: Int) : EventData()
+
+ @Serializable
+ data class OpenLoopRequestConfirmed(val timeStamp: Long) : EventData()
+
+ // Mobile -> Wear
+ @Serializable
+ data class CancelNotification(val timeStamp: Long) : EventData()
+
+ @Serializable
+ data class ActionPing(val timeStamp: Long) : EventData()
+
+ @Serializable
+ data class OpenSettings(val timeStamp: Long) : EventData()
+
+ @Serializable
+ data class BolusProgress(val percent: Int, val status: String) : EventData()
+
+ @Serializable
+ data class SingleBg @JvmOverloads constructor(
+ var timeStamp: Long,
+ val sgvString: String = "---",
+ val glucoseUnits: String = "-",
+ val slopeArrow: String = "--",
+ val delta: String = "--",
+ val avgDelta: String = "--",
+ val sgvLevel: Long = 0,
+ val sgv: Double,
+ val high: Double, // highLine
+ val low: Double, // lowLine
+ val color: Int = 0
+ ) : EventData(), Comparable {
+
+ override fun equals(other: Any?): Boolean =
+ when {
+ other !is SingleBg -> false
+ color != other.color -> false
+ else -> timeStamp == other.timeStamp
+ }
+
+ override fun hashCode(): Int {
+ return Objects.hash(timeStamp, color)
+ }
+
+ override fun compareTo(other: SingleBg): Int {
+ // reverse order endTime get latest first
+ if (this.timeStamp < other.timeStamp) return 1
+ return if (this.timeStamp > other.timeStamp) -1 else 0
+ }
+ }
+
+ @Serializable
+ data class GraphData(
+ val entries: ArrayList
+ ) : EventData()
+
+ @Serializable
+ data class TreatmentData(
+ val temps: ArrayList,
+ val basals: ArrayList,
+ val boluses: ArrayList,
+ val predictions: ArrayList
+ ) : EventData() {
+
+ @Serializable
+ data class TempBasal(
+ val startTime: Long,
+ val startBasal: Double,
+ val endTime: Long,
+ val endBasal: Double,
+ val amount: Double
+ )
+
+ @Serializable
+ data class Basal(
+ val startTime: Long,
+ val endTime: Long,
+ val amount: Double
+ )
+
+ @Serializable
+ data class Treatment(
+ val date: Long,
+ val bolus: Double,
+ val carbs: Double,
+ val isSMB: Boolean,
+ val isValid: Boolean,
+ )
+ }
+
+ @Serializable
+ data class Status(
+ val externalStatus: String,
+ val iobSum: String,
+ val iobDetail: String,
+ val detailedIob: Boolean,
+ val cob: String,
+ val currentBasal: String,
+ val battery: String,
+ val rigBattery: String,
+ val openApsStatus: Long,
+ val bgi: String,
+ val showBgi: Boolean,
+ val batteryLevel: Int
+ ) : EventData()
+
+ @Serializable
+ data class Preferences(
+ val timeStamp: Long,
+ val wearControl: Boolean,
+ val unitsMgdl: Boolean,
+ val bolusPercentage: Int,
+ val maxCarbs: Int,
+ val maxBolus: Double
+ ) : EventData()
+
+ @Serializable
+ data class QuickWizard(
+ val entries: ArrayList
+ ) : EventData() {
+
+ @Serializable
+ data class QuickWizardEntry(
+ val guid: String,
+ val buttonText: String,
+ val carbs: Int,
+ val validFrom: Int,
+ val validTo: Int
+ ) : EventData()
+ }
+
+ @Serializable
+ data class ActionProfileSwitchOpenActivity(val timeShift: Int, val percentage: Int) : EventData()
+
+ @Serializable
+ data class OpenLoopRequest(val title: String, val message: String, val returnCommand: EventData?) : EventData()
+
+ @Serializable // returnCommand is sent back to Mobile after confirmation
+ data class ConfirmAction(val title: String, val message: String, val returnCommand: EventData?) : EventData()
+}
\ No newline at end of file
diff --git a/shared/src/main/java/info/nightscout/shared/weardata/WearConstants.kt b/shared/src/main/java/info/nightscout/shared/weardata/WearConstants.kt
deleted file mode 100644
index 7d5558d5f8..0000000000
--- a/shared/src/main/java/info/nightscout/shared/weardata/WearConstants.kt
+++ /dev/null
@@ -1,61 +0,0 @@
-package info.nightscout.shared.weardata
-
-import android.content.Context
-import info.nightscout.shared.R
-import javax.inject.Inject
-import javax.inject.Singleton
-
-@Suppress("PropertyName")
-@Singleton
-class WearConstants @Inject constructor(private val context: Context) {
-
- // Paths must be defined in manifest
- // mobile -> wear (data)
- val M_W_DATA get() = context.getString(R.string.path_watch_data)
- val M_W_STATUS get() = context.getString(R.string.path_status)
- val M_W_PREFERENCES get() = context.getString(R.string.path_preferences)
- val M_W_QUICK_WIZARD get() = context.getString(R.string.path_quick_wizard)
- val M_W_BASAL get() = context.getString(R.string.path_basal)
- val M_W_BOLUS_PROGRESS get() = context.getString(R.string.path_bolus_progress)
- val M_W_ACTION_CONFIRMATION_REQUEST get() = context.getString(R.string.path_action_confirmation)
- val M_W_ACTION_CHANGE_CONFIRMATION_REQUEST get() = context.getString(R.string.path_change_confirmation_request)
- val M_W_ACTION_CANCEL_NOTIFICATION_REQUEST get() = context.getString(R.string.path_cancel_notification_request)
-
- // mobile -> wear (message)
- val M_W_OPEN_SETTINGS get() = context.getString(R.string.path_open_wear_setting)
- val M_W_PING get() = context.getString(R.string.path_ping)
-
- // wear -> mobile (message)
- val W_M_RESEND_DATA get() = context.getString(R.string.path_resend_data_request)
- val W_M_CANCEL_BOLUS get() = context.getString(R.string.path_cancel_bolus_on_phone)
- val W_M_CONFIRM_ACTION get() = context.getString(R.string.path_confirm_action)
- val W_M_INITIATE_ACTION get() = context.getString(R.string.path_initiate_action_on_phone)
- val W_M_PONG get() = context.getString(R.string.path_pong)
-
- companion object {
-
- // actions for WEAR_INITIATE_ACTION_ON_PHONE
- // used by
- // DataLayerListenerService::initiateAction
- // ActionStringHandler::handleInitiateActionOnPhone
- // EventWearInitiateAction
- const val ACTION_FILL_PRESET = "fillpreset"
- const val ACTION_FILL = "fill"
- const val ACTION_BOLUS = "bolus"
- const val ACTION_TEMPORARY_TARGET = "temptarget"
- const val ACTION_STATUS = "status"
- const val ACTION_WIZARD = "wizard"
- const val ACTION_WIZARD2 = "wizard2"
- const val ACTION_QUICK_WIZARD = "quick_wizard"
- const val ACTION_OPEN_CPP = "opencpp"
- const val ACTION_CPP_SET = "cppset"
- const val ACTION_TDD_STATS = "tddstats"
- const val ACTION_E_CARBS = "ecarbs"
- const val ACTION_CHANGE_REQUEST = "changeRequest"
- const val ACTION_CANCEL_CHANGE_REQUEST = "cancelChangeRequest"
- const val ACTION_DISMISS_OVERVIEW_NOTIF = "dismissoverviewnotification"
-
- //data keys
- const val KEY_ACTION_DATA = "actionData"
- }
-}
diff --git a/shared/src/main/res/values/wear_paths.xml b/shared/src/main/res/values/wear_paths.xml
index 1bf7203905..36d7325849 100644
--- a/shared/src/main/res/values/wear_paths.xml
+++ b/shared/src/main/res/values/wear_paths.xml
@@ -1,19 +1,4 @@
- /ping
- /pong
- /openwearsettings
- /nightscout_watch_data_resend
- /nightscout_watch_initiateactionstring
- /nightscout_watch_cancel_bolus
- /nightscout_watch_confirmactionstring
- /nightscout_watch_data
- /sendstatustowear
- /sendpreferencestowear
- /send_quick_wizard
- /nightscout_watch_basal
- /nightscout_watch_bolusprogress
- /nightscout_watch_actionconfirmationrequest
- /nightscout_watch_changeconfirmationrequest
- /nightscout_watch_cancelnotificationrequest
+ /rx_bridge
\ No newline at end of file
diff --git a/wear/src/main/AndroidManifest.xml b/wear/src/main/AndroidManifest.xml
index 95ff23dc3f..cd0fd89207 100644
--- a/wear/src/main/AndroidManifest.xml
+++ b/wear/src/main/AndroidManifest.xml
@@ -241,7 +241,7 @@
-
@@ -252,42 +252,6 @@
-
-
-
-
-
-
-
-
-
@@ -299,15 +263,7 @@
-
-
@@ -466,7 +422,7 @@
diff --git a/wear/src/main/java/info/nightscout/androidaps/Aaps.kt b/wear/src/main/java/info/nightscout/androidaps/Aaps.kt
index 565ddf002e..ef5ca19d11 100644
--- a/wear/src/main/java/info/nightscout/androidaps/Aaps.kt
+++ b/wear/src/main/java/info/nightscout/androidaps/Aaps.kt
@@ -7,7 +7,11 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.preference.PreferenceManager
import dagger.android.AndroidInjector
import dagger.android.DaggerApplication
+import info.nightscout.androidaps.comm.DataHandlerWear
+import info.nightscout.androidaps.comm.DataLayerListenerServiceWear
import info.nightscout.androidaps.di.DaggerWearComponent
+import info.nightscout.androidaps.events.EventWearPreferenceChange
+import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import javax.inject.Inject
@@ -15,11 +19,15 @@ import javax.inject.Inject
class Aaps : DaggerApplication(), OnSharedPreferenceChangeListener {
@Inject lateinit var aapsLogger: AAPSLogger
+ @Inject lateinit var rxBus: RxBus
+ @Inject lateinit var dataHandlerWear: DataHandlerWear // instantiate only
override fun onCreate() {
super.onCreate()
aapsLogger.debug(LTag.WEAR, "onCreate")
PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this)
+ startService(Intent(this, DataLayerListenerServiceWear::class.java))
+
}
override fun applicationInjector(): AndroidInjector =
@@ -30,6 +38,7 @@ class Aaps : DaggerApplication(), OnSharedPreferenceChangeListener {
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
// we trigger update on Complications
- LocalBroadcastManager.getInstance(this).sendBroadcast(Intent(Intent.ACTION_SEND))
+ LocalBroadcastManager.getInstance(this).sendBroadcast(Intent(DataLayerListenerServiceWear.INTENT_NEW_DATA))
+ rxBus.send(EventWearPreferenceChange(key))
}
}
\ No newline at end of file
diff --git a/wear/src/main/java/info/nightscout/androidaps/comm/DataHandlerWear.kt b/wear/src/main/java/info/nightscout/androidaps/comm/DataHandlerWear.kt
new file mode 100644
index 0000000000..67a4b7afe4
--- /dev/null
+++ b/wear/src/main/java/info/nightscout/androidaps/comm/DataHandlerWear.kt
@@ -0,0 +1,301 @@
+package info.nightscout.androidaps.comm
+
+import android.annotation.TargetApi
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.os.Build
+import android.os.Bundle
+import android.os.SystemClock
+import androidx.core.app.NotificationCompat
+import androidx.core.app.NotificationManagerCompat
+import androidx.localbroadcastmanager.content.LocalBroadcastManager
+import androidx.wear.tiles.TileService
+import com.google.android.gms.wearable.WearableListenerService
+import info.nightscout.androidaps.R
+import info.nightscout.androidaps.events.EventWearToMobile
+import info.nightscout.androidaps.interaction.AAPSPreferences
+import info.nightscout.androidaps.interaction.actions.AcceptActivity
+import info.nightscout.androidaps.interaction.actions.CPPActivity
+import info.nightscout.androidaps.interaction.utils.Persistence
+import info.nightscout.androidaps.plugins.bus.RxBus
+import info.nightscout.androidaps.tile.ActionsTileService
+import info.nightscout.androidaps.tile.QuickWizardTileService
+import info.nightscout.androidaps.tile.TempTargetTileService
+import info.nightscout.androidaps.utils.rx.AapsSchedulers
+import info.nightscout.shared.logging.AAPSLogger
+import info.nightscout.shared.logging.LTag
+import info.nightscout.shared.sharedPreferences.SP
+import info.nightscout.shared.weardata.EventData
+import io.reactivex.rxjava3.disposables.CompositeDisposable
+import io.reactivex.rxjava3.kotlin.plusAssign
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class DataHandlerWear @Inject constructor(
+ private val context: Context,
+ private val rxBus: RxBus,
+ private val aapsSchedulers: AapsSchedulers,
+ private val sp: SP,
+ private val aapsLogger: AAPSLogger,
+ private val persistence: Persistence
+) {
+
+ private val disposable = CompositeDisposable()
+
+ init {
+ setupBus()
+ }
+
+ private fun setupBus() {
+ disposable += rxBus
+ .toObservable(EventData.ActionPing::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe {
+ aapsLogger.debug(LTag.WEAR, "Ping received from ${it.sourceNodeId}")
+ rxBus.send(EventWearToMobile(EventData.ActionPong(System.currentTimeMillis())))
+ }
+ disposable += rxBus
+ .toObservable(EventData.ConfirmAction::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe {
+ aapsLogger.debug(LTag.WEAR, "ConfirmAction received from ${it.sourceNodeId}")
+ context.startActivity(Intent(context, AcceptActivity::class.java).apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ putExtras(
+ Bundle().also { bundle ->
+ bundle.putString("title", it.title)
+ bundle.putString("message", it.message)
+ bundle.putString(DataLayerListenerServiceWear.KEY_ACTION_DATA, it.returnCommand?.serialize())
+ }
+ )
+ })
+ }
+ disposable += rxBus
+ .toObservable(EventData.CancelNotification::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe {
+ aapsLogger.debug(LTag.WEAR, "ActionCancelNotification received from ${it.sourceNodeId}")
+ (context.getSystemService(WearableListenerService.NOTIFICATION_SERVICE) as NotificationManager).cancel(DataLayerListenerServiceWear.CHANGE_NOTIF_ID)
+ }
+ disposable += rxBus
+ .toObservable(EventData.OpenLoopRequest::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe {
+ aapsLogger.debug(LTag.WEAR, "OpenLoopRequest received from ${it.sourceNodeId}")
+ handleOpenLoopRequest(it)
+ }
+ disposable += rxBus
+ .toObservable(EventData.OpenSettings::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe {
+ aapsLogger.debug(LTag.WEAR, "ActionOpenSettings received from ${it.sourceNodeId}")
+ context.startActivity(Intent(context, AAPSPreferences::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) })
+ }
+ disposable += rxBus
+ .toObservable(EventData.ActionProfileSwitchOpenActivity::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe { event ->
+ aapsLogger.debug(LTag.WEAR, "ActionProfileSwitchOpenActivity received from ${event.sourceNodeId}")
+ context.startActivity(Intent(context, CPPActivity::class.java).apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ putExtras(Bundle().also { bundle ->
+ bundle.putInt("percentage", event.percentage)
+ bundle.putInt("timeshift", event.timeShift)
+ })
+ })
+ }
+ disposable += rxBus
+ .toObservable(EventData.BolusProgress::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe {
+ aapsLogger.debug(LTag.WEAR, "Bolus progress received from ${it.sourceNodeId}")
+ handleBolusProgress(it)
+ rxBus.send(EventWearToMobile(EventData.ActionPong(System.currentTimeMillis())))
+ }
+ disposable += rxBus
+ .toObservable(EventData.Status::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe {
+ aapsLogger.debug(LTag.WEAR, "Status received from ${it.sourceNodeId}")
+ persistence.store(it)
+ LocalBroadcastManager.getInstance(context).sendBroadcast(
+ Intent(DataLayerListenerServiceWear.INTENT_NEW_DATA).apply {
+ putExtra(DataLayerListenerServiceWear.KEY_STATUS_DATA, it.serialize())
+ }
+ )
+ }
+ disposable += rxBus
+ .toObservable(EventData.SingleBg::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe {
+ aapsLogger.debug(LTag.WEAR, "SingleBg received from ${it.sourceNodeId}")
+ persistence.store(it)
+ LocalBroadcastManager.getInstance(context).sendBroadcast(
+ Intent(DataLayerListenerServiceWear.INTENT_NEW_DATA).apply {
+ putExtra(DataLayerListenerServiceWear.KEY_SINGLE_BG_DATA, it.serialize())
+ }
+ )
+ }
+ disposable += rxBus
+ .toObservable(EventData.GraphData::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe {
+ aapsLogger.debug(LTag.WEAR, "GraphData received from ${it.sourceNodeId}")
+ persistence.store(it)
+ LocalBroadcastManager.getInstance(context).sendBroadcast(
+ Intent(DataLayerListenerServiceWear.INTENT_NEW_DATA).apply {
+ putExtra(DataLayerListenerServiceWear.KEY_GRAPH_DATA, it.serialize())
+ }
+ )
+ }
+ disposable += rxBus
+ .toObservable(EventData.TreatmentData::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe {
+ aapsLogger.debug(LTag.WEAR, "TreatmentData received from ${it.sourceNodeId}")
+ persistence.store(it)
+ LocalBroadcastManager.getInstance(context).sendBroadcast(
+ Intent(DataLayerListenerServiceWear.INTENT_NEW_DATA).apply {
+ putExtra(DataLayerListenerServiceWear.KEY_TREATMENTS_DATA, it.serialize())
+ }
+ )
+ }
+ disposable += rxBus
+ .toObservable(EventData.Preferences::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe {
+ aapsLogger.debug(LTag.WEAR, "Preferences received from ${it.sourceNodeId}")
+ if (it.wearControl != sp.getBoolean(R.string.key_wear_control, false)) {
+ sp.putBoolean(R.string.key_wear_control, it.wearControl)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ TileService.getUpdater(context).requestUpdate(ActionsTileService::class.java)
+ TileService.getUpdater(context).requestUpdate(TempTargetTileService::class.java)
+ TileService.getUpdater(context).requestUpdate(QuickWizardTileService::class.java)
+ }
+ }
+ sp.putBoolean(R.string.key_units_mgdl, it.unitsMgdl)
+ sp.putInt(R.string.key_boluswizard_percentage, it.bolusPercentage)
+ sp.putInt(R.string.key_treatmentssafety_maxcarbs, it.maxCarbs)
+ sp.putDouble(R.string.key_treatmentssafety_maxbolus, it.maxBolus)
+ }
+ disposable += rxBus
+ .toObservable(EventData.QuickWizard::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe {
+ aapsLogger.debug(LTag.WEAR, "QuickWizard received from ${it.sourceNodeId}")
+ val serialized = it.serialize()
+ if (serialized != sp.getString(R.string.key_quick_wizard_data, "")) {
+ sp.putString(R.string.key_quick_wizard_data, serialized)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+ TileService.getUpdater(context).requestUpdate(QuickWizardTileService::class.java)
+ }
+ }
+ }
+
+ private fun handleBolusProgress(bolusProgress: EventData.BolusProgress) {
+ val vibratePattern: LongArray
+ val vibrate = sp.getBoolean("vibrateOnBolus", true)
+ vibratePattern = if (vibrate) longArrayOf(0, 50, 1000) else longArrayOf(0, 1, 1000)
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ createBolusProgressChannels()
+ }
+ val cancelIntent = Intent(context, DataLayerListenerServiceWear::class.java)
+ cancelIntent.action = DataLayerListenerServiceWear.INTENT_CANCEL_BOLUS
+ val cancelPendingIntent = PendingIntent.getService(context, 0, cancelIntent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
+ val notificationBuilder: NotificationCompat.Builder =
+ NotificationCompat.Builder(context, if (vibrate) DataLayerListenerServiceWear.AAPS_NOTIFY_CHANNEL_ID_BOLUS_PROGRESS else DataLayerListenerServiceWear.AAPS_NOTIFY_CHANNEL_ID_BOLUS_PROGRESS_SILENT)
+ .setSmallIcon(R.drawable.ic_icon)
+ .setContentTitle(context.getString(R.string.bolus_progress))
+ .setContentText("${bolusProgress.percent}% - ${bolusProgress.status}")
+ .setSubText(context.getString(R.string.press_to_cancel))
+ .setContentIntent(cancelPendingIntent)
+ .setPriority(NotificationCompat.PRIORITY_MAX)
+ .setVibrate(vibratePattern)
+ .addAction(R.drawable.ic_cancel, context.getString(R.string.cancel_bolus), cancelPendingIntent)
+ val notificationManager = NotificationManagerCompat.from(context)
+ notificationManager.notify(DataLayerListenerServiceWear.BOLUS_PROGRESS_NOTIF_ID, notificationBuilder.build())
+ notificationManager.cancel(DataLayerListenerServiceWear.CONFIRM_NOTIF_ID) // multiple watch setup
+ if (bolusProgress.percent == 100) {
+ scheduleDismissBolusProgress(5)
+ }
+ }
+
+ @TargetApi(value = 26) private fun createBolusProgressChannels() {
+ createNotificationChannel(
+ longArrayOf(0, 50, 1000),
+ DataLayerListenerServiceWear.AAPS_NOTIFY_CHANNEL_ID_BOLUS_PROGRESS,
+ context.getString(R.string.bolus_progress_channel_name),
+ context.getString(R.string.bolus_progress_channel_description)
+ )
+ createNotificationChannel(
+ longArrayOf(0, 1, 1000),
+ DataLayerListenerServiceWear.AAPS_NOTIFY_CHANNEL_ID_BOLUS_PROGRESS_SILENT,
+ context.getString(R.string.bolus_progress_silent_channel_name),
+ context.getString(R.string.bolus_progress_silent_channel_description)
+ )
+ }
+
+ @TargetApi(value = 26) private fun createNotificationChannel(vibratePattern: LongArray, channelID: String, name: CharSequence, description: String) {
+ val channel = NotificationChannel(channelID, name, NotificationManager.IMPORTANCE_HIGH)
+ channel.description = description
+ channel.enableVibration(true)
+ channel.vibrationPattern = vibratePattern
+
+ // Register the channel with the system; you can't change the importance
+ // or other notification behaviors after this
+ context.getSystemService(NotificationManager::class.java)
+ .createNotificationChannel(channel)
+ }
+
+ private fun handleOpenLoopRequest(command: EventData.OpenLoopRequest) {
+ // Create the NotificationChannel, but only on API 26+ because
+ // the NotificationChannel class is new and not in the support library
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val name: CharSequence = "AAPS Open Loop"
+ val description = "Open Loop request notification"
+ val channel = NotificationChannel(DataLayerListenerServiceWear.AAPS_NOTIFY_CHANNEL_ID_OPEN_LOOP, name, NotificationManager.IMPORTANCE_HIGH)
+ channel.description = description
+ channel.enableVibration(true)
+
+ // Register the channel with the system; you can't change the importance
+ // or other notification behaviors after this
+ val notificationManager = context.getSystemService(NotificationManager::class.java)
+ notificationManager.createNotificationChannel(channel)
+ }
+ var builder = NotificationCompat.Builder(context, DataLayerListenerServiceWear.AAPS_NOTIFY_CHANNEL_ID_OPEN_LOOP)
+ builder = builder.setSmallIcon(R.drawable.notif_icon)
+ .setContentTitle(command.title)
+ .setContentText(command.message)
+ .setPriority(Notification.PRIORITY_HIGH)
+ .setVibrate(longArrayOf(1000, 1000, 1000, 1000, 1000))
+
+ // Creates an explicit intent for an Activity in your app
+ val intent = Intent(context, AcceptActivity::class.java).apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ putExtras(Bundle().also { bundle ->
+ bundle.putString("title", command.title)
+ bundle.putString("message", command.message)
+ bundle.putString(DataLayerListenerServiceWear.KEY_ACTION_DATA, command.returnCommand?.serialize())
+ })
+ }
+ val resultPendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
+ builder = builder.setContentIntent(resultPendingIntent)
+ val mNotificationManager = context.getSystemService(WearableListenerService.NOTIFICATION_SERVICE) as NotificationManager
+ // mId allows you to update the notification later on.
+ mNotificationManager.notify(DataLayerListenerServiceWear.CHANGE_NOTIF_ID, builder.build())
+ }
+
+ @Suppress("SameParameterValue")
+ private fun scheduleDismissBolusProgress(seconds: Int) {
+ Thread {
+ SystemClock.sleep(seconds * 1000L)
+ NotificationManagerCompat.from(context).cancel(DataLayerListenerServiceWear.BOLUS_PROGRESS_NOTIF_ID)
+ }.start()
+ }
+}
\ No newline at end of file
diff --git a/wear/src/main/java/info/nightscout/androidaps/comm/DataLayerListenerServiceWear.kt b/wear/src/main/java/info/nightscout/androidaps/comm/DataLayerListenerServiceWear.kt
new file mode 100644
index 0000000000..3950cd0c10
--- /dev/null
+++ b/wear/src/main/java/info/nightscout/androidaps/comm/DataLayerListenerServiceWear.kt
@@ -0,0 +1,210 @@
+package info.nightscout.androidaps.comm
+
+import android.content.Intent
+import android.os.Handler
+import android.os.HandlerThread
+import androidx.core.app.NotificationManagerCompat
+import com.google.android.gms.tasks.Tasks
+import com.google.android.gms.wearable.*
+import dagger.android.AndroidInjection
+import info.nightscout.androidaps.R
+import info.nightscout.androidaps.events.EventWearToMobile
+import info.nightscout.androidaps.interaction.utils.Persistence
+import info.nightscout.androidaps.interaction.utils.WearUtil
+import info.nightscout.androidaps.plugins.bus.RxBus
+import info.nightscout.androidaps.utils.rx.AapsSchedulers
+import info.nightscout.shared.logging.AAPSLogger
+import info.nightscout.shared.logging.LTag
+import info.nightscout.shared.sharedPreferences.SP
+import info.nightscout.shared.weardata.EventData
+import io.reactivex.rxjava3.disposables.CompositeDisposable
+import io.reactivex.rxjava3.kotlin.plusAssign
+import kotlinx.coroutines.*
+import kotlinx.coroutines.tasks.await
+import javax.inject.Inject
+
+class DataLayerListenerServiceWear : WearableListenerService() {
+
+ @Inject lateinit var aapsLogger: AAPSLogger
+ @Inject lateinit var wearUtil: WearUtil
+ @Inject lateinit var persistence: Persistence
+ @Inject lateinit var sp: SP
+ @Inject lateinit var rxBus: RxBus
+ @Inject lateinit var aapsSchedulers: AapsSchedulers
+
+ private val dataClient by lazy { Wearable.getDataClient(this) }
+ private val messageClient by lazy { Wearable.getMessageClient(this) }
+ private val capabilityClient by lazy { Wearable.getCapabilityClient(this) }
+ //private val nodeClient by lazy { Wearable.getNodeClient(this) }
+
+ private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
+ private var handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper)
+
+ private val disposable = CompositeDisposable()
+
+ private val rxPath get() = getString(R.string.path_rx_bridge)
+
+ override fun onCreate() {
+ AndroidInjection.inject(this)
+ super.onCreate()
+ handler.post { updateTranscriptionCapability() }
+ disposable += rxBus
+ .toObservable(EventWearToMobile::class.java)
+ .observeOn(aapsSchedulers.io)
+ .subscribe {
+ sendMessage(rxPath, it.payload.serialize())
+ }
+ }
+
+ override fun onPeerConnected(p0: Node) {
+ super.onPeerConnected(p0)
+ }
+
+ override fun onCapabilityChanged(p0: CapabilityInfo) {
+ super.onCapabilityChanged(p0)
+ handler.post { updateTranscriptionCapability() }
+ aapsLogger.debug(LTag.WEAR, "onCapabilityChanged: ${p0.name} ${p0.nodes.joinToString(", ") { it.displayName + "(" + it.id + ")" }}")
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ scope.cancel()
+ disposable.clear()
+ }
+
+ override fun onDataChanged(dataEvents: DataEventBuffer) {
+ //aapsLogger.debug(LTag.WEAR, "onDataChanged")
+
+ dataEvents.forEach { event ->
+ if (event.type == DataEvent.TYPE_CHANGED) {
+ val path = event.dataItem.uri.path
+
+ aapsLogger.debug(LTag.WEAR, "onDataChanged: Path: $path, EventDataItem=${event.dataItem}")
+ try {
+ @Suppress("ControlFlowWithEmptyBody", "UNUSED_EXPRESSION")
+ when (path) {
+ }
+ } catch (exception: Exception) {
+ aapsLogger.error(LTag.WEAR, "onDataChanged failed", exception)
+ }
+ }
+ }
+ super.onDataChanged(dataEvents)
+ }
+
+ override fun onMessageReceived(messageEvent: MessageEvent) {
+ super.onMessageReceived(messageEvent)
+
+ when (messageEvent.path) {
+ rxPath -> {
+ aapsLogger.debug(LTag.WEAR, "onMessageReceived: ${String(messageEvent.data)}")
+ val command = EventData.deserialize(String(messageEvent.data))
+ rxBus.send(command.also { it.sourceNodeId = messageEvent.sourceNodeId })
+ }
+ }
+ }
+
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ when (intent?.action) {
+
+ INTENT_CANCEL_BOLUS -> {
+ //dismiss notification
+ NotificationManagerCompat.from(this).cancel(BOLUS_PROGRESS_NOTIF_ID)
+ //send cancel-request to phone.
+ rxBus.send(EventWearToMobile(EventData.CancelBolus(System.currentTimeMillis())))
+ }
+
+ }
+ return START_STICKY
+ }
+
+ private var transcriptionNodeId: String? = null
+
+ private fun updateTranscriptionCapability() {
+ val capabilityInfo: CapabilityInfo = Tasks.await(
+ capabilityClient.getCapability(PHONE_CAPABILITY, CapabilityClient.FILTER_REACHABLE)
+ )
+ aapsLogger.debug(LTag.WEAR, "Nodes: ${capabilityInfo.nodes.joinToString(", ") { it.displayName + "(" + it.id + ")" }}")
+ transcriptionNodeId = pickBestNodeId(capabilityInfo.nodes)
+ aapsLogger.debug(LTag.WEAR, "Selected node: $transcriptionNodeId")
+ }
+
+ // Find a nearby node or pick one arbitrarily
+ private fun pickBestNodeId(nodes: Set): String? =
+ nodes.firstOrNull { it.isNearby }?.id ?: nodes.firstOrNull()?.id
+
+ @Suppress("unused")
+ private fun sendData(path: String, vararg params: DataMap) {
+ scope.launch {
+ try {
+ for (dm in params) {
+ val request = PutDataMapRequest.create(path).apply {
+ dataMap.putAll(dm)
+ }
+ .asPutDataRequest()
+ .setUrgent()
+
+ val result = dataClient.putDataItem(request).await()
+ aapsLogger.debug(LTag.WEAR, "sendData: ${result.uri} ${params.joinToString()}")
+ }
+ } catch (cancellationException: CancellationException) {
+ throw cancellationException
+ } catch (exception: Exception) {
+ aapsLogger.error(LTag.WEAR, "DataItem failed: $exception")
+ }
+ }
+ }
+
+ private fun sendMessage(path: String, data: String?) {
+ aapsLogger.debug(LTag.WEAR, "sendMessage: $path $data")
+ transcriptionNodeId?.also { nodeId ->
+ messageClient
+ .sendMessage(nodeId, path, data?.toByteArray() ?: byteArrayOf()).apply {
+ addOnSuccessListener { }
+ addOnFailureListener {
+ aapsLogger.debug(LTag.WEAR, "sendMessage: $path failure")
+ }
+ }
+ }
+ }
+
+ @Suppress("unused")
+ private fun sendMessage(path: String, data: ByteArray) {
+ aapsLogger.debug(LTag.WEAR, "sendMessage: $path")
+ transcriptionNodeId?.also { nodeId ->
+ messageClient
+ .sendMessage(nodeId, path, data).apply {
+ addOnSuccessListener { }
+ addOnFailureListener {
+ aapsLogger.debug(LTag.WEAR, "sendMessage: $path failure")
+ }
+ }
+ }
+ }
+
+ companion object {
+
+ const val PHONE_CAPABILITY = "androidaps_mobile"
+
+ // Accepted intents
+ val INTENT_CANCEL_BOLUS = DataLayerListenerServiceWear::class.java.name + ".CancelBolus"
+ val INTENT_NEW_DATA = DataLayerListenerServiceWear::class.java.name + ".NewData"
+
+ //data keys
+ const val KEY_ACTION_DATA = "actionData"
+ const val KEY_ACTION = "action"
+ const val KEY_MESSAGE = "message"
+ const val KEY_SINGLE_BG_DATA = "single_bg_data"
+ const val KEY_TREATMENTS_DATA = "treatments_data"
+ const val KEY_GRAPH_DATA = "graph_data"
+ const val KEY_STATUS_DATA = "status_data"
+
+ const val BOLUS_PROGRESS_NOTIF_ID = 1
+ const val CONFIRM_NOTIF_ID = 2
+ const val CHANGE_NOTIF_ID = 556677
+
+ const val AAPS_NOTIFY_CHANNEL_ID_OPEN_LOOP = "AndroidAPS-OpenLoop"
+ const val AAPS_NOTIFY_CHANNEL_ID_BOLUS_PROGRESS = "bolus progress vibration"
+ const val AAPS_NOTIFY_CHANNEL_ID_BOLUS_PROGRESS_SILENT = "bolus progress silent"
+ }
+}
\ No newline at end of file
diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/BaseComplicationProviderService.java b/wear/src/main/java/info/nightscout/androidaps/complications/BaseComplicationProviderService.java
index 7d65be3080..9593474390 100644
--- a/wear/src/main/java/info/nightscout/androidaps/complications/BaseComplicationProviderService.java
+++ b/wear/src/main/java/info/nightscout/androidaps/complications/BaseComplicationProviderService.java
@@ -22,15 +22,18 @@ import javax.inject.Inject;
import dagger.android.AndroidInjection;
import info.nightscout.androidaps.R;
-import info.nightscout.androidaps.data.DataLayerListenerService;
+import info.nightscout.androidaps.comm.DataLayerListenerServiceWear;
import info.nightscout.androidaps.data.RawDisplayData;
+import info.nightscout.androidaps.events.EventWearToMobile;
import info.nightscout.androidaps.interaction.utils.Constants;
import info.nightscout.androidaps.interaction.utils.DisplayFormat;
import info.nightscout.androidaps.interaction.utils.Inevitable;
import info.nightscout.androidaps.interaction.utils.Persistence;
import info.nightscout.androidaps.interaction.utils.WearUtil;
+import info.nightscout.androidaps.plugins.bus.RxBus;
import info.nightscout.shared.logging.AAPSLogger;
import info.nightscout.shared.logging.LTag;
+import info.nightscout.shared.weardata.EventData;
/**
* Base class for all complications
@@ -44,6 +47,7 @@ public abstract class BaseComplicationProviderService extends ComplicationProvid
@Inject DisplayFormat displayFormat;
@Inject Persistence persistence;
@Inject AAPSLogger aapsLogger;
+ @Inject RxBus rxBus;
// Not derived from DaggerService, do injection here
@Override
@@ -199,13 +203,13 @@ public abstract class BaseComplicationProviderService extends ComplicationProvid
persistence.putBoolean("complication_" + complicationId + "_since", usesSinceField());
persistence.addToSet(KEY_COMPLICATIONS, "complication_" + complicationId);
- IntentFilter messageFilter = new IntentFilter(Intent.ACTION_SEND);
+ IntentFilter messageFilter = new IntentFilter(DataLayerListenerServiceWear.Companion.getINTENT_NEW_DATA());
messageReceiver = new MessageReceiver();
localBroadcastManager = LocalBroadcastManager.getInstance(this);
localBroadcastManager.registerReceiver(messageReceiver, messageFilter);
- DataLayerListenerService.Companion.requestData(this);
+ rxBus.send(new EventWearToMobile(new EventData.ActionResendData("BaseComplicationProviderService")));
checkIfUpdateNeeded();
}
@@ -232,12 +236,13 @@ public abstract class BaseComplicationProviderService extends ComplicationProvid
ComplicationTapBroadcastReceiver.getTapActionIntent(
getApplicationContext(), thisProvider, complicationId, getComplicationAction());
- final RawDisplayData raw = new RawDisplayData(wearUtil);
+ final RawDisplayData raw = new RawDisplayData();
raw.updateForComplicationsFromPersistence(persistence);
aapsLogger.warn(LTag.WEAR, "Complication data: " + raw.toDebugString());
// store what is currently rendered in 'SGV since' field, to detect if it was changed and need update
- persistence.putString(KEY_LAST_SHOWN_SINCE_VALUE, displayFormat.shortTimeSince(raw.datetime));
+ persistence.putString(KEY_LAST_SHOWN_SINCE_VALUE,
+ displayFormat.shortTimeSince(raw.getSingleBg().getTimeStamp()));
// by each render we clear stale flag to ensure it is re-rendered at next refresh detection round
persistence.putBoolean(KEY_STALE_REPORTED, false);
@@ -249,11 +254,11 @@ public abstract class BaseComplicationProviderService extends ComplicationProvid
final PendingIntent infoToast = ComplicationTapBroadcastReceiver.getTapWarningSinceIntent(
getApplicationContext(), thisProvider, complicationId, ComplicationAction.WARNING_SYNC, persistence.whenDataUpdated());
complicationData = buildNoSyncComplicationData(dataType, raw, complicationPendingIntent, infoToast, persistence.whenDataUpdated());
- } else if (wearUtil.msSince(raw.datetime) > Constants.STALE_MS) {
+ } else if (wearUtil.msSince(raw.getSingleBg().getTimeStamp()) > Constants.STALE_MS) {
// data arriving from phone AAPS, but it is outdated (uploader/NS/xDrip/Sensor error)
final PendingIntent infoToast = ComplicationTapBroadcastReceiver.getTapWarningSinceIntent(
- getApplicationContext(), thisProvider, complicationId, ComplicationAction.WARNING_OLD, raw.datetime);
- complicationData = buildOutdatedComplicationData(dataType, raw, complicationPendingIntent, infoToast, raw.datetime);
+ getApplicationContext(), thisProvider, complicationId, ComplicationAction.WARNING_OLD, raw.getSingleBg().getTimeStamp());
+ complicationData = buildOutdatedComplicationData(dataType, raw, complicationPendingIntent, infoToast, raw.getSingleBg().getTimeStamp());
} else {
// data is up-to-date, we can render standard complication
complicationData = buildComplicationData(dataType, raw, complicationPendingIntent);
@@ -310,13 +315,13 @@ public abstract class BaseComplicationProviderService extends ComplicationProvid
* is up-to-date or need to be changed (a minute or more elapsed)
*/
private void requestUpdateIfSinceChanged() {
- final RawDisplayData raw = new RawDisplayData(wearUtil);
+ final RawDisplayData raw = new RawDisplayData();
raw.updateForComplicationsFromPersistence(persistence);
final String lastSince = persistence.getString(KEY_LAST_SHOWN_SINCE_VALUE, "-");
- final String calcSince = displayFormat.shortTimeSince(raw.datetime);
+ final String calcSince = displayFormat.shortTimeSince(raw.getSingleBg().getTimeStamp());
final boolean isStale = (wearUtil.msSince(persistence.whenDataUpdated()) > Constants.STALE_MS)
- || (wearUtil.msSince(raw.datetime) > Constants.STALE_MS);
+ || (wearUtil.msSince(raw.getSingleBg().getTimeStamp()) > Constants.STALE_MS);
final boolean staleWasRefreshed = persistence.getBoolean(KEY_STALE_REPORTED, false);
final boolean sinceWasChanged = !lastSince.equals(calcSince);
diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/BrCobIobComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/BrCobIobComplication.java
index 10295844c4..bdbc079793 100644
--- a/wear/src/main/java/info/nightscout/androidaps/complications/BrCobIobComplication.java
+++ b/wear/src/main/java/info/nightscout/androidaps/complications/BrCobIobComplication.java
@@ -3,7 +3,6 @@ package info.nightscout.androidaps.complications;
import android.app.PendingIntent;
import android.support.wearable.complications.ComplicationData;
import android.support.wearable.complications.ComplicationText;
-import android.util.Log;
import javax.inject.Inject;
@@ -32,11 +31,11 @@ public class BrCobIobComplication extends BaseComplicationProviderService {
ComplicationData complicationData = null;
if (dataType == ComplicationData.TYPE_SHORT_TEXT) {
- final String cob = new SmallestDoubleString(raw.sCOB2, SmallestDoubleString.Units.USE).minimise(displayFormat.MIN_FIELD_LEN_COB);
- final String iob = new SmallestDoubleString(raw.sIOB1, SmallestDoubleString.Units.USE).minimise(Math.max(displayFormat.MIN_FIELD_LEN_IOB, (displayFormat.MAX_FIELD_LEN_SHORT - 1) - cob.length()));
+ final String cob = new SmallestDoubleString(raw.getStatus().getCob(), SmallestDoubleString.Units.USE).minimise(displayFormat.MIN_FIELD_LEN_COB);
+ final String iob = new SmallestDoubleString(raw.getStatus().getIobSum(), SmallestDoubleString.Units.USE).minimise(Math.max(displayFormat.MIN_FIELD_LEN_IOB, (displayFormat.MAX_FIELD_LEN_SHORT - 1) - cob.length()));
final ComplicationData.Builder builder = new ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
- .setShortText(ComplicationText.plainText(displayFormat.basalRateSymbol() + raw.sBasalRate))
+ .setShortText(ComplicationText.plainText(displayFormat.basalRateSymbol() + raw.getStatus().getCurrentBasal()))
.setShortTitle(ComplicationText.plainText(cob + " " + iob))
.setTapAction(complicationPendingIntent);
diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/CobIconComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/CobIconComplication.java
index 98ffdb1e56..c679635e4c 100644
--- a/wear/src/main/java/info/nightscout/androidaps/complications/CobIconComplication.java
+++ b/wear/src/main/java/info/nightscout/androidaps/complications/CobIconComplication.java
@@ -20,7 +20,7 @@ public class CobIconComplication extends BaseComplicationProviderService {
if (dataType == ComplicationData.TYPE_SHORT_TEXT) {
final ComplicationData.Builder builder = new ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
- .setShortText(ComplicationText.plainText(raw.sCOB2))
+ .setShortText(ComplicationText.plainText(raw.getStatus().getCob()))
.setIcon(
Icon.createWithResource(
this, R.drawable.ic_carbs))
diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/CobIobComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/CobIobComplication.java
index 62109b1dfc..411b1e943b 100644
--- a/wear/src/main/java/info/nightscout/androidaps/complications/CobIobComplication.java
+++ b/wear/src/main/java/info/nightscout/androidaps/complications/CobIobComplication.java
@@ -18,8 +18,8 @@ public class CobIobComplication extends BaseComplicationProviderService {
ComplicationData complicationData = null;
if (dataType == ComplicationData.TYPE_SHORT_TEXT) {
- final String cob = raw.sCOB2;
- final String iob = new SmallestDoubleString(raw.sIOB1, SmallestDoubleString.Units.USE).minimise(displayFormat.MAX_FIELD_LEN_SHORT);
+ final String cob = raw.getStatus().getCob();
+ final String iob = new SmallestDoubleString(raw.getStatus().getIobSum(), SmallestDoubleString.Units.USE).minimise(displayFormat.MAX_FIELD_LEN_SHORT);
final ComplicationData.Builder builder = new ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
.setShortText(ComplicationText.plainText(cob))
diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/IobIconComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/IobIconComplication.java
index ce76524fb9..632ac70c05 100644
--- a/wear/src/main/java/info/nightscout/androidaps/complications/IobIconComplication.java
+++ b/wear/src/main/java/info/nightscout/androidaps/complications/IobIconComplication.java
@@ -20,7 +20,7 @@ public class IobIconComplication extends BaseComplicationProviderService {
ComplicationData complicationData = null;
if (dataType == ComplicationData.TYPE_SHORT_TEXT) {
- final String iob = new SmallestDoubleString(raw.sIOB1, SmallestDoubleString.Units.USE).minimise(displayFormat.MAX_FIELD_LEN_SHORT);
+ final String iob = new SmallestDoubleString(raw.getStatus().getIobSum(), SmallestDoubleString.Units.USE).minimise(displayFormat.MAX_FIELD_LEN_SHORT);
final ComplicationData.Builder builder = new ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
.setShortText(ComplicationText.plainText(iob))
diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/SgvComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/SgvComplication.java
index d09a58a7ea..62d08ad8ba 100644
--- a/wear/src/main/java/info/nightscout/androidaps/complications/SgvComplication.java
+++ b/wear/src/main/java/info/nightscout/androidaps/complications/SgvComplication.java
@@ -32,7 +32,8 @@ public class SgvComplication extends BaseComplicationProviderService {
switch (dataType) {
case ComplicationData.TYPE_SHORT_TEXT:
final ComplicationData.Builder builder = new ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
- .setShortText(ComplicationText.plainText(raw.sSgv + raw.sDirection + "\uFE0E"))
+ .setShortText(ComplicationText.plainText(raw.getSingleBg().getSgvString() + raw.getSingleBg().getSlopeArrow() +
+ "\uFE0E"))
.setShortTitle(ComplicationText.plainText(displayFormat.shortTrend(raw)))
.setTapAction(complicationPendingIntent);
diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/UploaderBattery.java b/wear/src/main/java/info/nightscout/androidaps/complications/UploaderBatteryComplication.java
similarity index 95%
rename from wear/src/main/java/info/nightscout/androidaps/complications/UploaderBattery.java
rename to wear/src/main/java/info/nightscout/androidaps/complications/UploaderBatteryComplication.java
index 10f214ee33..6211641bb8 100644
--- a/wear/src/main/java/info/nightscout/androidaps/complications/UploaderBattery.java
+++ b/wear/src/main/java/info/nightscout/androidaps/complications/UploaderBatteryComplication.java
@@ -14,7 +14,7 @@ import info.nightscout.shared.logging.LTag;
/*
* Created by dlvoy on 2019-11-12
*/
-public class UploaderBattery extends BaseComplicationProviderService {
+public class UploaderBatteryComplication extends BaseComplicationProviderService {
public ComplicationData buildComplicationData(int dataType, RawDisplayData raw, PendingIntent complicationPendingIntent) {
@@ -25,9 +25,9 @@ public class UploaderBattery extends BaseComplicationProviderService {
int level = 0;
String levelStr = "???";
- if (raw.sUploaderBattery.matches("^[0-9]+$")) {
+ if (raw.getStatus().getBattery().matches("^[0-9]+$")) {
try {
- level = Integer.parseInt(raw.sUploaderBattery);
+ level = Integer.parseInt(raw.getStatus().getBattery());
level = Math.max(Math.min(level, 100), 0);
levelStr = level + "%";
int iconNo = (int) Math.floor(level / 10.0);
@@ -112,7 +112,7 @@ public class UploaderBattery extends BaseComplicationProviderService {
} catch (NumberFormatException ex) {
- aapsLogger.error(LTag.WEAR, "Cannot parse battery level of: " + raw.sUploaderBattery);
+ aapsLogger.error(LTag.WEAR, "Cannot parse battery level of: " + raw.getStatus().getBattery());
}
}
@@ -147,7 +147,7 @@ public class UploaderBattery extends BaseComplicationProviderService {
@Override
public String getProviderCanonicalName() {
- return UploaderBattery.class.getCanonicalName();
+ return UploaderBatteryComplication.class.getCanonicalName();
}
@Override
diff --git a/wear/src/main/java/info/nightscout/androidaps/data/BasalWatchData.java b/wear/src/main/java/info/nightscout/androidaps/data/BasalWatchData.java
deleted file mode 100644
index ebed526ec8..0000000000
--- a/wear/src/main/java/info/nightscout/androidaps/data/BasalWatchData.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package info.nightscout.androidaps.data;
-
-/**
- * Created by adrian on 18/11/16.
- */
-
-public class BasalWatchData {
- public long startTime;
- public long endTime;
- public double amount;
-}
diff --git a/wear/src/main/java/info/nightscout/androidaps/data/BgWatchData.java b/wear/src/main/java/info/nightscout/androidaps/data/BgWatchData.java
deleted file mode 100644
index 4f258ee074..0000000000
--- a/wear/src/main/java/info/nightscout/androidaps/data/BgWatchData.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package info.nightscout.androidaps.data;
-
-import java.util.Objects;
-
-/**
- * Created by emmablack on 1/7/15.
- */
-public class BgWatchData implements Comparable{
- public double sgv;
- public double high;
- public double low;
- public long timestamp;
- public int color;
-
- public BgWatchData(double aSgv, double aHigh, double aLow, long aTimestamp, int aColor) {
- this.sgv = aSgv;
- this.high = aHigh;
- this.low = aLow;
- this.timestamp = aTimestamp;
- this.color = aColor;
- }
-
- public BgWatchData(){
-
- }
-
- @Override
- public boolean equals(Object that){
- if(! (that instanceof BgWatchData)){
- return false;
- }
- if (this.color != ((BgWatchData) that).color)
- return false;
- return this.timestamp == ((BgWatchData) that).timestamp;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(timestamp, color);
- }
-
- @Override
- public int compareTo(BgWatchData that) {
- // reverse order endTime get latest first
- if(this.timestamp < that.timestamp) return 1;
- if(this.timestamp > that.timestamp) return -1;
- return 0;
- }
-}
diff --git a/wear/src/main/java/info/nightscout/androidaps/data/BolusWatchData.java b/wear/src/main/java/info/nightscout/androidaps/data/BolusWatchData.java
deleted file mode 100644
index 6a474f3632..0000000000
--- a/wear/src/main/java/info/nightscout/androidaps/data/BolusWatchData.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package info.nightscout.androidaps.data;
-
-/**
- * Created by adrian on 17/11/16.
- */
-
-public class BolusWatchData {
- public long date;
- public double bolus;
- public double carbs;
- public boolean isSMB;
- public boolean isValid;
-}
diff --git a/wear/src/main/java/info/nightscout/androidaps/data/DataLayerListenerService.kt b/wear/src/main/java/info/nightscout/androidaps/data/DataLayerListenerService.kt
deleted file mode 100644
index 7cf6bb7840..0000000000
--- a/wear/src/main/java/info/nightscout/androidaps/data/DataLayerListenerService.kt
+++ /dev/null
@@ -1,552 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package info.nightscout.androidaps.data
-
-import android.annotation.TargetApi
-import android.app.Notification
-import android.app.NotificationChannel
-import android.app.NotificationManager
-import android.app.PendingIntent
-import android.content.Context
-import android.content.Intent
-import android.os.Build
-import android.os.Bundle
-import android.os.Handler
-import android.os.HandlerThread
-import android.os.SystemClock
-import android.util.Base64
-import androidx.core.app.NotificationCompat
-import androidx.core.app.NotificationManagerCompat
-import androidx.localbroadcastmanager.content.LocalBroadcastManager
-import androidx.wear.tiles.TileService
-import com.google.android.gms.tasks.Tasks
-import com.google.android.gms.wearable.*
-import dagger.android.AndroidInjection
-import info.nightscout.androidaps.R
-import info.nightscout.androidaps.events.EventWearToMobileAction
-import info.nightscout.androidaps.events.EventWearToMobileChange
-import info.nightscout.androidaps.events.EventWearToMobileConfirm
-import info.nightscout.androidaps.interaction.AAPSPreferences
-import info.nightscout.androidaps.interaction.actions.AcceptActivity
-import info.nightscout.androidaps.interaction.actions.CPPActivity
-import info.nightscout.androidaps.interaction.utils.Persistence
-import info.nightscout.androidaps.interaction.utils.WearUtil
-import info.nightscout.androidaps.plugins.bus.RxBus
-import info.nightscout.androidaps.tile.ActionsTileService
-import info.nightscout.androidaps.tile.QuickWizardTileService
-import info.nightscout.androidaps.tile.TempTargetTileService
-import info.nightscout.androidaps.utils.rx.AapsSchedulers
-import info.nightscout.shared.SafeParse.stringToInt
-import info.nightscout.shared.logging.AAPSLogger
-import info.nightscout.shared.logging.LTag
-import info.nightscout.shared.sharedPreferences.SP
-import info.nightscout.shared.weardata.ActionData
-import info.nightscout.shared.weardata.WearConstants
-import info.nightscout.shared.weardata.WearConstants.Companion.KEY_ACTION_DATA
-import io.reactivex.rxjava3.disposables.CompositeDisposable
-import io.reactivex.rxjava3.kotlin.plusAssign
-import kotlinx.coroutines.*
-import kotlinx.coroutines.tasks.await
-import javax.inject.Inject
-
-class DataLayerListenerService : WearableListenerService() {
-
- @Inject lateinit var aapsLogger: AAPSLogger
- @Inject lateinit var wearUtil: WearUtil
- @Inject lateinit var persistence: Persistence
- @Inject lateinit var sp: SP
- @Inject lateinit var rxBus: RxBus
- @Inject lateinit var aapsSchedulers: AapsSchedulers
- @Inject lateinit var wearConstants: WearConstants
-
- private val dataClient by lazy { Wearable.getDataClient(this) }
- private val messageClient by lazy { Wearable.getMessageClient(this) }
- private val capabilityClient by lazy { Wearable.getCapabilityClient(this) }
- //private val nodeClient by lazy { Wearable.getNodeClient(this) }
-
- private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
- private var handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper)
-
- private val disposable = CompositeDisposable()
-
- override fun onCreate() {
- AndroidInjection.inject(this)
- super.onCreate()
- handler.post { updateTranscriptionCapability() }
- disposable += rxBus
- .toObservable(EventWearToMobileAction::class.java)
- .observeOn(aapsSchedulers.io)
- .subscribe { sendMessage(wearConstants.W_M_INITIATE_ACTION, it.actionData.serialize().toByteArray()) }
- disposable += rxBus
- .toObservable(EventWearToMobileConfirm::class.java)
- .observeOn(aapsSchedulers.io)
- .subscribe {
- NotificationManagerCompat.from(this).cancel(CONFIRM_NOTIF_ID)
- sendMessage(wearConstants.W_M_CONFIRM_ACTION, it.actionData.serialize().toByteArray())
- }
- disposable += rxBus
- .toObservable(EventWearToMobileChange::class.java)
- .observeOn(aapsSchedulers.io)
- .subscribe {
- NotificationManagerCompat.from(this).cancel(CHANGE_NOTIF_ID)
- sendMessage(wearConstants.W_M_CONFIRM_ACTION, it.actionData.serialize().toByteArray())
- }
- }
-
- override fun onPeerConnected(p0: Node) {
- super.onPeerConnected(p0)
- }
-
- override fun onCapabilityChanged(p0: CapabilityInfo) {
- super.onCapabilityChanged(p0)
- handler.post { updateTranscriptionCapability() }
- aapsLogger.debug(LTag.WEAR, "onCapabilityChanged: ${p0.name} ${p0.nodes.joinToString(", ") { it.displayName + "(" + it.id + ")" }}")
- }
-
- override fun onDestroy() {
- super.onDestroy()
- scope.cancel()
- disposable.clear()
- }
-
- override fun onDataChanged(dataEvents: DataEventBuffer) {
- //aapsLogger.debug(LTag.WEAR, "onDataChanged")
-
- dataEvents.forEach { event ->
- if (event.type == DataEvent.TYPE_CHANGED) {
- val path = event.dataItem.uri.path
-
- aapsLogger.debug(LTag.WEAR, "onDataChanged: Path: $path, EventDataItem=${event.dataItem}")
- try {
- when (path) {
- wearConstants.M_W_BOLUS_PROGRESS -> {
- val progress = DataMapItem.fromDataItem(event.dataItem).dataMap.getInt("progresspercent", 0)
- val status = DataMapItem.fromDataItem(event.dataItem).dataMap.getString("progressstatus", "")
- showBolusProgress(progress, status)
- }
- // remove when finished -> converted to message
- wearConstants.M_W_ACTION_CONFIRMATION_REQUEST -> {
- val title = DataMapItem.fromDataItem(event.dataItem).dataMap.getString("title") ?: return@forEach
- val message = DataMapItem.fromDataItem(event.dataItem).dataMap.getString("message") ?: return@forEach
- val actionstring = DataMapItem.fromDataItem(event.dataItem).dataMap.getString("actionstring") ?: return@forEach
- if ("opencpp" == title && actionstring.startsWith("opencpp")) {
- val act = actionstring.split("\\s+").toTypedArray()
- startActivity(Intent(this@DataLayerListenerService, CPPActivity::class.java).also { intent ->
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- intent.putExtras(Bundle().also {
- it.putInt("percentage", stringToInt(act[1]))
- it.putInt("timeshift", stringToInt(act[2]))
- })
- })
- } else {
- showConfirmationDialog(title, message, actionstring)
- }
- }
-
- wearConstants.M_W_STATUS -> {
- val dataMap = DataMapItem.fromDataItem(event.dataItem).dataMap
- val messageIntent = Intent()
- messageIntent.action = Intent.ACTION_SEND
- messageIntent.putExtra("status", dataMap.toBundle())
- persistence.storeDataMap(RawDisplayData.STATUS_PERSISTENCE_KEY, dataMap)
- LocalBroadcastManager.getInstance(this@DataLayerListenerService).sendBroadcast(messageIntent)
- }
-
- wearConstants.M_W_BASAL -> {
- val dataMap = DataMapItem.fromDataItem(event.dataItem).dataMap
- val messageIntent = Intent()
- messageIntent.action = Intent.ACTION_SEND
- messageIntent.putExtra("basals", dataMap.toBundle())
- persistence.storeDataMap(RawDisplayData.BASALS_PERSISTENCE_KEY, dataMap)
- LocalBroadcastManager.getInstance(this@DataLayerListenerService).sendBroadcast(messageIntent)
- }
-
- wearConstants.M_W_PREFERENCES -> {
- val dataMap = DataMapItem.fromDataItem(event.dataItem).dataMap
- val keyControl = getString(R.string.key_wear_control)
- if (dataMap.containsKey(keyControl)) {
- val previousWearControl = sp.getBoolean(keyControl, false)
- val wearControl: Boolean = dataMap.getBoolean(keyControl, false)
- sp.putBoolean(keyControl, wearControl)
- if (wearControl != previousWearControl) {
- updateTiles()
- }
- }
- val keyPercentage = getString(R.string.key_boluswizard_percentage)
- if (dataMap.containsKey(keyPercentage)) {
- val wpercentage: Int = dataMap.getInt(keyPercentage, 100)
- sp.putInt(keyPercentage, wpercentage)
- }
- val keyUnits = getString(R.string.key_units_mgdl)
- if (dataMap.containsKey(keyUnits)) {
- val mgdl: Boolean = dataMap.getBoolean(keyUnits, true)
- sp.putBoolean(keyUnits, mgdl)
- }
- val keyMaxCarbs = getString(R.string.key_treatmentssafety_maxcarbs)
- if (dataMap.containsKey(keyMaxCarbs)) {
- val maxCarbs: Int = dataMap.getInt(keyMaxCarbs, 48)
- sp.putInt(keyMaxCarbs, maxCarbs)
- }
- val keyMaxBolus = getString(R.string.key_treatmentssafety_maxbolus)
- if (dataMap.containsKey(keyMaxBolus)) {
- sp.putDouble(keyMaxBolus, dataMap.getDouble(keyMaxBolus, 3.0))
- }
- }
-
- wearConstants.M_W_QUICK_WIZARD -> {
- val dataMap = DataMapItem.fromDataItem(event.dataItem).dataMap
- aapsLogger.info(LTag.WEAR, "onDataChanged: QUICK_WIZARD_PATH$dataMap")
- dataMap.remove("timestamp")
- val key = getString(R.string.key_quick_wizard_data_map)
- val dataString = Base64.encodeToString(dataMap.toByteArray(), Base64.DEFAULT)
- if (dataString != sp.getString(key, "")) {
- sp.putString(key, dataString)
- // Todo maybe add debounce function, due to 20 seconds update limit?
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- TileService.getUpdater(this@DataLayerListenerService).requestUpdate(QuickWizardTileService::class.java)
- }
- aapsLogger.info(LTag.WEAR, "onDataChanged: updated QUICK_WIZARD")
- } else {
- aapsLogger.info(LTag.WEAR, "onDataChanged: ignore update")
- }
- }
-
- wearConstants.M_W_ACTION_CHANGE_CONFIRMATION_REQUEST -> {
- val title = DataMapItem.fromDataItem(event.dataItem).dataMap.getString("title") ?: return@forEach
- val message = DataMapItem.fromDataItem(event.dataItem).dataMap.getString("message") ?: return@forEach
- val actionstring = DataMapItem.fromDataItem(event.dataItem).dataMap.getString("actionstring") ?: return@forEach
- notifyChangeRequest(title, message, actionstring)
- }
-
- wearConstants.M_W_ACTION_CANCEL_NOTIFICATION_REQUEST -> {
- //val actionstring = DataMapItem.fromDataItem(event.getDataItem()).dataMap.getString("actionstring") ?: return@forEach
- cancelNotificationRequest()
- }
-
- wearConstants.M_W_DATA -> {
- val dataMap = DataMapItem.fromDataItem(event.dataItem).dataMap
- val messageIntent = Intent()
- messageIntent.action = Intent.ACTION_SEND
- messageIntent.putExtra("data", dataMap.toBundle())
- persistence.storeDataMap(RawDisplayData.DATA_PERSISTENCE_KEY, dataMap)
- LocalBroadcastManager.getInstance(this@DataLayerListenerService).sendBroadcast(messageIntent)
- }
- }
- } catch (exception: Exception) {
- aapsLogger.error(LTag.WEAR, "onDataChanged failed", exception)
- }
- }
- }
- super.onDataChanged(dataEvents)
- }
-
- override fun onMessageReceived(messageEvent: MessageEvent) {
- super.onMessageReceived(messageEvent)
- aapsLogger.debug(LTag.WEAR, "onMessageReceived: $messageEvent")
-
- when (messageEvent.path) {
- wearConstants.M_W_PING -> sendMessage(wearConstants.W_M_PONG, byteArrayOf())
- wearConstants.M_W_OPEN_SETTINGS -> startActivity(Intent(this@DataLayerListenerService, AAPSPreferences::class.java).also { it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) })
-
- wearConstants.M_W_ACTION_CONFIRMATION_REQUEST -> {
- val command = ActionData.deserialize(String(messageEvent.data)) as ActionData.ConfirmAction
- if (command.originalCommand is ActionData.OpenProfileSwitch) {
- val originalCommand = command.originalCommand as ActionData.OpenProfileSwitch
- startActivity(Intent(this, CPPActivity::class.java).also { intent ->
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- intent.putExtras(Bundle().also {
- it.putInt("percentage", originalCommand.percentage)
- it.putInt("timeshift", originalCommand.timeShift)
- })
- })
- } else {
- startActivity(
- Intent(this, AcceptActivity::class.java).also { intent ->
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- intent.putExtras(
- Bundle().also { bundle ->
- bundle.putString("title", command.title)
- bundle.putString("message", command.message)
- bundle.putString(KEY_ACTION_DATA, command.originalCommand.serialize())
- }
- )
- })
- }
- }
- }
- }
-
- override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
- when (intent?.action) {
- ACTION_RESEND -> sendMessage(wearConstants.W_M_RESEND_DATA, byteArrayOf())
-
- ACTION_CANCEL_BOLUS -> {
- //dismiss notification
- val notificationManager = NotificationManagerCompat.from(this)
- notificationManager.cancel(BOLUS_PROGRESS_NOTIF_ID)
- //send cancel-request to phone.
- sendMessage(wearConstants.W_M_CANCEL_BOLUS, byteArrayOf())
- }
-
- ACTION_CONFIRMATION -> {
- //dismiss notification
- val notificationManager = NotificationManagerCompat.from(this)
- notificationManager.cancel(CONFIRM_NOTIF_ID)
- intent.getStringExtra("actionstring")?.let { actionString ->
- sendMessage(wearConstants.W_M_CONFIRM_ACTION, actionString.toByteArray())
- }
- }
-
- ACTION_CONFIRM_CHANGE -> {
- //dismiss notification
- val notificationManager = NotificationManagerCompat.from(this)
- notificationManager.cancel(CHANGE_NOTIF_ID)
- intent.getStringExtra("actionstring")?.let { actionString ->
- sendMessage(wearConstants.W_M_CONFIRM_ACTION, actionString.toByteArray())
- }
- }
-
- ACTION_INITIATE_ACTION ->
- if (intent.hasExtra("actionstring"))
- intent.getStringExtra("actionstring")?.let { actionString ->
- sendMessage(wearConstants.W_M_INITIATE_ACTION, actionString.toByteArray())
- }
- else if (intent.hasExtra(KEY_ACTION_DATA))
- intent.getStringExtra(KEY_ACTION_DATA)?.let { actionData ->
- sendMessage(wearConstants.W_M_INITIATE_ACTION, actionData.toByteArray())
- }
- }
- return START_STICKY
- }
-
- private var transcriptionNodeId: String? = null
-
- private fun updateTranscriptionCapability() {
- val capabilityInfo: CapabilityInfo = Tasks.await(
- capabilityClient.getCapability(PHONE_CAPABILITY, CapabilityClient.FILTER_REACHABLE)
- )
- aapsLogger.debug(LTag.WEAR, "Nodes: ${capabilityInfo.nodes.joinToString(", ") { it.displayName + "(" + it.id + ")" }}")
- transcriptionNodeId = pickBestNodeId(capabilityInfo.nodes)
- aapsLogger.debug(LTag.WEAR, "Selected node: $transcriptionNodeId")
- }
-
- // Find a nearby node or pick one arbitrarily
- private fun pickBestNodeId(nodes: Set): String? =
- nodes.firstOrNull { it.isNearby }?.id ?: nodes.firstOrNull()?.id
-
- private fun sendData(path: String, vararg params: DataMap) {
- scope.launch {
- try {
- for (dm in params) {
- val request = PutDataMapRequest.create(path).apply {
- dataMap.putAll(dm)
- }
- .asPutDataRequest()
- .setUrgent()
-
- val result = dataClient.putDataItem(request).await()
- aapsLogger.debug(LTag.WEAR, "sendData: ${result.uri} ${params.joinToString()}")
- }
- } catch (cancellationException: CancellationException) {
- throw cancellationException
- } catch (exception: Exception) {
- aapsLogger.error(LTag.WEAR, "DataItem failed: $exception")
- }
- }
- }
-
- private fun sendMessage(path: String, data: ByteArray) {
- aapsLogger.debug(LTag.WEAR, "sendMessage: $path")
- transcriptionNodeId?.also { nodeId ->
- messageClient
- .sendMessage(nodeId, path, data).apply {
- addOnSuccessListener { }
- addOnFailureListener {
- aapsLogger.debug(LTag.WEAR, "sendMessage: $path failure")
- }
- }
- }
- }
-
- private fun updateTiles() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- TileService.getUpdater(this)
- .requestUpdate(ActionsTileService::class.java)
- TileService.getUpdater(this)
- .requestUpdate(TempTargetTileService::class.java)
- TileService.getUpdater(this)
- .requestUpdate(QuickWizardTileService::class.java)
- }
- }
-
- private fun notifyChangeRequest(title: String, message: String, actionstring: String) {
- // Create the NotificationChannel, but only on API 26+ because
- // the NotificationChannel class is new and not in the support library
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- val name: CharSequence = "AAPS Open Loop"
- val description = "Open Loop request notification"
- val channel = NotificationChannel(AAPS_NOTIFY_CHANNEL_ID_OPENLOOP, name, NotificationManager.IMPORTANCE_HIGH)
- channel.description = description
- channel.enableVibration(true)
-
- // Register the channel with the system; you can't change the importance
- // or other notification behaviors after this
- val notificationManager = getSystemService(NotificationManager::class.java)
- notificationManager.createNotificationChannel(channel)
- }
- var builder = NotificationCompat.Builder(this, AAPS_NOTIFY_CHANNEL_ID_OPENLOOP)
- builder = builder.setSmallIcon(R.drawable.notif_icon)
- .setContentTitle(title)
- .setContentText(message)
- .setPriority(Notification.PRIORITY_HIGH)
- .setVibrate(longArrayOf(1000, 1000, 1000, 1000, 1000))
-
- // Creates an explicit intent for an Activity in your app
- val intent = Intent(this, AcceptActivity::class.java)
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- val params = Bundle()
- params.putString("title", title)
- params.putString("message", message)
- params.putString("actionstring", actionstring)
- intent.putExtras(params)
- val resultPendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
- builder = builder.setContentIntent(resultPendingIntent)
- val mNotificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
- // mId allows you to update the notification later on.
- mNotificationManager.notify(CHANGE_NOTIF_ID, builder.build())
- }
-
- private fun cancelNotificationRequest() {
- (getSystemService(NOTIFICATION_SERVICE) as NotificationManager).cancel(CHANGE_NOTIF_ID)
- }
-
- private fun showBolusProgress(progressPercent: Int, progresStatus: String) {
- val vibratePattern: LongArray
- val vibrate = sp.getBoolean("vibrateOnBolus", true)
- vibratePattern = if (vibrate) longArrayOf(0, 50, 1000) else longArrayOf(0, 1, 1000)
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- createBolusProgressChannels()
- }
- val cancelIntent = Intent(this, DataLayerListenerService::class.java)
- cancelIntent.action = ACTION_CANCEL_BOLUS
- val cancelPendingIntent = PendingIntent.getService(this, 0, cancelIntent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
- val notificationBuilder: NotificationCompat.Builder =
- NotificationCompat.Builder(this, if (vibrate) AAPS_NOTIFY_CHANNEL_ID_BOLUSPROGRESS else AAPS_NOTIFY_CHANNEL_ID_BOLUSPROGRESS_SILENT)
- .setSmallIcon(R.drawable.ic_icon)
- .setContentTitle(getString(R.string.bolus_progress))
- .setContentText("$progressPercent% - $progresStatus")
- .setSubText(getString(R.string.press_to_cancel))
- .setContentIntent(cancelPendingIntent)
- .setPriority(NotificationCompat.PRIORITY_MAX)
- .setVibrate(vibratePattern)
- .addAction(R.drawable.ic_cancel, getString(R.string.cancel_bolus), cancelPendingIntent)
- val notificationManager = NotificationManagerCompat.from(this)
- notificationManager.notify(BOLUS_PROGRESS_NOTIF_ID, notificationBuilder.build())
- notificationManager.cancel(CONFIRM_NOTIF_ID) // multiple watch setup
- if (progressPercent == 100) {
- scheduleDismissBolusProgress(5)
- }
- }
-
- @TargetApi(value = 26) private fun createBolusProgressChannels() {
- createNotificationChannel(
- longArrayOf(0, 50, 1000),
- AAPS_NOTIFY_CHANNEL_ID_BOLUSPROGRESS,
- getString(R.string.bolus_progress_channel_name),
- getString(R.string.bolus_progress_channel_description)
- )
- createNotificationChannel(
- longArrayOf(0, 1, 1000),
- AAPS_NOTIFY_CHANNEL_ID_BOLUSPROGRESS_SILENT,
- getString(R.string.bolus_progress_silent_channel_name),
- getString(R.string.bolus_progress_silent_channel_description)
- )
- }
-
- @TargetApi(value = 26) private fun createNotificationChannel(vibratePattern: LongArray, channelID: String, name: CharSequence, description: String) {
- val channel = NotificationChannel(channelID, name, NotificationManager.IMPORTANCE_HIGH)
- channel.description = description
- channel.enableVibration(true)
- channel.vibrationPattern = vibratePattern
-
- // Register the channel with the system; you can't change the importance
- // or other notification behaviors after this
- val notificationManager = getSystemService(NotificationManager::class.java)
- notificationManager.createNotificationChannel(channel)
- }
-
- private fun showConfirmationDialog(title: String, message: String, actionstring: String) {
- val intent = Intent(this, AcceptActivity::class.java)
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- val params = Bundle()
- params.putString("title", title)
- params.putString("message", message)
- params.putString("actionstring", actionstring)
- intent.putExtras(params)
- startActivity(intent)
- }
-
- @Suppress("SameParameterValue")
- private fun scheduleDismissBolusProgress(seconds: Int) {
- Thread {
- SystemClock.sleep(seconds * 1000L)
- NotificationManagerCompat.from(this@DataLayerListenerService)
- .cancel(BOLUS_PROGRESS_NOTIF_ID)
- }.start()
- }
-
- companion object {
-
- const val PHONE_CAPABILITY = "androidaps_mobile"
-
- const val ACTION_RESEND = "com.dexdrip.stephenblack.nightwatch.RESEND_DATA"
- const val ACTION_CANCEL_BOLUS = "com.dexdrip.stephenblack.nightwatch.CANCELBOLUS"
- const val ACTION_CONFIRMATION = "com.dexdrip.stephenblack.nightwatch.CONFIRMACTION"
- const val ACTION_CONFIRM_CHANGE = "com.dexdrip.stephenblack.nightwatch.CONFIRMCHANGE"
- val ACTION_INITIATE_ACTION = DataLayerListenerService::class.java.name + ".INITIATE_ACTION"
-
- const val BOLUS_PROGRESS_NOTIF_ID = 1
- const val CONFIRM_NOTIF_ID = 2
- const val CHANGE_NOTIF_ID = 556677
-
- const val AAPS_NOTIFY_CHANNEL_ID_OPENLOOP = "AndroidAPS-OpenLoop"
- const val AAPS_NOTIFY_CHANNEL_ID_BOLUSPROGRESS = "bolus progress vibration"
- const val AAPS_NOTIFY_CHANNEL_ID_BOLUSPROGRESS_SILENT = "bolus progress silent"
-
- fun initiateAction(context: Context, actionstring: String) {
- context.startService(
- Intent(context, DataLayerListenerService::class.java).also {
- it.putExtra("actionstring", actionstring)
- it.action = ACTION_INITIATE_ACTION
- })
- }
-
- fun requestData(context: Context) {
- context.startService(
- Intent(context, DataLayerListenerService::class.java).also { it.action = ACTION_RESEND })
- }
-
- fun confirmAction(context: Context, actionstring: String) {
- context.startService(
- Intent(context, DataLayerListenerService::class.java).also {
- it.putExtra("actionstring", actionstring)
- if (actionstring == "changeRequest") it.action = ACTION_CONFIRM_CHANGE
- else it.action = ACTION_CONFIRMATION
- })
- }
- }
-}
diff --git a/wear/src/main/java/info/nightscout/androidaps/data/RawDisplayData.java b/wear/src/main/java/info/nightscout/androidaps/data/RawDisplayData.java
deleted file mode 100644
index a13c347a10..0000000000
--- a/wear/src/main/java/info/nightscout/androidaps/data/RawDisplayData.java
+++ /dev/null
@@ -1,281 +0,0 @@
-package info.nightscout.androidaps.data;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.PowerManager;
-
-import com.google.android.gms.wearable.DataMap;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-
-import info.nightscout.androidaps.interaction.utils.Constants;
-import info.nightscout.androidaps.interaction.utils.Persistence;
-import info.nightscout.androidaps.interaction.utils.WearUtil;
-
-/**
- * Holds bunch of data model variables and lists that arrive from phone app and are due to be
- * displayed on watchface and complications. Keeping them together makes code cleaner and allows
- * passing it to complications via persistence layer.
- *
- * Created by dlvoy on 2019-11-12
- */
-public class RawDisplayData {
-
- private final WearUtil wearUtil;
-
- public RawDisplayData(WearUtil wearUtil) {
- this.wearUtil = wearUtil;
- }
-
- static final String DATA_PERSISTENCE_KEY = "raw_data";
- static final String BASALS_PERSISTENCE_KEY = "raw_basals";
- static final String STATUS_PERSISTENCE_KEY = "raw_status";
-
- // data bundle
- public long sgvLevel = 0;
- public long datetime;
- public String sSgv = "---";
- public String sDirection = "--";
- public String sDelta = "--";
- public String sAvgDelta = "--";
- public String sUnits = "-";
-
- // status bundle
- public String sBasalRate = "-.--U/h";
- public String sUploaderBattery = "--";
- public String sRigBattery = "--";
- public boolean detailedIOB = false;
- public String sIOB1 = "IOB";
- public String sIOB2 = "-.--";
- public String sCOB1 = "Carb";
- public String sCOB2= "--g";
- public String sBgi = "--";
- public boolean showBGI = false;
- public String externalStatusString = "no status";
- public int batteryLevel = 1;
- public long openApsStatus = -1;
-
- // basals bundle
- public ArrayList bgDataList = new ArrayList<>();
- public ArrayList tempWatchDataList = new ArrayList<>();
- public ArrayList basalWatchDataList = new ArrayList<>();
- public ArrayList bolusWatchDataList = new ArrayList<>();
- public ArrayList predictionList = new ArrayList<>();
-
- public String toDebugString() {
- return "DisplayRawData{" +
- "sgvLevel=" + sgvLevel +
- ", datetime=" + datetime +
- ", sSgv='" + sSgv + '\'' +
- ", sDirection='" + sDirection + '\'' +
- ", sDelta='" + sDelta + '\'' +
- ", sAvgDelta='" + sAvgDelta + '\'' +
- ", sUnits='" + sUnits + '\'' +
- ", sBasalRate='" + sBasalRate + '\'' +
- ", sUploaderBattery='" + sUploaderBattery + '\'' +
- ", sRigBattery='" + sRigBattery + '\'' +
- ", detailedIOB=" + detailedIOB +
- ", sIOB1='" + sIOB1 + '\'' +
- ", sIOB2='" + sIOB2 + '\'' +
- ", sCOB1='" + sCOB1 + '\'' +
- ", sCOB2='" + sCOB2 + '\'' +
- ", sBgi='" + sBgi + '\'' +
- ", showBGI=" + showBGI +
- ", externalStatusString='" + externalStatusString + '\'' +
- ", batteryLevel=" + batteryLevel +
- ", openApsStatus=" + openApsStatus +
- ", bgDataList size=" + bgDataList.size() +
- ", tempWatchDataList size=" + tempWatchDataList.size() +
- ", basalWatchDataList size=" + basalWatchDataList.size() +
- ", bolusWatchDataLis size=" + bolusWatchDataList.size() +
- ", predictionList size=" + predictionList.size() +
- '}';
- }
-
- public void updateFromPersistence(Persistence persistence) {
-
- DataMap dataMapData = persistence.getDataMap(DATA_PERSISTENCE_KEY);
- if (dataMapData != null) {
- updateData(dataMapData);
- }
- DataMap dataMapStatus = persistence.getDataMap(STATUS_PERSISTENCE_KEY);
- if (dataMapStatus != null) {
- updateStatus(dataMapStatus);
- }
- DataMap dataMapBasals = persistence.getDataMap(BASALS_PERSISTENCE_KEY);
- if (dataMapBasals != null) {
- updateBasals(dataMapBasals);
- }
- }
-
- /*
- * Since complications do not need Basals, we skip them for performance
- */
- public void updateForComplicationsFromPersistence(Persistence persistence) {
-
- DataMap dataMapData = persistence.getDataMap(DATA_PERSISTENCE_KEY);
- if (dataMapData != null) {
- updateData(dataMapData);
- }
- DataMap dataMapStatus = persistence.getDataMap(STATUS_PERSISTENCE_KEY);
- if (dataMapStatus != null) {
- updateStatus(dataMapStatus);
- }
- }
-
- public DataMap updateDataFromMessage(Intent intent, PowerManager.WakeLock wakeLock) {
- Bundle bundle = intent.getBundleExtra("data");
- if (bundle != null) {
- DataMap dataMap = wearUtil.bundleToDataMap(bundle);
- updateData(dataMap);
- return dataMap;
- }
- return null;
- }
-
- private void updateData(DataMap dataMap) {
- PowerManager.WakeLock wl = wearUtil.getWakeLock("readingPrefs", 50);
- sgvLevel = dataMap.getLong("sgvLevel");
- datetime = dataMap.getLong("timestamp");
- sSgv = dataMap.getString("sgvString");
- sDirection = dataMap.getString("slopeArrow");
- sDelta = dataMap.getString("delta");
- sAvgDelta = dataMap.getString("avgDelta");
- sUnits = dataMap.getString("glucoseUnits");
- wearUtil.releaseWakeLock(wl);
- }
-
- public DataMap updateStatusFromMessage(Intent intent, PowerManager.WakeLock wakeLock) {
- Bundle bundle = intent.getBundleExtra("status");
- if (bundle != null) {
- DataMap dataMap = wearUtil.bundleToDataMap(bundle);
- updateStatus(dataMap);
- return dataMap;
- }
- return null;
- }
-
- private void updateStatus(DataMap dataMap) {
- PowerManager.WakeLock wl = wearUtil.getWakeLock("readingPrefs", 50);
- sBasalRate = dataMap.getString("currentBasal");
- sUploaderBattery = dataMap.getString("battery");
- sRigBattery = dataMap.getString("rigBattery");
- detailedIOB = dataMap.getBoolean("detailedIob");
- sIOB1 = dataMap.getString("iobSum") + "U";
- sIOB2 = dataMap.getString("iobDetail");
- sCOB1 = "Carb";
- sCOB2 = dataMap.getString("cob");
- sBgi = dataMap.getString("bgi");
- showBGI = dataMap.getBoolean("showBgi");
- externalStatusString = dataMap.getString("externalStatusString");
- batteryLevel = dataMap.getInt("batteryLevel");
- openApsStatus = dataMap.getLong("openApsStatus");
- wearUtil.releaseWakeLock(wl);
- }
-
- public DataMap updateBasalsFromMessage(Intent intent) {
- Bundle bundle = intent.getBundleExtra("basals");
- if (bundle != null) {
- DataMap dataMap = wearUtil.bundleToDataMap(bundle);
- updateBasals(dataMap);
- return dataMap;
- }
- return null;
- }
-
- private void updateBasals(DataMap dataMap) {
- PowerManager.WakeLock wl = wearUtil.getWakeLock("readingPrefs", 500);
- loadBasalsAndTemps(dataMap);
- wearUtil.releaseWakeLock(wl);
- }
-
- private void loadBasalsAndTemps(DataMap dataMap) {
- ArrayList temps = dataMap.getDataMapArrayList("temps");
- if (temps != null) {
- tempWatchDataList = new ArrayList<>();
- for (DataMap temp : temps) {
- TempWatchData twd = new TempWatchData();
- twd.startTime = temp.getLong("starttime");
- twd.startBasal = temp.getDouble("startBasal");
- twd.endTime = temp.getLong("endtime");
- twd.endBasal = temp.getDouble("endbasal");
- twd.amount = temp.getDouble("amount");
- tempWatchDataList.add(twd);
- }
- }
- ArrayList basals = dataMap.getDataMapArrayList("basals");
- if (basals != null) {
- basalWatchDataList = new ArrayList<>();
- for (DataMap basal : basals) {
- BasalWatchData bwd = new BasalWatchData();
- bwd.startTime = basal.getLong("starttime");
- bwd.endTime = basal.getLong("endtime");
- bwd.amount = basal.getDouble("amount");
- basalWatchDataList.add(bwd);
- }
- }
- ArrayList boluses = dataMap.getDataMapArrayList("boluses");
- if (boluses != null) {
- bolusWatchDataList = new ArrayList<>();
- for (DataMap bolus : boluses) {
- BolusWatchData bwd = new BolusWatchData();
- bwd.date = bolus.getLong("date");
- bwd.bolus = bolus.getDouble("bolus");
- bwd.carbs = bolus.getDouble("carbs");
- bwd.isSMB = bolus.getBoolean("isSMB");
- bwd.isValid = bolus.getBoolean("isValid");
- bolusWatchDataList.add(bwd);
- }
- }
- ArrayList predictions = dataMap.getDataMapArrayList("predictions");
- if (boluses != null) {
- predictionList = new ArrayList<>();
- for (DataMap prediction : predictions) {
- BgWatchData bwd = new BgWatchData();
- bwd.timestamp = prediction.getLong("timestamp");
- bwd.sgv = prediction.getDouble("sgv");
- bwd.color = prediction.getInt("color");
- predictionList.add(bwd);
- }
- }
- }
-
- public void addToWatchSet(DataMap dataMap) {
- ArrayList entries = dataMap.getDataMapArrayList("entries");
- if (entries != null) {
- bgDataList = new ArrayList<>();
- for (DataMap entry : entries) {
- double sgv = entry.getDouble("sgvDouble");
- double high = entry.getDouble("high");
- double low = entry.getDouble("low");
- long timestamp = entry.getLong("timestamp");
- int color = entry.getInt("color", 0);
- bgDataList.add(new BgWatchData(sgv, high, low, timestamp, color));
- }
- } else {
- double sgv = dataMap.getDouble("sgvDouble");
- double high = dataMap.getDouble("high");
- double low = dataMap.getDouble("low");
- long timestamp = dataMap.getLong("timestamp");
- int color = dataMap.getInt("color", 0);
-
- final int size = bgDataList.size();
- if (size > 0) {
- if (bgDataList.get(size - 1).timestamp == timestamp)
- return; // Ignore duplicates.
- }
-
- bgDataList.add(new BgWatchData(sgv, high, low, timestamp, color));
- }
-
- // We use iterator instead for-loop because we iterate and remove on the go
- Iterator itr = bgDataList.iterator();
- while (itr.hasNext()) {
- BgWatchData entry = (BgWatchData)itr.next();
- if (entry.timestamp < (wearUtil.timestamp() - (Constants.HOUR_IN_MS * 5))) {
- itr.remove(); //Get rid of anything more than 5 hours old
- }
- }
- }
-}
diff --git a/wear/src/main/java/info/nightscout/androidaps/data/RawDisplayData.kt b/wear/src/main/java/info/nightscout/androidaps/data/RawDisplayData.kt
new file mode 100644
index 0000000000..ece27dee0d
--- /dev/null
+++ b/wear/src/main/java/info/nightscout/androidaps/data/RawDisplayData.kt
@@ -0,0 +1,95 @@
+package info.nightscout.androidaps.data
+
+import android.content.Intent
+import info.nightscout.androidaps.comm.DataLayerListenerServiceWear
+import info.nightscout.androidaps.interaction.utils.Persistence
+import info.nightscout.shared.weardata.EventData
+
+/**
+ * Holds bunch of data model variables and lists that arrive from phone app and are due to be
+ * displayed on watchface and complications. Keeping them together makes code cleaner and allows
+ * passing it to complications via persistence layer.
+ *
+ * Created by dlvoy on 2019-11-12
+ */
+class RawDisplayData {
+
+ // bg data bundle
+ var singleBg = EventData.SingleBg(
+ timeStamp = 0,
+ sgv = 0.0,
+ high = 0.0,
+ low = 0.0,
+ color = 0
+ )
+
+ // status bundle
+ var status = EventData.Status(
+ externalStatus = "no status",
+ iobSum = "IOB",
+ iobDetail = "-.--",
+ detailedIob = false,
+ cob = "--g",
+ currentBasal = "-.--U/h",
+ battery = "--",
+ rigBattery = "--",
+ openApsStatus = -1,
+ bgi = "--",
+ showBgi = false,
+ batteryLevel = 1
+ )
+
+ // basals bundle
+ var graphData = EventData.GraphData(
+ entries = ArrayList()
+ )
+
+ var treatmentData = EventData.TreatmentData(
+ temps = ArrayList(),
+ basals = ArrayList(),
+ boluses = ArrayList(),
+ predictions = ArrayList()
+ )
+
+ fun toDebugString(): String =
+ "DisplayRawData{singleBg=$singleBg, status=$status, graphData=$graphData, treatmentData=$treatmentData}"
+
+ fun updateFromPersistence(persistence: Persistence) {
+ persistence.readSingleBg()?.let { singleBg = it }
+ persistence.readGraphData()?.let { graphData = it }
+ persistence.readStatus()?.let { status = it }
+ persistence.readTreatments()?.let { treatmentData = it }
+ }
+
+ /*
+ * Since complications do not need Basals, we skip them for performance
+ */
+ fun updateForComplicationsFromPersistence(persistence: Persistence) {
+ persistence.readSingleBg()?.let { singleBg = it }
+ persistence.readGraphData()?.let { graphData = it }
+ persistence.readStatus()?.let { status = it }
+ }
+
+ fun updateFromMessage(intent: Intent) {
+ intent.getStringExtra(DataLayerListenerServiceWear.KEY_SINGLE_BG_DATA)?.let{
+ singleBg = EventData.deserialize(it) as EventData.SingleBg
+ }
+ intent.getStringExtra(DataLayerListenerServiceWear.KEY_STATUS_DATA)?.let{
+ status = EventData.deserialize(it) as EventData.Status
+ }
+ intent.getStringExtra(DataLayerListenerServiceWear.KEY_TREATMENTS_DATA)?.let{
+ treatmentData = EventData.deserialize(it) as EventData.TreatmentData
+ }
+ intent.getStringExtra(DataLayerListenerServiceWear.KEY_GRAPH_DATA)?.let{
+ graphData = EventData.deserialize(it) as EventData.GraphData
+ }
+ }
+
+ companion object {
+
+ const val BG_DATA_PERSISTENCE_KEY = "raw_data"
+ const val GRAPH_DATA_PERSISTENCE_KEY = "raw_data"
+ const val BASALS_PERSISTENCE_KEY = "raw_basals"
+ const val STATUS_PERSISTENCE_KEY = "raw_status"
+ }
+}
\ No newline at end of file
diff --git a/wear/src/main/java/info/nightscout/androidaps/data/TempWatchData.java b/wear/src/main/java/info/nightscout/androidaps/data/TempWatchData.java
deleted file mode 100644
index a7a23962eb..0000000000
--- a/wear/src/main/java/info/nightscout/androidaps/data/TempWatchData.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package info.nightscout.androidaps.data;
-
-/**
- * Created by adrian on 17/11/16.
- */
-
-public class TempWatchData {
- public long startTime;
- public double startBasal;
- public long endTime;
- public double endBasal;
- public double amount;
-}
diff --git a/wear/src/main/java/info/nightscout/androidaps/di/WearActivitiesModule.kt b/wear/src/main/java/info/nightscout/androidaps/di/WearActivitiesModule.kt
index 253935147e..90ef38717e 100644
--- a/wear/src/main/java/info/nightscout/androidaps/di/WearActivitiesModule.kt
+++ b/wear/src/main/java/info/nightscout/androidaps/di/WearActivitiesModule.kt
@@ -3,6 +3,10 @@ package info.nightscout.androidaps.di
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.interaction.actions.*
+import info.nightscout.androidaps.interaction.menus.FillMenuActivity
+import info.nightscout.androidaps.interaction.menus.MainMenuActivity
+import info.nightscout.androidaps.interaction.menus.StatusMenuActivity
+import info.nightscout.androidaps.interaction.utils.MenuListActivity
@Module
@Suppress("unused")
@@ -20,4 +24,9 @@ abstract class WearActivitiesModule {
@ContributesAndroidInjector abstract fun contributesTempTargetActivity(): TempTargetActivity
@ContributesAndroidInjector abstract fun contributesTreatmentActivity(): TreatmentActivity
@ContributesAndroidInjector abstract fun contributesWizardActivity(): WizardActivity
+
+ @ContributesAndroidInjector abstract fun contributesMenuListActivity(): MenuListActivity
+ @ContributesAndroidInjector abstract fun contributesFillMenuActivity(): FillMenuActivity
+ @ContributesAndroidInjector abstract fun contributesMainMenuActivity(): MainMenuActivity
+ @ContributesAndroidInjector abstract fun contributesStatusMenuActivity(): StatusMenuActivity
}
\ No newline at end of file
diff --git a/wear/src/main/java/info/nightscout/androidaps/di/WearServicesModule.kt b/wear/src/main/java/info/nightscout/androidaps/di/WearServicesModule.kt
index de47454131..800aa4205a 100644
--- a/wear/src/main/java/info/nightscout/androidaps/di/WearServicesModule.kt
+++ b/wear/src/main/java/info/nightscout/androidaps/di/WearServicesModule.kt
@@ -2,16 +2,18 @@ package info.nightscout.androidaps.di
import dagger.Module
import dagger.android.ContributesAndroidInjector
+import info.nightscout.androidaps.comm.DataLayerListenerServiceWear
import info.nightscout.androidaps.complications.*
-import info.nightscout.androidaps.data.DataLayerListenerService
-import info.nightscout.androidaps.interaction.actions.BackgroundActionActivity
+import info.nightscout.androidaps.tile.QuickWizardTileService
+import info.nightscout.androidaps.tile.TempTargetTileService
+import info.nightscout.androidaps.tile.TileBase
import info.nightscout.androidaps.watchfaces.*
@Module
@Suppress("unused")
abstract class WearServicesModule {
- @ContributesAndroidInjector abstract fun contributesDataLayerListenerService(): DataLayerListenerService
+ @ContributesAndroidInjector abstract fun contributesDataLayerListenerService(): DataLayerListenerServiceWear
@ContributesAndroidInjector abstract fun contributesBaseComplicationProviderService(): BaseComplicationProviderService
@ContributesAndroidInjector abstract fun contributesBrCobIobComplication(): BrCobIobComplication
@@ -24,7 +26,7 @@ abstract class WearServicesModule {
@ContributesAndroidInjector abstract fun contributesLongStatusComplication(): LongStatusComplication
@ContributesAndroidInjector abstract fun contributesLongStatusFlippedComplication(): LongStatusFlippedComplication
@ContributesAndroidInjector abstract fun contributesSgvComplication(): SgvComplication
- @ContributesAndroidInjector abstract fun contributesUploaderBattery(): UploaderBattery
+ @ContributesAndroidInjector abstract fun contributesUploaderBatteryComplication(): UploaderBatteryComplication
@ContributesAndroidInjector abstract fun contributesWallpaperComplication(): WallpaperComplication
@ContributesAndroidInjector abstract fun contributesBaseWatchFace(): BaseWatchFace
@@ -34,4 +36,12 @@ abstract class WearServicesModule {
@ContributesAndroidInjector abstract fun contributesSteampunk(): Steampunk
@ContributesAndroidInjector abstract fun contributesDigitalStyle(): DigitalStyle
@ContributesAndroidInjector abstract fun contributesCockpit(): Cockpit
+
+ @ContributesAndroidInjector abstract fun contributesBIGChart(): BIGChart
+ @ContributesAndroidInjector abstract fun contributesNOChart(): NOChart
+ @ContributesAndroidInjector abstract fun contributesCircleWatchface(): CircleWatchface
+
+ @ContributesAndroidInjector abstract fun contributesTileBase(): TileBase
+ @ContributesAndroidInjector abstract fun contributesQuickWizardTileService(): QuickWizardTileService
+ @ContributesAndroidInjector abstract fun contributesTempTargetTileService(): TempTargetTileService
}
\ No newline at end of file
diff --git a/wear/src/main/java/info/nightscout/androidaps/events/EventWearPreferenceChange.kt b/wear/src/main/java/info/nightscout/androidaps/events/EventWearPreferenceChange.kt
new file mode 100644
index 0000000000..51fe819340
--- /dev/null
+++ b/wear/src/main/java/info/nightscout/androidaps/events/EventWearPreferenceChange.kt
@@ -0,0 +1,22 @@
+package info.nightscout.androidaps.events
+
+import android.content.Context
+
+@Suppress("unused")
+class EventWearPreferenceChange : Event {
+
+ var changedKey: String? = null
+ private set
+
+ constructor(key: String) {
+ changedKey = key
+ }
+
+ constructor(context: Context, resourceID: Int) {
+ changedKey = context.getString(resourceID)
+ }
+
+ fun isChanged(context: Context, id: Int): Boolean {
+ return changedKey == context.getString(id)
+ }
+}
\ No newline at end of file
diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/AcceptActivity.java b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/AcceptActivity.java
index 413bf0ee26..a5feccda80 100644
--- a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/AcceptActivity.java
+++ b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/AcceptActivity.java
@@ -1,6 +1,6 @@
package info.nightscout.androidaps.interaction.actions;
-import static info.nightscout.shared.weardata.WearConstants.KEY_ACTION_DATA;
+import static info.nightscout.androidaps.comm.DataLayerListenerServiceWear.KEY_ACTION_DATA;
import android.content.Context;
import android.content.Intent;
@@ -21,10 +21,8 @@ import androidx.core.view.MotionEventCompat;
import androidx.core.view.ViewConfigurationCompat;
import info.nightscout.androidaps.R;
-import info.nightscout.androidaps.data.DataLayerListenerService;
-import info.nightscout.androidaps.events.EventWearToMobileChange;
-import info.nightscout.androidaps.events.EventWearToMobileConfirm;
-import info.nightscout.shared.weardata.ActionData;
+import info.nightscout.androidaps.events.EventWearToMobile;
+import info.nightscout.shared.weardata.EventData;
/**
* Created by adrian on 09/02/17.
@@ -33,7 +31,6 @@ import info.nightscout.shared.weardata.ActionData;
public class AcceptActivity extends ViewSelectorActivity {
String message = "";
- String actionstring = "";
String actionKey = "";
private DismissThread dismissThread;
@@ -46,10 +43,9 @@ public class AcceptActivity extends ViewSelectorActivity {
Bundle extras = getIntent().getExtras();
message = extras.getString("message", "");
- actionstring = extras.getString("actionstring", "");
actionKey = extras.getString(KEY_ACTION_DATA, "");
- if (message.isEmpty() || (actionstring.isEmpty() && actionKey.isEmpty())) {
+ if (message.isEmpty() || actionKey.isEmpty()) {
finish();
return;
}
@@ -109,15 +105,9 @@ public class AcceptActivity extends ViewSelectorActivity {
view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.action_send_item, container, false);
final ImageView confirmButton = view.findViewById(R.id.confirmbutton);
confirmButton.setOnClickListener((View v) -> {
- if (!actionstring.isEmpty())
- DataLayerListenerService.Companion.confirmAction(AcceptActivity.this, actionstring);
- else {
- ActionData actionData = ActionData.Companion.deserialize(actionKey);
- if (actionData instanceof ActionData.ConfirmAction)
- rxBus.send(new EventWearToMobileConfirm(actionData));
- if (actionData instanceof ActionData.ChangeAction)
- rxBus.send(new EventWearToMobileChange(actionData));
- }
+ EventData returnCommand = EventData.Companion.deserialize(actionKey);
+ rxBus.send(new EventWearToMobile(returnCommand));
+ rxBus.send(new EventData.CancelNotification(System.currentTimeMillis()));
finishAffinity();
});
container.addView(view);
diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/BackgroundActionActivity.kt b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/BackgroundActionActivity.kt
index 4d0ecbc0a9..15afd34b46 100644
--- a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/BackgroundActionActivity.kt
+++ b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/BackgroundActionActivity.kt
@@ -3,21 +3,25 @@ package info.nightscout.androidaps.interaction.actions
import android.os.Bundle
import android.widget.Toast
import dagger.android.DaggerActivity
-import info.nightscout.androidaps.data.DataLayerListenerService
+import info.nightscout.androidaps.comm.DataLayerListenerServiceWear
+import info.nightscout.androidaps.events.EventWearToMobile
+import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
+import info.nightscout.shared.weardata.EventData
import javax.inject.Inject
class BackgroundActionActivity : DaggerActivity() {
@Inject lateinit var aapsLogger: AAPSLogger
+ @Inject lateinit var rxBus: RxBus
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- intent.extras?.getString("actionString")?.let { actionString ->
- aapsLogger.info(LTag.WEAR, "QuickWizardActivity.onCreate: actionString=$actionString")
- DataLayerListenerService.initiateAction(this, actionString)
- intent.extras?.getString("message")?.let { message ->
+ intent.extras?.getString(DataLayerListenerServiceWear.KEY_ACTION)?.let { action ->
+ aapsLogger.info(LTag.WEAR, "QuickWizardActivity.onCreate: action=$action")
+ rxBus.send(EventWearToMobile(EventData.deserialize(action)))
+ intent.extras?.getString(DataLayerListenerServiceWear.KEY_MESSAGE)?.let { message ->
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
}
} ?: aapsLogger.error(LTag.WEAR, "BackgroundActionActivity.onCreate extras 'actionString' required")
diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/BolusActivity.java b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/BolusActivity.java
index 55109a352f..372330ca7f 100644
--- a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/BolusActivity.java
+++ b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/BolusActivity.java
@@ -10,10 +10,10 @@ import android.widget.ImageView;
import java.text.DecimalFormat;
import info.nightscout.androidaps.R;
-import info.nightscout.androidaps.events.EventWearToMobileAction;
+import info.nightscout.androidaps.events.EventWearToMobile;
import info.nightscout.androidaps.interaction.utils.PlusMinusEditText;
import info.nightscout.shared.SafeParse;
-import info.nightscout.shared.weardata.ActionData;
+import info.nightscout.shared.weardata.EventData;
public class BolusActivity extends ViewSelectorActivity {
@@ -63,8 +63,7 @@ public class BolusActivity extends ViewSelectorActivity {
view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.action_send_item, container, false);
final ImageView confirmButton = view.findViewById(R.id.confirmbutton);
confirmButton.setOnClickListener((View v) -> {
- ActionData.Bolus bolus = new ActionData.Bolus(SafeParse.stringToDouble(editInsulin.editText.getText().toString()), 0);
- rxBus.send(new EventWearToMobileAction(bolus));
+ rxBus.send(new EventWearToMobile(new EventData.ActionBolusPreCheck(SafeParse.stringToDouble(editInsulin.editText.getText().toString()), 0)));
showToast(BolusActivity.this, R.string.action_bolus_confirmation);
finishAffinity();
});
diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/CPPActivity.java b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/CPPActivity.java
index dde240c015..c0b6cedb33 100644
--- a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/CPPActivity.java
+++ b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/CPPActivity.java
@@ -10,11 +10,10 @@ import android.widget.ImageView;
import java.text.DecimalFormat;
import info.nightscout.androidaps.R;
-import info.nightscout.androidaps.data.DataLayerListenerService;
-import info.nightscout.androidaps.events.EventWearToMobileAction;
+import info.nightscout.androidaps.events.EventWearToMobile;
import info.nightscout.androidaps.interaction.utils.PlusMinusEditText;
import info.nightscout.shared.SafeParse;
-import info.nightscout.shared.weardata.ActionData;
+import info.nightscout.shared.weardata.EventData;
/**
* Created by adrian on 09/02/17.
@@ -52,6 +51,7 @@ public class CPPActivity extends ViewSelectorActivity {
finish();
}
+ @SuppressWarnings("deprecation")
private class MyGridViewPagerAdapter extends GridPagerAdapter {
@Override
public int getColumnCount(int arg0) {
@@ -94,9 +94,9 @@ public class CPPActivity extends ViewSelectorActivity {
confirmButton.setOnClickListener((View v) -> {
//check if it can happen that the fragment is never created that hold data?
// (you have to swipe past them anyways - but still)
- ActionData.ProfileSwitch ps =
- new ActionData.ProfileSwitch(SafeParse.stringToInt(editTimeshift.editText.getText().toString()), SafeParse.stringToInt(editPercentage.editText.getText().toString()));
- rxBus.send(new EventWearToMobileAction(ps));
+ EventData.ActionProfileSwitchPreCheck ps =
+ new EventData.ActionProfileSwitchPreCheck(SafeParse.stringToInt(editTimeshift.editText.getText().toString()), SafeParse.stringToInt(editPercentage.editText.getText().toString()));
+ rxBus.send(new EventWearToMobile(ps));
showToast(CPPActivity.this, R.string.action_cpp_confirmation);
finishAffinity();
});
@@ -108,7 +108,7 @@ public class CPPActivity extends ViewSelectorActivity {
@Override
public void destroyItem(ViewGroup container, int row, int col, Object view) {
// Handle this to get the data before the view is destroyed?
- // Object should still be kept by this, just setup for reinit?
+ // Object should still be kept by this, just setup for re-init?
container.removeView((View) view);
}
diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/CarbActivity.java b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/CarbActivity.java
index d9c9298236..fe6f9c6767 100644
--- a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/CarbActivity.java
+++ b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/CarbActivity.java
@@ -10,9 +10,10 @@ import android.widget.ImageView;
import java.text.DecimalFormat;
import info.nightscout.androidaps.R;
-import info.nightscout.androidaps.data.DataLayerListenerService;
+import info.nightscout.androidaps.events.EventWearToMobile;
import info.nightscout.androidaps.interaction.utils.PlusMinusEditText;
import info.nightscout.shared.SafeParse;
+import info.nightscout.shared.weardata.EventData;
public class CarbActivity extends ViewSelectorActivity {
@@ -32,6 +33,7 @@ public class CarbActivity extends ViewSelectorActivity {
finish();
}
+ @SuppressWarnings("deprecation")
private class MyGridViewPagerAdapter extends GridPagerAdapter {
@Override
public int getColumnCount(int arg0) {
@@ -46,37 +48,42 @@ public class CarbActivity extends ViewSelectorActivity {
@Override
public Object instantiateItem(ViewGroup container, int row, int col) {
+ final View view;
if (col == 0) {
- final View view = getInflatedPlusMinusView(container);
+ view = getInflatedPlusMinusView(container);
double def = 0;
if (editCarbs != null) {
def = SafeParse.stringToDouble(editCarbs.editText.getText().toString());
}
- editCarbs = new PlusMinusEditText(view, R.id.amountfield, R.id.plusbutton, R.id.minusbutton, def, 0d, (double)maxCarbs, 1d, new DecimalFormat("0"), true);
+ editCarbs = new PlusMinusEditText(view, R.id.amountfield, R.id.plusbutton, R.id.minusbutton, def, 0d, (double) maxCarbs, 1d, new DecimalFormat("0"), true);
setLabelToPlusMinusView(view, getString(R.string.action_carbs));
container.addView(view);
view.requestFocus();
- return view;
} else {
- final View view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.action_send_item, container, false);
- final ImageView confirmbutton = view.findViewById(R.id.confirmbutton);
- confirmbutton.setOnClickListener((View v) -> {
+ view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.action_send_item, container, false);
+ final ImageView confirmButton = view.findViewById(R.id.confirmbutton);
+ confirmButton.setOnClickListener((View v) -> {
// With start time 0 and duration 0
- String actionstring = "ecarbs " + SafeParse.stringToInt(editCarbs.editText.getText().toString()) + " 0 0";
- DataLayerListenerService.Companion.initiateAction(CarbActivity.this, actionstring);
+ EventData.ActionECarbsPreCheck bolus =
+ new EventData.ActionECarbsPreCheck(
+ SafeParse.stringToInt(editCarbs.editText.getText().toString()),
+ 0,
+ 0
+ );
+ rxBus.send(new EventWearToMobile(bolus));
showToast(CarbActivity.this, R.string.action_ecarb_confirmation);
finishAffinity();
});
container.addView(view);
- return view;
}
+ return view;
}
@Override
public void destroyItem(ViewGroup container, int row, int col, Object view) {
// Handle this to get the data before the view is destroyed?
- // Object should still be kept by this, just setup for reinit?
+ // Object should still be kept by this, just setup for re-init?
container.removeView((View) view);
}
diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/ECarbActivity.java b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/ECarbActivity.java
index cc36635961..3b8f548c59 100644
--- a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/ECarbActivity.java
+++ b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/ECarbActivity.java
@@ -10,9 +10,10 @@ import android.widget.ImageView;
import java.text.DecimalFormat;
import info.nightscout.androidaps.R;
-import info.nightscout.androidaps.data.DataLayerListenerService;
+import info.nightscout.androidaps.events.EventWearToMobile;
import info.nightscout.androidaps.interaction.utils.PlusMinusEditText;
import info.nightscout.shared.SafeParse;
+import info.nightscout.shared.weardata.EventData;
/**
* Created by adrian on 04/08/18.
@@ -38,6 +39,7 @@ public class ECarbActivity extends ViewSelectorActivity {
finish();
}
+ @SuppressWarnings("deprecation")
private class MyGridViewPagerAdapter extends GridPagerAdapter {
@Override
public int getColumnCount(int arg0) {
@@ -86,16 +88,19 @@ public class ECarbActivity extends ViewSelectorActivity {
} else {
final View view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.action_send_item, container, false);
- final ImageView confirmbutton = view.findViewById(R.id.confirmbutton);
- confirmbutton.setOnClickListener((View v) -> {
+ final ImageView confirmButton = view.findViewById(R.id.confirmbutton);
+ confirmButton.setOnClickListener((View v) -> {
//check if it can happen that the fragment is never created that hold data?
// (you have to swipe past them anyways - but still)
- String actionstring = "ecarbs " + SafeParse.stringToInt(editCarbs.editText.getText().toString())
- + " " + SafeParse.stringToInt(editStartTime.editText.getText().toString())
- + " " + SafeParse.stringToInt(editDuration.editText.getText().toString());
- DataLayerListenerService.Companion.initiateAction(ECarbActivity.this, actionstring);
+ EventData.ActionECarbsPreCheck bolus =
+ new EventData.ActionECarbsPreCheck(
+ SafeParse.stringToInt(editCarbs.editText.getText().toString()),
+ SafeParse.stringToInt(editStartTime.editText.getText().toString()),
+ SafeParse.stringToInt(editDuration.editText.getText().toString())
+ );
+ rxBus.send(new EventWearToMobile(bolus));
showToast(ECarbActivity.this, R.string.action_ecarb_confirmation);
finishAffinity();
@@ -108,7 +113,7 @@ public class ECarbActivity extends ViewSelectorActivity {
@Override
public void destroyItem(ViewGroup container, int row, int col, Object view) {
// Handle this to get the data before the view is destroyed?
- // Object should still be kept by this, just setup for reinit?
+ // Object should still be kept by this, just setup for re-init?
container.removeView((View) view);
}
diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/FillActivity.java b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/FillActivity.java
index 28493146ca..c7cbb892ce 100644
--- a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/FillActivity.java
+++ b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/FillActivity.java
@@ -10,9 +10,10 @@ import android.widget.ImageView;
import java.text.DecimalFormat;
import info.nightscout.androidaps.R;
-import info.nightscout.androidaps.data.DataLayerListenerService;
+import info.nightscout.androidaps.events.EventWearToMobile;
import info.nightscout.androidaps.interaction.utils.PlusMinusEditText;
import info.nightscout.shared.SafeParse;
+import info.nightscout.shared.weardata.EventData;
/**
* Created by adrian on 09/02/17.
@@ -34,6 +35,7 @@ public class FillActivity extends ViewSelectorActivity {
finish();
}
+ @SuppressWarnings("deprecation")
private class MyGridViewPagerAdapter extends GridPagerAdapter {
@Override
public int getColumnCount(int arg0) {
@@ -67,8 +69,7 @@ public class FillActivity extends ViewSelectorActivity {
//check if it can happen that the fagment is never created that hold data?
// (you have to swipe past them anyways - but still)
- String actionstring = "fill " + SafeParse.stringToDouble(editInsulin.editText.getText().toString());
- DataLayerListenerService.Companion.initiateAction(FillActivity.this, actionstring);
+ rxBus.send(new EventWearToMobile(new EventData.ActionFillPreCheck(SafeParse.stringToDouble(editInsulin.editText.getText().toString()))));
showToast(FillActivity.this, R.string.action_fill_confirmation);
finishAffinity();
});
diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/TempTargetActivity.java b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/TempTargetActivity.java
index 4bab152cb5..22cb47811f 100644
--- a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/TempTargetActivity.java
+++ b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/TempTargetActivity.java
@@ -10,9 +10,10 @@ import android.widget.ImageView;
import java.text.DecimalFormat;
import info.nightscout.androidaps.R;
-import info.nightscout.androidaps.data.DataLayerListenerService;
+import info.nightscout.androidaps.events.EventWearToMobile;
import info.nightscout.androidaps.interaction.utils.PlusMinusEditText;
import info.nightscout.shared.SafeParse;
+import info.nightscout.shared.weardata.EventData;
/**
* Created by adrian on 09/02/17.
@@ -42,6 +43,7 @@ public class TempTargetActivity extends ViewSelectorActivity {
finish();
}
+ @SuppressWarnings("deprecation")
private class MyGridViewPagerAdapter extends GridPagerAdapter {
@Override
public int getColumnCount(int arg0) {
@@ -112,18 +114,20 @@ public class TempTargetActivity extends ViewSelectorActivity {
} else {
final View view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.action_send_item, container, false);
- final ImageView confirmbutton = view.findViewById(R.id.confirmbutton);
- confirmbutton.setOnClickListener((View v) -> {
- //check if it can happen that the fagment is never created that hold data?
+ final ImageView confirmButton = view.findViewById(R.id.confirmbutton);
+ confirmButton.setOnClickListener((View v) -> {
+ //check if it can happen that the fragment is never created that hold data?
// (you have to swipe past them anyways - but still)
- String actionstring = "temptarget"
- + " " + isMGDL
- + " " + SafeParse.stringToInt(time.editText.getText().toString())
- + " " + SafeParse.stringToDouble(lowRange.editText.getText().toString())
- + " " + (isSingleTarget ? SafeParse.stringToDouble(lowRange.editText.getText().toString()) : SafeParse.stringToDouble(highRange.editText.getText().toString()));
-
- DataLayerListenerService.Companion.initiateAction(TempTargetActivity.this, actionstring);
+ EventData.ActionTempTargetPreCheck action = new EventData.ActionTempTargetPreCheck(
+ EventData.ActionTempTargetPreCheck.TempTargetCommand.MANUAL,
+ isMGDL,
+ SafeParse.stringToInt(time.editText.getText().toString()),
+ SafeParse.stringToDouble(lowRange.editText.getText().toString()),
+ (isSingleTarget ?
+ SafeParse.stringToDouble(lowRange.editText.getText().toString()) : SafeParse.stringToDouble(highRange.editText.getText().toString()))
+ );
+ rxBus.send(new EventWearToMobile(action));
showToast(TempTargetActivity.this, R.string.action_tempt_confirmation);
finishAffinity();
});
@@ -135,7 +139,7 @@ public class TempTargetActivity extends ViewSelectorActivity {
@Override
public void destroyItem(ViewGroup container, int row, int col, Object view) {
// Handle this to get the data before the view is destroyed?
- // Object should still be kept by this, just setup for reinit?
+ // Object should still be kept by this, just setup for re-init?
container.removeView((View) view);
}
diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/TreatmentActivity.java b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/TreatmentActivity.java
index 2b0f0dd4a6..fa353128ba 100644
--- a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/TreatmentActivity.java
+++ b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/TreatmentActivity.java
@@ -10,11 +10,10 @@ import android.widget.ImageView;
import java.text.DecimalFormat;
import info.nightscout.androidaps.R;
-import info.nightscout.androidaps.data.DataLayerListenerService;
-import info.nightscout.androidaps.events.EventWearToMobileAction;
+import info.nightscout.androidaps.events.EventWearToMobile;
import info.nightscout.androidaps.interaction.utils.PlusMinusEditText;
import info.nightscout.shared.SafeParse;
-import info.nightscout.shared.weardata.ActionData;
+import info.nightscout.shared.weardata.EventData;
/**
* Created by adrian on 09/02/17.
@@ -42,6 +41,7 @@ public class TreatmentActivity extends ViewSelectorActivity {
}
+ @SuppressWarnings("deprecation")
private class MyGridViewPagerAdapter extends GridPagerAdapter {
@Override
public int getColumnCount(int arg0) {
@@ -80,12 +80,12 @@ public class TreatmentActivity extends ViewSelectorActivity {
} else {
final View view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.action_send_item, container, false);
- final ImageView confirmbutton = view.findViewById(R.id.confirmbutton);
- confirmbutton.setOnClickListener((View v) -> {
+ final ImageView confirmButton = view.findViewById(R.id.confirmbutton);
+ confirmButton.setOnClickListener((View v) -> {
//check if it can happen that the fragment is never created that hold data?
// (you have to swipe past them anyways - but still)
- ActionData.Bolus bolus = new ActionData.Bolus(SafeParse.stringToDouble(editInsulin.editText.getText().toString()), SafeParse.stringToInt(editCarbs.editText.getText().toString()));
- rxBus.send(new EventWearToMobileAction(bolus));
+ EventData.ActionBolusPreCheck bolus = new EventData.ActionBolusPreCheck(SafeParse.stringToDouble(editInsulin.editText.getText().toString()), SafeParse.stringToInt(editCarbs.editText.getText().toString()));
+ rxBus.send(new EventWearToMobile(bolus));
showToast(TreatmentActivity.this, R.string.action_treatment_confirmation);
finishAffinity();
});
@@ -97,7 +97,7 @@ public class TreatmentActivity extends ViewSelectorActivity {
@Override
public void destroyItem(ViewGroup container, int row, int col, Object view) {
// Handle this to get the data before the view is destroyed?
- // Object should still be kept by this, just setup for reinit?
+ // Object should still be kept by this, just setup for re-init?
container.removeView((View) view);
}
diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/WizardActivity.java b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/WizardActivity.java
index ac2ecb30fe..698c3fc59d 100644
--- a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/WizardActivity.java
+++ b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/WizardActivity.java
@@ -10,9 +10,10 @@ import android.widget.ImageView;
import java.text.DecimalFormat;
import info.nightscout.androidaps.R;
-import info.nightscout.androidaps.data.DataLayerListenerService;
+import info.nightscout.androidaps.events.EventWearToMobile;
import info.nightscout.androidaps.interaction.utils.PlusMinusEditText;
import info.nightscout.shared.SafeParse;
+import info.nightscout.shared.weardata.EventData;
/**
* Created by adrian on 09/02/17.
@@ -42,6 +43,7 @@ public class WizardActivity extends ViewSelectorActivity {
finish();
}
+ @SuppressWarnings("deprecation")
private class MyGridViewPagerAdapter extends GridPagerAdapter {
@Override
public int getColumnCount(int arg0) {
@@ -82,15 +84,17 @@ public class WizardActivity extends ViewSelectorActivity {
} else {
final View view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.action_send_item, container, false);
- final ImageView confirmbutton = view.findViewById(R.id.confirmbutton);
- confirmbutton.setOnClickListener((View v) -> {
+ final ImageView confirmButton = view.findViewById(R.id.confirmbutton);
+ confirmButton.setOnClickListener((View v) -> {
if (editPercentage != null) {
percentage = SafeParse.stringToInt(editPercentage.editText.getText().toString());
}
- String actionstring = "wizard2 " + SafeParse.stringToInt(editCarbs.editText.getText().toString())
- + " " + percentage;
- DataLayerListenerService.Companion.initiateAction(WizardActivity.this, actionstring);
+ EventData.ActionWizardPreCheck action = new EventData.ActionWizardPreCheck(
+ SafeParse.stringToInt(editCarbs.editText.getText().toString()),
+ percentage
+ );
+ rxBus.send(new EventWearToMobile(action));
showToast(WizardActivity.this, R.string.action_wizard_confirmation);
finishAffinity();
});
@@ -102,7 +106,7 @@ public class WizardActivity extends ViewSelectorActivity {
@Override
public void destroyItem(ViewGroup container, int row, int col, Object view) {
// Handle this to get the data before the view is destroyed?
- // Object should still be kept by this, just setup for reinit?
+ // Object should still be kept by this, just setup for re-init?
container.removeView((View) view);
}
diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/menus/FillMenuActivity.java b/wear/src/main/java/info/nightscout/androidaps/interaction/menus/FillMenuActivity.java
deleted file mode 100644
index f11c887130..0000000000
--- a/wear/src/main/java/info/nightscout/androidaps/interaction/menus/FillMenuActivity.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package info.nightscout.androidaps.interaction.menus;
-
-import android.content.Intent;
-import android.os.Bundle;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import info.nightscout.androidaps.R;
-import info.nightscout.androidaps.data.DataLayerListenerService;
-import info.nightscout.androidaps.interaction.actions.FillActivity;
-import info.nightscout.androidaps.interaction.utils.MenuListActivity;
-
-/**
- * Created by adrian on 09/02/17.
- */
-
-public class FillMenuActivity extends MenuListActivity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- setTitle(R.string.menu_prime_fill);
- super.onCreate(savedInstanceState);
- }
-
- @Override
- protected List