From 2dba08117648b725d61eb67528508f87bfa03287 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Mon, 11 Apr 2022 14:25:00 +0200 Subject: [PATCH] Wear refactor --- app/src/main/AndroidManifest.xml | 72 +- .../info/nightscout/androidaps/MainApp.kt | 6 +- .../androidaps/activities/SurveyActivity.kt | 4 +- .../tidepool/events/EventTidepoolResetData.kt | 2 +- .../general/wear/ActionStringHandler.kt | 745 ++++++++++-------- .../plugins/general/wear/WearFragment.kt | 32 +- .../plugins/general/wear/WearPlugin.kt | 183 ++--- .../general/wear/events/EventWearUpdateGui.kt | 5 + .../SendToDataLayerThread.java | 139 ---- .../wearintegration/WatchUpdaterService.kt | 633 +++++++++++++++ ...Service.java => WatchUpdaterService1.java} | 71 +- .../IobCobCalculatorPlugin.kt | 1 + .../androidaps/plugins/pump/mdi/MDIPlugin.kt | 8 +- .../plugins/pump/virtual/VirtualPumpPlugin.kt | 8 +- app/src/main/res/layout/wear_fragment.xml | 51 +- app/src/main/res/values/wear.xml | 25 + build.gradle | 3 +- .../plugins/pump/combo/ComboPlugin.java | 2 +- core/core_dependencies.gradle | 4 +- .../androidaps/events/EventNetworkChange.kt | 2 - .../androidaps/events/EventNtpStatus.kt | 2 - .../nightscout/androidaps/utils/InstanceId.kt | 11 +- database/build.gradle | 3 + shared/build.gradle | 4 + .../androidaps/annotations/OpenForTesting.kt | 0 .../nightscout/androidaps/events/Event.kt | 2 +- .../events/EventWearToMobileAction.kt | 7 + .../events/EventWearToMobileChange.kt | 7 + .../events/EventWearToMobileConfirm.kt | 7 + .../androidaps/plugins/bus/RxBus.kt | 0 .../nightscout/shared/weardata/ActionData.kt | 33 + .../shared/weardata/WearConstants.kt | 61 ++ .../nightscout/shared/weardata/WearUris.kt | 21 - shared/src/main/res/values/wear_paths.xml | 19 + .../androidaps/annotations/OpenForTesting.kt | 0 wear/build.gradle | 10 +- wear/src/main/AndroidManifest.xml | 145 ++-- wear/src/main/assets/logback.xml | 39 + .../java/info/nightscout/androidaps/Aaps.kt | 6 + .../BaseComplicationProviderService.java | 4 +- .../data/DataLayerListenerService.kt | 552 +++++++++++++ .../androidaps/data/ListenerService.java | 628 --------------- .../androidaps/data/RawDisplayData.java | 2 +- .../androidaps/di/WearActivitiesModule.kt | 23 + .../nightscout/androidaps/di/WearModule.kt | 9 +- .../androidaps/di/WearServicesModule.kt | 5 +- .../interaction/actions/AcceptActivity.java | 61 +- .../actions/BackgroundActionActivity.kt | 26 +- .../interaction/actions/BolusActivity.java | 32 +- .../interaction/actions/CPPActivity.java | 19 +- .../interaction/actions/CarbActivity.java | 9 +- .../interaction/actions/ECarbActivity.java | 11 +- .../interaction/actions/FillActivity.java | 14 +- .../actions/TempTargetActivity.java | 9 +- .../actions/TreatmentActivity.java | 28 +- .../actions/ViewSelectorActivity.java | 19 +- .../interaction/actions/WizardActivity.java | 9 +- .../interaction/menus/FillMenuActivity.java | 8 +- .../interaction/menus/MainMenuActivity.java | 8 +- .../interaction/menus/StatusMenuActivity.java | 10 +- .../androidaps/watchfaces/BIGChart.java | 12 +- .../androidaps/watchfaces/BaseWatchFace.java | 8 +- .../androidaps/watchfaces/NOChart.java | 6 +- wear/src/main/res/values/wear.xml | 39 +- .../data/RawDisplayDataBasalsTest.java | 4 +- 65 files changed, 2305 insertions(+), 1623 deletions(-) create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/general/wear/events/EventWearUpdateGui.kt delete mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/SendToDataLayerThread.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.kt rename app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/{WatchUpdaterService.java => WatchUpdaterService1.java} (90%) create mode 100644 app/src/main/res/values/wear.xml rename {database => shared}/src/debug/java/info/nightscout/androidaps/annotations/OpenForTesting.kt (100%) rename {core => shared}/src/main/java/info/nightscout/androidaps/events/Event.kt (99%) create mode 100644 shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobileAction.kt create mode 100644 shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobileChange.kt create mode 100644 shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobileConfirm.kt rename {core => shared}/src/main/java/info/nightscout/androidaps/plugins/bus/RxBus.kt (100%) create mode 100644 shared/src/main/java/info/nightscout/shared/weardata/ActionData.kt create mode 100644 shared/src/main/java/info/nightscout/shared/weardata/WearConstants.kt delete mode 100644 shared/src/main/java/info/nightscout/shared/weardata/WearUris.kt create mode 100644 shared/src/main/res/values/wear_paths.xml rename {database => shared}/src/release/java/info/nightscout/androidaps/annotations/OpenForTesting.kt (100%) create mode 100644 wear/src/main/assets/logback.xml create mode 100644 wear/src/main/java/info/nightscout/androidaps/data/DataLayerListenerService.kt delete mode 100644 wear/src/main/java/info/nightscout/androidaps/data/ListenerService.java create mode 100644 wear/src/main/java/info/nightscout/androidaps/di/WearActivitiesModule.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 84de1769b2..3069f7d830 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -46,6 +46,10 @@ android:supportsRtl="true" android:theme="@style/AppTheme.Launcher" > + + - - - - + + + + + + + + + - - - - - - - - @@ -247,10 +219,6 @@ - - 120) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/events/EventTidepoolResetData.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/events/EventTidepoolResetData.kt index 833353e6b0..8ea3460801 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/events/EventTidepoolResetData.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/tidepool/events/EventTidepoolResetData.kt @@ -2,4 +2,4 @@ package info.nightscout.androidaps.plugins.general.tidepool.events import info.nightscout.androidaps.events.Event -class EventTidepoolResetData :Event() \ No newline at end of file +class EventTidepoolResetData : Event() \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt index d4eb8a30e1..52a2a5ce27 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt @@ -2,7 +2,6 @@ package info.nightscout.androidaps.plugins.general.wear import android.app.NotificationManager import android.content.Context -import android.util.Log import dagger.android.HasAndroidInjector import info.nightscout.androidaps.Constants import info.nightscout.androidaps.R @@ -25,8 +24,6 @@ import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTe import info.nightscout.androidaps.extensions.total import info.nightscout.androidaps.extensions.valueToUnits import info.nightscout.androidaps.interfaces.* -import info.nightscout.shared.logging.AAPSLogger -import info.nightscout.shared.logging.LTag import info.nightscout.androidaps.logging.UserEntryLogger import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker @@ -38,10 +35,28 @@ 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.shared.sharedPreferences.SP 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 @@ -96,7 +111,7 @@ class ActionStringHandler @Inject constructor( disposable += rxBus .toObservable(EventWearInitiateAction::class.java) .observeOn(aapsSchedulers.main) - .subscribe({ handleInitiate(it.action) }, fabricPrivacy::logException) + .subscribe({ handleInitiateActionOnPhone(it.action) }, fabricPrivacy::logException) disposable += rxBus .toObservable(EventWearConfirmAction::class.java) @@ -109,328 +124,360 @@ class ActionStringHandler @Inject constructor( } @Synchronized - private fun handleInitiate(actionString: String) { + private fun handleInitiateActionOnPhone(actionString: String) { //TODO: i18n - Log.i("ActionStringHandler", "handleInitiate actionString=$actionString") + 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 = "" - // do the parsing and check constraints - val act = actionString.split("\\s+".toRegex()).toTypedArray() - if ("fillpreset" == act[0]) { ///////////////////////////////////// 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" - } else if ("fill" == act[0]) { ////////////////////////////////////////////// PRIME/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" - } else if ("bolus" == act[0]) { ////////////////////////////////////////////// BOLUS - val insulin = SafeParse.stringToDouble(act[1]) - val carbs = SafeParse.stringToInt(act[2]) - val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(insulin)).value() - val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value() - val pump = activePlugin.activePump - if (insulinAfterConstraints > 0 && (!pump.isInitialized() || pump.isSuspended() || loop.isDisconnected)) { - sendError(rh.gs(R.string.wizard_pump_not_available)) - return - } - rMessage += rh.gs(R.string.bolus) + ": " + insulinAfterConstraints + "U\n" - rMessage += rh.gs(R.string.carbs) + ": " + carbsAfterConstraints + "g" - if (insulinAfterConstraints - insulin != 0.0 || carbsAfterConstraints - carbs != 0) { - rMessage += "\n" + rh.gs(R.string.constraintapllied) - } - rAction += "bolus $insulinAfterConstraints $carbsAfterConstraints" - } else if ("temptarget" == act[0]) { ///////////////////////////////////////////////////////// TEMPTARGET - 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" + 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 } - - "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" + 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) } - - else -> { - sendError(rh.gs(R.string.wear_action_tempt_preset_error, preset)) + 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 { - 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 + } + } 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" } - 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_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" } - } - } else if ("status" == act[0]) { ////////////////////////////////////////////// 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" - } - } else if ("wizard" == act[0]) { - sendError("Update APP on Watch!") - return - } else if ("wizard2" == act[0]) { ////////////////////////////////////////////// WIZARD - val pump = activePlugin.activePump - if (!pump.isInitialized() || pump.isSuspended() || loop.isDisconnected) { - sendError(rh.gs(R.string.wizard_pump_not_available)) - return - } - val carbsBeforeConstraints = SafeParse.stringToInt(act[1]) - val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbsBeforeConstraints)).value() - if (carbsAfterConstraints - carbsBeforeConstraints != 0) { - sendError(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 - } else if ("quick_wizard" == act[0]) { - val guid = act[1] - val actualBg = iobCobCalculator.ads.actualBg() - val profile = profileFunction.getProfile() - val profileName = profileFunction.getProfileName() - val quickWizardEntry = quickWizard.get(guid) - Log.i("QuickWizard", "handleInitiate: quick_wizard " + quickWizardEntry?.buttonText() + " c " + quickWizardEntry?.carbs()) - if (quickWizardEntry == null) { - sendError(rh.gs(R.string.quick_wizard_not_available)) - return - } - if (actualBg == null) { - sendError(rh.gs(R.string.wizard_no_actual_bg)) - return - } - if (profile == null) { - sendError(rh.gs(R.string.wizard_no_active_profile)) - return - } - val cobInfo = iobCobCalculator.getCobInfo(false, "QuickWizard wear") - if (cobInfo.displayCob == null) { - sendError(rh.gs(R.string.wizard_no_cob)) - return - } - val pump = activePlugin.activePump - if (!pump.isInitialized() || pump.isSuspended() || loop.isDisconnected) { - sendError(rh.gs(R.string.wizard_pump_not_available)) - return - } + 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" + } - val wizard = quickWizardEntry.doCalc(profile, profileName, actualBg, true) + "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" + } - 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 - } + "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" + } - rMessage = rh.gs(R.string.quick_wizard_message, quickWizardEntry.buttonText(), wizard.calculatedTotalInsulin, quickWizardEntry.carbs()) - rAction = "bolus $insulinAfterConstraints $carbsAfterConstraints" - Log.i("QuickWizard", "handleInitiate: quick_wizard action=$rAction") - - rMessage += "\n_____________" - rMessage += "\n" + wizard.explainShort() - - } else if ("opencpp" == act[0]) { - val activeProfileSwitch = repository.getEffectiveProfileSwitchActiveAt(dateUtil.now()).blockingGet() - if (activeProfileSwitch is ValueWrapper.Existing) { // read CPP values - rTitle = "opencpp" - rMessage = "opencpp" - rAction = "opencpp" + " " + activeProfileSwitch.value.originalPercentage + " " + activeProfileSwitch.value.originalTimeshift - } else { - sendError("No active profile switch!") - return - } - } else if ("cppset" == act[0]) { - val activeProfileSwitch = repository.getEffectiveProfileSwitchActiveAt(dateUtil.now()).blockingGet() - if (activeProfileSwitch is ValueWrapper.Existing) { - rMessage = "CPP:" + "\n\n" + - "Timeshift: " + act[1] + "\n" + - "Percentage: " + act[2] + "%" - rAction = actionString - } else { // read CPP values - sendError("No active profile switch!") - return - } - } else if ("tddstats" == act[0]) { - 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 -> { + 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 } - } else { // if up to date: prepare, send (check if CPP is activated -> add CPP stats) - rTitle = "TDD" - rAction = "statusmessage" - rMessage = generateTDDMessage(historyList, dummies) } - } else if ("ecarbs" == act[0]) { ////////////////////////////////////////////// ECARBS - 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" - } else if ("changeRequest" == act[0]) { ////////////////////////////////////////////// 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 - } else if ("cancelChangeRequest" == act[0]) { ////////////////////////////////////////////// CANCEL CHANGE REQUEST NOTIFICATION - 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) @@ -594,44 +641,60 @@ class ActionStringHandler @Inject constructor( lastConfirmActionString = null // do the parsing, check constraints and enact! val act = actionString.split("\\s+".toRegex()).toTypedArray() - if ("fill" == act[0]) { - 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 + 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) } - doFillBolus(amount) - } else if ("temptarget" == act[0]) { - val duration = SafeParse.stringToInt(act[2]) - val low = SafeParse.stringToDouble(act[3]) - val high = SafeParse.stringToDouble(act[4]) - generateTempTarget(duration, low, high) - } else if ("wizard2" == act[0]) { - if (lastBolusWizard != null) { //use last calculation as confirmed string matches - doBolus(lastBolusWizard!!.calculatedTotalInsulin, lastBolusWizard!!.carbs, null, 0) - lastBolusWizard = null + + 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) } - } else if ("bolus" == act[0]) { - val insulin = SafeParse.stringToDouble(act[1]) - val carbs = SafeParse.stringToInt(act[2]) - doBolus(insulin, carbs, null, 0) - } else if ("cppset" == act[0]) { - val timeshift = SafeParse.stringToInt(act[1]) - val percentage = SafeParse.stringToInt(act[2]) - setCPP(timeshift, percentage) - } else if ("ecarbs" == act[0]) { - val carbs = SafeParse.stringToInt(act[1]) - val starttime = SafeParse.stringToLong(act[2]) - val duration = SafeParse.stringToInt(act[3]) - doECarbs(carbs, starttime, duration) - } else if ("dismissoverviewnotification" == act[0]) { - rxBus.send(EventDismissNotification(SafeParse.stringToInt(act[1]))) - } else if ("changeRequest" == act[0]) { - loop.acceptChangeRequest() - val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - notificationManager.cancel(Constants.notificationID) } 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 91ed047770..95e21f730d 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,14 +6,25 @@ import android.view.View import android.view.ViewGroup import dagger.android.support.DaggerFragment import info.nightscout.androidaps.databinding.WearFragmentBinding +import info.nightscout.androidaps.plugins.bus.RxBus +import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientUpdateGUI +import info.nightscout.androidaps.utils.FabricPrivacy +import info.nightscout.androidaps.utils.rx.AapsSchedulers +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import javax.inject.Inject class WearFragment : DaggerFragment() { @Inject lateinit var wearPlugin: WearPlugin + @Inject lateinit var rxBus: RxBus + @Inject lateinit var aapsSchedulers: AapsSchedulers + @Inject lateinit var fabricPrivacy: FabricPrivacy private var _binding: WearFragmentBinding? = null + private val disposable = CompositeDisposable() + // This property is only valid between onCreateView and // onDestroyView. private val binding get() = _binding!! @@ -27,7 +38,21 @@ 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.openSettings.setOnClickListener { wearPlugin.openSettings() } + } + + override fun onResume() { + super.onResume() + disposable += rxBus + .toObservable(EventNSClientUpdateGUI::class.java) + .observeOn(aapsSchedulers.main) + .subscribe({ updateGui() }, fabricPrivacy::logException) + updateGui() + } + + override fun onPause() { + super.onPause() + disposable.clear() } @Synchronized @@ -35,4 +60,9 @@ class WearFragment : DaggerFragment() { super.onDestroyView() _binding = null } + + fun updateGui() { + _binding ?: return + binding.connectedDevice.text = wearPlugin.connectedDevice + } } \ No newline at end of file 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 43b4130778..ead6df3277 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 @@ -6,12 +6,11 @@ import dagger.Lazy import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R import info.nightscout.androidaps.events.* -import info.nightscout.androidaps.interfaces.Loop import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginDescription import info.nightscout.androidaps.interfaces.PluginType -import info.nightscout.shared.logging.AAPSLogger 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 @@ -19,8 +18,10 @@ import info.nightscout.androidaps.plugins.general.wear.wearintegration.WatchUpda 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 io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign import javax.inject.Inject import javax.inject.Singleton @@ -33,100 +34,103 @@ class WearPlugin @Inject constructor( private val sp: SP, private val ctx: Context, private val fabricPrivacy: FabricPrivacy, - private val loop: Loop, private val rxBus: RxBus, private val actionStringHandler: Lazy -) : PluginBase(PluginDescription() - .mainType(PluginType.GENERAL) - .fragmentClass(WearFragment::class.java.name) - .pluginIcon(R.drawable.ic_watch) - .pluginName(R.string.wear) - .shortName(R.string.wear_shortname) - .preferencesId(R.xml.pref_wear) - .description(R.string.description_wear), +) : PluginBase( + PluginDescription() + .mainType(PluginType.GENERAL) + .fragmentClass(WearFragment::class.java.name) + .pluginIcon(R.drawable.ic_watch) + .pluginName(R.string.wear) + .shortName(R.string.wear_shortname) + .preferencesId(R.xml.pref_wear) + .description(R.string.description_wear), aapsLogger, rh, injector ) { private val disposable = CompositeDisposable() + + var connectedDevice = "---" + override fun onStart() { super.onStart() - disposable.add(rxBus + disposable += rxBus .toObservable(EventOpenAPSUpdateGui::class.java) .observeOn(aapsSchedulers.io) - .subscribe({ sendDataToWatch(status = true, basals = true, bgValue = false) }, fabricPrivacy::logException)) - disposable.add(rxBus + .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.add(rxBus + .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.add(rxBus + .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.add(rxBus + .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.add(rxBus + .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.add(rxBus + .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.add(rxBus - .toObservable(EventRefreshOverview::class.java) + // 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({ - if (WatchUpdaterService.shouldReportLoopStatus((loop as PluginBase).isEnabled())) - sendDataToWatch(status = true, basals = false, bgValue = false) - }, fabricPrivacy::logException)) - disposable.add(rxBus + 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_BOLUSPROGRESS) - intent.putExtra("progresspercent", 0) - intent.putExtra("progressstatus", status) - ctx.startService(intent) - }, fabricPrivacy::logException)) - disposable.add(rxBus + 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) + 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) - } - val intent = Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_BOLUSPROGRESS) - intent.putExtra("progresspercent", 100) - intent.putExtra("progressstatus", status) - ctx.startService(intent) - }, fabricPrivacy::logException)) - disposable.add(rxBus + if (event.result == null) return@subscribe + val status: String = if (event.result!!.success) { + rh.gs(R.string.success) + } else { + rh.gs(R.string.nosuccess) + } + 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_BOLUSPROGRESS) - intent.putExtra("progresspercent", event.percent) - intent.putExtra("progressstatus", event.status) - ctx.startService(intent) - } - }, fabricPrivacy::logException)) + 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) + } + }, fabricPrivacy::logException) actionStringHandler.get().setup() } @@ -137,51 +141,50 @@ class WearPlugin @Inject constructor( } private fun sendDataToWatch(status: Boolean, basals: Boolean, bgValue: Boolean) { - //Log.d(TAG, "WR: WearPlugin:sendDataToWatch (status=" + status + ",basals=" + basals + ",bgValue=" + bgValue + ")"); - if (isEnabled(getType())) { - // only start service when this plugin is enabled - 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)) - } + // 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() { - //Log.d(TAG, "WR: WearPlugin:resendDataToWatch"); ctx.startService(Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_RESEND)) } fun openSettings() { - //Log.d(TAG, "WR: WearPlugin:openSettings"); ctx.startService(Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_OPEN_SETTINGS)) } - fun requestNotificationCancel(actionString: String?) { //Log.d(TAG, "WR: WearPlugin:requestNotificationCancel"); - val intent = Intent(ctx, WatchUpdaterService::class.java) - .setAction(WatchUpdaterService.ACTION_CANCEL_NOTIFICATION) - intent.putExtra("actionstring", actionString) - ctx.startService(intent) + 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) { - val intent = Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_ACTIONCONFIRMATIONREQUEST) - intent.putExtra("title", title) - intent.putExtra("message", message) - intent.putExtra("actionstring", actionString) - ctx.startService(intent) + 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) { - val intent = Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_CHANGECONFIRMATIONREQUEST) - intent.putExtra("title", title) - intent.putExtra("message", message) - intent.putExtra("actionstring", actionString) - ctx.startService(intent) + 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) + }) } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/events/EventWearUpdateGui.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/events/EventWearUpdateGui.kt new file mode 100644 index 0000000000..4736566059 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/events/EventWearUpdateGui.kt @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.plugins.general.wear.events + +import info.nightscout.androidaps.events.Event + +class EventWearUpdateGui : Event() \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/SendToDataLayerThread.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/SendToDataLayerThread.java deleted file mode 100644 index 0852b0a6aa..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/SendToDataLayerThread.java +++ /dev/null @@ -1,139 +0,0 @@ -package info.nightscout.androidaps.plugins.general.wear.wearintegration; - -import android.os.AsyncTask; -import android.util.Log; - -import com.google.android.gms.common.api.GoogleApiClient; -import com.google.android.gms.wearable.DataApi; -import com.google.android.gms.wearable.DataMap; -import com.google.android.gms.wearable.Node; -import com.google.android.gms.wearable.NodeApi; -import com.google.android.gms.wearable.PutDataMapRequest; -import com.google.android.gms.wearable.PutDataRequest; -import com.google.android.gms.wearable.Wearable; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.ReentrantLock; - -/** - * Created by emmablack on 12/26/14. - */ -class SendToDataLayerThread extends AsyncTask { - private final GoogleApiClient googleApiClient; - private static final String TAG = "SendToDataLayerThread"; - private final String path; - private final String logPrefix = ""; // "WR: "; - private static int concurrency = 0; - private static int state = 0; - private static final ReentrantLock lock = new ReentrantLock(); - private static long lastlock = 0; - private static final boolean testlockup = false; // always false in production - - - SendToDataLayerThread(String path, GoogleApiClient pGoogleApiClient) { - // Log.d(TAG, logPrefix + "SendToDataLayerThread: " + path); - this.path = path; - googleApiClient = pGoogleApiClient; - } - - - @Override - protected void onPreExecute() { - concurrency++; - if ((concurrency > 12) || ((concurrency > 3 && (lastlock != 0) && (tsl() - lastlock) > 300000))) { - // error if 9 concurrent threads or lock held for >5 minutes with concurrency of 4 - final String err = "Wear Integration deadlock detected!! " + ((lastlock != 0) ? "locked" : "") + " state:" - + state + " @" + hourMinuteString(tsl()); - // Home.toaststaticnext(err); - Log.e(TAG, logPrefix + err); - } - if (concurrency < 0) - Log.d(TAG, logPrefix + "Wear Integration impossible concurrency!!"); - - Log.d(TAG, logPrefix + "SendDataToLayerThread pre-execute concurrency: " + concurrency); - } - - - @Override - protected Void doInBackground(DataMap... params) { - if (testlockup) { - try { - Log.e(TAG, logPrefix + "WARNING RUNNING TEST LOCK UP CODE - NEVER FOR PRODUCTION"); - Thread.sleep(1000000); // DEEEBBUUGGGG - } catch (Exception e) { - } - } - sendToWear(params); - concurrency--; - Log.d(TAG, logPrefix + "SendDataToLayerThread post-execute concurrency: " + concurrency); - return null; - } - - - // Debug function to expose where it might be locking up - private synchronized void sendToWear(final DataMap... params) { - if (!lock.tryLock()) { - Log.d(TAG, logPrefix + "Concurrent access - waiting for thread unlock"); - lock.lock(); // enforce single threading - Log.d(TAG, logPrefix + "Thread unlocked - proceeding"); - } - lastlock = tsl(); - try { - if (state != 0) { - Log.e(TAG, logPrefix + "WEAR STATE ERROR: state=" + state); - } - state = 1; - final NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(googleApiClient).await(15, - TimeUnit.SECONDS); - - Log.d(TAG, logPrefix + "Nodes: " + nodes); - - state = 2; - for (Node node : nodes.getNodes()) { - state = 3; - for (DataMap dataMap : params) { - state = 4; - PutDataMapRequest putDMR = PutDataMapRequest.create(path); - state = 5; - putDMR.getDataMap().putAll(dataMap); - putDMR.setUrgent(); - state = 6; - PutDataRequest request = putDMR.asPutDataRequest(); - state = 7; - DataApi.DataItemResult result = Wearable.DataApi.putDataItem(googleApiClient, request).await(15, - TimeUnit.SECONDS); - state = 8; - if (result.getStatus().isSuccess()) { - Log.d(TAG, logPrefix + "DataMap: " + dataMap + " sent to: " + node.getDisplayName()); - } else { - Log.e(TAG, logPrefix + "ERROR: failed to send DataMap"); - result = Wearable.DataApi.putDataItem(googleApiClient, request).await(30, TimeUnit.SECONDS); - if (result.getStatus().isSuccess()) { - Log.d(TAG, logPrefix + "DataMap retry: " + dataMap + " sent to: " + node.getDisplayName()); - } else { - Log.e(TAG, logPrefix + "ERROR on retry: failed to send DataMap: " - + result.getStatus().toString()); - } - } - state = 9; - } - } - state = 0; - } catch (Exception e) { - Log.e(TAG, logPrefix + "Got exception in sendToWear: " + e); - } finally { - lastlock = 0; - lock.unlock(); - } - } - - - private static long tsl() { - return System.currentTimeMillis(); - } - - - private static String hourMinuteString(long timestamp) { - return android.text.format.DateFormat.format("kk:mm", timestamp).toString(); - } -} 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 new file mode 100644 index 0000000000..c3a37d4f61 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.kt @@ -0,0 +1,633 @@ +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/WatchUpdaterService.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService1.java similarity index 90% rename from app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.java rename to app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService1.java index 85d08c142c..95a2419d4a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService1.java @@ -1,73 +1,7 @@ package info.nightscout.androidaps.plugins.general.wear.wearintegration; -import android.content.Intent; -import android.os.AsyncTask; -import android.os.Bundle; -import android.os.Handler; -import android.os.HandlerThread; -import android.util.Log; - -import androidx.annotation.NonNull; - -import com.google.android.gms.common.ConnectionResult; -import com.google.android.gms.common.api.GoogleApiClient; -import com.google.android.gms.wearable.CapabilityApi; -import com.google.android.gms.wearable.CapabilityInfo; -import com.google.android.gms.wearable.DataMap; -import com.google.android.gms.wearable.MessageEvent; -import com.google.android.gms.wearable.Node; -import com.google.android.gms.wearable.PutDataMapRequest; -import com.google.android.gms.wearable.PutDataRequest; -import com.google.android.gms.wearable.Wearable; -import com.google.android.gms.wearable.WearableListenerService; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import javax.inject.Inject; - -import dagger.android.AndroidInjection; -import info.nightscout.androidaps.Constants; -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.IobTotal; -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.GlucoseValueExtensionKt; -import info.nightscout.androidaps.extensions.TemporaryBasalExtensionKt; -import info.nightscout.androidaps.interfaces.ActivePlugin; -import info.nightscout.androidaps.interfaces.Config; -import info.nightscout.androidaps.interfaces.GlucoseUnit; -import info.nightscout.androidaps.interfaces.IobCobCalculator; -import info.nightscout.androidaps.interfaces.Loop; -import info.nightscout.androidaps.interfaces.PluginBase; -import info.nightscout.androidaps.interfaces.Profile; -import info.nightscout.androidaps.interfaces.ProfileFunction; -import info.nightscout.androidaps.plugins.aps.loop.LoopPlugin; -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.iob.iobCobCalculator.GlucoseStatus; -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider; -import info.nightscout.androidaps.receivers.ReceiverStatusStore; -import info.nightscout.androidaps.utils.DecimalFormatter; -import info.nightscout.androidaps.utils.DefaultValueHelper; -import info.nightscout.androidaps.utils.TrendCalculator; -import info.nightscout.androidaps.utils.resources.ResourceHelper; -import info.nightscout.androidaps.utils.wizard.QuickWizard; -import info.nightscout.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.WearUris; - -public class WatchUpdaterService extends WearableListenerService implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { +/* +public class WatchUpdaterService1 extends WearableListenerService implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { @Inject public GlucoseStatusProvider glucoseStatusProvider; @Inject public AAPSLogger aapsLogger; @Inject public WearPlugin wearPlugin; @@ -841,3 +775,4 @@ public class WatchUpdaterService extends WearableListenerService implements Goog return (lastLoopStatus != enabled); } } +*/ \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobCalculatorPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobCalculatorPlugin.kt index cf38b3e857..8968308243 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobCalculatorPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/iob/iobCobCalculator/IobCobCalculatorPlugin.kt @@ -29,6 +29,7 @@ import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.androidaps.workflow.CalculationWorkflow +import info.nightscout.androidaps.events.Event import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.logging.LTag import info.nightscout.shared.sharedPreferences.SP diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/mdi/MDIPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/mdi/MDIPlugin.kt index c7e9ee9b9b..809ae5472f 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/mdi/MDIPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/mdi/MDIPlugin.kt @@ -5,13 +5,13 @@ import info.nightscout.androidaps.R import info.nightscout.androidaps.data.DetailedBolusInfo import info.nightscout.androidaps.data.PumpEnactResult import info.nightscout.androidaps.interfaces.* -import info.nightscout.shared.logging.AAPSLogger -import info.nightscout.shared.logging.LTag import info.nightscout.androidaps.plugins.common.ManufacturerType import info.nightscout.androidaps.plugins.pump.common.defs.PumpType import info.nightscout.androidaps.utils.DateUtil -import info.nightscout.androidaps.utils.InstanceId.instanceId +import info.nightscout.androidaps.utils.InstanceId import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.shared.logging.AAPSLogger +import info.nightscout.shared.logging.LTag import org.json.JSONException import org.json.JSONObject import javax.inject.Inject @@ -152,7 +152,7 @@ class MDIPlugin @Inject constructor( override fun manufacturer(): ManufacturerType = ManufacturerType.AndroidAPS override fun model(): PumpType = PumpType.MDI - override fun serialNumber(): String = instanceId() + override fun serialNumber(): String = InstanceId.instanceId override fun shortStatus(veryShort: Boolean): String = model().model override fun canHandleDST(): Boolean = true } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpPlugin.kt index f900d942da..03bc0655e4 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/pump/virtual/VirtualPumpPlugin.kt @@ -11,8 +11,6 @@ import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.extensions.convertedToAbsolute import info.nightscout.androidaps.extensions.plannedRemainingMinutes 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.common.ManufacturerType import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification @@ -22,11 +20,13 @@ import info.nightscout.androidaps.plugins.pump.common.defs.PumpType import info.nightscout.androidaps.plugins.pump.virtual.events.EventVirtualPumpUpdateGui import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy -import info.nightscout.androidaps.utils.InstanceId.instanceId +import info.nightscout.androidaps.utils.InstanceId import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.TimeChangeType 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 io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign @@ -365,7 +365,7 @@ open class VirtualPumpPlugin @Inject constructor( override fun model(): PumpType = pumpDescription.pumpType - override fun serialNumber(): String = instanceId() + override fun serialNumber(): String = InstanceId.instanceId override fun shortStatus(veryShort: Boolean): String = "Virtual Pump" diff --git a/app/src/main/res/layout/wear_fragment.xml b/app/src/main/res/layout/wear_fragment.xml index f93423a895..8b271deb9c 100644 --- a/app/src/main/res/layout/wear_fragment.xml +++ b/app/src/main/res/layout/wear_fragment.xml @@ -10,31 +10,38 @@ android:layout_height="wrap_content" android:orientation="vertical"> - + android:gravity="center_vertical|center_horizontal" + android:paddingTop="10dp" + android:paddingBottom="10dp" + android:textAppearance="@style/TextAppearance.AppCompat.Medium" + tools:text="---" /> + android:id="@+id/resend" + style="@style/ButtonSmallFontStyle" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_weight="1" + android:drawableTop="@drawable/ic_refresh" + android:paddingLeft="0dp" + android:paddingRight="0dp" + android:text="@string/resend_all_data" /> + + diff --git a/app/src/main/res/values/wear.xml b/app/src/main/res/values/wear.xml new file mode 100644 index 0000000000..cc1a357cba --- /dev/null +++ b/app/src/main/res/values/wear.xml @@ -0,0 +1,25 @@ + + + + + + + androidaps_mobile + + diff --git a/build.gradle b/build.gradle index e9a584a8a7..4258956182 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ buildscript { room_version = '2.4.2' lifecycle_version = '2.4.1' dagger_version = '2.41' - coroutines_version = '1.4.1' + coroutines_version = '1.6.1' activity_version = '1.3.1' fragmentktx_version = '1.3.6' ormLite_version = '4.46' @@ -51,6 +51,7 @@ buildscript { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" classpath 'com.hiya:jacoco-android:0.2' } } diff --git a/combo/src/main/java/info/nightscout/androidaps/plugins/pump/combo/ComboPlugin.java b/combo/src/main/java/info/nightscout/androidaps/plugins/pump/combo/ComboPlugin.java index db413a0130..0cb2fde701 100644 --- a/combo/src/main/java/info/nightscout/androidaps/plugins/pump/combo/ComboPlugin.java +++ b/combo/src/main/java/info/nightscout/androidaps/plugins/pump/combo/ComboPlugin.java @@ -1343,7 +1343,7 @@ public class ComboPlugin extends PumpPluginBase implements Pump, Constraints { } private String fakeSerialNumber() { - return InstanceId.INSTANCE.instanceId(); + return InstanceId.INSTANCE.getInstanceId(); } @NonNull @Override diff --git a/core/core_dependencies.gradle b/core/core_dependencies.gradle index a84479cf9f..d824f0c142 100644 --- a/core/core_dependencies.gradle +++ b/core/core_dependencies.gradle @@ -5,6 +5,8 @@ dependencies { api "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" api "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" + api "org.jetbrains.kotlinx:kotlinx-coroutines-guava:$coroutines_version" + api "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:$coroutines_version" api "androidx.core:core-ktx:$core_version" api 'androidx.legacy:legacy-support-v13:1.0.0' api 'androidx.legacy:legacy-support-v4:1.0.0' @@ -25,7 +27,7 @@ dependencies { api "com.google.dagger:dagger-android-support:$dagger_version" //Firebase - api platform('com.google.firebase:firebase-bom:25.12.0') + api platform('com.google.firebase:firebase-bom:29.3.0') api "com.google.firebase:firebase-analytics-ktx" api "com.google.firebase:firebase-crashlytics-ktx" api "com.google.firebase:firebase-messaging-ktx" diff --git a/core/src/main/java/info/nightscout/androidaps/events/EventNetworkChange.kt b/core/src/main/java/info/nightscout/androidaps/events/EventNetworkChange.kt index 716b2b1810..36650f1838 100644 --- a/core/src/main/java/info/nightscout/androidaps/events/EventNetworkChange.kt +++ b/core/src/main/java/info/nightscout/androidaps/events/EventNetworkChange.kt @@ -1,7 +1,5 @@ package info.nightscout.androidaps.events -import info.nightscout.androidaps.utils.StringUtils - class EventNetworkChange : Event() { var mobileConnected = false diff --git a/core/src/main/java/info/nightscout/androidaps/events/EventNtpStatus.kt b/core/src/main/java/info/nightscout/androidaps/events/EventNtpStatus.kt index 0ad455836a..46b90b33f4 100644 --- a/core/src/main/java/info/nightscout/androidaps/events/EventNtpStatus.kt +++ b/core/src/main/java/info/nightscout/androidaps/events/EventNtpStatus.kt @@ -1,5 +1,3 @@ package info.nightscout.androidaps.events -import info.nightscout.androidaps.events.Event - class EventNtpStatus(val status: String, val percent: Int) : Event() \ No newline at end of file diff --git a/core/src/main/java/info/nightscout/androidaps/utils/InstanceId.kt b/core/src/main/java/info/nightscout/androidaps/utils/InstanceId.kt index 64c84fcaa9..a5cd3187f0 100644 --- a/core/src/main/java/info/nightscout/androidaps/utils/InstanceId.kt +++ b/core/src/main/java/info/nightscout/androidaps/utils/InstanceId.kt @@ -1,10 +1,13 @@ package info.nightscout.androidaps.utils -import com.google.firebase.iid.FirebaseInstanceId +import com.google.firebase.installations.FirebaseInstallations object InstanceId { - fun instanceId(): String { - var id = FirebaseInstanceId.getInstance().id - return id + var instanceId : String = "" + + init { + FirebaseInstallations.getInstance().id.addOnCompleteListener { + instanceId = it.result + } } } \ No newline at end of file diff --git a/database/build.gradle b/database/build.gradle index 85fec1fec0..82516d6e33 100644 --- a/database/build.gradle +++ b/database/build.gradle @@ -18,6 +18,9 @@ android { } dependencies { + // shared needed for OpenForTesting + implementation project(':shared') + api "androidx.core:core-ktx:$core_version" api "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" diff --git a/shared/build.gradle b/shared/build.gradle index 12354733b6..764adc301d 100644 --- a/shared/build.gradle +++ b/shared/build.gradle @@ -3,6 +3,7 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-allopen' apply plugin: 'com.hiya.jacoco-android' +apply plugin: 'kotlinx-serialization' apply from: "${project.rootDir}/gradle/android_dependencies.gradle" apply from: "${project.rootDir}/gradle/android_module_dependencies.gradle" @@ -29,6 +30,9 @@ dependencies { exclude group: "com.google.android", module: "android" } + api "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2" + api "org.apache.commons:commons-lang3:$commonslang3_version" + //RxBus api "io.reactivex.rxjava3:rxjava:$rxjava_version" api "io.reactivex.rxjava3:rxkotlin:$rxkotlin_version" diff --git a/database/src/debug/java/info/nightscout/androidaps/annotations/OpenForTesting.kt b/shared/src/debug/java/info/nightscout/androidaps/annotations/OpenForTesting.kt similarity index 100% rename from database/src/debug/java/info/nightscout/androidaps/annotations/OpenForTesting.kt rename to shared/src/debug/java/info/nightscout/androidaps/annotations/OpenForTesting.kt diff --git a/core/src/main/java/info/nightscout/androidaps/events/Event.kt b/shared/src/main/java/info/nightscout/androidaps/events/Event.kt similarity index 99% rename from core/src/main/java/info/nightscout/androidaps/events/Event.kt rename to shared/src/main/java/info/nightscout/androidaps/events/Event.kt index a44f65e836..5c06afcc15 100644 --- a/core/src/main/java/info/nightscout/androidaps/events/Event.kt +++ b/shared/src/main/java/info/nightscout/androidaps/events/Event.kt @@ -15,4 +15,4 @@ abstract class Event { ReflectionToStringBuilder.setDefaultStyle(ToStringStyle.SHORT_PREFIX_STYLE) } } -} +} \ 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 new file mode 100644 index 0000000000..1d5ca463e9 --- /dev/null +++ b/shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobileAction.kt @@ -0,0 +1,7 @@ +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 new file mode 100644 index 0000000000..49ae662e82 --- /dev/null +++ b/shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobileChange.kt @@ -0,0 +1,7 @@ +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 new file mode 100644 index 0000000000..c999248cde --- /dev/null +++ b/shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobileConfirm.kt @@ -0,0 +1,7 @@ +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/core/src/main/java/info/nightscout/androidaps/plugins/bus/RxBus.kt b/shared/src/main/java/info/nightscout/androidaps/plugins/bus/RxBus.kt similarity index 100% rename from core/src/main/java/info/nightscout/androidaps/plugins/bus/RxBus.kt rename to shared/src/main/java/info/nightscout/androidaps/plugins/bus/RxBus.kt diff --git a/shared/src/main/java/info/nightscout/shared/weardata/ActionData.kt b/shared/src/main/java/info/nightscout/shared/weardata/ActionData.kt new file mode 100644 index 0000000000..042edc0d6f --- /dev/null +++ b/shared/src/main/java/info/nightscout/shared/weardata/ActionData.kt @@ -0,0 +1,33 @@ +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/WearConstants.kt b/shared/src/main/java/info/nightscout/shared/weardata/WearConstants.kt new file mode 100644 index 0000000000..7d5558d5f8 --- /dev/null +++ b/shared/src/main/java/info/nightscout/shared/weardata/WearConstants.kt @@ -0,0 +1,61 @@ +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/java/info/nightscout/shared/weardata/WearUris.kt b/shared/src/main/java/info/nightscout/shared/weardata/WearUris.kt deleted file mode 100644 index 2e16f8e459..0000000000 --- a/shared/src/main/java/info/nightscout/shared/weardata/WearUris.kt +++ /dev/null @@ -1,21 +0,0 @@ -package info.nightscout.shared.weardata - -object WearUris { - - const val WEARABLE_DATA_PATH = "/nightscout_watch_data" - const val WEARABLE_RESEND_PATH = "/nightscout_watch_data_resend" - const val WEARABLE_CANCELBOLUS_PATH = "/nightscout_watch_cancel_bolus" - const val WEARABLE_CONFIRM_ACTIONSTRING_PATH = "/nightscout_watch_confirmactionstring" - const val WEARABLE_INITIATE_ACTIONSTRING_PATH = "/nightscout_watch_initiateactionstring" - - const val OPEN_SETTINGS_PATH = "/openwearsettings" - const val NEW_STATUS_PATH = "/sendstatustowear" - const val NEW_PREFERENCES_PATH = "/sendpreferencestowear" - const val QUICK_WIZARD_PATH = "/send_quick_wizard" - const val BASAL_DATA_PATH = "/nightscout_watch_basal" - const val BOLUS_PROGRESS_PATH = "/nightscout_watch_bolusprogress" - const val ACTION_CONFIRMATION_REQUEST_PATH = "/nightscout_watch_actionconfirmationrequest" - const val ACTION_CHANGECONFIRMATION_REQUEST_PATH = "/nightscout_watch_changeconfirmationrequest" - const val ACTION_CANCELNOTIFICATION_REQUEST_PATH = "/nightscout_watch_cancelnotificationrequest" - -} \ No newline at end of file diff --git a/shared/src/main/res/values/wear_paths.xml b/shared/src/main/res/values/wear_paths.xml new file mode 100644 index 0000000000..1bf7203905 --- /dev/null +++ b/shared/src/main/res/values/wear_paths.xml @@ -0,0 +1,19 @@ + + + /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 + \ No newline at end of file diff --git a/database/src/release/java/info/nightscout/androidaps/annotations/OpenForTesting.kt b/shared/src/release/java/info/nightscout/androidaps/annotations/OpenForTesting.kt similarity index 100% rename from database/src/release/java/info/nightscout/androidaps/annotations/OpenForTesting.kt rename to shared/src/release/java/info/nightscout/androidaps/annotations/OpenForTesting.kt diff --git a/wear/build.gradle b/wear/build.gradle index c18fc1be46..93d9e3281b 100644 --- a/wear/build.gradle +++ b/wear/build.gradle @@ -23,8 +23,7 @@ apply from: "${project.rootDir}/gradle/jacoco_global.gradle" ext { wearableVersion = "2.9.0" - // playServicesWearable 17.1.0 breaks test - playServicesWearable = "17.0.0" + playServicesWearable = "17.1.0" } def generateGitBuild = { -> @@ -52,7 +51,6 @@ android { compileSdkVersion 31 defaultConfig { - applicationId "info.nightscout.androidaps" minSdkVersion 23 targetSdkVersion 29 versionCode 2 @@ -112,8 +110,10 @@ dependencies { implementation(files('libs/wearpreferenceactivity-0.5.0.aar')) implementation('com.github.lecho:hellocharts-library:1.5.8@aar') - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.6.1' + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-guava:$coroutines_version" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:$coroutines_version" implementation "androidx.core:core-ktx:$core_version" implementation "androidx.wear.tiles:tiles:1.0.1" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" diff --git a/wear/src/main/AndroidManifest.xml b/wear/src/main/AndroidManifest.xml index 0bd323b107..95ff23dc3f 100644 --- a/wear/src/main/AndroidManifest.xml +++ b/wear/src/main/AndroidManifest.xml @@ -17,6 +17,11 @@ android:icon="@drawable/ic_icon" android:label="@string/app_name" android:theme="@android:style/Theme.DeviceDefault"> + + + @@ -25,7 +30,8 @@ android:name=".watchfaces.BIGChart" android:allowEmbedded="true" android:label="@string/label_xdrip_big_chart" - android:permission="android.permission.BIND_WALLPAPER"> + android:permission="android.permission.BIND_WALLPAPER" + android:exported="false"> @@ -46,7 +52,8 @@ android:name=".watchfaces.NOChart" android:allowEmbedded="true" android:label="@string/label_xdrip_no_chart" - android:permission="android.permission.BIND_WALLPAPER"> + android:permission="android.permission.BIND_WALLPAPER" + android:exported="false"> @@ -67,7 +74,8 @@ android:name=".watchfaces.Home" android:allowEmbedded="true" android:label="@string/label_xdrip" - android:permission="android.permission.BIND_WALLPAPER"> + android:permission="android.permission.BIND_WALLPAPER" + android:exported="false"> @@ -91,7 +99,8 @@ android:name=".watchfaces.Home2" android:allowEmbedded="true" android:label="@string/label_xdrip_v2" - android:permission="android.permission.BIND_WALLPAPER"> + android:permission="android.permission.BIND_WALLPAPER" + android:exported="false"> @@ -115,7 +124,8 @@ android:name=".watchfaces.Cockpit" android:allowEmbedded="true" android:label="@string/label_xdrip_cockpit" - android:permission="android.permission.BIND_WALLPAPER"> + android:permission="android.permission.BIND_WALLPAPER" + android:exported="false"> @@ -139,7 +149,8 @@ android:name=".watchfaces.Steampunk" android:allowEmbedded="true" android:label="@string/label_xdrip_steampunk" - android:permission="android.permission.BIND_WALLPAPER"> + android:permission="android.permission.BIND_WALLPAPER" + android:exported="false"> @@ -163,7 +174,8 @@ android:name=".watchfaces.LargeHome" android:allowEmbedded="true" android:label="@string/label_xdrip_large" - android:permission="android.permission.BIND_WALLPAPER"> + android:permission="android.permission.BIND_WALLPAPER" + android:exported="false"> @@ -186,7 +198,8 @@ android:name=".watchfaces.CircleWatchface" android:allowEmbedded="true" android:label="@string/label_xdrip_circle" - android:permission="android.permission.BIND_WALLPAPER"> + android:permission="android.permission.BIND_WALLPAPER" + android:exported="false"> @@ -207,7 +220,8 @@ android:name=".watchfaces.DigitalStyle" android:allowEmbedded="true" android:label="@string/label_digitalstyle" - android:permission="android.permission.BIND_WALLPAPER"> + android:permission="android.permission.BIND_WALLPAPER" + android:exported="false"> @@ -227,68 +241,73 @@ - + - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - @@ -466,7 +485,7 @@ @@ -483,7 +502,7 @@ @@ -500,7 +519,7 @@ @@ -623,7 +642,7 @@ - + diff --git a/wear/src/main/assets/logback.xml b/wear/src/main/assets/logback.xml new file mode 100644 index 0000000000..b901b4b738 --- /dev/null +++ b/wear/src/main/assets/logback.xml @@ -0,0 +1,39 @@ + + + + + ${EXT_FILES_DIR}/AndroidAPS.log + + + ${EXT_FILES_DIR}/AndroidAPS._%d{yyyy-MM-dd}_%d{HH-mm-ss, aux}_.%i.zip + + + + 5MB + + + 120 + + + %d{HH:mm:ss.SSS} [%thread] %.-1level/%logger: %msg%n + + + + + + %logger{0} + + + [%thread]: %msg%n + + + + + + + + + diff --git a/wear/src/main/java/info/nightscout/androidaps/Aaps.kt b/wear/src/main/java/info/nightscout/androidaps/Aaps.kt index c3ddb09936..565ddf002e 100644 --- a/wear/src/main/java/info/nightscout/androidaps/Aaps.kt +++ b/wear/src/main/java/info/nightscout/androidaps/Aaps.kt @@ -8,11 +8,17 @@ import androidx.preference.PreferenceManager import dagger.android.AndroidInjector import dagger.android.DaggerApplication import info.nightscout.androidaps.di.DaggerWearComponent +import info.nightscout.shared.logging.AAPSLogger +import info.nightscout.shared.logging.LTag +import javax.inject.Inject class Aaps : DaggerApplication(), OnSharedPreferenceChangeListener { + @Inject lateinit var aapsLogger: AAPSLogger + override fun onCreate() { super.onCreate() + aapsLogger.debug(LTag.WEAR, "onCreate") PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this) } 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 8c5a3d2c47..7d65be3080 100644 --- a/wear/src/main/java/info/nightscout/androidaps/complications/BaseComplicationProviderService.java +++ b/wear/src/main/java/info/nightscout/androidaps/complications/BaseComplicationProviderService.java @@ -22,7 +22,7 @@ import javax.inject.Inject; import dagger.android.AndroidInjection; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.ListenerService; +import info.nightscout.androidaps.data.DataLayerListenerService; import info.nightscout.androidaps.data.RawDisplayData; import info.nightscout.androidaps.interaction.utils.Constants; import info.nightscout.androidaps.interaction.utils.DisplayFormat; @@ -205,7 +205,7 @@ public abstract class BaseComplicationProviderService extends ComplicationProvid localBroadcastManager = LocalBroadcastManager.getInstance(this); localBroadcastManager.registerReceiver(messageReceiver, messageFilter); - ListenerService.requestData(this); + DataLayerListenerService.Companion.requestData(this); checkIfUpdateNeeded(); } diff --git a/wear/src/main/java/info/nightscout/androidaps/data/DataLayerListenerService.kt b/wear/src/main/java/info/nightscout/androidaps/data/DataLayerListenerService.kt new file mode 100644 index 0000000000..7cf6bb7840 --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/data/DataLayerListenerService.kt @@ -0,0 +1,552 @@ +/* + * 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/ListenerService.java b/wear/src/main/java/info/nightscout/androidaps/data/ListenerService.java deleted file mode 100644 index 47a3aa0a60..0000000000 --- a/wear/src/main/java/info/nightscout/androidaps/data/ListenerService.java +++ /dev/null @@ -1,628 +0,0 @@ -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.content.SharedPreferences; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Bundle; -import android.os.SystemClock; -import android.preference.PreferenceManager; -import android.util.Base64; -import android.util.Log; - -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.common.ConnectionResult; -import com.google.android.gms.common.api.GoogleApiClient; -import com.google.android.gms.wearable.ChannelApi; -import com.google.android.gms.wearable.DataEvent; -import com.google.android.gms.wearable.DataEventBuffer; -import com.google.android.gms.wearable.DataMap; -import com.google.android.gms.wearable.DataMapItem; -import com.google.android.gms.wearable.Node; -import com.google.android.gms.wearable.NodeApi; -import com.google.android.gms.wearable.Wearable; -import com.google.android.gms.wearable.WearableListenerService; - -import org.jetbrains.annotations.NotNull; - -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -import dagger.android.AndroidInjection; -import info.nightscout.androidaps.R; -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.tile.ActionsTileService; -import info.nightscout.androidaps.tile.QuickWizardTileService; -import info.nightscout.androidaps.tile.TempTargetTileService; -import info.nightscout.shared.SafeParse; -import info.nightscout.shared.weardata.WearUris; - -/** - * Created by emmablack on 12/26/14. - */ -public class ListenerService extends WearableListenerService implements GoogleApiClient.ConnectionCallbacks, - GoogleApiClient.OnConnectionFailedListener, ChannelApi.ChannelListener { - - @Inject WearUtil wearUtil; - @Inject Persistence persistence; - - public static final int BOLUS_PROGRESS_NOTIF_ID = 1; - public static final int CONFIRM_NOTIF_ID = 2; - public static final int CHANGE_NOTIF_ID = 556677; - - private static final String ACTION_RESEND = "com.dexdrip.stephenblack.nightwatch.RESEND_DATA"; - private static final String ACTION_CANCELBOLUS = "com.dexdrip.stephenblack.nightwatch.CANCELBOLUS"; - private static final String ACTION_CONFIRMATION = "com.dexdrip.stephenblack.nightwatch.CONFIRMACTION"; - private static final String ACTION_CONFIRMCHANGE = "com.dexdrip.stephenblack.nightwatch.CONFIRMCHANGE"; - private static final String ACTION_INITIATE_ACTION = "com.dexdrip.stephenblack.nightwatch.INITIATE_ACTION"; - - private static final String AAPS_NOTIFY_CHANNEL_ID_OPENLOOP = "AndroidAPS-OpenLoop"; - private static final String AAPS_NOTIFY_CHANNEL_ID_BOLUSPROGRESS = "bolus progress vibration"; - private static final String AAPS_NOTIFY_CHANNEL_ID_BOLUSPROGRESS_SILENT = "bolus progress silent"; - - GoogleApiClient googleApiClient; - - private DismissThread bolusprogressThread; - private static final String TAG = "ListenerService"; - - private final String logPrefix = ""; // "WR: " - - // Not derived from DaggerService, do injection here - @Override - public void onCreate() { - AndroidInjection.inject(this); - super.onCreate(); - } - - public class BolusCancelTask extends AsyncTask { - Context mContext; - - BolusCancelTask(Context context) { - mContext = context; - } - - @Override - protected Void doInBackground(Void... params) { - Log.d(TAG, logPrefix + "BolusCancelTask.doInBackground: " + params); - if (!googleApiClient.isConnected()) { - Log.i(TAG, "BolusCancelTask.doInBackground: not connected"); - googleApiClient.blockingConnect(15, TimeUnit.SECONDS); - } - if (googleApiClient.isConnected()) { - NodeApi.GetConnectedNodesResult nodes = - Wearable.NodeApi.getConnectedNodes(googleApiClient).await(); - for (Node node : nodes.getNodes()) { - Wearable.MessageApi.sendMessage(googleApiClient, node.getId(), - WearUris.WEARABLE_CANCELBOLUS_PATH, null); - } - - } - return null; - } - } - - public class MessageActionTask extends AsyncTask { - Context mContext; - String mActionstring; - String mMessagePath; - - MessageActionTask(Context context, String messagePath, String actionstring) { - mContext = context; - mActionstring = actionstring; - mMessagePath = messagePath; - } - - @Override - protected Void doInBackground(Void... params) { - Log.i(TAG, "MessageActionTask.doInBackground: "); - - if (!googleApiClient.isConnected()) { - Log.i(TAG, "MessageActionTask.doInBackground: not connected"); - googleApiClient.blockingConnect(15, TimeUnit.SECONDS); - } - if (googleApiClient.isConnected()) { - NodeApi.GetConnectedNodesResult nodes = - Wearable.NodeApi.getConnectedNodes(googleApiClient).await(); - for (Node node : nodes.getNodes()) { - Wearable.MessageApi.sendMessage(googleApiClient, node.getId(), mMessagePath, mActionstring.getBytes()); - } - - } - return null; - } - } - - public class ResendDataTask extends AsyncTask { - Context mContext; - - ResendDataTask(Context context) { - mContext = context; - } - - @Override - protected Void doInBackground(Void... params) { - Log.d(TAG, logPrefix + "ResendDataTask.doInBackground: " + params); - - if (!googleApiClient.isConnected()) { - Log.i(TAG, "ResendDataTask.doInBackground: not connected"); - googleApiClient.blockingConnect(15, TimeUnit.SECONDS); - } - if (googleApiClient.isConnected()) { - Log.i(TAG, "ResendDataTask.doInBackground: connected"); - NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(googleApiClient).await(); - for (Node node : nodes.getNodes()) { - Wearable.MessageApi.sendMessage(googleApiClient, node.getId(), WearUris.WEARABLE_RESEND_PATH, null); - } - } else { - Log.i(TAG, "ResendDataTask.doInBackground: could not connect"); - } - return null; - - } - } - - public void requestData() { - new ResendDataTask(this).execute(); - } - - public void cancelBolus() { - new BolusCancelTask(this).execute(); - } - - private void sendConfirmActionstring(String actionstring) { - new MessageActionTask(this, WearUris.WEARABLE_CONFIRM_ACTIONSTRING_PATH, actionstring).execute(); - } - - private void sendInitiateActionstring(String actionstring) { - new MessageActionTask(this, WearUris.WEARABLE_INITIATE_ACTIONSTRING_PATH, actionstring).execute(); - } - - private void googleApiConnect() { - if (googleApiClient != null) { - // Remove old listener(s) - try { - Wearable.ChannelApi.removeListener(googleApiClient, this); - } catch (Exception e) { - // - } - try { - Wearable.MessageApi.removeListener(googleApiClient, this); - } catch (Exception e) { - // - } - } - - googleApiClient = new GoogleApiClient.Builder(this) - .addConnectionCallbacks(this) - .addOnConnectionFailedListener(this) - .addApi(Wearable.API) - .build(); - Wearable.MessageApi.addListener(googleApiClient, this); - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - - // Log.d(TAG, logPrefix + "onStartCommand: Intent: " + intent); - - if (intent != null && ACTION_RESEND.equals(intent.getAction())) { - googleApiConnect(); - requestData(); - } else if (intent != null && ACTION_CANCELBOLUS.equals(intent.getAction())) { - googleApiConnect(); - - //dismiss notification - NotificationManagerCompat notificationManager = - NotificationManagerCompat.from(ListenerService.this); - notificationManager.cancel(BOLUS_PROGRESS_NOTIF_ID); - - //send cancel-request to phone. - cancelBolus(); - - - } else if (intent != null && ACTION_CONFIRMATION.equals(intent.getAction())) { - googleApiConnect(); - - //dismiss notification - NotificationManagerCompat notificationManager = - NotificationManagerCompat.from(ListenerService.this); - notificationManager.cancel(CONFIRM_NOTIF_ID); - - String actionstring = intent.getStringExtra("actionstring"); - sendConfirmActionstring(actionstring); - - } else if (intent != null && ACTION_CONFIRMCHANGE.equals(intent.getAction())) { - googleApiConnect(); - - //dismiss notification - NotificationManagerCompat notificationManager = - NotificationManagerCompat.from(ListenerService.this); - notificationManager.cancel(CHANGE_NOTIF_ID); - - String actionstring = intent.getStringExtra("actionstring"); - sendConfirmActionstring(actionstring); - - } else if (intent != null && ACTION_INITIATE_ACTION.equals(intent.getAction())) { - googleApiConnect(); - - String actionstring = intent.getStringExtra("actionstring"); - sendInitiateActionstring(actionstring); - - } - - return START_STICKY; - } - - @Override - public void onDataChanged(DataEventBuffer dataEvents) { - - DataMap dataMap; - // Log.d(TAG, logPrefix + "onDataChanged: DataEvents=" + dataEvents); - - for (DataEvent event : dataEvents) { - - if (event.getType() == DataEvent.TYPE_CHANGED) { - - String path = event.getDataItem().getUri().getPath(); - - //Log.d(TAG, "WR: onDataChanged: Path: " + path + ", EventDataItem=" + event.getDataItem()); - - if (path.equals(WearUris.OPEN_SETTINGS_PATH)) { - Intent intent = new Intent(this, AAPSPreferences.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(intent); - } else if (path.equals(WearUris.BOLUS_PROGRESS_PATH)) { - int progress = DataMapItem.fromDataItem(event.getDataItem()).getDataMap().getInt("progresspercent", 0); - String status = DataMapItem.fromDataItem(event.getDataItem()).getDataMap().getString("progressstatus", ""); - showBolusProgress(progress, status); - } else if (path.equals(WearUris.ACTION_CONFIRMATION_REQUEST_PATH)) { - String title = DataMapItem.fromDataItem(event.getDataItem()).getDataMap().getString("title"); - String message = DataMapItem.fromDataItem(event.getDataItem()).getDataMap().getString("message"); - String actionstring = DataMapItem.fromDataItem(event.getDataItem()).getDataMap().getString("actionstring"); - - if ("opencpp".equals(title) && actionstring.startsWith("opencpp")) { - String[] act = actionstring.split("\\s+"); - Intent intent = new Intent(this, CPPActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - //TODO adrian: parse actionstring and add parameters - Bundle params = new Bundle(); - params.putInt("percentage", SafeParse.stringToInt(act[1])); - params.putInt("timeshift", SafeParse.stringToInt(act[2])); - intent.putExtras(params); - startActivity(intent); - } else { - showConfirmationDialog(title, message, actionstring); - } - - } else if (path.equals(WearUris.NEW_STATUS_PATH)) { - dataMap = DataMapItem.fromDataItem(event.getDataItem()).getDataMap(); - Intent messageIntent = new Intent(); - messageIntent.setAction(Intent.ACTION_SEND); - messageIntent.putExtra("status", dataMap.toBundle()); - persistence.storeDataMap(RawDisplayData.STATUS_PERSISTENCE_KEY, dataMap); - LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent); - } else if (path.equals(WearUris.BASAL_DATA_PATH)) { - dataMap = DataMapItem.fromDataItem(event.getDataItem()).getDataMap(); - Intent messageIntent = new Intent(); - messageIntent.setAction(Intent.ACTION_SEND); - messageIntent.putExtra("basals", dataMap.toBundle()); - persistence.storeDataMap(RawDisplayData.BASALS_PERSISTENCE_KEY, dataMap); - LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent); - } else if (path.equals(WearUris.NEW_PREFERENCES_PATH)) { - dataMap = DataMapItem.fromDataItem(event.getDataItem()).getDataMap(); - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - SharedPreferences.Editor editor = sharedPreferences.edit(); - String keyControl = getString(R.string.key_wear_control); - if (dataMap.containsKey(keyControl)) { - boolean previousWearControl = sharedPreferences.getBoolean(keyControl, false); - boolean wearControl = dataMap.getBoolean(keyControl, false); - editor.putBoolean(keyControl, wearControl); - editor.apply(); - if (wearControl != previousWearControl) { - updateTiles(); - } - } - String keyPercentage = getString(R.string.key_boluswizard_percentage); - if (dataMap.containsKey(keyPercentage)) { - int wpercentage = dataMap.getInt(keyPercentage, 100); - editor.putInt(keyPercentage, wpercentage); - editor.apply(); - } - String keyUnits = getString(R.string.key_units_mgdl); - if (dataMap.containsKey(keyUnits)) { - boolean mgdl = dataMap.getBoolean(keyUnits, true); - editor.putBoolean(keyUnits, mgdl); - editor.apply(); - } - String keyMaxCarbs = getString(R.string.key_treatmentssafety_maxcarbs); - if (dataMap.containsKey(keyMaxCarbs)) { - int maxCarbs = dataMap.getInt(keyMaxCarbs, 48); - editor.putInt(keyMaxCarbs, maxCarbs); - editor.apply(); - } - String keyMaxBolus = getString(R.string.key_treatmentssafety_maxbolus); - if (dataMap.containsKey(keyMaxBolus)) { - float maxBolus = (float) dataMap.getDouble(keyMaxBolus, 3.0f); - editor.putFloat(keyMaxBolus, maxBolus); - editor.apply(); - } - - } else if (path.equals(WearUris.QUICK_WIZARD_PATH)) { - dataMap = DataMapItem.fromDataItem(event.getDataItem()).getDataMap(); - Log.i(TAG, "onDataChanged: QUICK_WIZARD_PATH" + dataMap); - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - dataMap.remove("timestamp"); - String key = getString(R.string.key_quick_wizard_data_map); - String dataString = Base64.encodeToString(dataMap.toByteArray(), Base64.DEFAULT); - if (!dataString.equals(sharedPreferences.getString(key, ""))) { - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putString(key, dataString); - editor.apply(); - // Todo maybe add debounce function, due to 20 seconds update limit? - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - TileService.getUpdater(this) - .requestUpdate(QuickWizardTileService.class); - } - Log.i(TAG, "onDataChanged: updated QUICK_WIZARD"); - } else { - Log.i(TAG, "onDataChanged: ignore update"); - } - } else if (path.equals(WearUris.ACTION_CHANGECONFIRMATION_REQUEST_PATH)) { - String title = DataMapItem.fromDataItem(event.getDataItem()).getDataMap().getString("title"); - String message = DataMapItem.fromDataItem(event.getDataItem()).getDataMap().getString("message"); - String actionstring = DataMapItem.fromDataItem(event.getDataItem()).getDataMap().getString("actionstring"); - notifyChangeRequest(title, message, actionstring); - } else if (path.equals(WearUris.ACTION_CANCELNOTIFICATION_REQUEST_PATH)) { - String actionstring = DataMapItem.fromDataItem(event.getDataItem()).getDataMap().getString("actionstring"); - cancelNotificationRequest(actionstring); - } else { - dataMap = DataMapItem.fromDataItem(event.getDataItem()).getDataMap(); - Intent messageIntent = new Intent(); - messageIntent.setAction(Intent.ACTION_SEND); - messageIntent.putExtra("data", dataMap.toBundle()); - persistence.storeDataMap(RawDisplayData.DATA_PERSISTENCE_KEY, dataMap); - LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent); - } - } - } - } - - private void updateTiles() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - TileService.getUpdater(this) - .requestUpdate(ActionsTileService.class); - - TileService.getUpdater(this) - .requestUpdate(TempTargetTileService.class); - - TileService.getUpdater(this) - .requestUpdate(QuickWizardTileService.class); - } - } - - private void notifyChangeRequest(String title, String message, String actionstring) { - // 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) { - CharSequence name = "AAPS Open Loop"; - String description = "Open Loop request notiffication";//getString(R.string.channel_description); - NotificationChannel channel = new NotificationChannel(AAPS_NOTIFY_CHANNEL_ID_OPENLOOP, name, NotificationManager.IMPORTANCE_HIGH); - channel.setDescription(description); - channel.enableVibration(true); - - // Register the channel with the system; you can't change the importance - // or other notification behaviors after this - NotificationManager notificationManager = getSystemService(NotificationManager.class); - notificationManager.createNotificationChannel(channel); - } - - NotificationCompat.Builder builder = - new NotificationCompat.Builder(this, AAPS_NOTIFY_CHANNEL_ID_OPENLOOP); - - builder = builder.setSmallIcon(R.drawable.notif_icon) - .setContentTitle(title) - .setContentText(message) - .setPriority(Notification.PRIORITY_HIGH) - .setVibrate(new long[]{1000, 1000, 1000, 1000, 1000}); - - // Creates an explicit intent for an Activity in your app - Intent intent = new Intent(this, AcceptActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - Bundle params = new Bundle(); - params.putString("title", title); - params.putString("message", message); - params.putString("actionstring", actionstring); - intent.putExtras(params); - - PendingIntent resultPendingIntent = - PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT); - - builder = builder.setContentIntent(resultPendingIntent); - - NotificationManager mNotificationManager = - (NotificationManager) getSystemService(NOTIFICATION_SERVICE); - // mId allows you to update the notification later on. - mNotificationManager.notify(CHANGE_NOTIF_ID, builder.build()); - } - - private void cancelNotificationRequest(String actionstring) { - NotificationManager mNotificationManager = - (NotificationManager) getSystemService(NOTIFICATION_SERVICE); - mNotificationManager.cancel(CHANGE_NOTIF_ID); - } - - private void showBolusProgress(int progresspercent, String progresstatus) { - - long[] vibratePattern; - boolean vibrate = PreferenceManager - .getDefaultSharedPreferences(this).getBoolean("vibrateOnBolus", true); - if (vibrate) { - vibratePattern = new long[]{0, 50, 1000}; - } else { - vibratePattern = new long[]{0, 1, 1000}; - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - createBolusProgressChannels(); - } - - Intent cancelIntent = new Intent(this, ListenerService.class); - cancelIntent.setAction(ACTION_CANCELBOLUS); - PendingIntent cancelPendingIntent = PendingIntent.getService(this, 0, cancelIntent, 0); - - NotificationCompat.Builder notificationBuilder = - new NotificationCompat.Builder(this, vibrate ? AAPS_NOTIFY_CHANNEL_ID_BOLUSPROGRESS : 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); - - NotificationManagerCompat 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 void createBolusProgressChannels() { - createNotificationChannel(new long[]{0, 50, 1000}, AAPS_NOTIFY_CHANNEL_ID_BOLUSPROGRESS, getString(R.string.bolus_progress_channel_name), getString(R.string.bolus_progress_channel_description)); - createNotificationChannel(new long[]{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 void createNotificationChannel(long[] vibratePattern, String channelID, CharSequence name, String description) { - NotificationChannel channel = new NotificationChannel(channelID, name, NotificationManager.IMPORTANCE_HIGH); - channel.setDescription(description); - channel.enableVibration(true); - channel.setVibrationPattern(vibratePattern); - - // Register the channel with the system; you can't change the importance - // or other notification behaviors after this - NotificationManager notificationManager = getSystemService(NotificationManager.class); - notificationManager.createNotificationChannel(channel); - } - - private void showConfirmationDialog(String title, String message, String actionstring) { - Intent intent = new Intent(this, AcceptActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - Bundle params = new Bundle(); - params.putString("title", title); - params.putString("message", message); - params.putString("actionstring", actionstring); - intent.putExtras(params); - startActivity(intent); - } - - private void scheduleDismissBolusprogress(final int seconds) { - bolusprogressThread = new DismissThread(BOLUS_PROGRESS_NOTIF_ID, seconds); - bolusprogressThread.start(); - } - - private class DismissThread extends Thread { - private final int notificationID; - private final int seconds; - private boolean valid = true; - - DismissThread(int notificationID, int seconds) { - this.notificationID = notificationID; - this.seconds = seconds; - } - - public synchronized void invalidate() { - valid = false; - } - - @Override - public void run() { - SystemClock.sleep(seconds * 1000); - synchronized (this) { - if (valid) { - NotificationManagerCompat notificationManager = - NotificationManagerCompat.from(ListenerService.this); - notificationManager.cancel(notificationID); - } - } - } - } - - public static void requestData(Context context) { - Intent intent = new Intent(context, ListenerService.class); - intent.setAction(ACTION_RESEND); - context.startService(intent); - } - - public static void initiateAction(Context context, @NotNull String actionstring) { - Intent intent = new Intent(context, ListenerService.class); - intent.putExtra("actionstring", actionstring); - intent.setAction(ACTION_INITIATE_ACTION); - context.startService(intent); - } - - public static void confirmAction(Context context, String actionstring) { - Intent intent = new Intent(context, ListenerService.class); - intent.putExtra("actionstring", actionstring); - - if (actionstring.equals("changeRequest")) { - intent.setAction(ACTION_CONFIRMCHANGE); - } else { - intent.setAction(ACTION_CONFIRMATION); - } - context.startService(intent); - } - - @Override - public void onConnected(Bundle bundle) { - // Log.d(TAG, logPrefix + "onConnected call requestData"); - - Wearable.ChannelApi.addListener(googleApiClient, this); - // requestData(); - } - - @Override - public void onConnectionSuspended(int i) { - - } - - @Override - public void onConnectionFailed(ConnectionResult connectionResult) { - - } - - @Override - public void onDestroy() { - super.onDestroy(); - if (googleApiClient != null && googleApiClient.isConnected()) { - googleApiClient.disconnect(); - } - - if (googleApiClient != null) { - Wearable.MessageApi.removeListener(googleApiClient, this); - Wearable.ChannelApi.removeListener(googleApiClient, this); - } - } -} diff --git a/wear/src/main/java/info/nightscout/androidaps/data/RawDisplayData.java b/wear/src/main/java/info/nightscout/androidaps/data/RawDisplayData.java index ecadbd7d4f..a13c347a10 100644 --- a/wear/src/main/java/info/nightscout/androidaps/data/RawDisplayData.java +++ b/wear/src/main/java/info/nightscout/androidaps/data/RawDisplayData.java @@ -174,7 +174,7 @@ public class RawDisplayData { wearUtil.releaseWakeLock(wl); } - public DataMap updateBasalsFromMessage(Intent intent, PowerManager.WakeLock wakeLock) { + public DataMap updateBasalsFromMessage(Intent intent) { Bundle bundle = intent.getBundleExtra("basals"); if (bundle != null) { DataMap dataMap = wearUtil.bundleToDataMap(bundle); diff --git a/wear/src/main/java/info/nightscout/androidaps/di/WearActivitiesModule.kt b/wear/src/main/java/info/nightscout/androidaps/di/WearActivitiesModule.kt new file mode 100644 index 0000000000..253935147e --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/di/WearActivitiesModule.kt @@ -0,0 +1,23 @@ +package info.nightscout.androidaps.di + +import dagger.Module +import dagger.android.ContributesAndroidInjector +import info.nightscout.androidaps.interaction.actions.* + +@Module +@Suppress("unused") +abstract class WearActivitiesModule { + + @ContributesAndroidInjector abstract fun contributesBackgroundActionActivity(): BackgroundActionActivity + + @ContributesAndroidInjector abstract fun contributesViewSelectorActivity(): ViewSelectorActivity + @ContributesAndroidInjector abstract fun contributesAcceptActivity(): AcceptActivity + @ContributesAndroidInjector abstract fun contributesBolusActivity(): BolusActivity + @ContributesAndroidInjector abstract fun contributesCarbActivity(): CarbActivity + @ContributesAndroidInjector abstract fun contributesCPPActivity(): CPPActivity + @ContributesAndroidInjector abstract fun contributesECarbActivity(): ECarbActivity + @ContributesAndroidInjector abstract fun contributesFillActivity(): FillActivity + @ContributesAndroidInjector abstract fun contributesTempTargetActivity(): TempTargetActivity + @ContributesAndroidInjector abstract fun contributesTreatmentActivity(): TreatmentActivity + @ContributesAndroidInjector abstract fun contributesWizardActivity(): WizardActivity +} \ No newline at end of file diff --git a/wear/src/main/java/info/nightscout/androidaps/di/WearModule.kt b/wear/src/main/java/info/nightscout/androidaps/di/WearModule.kt index 01e49b6a6f..4315149eb8 100644 --- a/wear/src/main/java/info/nightscout/androidaps/di/WearModule.kt +++ b/wear/src/main/java/info/nightscout/androidaps/di/WearModule.kt @@ -7,6 +7,8 @@ import dagger.Module import dagger.Provides import dagger.android.HasAndroidInjector import info.nightscout.androidaps.Aaps +import info.nightscout.androidaps.utils.rx.AapsSchedulers +import info.nightscout.androidaps.utils.rx.DefaultAapsSchedulers import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.logging.AAPSLoggerProduction import info.nightscout.shared.logging.L @@ -16,7 +18,8 @@ import javax.inject.Singleton @Suppress("unused") @Module(includes = [ - WearModule.AppBindings::class + WearModule.AppBindings::class, + WearActivitiesModule::class ]) open class WearModule { @@ -28,6 +31,10 @@ open class WearModule { @Singleton fun provideAAPSLogger(l: L): AAPSLogger = AAPSLoggerProduction(l) + @Provides + @Singleton + internal fun provideSchedulers(): AapsSchedulers = DefaultAapsSchedulers() + @Module interface AppBindings { 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 49a3eb5561..de47454131 100644 --- a/wear/src/main/java/info/nightscout/androidaps/di/WearServicesModule.kt +++ b/wear/src/main/java/info/nightscout/androidaps/di/WearServicesModule.kt @@ -3,14 +3,15 @@ package info.nightscout.androidaps.di import dagger.Module import dagger.android.ContributesAndroidInjector import info.nightscout.androidaps.complications.* -import info.nightscout.androidaps.data.ListenerService +import info.nightscout.androidaps.data.DataLayerListenerService +import info.nightscout.androidaps.interaction.actions.BackgroundActionActivity import info.nightscout.androidaps.watchfaces.* @Module @Suppress("unused") abstract class WearServicesModule { - @ContributesAndroidInjector abstract fun contributesListenerService(): ListenerService + @ContributesAndroidInjector abstract fun contributesDataLayerListenerService(): DataLayerListenerService @ContributesAndroidInjector abstract fun contributesBaseComplicationProviderService(): BaseComplicationProviderService @ContributesAndroidInjector abstract fun contributesBrCobIobComplication(): BrCobIobComplication 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 1b3c1ba0f2..413bf0ee26 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,5 +1,7 @@ package info.nightscout.androidaps.interaction.actions; +import static info.nightscout.shared.weardata.WearConstants.KEY_ACTION_DATA; + import android.content.Context; import android.content.Intent; import android.os.Bundle; @@ -19,7 +21,10 @@ import androidx.core.view.MotionEventCompat; import androidx.core.view.ViewConfigurationCompat; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.ListenerService; +import info.nightscout.androidaps.data.DataLayerListenerService; +import info.nightscout.androidaps.events.EventWearToMobileChange; +import info.nightscout.androidaps.events.EventWearToMobileConfirm; +import info.nightscout.shared.weardata.ActionData; /** * Created by adrian on 09/02/17. @@ -29,6 +34,7 @@ public class AcceptActivity extends ViewSelectorActivity { String message = ""; String actionstring = ""; + String actionKey = ""; private DismissThread dismissThread; @Override @@ -41,8 +47,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 ("".equals(message) || "".equals(actionstring)) { + if (message.isEmpty() || (actionstring.isEmpty() && actionKey.isEmpty())) { finish(); return; } @@ -60,6 +67,7 @@ public class AcceptActivity extends ViewSelectorActivity { finish(); } + @SuppressWarnings("deprecation") private class MyGridViewPagerAdapter extends GridPagerAdapter { @Override public int getColumnCount(int arg0) { @@ -74,48 +82,53 @@ public class AcceptActivity extends ViewSelectorActivity { @Override public Object instantiateItem(ViewGroup container, int row, int col) { + final View view; if (col == 0) { - final View view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.action_confirm_text, container, false); + view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.action_confirm_text, container, false); final TextView textView = view.findViewById(R.id.message); final View scrollView = view.findViewById(R.id.message_scroll); textView.setText(message); container.addView(view); - scrollView.setOnGenericMotionListener(new View.OnGenericMotionListener() { - @Override - public boolean onGenericMotion(View v, MotionEvent ev) { - if (ev.getAction() == MotionEvent.ACTION_SCROLL && - ev.isFromSource(InputDeviceCompat.SOURCE_ROTARY_ENCODER) - ) { - float delta = -ev.getAxisValue(MotionEventCompat.AXIS_SCROLL) * - ViewConfigurationCompat.getScaledVerticalScrollFactor( - ViewConfiguration.get(container.getContext()), - container.getContext()); - v.scrollBy(0, Math.round(delta)); + scrollView.setOnGenericMotionListener((v, ev) -> { + if (ev.getAction() == MotionEvent.ACTION_SCROLL && + ev.isFromSource(InputDeviceCompat.SOURCE_ROTARY_ENCODER) + ) { + float delta = -ev.getAxisValue(MotionEventCompat.AXIS_SCROLL) * + ViewConfigurationCompat.getScaledVerticalScrollFactor( + ViewConfiguration.get(container.getContext()), + container.getContext()); + v.scrollBy(0, Math.round(delta)); - return true; - } - return false; + return true; } + return false; }); scrollView.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) -> { - ListenerService.confirmAction(AcceptActivity.this, actionstring); + 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)); + } 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/BackgroundActionActivity.kt b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/BackgroundActionActivity.kt index a7db92eea9..4d0ecbc0a9 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 @@ -1,28 +1,26 @@ package info.nightscout.androidaps.interaction.actions -import android.app.Activity import android.os.Bundle -import android.util.Log import android.widget.Toast -import info.nightscout.androidaps.data.ListenerService +import dagger.android.DaggerActivity +import info.nightscout.androidaps.data.DataLayerListenerService +import info.nightscout.shared.logging.AAPSLogger +import info.nightscout.shared.logging.LTag +import javax.inject.Inject -const val TAG = "QuickWizard" +class BackgroundActionActivity : DaggerActivity() { -class BackgroundActionActivity : Activity() { + @Inject lateinit var aapsLogger: AAPSLogger override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val actionString = intent.extras?.getString("actionString") - Log.i(TAG, "QuickWizardActivity.onCreate: actionString=$actionString") - if (actionString != null) { - ListenerService.initiateAction(this, actionString) - val message = intent.extras?.getString("message") - if (message != null) { + intent.extras?.getString("actionString")?.let { actionString -> + aapsLogger.info(LTag.WEAR, "QuickWizardActivity.onCreate: actionString=$actionString") + DataLayerListenerService.initiateAction(this, actionString) + intent.extras?.getString("message")?.let { message -> Toast.makeText(this, message, Toast.LENGTH_LONG).show() } - } else { - Log.e(TAG, "BackgroundActionActivity.onCreate extras 'actionString' required") - } + } ?: aapsLogger.error(LTag.WEAR, "BackgroundActionActivity.onCreate extras 'actionString' required") finishAffinity() } 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 0814ac6a52..55109a352f 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 @@ -1,8 +1,6 @@ package info.nightscout.androidaps.interaction.actions; -import android.content.SharedPreferences; import android.os.Bundle; -import android.preference.PreferenceManager; import android.support.wearable.view.GridPagerAdapter; import android.view.LayoutInflater; import android.view.View; @@ -12,21 +10,21 @@ import android.widget.ImageView; import java.text.DecimalFormat; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.ListenerService; +import info.nightscout.androidaps.events.EventWearToMobileAction; import info.nightscout.androidaps.interaction.utils.PlusMinusEditText; import info.nightscout.shared.SafeParse; +import info.nightscout.shared.weardata.ActionData; public class BolusActivity extends ViewSelectorActivity { PlusMinusEditText editInsulin; - float maxBolus; + double maxBolus; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setAdapter(new MyGridViewPagerAdapter()); - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); - maxBolus = sp.getFloat(getString(R.string.key_treatmentssafety_maxbolus), 3f); + maxBolus = sp.getDouble(getString(R.string.key_treatmentssafety_maxbolus), 3.0); } @Override @@ -35,6 +33,7 @@ public class BolusActivity extends ViewSelectorActivity { finish(); } + @SuppressWarnings("deprecation") private class MyGridViewPagerAdapter extends GridPagerAdapter { @Override public int getColumnCount(int arg0) { @@ -49,30 +48,29 @@ public class BolusActivity 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 (editInsulin != null) { def = SafeParse.stringToDouble(editInsulin.editText.getText().toString()); } - editInsulin = new PlusMinusEditText(view, R.id.amountfield, R.id.plusbutton, R.id.minusbutton, def, 0d, (double)maxBolus, 0.1d, new DecimalFormat("#0.0"),false); + editInsulin = new PlusMinusEditText(view, R.id.amountfield, R.id.plusbutton, R.id.minusbutton, def, 0d, maxBolus, 0.1d, new DecimalFormat("#0.0"), false); setLabelToPlusMinusView(view, getString(R.string.action_insulin)); 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) -> { - String actionstring = "bolus " + SafeParse.stringToDouble(editInsulin.editText.getText().toString()) - + " 0"; // Zero carbs - ListenerService.initiateAction(BolusActivity.this, actionstring); - confirmAction(BolusActivity.this, R.string.action_bolus_confirmation); + 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)); + showToast(BolusActivity.this, R.string.action_bolus_confirmation); finishAffinity(); }); container.addView(view); - return view; } + return view; } @Override 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 d5c488f185..dde240c015 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,9 +10,11 @@ import android.widget.ImageView; import java.text.DecimalFormat; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.ListenerService; +import info.nightscout.androidaps.data.DataLayerListenerService; +import info.nightscout.androidaps.events.EventWearToMobileAction; import info.nightscout.androidaps.interaction.utils.PlusMinusEditText; import info.nightscout.shared.SafeParse; +import info.nightscout.shared.weardata.ActionData; /** * Created by adrian on 09/02/17. @@ -88,15 +90,14 @@ public class CPPActivity 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 = "cppset " + SafeParse.stringToInt(editTimeshift.editText.getText().toString()) - + " " + SafeParse.stringToInt(editPercentage.editText.getText().toString()); - ListenerService.initiateAction(CPPActivity.this, actionstring); - confirmAction(CPPActivity.this, R.string.action_cpp_confirmation); + ActionData.ProfileSwitch ps = + new ActionData.ProfileSwitch(SafeParse.stringToInt(editTimeshift.editText.getText().toString()), SafeParse.stringToInt(editPercentage.editText.getText().toString())); + rxBus.send(new EventWearToMobileAction(ps)); + showToast(CPPActivity.this, R.string.action_cpp_confirmation); finishAffinity(); }); container.addView(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 f5a6b95d08..d9c9298236 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 @@ -1,8 +1,6 @@ package info.nightscout.androidaps.interaction.actions; -import android.content.SharedPreferences; import android.os.Bundle; -import android.preference.PreferenceManager; import android.support.wearable.view.GridPagerAdapter; import android.view.LayoutInflater; import android.view.View; @@ -12,7 +10,7 @@ import android.widget.ImageView; import java.text.DecimalFormat; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.ListenerService; +import info.nightscout.androidaps.data.DataLayerListenerService; import info.nightscout.androidaps.interaction.utils.PlusMinusEditText; import info.nightscout.shared.SafeParse; @@ -25,7 +23,6 @@ public class CarbActivity extends ViewSelectorActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setAdapter(new MyGridViewPagerAdapter()); - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); maxCarbs = sp.getInt(getString(R.string.key_treatmentssafety_maxcarbs), 48); } @@ -66,8 +63,8 @@ public class CarbActivity extends ViewSelectorActivity { confirmbutton.setOnClickListener((View v) -> { // With start time 0 and duration 0 String actionstring = "ecarbs " + SafeParse.stringToInt(editCarbs.editText.getText().toString()) + " 0 0"; - ListenerService.initiateAction(CarbActivity.this, actionstring); - confirmAction(CarbActivity.this, R.string.action_ecarb_confirmation); + DataLayerListenerService.Companion.initiateAction(CarbActivity.this, actionstring); + showToast(CarbActivity.this, R.string.action_ecarb_confirmation); finishAffinity(); }); 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 61d0882d70..cc36635961 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 @@ -1,8 +1,6 @@ package info.nightscout.androidaps.interaction.actions; -import android.content.SharedPreferences; import android.os.Bundle; -import android.preference.PreferenceManager; import android.support.wearable.view.GridPagerAdapter; import android.view.LayoutInflater; import android.view.View; @@ -12,7 +10,7 @@ import android.widget.ImageView; import java.text.DecimalFormat; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.ListenerService; +import info.nightscout.androidaps.data.DataLayerListenerService; import info.nightscout.androidaps.interaction.utils.PlusMinusEditText; import info.nightscout.shared.SafeParse; @@ -31,7 +29,6 @@ public class ECarbActivity extends ViewSelectorActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setAdapter(new MyGridViewPagerAdapter()); - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); maxCarbs = sp.getInt(getString(R.string.key_treatmentssafety_maxcarbs), 48); } @@ -92,14 +89,14 @@ public class ECarbActivity extends ViewSelectorActivity { 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? + //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()); - ListenerService.initiateAction(ECarbActivity.this, actionstring); - confirmAction(ECarbActivity.this, R.string.action_ecarb_confirmation); + DataLayerListenerService.Companion.initiateAction(ECarbActivity.this, actionstring); + showToast(ECarbActivity.this, R.string.action_ecarb_confirmation); finishAffinity(); }); 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 b76829182e..28493146ca 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,7 +10,7 @@ import android.widget.ImageView; import java.text.DecimalFormat; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.ListenerService; +import info.nightscout.androidaps.data.DataLayerListenerService; import info.nightscout.androidaps.interaction.utils.PlusMinusEditText; import info.nightscout.shared.SafeParse; @@ -64,13 +64,13 @@ public class FillActivity extends ViewSelectorActivity { 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? - // (you have to swipe past them anyways - but still) + //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()); - ListenerService.initiateAction(FillActivity.this, actionstring); - confirmAction(FillActivity.this, R.string.action_fill_confirmation); - finishAffinity(); + String actionstring = "fill " + SafeParse.stringToDouble(editInsulin.editText.getText().toString()); + DataLayerListenerService.Companion.initiateAction(FillActivity.this, actionstring); + showToast(FillActivity.this, R.string.action_fill_confirmation); + finishAffinity(); }); container.addView(view); return view; 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 df2db8fafa..4bab152cb5 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 @@ -1,8 +1,6 @@ package info.nightscout.androidaps.interaction.actions; -import android.content.SharedPreferences; import android.os.Bundle; -import android.preference.PreferenceManager; import android.support.wearable.view.GridPagerAdapter; import android.view.LayoutInflater; import android.view.View; @@ -12,7 +10,7 @@ import android.widget.ImageView; import java.text.DecimalFormat; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.ListenerService; +import info.nightscout.androidaps.data.DataLayerListenerService; import info.nightscout.androidaps.interaction.utils.PlusMinusEditText; import info.nightscout.shared.SafeParse; @@ -34,7 +32,6 @@ public class TempTargetActivity extends ViewSelectorActivity { setAdapter(new MyGridViewPagerAdapter()); - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); isMGDL = sp.getBoolean("units_mgdl", true); isSingleTarget = sp.getBoolean("singletarget", true); } @@ -126,8 +123,8 @@ public class TempTargetActivity extends ViewSelectorActivity { + " " + SafeParse.stringToDouble(lowRange.editText.getText().toString()) + " " + (isSingleTarget ? SafeParse.stringToDouble(lowRange.editText.getText().toString()) : SafeParse.stringToDouble(highRange.editText.getText().toString())); - ListenerService.initiateAction(TempTargetActivity.this, actionstring); - confirmAction(TempTargetActivity.this, R.string.action_tempt_confirmation); + DataLayerListenerService.Companion.initiateAction(TempTargetActivity.this, actionstring); + showToast(TempTargetActivity.this, R.string.action_tempt_confirmation); finishAffinity(); }); container.addView(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 7929dc2299..2b0f0dd4a6 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 @@ -1,8 +1,6 @@ package info.nightscout.androidaps.interaction.actions; -import android.content.SharedPreferences; import android.os.Bundle; -import android.preference.PreferenceManager; import android.support.wearable.view.GridPagerAdapter; import android.view.LayoutInflater; import android.view.View; @@ -12,9 +10,11 @@ import android.widget.ImageView; import java.text.DecimalFormat; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.ListenerService; +import info.nightscout.androidaps.data.DataLayerListenerService; +import info.nightscout.androidaps.events.EventWearToMobileAction; import info.nightscout.androidaps.interaction.utils.PlusMinusEditText; import info.nightscout.shared.SafeParse; +import info.nightscout.shared.weardata.ActionData; /** * Created by adrian on 09/02/17. @@ -25,15 +25,14 @@ public class TreatmentActivity extends ViewSelectorActivity { PlusMinusEditText editCarbs; PlusMinusEditText editInsulin; int maxCarbs; - float maxBolus; + double maxBolus; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setAdapter(new MyGridViewPagerAdapter()); - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); maxCarbs = sp.getInt(getString(R.string.key_treatmentssafety_maxcarbs), 48); - maxBolus = sp.getFloat(getString(R.string.key_treatmentssafety_maxbolus), 3f); + maxBolus = sp.getDouble(getString(R.string.key_treatmentssafety_maxbolus), 3.0); } @Override @@ -63,7 +62,7 @@ public class TreatmentActivity extends ViewSelectorActivity { if (editInsulin != null) { def = SafeParse.stringToDouble(editInsulin.editText.getText().toString()); } - editInsulin = new PlusMinusEditText(view, R.id.amountfield, R.id.plusbutton, R.id.minusbutton, def, 0d, (double) maxBolus, 0.1d, new DecimalFormat("#0.0"),false); + editInsulin = new PlusMinusEditText(view, R.id.amountfield, R.id.plusbutton, R.id.minusbutton, def, 0d, (double) maxBolus, 0.1d, new DecimalFormat("#0.0"), false); setLabelToPlusMinusView(view, getString(R.string.action_insulin)); container.addView(view); view.requestFocus(); @@ -74,7 +73,7 @@ public class TreatmentActivity extends ViewSelectorActivity { 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"),false); + editCarbs = new PlusMinusEditText(view, R.id.amountfield, R.id.plusbutton, R.id.minusbutton, def, 0d, (double) maxCarbs, 1d, new DecimalFormat("0"), false); setLabelToPlusMinusView(view, getString(R.string.action_carbs)); container.addView(view); return view; @@ -83,13 +82,12 @@ public class TreatmentActivity extends ViewSelectorActivity { 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? - // (you have to swipe past them anyways - but still) - String actionstring = "bolus " + SafeParse.stringToDouble(editInsulin.editText.getText().toString()) - + " " + SafeParse.stringToInt(editCarbs.editText.getText().toString()); - ListenerService.initiateAction(TreatmentActivity.this, actionstring); - confirmAction(TreatmentActivity.this, R.string.action_treatment_confirmation); - finishAffinity(); + //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)); + showToast(TreatmentActivity.this, R.string.action_treatment_confirmation); + finishAffinity(); }); container.addView(view); return view; diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/ViewSelectorActivity.java b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/ViewSelectorActivity.java index 068cca59f7..a5aaa18768 100644 --- a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/ViewSelectorActivity.java +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/ViewSelectorActivity.java @@ -1,10 +1,7 @@ package info.nightscout.androidaps.interaction.actions; -import android.app.Activity; import android.content.Context; -import android.content.SharedPreferences; import android.os.Bundle; -import android.preference.PreferenceManager; import android.support.wearable.view.DotsPageIndicator; import android.support.wearable.view.GridPagerAdapter; import android.support.wearable.view.GridViewPager; @@ -16,13 +13,21 @@ import android.widget.Toast; import androidx.wear.widget.CurvedTextView; +import javax.inject.Inject; + +import dagger.android.DaggerActivity; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.shared.sharedPreferences.SP; /** * Created by adrian on 13/02/17. */ -public class ViewSelectorActivity extends Activity { +public class ViewSelectorActivity extends DaggerActivity { + + @Inject SP sp; + @Inject RxBus rxBus; private GridViewPager pager; @@ -81,9 +86,7 @@ public class ViewSelectorActivity extends Activity { } View getInflatedPlusMinusView(ViewGroup container) { - SharedPreferences sharedPrefs = PreferenceManager - .getDefaultSharedPreferences(this); - int design = Integer.parseInt(sharedPrefs.getString("input_design", "1")); + int design = sp.getInt("input_design", 1); if (design == 2) { return LayoutInflater.from(getApplicationContext()).inflate(R.layout.action_editplusminus_item_quickrighty, container, false); @@ -102,7 +105,7 @@ public class ViewSelectorActivity extends Activity { textView.setText(labelText); } - void confirmAction(Context context, int text) { + void showToast(Context context, int text) { Toast.makeText(context, getString(text), Toast.LENGTH_LONG).show(); } 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 0d7be17da4..ac2ecb30fe 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 @@ -1,8 +1,6 @@ package info.nightscout.androidaps.interaction.actions; -import android.content.SharedPreferences; import android.os.Bundle; -import android.preference.PreferenceManager; import android.support.wearable.view.GridPagerAdapter; import android.view.LayoutInflater; import android.view.View; @@ -12,7 +10,7 @@ import android.widget.ImageView; import java.text.DecimalFormat; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.ListenerService; +import info.nightscout.androidaps.data.DataLayerListenerService; import info.nightscout.androidaps.interaction.utils.PlusMinusEditText; import info.nightscout.shared.SafeParse; @@ -33,7 +31,6 @@ public class WizardActivity extends ViewSelectorActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setAdapter(new MyGridViewPagerAdapter()); - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); hasPercentage = sp.getBoolean("wizardpercentage", false); percentage = sp.getInt(getString(R.string.key_boluswizard_percentage), 100); maxCarbs = sp.getInt(getString(R.string.key_treatmentssafety_maxcarbs), 48); @@ -93,8 +90,8 @@ public class WizardActivity extends ViewSelectorActivity { String actionstring = "wizard2 " + SafeParse.stringToInt(editCarbs.editText.getText().toString()) + " " + percentage; - ListenerService.initiateAction(WizardActivity.this, actionstring); - confirmAction(WizardActivity.this, R.string.action_wizard_confirmation); + DataLayerListenerService.Companion.initiateAction(WizardActivity.this, actionstring); + showToast(WizardActivity.this, R.string.action_wizard_confirmation); finishAffinity(); }); container.addView(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 index 92f557268f..f11c887130 100644 --- a/wear/src/main/java/info/nightscout/androidaps/interaction/menus/FillMenuActivity.java +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/menus/FillMenuActivity.java @@ -7,7 +7,7 @@ import java.util.ArrayList; import java.util.List; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.ListenerService; +import info.nightscout.androidaps.data.DataLayerListenerService; import info.nightscout.androidaps.interaction.actions.FillActivity; import info.nightscout.androidaps.interaction.utils.MenuListActivity; @@ -37,11 +37,11 @@ public class FillMenuActivity extends MenuListActivity { @Override protected void doAction(String action) { if (getString(R.string.action_preset_1).equals(action)) { - ListenerService.initiateAction(this, "fillpreset 1"); + DataLayerListenerService.Companion.initiateAction(this, "fillpreset 1"); } else if (getString(R.string.action_preset_2).equals(action)) { - ListenerService.initiateAction(this, "fillpreset 2"); + DataLayerListenerService.Companion.initiateAction(this, "fillpreset 2"); } else if (getString(R.string.action_preset_3).equals(action)) { - ListenerService.initiateAction(this, "fillpreset 3"); + DataLayerListenerService.Companion.initiateAction(this, "fillpreset 3"); } else if (getString(R.string.action_free_amount).equals(action)) { Intent intent = new Intent(this, FillActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/menus/MainMenuActivity.java b/wear/src/main/java/info/nightscout/androidaps/interaction/menus/MainMenuActivity.java index fcc21cee0d..92f82f5920 100644 --- a/wear/src/main/java/info/nightscout/androidaps/interaction/menus/MainMenuActivity.java +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/menus/MainMenuActivity.java @@ -9,11 +9,11 @@ import java.util.ArrayList; import java.util.List; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.ListenerService; +import info.nightscout.androidaps.data.DataLayerListenerService; import info.nightscout.androidaps.interaction.AAPSPreferences; -import info.nightscout.androidaps.interaction.actions.TreatmentActivity; import info.nightscout.androidaps.interaction.actions.ECarbActivity; import info.nightscout.androidaps.interaction.actions.TempTargetActivity; +import info.nightscout.androidaps.interaction.actions.TreatmentActivity; import info.nightscout.androidaps.interaction.actions.WizardActivity; import info.nightscout.androidaps.interaction.utils.MenuListActivity; @@ -30,7 +30,7 @@ public class MainMenuActivity extends MenuListActivity { sp = PreferenceManager.getDefaultSharedPreferences(this); setTitle(R.string.label_actions_activity); super.onCreate(savedInstanceState); - ListenerService.requestData(this); + DataLayerListenerService.Companion.requestData(this); } @Override @@ -68,7 +68,7 @@ public class MainMenuActivity extends MenuListActivity { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); this.startActivity(intent); } else if (getString(R.string.menu_resync).equals(action)) { - ListenerService.requestData(this); + DataLayerListenerService.Companion.requestData(this); } else if (getString(R.string.menu_tempt).equals(action)) { intent = new Intent(this, TempTargetActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/menus/StatusMenuActivity.java b/wear/src/main/java/info/nightscout/androidaps/interaction/menus/StatusMenuActivity.java index 8861e45ed6..6f9c5586c8 100644 --- a/wear/src/main/java/info/nightscout/androidaps/interaction/menus/StatusMenuActivity.java +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/menus/StatusMenuActivity.java @@ -6,7 +6,7 @@ import java.util.ArrayList; import java.util.List; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.ListenerService; +import info.nightscout.androidaps.data.DataLayerListenerService; import info.nightscout.androidaps.interaction.utils.MenuListActivity; /** @@ -35,13 +35,13 @@ public class StatusMenuActivity extends MenuListActivity { @Override protected void doAction(String action) { if (getString(R.string.status_pump).equals(action)) { - ListenerService.initiateAction(this, "status pump"); + DataLayerListenerService.Companion.initiateAction(this, "status pump"); } else if (getString(R.string.status_loop).equals(action)) { - ListenerService.initiateAction(this, "status loop"); + DataLayerListenerService.Companion.initiateAction(this, "status loop"); } else if (getString(R.string.status_cpp).equals(action)) { - ListenerService.initiateAction(this, "opencpp"); + DataLayerListenerService.Companion.initiateAction(this, "opencpp"); } else if (getString(R.string.status_tdd).equals(action)) { - ListenerService.initiateAction(this, "tddstats"); + DataLayerListenerService.Companion.initiateAction(this, "tddstats"); } } } diff --git a/wear/src/main/java/info/nightscout/androidaps/watchfaces/BIGChart.java b/wear/src/main/java/info/nightscout/androidaps/watchfaces/BIGChart.java index 73fcac445a..618dc68998 100644 --- a/wear/src/main/java/info/nightscout/androidaps/watchfaces/BIGChart.java +++ b/wear/src/main/java/info/nightscout/androidaps/watchfaces/BIGChart.java @@ -44,7 +44,7 @@ import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.BasalWatchData; import info.nightscout.androidaps.data.BgWatchData; import info.nightscout.androidaps.data.BolusWatchData; -import info.nightscout.androidaps.data.ListenerService; +import info.nightscout.androidaps.data.DataLayerListenerService; import info.nightscout.androidaps.data.TempWatchData; import info.nightscout.androidaps.interaction.menus.MainMenuActivity; import lecho.lib.hellocharts.view.LineChartView; @@ -158,7 +158,7 @@ public class BIGChart extends WatchFace implements SharedPreferences.OnSharedPre mRelativeLayout.getMeasuredHeight()); } }); - ListenerService.requestData(this); + DataLayerListenerService.Companion.requestData(this); wakeLock.acquire(50); } @@ -634,9 +634,9 @@ public class BIGChart extends WatchFace implements SharedPreferences.OnSharedPre } public void missedReadingAlert() { - int minutes_since = (int) Math.floor(timeSince()/(1000*60)); - if(minutes_since >= 16 && ((minutes_since - 16) % 5) == 0) { - ListenerService.requestData(this); // attempt endTime recover missing data + int minutes_since = (int) Math.floor(timeSince() / (1000 * 60)); + if (minutes_since >= 16 && ((minutes_since - 16) % 5) == 0) { + DataLayerListenerService.Companion.requestData(this); // attempt endTime recover missing data } } @@ -690,7 +690,7 @@ public class BIGChart extends WatchFace implements SharedPreferences.OnSharedPre chart.setViewportCalculationEnabled(true); chart.setMaximumViewport(chart.getMaximumViewport()); } else { - ListenerService.requestData(this); + DataLayerListenerService.Companion.requestData(this); } } } diff --git a/wear/src/main/java/info/nightscout/androidaps/watchfaces/BaseWatchFace.java b/wear/src/main/java/info/nightscout/androidaps/watchfaces/BaseWatchFace.java index c28ac459ea..6765a751bb 100644 --- a/wear/src/main/java/info/nightscout/androidaps/watchfaces/BaseWatchFace.java +++ b/wear/src/main/java/info/nightscout/androidaps/watchfaces/BaseWatchFace.java @@ -44,7 +44,7 @@ import javax.inject.Inject; import dagger.android.AndroidInjection; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.ListenerService; +import info.nightscout.androidaps.data.DataLayerListenerService; import info.nightscout.androidaps.data.RawDisplayData; import info.nightscout.androidaps.interaction.utils.Persistence; import info.nightscout.androidaps.interaction.utils.WearUtil; @@ -678,7 +678,7 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { setupBatteryReceiver(); if ("delta_granularity".equals(key)) { - ListenerService.requestData(this); + DataLayerListenerService.Companion.requestData(this); } if (layoutSet) { setDataFields(); @@ -695,7 +695,7 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc public void missedReadingAlert() { int minutes_since = (int) Math.floor(timeSince() / (1000 * 60)); if (rawData.datetime == 0 || minutes_since >= 16 && ((minutes_since - 16) % 5) == 0) { - ListenerService.requestData(this); // Attempt endTime recover missing data + DataLayerListenerService.Companion.requestData(this); // Attempt endTime recover missing data } } @@ -728,7 +728,7 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc setupCharts(); } rawData.updateStatusFromMessage(intent, wakeLock); - rawData.updateBasalsFromMessage(intent, wakeLock); + rawData.updateBasalsFromMessage(intent); if (isSimpleUi()) { if (needUpdate()) { diff --git a/wear/src/main/java/info/nightscout/androidaps/watchfaces/NOChart.java b/wear/src/main/java/info/nightscout/androidaps/watchfaces/NOChart.java index 9e56a8aa21..ed773c68d9 100644 --- a/wear/src/main/java/info/nightscout/androidaps/watchfaces/NOChart.java +++ b/wear/src/main/java/info/nightscout/androidaps/watchfaces/NOChart.java @@ -43,7 +43,7 @@ import java.util.ArrayList; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.BasalWatchData; import info.nightscout.androidaps.data.BgWatchData; -import info.nightscout.androidaps.data.ListenerService; +import info.nightscout.androidaps.data.DataLayerListenerService; import info.nightscout.androidaps.data.TempWatchData; import info.nightscout.androidaps.interaction.menus.MainMenuActivity; @@ -137,7 +137,7 @@ public class NOChart extends WatchFace implements SharedPreferences.OnSharedPref mRelativeLayout.getMeasuredHeight()); } }); - ListenerService.requestData(this); + DataLayerListenerService.Companion.requestData(this); wakeLock.acquire(50); } @@ -518,7 +518,7 @@ public class NOChart extends WatchFace implements SharedPreferences.OnSharedPref public void missedReadingAlert() { int minutes_since = (int) Math.floor(timeSince()/(1000*60)); if(minutes_since >= 16 && ((minutes_since - 16) % 5) == 0) { - ListenerService.requestData(this); // attempt endTime recover missing data + DataLayerListenerService.Companion.requestData(this); // attempt endTime recover missing data } } } diff --git a/wear/src/main/res/values/wear.xml b/wear/src/main/res/values/wear.xml index 41df8ef193..2049ee7ec0 100644 --- a/wear/src/main/res/values/wear.xml +++ b/wear/src/main/res/values/wear.xml @@ -1,18 +1,25 @@ - + + + + + + + androidaps_wear - \ No newline at end of file + diff --git a/wear/src/test/java/info/nightscout/androidaps/data/RawDisplayDataBasalsTest.java b/wear/src/test/java/info/nightscout/androidaps/data/RawDisplayDataBasalsTest.java index 960eeacee5..f6fefeefd6 100644 --- a/wear/src/test/java/info/nightscout/androidaps/data/RawDisplayDataBasalsTest.java +++ b/wear/src/test/java/info/nightscout/androidaps/data/RawDisplayDataBasalsTest.java @@ -220,7 +220,7 @@ public class RawDisplayDataBasalsTest extends TestBase { RawDisplayData newRaw = new RawDisplayData(getWearUtil()); // WHEN - newRaw.updateBasalsFromMessage(intent, null); + newRaw.updateBasalsFromMessage(intent); // THEN assertBasalsOk(newRaw); @@ -233,7 +233,7 @@ public class RawDisplayDataBasalsTest extends TestBase { RawDisplayData newRaw = new RawDisplayData(getWearUtil()); // WHEN - newRaw.updateBasalsFromMessage(intent, null); + newRaw.updateBasalsFromMessage(intent); // THEN assertBasalsEmpty(newRaw);