diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3069f7d830..668d69029d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -167,7 +167,7 @@ @@ -185,23 +185,7 @@ - - - - diff --git a/app/src/main/java/info/nightscout/androidaps/di/ServicesModule.kt b/app/src/main/java/info/nightscout/androidaps/di/ServicesModule.kt index b10b4b90e9..9fc54fa7cf 100644 --- a/app/src/main/java/info/nightscout/androidaps/di/ServicesModule.kt +++ b/app/src/main/java/info/nightscout/androidaps/di/ServicesModule.kt @@ -5,7 +5,7 @@ import dagger.android.ContributesAndroidInjector import info.nightscout.androidaps.plugins.general.nsclient.services.NSClientService import info.nightscout.androidaps.plugins.general.overview.notifications.DismissNotificationService import info.nightscout.androidaps.plugins.general.persistentNotification.DummyService -import info.nightscout.androidaps.plugins.general.wear.wearintegration.WatchUpdaterService +import info.nightscout.androidaps.plugins.general.wear.wearintegration.DataLayerListenerServiceMobile import info.nightscout.androidaps.services.AlarmSoundService import info.nightscout.androidaps.services.LocationService @@ -18,5 +18,5 @@ abstract class ServicesModule { @ContributesAndroidInjector abstract fun contributesDummyService(): DummyService @ContributesAndroidInjector abstract fun contributesLocationService(): LocationService @ContributesAndroidInjector abstract fun contributesNSClientService(): NSClientService - @ContributesAndroidInjector abstract fun contributesWatchUpdaterService(): WatchUpdaterService + @ContributesAndroidInjector abstract fun contributesWatchUpdaterService(): DataLayerListenerServiceMobile } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventBolusRequested.kt b/app/src/main/java/info/nightscout/androidaps/events/EventBolusRequested.kt deleted file mode 100644 index a528ef1656..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/events/EventBolusRequested.kt +++ /dev/null @@ -1,3 +0,0 @@ -package info.nightscout.androidaps.events - -class EventBolusRequested(var amount: Double) : Event() \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt index fa8c085404..0cb9545d61 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/aps/loop/LoopPlugin.kt @@ -27,6 +27,7 @@ import info.nightscout.androidaps.database.entities.ValueWithUnit import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentOfflineEventTransaction import info.nightscout.androidaps.database.transactions.InsertTherapyEventAnnouncementTransaction import info.nightscout.androidaps.events.EventAcceptOpenLoopChange +import info.nightscout.androidaps.events.EventMobileToWear import info.nightscout.androidaps.events.EventTempTargetChange import info.nightscout.androidaps.extensions.buildDeviceStatus import info.nightscout.androidaps.extensions.convertedToAbsolute @@ -44,8 +45,6 @@ import info.nightscout.androidaps.plugins.configBuilder.RunningConfiguration import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification import info.nightscout.androidaps.plugins.general.overview.notifications.Notification -import info.nightscout.androidaps.plugins.general.wear.events.EventWearConfirmAction -import info.nightscout.androidaps.plugins.general.wear.events.EventWearInitiateAction import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpPlugin import info.nightscout.androidaps.queue.Callback import info.nightscout.androidaps.receivers.ReceiverStatusStore @@ -58,6 +57,7 @@ import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.logging.LTag import info.nightscout.shared.sharedPreferences.SP +import info.nightscout.shared.weardata.EventData import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign import javax.inject.Inject @@ -349,7 +349,7 @@ class LoopPlugin @Inject constructor( //only send to wear if Native notifications are turned off if (!sp.getBoolean(R.string.key_raise_notifications_as_android_notifications, true)) { // Send to Wear - rxBus.send(EventWearInitiateAction("changeRequest")) + sendToWear() } } } else { @@ -454,14 +454,28 @@ class LoopPlugin @Inject constructor( rxBus.send(EventNewOpenLoopNotification()) // Send to Wear - rxBus.send(EventWearInitiateAction("changeRequest")) + sendToWear() } private fun dismissSuggestion() { // dismiss notifications val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.cancel(Constants.notificationID) - rxBus.send(EventWearConfirmAction("cancelChangeRequest")) + rxBus.send(EventMobileToWear(EventData.CancelNotification(dateUtil.now()))) + } + + private fun sendToWear() { + lastRun?.let { + rxBus.send( + EventMobileToWear( + EventData.OpenLoopRequest( + rh.gs(R.string.openloop_newsuggestion), + it.constraintsProcessed.toString(), + EventData.OpenLoopRequestConfirmed(dateUtil.now()) + ) + ) + ) + } } override fun acceptChangeRequest() { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt index efcc0c154e..21a8ee9f95 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/overview/OverviewFragment.kt @@ -54,7 +54,6 @@ import info.nightscout.androidaps.plugins.general.overview.activities.QuickWizar import info.nightscout.androidaps.plugins.general.overview.events.* import info.nightscout.androidaps.plugins.general.overview.graphData.GraphData import info.nightscout.androidaps.plugins.general.overview.notifications.NotificationStore -import info.nightscout.androidaps.plugins.general.wear.events.EventWearInitiateAction import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider import info.nightscout.androidaps.plugins.pump.common.defs.PumpType import info.nightscout.androidaps.plugins.pump.omnipod.eros.OmnipodErosPumpPlugin @@ -76,6 +75,7 @@ import info.nightscout.androidaps.utils.ui.UIRunnable import info.nightscout.androidaps.utils.wizard.QuickWizard import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.sharedPreferences.SP +import info.nightscout.shared.weardata.EventData import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign import java.util.* @@ -408,7 +408,7 @@ class OverviewFragment : DaggerFragment(), View.OnClickListener, OnLongClickList ?: "".toSpanned(), { uel.log(Action.ACCEPTS_TEMP_BASAL, Sources.Overview) (context?.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager?)?.cancel(Constants.notificationID) - rxBus.send(EventWearInitiateAction("cancelChangeRequest")) + rxBus.send(EventMobileToWear(EventData.CancelNotification(dateUtil.now()))) Thread { loop.acceptChangeRequest() }.run() binding.buttonsLayout.acceptTempButton.visibility = View.GONE }) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt deleted file mode 100644 index 52a2a5ce27..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/ActionStringHandler.kt +++ /dev/null @@ -1,841 +0,0 @@ -package info.nightscout.androidaps.plugins.general.wear - -import android.app.NotificationManager -import android.content.Context -import dagger.android.HasAndroidInjector -import info.nightscout.androidaps.Constants -import info.nightscout.androidaps.R -import info.nightscout.androidaps.dana.DanaPump -import info.nightscout.androidaps.danaRKorean.DanaRKoreanPlugin -import info.nightscout.androidaps.danaRv2.DanaRv2Plugin -import info.nightscout.androidaps.danar.DanaRPlugin -import info.nightscout.androidaps.danars.DanaRSPlugin -import info.nightscout.androidaps.data.DetailedBolusInfo -import info.nightscout.androidaps.database.AppRepository -import info.nightscout.androidaps.database.ValueWrapper -import info.nightscout.androidaps.database.entities.TemporaryTarget -import info.nightscout.androidaps.database.entities.TotalDailyDose -import info.nightscout.androidaps.database.entities.UserEntry.Action -import info.nightscout.androidaps.database.entities.UserEntry.Sources -import info.nightscout.androidaps.database.entities.ValueWithUnit -import info.nightscout.androidaps.database.interfaces.end -import info.nightscout.androidaps.database.transactions.CancelCurrentTemporaryTargetIfAnyTransaction -import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction -import info.nightscout.androidaps.extensions.total -import info.nightscout.androidaps.extensions.valueToUnits -import info.nightscout.androidaps.interfaces.* -import info.nightscout.androidaps.logging.UserEntryLogger -import info.nightscout.androidaps.plugins.bus.RxBus -import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker -import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification -import info.nightscout.androidaps.plugins.general.wear.events.EventWearConfirmAction -import info.nightscout.androidaps.plugins.general.wear.events.EventWearInitiateAction -import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin -import info.nightscout.androidaps.queue.Callback -import info.nightscout.androidaps.utils.* -import info.nightscout.androidaps.utils.resources.ResourceHelper -import info.nightscout.androidaps.utils.rx.AapsSchedulers -import info.nightscout.androidaps.utils.wizard.BolusWizard -import info.nightscout.androidaps.utils.wizard.QuickWizard -import info.nightscout.shared.SafeParse -import info.nightscout.shared.logging.AAPSLogger -import info.nightscout.shared.logging.LTag -import info.nightscout.shared.sharedPreferences.SP -import info.nightscout.shared.weardata.ActionData -import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_BOLUS -import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_CANCEL_CHANGE_REQUEST -import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_CHANGE_REQUEST -import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_CPP_SET -import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_DISMISS_OVERVIEW_NOTIF -import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_E_CARBS -import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_FILL -import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_FILL_PRESET -import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_OPEN_CPP -import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_QUICK_WIZARD -import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_STATUS -import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_TDD_STATS -import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_TEMPORARY_TARGET -import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_WIZARD -import info.nightscout.shared.weardata.WearConstants.Companion.ACTION_WIZARD2 -import io.reactivex.rxjava3.disposables.CompositeDisposable -import io.reactivex.rxjava3.kotlin.plusAssign -import java.text.DateFormat -import java.text.SimpleDateFormat -import java.util.* -import java.util.concurrent.TimeUnit -import javax.inject.Inject -import javax.inject.Singleton -import kotlin.math.abs -import kotlin.math.min - -@Suppress("SpellCheckingInspection") -@Singleton -class ActionStringHandler @Inject constructor( - private val sp: SP, - private val rxBus: RxBus, - private val aapsLogger: AAPSLogger, - private val aapsSchedulers: AapsSchedulers, - private val rh: ResourceHelper, - private val injector: HasAndroidInjector, - private val context: Context, - private val constraintChecker: ConstraintChecker, - private val profileFunction: ProfileFunction, - private val loop: Loop, - private val wearPlugin: WearPlugin, - private val fabricPrivacy: FabricPrivacy, - private val commandQueue: CommandQueue, - private val activePlugin: ActivePlugin, - private val iobCobCalculator: IobCobCalculator, - private val localInsightPlugin: LocalInsightPlugin, - private val quickWizard: QuickWizard, - private val danaRPlugin: DanaRPlugin, - private val danaRKoreanPlugin: DanaRKoreanPlugin, - private val danaRv2Plugin: DanaRv2Plugin, - private val danaRSPlugin: DanaRSPlugin, - private val danaPump: DanaPump, - private val dateUtil: DateUtil, - private val config: Config, - private val repository: AppRepository, - private val uel: UserEntryLogger, - private val defaultValueHelper: DefaultValueHelper -) { - - private val timeout = 65 * 1000 - private var lastSentTimestamp: Long = 0 - private var lastConfirmActionString: String? = null - private var lastBolusWizard: BolusWizard? = null - - private val disposable = CompositeDisposable() - - fun setup() { - disposable += rxBus - .toObservable(EventWearInitiateAction::class.java) - .observeOn(aapsSchedulers.main) - .subscribe({ handleInitiateActionOnPhone(it.action) }, fabricPrivacy::logException) - - disposable += rxBus - .toObservable(EventWearConfirmAction::class.java) - .observeOn(aapsSchedulers.main) - .subscribe({ handleConfirmation(it.action) }, fabricPrivacy::logException) - } - - fun tearDown() { - disposable.clear() - } - - @Synchronized - private fun handleInitiateActionOnPhone(actionString: String) { - //TODO: i18n - aapsLogger.debug(LTag.WEAR, "handleInitiateActionOnPhone actionString=$actionString") - if (!sp.getBoolean(R.string.key_wear_control, false)) return - lastBolusWizard = null - var rTitle = rh.gs(R.string.confirm).uppercase() - var rMessage = "" - var rAction = "" - - if (actionString.startsWith("{")) { - when (val command = ActionData.deserialize(actionString)) { - is ActionData.Bolus -> { - val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(command.insulin)).value() - val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(command.carbs)).value() - val pump = activePlugin.activePump - if (insulinAfterConstraints > 0 && (!pump.isInitialized() || pump.isSuspended() || loop.isDisconnected)) { - sendError(rh.gs(R.string.wizard_pump_not_available)) - return - } - rMessage += rh.gs(R.string.bolus) + ": " + insulinAfterConstraints + "U\n" - rMessage += rh.gs(R.string.carbs) + ": " + carbsAfterConstraints + "g" - if (insulinAfterConstraints - command.insulin != 0.0 || carbsAfterConstraints - command.carbs != 0) { - rMessage += "\n" + rh.gs(R.string.constraintapllied) - } - rAction += "bolus $insulinAfterConstraints $carbsAfterConstraints" - } - is ActionData.ProfileSwitch -> { - val activeProfileSwitch = repository.getEffectiveProfileSwitchActiveAt(dateUtil.now()).blockingGet() - if (activeProfileSwitch is ValueWrapper.Existing) { - rMessage = "Profile:" + "\n\n" + - "Timeshift: " + command.timeShift + "\n" + - "Percentage: " + command.percentage + "%" - rAction = actionString - } else { // read CPP values - sendError("No active profile switch!") - return - } - } - } - } else { - // do the parsing and check constraints - val act = actionString.split("\\s+".toRegex()).toTypedArray() - when (act[0]) { - ACTION_FILL_PRESET -> { ///////////////////////////////////// PRIME/FILL - val amount: Double = when { - "1" == act[1] -> sp.getDouble("fill_button1", 0.3) - "2" == act[1] -> sp.getDouble("fill_button2", 0.0) - "3" == act[1] -> sp.getDouble("fill_button3", 0.0) - else -> return - } - val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(amount)).value() - rMessage += rh.gs(R.string.primefill) + ": " + insulinAfterConstraints + "U" - if (insulinAfterConstraints - amount != 0.0) rMessage += "\n" + rh.gs(R.string.constraintapllied) - rAction += "fill $insulinAfterConstraints" - } - - ACTION_FILL -> { - val amount = SafeParse.stringToDouble(act[1]) - val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(amount)).value() - rMessage += rh.gs(R.string.primefill) + ": " + insulinAfterConstraints + "U" - if (insulinAfterConstraints - amount != 0.0) rMessage += "\n" + rh.gs(R.string.constraintapllied) - rAction += "fill $insulinAfterConstraints" - } - - ACTION_TEMPORARY_TARGET -> { - aapsLogger.info(LTag.WEAR, "temptarget received: $act") - if ("cancel" == act[1]) { - rMessage += rh.gs(R.string.wear_action_tempt_cancel_message) - rAction = "temptarget true 0 0 0" - } else if ("preset" == act[1]) { - val presetIsMGDL = profileFunction.getUnits() == GlucoseUnit.MGDL - val preset = act[2] - when (preset) { - "activity" -> { - val activityTTDuration = defaultValueHelper.determineActivityTTDuration() - val activityTT = defaultValueHelper.determineActivityTT() - val reason = rh.gs(R.string.activity) - rMessage += rh.gs(R.string.wear_action_tempt_preset_message, reason, activityTT, activityTTDuration) - rAction = "temptarget $presetIsMGDL $activityTTDuration $activityTT $activityTT" - } - - "hypo" -> { - val hypoTTDuration = defaultValueHelper.determineHypoTTDuration() - val hypoTT = defaultValueHelper.determineHypoTT() - val reason = rh.gs(R.string.hypo) - rMessage += rh.gs(R.string.wear_action_tempt_preset_message, reason, hypoTT, hypoTTDuration) - rAction = "temptarget $presetIsMGDL $hypoTTDuration $hypoTT $hypoTT" - } - - "eating" -> { - val eatingSoonTTDuration = defaultValueHelper.determineEatingSoonTTDuration() - val eatingSoonTT = defaultValueHelper.determineEatingSoonTT() - val reason = rh.gs(R.string.eatingsoon) - rMessage += rh.gs(R.string.wear_action_tempt_preset_message, reason, eatingSoonTT, eatingSoonTTDuration) - rAction = "temptarget $presetIsMGDL $eatingSoonTTDuration $eatingSoonTT $eatingSoonTT" - } - - else -> { - sendError(rh.gs(R.string.wear_action_tempt_preset_error, preset)) - return - } - } - } else { - val isMGDL = java.lang.Boolean.parseBoolean(act[1]) - if (profileFunction.getUnits() == GlucoseUnit.MGDL != isMGDL) { - sendError(rh.gs(R.string.wear_action_tempt_unit_error)) - return - } - val duration = SafeParse.stringToInt(act[2]) - if (duration == 0) { - rMessage += rh.gs(R.string.wear_action_tempt_zero_message) - rAction = "temptarget true 0 0 0" - } else { - var low = SafeParse.stringToDouble(act[3]) - var high = SafeParse.stringToDouble(act[4]) - if (!isMGDL) { - low *= Constants.MMOLL_TO_MGDL - high *= Constants.MMOLL_TO_MGDL - } - if (low < HardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[0] || low > HardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[1]) { - sendError(rh.gs(R.string.wear_action_tempt_min_bg_error)) - return - } - if (high < HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[0] || high > HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[1]) { - sendError(rh.gs(R.string.wear_action_tempt_max_bg_error)) - return - } - rMessage += if (act[3] === act[4]) rh.gs(R.string.wear_action_tempt_manual_message, act[3], act[2]) - else rh.gs(R.string.wear_action_tempt_manual_range_message, act[3], act[4], act[2]) - rAction = actionString - } - } - } - - ACTION_STATUS -> { - rTitle = "STATUS" - rAction = "statusmessage" - if ("pump" == act[1]) { - rTitle += " PUMP" - rMessage = pumpStatus - } else if ("loop" == act[1]) { - rTitle += " LOOP" - rMessage = "TARGETS:\n$targetsStatus" - rMessage += "\n\n" + loopStatus - rMessage += "\n\nOAPS RESULT:\n$oAPSResultStatus" - } - } - - ACTION_WIZARD -> { - sendError("Update APP on Watch!") - return - } - - ACTION_WIZARD2 -> { - val pump = activePlugin.activePump - if (!pump.isInitialized() || pump.isSuspended() || loop.isDisconnected) { - sendError(rh.gs(R.string.wizard_pump_not_available)) - return - } - val carbsBeforeConstraints = SafeParse.stringToInt(act[1]) - val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbsBeforeConstraints)).value() - if (carbsAfterConstraints - carbsBeforeConstraints != 0) { - sendError(rh.gs(R.string.wizard_carbs_constraint)) - return - } - val useBG = sp.getBoolean(R.string.key_wearwizard_bg, true) - val useTT = sp.getBoolean(R.string.key_wearwizard_tt, false) - val useBolusIOB = sp.getBoolean(R.string.key_wearwizard_bolusiob, true) - val useBasalIOB = sp.getBoolean(R.string.key_wearwizard_basaliob, true) - val useCOB = sp.getBoolean(R.string.key_wearwizard_cob, true) - val useTrend = sp.getBoolean(R.string.key_wearwizard_trend, false) - val percentage = act[2].toInt() - val profile = profileFunction.getProfile() - val profileName = profileFunction.getProfileName() - if (profile == null) { - sendError(rh.gs(R.string.wizard_no_active_profile)) - return - } - val bgReading = iobCobCalculator.ads.actualBg() - if (bgReading == null) { - sendError(rh.gs(R.string.wizard_no_actual_bg)) - return - } - val cobInfo = iobCobCalculator.getCobInfo(false, "Wizard wear") - if (cobInfo.displayCob == null) { - sendError(rh.gs(R.string.wizard_no_cob)) - return - } - val dbRecord = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() - val tempTarget = if (dbRecord is ValueWrapper.Existing) dbRecord.value else null - - val bolusWizard = BolusWizard(injector).doCalc( - profile, profileName, tempTarget, - carbsAfterConstraints, cobInfo.displayCob!!, bgReading.valueToUnits(profileFunction.getUnits()), - 0.0, percentage, useBG, useCOB, useBolusIOB, useBasalIOB, false, useTT, useTrend, false - ) - val insulinAfterConstraints = bolusWizard.insulinAfterConstraints - val minStep = pump.pumpDescription.pumpType.determineCorrectBolusStepSize(insulinAfterConstraints) - if (abs(insulinAfterConstraints - bolusWizard.calculatedTotalInsulin) >= minStep) { - sendError(rh.gs(R.string.wizard_constraint_bolus_size, bolusWizard.calculatedTotalInsulin)) - return - } - if (bolusWizard.calculatedTotalInsulin <= 0 && bolusWizard.carbs <= 0) { - rAction = "info" - rTitle = rh.gs(R.string.info) - } else { - rAction = actionString - } - rMessage += rh.gs(R.string.wizard_result, bolusWizard.calculatedTotalInsulin, bolusWizard.carbs) - rMessage += "\n_____________" - rMessage += "\n" + bolusWizard.explainShort() - lastBolusWizard = bolusWizard - } - - ACTION_QUICK_WIZARD -> { - val guid = act[1] - val actualBg = iobCobCalculator.ads.actualBg() - val profile = profileFunction.getProfile() - val profileName = profileFunction.getProfileName() - val quickWizardEntry = quickWizard.get(guid) - //Log.i("QuickWizard", "handleInitiate: quick_wizard " + quickWizardEntry?.buttonText() + " c " + quickWizardEntry?.carbs()) - if (quickWizardEntry == null) { - sendError(rh.gs(R.string.quick_wizard_not_available)) - return - } - if (actualBg == null) { - sendError(rh.gs(R.string.wizard_no_actual_bg)) - return - } - if (profile == null) { - sendError(rh.gs(R.string.wizard_no_active_profile)) - return - } - val cobInfo = iobCobCalculator.getCobInfo(false, "QuickWizard wear") - if (cobInfo.displayCob == null) { - sendError(rh.gs(R.string.wizard_no_cob)) - return - } - val pump = activePlugin.activePump - if (!pump.isInitialized() || pump.isSuspended() || loop.isDisconnected) { - sendError(rh.gs(R.string.wizard_pump_not_available)) - return - } - - val wizard = quickWizardEntry.doCalc(profile, profileName, actualBg, true) - - val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(quickWizardEntry.carbs())).value() - if (carbsAfterConstraints != quickWizardEntry.carbs()) { - sendError(rh.gs(R.string.wizard_carbs_constraint)) - return - } - val insulinAfterConstraints = wizard.insulinAfterConstraints - val minStep = pump.pumpDescription.pumpType.determineCorrectBolusStepSize(insulinAfterConstraints) - if (abs(insulinAfterConstraints - wizard.calculatedTotalInsulin) >= minStep) { - sendError(rh.gs(R.string.wizard_constraint_bolus_size, wizard.calculatedTotalInsulin)) - return - } - - rMessage = rh.gs(R.string.quick_wizard_message, quickWizardEntry.buttonText(), wizard.calculatedTotalInsulin, quickWizardEntry.carbs()) - rAction = "bolus $insulinAfterConstraints $carbsAfterConstraints" - //Log.i("QuickWizard", "handleInitiate: quick_wizard action=$rAction") - - rMessage += "\n_____________" - rMessage += "\n" + wizard.explainShort() - - } - - ACTION_OPEN_CPP -> { - val activeProfileSwitch = repository.getEffectiveProfileSwitchActiveAt(dateUtil.now()).blockingGet() - if (activeProfileSwitch is ValueWrapper.Existing) { // read CPP values - rTitle = "opencpp" - rMessage = "opencpp" - rAction = "opencpp" + " " + activeProfileSwitch.value.originalPercentage + " " + activeProfileSwitch.value.originalTimeshift - } else { - sendError("No active profile switch!") - return - } - } - - ACTION_TDD_STATS -> { - val activePump = activePlugin.activePump - // check if DB up to date - val dummies: MutableList = LinkedList() - val historyList = getTDDList(dummies) - if (isOldData(historyList)) { - rTitle = "TDD" - rAction = "statusmessage" - rMessage = "OLD DATA - " - //if pump is not busy: try to fetch data - if (activePump.isBusy()) { - rMessage += rh.gs(R.string.pumpbusy) - } else { - rMessage += "trying to fetch data from pump." - commandQueue.loadTDDs(object : Callback() { - override fun run() { - val dummies1: MutableList = LinkedList() - val historyList1 = getTDDList(dummies1) - if (isOldData(historyList1)) { - sendStatusMessage("TDD: Still old data! Cannot load from pump.\n" + generateTDDMessage(historyList1, dummies1)) - } else { - sendStatusMessage(generateTDDMessage(historyList1, dummies1)) - } - } - }) - } - } else { // if up to date: prepare, send (check if CPP is activated -> add CPP stats) - rTitle = "TDD" - rAction = "statusmessage" - rMessage = generateTDDMessage(historyList, dummies) - } - } - - ACTION_E_CARBS -> { - val carbs = SafeParse.stringToInt(act[1]) - val starttime = SafeParse.stringToInt(act[2]) - val duration = SafeParse.stringToInt(act[3]) - val starttimestamp = System.currentTimeMillis() + starttime * 60 * 1000 - val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbs)).value() - rMessage += rh.gs(R.string.carbs) + ": " + carbsAfterConstraints + "g" - rMessage += "\n" + rh.gs(R.string.time) + ": " + dateUtil.timeString(starttimestamp) - rMessage += "\n" + rh.gs(R.string.duration) + ": " + duration + "h" - if (carbsAfterConstraints - carbs != 0) { - rMessage += "\n" + rh.gs(R.string.constraintapllied) - } - if (carbsAfterConstraints <= 0) { - sendError("Carbs = 0! No action taken!") - return - } - rAction += "ecarbs $carbsAfterConstraints $starttimestamp $duration" - } - - ACTION_CHANGE_REQUEST -> { - rTitle = rh.gs(R.string.openloop_newsuggestion) - rAction = "changeRequest" - loop.lastRun?.let { - rMessage += it.constraintsProcessed - wearPlugin.requestChangeConfirmation(rTitle, rMessage, rAction) - lastSentTimestamp = System.currentTimeMillis() - lastConfirmActionString = rAction - } - return - } - - ACTION_CANCEL_CHANGE_REQUEST -> { - rAction = "cancelChangeRequest" - wearPlugin.requestNotificationCancel(rAction) - return - } - - else -> { - sendError(rh.gs(R.string.wear_unknown_action_string) + " " + act[0]) - return - } - } - } - // send result - wearPlugin.requestActionConfirmation(rTitle, rMessage, rAction) - lastSentTimestamp = System.currentTimeMillis() - lastConfirmActionString = rAction - } - - private fun generateTDDMessage(historyList: MutableList, dummies: MutableList): String { - val profile = profileFunction.getProfile() ?: return "No profile loaded :(" - if (historyList.isEmpty()) { - return "No history data!" - } - val df: DateFormat = SimpleDateFormat("dd.MM.", Locale.getDefault()) - var message = "" - val refTDD = profile.baseBasalSum() * 2 - val pump = activePlugin.activePump - if (df.format(Date(historyList[0].timestamp)) == df.format(Date())) { - val tdd = historyList[0].total - historyList.removeAt(0) - message += "Today: " + DecimalFormatter.to2Decimal(tdd) + "U " + (DecimalFormatter.to0Decimal(100 * tdd / refTDD) + "%") + "\n" - message += "\n" - } else if (pump is DanaRPlugin) { - val tdd = danaPump.dailyTotalUnits - message += "Today: " + DecimalFormatter.to2Decimal(tdd) + "U " + (DecimalFormatter.to0Decimal(100 * tdd / refTDD) + "%") + "\n" - message += "\n" - } - var weighted03 = 0.0 - var weighted05 = 0.0 - var weighted07 = 0.0 - historyList.reverse() - for ((i, record) in historyList.withIndex()) { - val tdd = record.total - if (i == 0) { - weighted03 = tdd - weighted05 = tdd - weighted07 = tdd - } else { - weighted07 = weighted07 * 0.3 + tdd * 0.7 - weighted05 = weighted05 * 0.5 + tdd * 0.5 - weighted03 = weighted03 * 0.7 + tdd * 0.3 - } - } - message += "weighted:\n" - message += "0.3: " + DecimalFormatter.to2Decimal(weighted03) + "U " + (DecimalFormatter.to0Decimal(100 * weighted03 / refTDD) + "%") + "\n" - message += "0.5: " + DecimalFormatter.to2Decimal(weighted05) + "U " + (DecimalFormatter.to0Decimal(100 * weighted05 / refTDD) + "%") + "\n" - message += "0.7: " + DecimalFormatter.to2Decimal(weighted07) + "U " + (DecimalFormatter.to0Decimal(100 * weighted07 / refTDD) + "%") + "\n" - message += "\n" - historyList.reverse() - //add TDDs: - for (record in historyList) { - val tdd = record.total - message += df.format(Date(record.timestamp)) + " " + DecimalFormatter.to2Decimal(tdd) + "U " + (DecimalFormatter.to0Decimal(100 * tdd / refTDD) + "%") + (if (dummies.contains(record)) "x" else "") + "\n" - } - return message - } - - private fun isOldData(historyList: List): Boolean { - val activePump = activePlugin.activePump - val startsYesterday = activePump === danaRPlugin || activePump === danaRSPlugin || activePump === danaRv2Plugin || activePump === danaRKoreanPlugin || activePump === localInsightPlugin - val df: DateFormat = SimpleDateFormat("dd.MM.", Locale.getDefault()) - return historyList.size < 3 || df.format(Date(historyList[0].timestamp)) != df.format(Date(System.currentTimeMillis() - if (startsYesterday) 1000 * 60 * 60 * 24 else 0)) - } - - private fun getTDDList(returnDummies: MutableList): MutableList { - var historyList = repository.getLastTotalDailyDoses(10, false).blockingGet().toMutableList() - //var historyList = databaseHelper.getTDDs().toMutableList() - historyList = historyList.subList(0, min(10, historyList.size)) - //fill single gaps - only needed for Dana*R data - val dummies: MutableList = returnDummies - val df: DateFormat = SimpleDateFormat("dd.MM.", Locale.getDefault()) - for (i in 0 until historyList.size - 1) { - val elem1 = historyList[i] - val elem2 = historyList[i + 1] - if (df.format(Date(elem1.timestamp)) != df.format(Date(elem2.timestamp + 25 * 60 * 60 * 1000))) { - val dummy = TotalDailyDose(timestamp = elem1.timestamp - T.hours(24).msecs(), bolusAmount = elem1.bolusAmount / 2, basalAmount = elem1.basalAmount / 2) - dummies.add(dummy) - elem1.basalAmount /= 2.0 - elem1.bolusAmount /= 2.0 - } - } - historyList.addAll(dummies) - historyList.sortWith { lhs, rhs -> (rhs.timestamp - lhs.timestamp).toInt() } - return historyList - } - - private val pumpStatus: String - get() = activePlugin.activePump.shortStatus(false) - - // decide if enabled/disabled closed/open; what Plugin as APS? - private val loopStatus: String - get() { - var ret = "" - // decide if enabled/disabled closed/open; what Plugin as APS? - if ((loop as PluginBase).isEnabled()) { - ret += if (constraintChecker.isClosedLoopAllowed().value()) { - "CLOSED LOOP\n" - } else { - "OPEN LOOP\n" - } - val aps = activePlugin.activeAPS - ret += "APS: " + (aps as PluginBase).name - val lastRun = loop.lastRun - if (lastRun != null) { - ret += "\nLast Run: " + dateUtil.timeString(lastRun.lastAPSRun) - if (lastRun.lastTBREnact != 0L) ret += "\nLast Enact: " + dateUtil.timeString(lastRun.lastTBREnact) - } - } else { - ret += "LOOP DISABLED\n" - } - return ret - } - - //Check for Temp-Target: - private val targetsStatus: String - get() { - var ret = "" - if (!config.APS) { - return "Targets only apply in APS mode!" - } - val profile = profileFunction.getProfile() ?: return "No profile set :(" - //Check for Temp-Target: - val tempTarget = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() - if (tempTarget is ValueWrapper.Existing) { - ret += "Temp Target: " + Profile.toTargetRangeString(tempTarget.value.lowTarget, tempTarget.value.lowTarget, GlucoseUnit.MGDL, profileFunction.getUnits()) - ret += "\nuntil: " + dateUtil.timeString(tempTarget.value.end) - ret += "\n\n" - } - ret += "DEFAULT RANGE: " - ret += Profile.fromMgdlToUnits(profile.getTargetLowMgdl(), profileFunction.getUnits()).toString() + " - " + Profile.fromMgdlToUnits(profile.getTargetHighMgdl(), profileFunction.getUnits()) - ret += " target: " + Profile.fromMgdlToUnits(profile.getTargetMgdl(), profileFunction.getUnits()) - return ret - } - - private val oAPSResultStatus: String - get() { - var ret = "" - if (!config.APS) - return "Only apply in APS mode!" - val usedAPS = activePlugin.activeAPS - val result = usedAPS.lastAPSResult ?: return "Last result not available!" - ret += if (!result.isChangeRequested) { - rh.gs(R.string.nochangerequested) + "\n" - } else if (result.rate == 0.0 && result.duration == 0) { - rh.gs(R.string.canceltemp) + "\n" - } else { - rh.gs(R.string.rate) + ": " + DecimalFormatter.to2Decimal(result.rate) + " U/h " + - "(" + DecimalFormatter.to2Decimal(result.rate / activePlugin.activePump.baseBasalRate * 100) + "%)\n" + - rh.gs(R.string.duration) + ": " + DecimalFormatter.to0Decimal(result.duration.toDouble()) + " min\n" - } - ret += "\n" + rh.gs(R.string.reason) + ": " + result.reason - return ret - } - - @Synchronized - fun handleConfirmation(actionString: String) { - if (!sp.getBoolean(R.string.key_wear_control, false)) return - //Guard from old or duplicate confirmations - if (lastConfirmActionString == null) return - if (lastConfirmActionString != actionString) return - if (System.currentTimeMillis() - lastSentTimestamp > timeout) return - lastConfirmActionString = null - // do the parsing, check constraints and enact! - val act = actionString.split("\\s+".toRegex()).toTypedArray() - when (act[0]) { - ACTION_FILL -> { - val amount = SafeParse.stringToDouble(act[1]) - val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(amount)).value() - if (amount - insulinAfterConstraints != 0.0) { - ToastUtils.showToastInUiThread(context, "aborting: previously applied constraint changed") - sendError("aborting: previously applied constraint changed") - return - } - doFillBolus(amount) - } - - ACTION_TEMPORARY_TARGET -> { - val duration = SafeParse.stringToInt(act[2]) - val low = SafeParse.stringToDouble(act[3]) - val high = SafeParse.stringToDouble(act[4]) - generateTempTarget(duration, low, high) - } - - ACTION_WIZARD2 -> { - if (lastBolusWizard != null) { //use last calculation as confirmed string matches - doBolus(lastBolusWizard!!.calculatedTotalInsulin, lastBolusWizard!!.carbs, null, 0) - lastBolusWizard = null - } - } - - ACTION_BOLUS -> { - val insulin = SafeParse.stringToDouble(act[1]) - val carbs = SafeParse.stringToInt(act[2]) - doBolus(insulin, carbs, null, 0) - } - - ACTION_CPP_SET -> { - val timeshift = SafeParse.stringToInt(act[1]) - val percentage = SafeParse.stringToInt(act[2]) - setCPP(timeshift, percentage) - } - - ACTION_E_CARBS -> { - val carbs = SafeParse.stringToInt(act[1]) - val starttime = SafeParse.stringToLong(act[2]) - val duration = SafeParse.stringToInt(act[3]) - doECarbs(carbs, starttime, duration) - } - - ACTION_DISMISS_OVERVIEW_NOTIF -> { - rxBus.send(EventDismissNotification(SafeParse.stringToInt(act[1]))) - } - - ACTION_CHANGE_REQUEST -> { - loop.acceptChangeRequest() - val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - notificationManager.cancel(Constants.notificationID) - } - } - lastBolusWizard = null - } - - private fun setCPP(timeshift: Int, percentage: Int) { - var msg = "" - //check for validity - if (percentage < Constants.CPP_MIN_PERCENTAGE || percentage > Constants.CPP_MAX_PERCENTAGE) { - msg += rh.gs(R.string.valueoutofrange, "Profile-Percentage") + "\n" - } - if (timeshift < 0 || timeshift > 23) { - msg += rh.gs(R.string.valueoutofrange, "Profile-Timeshift") + "\n" - } - val profile = profileFunction.getProfile() - if (profile == null) { - msg += rh.gs(R.string.notloadedplugins) + "\n" - } - if ("" != msg) { - msg += rh.gs(R.string.valuesnotstored) - val rTitle = "STATUS" - val rAction = "statusmessage" - wearPlugin.requestActionConfirmation(rTitle, msg, rAction) - lastSentTimestamp = System.currentTimeMillis() - lastConfirmActionString = rAction - return - } - //send profile to pump - uel.log(Action.PROFILE_SWITCH, Sources.Wear, - ValueWithUnit.Percent(percentage), - ValueWithUnit.Hour(timeshift).takeIf { timeshift != 0 }) - profileFunction.createProfileSwitch(0, percentage, timeshift) - } - - private fun generateTempTarget(duration: Int, low: Double, high: Double) { - if (duration != 0) { - disposable += repository.runTransactionForResult( - InsertAndCancelCurrentTemporaryTargetTransaction( - timestamp = System.currentTimeMillis(), - duration = TimeUnit.MINUTES.toMillis(duration.toLong()), - reason = TemporaryTarget.Reason.WEAR, - lowTarget = Profile.toMgdl(low, profileFunction.getUnits()), - highTarget = Profile.toMgdl(high, profileFunction.getUnits()) - ) - ).subscribe({ result -> - result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted temp target $it") } - result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") } - }, { - aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) - }) - uel.log( - Action.TT, Sources.Wear, - ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.WEAR), - ValueWithUnit.fromGlucoseUnit(low, profileFunction.getUnits().asText), - ValueWithUnit.fromGlucoseUnit(high, profileFunction.getUnits().asText).takeIf { low != high }, - ValueWithUnit.Minute(duration) - ) - } else { - disposable += repository.runTransactionForResult(CancelCurrentTemporaryTargetIfAnyTransaction(System.currentTimeMillis())) - .subscribe({ result -> - result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") } - }, { - aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) - }) - uel.log( - Action.CANCEL_TT, Sources.Wear, - ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.WEAR) - ) - } - } - - private fun doFillBolus(amount: Double) { - val detailedBolusInfo = DetailedBolusInfo() - detailedBolusInfo.insulin = amount - detailedBolusInfo.bolusType = DetailedBolusInfo.BolusType.PRIMING - uel.log(Action.PRIME_BOLUS, Sources.Wear, - ValueWithUnit.Insulin(amount).takeIf { amount != 0.0 }) - commandQueue.bolus(detailedBolusInfo, object : Callback() { - override fun run() { - if (!result.success) { - sendError( - rh.gs(R.string.treatmentdeliveryerror) + - "\n" + - result.comment - ) - } - } - }) - } - - private fun doECarbs(carbs: Int, time: Long, duration: Int) { - uel.log(if (duration == 0) Action.CARBS else Action.EXTENDED_CARBS, Sources.Wear, - ValueWithUnit.Timestamp(time), - ValueWithUnit.Gram(carbs), - ValueWithUnit.Hour(duration).takeIf { duration != 0 }) - doBolus(0.0, carbs, time, duration) - } - - private fun doBolus(amount: Double, carbs: Int, carbsTime: Long?, carbsDuration: Int) { - val detailedBolusInfo = DetailedBolusInfo() - detailedBolusInfo.insulin = amount - detailedBolusInfo.carbs = carbs.toDouble() - detailedBolusInfo.bolusType = DetailedBolusInfo.BolusType.NORMAL - detailedBolusInfo.carbsTimestamp = carbsTime - detailedBolusInfo.carbsDuration = T.hours(carbsDuration.toLong()).msecs() - if (detailedBolusInfo.insulin > 0 || detailedBolusInfo.carbs > 0) { - val action = when { - amount.equals(0.0) -> Action.CARBS - carbs.equals(0) -> Action.BOLUS - carbsDuration > 0 -> Action.EXTENDED_CARBS - else -> Action.TREATMENT - } - uel.log(action, Sources.Wear, - ValueWithUnit.Insulin(amount).takeIf { amount != 0.0 }, - ValueWithUnit.Gram(carbs).takeIf { carbs != 0 }, - ValueWithUnit.Hour(carbsDuration).takeIf { carbsDuration != 0 }) - commandQueue.bolus(detailedBolusInfo, object : Callback() { - override fun run() { - if (!result.success) { - sendError( - rh.gs(R.string.treatmentdeliveryerror) + - "\n" + - result.comment - ) - } - } - }) - } - } - - @Synchronized private fun sendError(errorMessage: String) { - wearPlugin.requestActionConfirmation("ERROR", errorMessage, "error") - lastSentTimestamp = System.currentTimeMillis() - lastConfirmActionString = null - lastBolusWizard = null - } - - @Synchronized - private fun sendStatusMessage(message: String) { - wearPlugin.requestActionConfirmation("TDD", message, "statusmessage") - lastSentTimestamp = System.currentTimeMillis() - lastConfirmActionString = null - lastBolusWizard = null - } -} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearFragment.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearFragment.kt index 95e21f730d..0cad0dcd55 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearFragment.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearFragment.kt @@ -6,10 +6,13 @@ import android.view.View import android.view.ViewGroup import dagger.android.support.DaggerFragment import info.nightscout.androidaps.databinding.WearFragmentBinding +import info.nightscout.androidaps.events.EventMobileToWear import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientUpdateGUI +import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.rx.AapsSchedulers +import info.nightscout.shared.weardata.EventData import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign import javax.inject.Inject @@ -20,6 +23,7 @@ class WearFragment : DaggerFragment() { @Inject lateinit var rxBus: RxBus @Inject lateinit var aapsSchedulers: AapsSchedulers @Inject lateinit var fabricPrivacy: FabricPrivacy + @Inject lateinit var dateUtil: DateUtil private var _binding: WearFragmentBinding? = null @@ -37,8 +41,8 @@ class WearFragment : DaggerFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - binding.resend.setOnClickListener { wearPlugin.resendDataToWatch() } - binding.openSettings.setOnClickListener { wearPlugin.openSettings() } + binding.resend.setOnClickListener { rxBus.send(EventData.ActionResendData("WearFragment")) } + binding.openSettings.setOnClickListener { rxBus.send(EventMobileToWear(EventData.OpenSettings(dateUtil.now()))) } } override fun onResume() { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearPlugin.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearPlugin.kt index ead6df3277..3ac1f5adbb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearPlugin.kt +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/WearPlugin.kt @@ -2,24 +2,26 @@ package info.nightscout.androidaps.plugins.general.wear import android.content.Context import android.content.Intent -import dagger.Lazy import dagger.android.HasAndroidInjector import info.nightscout.androidaps.R -import info.nightscout.androidaps.events.* +import info.nightscout.androidaps.events.EventAutosensCalculationFinished +import info.nightscout.androidaps.events.EventMobileToWear +import info.nightscout.androidaps.events.EventPreferenceChange import info.nightscout.androidaps.interfaces.PluginBase import info.nightscout.androidaps.interfaces.PluginDescription import info.nightscout.androidaps.interfaces.PluginType -import info.nightscout.androidaps.plugins.aps.events.EventOpenAPSUpdateGui import info.nightscout.androidaps.plugins.aps.loop.events.EventLoopUpdateGui import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.general.overview.events.EventDismissBolusProgressIfRunning import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress -import info.nightscout.androidaps.plugins.general.wear.wearintegration.WatchUpdaterService +import info.nightscout.androidaps.plugins.general.wear.wearintegration.DataHandlerMobile +import info.nightscout.androidaps.plugins.general.wear.wearintegration.DataLayerListenerServiceMobile import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.sharedPreferences.SP +import info.nightscout.shared.weardata.EventData import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign import javax.inject.Inject @@ -32,10 +34,10 @@ class WearPlugin @Inject constructor( rh: ResourceHelper, private val aapsSchedulers: AapsSchedulers, private val sp: SP, - private val ctx: Context, private val fabricPrivacy: FabricPrivacy, private val rxBus: RxBus, - private val actionStringHandler: Lazy + private val context: Context, + private val dataHandlerMobile: DataHandlerMobile ) : PluginBase( PluginDescription() @@ -55,136 +57,43 @@ class WearPlugin @Inject constructor( override fun onStart() { super.onStart() - disposable += rxBus - .toObservable(EventOpenAPSUpdateGui::class.java) - .observeOn(aapsSchedulers.io) - .subscribe({ sendDataToWatch(status = true, basals = true, bgValue = false) }, fabricPrivacy::logException) - - disposable += rxBus - .toObservable(EventExtendedBolusChange::class.java) - .observeOn(aapsSchedulers.io) - .subscribe({ sendDataToWatch(status = true, basals = true, bgValue = false) }, fabricPrivacy::logException) - disposable += rxBus - .toObservable(EventTempBasalChange::class.java) - .observeOn(aapsSchedulers.io) - .subscribe({ sendDataToWatch(status = true, basals = true, bgValue = false) }, fabricPrivacy::logException) - disposable += rxBus - .toObservable(EventTreatmentChange::class.java) - .observeOn(aapsSchedulers.io) - .subscribe({ sendDataToWatch(status = true, basals = true, bgValue = false) }, fabricPrivacy::logException) - disposable += rxBus - .toObservable(EventEffectiveProfileSwitchChanged::class.java) - .observeOn(aapsSchedulers.io) - .subscribe({ sendDataToWatch(status = false, basals = true, bgValue = false) }, fabricPrivacy::logException) - disposable += rxBus - .toObservable(EventAutosensCalculationFinished::class.java) - .observeOn(aapsSchedulers.io) - .subscribe({ sendDataToWatch(status = true, basals = true, bgValue = true) }, fabricPrivacy::logException) - disposable += rxBus - .toObservable(EventPreferenceChange::class.java) - .observeOn(aapsSchedulers.io) - .subscribe({ - // possibly new high or low mark - resendDataToWatch() - // status may be formatted differently - sendDataToWatch(status = true, basals = false, bgValue = false) - }, fabricPrivacy::logException) - disposable += rxBus - .toObservable(EventLoopUpdateGui::class.java) - .observeOn(aapsSchedulers.io) - .subscribe({ - sendDataToWatch(status = true, basals = false, bgValue = false) - }, fabricPrivacy::logException) - disposable += rxBus - .toObservable(EventBolusRequested::class.java) - .observeOn(aapsSchedulers.io) - .subscribe({ event: EventBolusRequested -> - val status = rh.gs(R.string.bolusrequested, event.amount) - val intent = Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_BOLUS_PROGRESS) - intent.putExtra("progresspercent", 0) - intent.putExtra("progressstatus", status) - ctx.startService(intent) - }, fabricPrivacy::logException) + context.startService(Intent(context, DataLayerListenerServiceMobile::class.java)) disposable += rxBus .toObservable(EventDismissBolusProgressIfRunning::class.java) .observeOn(aapsSchedulers.io) .subscribe({ event: EventDismissBolusProgressIfRunning -> - if (event.result == null) return@subscribe - val status: String = if (event.result!!.success) { - rh.gs(R.string.success) - } else { - rh.gs(R.string.nosuccess) + event.result?.let { + val status = + if (it.success) rh.gs(R.string.success) + else rh.gs(R.string.nosuccess) + if (isEnabled()) rxBus.send(EventMobileToWear(EventData.BolusProgress(percent = 100, status = status))) } - val intent = Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_BOLUS_PROGRESS) - intent.putExtra("progresspercent", 100) - intent.putExtra("progressstatus", status) - ctx.startService(intent) }, fabricPrivacy::logException) disposable += rxBus .toObservable(EventOverviewBolusProgress::class.java) .observeOn(aapsSchedulers.io) .subscribe({ event: EventOverviewBolusProgress -> if (!event.isSMB() || sp.getBoolean("wear_notifySMB", true)) { - val intent = Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_BOLUS_PROGRESS) - intent.putExtra("progresspercent", event.percent) - intent.putExtra("progressstatus", event.status) - ctx.startService(intent) + if (isEnabled()) rxBus.send(EventMobileToWear(EventData.BolusProgress(percent = event.percent, status = event.status))) } }, fabricPrivacy::logException) - actionStringHandler.get().setup() + disposable += rxBus + .toObservable(EventPreferenceChange::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ dataHandlerMobile.resendData("EventPreferenceChange") }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventAutosensCalculationFinished::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ dataHandlerMobile.resendData("EventAutosensCalculationFinished") }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventLoopUpdateGui::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ dataHandlerMobile.resendData("EventLoopUpdateGui") }, fabricPrivacy::logException) } override fun onStop() { disposable.clear() super.onStop() - actionStringHandler.get().tearDown() - } - - private fun sendDataToWatch(status: Boolean, basals: Boolean, bgValue: Boolean) { - // only start service when this plugin is enabled - if (isEnabled()) { - if (bgValue) ctx.startService(Intent(ctx, WatchUpdaterService::class.java)) - if (basals) ctx.startService(Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_BASALS)) - if (status) ctx.startService(Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_SEND_STATUS)) - } - } - - fun resendDataToWatch() { - ctx.startService(Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_RESEND)) - } - - fun openSettings() { - ctx.startService(Intent(ctx, WatchUpdaterService::class.java).setAction(WatchUpdaterService.ACTION_OPEN_SETTINGS)) - } - - fun requestNotificationCancel(actionString: String?) { - ctx.startService( - Intent(ctx, WatchUpdaterService::class.java) - .setAction(WatchUpdaterService.ACTION_CANCEL_NOTIFICATION) - .also { - it.putExtra("actionstring", actionString) - }) - } - - fun requestActionConfirmation(title: String, message: String, actionString: String) { - ctx.startService( - Intent(ctx, WatchUpdaterService::class.java) - .setAction(WatchUpdaterService.ACTION_SEND_ACTION_CONFIRMATION_REQUEST) - .also { - it.putExtra("title", title) - it.putExtra("message", message) - it.putExtra("actionstring", actionString) - }) - } - - fun requestChangeConfirmation(title: String, message: String, actionString: String) { - ctx.startService( - Intent(ctx, WatchUpdaterService::class.java) - .setAction(WatchUpdaterService.ACTION_SEND_CHANGE_CONFIRMATION_REQUEST) - .also { - it.putExtra("title", title) - it.putExtra("message", message) - it.putExtra("actionstring", actionString) - }) + context.stopService(Intent(context, DataLayerListenerServiceMobile::class.java)) } } \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/events/EventWearConfirmAction.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/events/EventWearConfirmAction.kt deleted file mode 100644 index 211836d27d..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/events/EventWearConfirmAction.kt +++ /dev/null @@ -1,5 +0,0 @@ -package info.nightscout.androidaps.plugins.general.wear.events - -import info.nightscout.androidaps.events.Event - -class EventWearConfirmAction(val action: String) : Event() \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/events/EventWearInitiateAction.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/events/EventWearInitiateAction.kt deleted file mode 100644 index 9661c4af38..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/events/EventWearInitiateAction.kt +++ /dev/null @@ -1,5 +0,0 @@ -package info.nightscout.androidaps.plugins.general.wear.events - -import info.nightscout.androidaps.events.Event - -class EventWearInitiateAction(val action: String) : Event() diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataHandlerMobile.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataHandlerMobile.kt new file mode 100644 index 0000000000..16db4f7241 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataHandlerMobile.kt @@ -0,0 +1,1173 @@ +package info.nightscout.androidaps.plugins.general.wear.wearintegration + +import android.app.NotificationManager +import android.content.Context +import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.Constants +import info.nightscout.androidaps.R +import info.nightscout.androidaps.data.DetailedBolusInfo +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.database.ValueWrapper +import info.nightscout.androidaps.database.entities.* +import info.nightscout.androidaps.database.interfaces.end +import info.nightscout.androidaps.database.transactions.CancelCurrentTemporaryTargetIfAnyTransaction +import info.nightscout.androidaps.database.transactions.InsertAndCancelCurrentTemporaryTargetTransaction +import info.nightscout.androidaps.events.EventMobileToWear +import info.nightscout.androidaps.extensions.convertedToAbsolute +import info.nightscout.androidaps.extensions.toStringShort +import info.nightscout.androidaps.extensions.total +import info.nightscout.androidaps.extensions.valueToUnits +import info.nightscout.androidaps.extensions.valueToUnitsString +import info.nightscout.androidaps.interfaces.* +import info.nightscout.androidaps.logging.UserEntryLogger +import info.nightscout.androidaps.plugins.bus.RxBus +import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker +import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus +import info.nightscout.androidaps.plugins.general.overview.graphExtensions.GlucoseValueDataPoint +import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider +import info.nightscout.androidaps.queue.Callback +import info.nightscout.androidaps.receivers.ReceiverStatusStore +import info.nightscout.androidaps.utils.* +import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.androidaps.utils.rx.AapsSchedulers +import info.nightscout.androidaps.utils.wizard.BolusWizard +import info.nightscout.androidaps.utils.wizard.QuickWizard +import info.nightscout.androidaps.utils.wizard.QuickWizardEntry +import info.nightscout.shared.logging.AAPSLogger +import info.nightscout.shared.logging.LTag +import info.nightscout.shared.sharedPreferences.SP +import info.nightscout.shared.weardata.EventData +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign +import java.text.DateFormat +import java.text.SimpleDateFormat +import java.util.* +import java.util.concurrent.TimeUnit +import java.util.stream.Collectors +import javax.inject.Inject +import javax.inject.Singleton +import kotlin.collections.ArrayList +import kotlin.math.abs +import kotlin.math.min + +@Singleton +class DataHandlerMobile @Inject constructor( + aapsSchedulers: AapsSchedulers, + private val injector: HasAndroidInjector, + private val context: Context, + private val rxBus: RxBus, + private val aapsLogger: AAPSLogger, + private val rh: ResourceHelper, + private val sp: SP, + private val config: Config, + private val iobCobCalculator: IobCobCalculator, + private val repository: AppRepository, + private val glucoseStatusProvider: GlucoseStatusProvider, + private val profileFunction: ProfileFunction, + private val loop: Loop, + private val nsDeviceStatus: NSDeviceStatus, + private val receiverStatusStore: ReceiverStatusStore, + private val quickWizard: QuickWizard, + private val defaultValueHelper: DefaultValueHelper, + private val trendCalculator: TrendCalculator, + private val dateUtil: DateUtil, + private val constraintChecker: ConstraintChecker, + private val uel: UserEntryLogger, + private val activePlugin: ActivePlugin, + private val commandQueue: CommandQueue, + private val fabricPrivacy: FabricPrivacy +) { + + private val disposable = CompositeDisposable() + + private var lastBolusWizard: BolusWizard? = null + + init { + // From Wear + disposable += rxBus + .toObservable(EventData.ActionPong::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + aapsLogger.debug(LTag.WEAR, "Pong received from ${it.sourceNodeId}") + }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventData.CancelBolus::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + aapsLogger.debug(LTag.WEAR, "CancelBolus received from ${it.sourceNodeId}") + activePlugin.activePump.stopBolusDelivering() + }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventData.OpenLoopRequestConfirmed::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + aapsLogger.debug(LTag.WEAR, "OpenLoopRequestConfirmed received from ${it.sourceNodeId}") + loop.acceptChangeRequest() + (context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).cancel(Constants.notificationID) + }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventData.ActionResendData::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + aapsLogger.debug(LTag.WEAR, "ResendData received from ${it.sourceNodeId}") + resendData(it.from) + }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventData.ActionPumpStatus::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + aapsLogger.debug(LTag.WEAR, "ActionPumpStatus received from ${it.sourceNodeId}") + rxBus.send( + EventMobileToWear( + EventData.ConfirmAction( + rh.gs(R.string.medtronic_pump_status).uppercase(), + activePlugin.activePump.shortStatus(false), + returnCommand = null + ) + ) + ) + }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventData.ActionLoopStatus::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + aapsLogger.debug(LTag.WEAR, "ActionLoopStatus received from ${it.sourceNodeId}") + rxBus.send( + EventMobileToWear( + EventData.ConfirmAction( + rh.gs(R.string.loop_status).uppercase(), + "TARGETS:\n$targetsStatus\n\n$loopStatus\n\nOAPS RESULT:\n$oAPSResultStatus", + returnCommand = null + ) + ) + ) + }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventData.ActionTddStatus::class.java) + .observeOn(aapsSchedulers.io) + .subscribe { + aapsLogger.debug(LTag.WEAR, "ActionTddStatus received from ${it.sourceNodeId}") + handleTddStatus() + } + disposable += rxBus + .toObservable(EventData.ActionProfileSwitchSendInitialData::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + aapsLogger.debug(LTag.WEAR, "ActionProfileSwitchSendInitialData received $it from ${it.sourceNodeId}") + handleProfileSwitchSendInitialData() + }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventData.ActionProfileSwitchPreCheck::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + aapsLogger.debug(LTag.WEAR, "ActionProfileSwitchPreCheck received $it from ${it.sourceNodeId}") + handleProfileSwitchPreCheck(it) + }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventData.ActionProfileSwitchConfirmed::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + aapsLogger.debug(LTag.WEAR, "ActionProfileSwitchConfirmed received $it from ${it.sourceNodeId}") + doProfileSwitch(it) + }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventData.ActionTempTargetPreCheck::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + aapsLogger.debug(LTag.WEAR, "ActionTempTargetPreCheck received $it from ${it.sourceNodeId}") + handleTempTargetPreCheck(it) + }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventData.ActionTempTargetConfirmed::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + aapsLogger.debug(LTag.WEAR, "ActionTempTargetConfirmed received $it from ${it.sourceNodeId}") + doTempTarget(it) + }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventData.ActionBolusPreCheck::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + aapsLogger.debug(LTag.WEAR, "ActionBolusPreCheck received $it from ${it.sourceNodeId}") + handleBolusPreCheck(it) + }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventData.ActionBolusConfirmed::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + aapsLogger.debug(LTag.WEAR, "ActionBolusConfirmed received $it from ${it.sourceNodeId}") + doBolus(it.insulin, it.carbs, null, 0) + }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventData.ActionECarbsPreCheck::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + aapsLogger.debug(LTag.WEAR, "ActionECarbsPreCheck received $it from ${it.sourceNodeId}") + handleECarbsPreCheck(it) + }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventData.ActionECarbsConfirmed::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + aapsLogger.debug(LTag.WEAR, "ActionECarbsConfirmed received $it from ${it.sourceNodeId}") + doECarbs(it.carbs, it.carbsTime, it.duration) + }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventData.ActionFillPresetPreCheck::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + aapsLogger.debug(LTag.WEAR, "ActionFillPresetPreCheck received $it from ${it.sourceNodeId}") + handleFillPresetPreCheck(it) + }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventData.ActionFillPreCheck::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + aapsLogger.debug(LTag.WEAR, "ActionFillPreCheck received $it from ${it.sourceNodeId}") + handleFillPreCheck(it) + }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventData.ActionFillConfirmed::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + aapsLogger.debug(LTag.WEAR, "ActionFillConfirmed received $it from ${it.sourceNodeId}") + if (constraintChecker.applyBolusConstraints(Constraint(it.insulin)).value() - it.insulin != 0.0) { + ToastUtils.showToastInUiThread(context, "aborting: previously applied constraint changed") + sendError("aborting: previously applied constraint changed") + } else + doFillBolus(it.insulin) + }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventData.ActionQuickWizardPreCheck::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + aapsLogger.debug(LTag.WEAR, "ActionWizardPreCheck received $it from ${it.sourceNodeId}") + handleQuickWizardPreCheck(it) + }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventData.ActionWizardPreCheck::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + aapsLogger.debug(LTag.WEAR, "ActionWizardPreCheck received $it from ${it.sourceNodeId}") + handleWizardPreCheck(it) + }, fabricPrivacy::logException) + disposable += rxBus + .toObservable(EventData.ActionWizardConfirmed::class.java) + .observeOn(aapsSchedulers.io) + .subscribe({ + aapsLogger.debug(LTag.WEAR, "ActionWizardConfirmed received $it from ${it.sourceNodeId}") + if (lastBolusWizard?.timeStamp == it.timeStamp) { //use last calculation as confirmed string matches + doBolus(lastBolusWizard!!.calculatedTotalInsulin, lastBolusWizard!!.carbs, null, 0) + } + lastBolusWizard = null + }, fabricPrivacy::logException) + } + + private fun handleTddStatus() { + val activePump = activePlugin.activePump + var message: String + // check if DB up to date + val dummies: MutableList = LinkedList() + val historyList = getTDDList(dummies) + if (isOldData(historyList)) { + message = "OLD DATA - " + //if pump is not busy: try to fetch data + if (activePump.isBusy()) { + message += rh.gs(R.string.pumpbusy) + } else { + message += "trying to fetch data from pump." + commandQueue.loadTDDs(object : Callback() { + override fun run() { + val dummies1: MutableList = LinkedList() + val historyList1 = getTDDList(dummies1) + val reloadMessage = + if (isOldData(historyList1)) + "TDD: Still old data! Cannot load from pump.\n" + generateTDDMessage(historyList1, dummies1) + else + generateTDDMessage(historyList1, dummies1) + rxBus.send( + EventMobileToWear( + EventData.ConfirmAction( + rh.gs(R.string.tdd), + reloadMessage, + returnCommand = null + ) + ) + ) + } + }) + } + } else { // if up to date: prepare, send (check if CPP is activated -> add CPP stats) + message = generateTDDMessage(historyList, dummies) + } + rxBus.send( + EventMobileToWear( + EventData.ConfirmAction( + rh.gs(R.string.tdd), + message, + returnCommand = null + ) + ) + ) + } + + private fun handleWizardPreCheck(command: EventData.ActionWizardPreCheck) { + val pump = activePlugin.activePump + if (!pump.isInitialized() || pump.isSuspended() || loop.isDisconnected) { + sendError(rh.gs(R.string.wizard_pump_not_available)) + return + } + val carbsBeforeConstraints = command.carbs + val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(carbsBeforeConstraints)).value() + if (carbsAfterConstraints - carbsBeforeConstraints != 0) { + sendError(rh.gs(R.string.wizard_carbs_constraint)) + return + } + val useBG = sp.getBoolean(R.string.key_wearwizard_bg, true) + val useTT = sp.getBoolean(R.string.key_wearwizard_tt, false) + val useBolusIOB = sp.getBoolean(R.string.key_wearwizard_bolusiob, true) + val useBasalIOB = sp.getBoolean(R.string.key_wearwizard_basaliob, true) + val useCOB = sp.getBoolean(R.string.key_wearwizard_cob, true) + val useTrend = sp.getBoolean(R.string.key_wearwizard_trend, false) + val percentage = command.percentage + val profile = profileFunction.getProfile() + val profileName = profileFunction.getProfileName() + if (profile == null) { + sendError(rh.gs(R.string.wizard_no_active_profile)) + return + } + val bgReading = iobCobCalculator.ads.actualBg() + if (bgReading == null) { + sendError(rh.gs(R.string.wizard_no_actual_bg)) + return + } + val cobInfo = iobCobCalculator.getCobInfo(false, "Wizard wear") + if (cobInfo.displayCob == null) { + sendError(rh.gs(R.string.wizard_no_cob)) + return + } + val dbRecord = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() + val tempTarget = if (dbRecord is ValueWrapper.Existing) dbRecord.value else null + + val bolusWizard = BolusWizard(injector).doCalc( + profile, profileName, tempTarget, + carbsAfterConstraints, cobInfo.displayCob!!, bgReading.valueToUnits(profileFunction.getUnits()), + 0.0, percentage, useBG, useCOB, useBolusIOB, useBasalIOB, false, useTT, useTrend, false + ) + val insulinAfterConstraints = bolusWizard.insulinAfterConstraints + val minStep = pump.pumpDescription.pumpType.determineCorrectBolusStepSize(insulinAfterConstraints) + if (abs(insulinAfterConstraints - bolusWizard.calculatedTotalInsulin) >= minStep) { + sendError(rh.gs(R.string.wizard_constraint_bolus_size, bolusWizard.calculatedTotalInsulin)) + return + } + if (bolusWizard.calculatedTotalInsulin <= 0 && bolusWizard.carbs <= 0) { + sendError("No insulin required") + return + } + val message = + rh.gs(R.string.wizard_result, bolusWizard.calculatedTotalInsulin, bolusWizard.carbs) + "\n_____________\n" + bolusWizard.explainShort() + lastBolusWizard = bolusWizard + rxBus.send( + EventMobileToWear( + EventData.ConfirmAction( + rh.gs(R.string.confirm).uppercase(), message, + returnCommand = EventData.ActionWizardConfirmed(bolusWizard.timeStamp) + ) + ) + ) + } + + private fun handleQuickWizardPreCheck(command: EventData.ActionQuickWizardPreCheck) { + val actualBg = iobCobCalculator.ads.actualBg() + val profile = profileFunction.getProfile() + val profileName = profileFunction.getProfileName() + val quickWizardEntry = quickWizard.get(command.guid) + //Log.i("QuickWizard", "handleInitiate: quick_wizard " + quickWizardEntry?.buttonText() + " c " + quickWizardEntry?.carbs()) + if (quickWizardEntry == null) { + sendError(rh.gs(R.string.quick_wizard_not_available)) + return + } + if (actualBg == null) { + sendError(rh.gs(R.string.wizard_no_actual_bg)) + return + } + if (profile == null) { + sendError(rh.gs(R.string.wizard_no_active_profile)) + return + } + val cobInfo = iobCobCalculator.getCobInfo(false, "QuickWizard wear") + if (cobInfo.displayCob == null) { + sendError(rh.gs(R.string.wizard_no_cob)) + return + } + val pump = activePlugin.activePump + if (!pump.isInitialized() || pump.isSuspended() || loop.isDisconnected) { + sendError(rh.gs(R.string.wizard_pump_not_available)) + return + } + + val wizard = quickWizardEntry.doCalc(profile, profileName, actualBg, true) + + val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(quickWizardEntry.carbs())).value() + if (carbsAfterConstraints != quickWizardEntry.carbs()) { + sendError(rh.gs(R.string.wizard_carbs_constraint)) + return + } + val insulinAfterConstraints = wizard.insulinAfterConstraints + val minStep = pump.pumpDescription.pumpType.determineCorrectBolusStepSize(insulinAfterConstraints) + if (abs(insulinAfterConstraints - wizard.calculatedTotalInsulin) >= minStep) { + sendError(rh.gs(R.string.wizard_constraint_bolus_size, wizard.calculatedTotalInsulin)) + return + } + + val message = rh.gs(R.string.quick_wizard_message, quickWizardEntry.buttonText(), wizard.calculatedTotalInsulin, quickWizardEntry.carbs()) + + "\n_____________\n" + wizard.explainShort() + + rxBus.send( + EventMobileToWear( + EventData.ConfirmAction( + rh.gs(R.string.confirm).uppercase(), message, + returnCommand = EventData.ActionBolusConfirmed(insulinAfterConstraints, carbsAfterConstraints) + ) + ) + ) + } + + private fun handleBolusPreCheck(command: EventData.ActionBolusPreCheck) { + val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(command.insulin)).value() + val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(command.carbs)).value() + val pump = activePlugin.activePump + if (insulinAfterConstraints > 0 && (!pump.isInitialized() || pump.isSuspended() || loop.isDisconnected)) { + sendError(rh.gs(R.string.wizard_pump_not_available)) + return + } + var message = "" + message += rh.gs(R.string.bolus) + ": " + insulinAfterConstraints + "U\n" + message += rh.gs(R.string.carbs) + ": " + carbsAfterConstraints + "g" + if (insulinAfterConstraints - command.insulin != 0.0 || carbsAfterConstraints - command.carbs != 0) + message += "\n" + rh.gs(R.string.constraintapllied) + rxBus.send( + EventMobileToWear( + EventData.ConfirmAction( + rh.gs(R.string.confirm).uppercase(), message, + returnCommand = EventData.ActionBolusConfirmed(insulinAfterConstraints, carbsAfterConstraints) + ) + ) + ) + } + + private fun handleECarbsPreCheck(command: EventData.ActionECarbsPreCheck) { + val startTimeStamp = System.currentTimeMillis() + T.hours(command.carbsTimeShift.toLong()).msecs() + val carbsAfterConstraints = constraintChecker.applyCarbsConstraints(Constraint(command.carbs)).value() + var message = rh.gs(R.string.carbs) + ": " + carbsAfterConstraints + "g" + + "\n" + rh.gs(R.string.time) + ": " + dateUtil.timeString(startTimeStamp) + + "\n" + rh.gs(R.string.duration) + ": " + command.duration + "h" + if (carbsAfterConstraints - command.carbs != 0) { + message += "\n" + rh.gs(R.string.constraintapllied) + } + if (carbsAfterConstraints <= 0) { + sendError("Carbs = 0! No action taken!") + return + } + rxBus.send( + EventMobileToWear( + EventData.ConfirmAction( + rh.gs(R.string.confirm).uppercase(), message, + returnCommand = EventData.ActionECarbsConfirmed(carbsAfterConstraints, startTimeStamp, command.duration) + ) + ) + ) + } + + private fun handleFillPresetPreCheck(command: EventData.ActionFillPresetPreCheck) { + val amount: Double = when (command.button) { + 1 -> sp.getDouble("fill_button1", 0.3) + 2 -> sp.getDouble("fill_button2", 0.0) + 3 -> sp.getDouble("fill_button3", 0.0) + else -> return + } + val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(amount)).value() + var message = rh.gs(R.string.primefill) + ": " + insulinAfterConstraints + "U" + if (insulinAfterConstraints - amount != 0.0) message += "\n" + rh.gs(R.string.constraintapllied) + rxBus.send( + EventMobileToWear( + EventData.ConfirmAction( + rh.gs(R.string.confirm).uppercase(), message, + returnCommand = EventData.ActionFillConfirmed(insulinAfterConstraints) + ) + ) + ) + } + + private fun handleFillPreCheck(command: EventData.ActionFillPreCheck) { + val insulinAfterConstraints = constraintChecker.applyBolusConstraints(Constraint(command.insulin)).value() + var message = rh.gs(R.string.primefill) + ": " + insulinAfterConstraints + "U" + if (insulinAfterConstraints - command.insulin != 0.0) message += "\n" + rh.gs(R.string.constraintapllied) + rxBus.send( + EventMobileToWear( + EventData.ConfirmAction( + rh.gs(R.string.confirm).uppercase(), message, + returnCommand = EventData.ActionFillConfirmed(insulinAfterConstraints) + ) + ) + ) + } + + private fun handleProfileSwitchSendInitialData() { + val activeProfileSwitch = repository.getEffectiveProfileSwitchActiveAt(dateUtil.now()).blockingGet() + if (activeProfileSwitch is ValueWrapper.Existing) { // read CPP values + rxBus.send( + EventMobileToWear(EventData.ActionProfileSwitchOpenActivity(T.msecs(activeProfileSwitch.value.originalTimeshift).hours().toInt(), activeProfileSwitch.value.originalPercentage)) + ) + } else { + sendError("No active profile switch!") + return + } + + } + + private fun handleProfileSwitchPreCheck(command: EventData.ActionProfileSwitchPreCheck) { + val activeProfileSwitch = repository.getEffectiveProfileSwitchActiveAt(dateUtil.now()).blockingGet() + if (activeProfileSwitch is ValueWrapper.Absent) { + sendError("No active profile switch!") + } + if (command.percentage < Constants.CPP_MIN_PERCENTAGE || command.percentage > Constants.CPP_MAX_PERCENTAGE) { + sendError(rh.gs(R.string.valueoutofrange, "Profile-Percentage")) + } + if (command.timeShift < 0 || command.timeShift > 23) { + sendError(rh.gs(R.string.valueoutofrange, "Profile-Timeshift")) + } + val message = "Profile:" + "\n\n" + + "Timeshift: " + command.timeShift + "\n" + + "Percentage: " + command.percentage + "%" + rxBus.send( + EventMobileToWear( + EventData.ConfirmAction( + rh.gs(R.string.confirm).uppercase(), message, + returnCommand = EventData.ActionProfileSwitchConfirmed(command.timeShift, command.percentage) + ) + ) + ) + } + + private fun handleTempTargetPreCheck(action: EventData.ActionTempTargetPreCheck) { + val title = rh.gs(R.string.confirm).uppercase() + var message = "" + val presetIsMGDL = profileFunction.getUnits() == GlucoseUnit.MGDL + when (action.command) { + EventData.ActionTempTargetPreCheck.TempTargetCommand.PRESET_ACTIVITY -> { + val activityTTDuration = defaultValueHelper.determineActivityTTDuration() + val activityTT = defaultValueHelper.determineActivityTT() + val reason = rh.gs(R.string.activity) + message += rh.gs(R.string.wear_action_tempt_preset_message, reason, activityTT, activityTTDuration) + rxBus.send( + EventMobileToWear( + EventData.ConfirmAction( + title, message, + returnCommand = EventData.ActionTempTargetConfirmed(presetIsMGDL, activityTTDuration, activityTT, activityTT) + ) + ) + ) + } + + EventData.ActionTempTargetPreCheck.TempTargetCommand.PRESET_HYPO -> { + val hypoTTDuration = defaultValueHelper.determineHypoTTDuration() + val hypoTT = defaultValueHelper.determineHypoTT() + val reason = rh.gs(R.string.hypo) + message += rh.gs(R.string.wear_action_tempt_preset_message, reason, hypoTT, hypoTTDuration) + rxBus.send( + EventMobileToWear( + EventData.ConfirmAction( + title, message, + returnCommand = EventData.ActionTempTargetConfirmed(presetIsMGDL, hypoTTDuration, hypoTT, hypoTT) + ) + ) + ) + } + + EventData.ActionTempTargetPreCheck.TempTargetCommand.PRESET_EATING -> { + val eatingSoonTTDuration = defaultValueHelper.determineEatingSoonTTDuration() + val eatingSoonTT = defaultValueHelper.determineEatingSoonTT() + val reason = rh.gs(R.string.eatingsoon) + message += rh.gs(R.string.wear_action_tempt_preset_message, reason, eatingSoonTT, eatingSoonTTDuration) + rxBus.send( + EventMobileToWear( + EventData.ConfirmAction( + title, message, + returnCommand = EventData.ActionTempTargetConfirmed(presetIsMGDL, eatingSoonTTDuration, eatingSoonTT, eatingSoonTT) + ) + ) + ) + } + + EventData.ActionTempTargetPreCheck.TempTargetCommand.CANCEL -> { + message += rh.gs(R.string.wear_action_tempt_cancel_message) + rxBus.send( + EventMobileToWear( + EventData.ConfirmAction( + title, message, + returnCommand = EventData.ActionTempTargetConfirmed(true, 0, 0.0, 0.0) + ) + ) + ) + } + + EventData.ActionTempTargetPreCheck.TempTargetCommand.MANUAL -> { + if (profileFunction.getUnits() == GlucoseUnit.MGDL != action.isMgdl) { + sendError(rh.gs(R.string.wear_action_tempt_unit_error)) + return + } + if (action.duration == 0) { + message += rh.gs(R.string.wear_action_tempt_zero_message) + rxBus.send( + EventMobileToWear( + EventData.ConfirmAction( + title, message, + returnCommand = EventData.ActionTempTargetConfirmed(true, 0, 0.0, 0.0) + ) + ) + ) + } else { + var low = action.low + var high = action.high + if (!action.isMgdl) { + low *= Constants.MMOLL_TO_MGDL + high *= Constants.MMOLL_TO_MGDL + } + if (low < HardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[0] || low > HardLimits.VERY_HARD_LIMIT_TEMP_MIN_BG[1]) { + sendError(rh.gs(R.string.wear_action_tempt_min_bg_error)) + return + } + if (high < HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[0] || high > HardLimits.VERY_HARD_LIMIT_TEMP_MAX_BG[1]) { + sendError(rh.gs(R.string.wear_action_tempt_max_bg_error)) + return + } + message += if (low == high) rh.gs(R.string.wear_action_tempt_manual_message, action.low, action.duration) + else rh.gs(R.string.wear_action_tempt_manual_range_message, action.low, action.high, action.duration) + rxBus.send( + EventMobileToWear( + EventData.ConfirmAction( + title, message, + returnCommand = EventData.ActionTempTargetConfirmed(presetIsMGDL, action.duration, action.low, action.high) + ) + ) + ) + } + } + } + } + + private fun QuickWizardEntry.toWear(): EventData.QuickWizard.QuickWizardEntry = + EventData.QuickWizard.QuickWizardEntry( + guid = guid(), + buttonText = buttonText(), + carbs = carbs(), + validFrom = validFrom(), + validTo = validTo() + ) + + fun resendData(from: String) { + aapsLogger.debug(LTag.WEAR, "Sending data to wear from $from") + // SingleBg + iobCobCalculator.ads.lastBg()?.let { rxBus.send(EventMobileToWear(getSingleBG(it))) } + // Preferences + rxBus.send( + EventMobileToWear( + EventData.Preferences( + timeStamp = System.currentTimeMillis(), + wearControl = sp.getBoolean(R.string.key_wear_control, false), + unitsMgdl = profileFunction.getUnits() == GlucoseUnit.MGDL, + bolusPercentage = sp.getInt(R.string.key_boluswizard_percentage, 100), + maxCarbs = sp.getInt(R.string.key_treatmentssafety_maxcarbs, 48), + maxBolus = sp.getDouble(R.string.key_treatmentssafety_maxbolus, 3.0) + ) + ) + ) + // QuickWizard + rxBus.send( + EventMobileToWear( + EventData.QuickWizard( + ArrayList(quickWizard.list().filter { it.forDevice(QuickWizardEntry.DEVICE_WATCH) }.map { it.toWear() }) + ) + ) + ) + // GraphData + val startTime = System.currentTimeMillis() - (60000 * 60 * 5.5).toLong() + rxBus.send(EventMobileToWear(EventData.GraphData(ArrayList(repository.compatGetBgReadingsDataFromTime(startTime, true).blockingGet().map { getSingleBG(it) })))) + // Treatments + sendTreatments() + // Status + // Keep status last. Wear start refreshing after status received + sendStatus() + } + + private fun sendTreatments() { + val now = System.currentTimeMillis() + val startTimeWindow = now - (60000 * 60 * 5.5).toLong() + val basals = arrayListOf() + val temps = arrayListOf() + val boluses = arrayListOf() + val predictions = arrayListOf() + val profile = profileFunction.getProfile() ?: return + var beginBasalSegmentTime = startTimeWindow + var runningTime = startTimeWindow + var beginBasalValue = profile.getBasal(beginBasalSegmentTime) + var endBasalValue = beginBasalValue + var tb1 = iobCobCalculator.getTempBasalIncludingConvertedExtended(runningTime) + var tb2: TemporaryBasal? + var tbBefore = beginBasalValue + var tbAmount = beginBasalValue + var tbStart = runningTime + if (tb1 != null) { + val profileTB = profileFunction.getProfile(runningTime) + if (profileTB != null) { + tbAmount = tb1.convertedToAbsolute(runningTime, profileTB) + tbStart = runningTime + } + } + while (runningTime < now) { + val profileTB = profileFunction.getProfile(runningTime) ?: return + //basal rate + endBasalValue = profile.getBasal(runningTime) + if (endBasalValue != beginBasalValue) { + //push the segment we recently left + basals.add(EventData.TreatmentData.Basal(beginBasalSegmentTime, runningTime, beginBasalValue)) + + //begin new Basal segment + beginBasalSegmentTime = runningTime + beginBasalValue = endBasalValue + } + + //temps + tb2 = iobCobCalculator.getTempBasalIncludingConvertedExtended(runningTime) + if (tb1 == null && tb2 == null) { + //no temp stays no temp + } else if (tb1 != null && tb2 == null) { + //temp is over -> push it + temps.add(EventData.TreatmentData.TempBasal(tbStart, tbBefore, runningTime, endBasalValue, tbAmount)) + tb1 = null + } else if (tb1 == null && tb2 != null) { + //temp begins + tb1 = tb2 + tbStart = runningTime + tbBefore = endBasalValue + tbAmount = tb1.convertedToAbsolute(runningTime, profileTB) + } else if (tb1 != null && tb2 != null) { + val currentAmount = tb2.convertedToAbsolute(runningTime, profileTB) + if (currentAmount != tbAmount) { + temps.add(EventData.TreatmentData.TempBasal(tbStart, tbBefore, runningTime, currentAmount, tbAmount)) + tbStart = runningTime + tbBefore = tbAmount + tbAmount = currentAmount + tb1 = tb2 + } + } + runningTime += (5 * 60 * 1000).toLong() + } + if (beginBasalSegmentTime != runningTime) { + //push the remaining segment + basals.add(EventData.TreatmentData.Basal(beginBasalSegmentTime, runningTime, beginBasalValue)) + } + if (tb1 != null) { + tb2 = iobCobCalculator.getTempBasalIncludingConvertedExtended(now) //use "now" to express current situation + if (tb2 == null) { + //express the cancelled temp by painting it down one minute early + temps.add(EventData.TreatmentData.TempBasal(tbStart, tbBefore, now - 60 * 1000, endBasalValue, tbAmount)) + } else { + //express currently running temp by painting it a bit into the future + val profileNow = profileFunction.getProfile(now) + val currentAmount = tb2.convertedToAbsolute(now, profileNow!!) + if (currentAmount != tbAmount) { + temps.add(EventData.TreatmentData.TempBasal(tbStart, tbBefore, now, tbAmount, tbAmount)) + temps.add(EventData.TreatmentData.TempBasal(now, tbAmount, runningTime + 5 * 60 * 1000, currentAmount, currentAmount)) + } else { + temps.add(EventData.TreatmentData.TempBasal(tbStart, tbBefore, runningTime + 5 * 60 * 1000, tbAmount, tbAmount)) + } + } + } else { + tb2 = iobCobCalculator.getTempBasalIncludingConvertedExtended(now) //use "now" to express current situation + if (tb2 != null) { + //onset at the end + val profileTB = profileFunction.getProfile(runningTime) + val currentAmount = tb2.convertedToAbsolute(runningTime, profileTB!!) + temps.add(EventData.TreatmentData.TempBasal(now - 60 * 1000, endBasalValue, runningTime + 5 * 60 * 1000, currentAmount, currentAmount)) + } + } + repository.getBolusesIncludingInvalidFromTime(startTimeWindow, true).blockingGet() + .stream() + .filter { (_, _, _, _, _, _, _, _, _, type) -> type !== Bolus.Type.PRIMING } + .forEach { (_, _, _, isValid, _, _, timestamp, _, amount, type) -> boluses.add(EventData.TreatmentData.Treatment(timestamp, amount, 0.0, type === Bolus.Type.SMB, isValid)) } + repository.getCarbsDataFromTimeExpanded(startTimeWindow, true).blockingGet() + .forEach { (_, _, _, isValid, _, _, timestamp, _, _, amount) -> boluses.add(EventData.TreatmentData.Treatment(timestamp, 0.0, amount, false, isValid)) } + val finalLastRun = loop.lastRun + if (sp.getBoolean("wear_predictions", true) && finalLastRun?.request?.hasPredictions == true && finalLastRun.constraintsProcessed != null) { + val predArray = finalLastRun.constraintsProcessed!!.predictions + .stream().map { bg: GlucoseValue -> GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, rh) } + .collect(Collectors.toList()) + if (predArray.isNotEmpty()) + for (bg in predArray) if (bg.data.value > 39) + predictions.add( + EventData.SingleBg( + timeStamp = bg.data.timestamp, + glucoseUnits = Constants.MGDL, + sgv = bg.data.value, + high = 0.0, + low = 0.0, + color = bg.color(null) + ) + ) + } + rxBus.send(EventMobileToWear(EventData.TreatmentData(temps, basals, boluses, predictions))) + } + + private fun sendStatus() { + val profile = profileFunction.getProfile() + var status = rh.gs(R.string.noprofile) + var iobSum = "" + var iobDetail = "" + var cobString = "" + var currentBasal = "" + var bgiString = "" + if (profile != null) { + val bolusIob = iobCobCalculator.calculateIobFromBolus().round() + val basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round() + iobSum = DecimalFormatter.to2Decimal(bolusIob.iob + basalIob.basaliob) + iobDetail = "(${DecimalFormatter.to2Decimal(bolusIob.iob)}|${DecimalFormatter.to2Decimal(basalIob.basaliob)})" + cobString = iobCobCalculator.getCobInfo(false, "WatcherUpdaterService").generateCOBString() + currentBasal = iobCobCalculator.getTempBasalIncludingConvertedExtended(System.currentTimeMillis())?.toStringShort() ?: DecimalFormatter.to2Decimal(profile.getBasal()) + "U/h" + + //bgi + val bgi = -(bolusIob.activity + basalIob.activity) * 5 * Profile.fromMgdlToUnits(profile.getIsfMgdl(), profileFunction.getUnits()) + bgiString = "" + (if (bgi >= 0) "+" else "") + DecimalFormatter.to1Decimal(bgi) + status = generateStatusString(profile, currentBasal, iobSum, iobDetail, bgiString) + } + + //batteries + val phoneBattery = receiverStatusStore.batteryLevel + val rigBattery = nsDeviceStatus.uploaderStatus.trim { it <= ' ' } + //OpenAPS status + val openApsStatus = + if (config.APS) loop.lastRun?.let { if (it.lastTBREnact != 0L) it.lastTBREnact else -1 } ?: -1 + else nsDeviceStatus.openApsTimestamp + + rxBus.send( + EventMobileToWear( + EventData.Status( + externalStatus = status, + iobSum = iobSum, + iobDetail = iobDetail, + detailedIob = sp.getBoolean(R.string.key_wear_detailediob, false), + cob = cobString, + currentBasal = currentBasal, + battery = phoneBattery.toString(), + rigBattery = rigBattery, + openApsStatus = openApsStatus, + bgi = bgiString, + showBgi = sp.getBoolean(R.string.key_wear_showbgi, false), + batteryLevel = if (phoneBattery >= 30) 1 else 0 + ) + ) + ) + } + + private fun deltaString(deltaMGDL: Double, deltaMMOL: Double, units: GlucoseUnit): String { + val detailed = sp.getBoolean(R.string.key_wear_detailed_delta, false) + var deltaString = if (deltaMGDL >= 0) "+" else "-" + deltaString += if (units == GlucoseUnit.MGDL) { + if (detailed) DecimalFormatter.to1Decimal(abs(deltaMGDL)) else DecimalFormatter.to0Decimal(abs(deltaMGDL)) + } else { + if (detailed) DecimalFormatter.to2Decimal(abs(deltaMMOL)) else DecimalFormatter.to1Decimal(abs(deltaMMOL)) + } + return deltaString + } + + private fun getSingleBG(glucoseValue: GlucoseValue): EventData.SingleBg { + val glucoseStatus = glucoseStatusProvider.getGlucoseStatusData(true) + val units = profileFunction.getUnits() + val lowLine = Profile.toMgdl(defaultValueHelper.determineLowLine(), units) + val highLine = Profile.toMgdl(defaultValueHelper.determineHighLine(), units) + + return EventData.SingleBg( + timeStamp = glucoseValue.timestamp, + sgvString = glucoseValue.valueToUnitsString(units), + glucoseUnits = units.asText, + slopeArrow = trendCalculator.getTrendArrow(glucoseValue).symbol, + delta = glucoseStatus?.let { deltaString(it.delta, it.delta * Constants.MGDL_TO_MMOLL, units) } ?: "--", + avgDelta = glucoseStatus?.let { deltaString(it.shortAvgDelta, it.shortAvgDelta * Constants.MGDL_TO_MMOLL, units) } ?: "--", + sgvLevel = if (glucoseValue.value > highLine) 1L else if (glucoseValue.value < lowLine) -1L else 0L, + sgv = glucoseValue.value, + high = highLine, + low = lowLine, + color = 0 + ) + } + + //Check for Temp-Target: + private + val targetsStatus: String + get() { + var ret = "" + if (!config.APS) { + return "Targets only apply in APS mode!" + } + val profile = profileFunction.getProfile() ?: return "No profile set :(" + //Check for Temp-Target: + val tempTarget = repository.getTemporaryTargetActiveAt(dateUtil.now()).blockingGet() + if (tempTarget is ValueWrapper.Existing) { + ret += "Temp Target: " + Profile.toTargetRangeString(tempTarget.value.lowTarget, tempTarget.value.lowTarget, GlucoseUnit.MGDL, profileFunction.getUnits()) + ret += "\nuntil: " + dateUtil.timeString(tempTarget.value.end) + ret += "\n\n" + } + ret += "DEFAULT RANGE: " + ret += Profile.fromMgdlToUnits(profile.getTargetLowMgdl(), profileFunction.getUnits()).toString() + " - " + Profile.fromMgdlToUnits( + profile.getTargetHighMgdl(), + profileFunction.getUnits() + ) + ret += " target: " + Profile.fromMgdlToUnits(profile.getTargetMgdl(), profileFunction.getUnits()) + return ret + } + + private + val oAPSResultStatus: String + get() { + var ret = "" + if (!config.APS) + return "Only apply in APS mode!" + val usedAPS = activePlugin.activeAPS + val result = usedAPS.lastAPSResult ?: return "Last result not available!" + ret += if (!result.isChangeRequested) { + rh.gs(R.string.nochangerequested) + "\n" + } else if (result.rate == 0.0 && result.duration == 0) { + rh.gs(R.string.canceltemp) + "\n" + } else { + rh.gs(R.string.rate) + ": " + DecimalFormatter.to2Decimal(result.rate) + " U/h " + + "(" + DecimalFormatter.to2Decimal(result.rate / activePlugin.activePump.baseBasalRate * 100) + "%)\n" + + rh.gs(R.string.duration) + ": " + DecimalFormatter.to0Decimal(result.duration.toDouble()) + " min\n" + } + ret += "\n" + rh.gs(R.string.reason) + ": " + result.reason + return ret + } + + // decide if enabled/disabled closed/open; what Plugin as APS? + private + val loopStatus: String + get() { + var ret = "" + // decide if enabled/disabled closed/open; what Plugin as APS? + if ((loop as PluginBase).isEnabled()) { + ret += if (constraintChecker.isClosedLoopAllowed().value()) { + "CLOSED LOOP\n" + } else { + "OPEN LOOP\n" + } + val aps = activePlugin.activeAPS + ret += "APS: " + (aps as PluginBase).name + val lastRun = loop.lastRun + if (lastRun != null) { + ret += "\nLast Run: " + dateUtil.timeString(lastRun.lastAPSRun) + if (lastRun.lastTBREnact != 0L) ret += "\nLast Enact: " + dateUtil.timeString(lastRun.lastTBREnact) + } + } else { + ret += "LOOP DISABLED\n" + } + return ret + } + + private fun isOldData(historyList: List): Boolean { + val startsYesterday = activePlugin.activePump.pumpDescription.supportsTDDs + val df: DateFormat = SimpleDateFormat("dd.MM.", Locale.getDefault()) + return historyList.size < 3 || df.format(Date(historyList[0].timestamp)) != df.format(Date(System.currentTimeMillis() - if (startsYesterday) 1000 * 60 * 60 * 24 else 0)) + } + + private fun getTDDList(returnDummies: MutableList): MutableList { + var historyList = repository.getLastTotalDailyDoses(10, false).blockingGet().toMutableList() + //var historyList = databaseHelper.getTDDs().toMutableList() + historyList = historyList.subList(0, min(10, historyList.size)) + //fill single gaps - only needed for Dana*R data + val dummies: MutableList = returnDummies + val df: DateFormat = SimpleDateFormat("dd.MM.", Locale.getDefault()) + for (i in 0 until historyList.size - 1) { + val elem1 = historyList[i] + val elem2 = historyList[i + 1] + if (df.format(Date(elem1.timestamp)) != df.format(Date(elem2.timestamp + 25 * 60 * 60 * 1000))) { + val dummy = TotalDailyDose(timestamp = elem1.timestamp - T.hours(24).msecs(), bolusAmount = elem1.bolusAmount / 2, basalAmount = elem1.basalAmount / 2) + dummies.add(dummy) + elem1.basalAmount /= 2.0 + elem1.bolusAmount /= 2.0 + } + } + historyList.addAll(dummies) + historyList.sortWith { lhs, rhs -> (rhs.timestamp - lhs.timestamp).toInt() } + return historyList + } + + private fun generateTDDMessage(historyList: MutableList, dummies: MutableList): String { + val profile = profileFunction.getProfile() ?: return "No profile loaded :(" + if (historyList.isEmpty()) { + return "No history data!" + } + val df: DateFormat = SimpleDateFormat("dd.MM.", Locale.getDefault()) + var message = "" + val refTDD = profile.baseBasalSum() * 2 + if (df.format(Date(historyList[0].timestamp)) == df.format(Date())) { + val tdd = historyList[0].total + historyList.removeAt(0) + message += "Today: " + DecimalFormatter.to2Decimal(tdd) + "U " + (DecimalFormatter.to0Decimal(100 * tdd / refTDD) + "%") + "\n" + message += "\n" + } + var weighted03 = 0.0 + var weighted05 = 0.0 + var weighted07 = 0.0 + historyList.reverse() + for ((i, record) in historyList.withIndex()) { + val tdd = record.total + if (i == 0) { + weighted03 = tdd + weighted05 = tdd + weighted07 = tdd + } else { + weighted07 = weighted07 * 0.3 + tdd * 0.7 + weighted05 = weighted05 * 0.5 + tdd * 0.5 + weighted03 = weighted03 * 0.7 + tdd * 0.3 + } + } + message += "weighted:\n" + message += "0.3: " + DecimalFormatter.to2Decimal(weighted03) + "U " + (DecimalFormatter.to0Decimal(100 * weighted03 / refTDD) + "%") + "\n" + message += "0.5: " + DecimalFormatter.to2Decimal(weighted05) + "U " + (DecimalFormatter.to0Decimal(100 * weighted05 / refTDD) + "%") + "\n" + message += "0.7: " + DecimalFormatter.to2Decimal(weighted07) + "U " + (DecimalFormatter.to0Decimal(100 * weighted07 / refTDD) + "%") + "\n" + message += "\n" + historyList.reverse() + //add TDDs: + for (record in historyList) { + val tdd = record.total + message += df.format(Date(record.timestamp)) + " " + DecimalFormatter.to2Decimal(tdd) + "U " + (DecimalFormatter.to0Decimal(100 * tdd / refTDD) + "%") + (if (dummies.contains( + record + ) + ) "x" else "") + "\n" + } + return message + } + + private fun generateStatusString(profile: Profile?, currentBasal: String, iobSum: String, iobDetail: String, bgiString: String): String { + var status = "" + profile ?: return rh.gs(R.string.noprofile) + if (!(loop as PluginBase).isEnabled()) status += rh.gs(R.string.disabledloop) + "\n" + + val iobString = + if (sp.getBoolean(R.string.key_wear_detailediob, false)) "$iobSum $iobDetail" + else iobSum + "U" + + status += "$currentBasal $iobString" + + //add BGI if shown, otherwise return + if (sp.getBoolean(R.string.key_wear_showbgi, false)) status += " $bgiString" + return status + } + + private fun doTempTarget(command: EventData.ActionTempTargetConfirmed) { + if (command.duration != 0) { + disposable += repository.runTransactionForResult( + InsertAndCancelCurrentTemporaryTargetTransaction( + timestamp = System.currentTimeMillis(), + duration = TimeUnit.MINUTES.toMillis(command.duration.toLong()), + reason = TemporaryTarget.Reason.WEAR, + lowTarget = Profile.toMgdl(command.low, profileFunction.getUnits()), + highTarget = Profile.toMgdl(command.high, profileFunction.getUnits()) + ) + ).subscribe({ result -> + result.inserted.forEach { aapsLogger.debug(LTag.DATABASE, "Inserted temp target $it") } + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") } + }, { + aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) + }) + uel.log( + UserEntry.Action.TT, UserEntry.Sources.Wear, + ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.WEAR), + ValueWithUnit.fromGlucoseUnit(command.low, profileFunction.getUnits().asText), + ValueWithUnit.fromGlucoseUnit(command.high, profileFunction.getUnits().asText).takeIf { command.low != command.high }, + ValueWithUnit.Minute(command.duration) + ) + } else { + disposable += repository.runTransactionForResult(CancelCurrentTemporaryTargetIfAnyTransaction(System.currentTimeMillis())) + .subscribe({ result -> + result.updated.forEach { aapsLogger.debug(LTag.DATABASE, "Updated temp target $it") } + }, { + aapsLogger.error(LTag.DATABASE, "Error while saving temporary target", it) + }) + uel.log( + UserEntry.Action.CANCEL_TT, UserEntry.Sources.Wear, + ValueWithUnit.TherapyEventTTReason(TemporaryTarget.Reason.WEAR) + ) + } + } + + private fun doBolus(amount: Double, carbs: Int, carbsTime: Long?, carbsDuration: Int) { + val detailedBolusInfo = DetailedBolusInfo() + detailedBolusInfo.insulin = amount + detailedBolusInfo.carbs = carbs.toDouble() + detailedBolusInfo.bolusType = DetailedBolusInfo.BolusType.NORMAL + detailedBolusInfo.carbsTimestamp = carbsTime + detailedBolusInfo.carbsDuration = T.hours(carbsDuration.toLong()).msecs() + if (detailedBolusInfo.insulin > 0 || detailedBolusInfo.carbs > 0) { + val action = when { + amount == 0.0 -> UserEntry.Action.CARBS + carbs == 0 -> UserEntry.Action.BOLUS + carbsDuration > 0 -> UserEntry.Action.EXTENDED_CARBS + else -> UserEntry.Action.TREATMENT + } + uel.log(action, UserEntry.Sources.Wear, + ValueWithUnit.Insulin(amount).takeIf { amount != 0.0 }, + ValueWithUnit.Gram(carbs).takeIf { carbs != 0 }, + ValueWithUnit.Hour(carbsDuration).takeIf { carbsDuration != 0 }) + commandQueue.bolus(detailedBolusInfo, object : Callback() { + override fun run() { + if (!result.success) + sendError(rh.gs(R.string.treatmentdeliveryerror) + "\n" + result.comment) + } + }) + } + } + + private fun doFillBolus(amount: Double) { + val detailedBolusInfo = DetailedBolusInfo() + detailedBolusInfo.insulin = amount + detailedBolusInfo.bolusType = DetailedBolusInfo.BolusType.PRIMING + uel.log( + UserEntry.Action.PRIME_BOLUS, UserEntry.Sources.Wear, + ValueWithUnit.Insulin(amount).takeIf { amount != 0.0 }) + commandQueue.bolus(detailedBolusInfo, object : Callback() { + override fun run() { + if (!result.success) { + sendError(rh.gs(R.string.treatmentdeliveryerror) + "\n" + result.comment) + } + } + }) + } + + private fun doECarbs(carbs: Int, carbsTime: Long, duration: Int) { + uel.log(if (duration == 0) UserEntry.Action.CARBS else UserEntry.Action.EXTENDED_CARBS, UserEntry.Sources.Wear, + ValueWithUnit.Timestamp(carbsTime), + ValueWithUnit.Gram(carbs), + ValueWithUnit.Hour(duration).takeIf { duration != 0 }) + doBolus(0.0, carbs, carbsTime, duration) + } + + private fun doProfileSwitch(command: EventData.ActionProfileSwitchConfirmed) { + //check for validity + if (command.percentage < Constants.CPP_MIN_PERCENTAGE || command.percentage > Constants.CPP_MAX_PERCENTAGE) + return + if (command.timeShift < 0 || command.timeShift > 23) + return + profileFunction.getProfile() ?: return + //send profile to pump + uel.log( + UserEntry.Action.PROFILE_SWITCH, UserEntry.Sources.Wear, + ValueWithUnit.Percent(command.percentage), + ValueWithUnit.Hour(command.timeShift).takeIf { command.timeShift != 0 }) + profileFunction.createProfileSwitch(0, command.percentage, command.timeShift) + } + + @Synchronized private fun sendError(errorMessage: String) { + rxBus.send(EventData.ConfirmAction(rh.gs(R.string.error), errorMessage, returnCommand = EventData.Error(dateUtil.now()))) // ignore return path + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataLayerListenerServiceMobile.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataLayerListenerServiceMobile.kt new file mode 100644 index 0000000000..b8305a981b --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/DataLayerListenerServiceMobile.kt @@ -0,0 +1,200 @@ +package info.nightscout.androidaps.plugins.general.wear.wearintegration + +import android.os.Handler +import android.os.HandlerThread +import com.google.android.gms.tasks.Tasks +import com.google.android.gms.wearable.* +import dagger.android.AndroidInjection +import info.nightscout.androidaps.R +import info.nightscout.androidaps.database.AppRepository +import info.nightscout.androidaps.events.EventMobileToWear +import info.nightscout.androidaps.interfaces.ActivePlugin +import info.nightscout.androidaps.interfaces.Config +import info.nightscout.androidaps.interfaces.IobCobCalculator +import info.nightscout.androidaps.interfaces.Loop +import info.nightscout.androidaps.interfaces.ProfileFunction +import info.nightscout.androidaps.plugins.bus.RxBus +import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus +import info.nightscout.androidaps.plugins.general.wear.WearPlugin +import info.nightscout.androidaps.plugins.general.wear.events.EventWearUpdateGui +import info.nightscout.androidaps.receivers.ReceiverStatusStore +import info.nightscout.androidaps.utils.DefaultValueHelper +import info.nightscout.androidaps.utils.resources.ResourceHelper +import info.nightscout.androidaps.utils.rx.AapsSchedulers +import info.nightscout.androidaps.utils.wizard.QuickWizard +import info.nightscout.shared.logging.AAPSLogger +import info.nightscout.shared.logging.LTag +import info.nightscout.shared.sharedPreferences.SP +import info.nightscout.shared.weardata.EventData +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign +import kotlinx.coroutines.* +import kotlinx.coroutines.tasks.await +import javax.inject.Inject + +class DataLayerListenerServiceMobile : WearableListenerService() { + + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var profileFunction: ProfileFunction + @Inject lateinit var iobCobCalculator: IobCobCalculator + @Inject lateinit var rh: ResourceHelper + @Inject lateinit var loop: Loop + @Inject lateinit var wearPlugin: WearPlugin + @Inject lateinit var sp: SP + @Inject lateinit var quickWizard: QuickWizard + @Inject lateinit var config: Config + @Inject lateinit var nsDeviceStatus: NSDeviceStatus + @Inject lateinit var receiverStatusStore: ReceiverStatusStore + @Inject lateinit var repository: AppRepository + @Inject lateinit var defaultValueHelper: DefaultValueHelper + @Inject lateinit var activePlugin: ActivePlugin + @Inject lateinit var rxBus: RxBus + @Inject lateinit var aapsSchedulers: AapsSchedulers + + private val dataClient by lazy { Wearable.getDataClient(this) } + private val messageClient by lazy { Wearable.getMessageClient(this) } + private val capabilityClient by lazy { Wearable.getCapabilityClient(this) } + //private val nodeClient by lazy { Wearable.getNodeClient(this) } + + private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) + private var handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper) + + private val disposable = CompositeDisposable() + + private val rxPath get() = getString(R.string.path_rx_bridge) + + override fun onCreate() { + AndroidInjection.inject(this) + super.onCreate() + aapsLogger.debug(LTag.WEAR, "onCreate") + handler.post { updateTranscriptionCapability() } + disposable += rxBus + .toObservable(EventMobileToWear::class.java) + .observeOn(aapsSchedulers.io) + .subscribe { sendMessage(rxPath, it.payload.serialize()) } + } + + override fun onCapabilityChanged(p0: CapabilityInfo) { + super.onCapabilityChanged(p0) + handler.post { updateTranscriptionCapability() } + aapsLogger.debug(LTag.WEAR, "onCapabilityChanged: ${p0.name} ${p0.nodes.joinToString(", ") { it.displayName + "(" + it.id + ")" }}") + } + + override fun onDestroy() { + super.onDestroy() + disposable.clear() + scope.cancel() + } + + @Suppress("ControlFlowWithEmptyBody", "UNUSED_EXPRESSION") + override fun onDataChanged(dataEvents: DataEventBuffer) { + //aapsLogger.debug(LTag.WEAR, "onDataChanged") + + if (wearPlugin.isEnabled()) { + dataEvents.forEach { event -> + if (event.type == DataEvent.TYPE_CHANGED) { + val path = event.dataItem.uri.path + + aapsLogger.debug(LTag.WEAR, "onDataChanged: Path: $path, EventDataItem=${event.dataItem}") + try { + when (path) { + } + } catch (exception: Exception) { + aapsLogger.error(LTag.WEAR, "Message failed", exception) + } + } + } + } + super.onDataChanged(dataEvents) + } + + override fun onMessageReceived(messageEvent: MessageEvent) { + super.onMessageReceived(messageEvent) + + if (wearPlugin.isEnabled()) { + when (messageEvent.path) { + rxPath -> { + aapsLogger.debug(LTag.WEAR, "onMessageReceived: ${String(messageEvent.data)}") + val command = EventData.deserialize(String(messageEvent.data)) + rxBus.send(command.also { it.sourceNodeId = messageEvent.sourceNodeId }) + } + } + } + } + + private var transcriptionNodeId: String? = null + + private fun updateTranscriptionCapability() { + val capabilityInfo: CapabilityInfo = Tasks.await( + capabilityClient.getCapability(WEAR_CAPABILITY, CapabilityClient.FILTER_REACHABLE) + ) + aapsLogger.debug(LTag.WEAR, "Nodes: ${capabilityInfo.nodes.joinToString(", ") { it.displayName + "(" + it.id + ")" }}") + val bestNode = pickBestNodeId(capabilityInfo.nodes) + transcriptionNodeId = bestNode?.id + wearPlugin.connectedDevice = bestNode?.displayName ?: "---" + rxBus.send(EventWearUpdateGui()) + aapsLogger.debug(LTag.WEAR, "Selected node: ${bestNode?.displayName} $transcriptionNodeId") + rxBus.send(EventMobileToWear(EventData.ActionPing(System.currentTimeMillis()))) + rxBus.send(EventData.ActionResendData("WatchUpdaterService")) + } + + // Find a nearby node or pick one arbitrarily + private fun pickBestNodeId(nodes: Set): Node? = + nodes.firstOrNull { it.isNearby } ?: nodes.firstOrNull() + + @Suppress("unused") + private fun sendData(path: String, vararg params: DataMap) { + if (wearPlugin.isEnabled()) { + scope.launch { + try { + for (dm in params) { + val request = PutDataMapRequest.create(path).apply { + dataMap.putAll(dm) + } + .asPutDataRequest() + .setUrgent() + + val result = dataClient.putDataItem(request).await() + aapsLogger.debug(LTag.WEAR, "sendData: ${result.uri} ${params.joinToString()}") + } + } catch (cancellationException: CancellationException) { + throw cancellationException + } catch (exception: Exception) { + aapsLogger.error(LTag.WEAR, "DataItem failed: $exception") + } + } + } + } + + private fun sendMessage(path: String, data: String?) { + aapsLogger.debug(LTag.WEAR, "sendMessage: $path $data") + transcriptionNodeId?.also { nodeId -> + messageClient + .sendMessage(nodeId, path, data?.toByteArray() ?: byteArrayOf()).apply { + addOnSuccessListener { } + addOnFailureListener { + aapsLogger.debug(LTag.WEAR, "sendMessage: $path failure") + } + } + } + } + + @Suppress("unused") + private fun sendMessage(path: String, data: ByteArray) { + aapsLogger.debug(LTag.WEAR, "sendMessage: $path") + transcriptionNodeId?.also { nodeId -> + messageClient + .sendMessage(nodeId, path, data).apply { + addOnSuccessListener { } + addOnFailureListener { + aapsLogger.debug(LTag.WEAR, "sendMessage: $path failure") + } + } + } + } + + companion object { + + const val WEAR_CAPABILITY = "androidaps_wear" + } +} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.kt b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.kt deleted file mode 100644 index c3a37d4f61..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService.kt +++ /dev/null @@ -1,633 +0,0 @@ -package info.nightscout.androidaps.plugins.general.wear.wearintegration - -import android.content.Intent -import android.os.Handler -import android.os.HandlerThread -import com.google.android.gms.tasks.Tasks -import com.google.android.gms.wearable.* -import dagger.android.AndroidInjection -import info.nightscout.androidaps.Constants -import info.nightscout.androidaps.R -import info.nightscout.androidaps.database.AppRepository -import info.nightscout.androidaps.database.entities.Bolus -import info.nightscout.androidaps.database.entities.GlucoseValue -import info.nightscout.androidaps.database.entities.TemporaryBasal -import info.nightscout.androidaps.extensions.convertedToAbsolute -import info.nightscout.androidaps.extensions.toStringShort -import info.nightscout.androidaps.extensions.valueToUnitsString -import info.nightscout.androidaps.interfaces.* -import info.nightscout.androidaps.interfaces.Profile.Companion.fromMgdlToUnits -import info.nightscout.androidaps.plugins.bus.RxBus -import info.nightscout.androidaps.plugins.general.nsclient.data.NSDeviceStatus -import info.nightscout.androidaps.plugins.general.overview.graphExtensions.GlucoseValueDataPoint -import info.nightscout.androidaps.plugins.general.wear.WearPlugin -import info.nightscout.androidaps.plugins.general.wear.events.EventWearConfirmAction -import info.nightscout.androidaps.plugins.general.wear.events.EventWearInitiateAction -import info.nightscout.androidaps.plugins.general.wear.events.EventWearUpdateGui -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatus -import info.nightscout.androidaps.plugins.iob.iobCobCalculator.GlucoseStatusProvider -import info.nightscout.androidaps.receivers.ReceiverStatusStore -import info.nightscout.androidaps.utils.DecimalFormatter.to0Decimal -import info.nightscout.androidaps.utils.DecimalFormatter.to1Decimal -import info.nightscout.androidaps.utils.DecimalFormatter.to2Decimal -import info.nightscout.androidaps.utils.DefaultValueHelper -import info.nightscout.androidaps.utils.TrendCalculator -import info.nightscout.androidaps.utils.resources.ResourceHelper -import info.nightscout.androidaps.utils.wizard.QuickWizard -import info.nightscout.androidaps.utils.wizard.QuickWizardEntry -import info.nightscout.shared.logging.AAPSLogger -import info.nightscout.shared.logging.LTag -import info.nightscout.shared.sharedPreferences.SP -import info.nightscout.shared.weardata.WearConstants -import io.reactivex.rxjava3.disposables.CompositeDisposable -import kotlinx.coroutines.* -import kotlinx.coroutines.tasks.await -import java.util.function.Consumer -import java.util.stream.Collectors -import javax.inject.Inject -import kotlin.math.abs - -class WatchUpdaterService : WearableListenerService() { - - @Inject lateinit var aapsLogger: AAPSLogger - @Inject lateinit var profileFunction: ProfileFunction - @Inject lateinit var iobCobCalculator: IobCobCalculator - @Inject lateinit var rh: ResourceHelper - @Inject lateinit var loop: Loop - @Inject lateinit var wearPlugin: WearPlugin - @Inject lateinit var sp: SP - @Inject lateinit var quickWizard: QuickWizard - @Inject lateinit var config: Config - @Inject lateinit var nsDeviceStatus: NSDeviceStatus - @Inject lateinit var receiverStatusStore: ReceiverStatusStore - @Inject lateinit var repository: AppRepository - @Inject lateinit var defaultValueHelper: DefaultValueHelper - @Inject lateinit var glucoseStatusProvider: GlucoseStatusProvider - @Inject lateinit var trendCalculator: TrendCalculator - @Inject lateinit var activePlugin: ActivePlugin - @Inject lateinit var rxBus: RxBus - @Inject lateinit var wearConstants: WearConstants - - private val dataClient by lazy { Wearable.getDataClient(this) } - private val messageClient by lazy { Wearable.getMessageClient(this) } - private val capabilityClient by lazy { Wearable.getCapabilityClient(this) } - //private val nodeClient by lazy { Wearable.getNodeClient(this) } - - private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) - private var handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper) - - private val disposable = CompositeDisposable() - - override fun onCreate() { - AndroidInjection.inject(this) - super.onCreate() - aapsLogger.debug(LTag.WEAR, "onCreate") - handler.post { updateTranscriptionCapability() } - } - - override fun onCapabilityChanged(p0: CapabilityInfo) { - super.onCapabilityChanged(p0) - handler.post { updateTranscriptionCapability() } - aapsLogger.debug(LTag.WEAR, "onCapabilityChanged: ${p0.name} ${p0.nodes.joinToString(", ") { it.displayName + "(" + it.id + ")" }}") - } - - override fun onDestroy() { - super.onDestroy() - disposable.clear() - scope.cancel() - } - - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - aapsLogger.debug(LTag.WEAR, "onStartCommand ${intent?.action}") - if (wearPlugin.isEnabled()) { - handler.post { - when (intent?.action) { - ACTION_RESEND -> resendData() - ACTION_OPEN_SETTINGS -> sendMessage(wearConstants.M_W_OPEN_SETTINGS, byteArrayOf()) - ACTION_SEND_STATUS -> sendStatus() - ACTION_SEND_BASALS -> sendBasals() - ACTION_SEND_BOLUS_PROGRESS -> sendBolusProgress( - intent.getIntExtra("progresspercent", 0), - intent.getStringExtra("progressstatus") - ) - ACTION_SEND_ACTION_CONFIRMATION_REQUEST -> sendActionConfirmationRequest( - intent.getStringExtra("title"), - intent.getStringExtra("message"), - intent.getStringExtra("actionstring") - ) - ACTION_SEND_CHANGE_CONFIRMATION_REQUEST -> sendChangeConfirmationRequest( - intent.getStringExtra("title"), - intent.getStringExtra("message"), - intent.getStringExtra("actionstring") - ) - ACTION_CANCEL_NOTIFICATION -> sendCancelNotificationRequest(intent.getStringExtra("actionstring")) - - null -> {} - - else -> sendData() - } - } - } - return START_STICKY - } - - @Suppress("ControlFlowWithEmptyBody", "UNUSED_EXPRESSION") - override fun onDataChanged(dataEvents: DataEventBuffer) { - //aapsLogger.debug(LTag.WEAR, "onDataChanged") - - if (wearPlugin.isEnabled()) { - dataEvents.forEach { event -> - if (event.type == DataEvent.TYPE_CHANGED) { - val path = event.dataItem.uri.path - - aapsLogger.debug(LTag.WEAR, "onDataChanged: Path: $path, EventDataItem=${event.dataItem}") - try { - when (path) { - } - } catch (exception: Exception) { - aapsLogger.error(LTag.WEAR, "Message failed", exception) - } - } - } - } - super.onDataChanged(dataEvents) - } - - override fun onMessageReceived(messageEvent: MessageEvent) { - super.onMessageReceived(messageEvent) - aapsLogger.debug(LTag.WEAR, "onMessageReceived: $messageEvent") - - if (wearPlugin.isEnabled()) { - when (messageEvent.path) { - wearConstants.W_M_RESEND_DATA -> resendData() - wearConstants.W_M_CANCEL_BOLUS -> activePlugin.activePump.stopBolusDelivering() - - wearConstants.W_M_INITIATE_ACTION -> - String(messageEvent.data).also { actionstring -> - aapsLogger.debug(LTag.WEAR, "Initiate action: $actionstring") - rxBus.send(EventWearInitiateAction(actionstring)) - } - - wearConstants.W_M_CONFIRM_ACTION -> - String(messageEvent.data).also { actionstring -> - aapsLogger.debug(LTag.WEAR, "Wear confirm action: $actionstring") - rxBus.send(EventWearConfirmAction(actionstring)) - } - - wearConstants.W_M_PONG -> aapsLogger.debug(LTag.WEAR, "Pong response from ${messageEvent.sourceNodeId}") - } - } - } - - private var transcriptionNodeId: String? = null - - private fun updateTranscriptionCapability() { - val capabilityInfo: CapabilityInfo = Tasks.await( - capabilityClient.getCapability(WEAR_CAPABILITY, CapabilityClient.FILTER_REACHABLE) - ) - aapsLogger.debug(LTag.WEAR, "Nodes: ${capabilityInfo.nodes.joinToString(", ") { it.displayName + "(" + it.id + ")" }}") - val bestNode = pickBestNodeId(capabilityInfo.nodes) - transcriptionNodeId = bestNode?.id - wearPlugin.connectedDevice = bestNode?.displayName ?: "---" - rxBus.send(EventWearUpdateGui()) - aapsLogger.debug(LTag.WEAR, "Selected node: ${bestNode?.displayName} $transcriptionNodeId") - sendMessage(wearConstants.M_W_PING, byteArrayOf()) - } - - // Find a nearby node or pick one arbitrarily - private fun pickBestNodeId(nodes: Set): Node? = - nodes.firstOrNull { it.isNearby } ?: nodes.firstOrNull() - - private fun sendData(path: String, vararg params: DataMap) { - if (wearPlugin.isEnabled()) { - scope.launch { - try { - for (dm in params) { - val request = PutDataMapRequest.create(path).apply { - dataMap.putAll(dm) - } - .asPutDataRequest() - .setUrgent() - - val result = dataClient.putDataItem(request).await() - aapsLogger.debug(LTag.WEAR, "sendData: ${result.uri} ${params.joinToString()}") - } - } catch (cancellationException: CancellationException) { - throw cancellationException - } catch (exception: Exception) { - aapsLogger.error(LTag.WEAR, "DataItem failed: $exception") - } - } - } - } - - private fun sendMessage(path: String, data: ByteArray) { - if (wearPlugin.isEnabled()) { - aapsLogger.debug(LTag.WEAR, "sendMessage: $path") - transcriptionNodeId?.also { nodeId -> - messageClient - .sendMessage(nodeId, path, data).apply { - addOnSuccessListener { } - addOnFailureListener { - aapsLogger.debug(LTag.WEAR, "sendMessage: $path failure") - } - } - } - } - } - - private fun sendData() { - val lastBG = iobCobCalculator.ads.lastBg() ?: return - val glucoseStatus = glucoseStatusProvider.glucoseStatusData - aapsLogger.debug(LTag.WEAR, "Sending bg data to wear") - sendData( - wearConstants.M_W_DATA, - dataMapSingleBG(lastBG, glucoseStatus) - ) - } - - private fun resendData() { - sendPreferences() - sendQuickWizard() - val startTime = System.currentTimeMillis() - (60000 * 60 * 5.5).toLong() - val lastBg = iobCobCalculator.ads.lastBg() ?: return - val graphBgs = repository.compatGetBgReadingsDataFromTime(startTime, true).blockingGet() - val glucoseStatus = glucoseStatusProvider.getGlucoseStatusData(true) - if (graphBgs.isNotEmpty()) { - val entries = dataMapSingleBG(lastBg, glucoseStatus) - val dataMaps = ArrayList(graphBgs.size) - for (bg in graphBgs) { - val dataMap: DataMap = dataMapSingleBG(bg, glucoseStatus) - dataMaps.add(dataMap) - } - entries.putDataMapArrayList("entries", dataMaps) - aapsLogger.debug(LTag.WEAR, "Sending graph bg data to wear") - sendData( - wearConstants.M_W_DATA, - entries - ) - } - sendBasals() - sendStatus() - } - - private fun sendBasals() { - val now = System.currentTimeMillis() - val startTimeWindow = now - (60000 * 60 * 5.5).toLong() - val basals = java.util.ArrayList() - val temps = java.util.ArrayList() - val boluses = java.util.ArrayList() - val predictions = java.util.ArrayList() - val profile = profileFunction.getProfile() ?: return - var beginBasalSegmentTime = startTimeWindow - var runningTime = startTimeWindow - var beginBasalValue = profile.getBasal(beginBasalSegmentTime) - var endBasalValue = beginBasalValue - var tb1 = iobCobCalculator.getTempBasalIncludingConvertedExtended(runningTime) - var tb2: TemporaryBasal? - var tbBefore = beginBasalValue - var tbAmount = beginBasalValue - var tbStart = runningTime - if (tb1 != null) { - val profileTB = profileFunction.getProfile(runningTime) - if (profileTB != null) { - tbAmount = tb1.convertedToAbsolute(runningTime, profileTB) - tbStart = runningTime - } - } - while (runningTime < now) { - val profileTB = profileFunction.getProfile(runningTime) ?: return - //basal rate - endBasalValue = profile.getBasal(runningTime) - if (endBasalValue != beginBasalValue) { - //push the segment we recently left - basals.add(basalMap(beginBasalSegmentTime, runningTime, beginBasalValue)) - - //begin new Basal segment - beginBasalSegmentTime = runningTime - beginBasalValue = endBasalValue - } - - //temps - tb2 = iobCobCalculator.getTempBasalIncludingConvertedExtended(runningTime) - if (tb1 == null && tb2 == null) { - //no temp stays no temp - } else if (tb1 != null && tb2 == null) { - //temp is over -> push it - temps.add(tempMap(tbStart, tbBefore, runningTime, endBasalValue, tbAmount)) - tb1 = null - } else if (tb1 == null && tb2 != null) { - //temp begins - tb1 = tb2 - tbStart = runningTime - tbBefore = endBasalValue - tbAmount = tb1.convertedToAbsolute(runningTime, profileTB) - } else if (tb1 != null && tb2 != null) { - val currentAmount = tb2.convertedToAbsolute(runningTime, profileTB) - if (currentAmount != tbAmount) { - temps.add(tempMap(tbStart, tbBefore, runningTime, currentAmount, tbAmount)) - tbStart = runningTime - tbBefore = tbAmount - tbAmount = currentAmount - tb1 = tb2 - } - } - runningTime += (5 * 60 * 1000).toLong() - } - if (beginBasalSegmentTime != runningTime) { - //push the remaining segment - basals.add(basalMap(beginBasalSegmentTime, runningTime, beginBasalValue)) - } - if (tb1 != null) { - tb2 = iobCobCalculator.getTempBasalIncludingConvertedExtended(now) //use "now" to express current situation - if (tb2 == null) { - //express the cancelled temp by painting it down one minute early - temps.add(tempMap(tbStart, tbBefore, now - 60 * 1000, endBasalValue, tbAmount)) - } else { - //express currently running temp by painting it a bit into the future - val profileNow = profileFunction.getProfile(now) - val currentAmount = tb2.convertedToAbsolute(now, profileNow!!) - if (currentAmount != tbAmount) { - temps.add(tempMap(tbStart, tbBefore, now, tbAmount, tbAmount)) - temps.add(tempMap(now, tbAmount, runningTime + 5 * 60 * 1000, currentAmount, currentAmount)) - } else { - temps.add(tempMap(tbStart, tbBefore, runningTime + 5 * 60 * 1000, tbAmount, tbAmount)) - } - } - } else { - tb2 = iobCobCalculator.getTempBasalIncludingConvertedExtended(now) //use "now" to express current situation - if (tb2 != null) { - //onset at the end - val profileTB = profileFunction.getProfile(runningTime) - val currentAmount = tb2.convertedToAbsolute(runningTime, profileTB!!) - temps.add(tempMap(now - 60 * 1000, endBasalValue, runningTime + 5 * 60 * 1000, currentAmount, currentAmount)) - } - } - repository.getBolusesIncludingInvalidFromTime(startTimeWindow, true).blockingGet() - .stream() - .filter { (_, _, _, _, _, _, _, _, _, type) -> type !== Bolus.Type.PRIMING } - .forEach { (_, _, _, isValid, _, _, timestamp, _, amount, type) -> boluses.add(treatmentMap(timestamp, amount, 0.0, type === Bolus.Type.SMB, isValid)) } - repository.getCarbsDataFromTimeExpanded(startTimeWindow, true).blockingGet() - .forEach(Consumer { (_, _, _, isValid, _, _, timestamp, _, _, amount) -> boluses.add(treatmentMap(timestamp, 0.0, amount, false, isValid)) }) - val finalLastRun = loop.lastRun - if (sp.getBoolean("wear_predictions", true) && finalLastRun?.request?.hasPredictions == true && finalLastRun.constraintsProcessed != null) { - val predArray = finalLastRun.constraintsProcessed!!.predictions - .stream().map { bg: GlucoseValue -> GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, rh) } - .collect(Collectors.toList()) - if (predArray.isNotEmpty()) - for (bg in predArray) if (bg.data.value > 39) predictions.add(predictionMap(bg.data.timestamp, bg.data.value, bg.color(null))) - } - aapsLogger.debug(LTag.WEAR, "Sending basal data to wear") - sendData( - wearConstants.M_W_BASAL, - DataMap().apply { - putDataMapArrayList("basals", basals) - putDataMapArrayList("temps", temps) - putDataMapArrayList("boluses", boluses) - putDataMapArrayList("predictions", predictions) - }) - } - - private fun deltaString(deltaMGDL: Double, deltaMMOL: Double, units: GlucoseUnit): String { - val detailed = sp.getBoolean(R.string.key_wear_detailed_delta, false) - var deltaString = if (deltaMGDL >= 0) "+" else "-" - deltaString += if (units == GlucoseUnit.MGDL) { - if (detailed) to1Decimal(abs(deltaMGDL)) else to0Decimal(abs(deltaMGDL)) - } else { - if (detailed) to2Decimal(abs(deltaMMOL)) else to1Decimal(abs(deltaMMOL)) - } - return deltaString - } - - private fun dataMapSingleBG(lastBG: GlucoseValue, glucoseStatus: GlucoseStatus?): DataMap { - val units = profileFunction.getUnits() - val lowLine = Profile.toMgdl(defaultValueHelper.determineLowLine(), units) - val highLine = Profile.toMgdl(defaultValueHelper.determineHighLine(), units) - val sgvLevel = if (lastBG.value > highLine) 1L else if (lastBG.value < lowLine) -1L else 0L - val dataMap = DataMap() - dataMap.putString("sgvString", lastBG.valueToUnitsString(units)) - dataMap.putString("glucoseUnits", units.asText) - dataMap.putLong("timestamp", lastBG.timestamp) - if (glucoseStatus == null) { - dataMap.putString("slopeArrow", "") - dataMap.putString("delta", "--") - dataMap.putString("avgDelta", "--") - } else { - dataMap.putString("slopeArrow", trendCalculator.getTrendArrow(lastBG).symbol) - dataMap.putString("delta", deltaString(glucoseStatus.delta, glucoseStatus.delta * Constants.MGDL_TO_MMOLL, units)) - dataMap.putString("avgDelta", deltaString(glucoseStatus.shortAvgDelta, glucoseStatus.shortAvgDelta * Constants.MGDL_TO_MMOLL, units)) - } - dataMap.putLong("sgvLevel", sgvLevel) - dataMap.putDouble("sgvDouble", lastBG.value) - dataMap.putDouble("high", highLine) - dataMap.putDouble("low", lowLine) - return dataMap - } - - private fun tempMap(startTime: Long, startBasal: Double, to: Long, toBasal: Double, amount: Double) = - DataMap().apply { - putLong("starttime", startTime) - putDouble("startBasal", startBasal) - putLong("endtime", to) - putDouble("endbasal", toBasal) - putDouble("amount", amount) - } - - private fun basalMap(startTime: Long, endTime: Long, amount: Double) = - DataMap().apply { - putLong("starttime", startTime) - putLong("endtime", endTime) - putDouble("amount", amount) - } - - private fun treatmentMap(date: Long, bolus: Double, carbs: Double, isSMB: Boolean, isValid: Boolean) = - DataMap().apply { - putLong("date", date) - putDouble("bolus", bolus) - putDouble("carbs", carbs) - putBoolean("isSMB", isSMB) - putBoolean("isValid", isValid) - } - - private fun predictionMap(timestamp: Long, sgv: Double, color: Int) = - DataMap().apply { - putLong("timestamp", timestamp) - putDouble("sgv", sgv) - putInt("color", color) - } - - private fun quickMap(q: QuickWizardEntry) = - DataMap().apply { - putString("guid", q.guid()) - putString("button_text", q.buttonText()) - putInt("carbs", q.carbs()) - putInt("from", q.validFrom()) - putInt("to", q.validTo()) - } - - private fun sendBolusProgress(progressPercent: Int?, status: String?) { - progressPercent ?: return - aapsLogger.debug(LTag.WEAR, "Sending bolus progress: $progressPercent $status") - sendData( - wearConstants.M_W_BOLUS_PROGRESS, - DataMap().apply { - putLong("timestamp", System.currentTimeMillis()) - putString("bolusProgress", "bolusProgress") - putString("progressstatus", status ?: "") - putInt("progresspercent", progressPercent) - }) - } - - private fun sendActionConfirmationRequest(title: String?, message: String?, actionstring: String?) { - title ?: message ?: actionstring ?: return - aapsLogger.debug(LTag.WEAR, "Requesting confirmation from wear: $actionstring") - sendData( - wearConstants.M_W_ACTION_CONFIRMATION_REQUEST, - DataMap().apply { - putLong("timestamp", System.currentTimeMillis()) - putString("actionConfirmationRequest", "actionConfirmationRequest") - putString("title", title) - putString("message", message) - putString("actionstring", actionstring) - }) - } - - private fun sendChangeConfirmationRequest(title: String?, message: String?, actionstring: String?) { - title ?: message ?: actionstring ?: return - aapsLogger.debug(LTag.WEAR, "Requesting confirmation from wear: $actionstring") - sendData( - wearConstants.M_W_ACTION_CHANGE_CONFIRMATION_REQUEST, - DataMap().apply { - putLong("timestamp", System.currentTimeMillis()) - putString("changeConfirmationRequest", "changeConfirmationRequest") - putString("title", title) - putString("message", message) - putString("actionstring", actionstring) - }) - } - - private fun sendCancelNotificationRequest(actionstring: String?) { - actionstring ?: return - aapsLogger.debug(LTag.WEAR, "Canceling notification on wear: $actionstring") - sendData( - wearConstants.M_W_ACTION_CANCEL_NOTIFICATION_REQUEST, - DataMap().apply { - putLong("timestamp", System.currentTimeMillis()) - putString("cancelNotificationRequest", "cancelNotificationRequest") - putString("actionstring", actionstring) - }) - } - - private fun sendStatus() { - aapsLogger.debug(LTag.WEAR, "Updating status on wear") - val profile = profileFunction.getProfile() - var status = rh.gs(R.string.noprofile) - var iobSum = "" - var iobDetail = "" - var cobString = "" - var currentBasal = "" - var bgiString = "" - if (profile != null) { - val bolusIob = iobCobCalculator.calculateIobFromBolus().round() - val basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round() - iobSum = to2Decimal(bolusIob.iob + basalIob.basaliob) - iobDetail = "(" + to2Decimal(bolusIob.iob) + "|" + to2Decimal(basalIob.basaliob) + ")" - cobString = iobCobCalculator.getCobInfo(false, "WatcherUpdaterService").generateCOBString() - currentBasal = generateBasalString() - - //bgi - val bgi = -(bolusIob.activity + basalIob.activity) * 5 * fromMgdlToUnits(profile.getIsfMgdl(), profileFunction.getUnits()) - bgiString = "" + (if (bgi >= 0) "+" else "") + to1Decimal(bgi) - status = generateStatusString(profile, currentBasal, iobSum, iobDetail, bgiString) - } - - //batteries - val phoneBattery = receiverStatusStore.batteryLevel - val rigBattery = nsDeviceStatus.uploaderStatus.trim { it <= ' ' } - //OpenAPS status - val openApsStatus = - if (config.APS) loop.lastRun?.let { if (it.lastTBREnact != 0L) it.lastTBREnact else -1 } ?: -1 - else nsDeviceStatus.openApsTimestamp - - sendData( - wearConstants.M_W_STATUS, - DataMap().apply { - //unique content - putString("externalStatusString", status) - putString("iobSum", iobSum) - putString("iobDetail", iobDetail) - putBoolean("detailedIob", sp.getBoolean(R.string.key_wear_detailediob, false)) - putString("cob", cobString) - putString("currentBasal", currentBasal) - putString("battery", "" + phoneBattery) - putString("rigBattery", rigBattery) - putLong("openApsStatus", openApsStatus) - putString("bgi", bgiString) - putBoolean("showBgi", sp.getBoolean(R.string.key_wear_showbgi, false)) - putInt("batteryLevel", if (phoneBattery >= 30) 1 else 0) - }) - } - - private fun sendPreferences() { - sendData( - wearConstants.M_W_PREFERENCES, - DataMap().apply { - putLong("timestamp", System.currentTimeMillis()) - putBoolean(rh.gs(R.string.key_wear_control), sp.getBoolean(R.string.key_wear_control, false)) - putBoolean(rh.gs(R.string.key_units_mgdl), profileFunction.getUnits() == GlucoseUnit.MGDL) - putInt(rh.gs(R.string.key_boluswizard_percentage), sp.getInt(R.string.key_boluswizard_percentage, 100)) - putInt(rh.gs(R.string.key_treatmentssafety_maxcarbs), sp.getInt(R.string.key_treatmentssafety_maxcarbs, 48)) - putDouble(rh.gs(R.string.key_treatmentssafety_maxbolus), sp.getDouble(R.string.key_treatmentssafety_maxbolus, 3.0)) - }) - } - - private fun sendQuickWizard() { - val entities = ArrayList() - for (i in 0 until quickWizard.size()) { - val q = quickWizard[i] - if (q.forDevice(QuickWizardEntry.DEVICE_WATCH)) entities.add(quickMap(q)) - } - sendData( - wearConstants.M_W_QUICK_WIZARD, - DataMap().apply { - putLong("timestamp", System.currentTimeMillis()) - putDataMapArrayList("quick_wizard", entities) - }) - } - - private fun generateStatusString(profile: Profile?, currentBasal: String, iobSum: String, iobDetail: String, bgiString: String): String { - var status = "" - if (profile == null) return rh.gs(R.string.noprofile) - if (!(loop as PluginBase).isEnabled()) status += rh.gs(R.string.disabledloop) + "\n" - - val iobString = - if (sp.getBoolean(R.string.key_wear_detailediob, false)) "$iobSum $iobDetail" - else iobSum + "U" - - status += "$currentBasal $iobString" - - //add BGI if shown, otherwise return - if (sp.getBoolean(R.string.key_wear_showbgi, false)) status += " $bgiString" - return status - } - - private fun generateBasalString(): String { - val profile: Profile = profileFunction.getProfile() ?: return "" - return iobCobCalculator.getTempBasalIncludingConvertedExtended(System.currentTimeMillis())?.toStringShort() ?: to2Decimal(profile.getBasal()) + "U/h" - } - - companion object { - - const val WEAR_CAPABILITY = "androidaps_wear" - - val ACTION_RESEND = WatchUpdaterService::class.java.name + ".Resend" - val ACTION_OPEN_SETTINGS = WatchUpdaterService::class.java.name + ".OpenSettings" - val ACTION_SEND_STATUS = WatchUpdaterService::class.java.name + ".SendStatus" - val ACTION_SEND_BASALS = WatchUpdaterService::class.java.name + ".SendBasals" - val ACTION_SEND_BOLUS_PROGRESS = WatchUpdaterService::class.java.name + ".BolusProgress" - val ACTION_SEND_ACTION_CONFIRMATION_REQUEST = WatchUpdaterService::class.java.name + ".ActionConfirmationRequest" - val ACTION_SEND_CHANGE_CONFIRMATION_REQUEST = WatchUpdaterService::class.java.name + ".ChangeConfirmationRequest" - val ACTION_CANCEL_NOTIFICATION = WatchUpdaterService::class.java.name + ".CancelNotification" - - } -} \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService1.java b/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService1.java deleted file mode 100644 index 95a2419d4a..0000000000 --- a/app/src/main/java/info/nightscout/androidaps/plugins/general/wear/wearintegration/WatchUpdaterService1.java +++ /dev/null @@ -1,778 +0,0 @@ -package info.nightscout.androidaps.plugins.general.wear.wearintegration; - -/* -public class WatchUpdaterService1 extends WearableListenerService implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { - @Inject public GlucoseStatusProvider glucoseStatusProvider; - @Inject public AAPSLogger aapsLogger; - @Inject public WearPlugin wearPlugin; - @Inject public ResourceHelper rh; - @Inject public SP sp; - @Inject public RxBus rxBus; - @Inject public ProfileFunction profileFunction; - @Inject public DefaultValueHelper defaultValueHelper; - @Inject public NSDeviceStatus nsDeviceStatus; - @Inject public ActivePlugin activePlugin; - @Inject public Loop loop; - @Inject public IobCobCalculator iobCobCalculator; - @Inject public AppRepository repository; - @Inject ReceiverStatusStore receiverStatusStore; - @Inject Config config; - @Inject public TrendCalculator trendCalculator; - @Inject public QuickWizard quickWizard; - - public static final String ACTION_RESEND = WatchUpdaterService.class.getName().concat(".Resend"); - public static final String ACTION_OPEN_SETTINGS = WatchUpdaterService.class.getName().concat(".OpenSettings"); - public static final String ACTION_SEND_STATUS = WatchUpdaterService.class.getName().concat(".SendStatus"); - public static final String ACTION_SEND_BASALS = WatchUpdaterService.class.getName().concat(".SendBasals"); - public static final String ACTION_SEND_BOLUSPROGRESS = WatchUpdaterService.class.getName().concat(".BolusProgress"); - public static final String ACTION_SEND_ACTIONCONFIRMATIONREQUEST = WatchUpdaterService.class.getName().concat(".ActionConfirmationRequest"); - public static final String ACTION_SEND_CHANGECONFIRMATIONREQUEST = WatchUpdaterService.class.getName().concat(".ChangeConfirmationRequest"); - public static final String ACTION_CANCEL_NOTIFICATION = WatchUpdaterService.class.getName().concat(".CancelNotification"); - - private GoogleApiClient googleApiClient; - - String TAG = "WatchUpdateService"; - - private static boolean lastLoopStatus; - - private Handler handler; - - // Phone - private static final String CAPABILITY_PHONE_APP = "phone_app_sync_bgs"; - private static final String MESSAGE_PATH_PHONE = "/phone_message_path"; - // Wear - private static final String CAPABILITY_WEAR_APP = "wear_app_sync_bgs"; - private static final String MESSAGE_PATH_WEAR = "/wear_message_path"; - - - @Override - public void onCreate() { - AndroidInjection.inject(this); - if (wearIntegration()) { - googleApiConnect(); - } - if (handler == null) { - HandlerThread handlerThread = new HandlerThread(this.getClass().getSimpleName() + "Handler"); - handlerThread.start(); - handler = new Handler(handlerThread.getLooper()); - } - } - - private boolean wearIntegration() { - return wearPlugin.isEnabled(); - } - - private void googleApiConnect() { - if (googleApiClient != null && (googleApiClient.isConnected() || googleApiClient.isConnecting())) { - googleApiClient.disconnect(); - } - googleApiClient = new GoogleApiClient.Builder(this).addConnectionCallbacks(this) - .addOnConnectionFailedListener(this).addApi(Wearable.API).build(); - Wearable.MessageApi.addListener(googleApiClient, this); - if (googleApiClient.isConnected()) { - aapsLogger.debug(LTag.WEAR, "API client is connected"); - } else { - // Log.d("WatchUpdater", logPrefix + "API client is not connected and is trying to connect"); - googleApiClient.connect(); - } - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - String action = intent != null ? intent.getAction() : null; - - // Log.d(TAG, "onStartCommand: " + action); - - if (wearIntegration()) { - handler.post(() -> { - if (googleApiClient != null && googleApiClient.isConnected()) { - if (ACTION_RESEND.equals(action)) { - resendData(); - } else if (ACTION_OPEN_SETTINGS.equals(action)) { - sendNotification(); - } else if (ACTION_SEND_STATUS.equals(action)) { - sendStatus(); - } else if (ACTION_SEND_BASALS.equals(action)) { - sendBasals(); - } else if (ACTION_SEND_BOLUSPROGRESS.equals(action)) { - sendBolusProgress(intent.getIntExtra("progresspercent", 0), intent.hasExtra("progressstatus") ? intent.getStringExtra("progressstatus") : ""); - } else if (ACTION_SEND_ACTIONCONFIRMATIONREQUEST.equals(action)) { - String title = intent.getStringExtra("title"); - String message = intent.getStringExtra("message"); - String actionstring = intent.getStringExtra("actionstring"); - sendActionConfirmationRequest(title, message, actionstring); - } else if (ACTION_SEND_CHANGECONFIRMATIONREQUEST.equals(action)) { - String title = intent.getStringExtra("title"); - String message = intent.getStringExtra("message"); - String actionstring = intent.getStringExtra("actionstring"); - sendChangeConfirmationRequest(title, message, actionstring); - } else if (ACTION_CANCEL_NOTIFICATION.equals(action)) { - String actionstring = intent.getStringExtra("actionstring"); - sendCancelNotificationRequest(actionstring); - } else { - sendData(); - } - } else { - if (googleApiClient != null) googleApiClient.connect(); - } - }); - } - - return START_STICKY; - } - - - private void updateWearSyncBgsCapability(CapabilityInfo capabilityInfo) { - Log.d("WatchUpdaterService", "CabilityInfo: " + capabilityInfo); - Set connectedNodes = capabilityInfo.getNodes(); - String mWearNodeId = pickBestNodeId(connectedNodes); - } - - - private String pickBestNodeId(Set nodes) { - String bestNodeId = null; - // Find a nearby node or pick one arbitrarily - for (Node node : nodes) { - if (node.isNearby()) { - return node.getId(); - } - bestNodeId = node.getId(); - } - return bestNodeId; - } - - - @Override - public void onConnected(Bundle connectionHint) { - CapabilityApi.CapabilityListener capabilityListener = capabilityInfo -> { - updateWearSyncBgsCapability(capabilityInfo); - // Log.d(TAG, logPrefix + "onConnected onCapabilityChanged mWearNodeID:" + mWearNodeId); - // new CheckWearableConnected().execute(); - }; - - Wearable.CapabilityApi.addCapabilityListener(googleApiClient, capabilityListener, CAPABILITY_WEAR_APP); - sendData(); - } - - - @Override - public void onPeerConnected(com.google.android.gms.wearable.Node peer) {// KS - super.onPeerConnected(peer); - String id = peer.getId(); - String name = peer.getDisplayName(); - Log.d(TAG, "onPeerConnected peer name & ID: " + name + "|" + id); - } - - - @Override - public void onPeerDisconnected(com.google.android.gms.wearable.Node peer) {// KS - super.onPeerDisconnected(peer); - String id = peer.getId(); - String name = peer.getDisplayName(); - Log.d(TAG, "onPeerDisconnected peer name & ID: " + name + "|" + id); - } - - - @Override - public void onMessageReceived(MessageEvent event) { - - // Log.d(TAG, "onMessageRecieved: " + event); - - if (wearIntegration()) { - if (event != null && event.getPath().equals(WearUris.WEARABLE_RESEND_PATH)) { - resendData(); - } - - if (event != null && event.getPath().equals(WearUris.WEARABLE_CANCELBOLUS_PATH)) { - cancelBolus(); - } - - if (event != null && event.getPath().equals(WearUris.WEARABLE_INITIATE_ACTIONSTRING_PATH)) { - String actionstring = new String(event.getData()); - aapsLogger.debug(LTag.WEAR, "Wear: " + actionstring); - rxBus.send(new EventWearInitiateAction(actionstring)); - } - - if (event != null && event.getPath().equals(WearUris.WEARABLE_CONFIRM_ACTIONSTRING_PATH)) { - String actionstring = new String(event.getData()); - aapsLogger.debug(LTag.WEAR, "Wear Confirm: " + actionstring); - rxBus.send(new EventWearConfirmAction(actionstring)); - } - } - } - - private void cancelBolus() { - activePlugin.getActivePump().stopBolusDelivering(); - } - - private void sendData() { - - GlucoseValue lastBG = iobCobCalculator.getAds().lastBg(); - // Log.d(TAG, "LastBg=" + lastBG); - if (lastBG != null) { - GlucoseStatus glucoseStatus = glucoseStatusProvider.getGlucoseStatusData(); - - if (googleApiClient != null && !googleApiClient.isConnected() && !googleApiClient.isConnecting()) { - googleApiConnect(); - } - if (wearIntegration()) { - - final DataMap dataMap = dataMapSingleBG(lastBG, glucoseStatus); - - (new SendToDataLayerThread(WearUris.WEARABLE_DATA_PATH, googleApiClient)).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, dataMap); - } - } - } - - - private DataMap dataMapSingleBG(GlucoseValue lastBG, GlucoseStatus glucoseStatus) { - GlucoseUnit units = profileFunction.getUnits(); - double convert2MGDL = 1.0; - if (units.equals(GlucoseUnit.MMOL)) - convert2MGDL = Constants.MMOLL_TO_MGDL; - double lowLine = defaultValueHelper.determineLowLine() * convert2MGDL; - double highLine = defaultValueHelper.determineHighLine() * convert2MGDL; - - long sgvLevel = 0L; - if (lastBG.getValue() > highLine) { - sgvLevel = 1; - } else if (lastBG.getValue() < lowLine) { - sgvLevel = -1; - } - - DataMap dataMap = new DataMap(); - dataMap.putString("sgvString", GlucoseValueExtensionKt.valueToUnitsString(lastBG, units)); - dataMap.putString("glucoseUnits", units.getAsText()); - dataMap.putLong("timestamp", lastBG.getTimestamp()); - if (glucoseStatus == null) { - dataMap.putString("slopeArrow", ""); - dataMap.putString("delta", "--"); - dataMap.putString("avgDelta", "--"); - } else { - dataMap.putString("slopeArrow", trendCalculator.getTrendArrow(lastBG).getSymbol()); - dataMap.putString("delta", deltastring(glucoseStatus.getDelta(), glucoseStatus.getDelta() * Constants.MGDL_TO_MMOLL, units)); - dataMap.putString("avgDelta", deltastring(glucoseStatus.getShortAvgDelta(), glucoseStatus.getShortAvgDelta() * Constants.MGDL_TO_MMOLL, units)); - } - dataMap.putLong("sgvLevel", sgvLevel); - dataMap.putDouble("sgvDouble", lastBG.getValue()); - dataMap.putDouble("high", highLine); - dataMap.putDouble("low", lowLine); - return dataMap; - } - - private String deltastring(double deltaMGDL, double deltaMMOL, GlucoseUnit units) { - String deltastring = ""; - if (deltaMGDL >= 0) { - deltastring += "+"; - } else { - deltastring += "-"; - } - - boolean detailed = sp.getBoolean(R.string.key_wear_detailed_delta, false); - if (units.equals(GlucoseUnit.MGDL)) { - if (detailed) { - deltastring += DecimalFormatter.INSTANCE.to1Decimal(Math.abs(deltaMGDL)); - } else { - deltastring += DecimalFormatter.INSTANCE.to0Decimal(Math.abs(deltaMGDL)); - } - } else { - if (detailed) { - deltastring += DecimalFormatter.INSTANCE.to2Decimal(Math.abs(deltaMMOL)); - } else { - deltastring += DecimalFormatter.INSTANCE.to1Decimal(Math.abs(deltaMMOL)); - } - } - return deltastring; - } - - private void resendData() { - if (googleApiClient != null && !googleApiClient.isConnected() && !googleApiClient.isConnecting()) { - googleApiConnect(); - } - - sendPreferences(); - sendQuickWizard(); - - long startTime = System.currentTimeMillis() - (long) (60000 * 60 * 5.5); - GlucoseValue last_bg = iobCobCalculator.getAds().lastBg(); - - if (last_bg == null) return; - - List graph_bgs = repository.compatGetBgReadingsDataFromTime(startTime, true).blockingGet(); - GlucoseStatus glucoseStatus = glucoseStatusProvider.getGlucoseStatusData(true); - - if (!graph_bgs.isEmpty()) { - DataMap entries = dataMapSingleBG(last_bg, glucoseStatus); - final ArrayList dataMaps = new ArrayList<>(graph_bgs.size()); - for (GlucoseValue bg : graph_bgs) { - DataMap dataMap = dataMapSingleBG(bg, glucoseStatus); - dataMaps.add(dataMap); - } - entries.putDataMapArrayList("entries", dataMaps); - (new SendToDataLayerThread(WearUris.WEARABLE_DATA_PATH, googleApiClient)).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, entries); - } - sendBasals(); - sendStatus(); - } - - private void sendBasals() { - if (googleApiClient != null && !googleApiClient.isConnected() && !googleApiClient.isConnecting()) { - googleApiConnect(); - } - - long now = System.currentTimeMillis(); - final long startTimeWindow = now - (long) (60000 * 60 * 5.5); - - - ArrayList basals = new ArrayList<>(); - ArrayList temps = new ArrayList<>(); - ArrayList boluses = new ArrayList<>(); - ArrayList predictions = new ArrayList<>(); - - - Profile profile = profileFunction.getProfile(); - - if (profile == null) { - return; - } - - long beginBasalSegmentTime = startTimeWindow; - long runningTime = startTimeWindow; - - double beginBasalValue = profile.getBasal(beginBasalSegmentTime); - double endBasalValue = beginBasalValue; - - TemporaryBasal tb1 = iobCobCalculator.getTempBasalIncludingConvertedExtended(runningTime); - TemporaryBasal tb2; - double tb_before = beginBasalValue; - double tb_amount = beginBasalValue; - long tb_start = runningTime; - - if (tb1 != null) { - Profile profileTB = profileFunction.getProfile(runningTime); - if (profileTB != null) { - tb_amount = TemporaryBasalExtensionKt.convertedToAbsolute(tb1, runningTime, profileTB); - tb_start = runningTime; - } - } - - - for (; runningTime < now; runningTime += 5 * 60 * 1000) { - Profile profileTB = profileFunction.getProfile(runningTime); - if (profileTB == null) - return; - //basal rate - endBasalValue = profile.getBasal(runningTime); - if (endBasalValue != beginBasalValue) { - //push the segment we recently left - basals.add(basalMap(beginBasalSegmentTime, runningTime, beginBasalValue)); - - //begin new Basal segment - beginBasalSegmentTime = runningTime; - beginBasalValue = endBasalValue; - } - - //temps - tb2 = iobCobCalculator.getTempBasalIncludingConvertedExtended(runningTime); - - if (tb1 == null && tb2 == null) { - ; //no temp stays no temp - - } else if (tb1 != null && tb2 == null) { - //temp is over -> push it - temps.add(tempDatamap(tb_start, tb_before, runningTime, endBasalValue, tb_amount)); - tb1 = null; - - } else if (tb1 == null && tb2 != null) { - //temp begins - tb1 = tb2; - tb_start = runningTime; - tb_before = endBasalValue; - tb_amount = TemporaryBasalExtensionKt.convertedToAbsolute(tb1, runningTime, profileTB); - - } else if (tb1 != null && tb2 != null) { - double currentAmount = TemporaryBasalExtensionKt.convertedToAbsolute(tb2, runningTime, profileTB); - if (currentAmount != tb_amount) { - temps.add(tempDatamap(tb_start, tb_before, runningTime, currentAmount, tb_amount)); - tb_start = runningTime; - tb_before = tb_amount; - tb_amount = currentAmount; - tb1 = tb2; - } - } - } - if (beginBasalSegmentTime != runningTime) { - //push the remaining segment - basals.add(basalMap(beginBasalSegmentTime, runningTime, beginBasalValue)); - } - if (tb1 != null) { - tb2 = iobCobCalculator.getTempBasalIncludingConvertedExtended(now); //use "now" to express current situation - if (tb2 == null) { - //express the cancelled temp by painting it down one minute early - temps.add(tempDatamap(tb_start, tb_before, now - 60 * 1000, endBasalValue, tb_amount)); - } else { - //express currently running temp by painting it a bit into the future - Profile profileNow = profileFunction.getProfile(now); - double currentAmount = TemporaryBasalExtensionKt.convertedToAbsolute(tb2, now, profileNow); - if (currentAmount != tb_amount) { - temps.add(tempDatamap(tb_start, tb_before, now, tb_amount, tb_amount)); - temps.add(tempDatamap(now, tb_amount, runningTime + 5 * 60 * 1000, currentAmount, currentAmount)); - } else { - temps.add(tempDatamap(tb_start, tb_before, runningTime + 5 * 60 * 1000, tb_amount, tb_amount)); - } - } - } else { - tb2 = iobCobCalculator.getTempBasalIncludingConvertedExtended(now); //use "now" to express current situation - if (tb2 != null) { - //onset at the end - Profile profileTB = profileFunction.getProfile(runningTime); - double currentAmount = TemporaryBasalExtensionKt.convertedToAbsolute(tb2, runningTime, profileTB); - temps.add(tempDatamap(now - 60 * 1000, endBasalValue, runningTime + 5 * 60 * 1000, currentAmount, currentAmount)); - } - } - - repository.getBolusesIncludingInvalidFromTime(startTimeWindow, true).blockingGet() - .stream() - .filter(bolus -> bolus.getType() != Bolus.Type.PRIMING) - .forEach(bolus -> boluses.add(treatmentMap(bolus.getTimestamp(), bolus.getAmount(), 0, bolus.getType() == Bolus.Type.SMB, bolus.isValid()))); - repository.getCarbsDataFromTimeExpanded(startTimeWindow, true).blockingGet() - .forEach(carb -> boluses.add(treatmentMap(carb.getTimestamp(), 0, carb.getAmount(), false, carb.isValid()))); - - final LoopPlugin.LastRun finalLastRun = loop.getLastRun(); - if (sp.getBoolean("wear_predictions", true) && finalLastRun != null && finalLastRun.getRequest().getHasPredictions() && finalLastRun.getConstraintsProcessed() != null) { - List predArray = - finalLastRun.getConstraintsProcessed().getPredictions() - .stream().map(bg -> new GlucoseValueDataPoint(bg, defaultValueHelper, profileFunction, rh)) - .collect(Collectors.toList()); - - if (!predArray.isEmpty()) { - for (GlucoseValueDataPoint bg : predArray) { - if (bg.getData().getValue() < 40) continue; - predictions.add(predictionMap(bg.getData().getTimestamp(), - bg.getData().getValue(), bg.color(null))); - } - } - } - - - DataMap dm = new DataMap(); - dm.putDataMapArrayList("basals", basals); - dm.putDataMapArrayList("temps", temps); - dm.putDataMapArrayList("boluses", boluses); - dm.putDataMapArrayList("predictions", predictions); - (new SendToDataLayerThread(WearUris.BASAL_DATA_PATH, googleApiClient)).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, dm); - } - - private DataMap tempDatamap(long startTime, double startBasal, long to, double toBasal, double amount) { - DataMap dm = new DataMap(); - dm.putLong("starttime", startTime); - dm.putDouble("startBasal", startBasal); - dm.putLong("endtime", to); - dm.putDouble("endbasal", toBasal); - dm.putDouble("amount", amount); - return dm; - } - - private DataMap basalMap(long startTime, long endTime, double amount) { - DataMap dm = new DataMap(); - dm.putLong("starttime", startTime); - dm.putLong("endtime", endTime); - dm.putDouble("amount", amount); - return dm; - } - - private DataMap treatmentMap(long date, double bolus, double carbs, boolean isSMB, boolean isValid) { - DataMap dm = new DataMap(); - dm.putLong("date", date); - dm.putDouble("bolus", bolus); - dm.putDouble("carbs", carbs); - dm.putBoolean("isSMB", isSMB); - dm.putBoolean("isValid", isValid); - return dm; - } - - private DataMap predictionMap(long timestamp, double sgv, int color) { - DataMap dm = new DataMap(); - dm.putLong("timestamp", timestamp); - dm.putDouble("sgv", sgv); - dm.putInt("color", color); - return dm; - } - - - private void sendNotification() { - if (googleApiClient != null && googleApiClient.isConnected()) { - PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.OPEN_SETTINGS_PATH); - //unique content - dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis()); - dataMapRequest.getDataMap().putString("openSettings", "openSettings"); - PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest(); - Wearable.DataApi.putDataItem(googleApiClient, putDataRequest); - } else { - Log.e("OpenSettings", "No connection to wearable available!"); - } - } - - private void sendBolusProgress(int progresspercent, String status) { - if (googleApiClient != null && googleApiClient.isConnected()) { - PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.BOLUS_PROGRESS_PATH); - //unique content - dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis()); - dataMapRequest.getDataMap().putString("bolusProgress", "bolusProgress"); - dataMapRequest.getDataMap().putString("progressstatus", status); - dataMapRequest.getDataMap().putInt("progresspercent", progresspercent); - PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest(); - Wearable.DataApi.putDataItem(googleApiClient, putDataRequest); - } else { - Log.e("BolusProgress", "No connection to wearable available!"); - } - } - - private void sendActionConfirmationRequest(String title, String message, String actionstring) { - if (googleApiClient != null && googleApiClient.isConnected()) { - PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.ACTION_CONFIRMATION_REQUEST_PATH); - //unique content - dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis()); - dataMapRequest.getDataMap().putString("actionConfirmationRequest", "actionConfirmationRequest"); - dataMapRequest.getDataMap().putString("title", title); - dataMapRequest.getDataMap().putString("message", message); - dataMapRequest.getDataMap().putString("actionstring", actionstring); - - aapsLogger.debug(LTag.WEAR, "Requesting confirmation from wear: " + actionstring); - - PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest(); - Wearable.DataApi.putDataItem(googleApiClient, putDataRequest); - } else { - Log.e("confirmationRequest", "No connection to wearable available!"); - } - } - - private void sendChangeConfirmationRequest(String title, String message, String actionstring) { - if (googleApiClient != null && googleApiClient.isConnected()) { - PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.ACTION_CHANGECONFIRMATION_REQUEST_PATH); - //unique content - dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis()); - dataMapRequest.getDataMap().putString("changeConfirmationRequest", "changeConfirmationRequest"); - dataMapRequest.getDataMap().putString("title", title); - dataMapRequest.getDataMap().putString("message", message); - dataMapRequest.getDataMap().putString("actionstring", actionstring); - - aapsLogger.debug(LTag.WEAR, "Requesting confirmation from wear: " + actionstring); - - PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest(); - Wearable.DataApi.putDataItem(googleApiClient, putDataRequest); - } else { - Log.e("changeConfirmRequest", "No connection to wearable available!"); - } - } - - private void sendCancelNotificationRequest(String actionstring) { - if (googleApiClient != null && googleApiClient.isConnected()) { - PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.ACTION_CANCELNOTIFICATION_REQUEST_PATH); - //unique content - dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis()); - dataMapRequest.getDataMap().putString("cancelNotificationRequest", "cancelNotificationRequest"); - dataMapRequest.getDataMap().putString("actionstring", actionstring); - - aapsLogger.debug(LTag.WEAR, "Canceling notification on wear: " + actionstring); - - PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest(); - Wearable.DataApi.putDataItem(googleApiClient, putDataRequest); - } else { - Log.e("cancelNotificationReq", "No connection to wearable available!"); - } - } - - private void sendStatus() { - - if (googleApiClient != null && googleApiClient.isConnected()) { - Profile profile = profileFunction.getProfile(); - String status = rh.gs(R.string.noprofile); - String iobSum, iobDetail, cobString, currentBasal, bgiString; - iobSum = iobDetail = cobString = currentBasal = bgiString = ""; - if (profile != null) { - IobTotal bolusIob = iobCobCalculator.calculateIobFromBolus().round(); - IobTotal basalIob = iobCobCalculator.calculateIobFromTempBasalsIncludingConvertedExtended().round(); - - iobSum = DecimalFormatter.INSTANCE.to2Decimal(bolusIob.getIob() + basalIob.getBasaliob()); - iobDetail = - "(" + DecimalFormatter.INSTANCE.to2Decimal(bolusIob.getIob()) + "|" + DecimalFormatter.INSTANCE.to2Decimal(basalIob.getBasaliob()) + ")"; - cobString = iobCobCalculator.getCobInfo(false, "WatcherUpdaterService").generateCOBString(); - currentBasal = generateBasalString(); - - //bgi - double bgi = - -(bolusIob.getActivity() + basalIob.getActivity()) * 5 * Profile.Companion.fromMgdlToUnits(profile.getIsfMgdl(), profileFunction.getUnits()); - bgiString = "" + ((bgi >= 0) ? "+" : "") + DecimalFormatter.INSTANCE.to1Decimal(bgi); - - status = generateStatusString(profile, currentBasal, iobSum, iobDetail, bgiString); - } - - - //batteries - int phoneBattery = receiverStatusStore.getBatteryLevel(); - String rigBattery = nsDeviceStatus.getUploaderStatus().trim(); - - - long openApsStatus; - //OpenAPS status - if (config.getAPS()) { - //we are AndroidAPS - openApsStatus = loop.getLastRun() != null && loop.getLastRun().getLastTBREnact() != 0 ? loop.getLastRun().getLastTBREnact() : -1; - } else { - //NSClient or remote - openApsStatus = nsDeviceStatus.getOpenApsTimestamp(); - } - - PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.NEW_STATUS_PATH); - //unique content - dataMapRequest.getDataMap().putString("externalStatusString", status); - dataMapRequest.getDataMap().putString("iobSum", iobSum); - dataMapRequest.getDataMap().putString("iobDetail", iobDetail); - dataMapRequest.getDataMap().putBoolean("detailedIob", sp.getBoolean(R.string.key_wear_detailediob, false)); - dataMapRequest.getDataMap().putString("cob", cobString); - dataMapRequest.getDataMap().putString("currentBasal", currentBasal); - dataMapRequest.getDataMap().putString("battery", "" + phoneBattery); - dataMapRequest.getDataMap().putString("rigBattery", rigBattery); - dataMapRequest.getDataMap().putLong("openApsStatus", openApsStatus); - dataMapRequest.getDataMap().putString("bgi", bgiString); - dataMapRequest.getDataMap().putBoolean("showBgi", sp.getBoolean(R.string.key_wear_showbgi, false)); - dataMapRequest.getDataMap().putInt("batteryLevel", (phoneBattery >= 30) ? 1 : 0); - PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest(); - Wearable.DataApi.putDataItem(googleApiClient, putDataRequest); - } else { - Log.e("SendStatus", "No connection to wearable available!"); - } - } - - private void sendPreferences() { - if (googleApiClient != null && googleApiClient.isConnected()) { - - GlucoseUnit units = profileFunction.getUnits(); - boolean wearcontrol = sp.getBoolean(R.string.key_wear_control, false); - boolean mgdl = units.equals(GlucoseUnit.MGDL); - int percentage = sp.getInt(R.string.key_boluswizard_percentage, 100); - int maxCarbs = sp.getInt(R.string.key_treatmentssafety_maxcarbs, 48); - double maxBolus = sp.getDouble(R.string.key_treatmentssafety_maxbolus, 3.0); - PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.NEW_PREFERENCES_PATH); - //unique content - dataMapRequest.getDataMap().putLong("timestamp", System.currentTimeMillis()); - dataMapRequest.getDataMap().putBoolean(rh.gs(R.string.key_wear_control), wearcontrol); - dataMapRequest.getDataMap().putBoolean(rh.gs(R.string.key_units_mgdl), mgdl); - dataMapRequest.getDataMap().putInt(rh.gs(R.string.key_boluswizard_percentage), percentage); - dataMapRequest.getDataMap().putInt(rh.gs(R.string.key_treatmentssafety_maxcarbs), maxCarbs); - dataMapRequest.getDataMap().putDouble(rh.gs(R.string.key_treatmentssafety_maxbolus), maxBolus); - PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest(); - Wearable.DataApi.putDataItem(googleApiClient, putDataRequest); - } else { - Log.e("SendPreferences", "No connection to wearable available!"); - } - } - - private void sendQuickWizard() { - if (googleApiClient != null && googleApiClient.isConnected()) { - int size = quickWizard.size(); - ArrayList entities = new ArrayList<>(); - for (int i = 0; i < size; i++) { - QuickWizardEntry q = quickWizard.get(i); - if (q.forDevice(QuickWizardEntry.DEVICE_WATCH)) { - entities.add(quickMap(q)); - } - } - - PutDataMapRequest dataMapRequest = PutDataMapRequest.create(WearUris.QUICK_WIZARD_PATH); - - DataMap dm = dataMapRequest.getDataMap(); - dm.putLong("timestamp", System.currentTimeMillis()); - dm.putDataMapArrayList("quick_wizard", entities); - - PutDataRequest putDataRequest = dataMapRequest.asPutDataRequest(); - Log.i(TAG, "sendQuickWizard: " + putDataRequest); - Wearable.DataApi.putDataItem(googleApiClient, putDataRequest); - } else { - Log.e("sendQuickWizard", "No connection to wearable available!"); - } - } - - private DataMap quickMap(QuickWizardEntry q) { - DataMap dm = new DataMap(); - dm.putString("guid", q.guid()); - dm.putString("button_text", q.buttonText()); - dm.putInt("carbs", q.carbs()); - dm.putInt("from", q.validFrom()); - dm.putInt("to", q.validTo()); - return dm; - } - - @NonNull - private String generateStatusString(Profile profile, String currentBasal, String iobSum, String iobDetail, String bgiString) { - - String status = ""; - - if (profile == null) { - status = rh.gs(R.string.noprofile); - return status; - } - - if (!((PluginBase) loop).isEnabled()) { - status += rh.gs(R.string.disabledloop) + "\n"; - lastLoopStatus = false; - } else { - lastLoopStatus = true; - } - - String iobString; - if (sp.getBoolean(R.string.key_wear_detailediob, false)) { - iobString = iobSum + " " + iobDetail; - } else { - iobString = iobSum + "U"; - } - - status += currentBasal + " " + iobString; - - //add BGI if shown, otherwise return - if (sp.getBoolean(R.string.key_wear_showbgi, false)) { - status += " " + bgiString; - } - - return status; - } - - @NonNull - private String generateBasalString() { - - String basalStringResult; - - Profile profile = profileFunction.getProfile(); - if (profile == null) - return ""; - - TemporaryBasal activeTemp = iobCobCalculator.getTempBasalIncludingConvertedExtended(System.currentTimeMillis()); - if (activeTemp != null) { - basalStringResult = TemporaryBasalExtensionKt.toStringShort(activeTemp); - } else { - basalStringResult = DecimalFormatter.INSTANCE.to2Decimal(profile.getBasal()) + "U/h"; - } - return basalStringResult; - } - - @Override - public void onDestroy() { - if (googleApiClient != null && googleApiClient.isConnected()) { - googleApiClient.disconnect(); - } - } - - @Override - public void onConnectionSuspended(int cause) { - } - - @Override - public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { - } - - public static boolean shouldReportLoopStatus(boolean enabled) { - return (lastLoopStatus != enabled); - } -} -*/ \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/queue/CommandQueueImplementation.kt b/app/src/main/java/info/nightscout/androidaps/queue/CommandQueueImplementation.kt index 1bf2891a81..4f9b16efcc 100644 --- a/app/src/main/java/info/nightscout/androidaps/queue/CommandQueueImplementation.kt +++ b/app/src/main/java/info/nightscout/androidaps/queue/CommandQueueImplementation.kt @@ -19,12 +19,10 @@ import info.nightscout.androidaps.database.entities.EffectiveProfileSwitch import info.nightscout.androidaps.database.entities.ProfileSwitch import info.nightscout.androidaps.database.interfaces.end import info.nightscout.androidaps.dialogs.BolusProgressDialog -import info.nightscout.androidaps.events.EventBolusRequested +import info.nightscout.androidaps.events.EventMobileToWear import info.nightscout.androidaps.events.EventProfileSwitchChanged import info.nightscout.androidaps.extensions.getCustomizedName import info.nightscout.androidaps.interfaces.* -import info.nightscout.shared.logging.AAPSLogger -import info.nightscout.shared.logging.LTag import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.androidaps.plugins.configBuilder.ConstraintChecker import info.nightscout.androidaps.plugins.general.overview.events.EventDismissBolusProgressIfRunning @@ -40,7 +38,10 @@ import info.nightscout.androidaps.utils.HtmlHelper import info.nightscout.androidaps.utils.buildHelper.BuildHelper import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.rx.AapsSchedulers +import info.nightscout.shared.logging.AAPSLogger +import info.nightscout.shared.logging.LTag import info.nightscout.shared.sharedPreferences.SP +import info.nightscout.shared.weardata.EventData import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign import io.reactivex.rxjava3.kotlin.subscribeBy @@ -297,7 +298,7 @@ class CommandQueueImplementation @Inject constructor( // not when the Bolus command is starting. The command closes the dialog upon completion). showBolusProgressDialog(detailedBolusInfo) // Notify Wear about upcoming bolus - rxBus.send(EventBolusRequested(detailedBolusInfo.insulin)) + rxBus.send(EventMobileToWear(EventData.BolusProgress(percent = 0, status = rh.gs(R.string.bolusrequested, detailedBolusInfo.insulin)))) } } notifyAboutNewCommand() diff --git a/app/src/main/java/info/nightscout/androidaps/utils/wizard/BolusWizard.kt b/app/src/main/java/info/nightscout/androidaps/utils/wizard/BolusWizard.kt index 4b691ec932..581e201deb 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/wizard/BolusWizard.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/wizard/BolusWizard.kt @@ -65,10 +65,14 @@ class BolusWizard @Inject constructor( private val disposable = CompositeDisposable() + var timeStamp : Long + init { injector.androidInjector().inject(this) + timeStamp = dateUtil.now() } + // Intermediate var sens = 0.0 private set @@ -236,7 +240,7 @@ class BolusWizard @Inject constructor( // Total calculatedTotalInsulin = insulinFromBG + insulinFromTrend + insulinFromCarbs + insulinFromBolusIOB + insulinFromBasalIOB + insulinFromCorrection + insulinFromSuperBolus + insulinFromCOB - var percentage = if (usePercentage) totalPercentage else percentageCorrection.toDouble() + val percentage = if (usePercentage) totalPercentage else percentageCorrection.toDouble() // Percentage adjustment totalBeforePercentageAdjustment = calculatedTotalInsulin diff --git a/app/src/main/java/info/nightscout/androidaps/utils/wizard/QuickWizard.kt b/app/src/main/java/info/nightscout/androidaps/utils/wizard/QuickWizard.kt index b48d44c9cc..450ce43d46 100644 --- a/app/src/main/java/info/nightscout/androidaps/utils/wizard/QuickWizard.kt +++ b/app/src/main/java/info/nightscout/androidaps/utils/wizard/QuickWizard.kt @@ -9,6 +9,7 @@ import org.json.JSONObject import java.util.* import javax.inject.Inject import javax.inject.Singleton +import kotlin.collections.ArrayList @Singleton class QuickWizard @Inject constructor( @@ -55,6 +56,11 @@ class QuickWizard @Inject constructor( operator fun get(position: Int): QuickWizardEntry = QuickWizardEntry(injector).from(storage.get(position) as JSONObject, position) + fun list(): ArrayList = + ArrayList().also { + for (i in 0 until size()) it.add(get(i)) + } + fun get(guid: String): QuickWizardEntry? { for (i in 0 until storage.length()) { val entry = QuickWizardEntry(injector).from(storage.get(i) as JSONObject, i) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a8f8bf0efd..316ef63054 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1212,4 +1212,5 @@ Hide loop records AndroidAPS widget Configure opacity + Loop status diff --git a/app/src/test/java/info/nightscout/androidaps/queue/CommandQueueImplementationTest.kt b/app/src/test/java/info/nightscout/androidaps/queue/CommandQueueImplementationTest.kt index 1f49ca02a5..c16f8a52bb 100644 --- a/app/src/test/java/info/nightscout/androidaps/queue/CommandQueueImplementationTest.kt +++ b/app/src/test/java/info/nightscout/androidaps/queue/CommandQueueImplementationTest.kt @@ -4,9 +4,9 @@ import android.content.Context import android.os.PowerManager import dagger.android.AndroidInjector import dagger.android.HasAndroidInjector +import info.nightscout.androidaps.R import info.nightscout.androidaps.TestBaseWithProfile import info.nightscout.androidaps.TestPumpPlugin -import info.nightscout.androidaps.core.R import info.nightscout.androidaps.data.DetailedBolusInfo import info.nightscout.androidaps.data.PumpEnactResult import info.nightscout.androidaps.database.AppRepository @@ -138,6 +138,7 @@ class CommandQueueImplementationTest : TestBaseWithProfile() { `when`(constraintChecker.applyBasalPercentConstraints(anyObject(), anyObject())).thenReturn(percentageConstraint) `when`(rh.gs(R.string.connectiontimedout)).thenReturn("Connection timed out") `when`(rh.gs(R.string.formatinsulinunits)).thenReturn("%1\$.2f U") + `when`(rh.gs(R.string.bolusrequested)).thenReturn("Going to deliver %1\$.2f U") } @Test diff --git a/app/src/test/java/info/nightscout/androidaps/utils/wizard/BolusWizardTest.kt b/app/src/test/java/info/nightscout/androidaps/utils/wizard/BolusWizardTest.kt index 6cec97fb42..420f062103 100644 --- a/app/src/test/java/info/nightscout/androidaps/utils/wizard/BolusWizardTest.kt +++ b/app/src/test/java/info/nightscout/androidaps/utils/wizard/BolusWizardTest.kt @@ -48,6 +48,7 @@ class BolusWizardTest : TestBase() { it.activePlugin = activePlugin it.commandQueue = commandQueue it.loop = loop + it.dateUtil = dateUtil it.iobCobCalculator = iobCobCalculator it.glucoseStatusProvider = GlucoseStatusProvider(aapsLogger = aapsLogger, iobCobCalculator = iobCobCalculator, dateUtil = dateUtil) } diff --git a/shared/src/main/java/info/nightscout/androidaps/events/EventMobileToWear.kt b/shared/src/main/java/info/nightscout/androidaps/events/EventMobileToWear.kt new file mode 100644 index 0000000000..c13bd0b2fe --- /dev/null +++ b/shared/src/main/java/info/nightscout/androidaps/events/EventMobileToWear.kt @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.events + +import info.nightscout.shared.weardata.EventData + +class EventMobileToWear(val payload: EventData) : Event() \ No newline at end of file diff --git a/shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobile.kt b/shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobile.kt new file mode 100644 index 0000000000..3967cebaa9 --- /dev/null +++ b/shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobile.kt @@ -0,0 +1,5 @@ +package info.nightscout.androidaps.events + +import info.nightscout.shared.weardata.EventData + +class EventWearToMobile(val payload: EventData) : Event() \ No newline at end of file diff --git a/shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobileAction.kt b/shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobileAction.kt deleted file mode 100644 index 1d5ca463e9..0000000000 --- a/shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobileAction.kt +++ /dev/null @@ -1,7 +0,0 @@ -package info.nightscout.androidaps.events - -import info.nightscout.shared.weardata.ActionData -import kotlinx.serialization.Serializable -import kotlinx.serialization.json.Json - -class EventWearToMobileAction(val actionData: ActionData) : Event() \ No newline at end of file diff --git a/shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobileChange.kt b/shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobileChange.kt deleted file mode 100644 index 49ae662e82..0000000000 --- a/shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobileChange.kt +++ /dev/null @@ -1,7 +0,0 @@ -package info.nightscout.androidaps.events - -import info.nightscout.shared.weardata.ActionData -import kotlinx.serialization.Serializable -import kotlinx.serialization.json.Json - -class EventWearToMobileChange(val actionData: ActionData) : Event() \ No newline at end of file diff --git a/shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobileConfirm.kt b/shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobileConfirm.kt deleted file mode 100644 index c999248cde..0000000000 --- a/shared/src/main/java/info/nightscout/androidaps/events/EventWearToMobileConfirm.kt +++ /dev/null @@ -1,7 +0,0 @@ -package info.nightscout.androidaps.events - -import info.nightscout.shared.weardata.ActionData -import kotlinx.serialization.Serializable -import kotlinx.serialization.json.Json - -class EventWearToMobileConfirm(val actionData: ActionData) : Event() \ No newline at end of file diff --git a/shared/src/main/java/info/nightscout/shared/weardata/ActionData.kt b/shared/src/main/java/info/nightscout/shared/weardata/ActionData.kt deleted file mode 100644 index 042edc0d6f..0000000000 --- a/shared/src/main/java/info/nightscout/shared/weardata/ActionData.kt +++ /dev/null @@ -1,33 +0,0 @@ -package info.nightscout.shared.weardata - -import kotlinx.serialization.Serializable -import kotlinx.serialization.json.Json - -@Serializable -sealed class ActionData { - - fun serialize() = Json.encodeToString(serializer(), this) - - companion object { - - fun deserialize(json: String) = Json.decodeFromString(serializer(), json) - } - // Wear -> Mobile - @Serializable - data class Pong(val timeStamp: Long) : ActionData() - @Serializable - data class Bolus(val insulin: Double, val carbs: Int) : ActionData() - @Serializable - data class ProfileSwitch(val timeShift: Int, val percentage: Int) : ActionData() - - @Serializable - data class OpenProfileSwitch(val timeShift: Int, val percentage: Int) : ActionData() - - // Mobile -> Wear - @Serializable - data class Ping(val timeStamp: Long) : ActionData() - @Serializable - data class ConfirmAction(val title: String, val message: String, val originalCommand: ActionData) : ActionData() - @Serializable - data class ChangeAction(val title: String, val message: String, val originalCommand: ActionData) : ActionData() -} \ No newline at end of file diff --git a/shared/src/main/java/info/nightscout/shared/weardata/EventData.kt b/shared/src/main/java/info/nightscout/shared/weardata/EventData.kt new file mode 100644 index 0000000000..45db4cd8fc --- /dev/null +++ b/shared/src/main/java/info/nightscout/shared/weardata/EventData.kt @@ -0,0 +1,237 @@ +package info.nightscout.shared.weardata + +import info.nightscout.androidaps.events.Event +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.Json +import java.util.* + +@Serializable +sealed class EventData : Event() { + + var sourceNodeId = "" + + fun serialize() = Json.encodeToString(serializer(), this) + + companion object { + + fun deserialize(json: String) = Json.decodeFromString(serializer(), json) + } + + // Mobile <- Wear + @Serializable + data class ActionPong(val timeStamp: Long) : EventData() + + @Serializable + data class Error(val timeStamp: Long) : EventData() // ignored + + @Serializable + data class CancelBolus(val timeStamp: Long) : EventData() + + @Serializable + data class ActionResendData(val from: String) : EventData() + + @Serializable + data class ActionPumpStatus(val timeStamp: Long) : EventData() + + @Serializable + data class ActionLoopStatus(val timeStamp: Long) : EventData() + + @Serializable + data class ActionTddStatus(val timeStamp: Long) : EventData() + + @Serializable + data class ActionECarbsPreCheck(val carbs: Int, val carbsTimeShift: Int, val duration: Int) : EventData() + + @Serializable + data class ActionBolusPreCheck(val insulin: Double, val carbs: Int) : EventData() + + @Serializable + data class ActionFillPreCheck(val insulin: Double) : EventData() + + @Serializable + data class ActionFillPresetPreCheck(val button: Int) : EventData() + + @Serializable + data class ActionProfileSwitchSendInitialData(val timeStamp: Long) : EventData() + + @Serializable + data class ActionProfileSwitchPreCheck(val timeShift: Int, val percentage: Int) : EventData() + + @Serializable + data class ActionWizardPreCheck(val carbs: Int, val percentage: Int) : EventData() + + @Serializable + data class ActionQuickWizardPreCheck(val guid: String) : EventData() + + @Serializable + data class ActionTempTargetPreCheck( + val command: TempTargetCommand, + val isMgdl: Boolean = true, val duration: Int = 0, val low: Double = 0.0, val high: Double = 0.0 // manual + ) : EventData() { + + @Serializable + enum class TempTargetCommand { + + PRESET_ACTIVITY, PRESET_HYPO, PRESET_EATING, CANCEL, MANUAL + } + + } + + // Mobile <- Wear return + + @Serializable + data class ActionWizardConfirmed(val timeStamp: Long) : EventData() + + @Serializable + data class ActionTempTargetConfirmed(val isMgdl: Boolean = true, val duration: Int = 0, val low: Double = 0.0, val high: Double = 0.0) : EventData() + + @Serializable + data class ActionBolusConfirmed(val insulin: Double, val carbs: Int) : EventData() + + @Serializable + data class ActionECarbsConfirmed(val carbs: Int, val carbsTime: Long, val duration: Int) : EventData() + + @Serializable + data class ActionFillConfirmed(val insulin: Double) : EventData() + + @Serializable + data class ActionProfileSwitchConfirmed(val timeShift: Int, val percentage: Int) : EventData() + + @Serializable + data class OpenLoopRequestConfirmed(val timeStamp: Long) : EventData() + + // Mobile -> Wear + @Serializable + data class CancelNotification(val timeStamp: Long) : EventData() + + @Serializable + data class ActionPing(val timeStamp: Long) : EventData() + + @Serializable + data class OpenSettings(val timeStamp: Long) : EventData() + + @Serializable + data class BolusProgress(val percent: Int, val status: String) : EventData() + + @Serializable + data class SingleBg @JvmOverloads constructor( + var timeStamp: Long, + val sgvString: String = "---", + val glucoseUnits: String = "-", + val slopeArrow: String = "--", + val delta: String = "--", + val avgDelta: String = "--", + val sgvLevel: Long = 0, + val sgv: Double, + val high: Double, // highLine + val low: Double, // lowLine + val color: Int = 0 + ) : EventData(), Comparable { + + override fun equals(other: Any?): Boolean = + when { + other !is SingleBg -> false + color != other.color -> false + else -> timeStamp == other.timeStamp + } + + override fun hashCode(): Int { + return Objects.hash(timeStamp, color) + } + + override fun compareTo(other: SingleBg): Int { + // reverse order endTime get latest first + if (this.timeStamp < other.timeStamp) return 1 + return if (this.timeStamp > other.timeStamp) -1 else 0 + } + } + + @Serializable + data class GraphData( + val entries: ArrayList + ) : EventData() + + @Serializable + data class TreatmentData( + val temps: ArrayList, + val basals: ArrayList, + val boluses: ArrayList, + val predictions: ArrayList + ) : EventData() { + + @Serializable + data class TempBasal( + val startTime: Long, + val startBasal: Double, + val endTime: Long, + val endBasal: Double, + val amount: Double + ) + + @Serializable + data class Basal( + val startTime: Long, + val endTime: Long, + val amount: Double + ) + + @Serializable + data class Treatment( + val date: Long, + val bolus: Double, + val carbs: Double, + val isSMB: Boolean, + val isValid: Boolean, + ) + } + + @Serializable + data class Status( + val externalStatus: String, + val iobSum: String, + val iobDetail: String, + val detailedIob: Boolean, + val cob: String, + val currentBasal: String, + val battery: String, + val rigBattery: String, + val openApsStatus: Long, + val bgi: String, + val showBgi: Boolean, + val batteryLevel: Int + ) : EventData() + + @Serializable + data class Preferences( + val timeStamp: Long, + val wearControl: Boolean, + val unitsMgdl: Boolean, + val bolusPercentage: Int, + val maxCarbs: Int, + val maxBolus: Double + ) : EventData() + + @Serializable + data class QuickWizard( + val entries: ArrayList + ) : EventData() { + + @Serializable + data class QuickWizardEntry( + val guid: String, + val buttonText: String, + val carbs: Int, + val validFrom: Int, + val validTo: Int + ) : EventData() + } + + @Serializable + data class ActionProfileSwitchOpenActivity(val timeShift: Int, val percentage: Int) : EventData() + + @Serializable + data class OpenLoopRequest(val title: String, val message: String, val returnCommand: EventData?) : EventData() + + @Serializable // returnCommand is sent back to Mobile after confirmation + data class ConfirmAction(val title: String, val message: String, val returnCommand: EventData?) : EventData() +} \ No newline at end of file diff --git a/shared/src/main/java/info/nightscout/shared/weardata/WearConstants.kt b/shared/src/main/java/info/nightscout/shared/weardata/WearConstants.kt deleted file mode 100644 index 7d5558d5f8..0000000000 --- a/shared/src/main/java/info/nightscout/shared/weardata/WearConstants.kt +++ /dev/null @@ -1,61 +0,0 @@ -package info.nightscout.shared.weardata - -import android.content.Context -import info.nightscout.shared.R -import javax.inject.Inject -import javax.inject.Singleton - -@Suppress("PropertyName") -@Singleton -class WearConstants @Inject constructor(private val context: Context) { - - // Paths must be defined in manifest - // mobile -> wear (data) - val M_W_DATA get() = context.getString(R.string.path_watch_data) - val M_W_STATUS get() = context.getString(R.string.path_status) - val M_W_PREFERENCES get() = context.getString(R.string.path_preferences) - val M_W_QUICK_WIZARD get() = context.getString(R.string.path_quick_wizard) - val M_W_BASAL get() = context.getString(R.string.path_basal) - val M_W_BOLUS_PROGRESS get() = context.getString(R.string.path_bolus_progress) - val M_W_ACTION_CONFIRMATION_REQUEST get() = context.getString(R.string.path_action_confirmation) - val M_W_ACTION_CHANGE_CONFIRMATION_REQUEST get() = context.getString(R.string.path_change_confirmation_request) - val M_W_ACTION_CANCEL_NOTIFICATION_REQUEST get() = context.getString(R.string.path_cancel_notification_request) - - // mobile -> wear (message) - val M_W_OPEN_SETTINGS get() = context.getString(R.string.path_open_wear_setting) - val M_W_PING get() = context.getString(R.string.path_ping) - - // wear -> mobile (message) - val W_M_RESEND_DATA get() = context.getString(R.string.path_resend_data_request) - val W_M_CANCEL_BOLUS get() = context.getString(R.string.path_cancel_bolus_on_phone) - val W_M_CONFIRM_ACTION get() = context.getString(R.string.path_confirm_action) - val W_M_INITIATE_ACTION get() = context.getString(R.string.path_initiate_action_on_phone) - val W_M_PONG get() = context.getString(R.string.path_pong) - - companion object { - - // actions for WEAR_INITIATE_ACTION_ON_PHONE - // used by - // DataLayerListenerService::initiateAction - // ActionStringHandler::handleInitiateActionOnPhone - // EventWearInitiateAction - const val ACTION_FILL_PRESET = "fillpreset" - const val ACTION_FILL = "fill" - const val ACTION_BOLUS = "bolus" - const val ACTION_TEMPORARY_TARGET = "temptarget" - const val ACTION_STATUS = "status" - const val ACTION_WIZARD = "wizard" - const val ACTION_WIZARD2 = "wizard2" - const val ACTION_QUICK_WIZARD = "quick_wizard" - const val ACTION_OPEN_CPP = "opencpp" - const val ACTION_CPP_SET = "cppset" - const val ACTION_TDD_STATS = "tddstats" - const val ACTION_E_CARBS = "ecarbs" - const val ACTION_CHANGE_REQUEST = "changeRequest" - const val ACTION_CANCEL_CHANGE_REQUEST = "cancelChangeRequest" - const val ACTION_DISMISS_OVERVIEW_NOTIF = "dismissoverviewnotification" - - //data keys - const val KEY_ACTION_DATA = "actionData" - } -} diff --git a/shared/src/main/res/values/wear_paths.xml b/shared/src/main/res/values/wear_paths.xml index 1bf7203905..36d7325849 100644 --- a/shared/src/main/res/values/wear_paths.xml +++ b/shared/src/main/res/values/wear_paths.xml @@ -1,19 +1,4 @@ - /ping - /pong - /openwearsettings - /nightscout_watch_data_resend - /nightscout_watch_initiateactionstring - /nightscout_watch_cancel_bolus - /nightscout_watch_confirmactionstring - /nightscout_watch_data - /sendstatustowear - /sendpreferencestowear - /send_quick_wizard - /nightscout_watch_basal - /nightscout_watch_bolusprogress - /nightscout_watch_actionconfirmationrequest - /nightscout_watch_changeconfirmationrequest - /nightscout_watch_cancelnotificationrequest + /rx_bridge \ No newline at end of file diff --git a/wear/src/main/AndroidManifest.xml b/wear/src/main/AndroidManifest.xml index 95ff23dc3f..cd0fd89207 100644 --- a/wear/src/main/AndroidManifest.xml +++ b/wear/src/main/AndroidManifest.xml @@ -241,7 +241,7 @@ - @@ -252,42 +252,6 @@ - - - - - - - - - @@ -299,15 +263,7 @@ - - @@ -466,7 +422,7 @@ diff --git a/wear/src/main/java/info/nightscout/androidaps/Aaps.kt b/wear/src/main/java/info/nightscout/androidaps/Aaps.kt index 565ddf002e..ef5ca19d11 100644 --- a/wear/src/main/java/info/nightscout/androidaps/Aaps.kt +++ b/wear/src/main/java/info/nightscout/androidaps/Aaps.kt @@ -7,7 +7,11 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.preference.PreferenceManager import dagger.android.AndroidInjector import dagger.android.DaggerApplication +import info.nightscout.androidaps.comm.DataHandlerWear +import info.nightscout.androidaps.comm.DataLayerListenerServiceWear import info.nightscout.androidaps.di.DaggerWearComponent +import info.nightscout.androidaps.events.EventWearPreferenceChange +import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.logging.LTag import javax.inject.Inject @@ -15,11 +19,15 @@ import javax.inject.Inject class Aaps : DaggerApplication(), OnSharedPreferenceChangeListener { @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var rxBus: RxBus + @Inject lateinit var dataHandlerWear: DataHandlerWear // instantiate only override fun onCreate() { super.onCreate() aapsLogger.debug(LTag.WEAR, "onCreate") PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this) + startService(Intent(this, DataLayerListenerServiceWear::class.java)) + } override fun applicationInjector(): AndroidInjector = @@ -30,6 +38,7 @@ class Aaps : DaggerApplication(), OnSharedPreferenceChangeListener { override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { // we trigger update on Complications - LocalBroadcastManager.getInstance(this).sendBroadcast(Intent(Intent.ACTION_SEND)) + LocalBroadcastManager.getInstance(this).sendBroadcast(Intent(DataLayerListenerServiceWear.INTENT_NEW_DATA)) + rxBus.send(EventWearPreferenceChange(key)) } } \ No newline at end of file diff --git a/wear/src/main/java/info/nightscout/androidaps/comm/DataHandlerWear.kt b/wear/src/main/java/info/nightscout/androidaps/comm/DataHandlerWear.kt new file mode 100644 index 0000000000..67a4b7afe4 --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/comm/DataHandlerWear.kt @@ -0,0 +1,301 @@ +package info.nightscout.androidaps.comm + +import android.annotation.TargetApi +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.os.Build +import android.os.Bundle +import android.os.SystemClock +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import androidx.localbroadcastmanager.content.LocalBroadcastManager +import androidx.wear.tiles.TileService +import com.google.android.gms.wearable.WearableListenerService +import info.nightscout.androidaps.R +import info.nightscout.androidaps.events.EventWearToMobile +import info.nightscout.androidaps.interaction.AAPSPreferences +import info.nightscout.androidaps.interaction.actions.AcceptActivity +import info.nightscout.androidaps.interaction.actions.CPPActivity +import info.nightscout.androidaps.interaction.utils.Persistence +import info.nightscout.androidaps.plugins.bus.RxBus +import info.nightscout.androidaps.tile.ActionsTileService +import info.nightscout.androidaps.tile.QuickWizardTileService +import info.nightscout.androidaps.tile.TempTargetTileService +import info.nightscout.androidaps.utils.rx.AapsSchedulers +import info.nightscout.shared.logging.AAPSLogger +import info.nightscout.shared.logging.LTag +import info.nightscout.shared.sharedPreferences.SP +import info.nightscout.shared.weardata.EventData +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class DataHandlerWear @Inject constructor( + private val context: Context, + private val rxBus: RxBus, + private val aapsSchedulers: AapsSchedulers, + private val sp: SP, + private val aapsLogger: AAPSLogger, + private val persistence: Persistence +) { + + private val disposable = CompositeDisposable() + + init { + setupBus() + } + + private fun setupBus() { + disposable += rxBus + .toObservable(EventData.ActionPing::class.java) + .observeOn(aapsSchedulers.io) + .subscribe { + aapsLogger.debug(LTag.WEAR, "Ping received from ${it.sourceNodeId}") + rxBus.send(EventWearToMobile(EventData.ActionPong(System.currentTimeMillis()))) + } + disposable += rxBus + .toObservable(EventData.ConfirmAction::class.java) + .observeOn(aapsSchedulers.io) + .subscribe { + aapsLogger.debug(LTag.WEAR, "ConfirmAction received from ${it.sourceNodeId}") + context.startActivity(Intent(context, AcceptActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + putExtras( + Bundle().also { bundle -> + bundle.putString("title", it.title) + bundle.putString("message", it.message) + bundle.putString(DataLayerListenerServiceWear.KEY_ACTION_DATA, it.returnCommand?.serialize()) + } + ) + }) + } + disposable += rxBus + .toObservable(EventData.CancelNotification::class.java) + .observeOn(aapsSchedulers.io) + .subscribe { + aapsLogger.debug(LTag.WEAR, "ActionCancelNotification received from ${it.sourceNodeId}") + (context.getSystemService(WearableListenerService.NOTIFICATION_SERVICE) as NotificationManager).cancel(DataLayerListenerServiceWear.CHANGE_NOTIF_ID) + } + disposable += rxBus + .toObservable(EventData.OpenLoopRequest::class.java) + .observeOn(aapsSchedulers.io) + .subscribe { + aapsLogger.debug(LTag.WEAR, "OpenLoopRequest received from ${it.sourceNodeId}") + handleOpenLoopRequest(it) + } + disposable += rxBus + .toObservable(EventData.OpenSettings::class.java) + .observeOn(aapsSchedulers.io) + .subscribe { + aapsLogger.debug(LTag.WEAR, "ActionOpenSettings received from ${it.sourceNodeId}") + context.startActivity(Intent(context, AAPSPreferences::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) }) + } + disposable += rxBus + .toObservable(EventData.ActionProfileSwitchOpenActivity::class.java) + .observeOn(aapsSchedulers.io) + .subscribe { event -> + aapsLogger.debug(LTag.WEAR, "ActionProfileSwitchOpenActivity received from ${event.sourceNodeId}") + context.startActivity(Intent(context, CPPActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + putExtras(Bundle().also { bundle -> + bundle.putInt("percentage", event.percentage) + bundle.putInt("timeshift", event.timeShift) + }) + }) + } + disposable += rxBus + .toObservable(EventData.BolusProgress::class.java) + .observeOn(aapsSchedulers.io) + .subscribe { + aapsLogger.debug(LTag.WEAR, "Bolus progress received from ${it.sourceNodeId}") + handleBolusProgress(it) + rxBus.send(EventWearToMobile(EventData.ActionPong(System.currentTimeMillis()))) + } + disposable += rxBus + .toObservable(EventData.Status::class.java) + .observeOn(aapsSchedulers.io) + .subscribe { + aapsLogger.debug(LTag.WEAR, "Status received from ${it.sourceNodeId}") + persistence.store(it) + LocalBroadcastManager.getInstance(context).sendBroadcast( + Intent(DataLayerListenerServiceWear.INTENT_NEW_DATA).apply { + putExtra(DataLayerListenerServiceWear.KEY_STATUS_DATA, it.serialize()) + } + ) + } + disposable += rxBus + .toObservable(EventData.SingleBg::class.java) + .observeOn(aapsSchedulers.io) + .subscribe { + aapsLogger.debug(LTag.WEAR, "SingleBg received from ${it.sourceNodeId}") + persistence.store(it) + LocalBroadcastManager.getInstance(context).sendBroadcast( + Intent(DataLayerListenerServiceWear.INTENT_NEW_DATA).apply { + putExtra(DataLayerListenerServiceWear.KEY_SINGLE_BG_DATA, it.serialize()) + } + ) + } + disposable += rxBus + .toObservable(EventData.GraphData::class.java) + .observeOn(aapsSchedulers.io) + .subscribe { + aapsLogger.debug(LTag.WEAR, "GraphData received from ${it.sourceNodeId}") + persistence.store(it) + LocalBroadcastManager.getInstance(context).sendBroadcast( + Intent(DataLayerListenerServiceWear.INTENT_NEW_DATA).apply { + putExtra(DataLayerListenerServiceWear.KEY_GRAPH_DATA, it.serialize()) + } + ) + } + disposable += rxBus + .toObservable(EventData.TreatmentData::class.java) + .observeOn(aapsSchedulers.io) + .subscribe { + aapsLogger.debug(LTag.WEAR, "TreatmentData received from ${it.sourceNodeId}") + persistence.store(it) + LocalBroadcastManager.getInstance(context).sendBroadcast( + Intent(DataLayerListenerServiceWear.INTENT_NEW_DATA).apply { + putExtra(DataLayerListenerServiceWear.KEY_TREATMENTS_DATA, it.serialize()) + } + ) + } + disposable += rxBus + .toObservable(EventData.Preferences::class.java) + .observeOn(aapsSchedulers.io) + .subscribe { + aapsLogger.debug(LTag.WEAR, "Preferences received from ${it.sourceNodeId}") + if (it.wearControl != sp.getBoolean(R.string.key_wear_control, false)) { + sp.putBoolean(R.string.key_wear_control, it.wearControl) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + TileService.getUpdater(context).requestUpdate(ActionsTileService::class.java) + TileService.getUpdater(context).requestUpdate(TempTargetTileService::class.java) + TileService.getUpdater(context).requestUpdate(QuickWizardTileService::class.java) + } + } + sp.putBoolean(R.string.key_units_mgdl, it.unitsMgdl) + sp.putInt(R.string.key_boluswizard_percentage, it.bolusPercentage) + sp.putInt(R.string.key_treatmentssafety_maxcarbs, it.maxCarbs) + sp.putDouble(R.string.key_treatmentssafety_maxbolus, it.maxBolus) + } + disposable += rxBus + .toObservable(EventData.QuickWizard::class.java) + .observeOn(aapsSchedulers.io) + .subscribe { + aapsLogger.debug(LTag.WEAR, "QuickWizard received from ${it.sourceNodeId}") + val serialized = it.serialize() + if (serialized != sp.getString(R.string.key_quick_wizard_data, "")) { + sp.putString(R.string.key_quick_wizard_data, serialized) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + TileService.getUpdater(context).requestUpdate(QuickWizardTileService::class.java) + } + } + } + + private fun handleBolusProgress(bolusProgress: EventData.BolusProgress) { + val vibratePattern: LongArray + val vibrate = sp.getBoolean("vibrateOnBolus", true) + vibratePattern = if (vibrate) longArrayOf(0, 50, 1000) else longArrayOf(0, 1, 1000) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + createBolusProgressChannels() + } + val cancelIntent = Intent(context, DataLayerListenerServiceWear::class.java) + cancelIntent.action = DataLayerListenerServiceWear.INTENT_CANCEL_BOLUS + val cancelPendingIntent = PendingIntent.getService(context, 0, cancelIntent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) + val notificationBuilder: NotificationCompat.Builder = + NotificationCompat.Builder(context, if (vibrate) DataLayerListenerServiceWear.AAPS_NOTIFY_CHANNEL_ID_BOLUS_PROGRESS else DataLayerListenerServiceWear.AAPS_NOTIFY_CHANNEL_ID_BOLUS_PROGRESS_SILENT) + .setSmallIcon(R.drawable.ic_icon) + .setContentTitle(context.getString(R.string.bolus_progress)) + .setContentText("${bolusProgress.percent}% - ${bolusProgress.status}") + .setSubText(context.getString(R.string.press_to_cancel)) + .setContentIntent(cancelPendingIntent) + .setPriority(NotificationCompat.PRIORITY_MAX) + .setVibrate(vibratePattern) + .addAction(R.drawable.ic_cancel, context.getString(R.string.cancel_bolus), cancelPendingIntent) + val notificationManager = NotificationManagerCompat.from(context) + notificationManager.notify(DataLayerListenerServiceWear.BOLUS_PROGRESS_NOTIF_ID, notificationBuilder.build()) + notificationManager.cancel(DataLayerListenerServiceWear.CONFIRM_NOTIF_ID) // multiple watch setup + if (bolusProgress.percent == 100) { + scheduleDismissBolusProgress(5) + } + } + + @TargetApi(value = 26) private fun createBolusProgressChannels() { + createNotificationChannel( + longArrayOf(0, 50, 1000), + DataLayerListenerServiceWear.AAPS_NOTIFY_CHANNEL_ID_BOLUS_PROGRESS, + context.getString(R.string.bolus_progress_channel_name), + context.getString(R.string.bolus_progress_channel_description) + ) + createNotificationChannel( + longArrayOf(0, 1, 1000), + DataLayerListenerServiceWear.AAPS_NOTIFY_CHANNEL_ID_BOLUS_PROGRESS_SILENT, + context.getString(R.string.bolus_progress_silent_channel_name), + context.getString(R.string.bolus_progress_silent_channel_description) + ) + } + + @TargetApi(value = 26) private fun createNotificationChannel(vibratePattern: LongArray, channelID: String, name: CharSequence, description: String) { + val channel = NotificationChannel(channelID, name, NotificationManager.IMPORTANCE_HIGH) + channel.description = description + channel.enableVibration(true) + channel.vibrationPattern = vibratePattern + + // Register the channel with the system; you can't change the importance + // or other notification behaviors after this + context.getSystemService(NotificationManager::class.java) + .createNotificationChannel(channel) + } + + private fun handleOpenLoopRequest(command: EventData.OpenLoopRequest) { + // Create the NotificationChannel, but only on API 26+ because + // the NotificationChannel class is new and not in the support library + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val name: CharSequence = "AAPS Open Loop" + val description = "Open Loop request notification" + val channel = NotificationChannel(DataLayerListenerServiceWear.AAPS_NOTIFY_CHANNEL_ID_OPEN_LOOP, name, NotificationManager.IMPORTANCE_HIGH) + channel.description = description + channel.enableVibration(true) + + // Register the channel with the system; you can't change the importance + // or other notification behaviors after this + val notificationManager = context.getSystemService(NotificationManager::class.java) + notificationManager.createNotificationChannel(channel) + } + var builder = NotificationCompat.Builder(context, DataLayerListenerServiceWear.AAPS_NOTIFY_CHANNEL_ID_OPEN_LOOP) + builder = builder.setSmallIcon(R.drawable.notif_icon) + .setContentTitle(command.title) + .setContentText(command.message) + .setPriority(Notification.PRIORITY_HIGH) + .setVibrate(longArrayOf(1000, 1000, 1000, 1000, 1000)) + + // Creates an explicit intent for an Activity in your app + val intent = Intent(context, AcceptActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + putExtras(Bundle().also { bundle -> + bundle.putString("title", command.title) + bundle.putString("message", command.message) + bundle.putString(DataLayerListenerServiceWear.KEY_ACTION_DATA, command.returnCommand?.serialize()) + }) + } + val resultPendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) + builder = builder.setContentIntent(resultPendingIntent) + val mNotificationManager = context.getSystemService(WearableListenerService.NOTIFICATION_SERVICE) as NotificationManager + // mId allows you to update the notification later on. + mNotificationManager.notify(DataLayerListenerServiceWear.CHANGE_NOTIF_ID, builder.build()) + } + + @Suppress("SameParameterValue") + private fun scheduleDismissBolusProgress(seconds: Int) { + Thread { + SystemClock.sleep(seconds * 1000L) + NotificationManagerCompat.from(context).cancel(DataLayerListenerServiceWear.BOLUS_PROGRESS_NOTIF_ID) + }.start() + } +} \ No newline at end of file diff --git a/wear/src/main/java/info/nightscout/androidaps/comm/DataLayerListenerServiceWear.kt b/wear/src/main/java/info/nightscout/androidaps/comm/DataLayerListenerServiceWear.kt new file mode 100644 index 0000000000..3950cd0c10 --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/comm/DataLayerListenerServiceWear.kt @@ -0,0 +1,210 @@ +package info.nightscout.androidaps.comm + +import android.content.Intent +import android.os.Handler +import android.os.HandlerThread +import androidx.core.app.NotificationManagerCompat +import com.google.android.gms.tasks.Tasks +import com.google.android.gms.wearable.* +import dagger.android.AndroidInjection +import info.nightscout.androidaps.R +import info.nightscout.androidaps.events.EventWearToMobile +import info.nightscout.androidaps.interaction.utils.Persistence +import info.nightscout.androidaps.interaction.utils.WearUtil +import info.nightscout.androidaps.plugins.bus.RxBus +import info.nightscout.androidaps.utils.rx.AapsSchedulers +import info.nightscout.shared.logging.AAPSLogger +import info.nightscout.shared.logging.LTag +import info.nightscout.shared.sharedPreferences.SP +import info.nightscout.shared.weardata.EventData +import io.reactivex.rxjava3.disposables.CompositeDisposable +import io.reactivex.rxjava3.kotlin.plusAssign +import kotlinx.coroutines.* +import kotlinx.coroutines.tasks.await +import javax.inject.Inject + +class DataLayerListenerServiceWear : WearableListenerService() { + + @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var wearUtil: WearUtil + @Inject lateinit var persistence: Persistence + @Inject lateinit var sp: SP + @Inject lateinit var rxBus: RxBus + @Inject lateinit var aapsSchedulers: AapsSchedulers + + private val dataClient by lazy { Wearable.getDataClient(this) } + private val messageClient by lazy { Wearable.getMessageClient(this) } + private val capabilityClient by lazy { Wearable.getCapabilityClient(this) } + //private val nodeClient by lazy { Wearable.getNodeClient(this) } + + private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) + private var handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper) + + private val disposable = CompositeDisposable() + + private val rxPath get() = getString(R.string.path_rx_bridge) + + override fun onCreate() { + AndroidInjection.inject(this) + super.onCreate() + handler.post { updateTranscriptionCapability() } + disposable += rxBus + .toObservable(EventWearToMobile::class.java) + .observeOn(aapsSchedulers.io) + .subscribe { + sendMessage(rxPath, it.payload.serialize()) + } + } + + override fun onPeerConnected(p0: Node) { + super.onPeerConnected(p0) + } + + override fun onCapabilityChanged(p0: CapabilityInfo) { + super.onCapabilityChanged(p0) + handler.post { updateTranscriptionCapability() } + aapsLogger.debug(LTag.WEAR, "onCapabilityChanged: ${p0.name} ${p0.nodes.joinToString(", ") { it.displayName + "(" + it.id + ")" }}") + } + + override fun onDestroy() { + super.onDestroy() + scope.cancel() + disposable.clear() + } + + override fun onDataChanged(dataEvents: DataEventBuffer) { + //aapsLogger.debug(LTag.WEAR, "onDataChanged") + + dataEvents.forEach { event -> + if (event.type == DataEvent.TYPE_CHANGED) { + val path = event.dataItem.uri.path + + aapsLogger.debug(LTag.WEAR, "onDataChanged: Path: $path, EventDataItem=${event.dataItem}") + try { + @Suppress("ControlFlowWithEmptyBody", "UNUSED_EXPRESSION") + when (path) { + } + } catch (exception: Exception) { + aapsLogger.error(LTag.WEAR, "onDataChanged failed", exception) + } + } + } + super.onDataChanged(dataEvents) + } + + override fun onMessageReceived(messageEvent: MessageEvent) { + super.onMessageReceived(messageEvent) + + when (messageEvent.path) { + rxPath -> { + aapsLogger.debug(LTag.WEAR, "onMessageReceived: ${String(messageEvent.data)}") + val command = EventData.deserialize(String(messageEvent.data)) + rxBus.send(command.also { it.sourceNodeId = messageEvent.sourceNodeId }) + } + } + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + when (intent?.action) { + + INTENT_CANCEL_BOLUS -> { + //dismiss notification + NotificationManagerCompat.from(this).cancel(BOLUS_PROGRESS_NOTIF_ID) + //send cancel-request to phone. + rxBus.send(EventWearToMobile(EventData.CancelBolus(System.currentTimeMillis()))) + } + + } + return START_STICKY + } + + private var transcriptionNodeId: String? = null + + private fun updateTranscriptionCapability() { + val capabilityInfo: CapabilityInfo = Tasks.await( + capabilityClient.getCapability(PHONE_CAPABILITY, CapabilityClient.FILTER_REACHABLE) + ) + aapsLogger.debug(LTag.WEAR, "Nodes: ${capabilityInfo.nodes.joinToString(", ") { it.displayName + "(" + it.id + ")" }}") + transcriptionNodeId = pickBestNodeId(capabilityInfo.nodes) + aapsLogger.debug(LTag.WEAR, "Selected node: $transcriptionNodeId") + } + + // Find a nearby node or pick one arbitrarily + private fun pickBestNodeId(nodes: Set): String? = + nodes.firstOrNull { it.isNearby }?.id ?: nodes.firstOrNull()?.id + + @Suppress("unused") + private fun sendData(path: String, vararg params: DataMap) { + scope.launch { + try { + for (dm in params) { + val request = PutDataMapRequest.create(path).apply { + dataMap.putAll(dm) + } + .asPutDataRequest() + .setUrgent() + + val result = dataClient.putDataItem(request).await() + aapsLogger.debug(LTag.WEAR, "sendData: ${result.uri} ${params.joinToString()}") + } + } catch (cancellationException: CancellationException) { + throw cancellationException + } catch (exception: Exception) { + aapsLogger.error(LTag.WEAR, "DataItem failed: $exception") + } + } + } + + private fun sendMessage(path: String, data: String?) { + aapsLogger.debug(LTag.WEAR, "sendMessage: $path $data") + transcriptionNodeId?.also { nodeId -> + messageClient + .sendMessage(nodeId, path, data?.toByteArray() ?: byteArrayOf()).apply { + addOnSuccessListener { } + addOnFailureListener { + aapsLogger.debug(LTag.WEAR, "sendMessage: $path failure") + } + } + } + } + + @Suppress("unused") + private fun sendMessage(path: String, data: ByteArray) { + aapsLogger.debug(LTag.WEAR, "sendMessage: $path") + transcriptionNodeId?.also { nodeId -> + messageClient + .sendMessage(nodeId, path, data).apply { + addOnSuccessListener { } + addOnFailureListener { + aapsLogger.debug(LTag.WEAR, "sendMessage: $path failure") + } + } + } + } + + companion object { + + const val PHONE_CAPABILITY = "androidaps_mobile" + + // Accepted intents + val INTENT_CANCEL_BOLUS = DataLayerListenerServiceWear::class.java.name + ".CancelBolus" + val INTENT_NEW_DATA = DataLayerListenerServiceWear::class.java.name + ".NewData" + + //data keys + const val KEY_ACTION_DATA = "actionData" + const val KEY_ACTION = "action" + const val KEY_MESSAGE = "message" + const val KEY_SINGLE_BG_DATA = "single_bg_data" + const val KEY_TREATMENTS_DATA = "treatments_data" + const val KEY_GRAPH_DATA = "graph_data" + const val KEY_STATUS_DATA = "status_data" + + const val BOLUS_PROGRESS_NOTIF_ID = 1 + const val CONFIRM_NOTIF_ID = 2 + const val CHANGE_NOTIF_ID = 556677 + + const val AAPS_NOTIFY_CHANNEL_ID_OPEN_LOOP = "AndroidAPS-OpenLoop" + const val AAPS_NOTIFY_CHANNEL_ID_BOLUS_PROGRESS = "bolus progress vibration" + const val AAPS_NOTIFY_CHANNEL_ID_BOLUS_PROGRESS_SILENT = "bolus progress silent" + } +} \ No newline at end of file diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/BaseComplicationProviderService.java b/wear/src/main/java/info/nightscout/androidaps/complications/BaseComplicationProviderService.java index 7d65be3080..9593474390 100644 --- a/wear/src/main/java/info/nightscout/androidaps/complications/BaseComplicationProviderService.java +++ b/wear/src/main/java/info/nightscout/androidaps/complications/BaseComplicationProviderService.java @@ -22,15 +22,18 @@ import javax.inject.Inject; import dagger.android.AndroidInjection; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.DataLayerListenerService; +import info.nightscout.androidaps.comm.DataLayerListenerServiceWear; import info.nightscout.androidaps.data.RawDisplayData; +import info.nightscout.androidaps.events.EventWearToMobile; import info.nightscout.androidaps.interaction.utils.Constants; import info.nightscout.androidaps.interaction.utils.DisplayFormat; import info.nightscout.androidaps.interaction.utils.Inevitable; import info.nightscout.androidaps.interaction.utils.Persistence; import info.nightscout.androidaps.interaction.utils.WearUtil; +import info.nightscout.androidaps.plugins.bus.RxBus; import info.nightscout.shared.logging.AAPSLogger; import info.nightscout.shared.logging.LTag; +import info.nightscout.shared.weardata.EventData; /** * Base class for all complications @@ -44,6 +47,7 @@ public abstract class BaseComplicationProviderService extends ComplicationProvid @Inject DisplayFormat displayFormat; @Inject Persistence persistence; @Inject AAPSLogger aapsLogger; + @Inject RxBus rxBus; // Not derived from DaggerService, do injection here @Override @@ -199,13 +203,13 @@ public abstract class BaseComplicationProviderService extends ComplicationProvid persistence.putBoolean("complication_" + complicationId + "_since", usesSinceField()); persistence.addToSet(KEY_COMPLICATIONS, "complication_" + complicationId); - IntentFilter messageFilter = new IntentFilter(Intent.ACTION_SEND); + IntentFilter messageFilter = new IntentFilter(DataLayerListenerServiceWear.Companion.getINTENT_NEW_DATA()); messageReceiver = new MessageReceiver(); localBroadcastManager = LocalBroadcastManager.getInstance(this); localBroadcastManager.registerReceiver(messageReceiver, messageFilter); - DataLayerListenerService.Companion.requestData(this); + rxBus.send(new EventWearToMobile(new EventData.ActionResendData("BaseComplicationProviderService"))); checkIfUpdateNeeded(); } @@ -232,12 +236,13 @@ public abstract class BaseComplicationProviderService extends ComplicationProvid ComplicationTapBroadcastReceiver.getTapActionIntent( getApplicationContext(), thisProvider, complicationId, getComplicationAction()); - final RawDisplayData raw = new RawDisplayData(wearUtil); + final RawDisplayData raw = new RawDisplayData(); raw.updateForComplicationsFromPersistence(persistence); aapsLogger.warn(LTag.WEAR, "Complication data: " + raw.toDebugString()); // store what is currently rendered in 'SGV since' field, to detect if it was changed and need update - persistence.putString(KEY_LAST_SHOWN_SINCE_VALUE, displayFormat.shortTimeSince(raw.datetime)); + persistence.putString(KEY_LAST_SHOWN_SINCE_VALUE, + displayFormat.shortTimeSince(raw.getSingleBg().getTimeStamp())); // by each render we clear stale flag to ensure it is re-rendered at next refresh detection round persistence.putBoolean(KEY_STALE_REPORTED, false); @@ -249,11 +254,11 @@ public abstract class BaseComplicationProviderService extends ComplicationProvid final PendingIntent infoToast = ComplicationTapBroadcastReceiver.getTapWarningSinceIntent( getApplicationContext(), thisProvider, complicationId, ComplicationAction.WARNING_SYNC, persistence.whenDataUpdated()); complicationData = buildNoSyncComplicationData(dataType, raw, complicationPendingIntent, infoToast, persistence.whenDataUpdated()); - } else if (wearUtil.msSince(raw.datetime) > Constants.STALE_MS) { + } else if (wearUtil.msSince(raw.getSingleBg().getTimeStamp()) > Constants.STALE_MS) { // data arriving from phone AAPS, but it is outdated (uploader/NS/xDrip/Sensor error) final PendingIntent infoToast = ComplicationTapBroadcastReceiver.getTapWarningSinceIntent( - getApplicationContext(), thisProvider, complicationId, ComplicationAction.WARNING_OLD, raw.datetime); - complicationData = buildOutdatedComplicationData(dataType, raw, complicationPendingIntent, infoToast, raw.datetime); + getApplicationContext(), thisProvider, complicationId, ComplicationAction.WARNING_OLD, raw.getSingleBg().getTimeStamp()); + complicationData = buildOutdatedComplicationData(dataType, raw, complicationPendingIntent, infoToast, raw.getSingleBg().getTimeStamp()); } else { // data is up-to-date, we can render standard complication complicationData = buildComplicationData(dataType, raw, complicationPendingIntent); @@ -310,13 +315,13 @@ public abstract class BaseComplicationProviderService extends ComplicationProvid * is up-to-date or need to be changed (a minute or more elapsed) */ private void requestUpdateIfSinceChanged() { - final RawDisplayData raw = new RawDisplayData(wearUtil); + final RawDisplayData raw = new RawDisplayData(); raw.updateForComplicationsFromPersistence(persistence); final String lastSince = persistence.getString(KEY_LAST_SHOWN_SINCE_VALUE, "-"); - final String calcSince = displayFormat.shortTimeSince(raw.datetime); + final String calcSince = displayFormat.shortTimeSince(raw.getSingleBg().getTimeStamp()); final boolean isStale = (wearUtil.msSince(persistence.whenDataUpdated()) > Constants.STALE_MS) - || (wearUtil.msSince(raw.datetime) > Constants.STALE_MS); + || (wearUtil.msSince(raw.getSingleBg().getTimeStamp()) > Constants.STALE_MS); final boolean staleWasRefreshed = persistence.getBoolean(KEY_STALE_REPORTED, false); final boolean sinceWasChanged = !lastSince.equals(calcSince); diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/BrCobIobComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/BrCobIobComplication.java index 10295844c4..bdbc079793 100644 --- a/wear/src/main/java/info/nightscout/androidaps/complications/BrCobIobComplication.java +++ b/wear/src/main/java/info/nightscout/androidaps/complications/BrCobIobComplication.java @@ -3,7 +3,6 @@ package info.nightscout.androidaps.complications; import android.app.PendingIntent; import android.support.wearable.complications.ComplicationData; import android.support.wearable.complications.ComplicationText; -import android.util.Log; import javax.inject.Inject; @@ -32,11 +31,11 @@ public class BrCobIobComplication extends BaseComplicationProviderService { ComplicationData complicationData = null; if (dataType == ComplicationData.TYPE_SHORT_TEXT) { - final String cob = new SmallestDoubleString(raw.sCOB2, SmallestDoubleString.Units.USE).minimise(displayFormat.MIN_FIELD_LEN_COB); - final String iob = new SmallestDoubleString(raw.sIOB1, SmallestDoubleString.Units.USE).minimise(Math.max(displayFormat.MIN_FIELD_LEN_IOB, (displayFormat.MAX_FIELD_LEN_SHORT - 1) - cob.length())); + final String cob = new SmallestDoubleString(raw.getStatus().getCob(), SmallestDoubleString.Units.USE).minimise(displayFormat.MIN_FIELD_LEN_COB); + final String iob = new SmallestDoubleString(raw.getStatus().getIobSum(), SmallestDoubleString.Units.USE).minimise(Math.max(displayFormat.MIN_FIELD_LEN_IOB, (displayFormat.MAX_FIELD_LEN_SHORT - 1) - cob.length())); final ComplicationData.Builder builder = new ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT) - .setShortText(ComplicationText.plainText(displayFormat.basalRateSymbol() + raw.sBasalRate)) + .setShortText(ComplicationText.plainText(displayFormat.basalRateSymbol() + raw.getStatus().getCurrentBasal())) .setShortTitle(ComplicationText.plainText(cob + " " + iob)) .setTapAction(complicationPendingIntent); diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/CobIconComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/CobIconComplication.java index 98ffdb1e56..c679635e4c 100644 --- a/wear/src/main/java/info/nightscout/androidaps/complications/CobIconComplication.java +++ b/wear/src/main/java/info/nightscout/androidaps/complications/CobIconComplication.java @@ -20,7 +20,7 @@ public class CobIconComplication extends BaseComplicationProviderService { if (dataType == ComplicationData.TYPE_SHORT_TEXT) { final ComplicationData.Builder builder = new ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT) - .setShortText(ComplicationText.plainText(raw.sCOB2)) + .setShortText(ComplicationText.plainText(raw.getStatus().getCob())) .setIcon( Icon.createWithResource( this, R.drawable.ic_carbs)) diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/CobIobComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/CobIobComplication.java index 62109b1dfc..411b1e943b 100644 --- a/wear/src/main/java/info/nightscout/androidaps/complications/CobIobComplication.java +++ b/wear/src/main/java/info/nightscout/androidaps/complications/CobIobComplication.java @@ -18,8 +18,8 @@ public class CobIobComplication extends BaseComplicationProviderService { ComplicationData complicationData = null; if (dataType == ComplicationData.TYPE_SHORT_TEXT) { - final String cob = raw.sCOB2; - final String iob = new SmallestDoubleString(raw.sIOB1, SmallestDoubleString.Units.USE).minimise(displayFormat.MAX_FIELD_LEN_SHORT); + final String cob = raw.getStatus().getCob(); + final String iob = new SmallestDoubleString(raw.getStatus().getIobSum(), SmallestDoubleString.Units.USE).minimise(displayFormat.MAX_FIELD_LEN_SHORT); final ComplicationData.Builder builder = new ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT) .setShortText(ComplicationText.plainText(cob)) diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/IobIconComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/IobIconComplication.java index ce76524fb9..632ac70c05 100644 --- a/wear/src/main/java/info/nightscout/androidaps/complications/IobIconComplication.java +++ b/wear/src/main/java/info/nightscout/androidaps/complications/IobIconComplication.java @@ -20,7 +20,7 @@ public class IobIconComplication extends BaseComplicationProviderService { ComplicationData complicationData = null; if (dataType == ComplicationData.TYPE_SHORT_TEXT) { - final String iob = new SmallestDoubleString(raw.sIOB1, SmallestDoubleString.Units.USE).minimise(displayFormat.MAX_FIELD_LEN_SHORT); + final String iob = new SmallestDoubleString(raw.getStatus().getIobSum(), SmallestDoubleString.Units.USE).minimise(displayFormat.MAX_FIELD_LEN_SHORT); final ComplicationData.Builder builder = new ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT) .setShortText(ComplicationText.plainText(iob)) diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/SgvComplication.java b/wear/src/main/java/info/nightscout/androidaps/complications/SgvComplication.java index d09a58a7ea..62d08ad8ba 100644 --- a/wear/src/main/java/info/nightscout/androidaps/complications/SgvComplication.java +++ b/wear/src/main/java/info/nightscout/androidaps/complications/SgvComplication.java @@ -32,7 +32,8 @@ public class SgvComplication extends BaseComplicationProviderService { switch (dataType) { case ComplicationData.TYPE_SHORT_TEXT: final ComplicationData.Builder builder = new ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT) - .setShortText(ComplicationText.plainText(raw.sSgv + raw.sDirection + "\uFE0E")) + .setShortText(ComplicationText.plainText(raw.getSingleBg().getSgvString() + raw.getSingleBg().getSlopeArrow() + + "\uFE0E")) .setShortTitle(ComplicationText.plainText(displayFormat.shortTrend(raw))) .setTapAction(complicationPendingIntent); diff --git a/wear/src/main/java/info/nightscout/androidaps/complications/UploaderBattery.java b/wear/src/main/java/info/nightscout/androidaps/complications/UploaderBatteryComplication.java similarity index 95% rename from wear/src/main/java/info/nightscout/androidaps/complications/UploaderBattery.java rename to wear/src/main/java/info/nightscout/androidaps/complications/UploaderBatteryComplication.java index 10f214ee33..6211641bb8 100644 --- a/wear/src/main/java/info/nightscout/androidaps/complications/UploaderBattery.java +++ b/wear/src/main/java/info/nightscout/androidaps/complications/UploaderBatteryComplication.java @@ -14,7 +14,7 @@ import info.nightscout.shared.logging.LTag; /* * Created by dlvoy on 2019-11-12 */ -public class UploaderBattery extends BaseComplicationProviderService { +public class UploaderBatteryComplication extends BaseComplicationProviderService { public ComplicationData buildComplicationData(int dataType, RawDisplayData raw, PendingIntent complicationPendingIntent) { @@ -25,9 +25,9 @@ public class UploaderBattery extends BaseComplicationProviderService { int level = 0; String levelStr = "???"; - if (raw.sUploaderBattery.matches("^[0-9]+$")) { + if (raw.getStatus().getBattery().matches("^[0-9]+$")) { try { - level = Integer.parseInt(raw.sUploaderBattery); + level = Integer.parseInt(raw.getStatus().getBattery()); level = Math.max(Math.min(level, 100), 0); levelStr = level + "%"; int iconNo = (int) Math.floor(level / 10.0); @@ -112,7 +112,7 @@ public class UploaderBattery extends BaseComplicationProviderService { } catch (NumberFormatException ex) { - aapsLogger.error(LTag.WEAR, "Cannot parse battery level of: " + raw.sUploaderBattery); + aapsLogger.error(LTag.WEAR, "Cannot parse battery level of: " + raw.getStatus().getBattery()); } } @@ -147,7 +147,7 @@ public class UploaderBattery extends BaseComplicationProviderService { @Override public String getProviderCanonicalName() { - return UploaderBattery.class.getCanonicalName(); + return UploaderBatteryComplication.class.getCanonicalName(); } @Override diff --git a/wear/src/main/java/info/nightscout/androidaps/data/BasalWatchData.java b/wear/src/main/java/info/nightscout/androidaps/data/BasalWatchData.java deleted file mode 100644 index ebed526ec8..0000000000 --- a/wear/src/main/java/info/nightscout/androidaps/data/BasalWatchData.java +++ /dev/null @@ -1,11 +0,0 @@ -package info.nightscout.androidaps.data; - -/** - * Created by adrian on 18/11/16. - */ - -public class BasalWatchData { - public long startTime; - public long endTime; - public double amount; -} diff --git a/wear/src/main/java/info/nightscout/androidaps/data/BgWatchData.java b/wear/src/main/java/info/nightscout/androidaps/data/BgWatchData.java deleted file mode 100644 index 4f258ee074..0000000000 --- a/wear/src/main/java/info/nightscout/androidaps/data/BgWatchData.java +++ /dev/null @@ -1,49 +0,0 @@ -package info.nightscout.androidaps.data; - -import java.util.Objects; - -/** - * Created by emmablack on 1/7/15. - */ -public class BgWatchData implements Comparable{ - public double sgv; - public double high; - public double low; - public long timestamp; - public int color; - - public BgWatchData(double aSgv, double aHigh, double aLow, long aTimestamp, int aColor) { - this.sgv = aSgv; - this.high = aHigh; - this.low = aLow; - this.timestamp = aTimestamp; - this.color = aColor; - } - - public BgWatchData(){ - - } - - @Override - public boolean equals(Object that){ - if(! (that instanceof BgWatchData)){ - return false; - } - if (this.color != ((BgWatchData) that).color) - return false; - return this.timestamp == ((BgWatchData) that).timestamp; - } - - @Override - public int hashCode() { - return Objects.hash(timestamp, color); - } - - @Override - public int compareTo(BgWatchData that) { - // reverse order endTime get latest first - if(this.timestamp < that.timestamp) return 1; - if(this.timestamp > that.timestamp) return -1; - return 0; - } -} diff --git a/wear/src/main/java/info/nightscout/androidaps/data/BolusWatchData.java b/wear/src/main/java/info/nightscout/androidaps/data/BolusWatchData.java deleted file mode 100644 index 6a474f3632..0000000000 --- a/wear/src/main/java/info/nightscout/androidaps/data/BolusWatchData.java +++ /dev/null @@ -1,13 +0,0 @@ -package info.nightscout.androidaps.data; - -/** - * Created by adrian on 17/11/16. - */ - -public class BolusWatchData { - public long date; - public double bolus; - public double carbs; - public boolean isSMB; - public boolean isValid; -} diff --git a/wear/src/main/java/info/nightscout/androidaps/data/DataLayerListenerService.kt b/wear/src/main/java/info/nightscout/androidaps/data/DataLayerListenerService.kt deleted file mode 100644 index 7cf6bb7840..0000000000 --- a/wear/src/main/java/info/nightscout/androidaps/data/DataLayerListenerService.kt +++ /dev/null @@ -1,552 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package info.nightscout.androidaps.data - -import android.annotation.TargetApi -import android.app.Notification -import android.app.NotificationChannel -import android.app.NotificationManager -import android.app.PendingIntent -import android.content.Context -import android.content.Intent -import android.os.Build -import android.os.Bundle -import android.os.Handler -import android.os.HandlerThread -import android.os.SystemClock -import android.util.Base64 -import androidx.core.app.NotificationCompat -import androidx.core.app.NotificationManagerCompat -import androidx.localbroadcastmanager.content.LocalBroadcastManager -import androidx.wear.tiles.TileService -import com.google.android.gms.tasks.Tasks -import com.google.android.gms.wearable.* -import dagger.android.AndroidInjection -import info.nightscout.androidaps.R -import info.nightscout.androidaps.events.EventWearToMobileAction -import info.nightscout.androidaps.events.EventWearToMobileChange -import info.nightscout.androidaps.events.EventWearToMobileConfirm -import info.nightscout.androidaps.interaction.AAPSPreferences -import info.nightscout.androidaps.interaction.actions.AcceptActivity -import info.nightscout.androidaps.interaction.actions.CPPActivity -import info.nightscout.androidaps.interaction.utils.Persistence -import info.nightscout.androidaps.interaction.utils.WearUtil -import info.nightscout.androidaps.plugins.bus.RxBus -import info.nightscout.androidaps.tile.ActionsTileService -import info.nightscout.androidaps.tile.QuickWizardTileService -import info.nightscout.androidaps.tile.TempTargetTileService -import info.nightscout.androidaps.utils.rx.AapsSchedulers -import info.nightscout.shared.SafeParse.stringToInt -import info.nightscout.shared.logging.AAPSLogger -import info.nightscout.shared.logging.LTag -import info.nightscout.shared.sharedPreferences.SP -import info.nightscout.shared.weardata.ActionData -import info.nightscout.shared.weardata.WearConstants -import info.nightscout.shared.weardata.WearConstants.Companion.KEY_ACTION_DATA -import io.reactivex.rxjava3.disposables.CompositeDisposable -import io.reactivex.rxjava3.kotlin.plusAssign -import kotlinx.coroutines.* -import kotlinx.coroutines.tasks.await -import javax.inject.Inject - -class DataLayerListenerService : WearableListenerService() { - - @Inject lateinit var aapsLogger: AAPSLogger - @Inject lateinit var wearUtil: WearUtil - @Inject lateinit var persistence: Persistence - @Inject lateinit var sp: SP - @Inject lateinit var rxBus: RxBus - @Inject lateinit var aapsSchedulers: AapsSchedulers - @Inject lateinit var wearConstants: WearConstants - - private val dataClient by lazy { Wearable.getDataClient(this) } - private val messageClient by lazy { Wearable.getMessageClient(this) } - private val capabilityClient by lazy { Wearable.getCapabilityClient(this) } - //private val nodeClient by lazy { Wearable.getNodeClient(this) } - - private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) - private var handler = Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper) - - private val disposable = CompositeDisposable() - - override fun onCreate() { - AndroidInjection.inject(this) - super.onCreate() - handler.post { updateTranscriptionCapability() } - disposable += rxBus - .toObservable(EventWearToMobileAction::class.java) - .observeOn(aapsSchedulers.io) - .subscribe { sendMessage(wearConstants.W_M_INITIATE_ACTION, it.actionData.serialize().toByteArray()) } - disposable += rxBus - .toObservable(EventWearToMobileConfirm::class.java) - .observeOn(aapsSchedulers.io) - .subscribe { - NotificationManagerCompat.from(this).cancel(CONFIRM_NOTIF_ID) - sendMessage(wearConstants.W_M_CONFIRM_ACTION, it.actionData.serialize().toByteArray()) - } - disposable += rxBus - .toObservable(EventWearToMobileChange::class.java) - .observeOn(aapsSchedulers.io) - .subscribe { - NotificationManagerCompat.from(this).cancel(CHANGE_NOTIF_ID) - sendMessage(wearConstants.W_M_CONFIRM_ACTION, it.actionData.serialize().toByteArray()) - } - } - - override fun onPeerConnected(p0: Node) { - super.onPeerConnected(p0) - } - - override fun onCapabilityChanged(p0: CapabilityInfo) { - super.onCapabilityChanged(p0) - handler.post { updateTranscriptionCapability() } - aapsLogger.debug(LTag.WEAR, "onCapabilityChanged: ${p0.name} ${p0.nodes.joinToString(", ") { it.displayName + "(" + it.id + ")" }}") - } - - override fun onDestroy() { - super.onDestroy() - scope.cancel() - disposable.clear() - } - - override fun onDataChanged(dataEvents: DataEventBuffer) { - //aapsLogger.debug(LTag.WEAR, "onDataChanged") - - dataEvents.forEach { event -> - if (event.type == DataEvent.TYPE_CHANGED) { - val path = event.dataItem.uri.path - - aapsLogger.debug(LTag.WEAR, "onDataChanged: Path: $path, EventDataItem=${event.dataItem}") - try { - when (path) { - wearConstants.M_W_BOLUS_PROGRESS -> { - val progress = DataMapItem.fromDataItem(event.dataItem).dataMap.getInt("progresspercent", 0) - val status = DataMapItem.fromDataItem(event.dataItem).dataMap.getString("progressstatus", "") - showBolusProgress(progress, status) - } - // remove when finished -> converted to message - wearConstants.M_W_ACTION_CONFIRMATION_REQUEST -> { - val title = DataMapItem.fromDataItem(event.dataItem).dataMap.getString("title") ?: return@forEach - val message = DataMapItem.fromDataItem(event.dataItem).dataMap.getString("message") ?: return@forEach - val actionstring = DataMapItem.fromDataItem(event.dataItem).dataMap.getString("actionstring") ?: return@forEach - if ("opencpp" == title && actionstring.startsWith("opencpp")) { - val act = actionstring.split("\\s+").toTypedArray() - startActivity(Intent(this@DataLayerListenerService, CPPActivity::class.java).also { intent -> - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - intent.putExtras(Bundle().also { - it.putInt("percentage", stringToInt(act[1])) - it.putInt("timeshift", stringToInt(act[2])) - }) - }) - } else { - showConfirmationDialog(title, message, actionstring) - } - } - - wearConstants.M_W_STATUS -> { - val dataMap = DataMapItem.fromDataItem(event.dataItem).dataMap - val messageIntent = Intent() - messageIntent.action = Intent.ACTION_SEND - messageIntent.putExtra("status", dataMap.toBundle()) - persistence.storeDataMap(RawDisplayData.STATUS_PERSISTENCE_KEY, dataMap) - LocalBroadcastManager.getInstance(this@DataLayerListenerService).sendBroadcast(messageIntent) - } - - wearConstants.M_W_BASAL -> { - val dataMap = DataMapItem.fromDataItem(event.dataItem).dataMap - val messageIntent = Intent() - messageIntent.action = Intent.ACTION_SEND - messageIntent.putExtra("basals", dataMap.toBundle()) - persistence.storeDataMap(RawDisplayData.BASALS_PERSISTENCE_KEY, dataMap) - LocalBroadcastManager.getInstance(this@DataLayerListenerService).sendBroadcast(messageIntent) - } - - wearConstants.M_W_PREFERENCES -> { - val dataMap = DataMapItem.fromDataItem(event.dataItem).dataMap - val keyControl = getString(R.string.key_wear_control) - if (dataMap.containsKey(keyControl)) { - val previousWearControl = sp.getBoolean(keyControl, false) - val wearControl: Boolean = dataMap.getBoolean(keyControl, false) - sp.putBoolean(keyControl, wearControl) - if (wearControl != previousWearControl) { - updateTiles() - } - } - val keyPercentage = getString(R.string.key_boluswizard_percentage) - if (dataMap.containsKey(keyPercentage)) { - val wpercentage: Int = dataMap.getInt(keyPercentage, 100) - sp.putInt(keyPercentage, wpercentage) - } - val keyUnits = getString(R.string.key_units_mgdl) - if (dataMap.containsKey(keyUnits)) { - val mgdl: Boolean = dataMap.getBoolean(keyUnits, true) - sp.putBoolean(keyUnits, mgdl) - } - val keyMaxCarbs = getString(R.string.key_treatmentssafety_maxcarbs) - if (dataMap.containsKey(keyMaxCarbs)) { - val maxCarbs: Int = dataMap.getInt(keyMaxCarbs, 48) - sp.putInt(keyMaxCarbs, maxCarbs) - } - val keyMaxBolus = getString(R.string.key_treatmentssafety_maxbolus) - if (dataMap.containsKey(keyMaxBolus)) { - sp.putDouble(keyMaxBolus, dataMap.getDouble(keyMaxBolus, 3.0)) - } - } - - wearConstants.M_W_QUICK_WIZARD -> { - val dataMap = DataMapItem.fromDataItem(event.dataItem).dataMap - aapsLogger.info(LTag.WEAR, "onDataChanged: QUICK_WIZARD_PATH$dataMap") - dataMap.remove("timestamp") - val key = getString(R.string.key_quick_wizard_data_map) - val dataString = Base64.encodeToString(dataMap.toByteArray(), Base64.DEFAULT) - if (dataString != sp.getString(key, "")) { - sp.putString(key, dataString) - // Todo maybe add debounce function, due to 20 seconds update limit? - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - TileService.getUpdater(this@DataLayerListenerService).requestUpdate(QuickWizardTileService::class.java) - } - aapsLogger.info(LTag.WEAR, "onDataChanged: updated QUICK_WIZARD") - } else { - aapsLogger.info(LTag.WEAR, "onDataChanged: ignore update") - } - } - - wearConstants.M_W_ACTION_CHANGE_CONFIRMATION_REQUEST -> { - val title = DataMapItem.fromDataItem(event.dataItem).dataMap.getString("title") ?: return@forEach - val message = DataMapItem.fromDataItem(event.dataItem).dataMap.getString("message") ?: return@forEach - val actionstring = DataMapItem.fromDataItem(event.dataItem).dataMap.getString("actionstring") ?: return@forEach - notifyChangeRequest(title, message, actionstring) - } - - wearConstants.M_W_ACTION_CANCEL_NOTIFICATION_REQUEST -> { - //val actionstring = DataMapItem.fromDataItem(event.getDataItem()).dataMap.getString("actionstring") ?: return@forEach - cancelNotificationRequest() - } - - wearConstants.M_W_DATA -> { - val dataMap = DataMapItem.fromDataItem(event.dataItem).dataMap - val messageIntent = Intent() - messageIntent.action = Intent.ACTION_SEND - messageIntent.putExtra("data", dataMap.toBundle()) - persistence.storeDataMap(RawDisplayData.DATA_PERSISTENCE_KEY, dataMap) - LocalBroadcastManager.getInstance(this@DataLayerListenerService).sendBroadcast(messageIntent) - } - } - } catch (exception: Exception) { - aapsLogger.error(LTag.WEAR, "onDataChanged failed", exception) - } - } - } - super.onDataChanged(dataEvents) - } - - override fun onMessageReceived(messageEvent: MessageEvent) { - super.onMessageReceived(messageEvent) - aapsLogger.debug(LTag.WEAR, "onMessageReceived: $messageEvent") - - when (messageEvent.path) { - wearConstants.M_W_PING -> sendMessage(wearConstants.W_M_PONG, byteArrayOf()) - wearConstants.M_W_OPEN_SETTINGS -> startActivity(Intent(this@DataLayerListenerService, AAPSPreferences::class.java).also { it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) }) - - wearConstants.M_W_ACTION_CONFIRMATION_REQUEST -> { - val command = ActionData.deserialize(String(messageEvent.data)) as ActionData.ConfirmAction - if (command.originalCommand is ActionData.OpenProfileSwitch) { - val originalCommand = command.originalCommand as ActionData.OpenProfileSwitch - startActivity(Intent(this, CPPActivity::class.java).also { intent -> - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - intent.putExtras(Bundle().also { - it.putInt("percentage", originalCommand.percentage) - it.putInt("timeshift", originalCommand.timeShift) - }) - }) - } else { - startActivity( - Intent(this, AcceptActivity::class.java).also { intent -> - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - intent.putExtras( - Bundle().also { bundle -> - bundle.putString("title", command.title) - bundle.putString("message", command.message) - bundle.putString(KEY_ACTION_DATA, command.originalCommand.serialize()) - } - ) - }) - } - } - } - } - - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - when (intent?.action) { - ACTION_RESEND -> sendMessage(wearConstants.W_M_RESEND_DATA, byteArrayOf()) - - ACTION_CANCEL_BOLUS -> { - //dismiss notification - val notificationManager = NotificationManagerCompat.from(this) - notificationManager.cancel(BOLUS_PROGRESS_NOTIF_ID) - //send cancel-request to phone. - sendMessage(wearConstants.W_M_CANCEL_BOLUS, byteArrayOf()) - } - - ACTION_CONFIRMATION -> { - //dismiss notification - val notificationManager = NotificationManagerCompat.from(this) - notificationManager.cancel(CONFIRM_NOTIF_ID) - intent.getStringExtra("actionstring")?.let { actionString -> - sendMessage(wearConstants.W_M_CONFIRM_ACTION, actionString.toByteArray()) - } - } - - ACTION_CONFIRM_CHANGE -> { - //dismiss notification - val notificationManager = NotificationManagerCompat.from(this) - notificationManager.cancel(CHANGE_NOTIF_ID) - intent.getStringExtra("actionstring")?.let { actionString -> - sendMessage(wearConstants.W_M_CONFIRM_ACTION, actionString.toByteArray()) - } - } - - ACTION_INITIATE_ACTION -> - if (intent.hasExtra("actionstring")) - intent.getStringExtra("actionstring")?.let { actionString -> - sendMessage(wearConstants.W_M_INITIATE_ACTION, actionString.toByteArray()) - } - else if (intent.hasExtra(KEY_ACTION_DATA)) - intent.getStringExtra(KEY_ACTION_DATA)?.let { actionData -> - sendMessage(wearConstants.W_M_INITIATE_ACTION, actionData.toByteArray()) - } - } - return START_STICKY - } - - private var transcriptionNodeId: String? = null - - private fun updateTranscriptionCapability() { - val capabilityInfo: CapabilityInfo = Tasks.await( - capabilityClient.getCapability(PHONE_CAPABILITY, CapabilityClient.FILTER_REACHABLE) - ) - aapsLogger.debug(LTag.WEAR, "Nodes: ${capabilityInfo.nodes.joinToString(", ") { it.displayName + "(" + it.id + ")" }}") - transcriptionNodeId = pickBestNodeId(capabilityInfo.nodes) - aapsLogger.debug(LTag.WEAR, "Selected node: $transcriptionNodeId") - } - - // Find a nearby node or pick one arbitrarily - private fun pickBestNodeId(nodes: Set): String? = - nodes.firstOrNull { it.isNearby }?.id ?: nodes.firstOrNull()?.id - - private fun sendData(path: String, vararg params: DataMap) { - scope.launch { - try { - for (dm in params) { - val request = PutDataMapRequest.create(path).apply { - dataMap.putAll(dm) - } - .asPutDataRequest() - .setUrgent() - - val result = dataClient.putDataItem(request).await() - aapsLogger.debug(LTag.WEAR, "sendData: ${result.uri} ${params.joinToString()}") - } - } catch (cancellationException: CancellationException) { - throw cancellationException - } catch (exception: Exception) { - aapsLogger.error(LTag.WEAR, "DataItem failed: $exception") - } - } - } - - private fun sendMessage(path: String, data: ByteArray) { - aapsLogger.debug(LTag.WEAR, "sendMessage: $path") - transcriptionNodeId?.also { nodeId -> - messageClient - .sendMessage(nodeId, path, data).apply { - addOnSuccessListener { } - addOnFailureListener { - aapsLogger.debug(LTag.WEAR, "sendMessage: $path failure") - } - } - } - } - - private fun updateTiles() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - TileService.getUpdater(this) - .requestUpdate(ActionsTileService::class.java) - TileService.getUpdater(this) - .requestUpdate(TempTargetTileService::class.java) - TileService.getUpdater(this) - .requestUpdate(QuickWizardTileService::class.java) - } - } - - private fun notifyChangeRequest(title: String, message: String, actionstring: String) { - // Create the NotificationChannel, but only on API 26+ because - // the NotificationChannel class is new and not in the support library - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val name: CharSequence = "AAPS Open Loop" - val description = "Open Loop request notification" - val channel = NotificationChannel(AAPS_NOTIFY_CHANNEL_ID_OPENLOOP, name, NotificationManager.IMPORTANCE_HIGH) - channel.description = description - channel.enableVibration(true) - - // Register the channel with the system; you can't change the importance - // or other notification behaviors after this - val notificationManager = getSystemService(NotificationManager::class.java) - notificationManager.createNotificationChannel(channel) - } - var builder = NotificationCompat.Builder(this, AAPS_NOTIFY_CHANNEL_ID_OPENLOOP) - builder = builder.setSmallIcon(R.drawable.notif_icon) - .setContentTitle(title) - .setContentText(message) - .setPriority(Notification.PRIORITY_HIGH) - .setVibrate(longArrayOf(1000, 1000, 1000, 1000, 1000)) - - // Creates an explicit intent for an Activity in your app - val intent = Intent(this, AcceptActivity::class.java) - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - val params = Bundle() - params.putString("title", title) - params.putString("message", message) - params.putString("actionstring", actionstring) - intent.putExtras(params) - val resultPendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) - builder = builder.setContentIntent(resultPendingIntent) - val mNotificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager - // mId allows you to update the notification later on. - mNotificationManager.notify(CHANGE_NOTIF_ID, builder.build()) - } - - private fun cancelNotificationRequest() { - (getSystemService(NOTIFICATION_SERVICE) as NotificationManager).cancel(CHANGE_NOTIF_ID) - } - - private fun showBolusProgress(progressPercent: Int, progresStatus: String) { - val vibratePattern: LongArray - val vibrate = sp.getBoolean("vibrateOnBolus", true) - vibratePattern = if (vibrate) longArrayOf(0, 50, 1000) else longArrayOf(0, 1, 1000) - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - createBolusProgressChannels() - } - val cancelIntent = Intent(this, DataLayerListenerService::class.java) - cancelIntent.action = ACTION_CANCEL_BOLUS - val cancelPendingIntent = PendingIntent.getService(this, 0, cancelIntent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) - val notificationBuilder: NotificationCompat.Builder = - NotificationCompat.Builder(this, if (vibrate) AAPS_NOTIFY_CHANNEL_ID_BOLUSPROGRESS else AAPS_NOTIFY_CHANNEL_ID_BOLUSPROGRESS_SILENT) - .setSmallIcon(R.drawable.ic_icon) - .setContentTitle(getString(R.string.bolus_progress)) - .setContentText("$progressPercent% - $progresStatus") - .setSubText(getString(R.string.press_to_cancel)) - .setContentIntent(cancelPendingIntent) - .setPriority(NotificationCompat.PRIORITY_MAX) - .setVibrate(vibratePattern) - .addAction(R.drawable.ic_cancel, getString(R.string.cancel_bolus), cancelPendingIntent) - val notificationManager = NotificationManagerCompat.from(this) - notificationManager.notify(BOLUS_PROGRESS_NOTIF_ID, notificationBuilder.build()) - notificationManager.cancel(CONFIRM_NOTIF_ID) // multiple watch setup - if (progressPercent == 100) { - scheduleDismissBolusProgress(5) - } - } - - @TargetApi(value = 26) private fun createBolusProgressChannels() { - createNotificationChannel( - longArrayOf(0, 50, 1000), - AAPS_NOTIFY_CHANNEL_ID_BOLUSPROGRESS, - getString(R.string.bolus_progress_channel_name), - getString(R.string.bolus_progress_channel_description) - ) - createNotificationChannel( - longArrayOf(0, 1, 1000), - AAPS_NOTIFY_CHANNEL_ID_BOLUSPROGRESS_SILENT, - getString(R.string.bolus_progress_silent_channel_name), - getString(R.string.bolus_progress_silent_channel_description) - ) - } - - @TargetApi(value = 26) private fun createNotificationChannel(vibratePattern: LongArray, channelID: String, name: CharSequence, description: String) { - val channel = NotificationChannel(channelID, name, NotificationManager.IMPORTANCE_HIGH) - channel.description = description - channel.enableVibration(true) - channel.vibrationPattern = vibratePattern - - // Register the channel with the system; you can't change the importance - // or other notification behaviors after this - val notificationManager = getSystemService(NotificationManager::class.java) - notificationManager.createNotificationChannel(channel) - } - - private fun showConfirmationDialog(title: String, message: String, actionstring: String) { - val intent = Intent(this, AcceptActivity::class.java) - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - val params = Bundle() - params.putString("title", title) - params.putString("message", message) - params.putString("actionstring", actionstring) - intent.putExtras(params) - startActivity(intent) - } - - @Suppress("SameParameterValue") - private fun scheduleDismissBolusProgress(seconds: Int) { - Thread { - SystemClock.sleep(seconds * 1000L) - NotificationManagerCompat.from(this@DataLayerListenerService) - .cancel(BOLUS_PROGRESS_NOTIF_ID) - }.start() - } - - companion object { - - const val PHONE_CAPABILITY = "androidaps_mobile" - - const val ACTION_RESEND = "com.dexdrip.stephenblack.nightwatch.RESEND_DATA" - const val ACTION_CANCEL_BOLUS = "com.dexdrip.stephenblack.nightwatch.CANCELBOLUS" - const val ACTION_CONFIRMATION = "com.dexdrip.stephenblack.nightwatch.CONFIRMACTION" - const val ACTION_CONFIRM_CHANGE = "com.dexdrip.stephenblack.nightwatch.CONFIRMCHANGE" - val ACTION_INITIATE_ACTION = DataLayerListenerService::class.java.name + ".INITIATE_ACTION" - - const val BOLUS_PROGRESS_NOTIF_ID = 1 - const val CONFIRM_NOTIF_ID = 2 - const val CHANGE_NOTIF_ID = 556677 - - const val AAPS_NOTIFY_CHANNEL_ID_OPENLOOP = "AndroidAPS-OpenLoop" - const val AAPS_NOTIFY_CHANNEL_ID_BOLUSPROGRESS = "bolus progress vibration" - const val AAPS_NOTIFY_CHANNEL_ID_BOLUSPROGRESS_SILENT = "bolus progress silent" - - fun initiateAction(context: Context, actionstring: String) { - context.startService( - Intent(context, DataLayerListenerService::class.java).also { - it.putExtra("actionstring", actionstring) - it.action = ACTION_INITIATE_ACTION - }) - } - - fun requestData(context: Context) { - context.startService( - Intent(context, DataLayerListenerService::class.java).also { it.action = ACTION_RESEND }) - } - - fun confirmAction(context: Context, actionstring: String) { - context.startService( - Intent(context, DataLayerListenerService::class.java).also { - it.putExtra("actionstring", actionstring) - if (actionstring == "changeRequest") it.action = ACTION_CONFIRM_CHANGE - else it.action = ACTION_CONFIRMATION - }) - } - } -} diff --git a/wear/src/main/java/info/nightscout/androidaps/data/RawDisplayData.java b/wear/src/main/java/info/nightscout/androidaps/data/RawDisplayData.java deleted file mode 100644 index a13c347a10..0000000000 --- a/wear/src/main/java/info/nightscout/androidaps/data/RawDisplayData.java +++ /dev/null @@ -1,281 +0,0 @@ -package info.nightscout.androidaps.data; - -import android.content.Intent; -import android.os.Bundle; -import android.os.PowerManager; - -import com.google.android.gms.wearable.DataMap; - -import java.util.ArrayList; -import java.util.Iterator; - -import info.nightscout.androidaps.interaction.utils.Constants; -import info.nightscout.androidaps.interaction.utils.Persistence; -import info.nightscout.androidaps.interaction.utils.WearUtil; - -/** - * Holds bunch of data model variables and lists that arrive from phone app and are due to be - * displayed on watchface and complications. Keeping them together makes code cleaner and allows - * passing it to complications via persistence layer. - * - * Created by dlvoy on 2019-11-12 - */ -public class RawDisplayData { - - private final WearUtil wearUtil; - - public RawDisplayData(WearUtil wearUtil) { - this.wearUtil = wearUtil; - } - - static final String DATA_PERSISTENCE_KEY = "raw_data"; - static final String BASALS_PERSISTENCE_KEY = "raw_basals"; - static final String STATUS_PERSISTENCE_KEY = "raw_status"; - - // data bundle - public long sgvLevel = 0; - public long datetime; - public String sSgv = "---"; - public String sDirection = "--"; - public String sDelta = "--"; - public String sAvgDelta = "--"; - public String sUnits = "-"; - - // status bundle - public String sBasalRate = "-.--U/h"; - public String sUploaderBattery = "--"; - public String sRigBattery = "--"; - public boolean detailedIOB = false; - public String sIOB1 = "IOB"; - public String sIOB2 = "-.--"; - public String sCOB1 = "Carb"; - public String sCOB2= "--g"; - public String sBgi = "--"; - public boolean showBGI = false; - public String externalStatusString = "no status"; - public int batteryLevel = 1; - public long openApsStatus = -1; - - // basals bundle - public ArrayList bgDataList = new ArrayList<>(); - public ArrayList tempWatchDataList = new ArrayList<>(); - public ArrayList basalWatchDataList = new ArrayList<>(); - public ArrayList bolusWatchDataList = new ArrayList<>(); - public ArrayList predictionList = new ArrayList<>(); - - public String toDebugString() { - return "DisplayRawData{" + - "sgvLevel=" + sgvLevel + - ", datetime=" + datetime + - ", sSgv='" + sSgv + '\'' + - ", sDirection='" + sDirection + '\'' + - ", sDelta='" + sDelta + '\'' + - ", sAvgDelta='" + sAvgDelta + '\'' + - ", sUnits='" + sUnits + '\'' + - ", sBasalRate='" + sBasalRate + '\'' + - ", sUploaderBattery='" + sUploaderBattery + '\'' + - ", sRigBattery='" + sRigBattery + '\'' + - ", detailedIOB=" + detailedIOB + - ", sIOB1='" + sIOB1 + '\'' + - ", sIOB2='" + sIOB2 + '\'' + - ", sCOB1='" + sCOB1 + '\'' + - ", sCOB2='" + sCOB2 + '\'' + - ", sBgi='" + sBgi + '\'' + - ", showBGI=" + showBGI + - ", externalStatusString='" + externalStatusString + '\'' + - ", batteryLevel=" + batteryLevel + - ", openApsStatus=" + openApsStatus + - ", bgDataList size=" + bgDataList.size() + - ", tempWatchDataList size=" + tempWatchDataList.size() + - ", basalWatchDataList size=" + basalWatchDataList.size() + - ", bolusWatchDataLis size=" + bolusWatchDataList.size() + - ", predictionList size=" + predictionList.size() + - '}'; - } - - public void updateFromPersistence(Persistence persistence) { - - DataMap dataMapData = persistence.getDataMap(DATA_PERSISTENCE_KEY); - if (dataMapData != null) { - updateData(dataMapData); - } - DataMap dataMapStatus = persistence.getDataMap(STATUS_PERSISTENCE_KEY); - if (dataMapStatus != null) { - updateStatus(dataMapStatus); - } - DataMap dataMapBasals = persistence.getDataMap(BASALS_PERSISTENCE_KEY); - if (dataMapBasals != null) { - updateBasals(dataMapBasals); - } - } - - /* - * Since complications do not need Basals, we skip them for performance - */ - public void updateForComplicationsFromPersistence(Persistence persistence) { - - DataMap dataMapData = persistence.getDataMap(DATA_PERSISTENCE_KEY); - if (dataMapData != null) { - updateData(dataMapData); - } - DataMap dataMapStatus = persistence.getDataMap(STATUS_PERSISTENCE_KEY); - if (dataMapStatus != null) { - updateStatus(dataMapStatus); - } - } - - public DataMap updateDataFromMessage(Intent intent, PowerManager.WakeLock wakeLock) { - Bundle bundle = intent.getBundleExtra("data"); - if (bundle != null) { - DataMap dataMap = wearUtil.bundleToDataMap(bundle); - updateData(dataMap); - return dataMap; - } - return null; - } - - private void updateData(DataMap dataMap) { - PowerManager.WakeLock wl = wearUtil.getWakeLock("readingPrefs", 50); - sgvLevel = dataMap.getLong("sgvLevel"); - datetime = dataMap.getLong("timestamp"); - sSgv = dataMap.getString("sgvString"); - sDirection = dataMap.getString("slopeArrow"); - sDelta = dataMap.getString("delta"); - sAvgDelta = dataMap.getString("avgDelta"); - sUnits = dataMap.getString("glucoseUnits"); - wearUtil.releaseWakeLock(wl); - } - - public DataMap updateStatusFromMessage(Intent intent, PowerManager.WakeLock wakeLock) { - Bundle bundle = intent.getBundleExtra("status"); - if (bundle != null) { - DataMap dataMap = wearUtil.bundleToDataMap(bundle); - updateStatus(dataMap); - return dataMap; - } - return null; - } - - private void updateStatus(DataMap dataMap) { - PowerManager.WakeLock wl = wearUtil.getWakeLock("readingPrefs", 50); - sBasalRate = dataMap.getString("currentBasal"); - sUploaderBattery = dataMap.getString("battery"); - sRigBattery = dataMap.getString("rigBattery"); - detailedIOB = dataMap.getBoolean("detailedIob"); - sIOB1 = dataMap.getString("iobSum") + "U"; - sIOB2 = dataMap.getString("iobDetail"); - sCOB1 = "Carb"; - sCOB2 = dataMap.getString("cob"); - sBgi = dataMap.getString("bgi"); - showBGI = dataMap.getBoolean("showBgi"); - externalStatusString = dataMap.getString("externalStatusString"); - batteryLevel = dataMap.getInt("batteryLevel"); - openApsStatus = dataMap.getLong("openApsStatus"); - wearUtil.releaseWakeLock(wl); - } - - public DataMap updateBasalsFromMessage(Intent intent) { - Bundle bundle = intent.getBundleExtra("basals"); - if (bundle != null) { - DataMap dataMap = wearUtil.bundleToDataMap(bundle); - updateBasals(dataMap); - return dataMap; - } - return null; - } - - private void updateBasals(DataMap dataMap) { - PowerManager.WakeLock wl = wearUtil.getWakeLock("readingPrefs", 500); - loadBasalsAndTemps(dataMap); - wearUtil.releaseWakeLock(wl); - } - - private void loadBasalsAndTemps(DataMap dataMap) { - ArrayList temps = dataMap.getDataMapArrayList("temps"); - if (temps != null) { - tempWatchDataList = new ArrayList<>(); - for (DataMap temp : temps) { - TempWatchData twd = new TempWatchData(); - twd.startTime = temp.getLong("starttime"); - twd.startBasal = temp.getDouble("startBasal"); - twd.endTime = temp.getLong("endtime"); - twd.endBasal = temp.getDouble("endbasal"); - twd.amount = temp.getDouble("amount"); - tempWatchDataList.add(twd); - } - } - ArrayList basals = dataMap.getDataMapArrayList("basals"); - if (basals != null) { - basalWatchDataList = new ArrayList<>(); - for (DataMap basal : basals) { - BasalWatchData bwd = new BasalWatchData(); - bwd.startTime = basal.getLong("starttime"); - bwd.endTime = basal.getLong("endtime"); - bwd.amount = basal.getDouble("amount"); - basalWatchDataList.add(bwd); - } - } - ArrayList boluses = dataMap.getDataMapArrayList("boluses"); - if (boluses != null) { - bolusWatchDataList = new ArrayList<>(); - for (DataMap bolus : boluses) { - BolusWatchData bwd = new BolusWatchData(); - bwd.date = bolus.getLong("date"); - bwd.bolus = bolus.getDouble("bolus"); - bwd.carbs = bolus.getDouble("carbs"); - bwd.isSMB = bolus.getBoolean("isSMB"); - bwd.isValid = bolus.getBoolean("isValid"); - bolusWatchDataList.add(bwd); - } - } - ArrayList predictions = dataMap.getDataMapArrayList("predictions"); - if (boluses != null) { - predictionList = new ArrayList<>(); - for (DataMap prediction : predictions) { - BgWatchData bwd = new BgWatchData(); - bwd.timestamp = prediction.getLong("timestamp"); - bwd.sgv = prediction.getDouble("sgv"); - bwd.color = prediction.getInt("color"); - predictionList.add(bwd); - } - } - } - - public void addToWatchSet(DataMap dataMap) { - ArrayList entries = dataMap.getDataMapArrayList("entries"); - if (entries != null) { - bgDataList = new ArrayList<>(); - for (DataMap entry : entries) { - double sgv = entry.getDouble("sgvDouble"); - double high = entry.getDouble("high"); - double low = entry.getDouble("low"); - long timestamp = entry.getLong("timestamp"); - int color = entry.getInt("color", 0); - bgDataList.add(new BgWatchData(sgv, high, low, timestamp, color)); - } - } else { - double sgv = dataMap.getDouble("sgvDouble"); - double high = dataMap.getDouble("high"); - double low = dataMap.getDouble("low"); - long timestamp = dataMap.getLong("timestamp"); - int color = dataMap.getInt("color", 0); - - final int size = bgDataList.size(); - if (size > 0) { - if (bgDataList.get(size - 1).timestamp == timestamp) - return; // Ignore duplicates. - } - - bgDataList.add(new BgWatchData(sgv, high, low, timestamp, color)); - } - - // We use iterator instead for-loop because we iterate and remove on the go - Iterator itr = bgDataList.iterator(); - while (itr.hasNext()) { - BgWatchData entry = (BgWatchData)itr.next(); - if (entry.timestamp < (wearUtil.timestamp() - (Constants.HOUR_IN_MS * 5))) { - itr.remove(); //Get rid of anything more than 5 hours old - } - } - } -} diff --git a/wear/src/main/java/info/nightscout/androidaps/data/RawDisplayData.kt b/wear/src/main/java/info/nightscout/androidaps/data/RawDisplayData.kt new file mode 100644 index 0000000000..ece27dee0d --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/data/RawDisplayData.kt @@ -0,0 +1,95 @@ +package info.nightscout.androidaps.data + +import android.content.Intent +import info.nightscout.androidaps.comm.DataLayerListenerServiceWear +import info.nightscout.androidaps.interaction.utils.Persistence +import info.nightscout.shared.weardata.EventData + +/** + * Holds bunch of data model variables and lists that arrive from phone app and are due to be + * displayed on watchface and complications. Keeping them together makes code cleaner and allows + * passing it to complications via persistence layer. + * + * Created by dlvoy on 2019-11-12 + */ +class RawDisplayData { + + // bg data bundle + var singleBg = EventData.SingleBg( + timeStamp = 0, + sgv = 0.0, + high = 0.0, + low = 0.0, + color = 0 + ) + + // status bundle + var status = EventData.Status( + externalStatus = "no status", + iobSum = "IOB", + iobDetail = "-.--", + detailedIob = false, + cob = "--g", + currentBasal = "-.--U/h", + battery = "--", + rigBattery = "--", + openApsStatus = -1, + bgi = "--", + showBgi = false, + batteryLevel = 1 + ) + + // basals bundle + var graphData = EventData.GraphData( + entries = ArrayList() + ) + + var treatmentData = EventData.TreatmentData( + temps = ArrayList(), + basals = ArrayList(), + boluses = ArrayList(), + predictions = ArrayList() + ) + + fun toDebugString(): String = + "DisplayRawData{singleBg=$singleBg, status=$status, graphData=$graphData, treatmentData=$treatmentData}" + + fun updateFromPersistence(persistence: Persistence) { + persistence.readSingleBg()?.let { singleBg = it } + persistence.readGraphData()?.let { graphData = it } + persistence.readStatus()?.let { status = it } + persistence.readTreatments()?.let { treatmentData = it } + } + + /* + * Since complications do not need Basals, we skip them for performance + */ + fun updateForComplicationsFromPersistence(persistence: Persistence) { + persistence.readSingleBg()?.let { singleBg = it } + persistence.readGraphData()?.let { graphData = it } + persistence.readStatus()?.let { status = it } + } + + fun updateFromMessage(intent: Intent) { + intent.getStringExtra(DataLayerListenerServiceWear.KEY_SINGLE_BG_DATA)?.let{ + singleBg = EventData.deserialize(it) as EventData.SingleBg + } + intent.getStringExtra(DataLayerListenerServiceWear.KEY_STATUS_DATA)?.let{ + status = EventData.deserialize(it) as EventData.Status + } + intent.getStringExtra(DataLayerListenerServiceWear.KEY_TREATMENTS_DATA)?.let{ + treatmentData = EventData.deserialize(it) as EventData.TreatmentData + } + intent.getStringExtra(DataLayerListenerServiceWear.KEY_GRAPH_DATA)?.let{ + graphData = EventData.deserialize(it) as EventData.GraphData + } + } + + companion object { + + const val BG_DATA_PERSISTENCE_KEY = "raw_data" + const val GRAPH_DATA_PERSISTENCE_KEY = "raw_data" + const val BASALS_PERSISTENCE_KEY = "raw_basals" + const val STATUS_PERSISTENCE_KEY = "raw_status" + } +} \ No newline at end of file diff --git a/wear/src/main/java/info/nightscout/androidaps/data/TempWatchData.java b/wear/src/main/java/info/nightscout/androidaps/data/TempWatchData.java deleted file mode 100644 index a7a23962eb..0000000000 --- a/wear/src/main/java/info/nightscout/androidaps/data/TempWatchData.java +++ /dev/null @@ -1,13 +0,0 @@ -package info.nightscout.androidaps.data; - -/** - * Created by adrian on 17/11/16. - */ - -public class TempWatchData { - public long startTime; - public double startBasal; - public long endTime; - public double endBasal; - public double amount; -} diff --git a/wear/src/main/java/info/nightscout/androidaps/di/WearActivitiesModule.kt b/wear/src/main/java/info/nightscout/androidaps/di/WearActivitiesModule.kt index 253935147e..90ef38717e 100644 --- a/wear/src/main/java/info/nightscout/androidaps/di/WearActivitiesModule.kt +++ b/wear/src/main/java/info/nightscout/androidaps/di/WearActivitiesModule.kt @@ -3,6 +3,10 @@ package info.nightscout.androidaps.di import dagger.Module import dagger.android.ContributesAndroidInjector import info.nightscout.androidaps.interaction.actions.* +import info.nightscout.androidaps.interaction.menus.FillMenuActivity +import info.nightscout.androidaps.interaction.menus.MainMenuActivity +import info.nightscout.androidaps.interaction.menus.StatusMenuActivity +import info.nightscout.androidaps.interaction.utils.MenuListActivity @Module @Suppress("unused") @@ -20,4 +24,9 @@ abstract class WearActivitiesModule { @ContributesAndroidInjector abstract fun contributesTempTargetActivity(): TempTargetActivity @ContributesAndroidInjector abstract fun contributesTreatmentActivity(): TreatmentActivity @ContributesAndroidInjector abstract fun contributesWizardActivity(): WizardActivity + + @ContributesAndroidInjector abstract fun contributesMenuListActivity(): MenuListActivity + @ContributesAndroidInjector abstract fun contributesFillMenuActivity(): FillMenuActivity + @ContributesAndroidInjector abstract fun contributesMainMenuActivity(): MainMenuActivity + @ContributesAndroidInjector abstract fun contributesStatusMenuActivity(): StatusMenuActivity } \ No newline at end of file diff --git a/wear/src/main/java/info/nightscout/androidaps/di/WearServicesModule.kt b/wear/src/main/java/info/nightscout/androidaps/di/WearServicesModule.kt index de47454131..800aa4205a 100644 --- a/wear/src/main/java/info/nightscout/androidaps/di/WearServicesModule.kt +++ b/wear/src/main/java/info/nightscout/androidaps/di/WearServicesModule.kt @@ -2,16 +2,18 @@ package info.nightscout.androidaps.di import dagger.Module import dagger.android.ContributesAndroidInjector +import info.nightscout.androidaps.comm.DataLayerListenerServiceWear import info.nightscout.androidaps.complications.* -import info.nightscout.androidaps.data.DataLayerListenerService -import info.nightscout.androidaps.interaction.actions.BackgroundActionActivity +import info.nightscout.androidaps.tile.QuickWizardTileService +import info.nightscout.androidaps.tile.TempTargetTileService +import info.nightscout.androidaps.tile.TileBase import info.nightscout.androidaps.watchfaces.* @Module @Suppress("unused") abstract class WearServicesModule { - @ContributesAndroidInjector abstract fun contributesDataLayerListenerService(): DataLayerListenerService + @ContributesAndroidInjector abstract fun contributesDataLayerListenerService(): DataLayerListenerServiceWear @ContributesAndroidInjector abstract fun contributesBaseComplicationProviderService(): BaseComplicationProviderService @ContributesAndroidInjector abstract fun contributesBrCobIobComplication(): BrCobIobComplication @@ -24,7 +26,7 @@ abstract class WearServicesModule { @ContributesAndroidInjector abstract fun contributesLongStatusComplication(): LongStatusComplication @ContributesAndroidInjector abstract fun contributesLongStatusFlippedComplication(): LongStatusFlippedComplication @ContributesAndroidInjector abstract fun contributesSgvComplication(): SgvComplication - @ContributesAndroidInjector abstract fun contributesUploaderBattery(): UploaderBattery + @ContributesAndroidInjector abstract fun contributesUploaderBatteryComplication(): UploaderBatteryComplication @ContributesAndroidInjector abstract fun contributesWallpaperComplication(): WallpaperComplication @ContributesAndroidInjector abstract fun contributesBaseWatchFace(): BaseWatchFace @@ -34,4 +36,12 @@ abstract class WearServicesModule { @ContributesAndroidInjector abstract fun contributesSteampunk(): Steampunk @ContributesAndroidInjector abstract fun contributesDigitalStyle(): DigitalStyle @ContributesAndroidInjector abstract fun contributesCockpit(): Cockpit + + @ContributesAndroidInjector abstract fun contributesBIGChart(): BIGChart + @ContributesAndroidInjector abstract fun contributesNOChart(): NOChart + @ContributesAndroidInjector abstract fun contributesCircleWatchface(): CircleWatchface + + @ContributesAndroidInjector abstract fun contributesTileBase(): TileBase + @ContributesAndroidInjector abstract fun contributesQuickWizardTileService(): QuickWizardTileService + @ContributesAndroidInjector abstract fun contributesTempTargetTileService(): TempTargetTileService } \ No newline at end of file diff --git a/wear/src/main/java/info/nightscout/androidaps/events/EventWearPreferenceChange.kt b/wear/src/main/java/info/nightscout/androidaps/events/EventWearPreferenceChange.kt new file mode 100644 index 0000000000..51fe819340 --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/events/EventWearPreferenceChange.kt @@ -0,0 +1,22 @@ +package info.nightscout.androidaps.events + +import android.content.Context + +@Suppress("unused") +class EventWearPreferenceChange : Event { + + var changedKey: String? = null + private set + + constructor(key: String) { + changedKey = key + } + + constructor(context: Context, resourceID: Int) { + changedKey = context.getString(resourceID) + } + + fun isChanged(context: Context, id: Int): Boolean { + return changedKey == context.getString(id) + } +} \ No newline at end of file diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/AcceptActivity.java b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/AcceptActivity.java index 413bf0ee26..a5feccda80 100644 --- a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/AcceptActivity.java +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/AcceptActivity.java @@ -1,6 +1,6 @@ package info.nightscout.androidaps.interaction.actions; -import static info.nightscout.shared.weardata.WearConstants.KEY_ACTION_DATA; +import static info.nightscout.androidaps.comm.DataLayerListenerServiceWear.KEY_ACTION_DATA; import android.content.Context; import android.content.Intent; @@ -21,10 +21,8 @@ import androidx.core.view.MotionEventCompat; import androidx.core.view.ViewConfigurationCompat; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.DataLayerListenerService; -import info.nightscout.androidaps.events.EventWearToMobileChange; -import info.nightscout.androidaps.events.EventWearToMobileConfirm; -import info.nightscout.shared.weardata.ActionData; +import info.nightscout.androidaps.events.EventWearToMobile; +import info.nightscout.shared.weardata.EventData; /** * Created by adrian on 09/02/17. @@ -33,7 +31,6 @@ import info.nightscout.shared.weardata.ActionData; public class AcceptActivity extends ViewSelectorActivity { String message = ""; - String actionstring = ""; String actionKey = ""; private DismissThread dismissThread; @@ -46,10 +43,9 @@ public class AcceptActivity extends ViewSelectorActivity { Bundle extras = getIntent().getExtras(); message = extras.getString("message", ""); - actionstring = extras.getString("actionstring", ""); actionKey = extras.getString(KEY_ACTION_DATA, ""); - if (message.isEmpty() || (actionstring.isEmpty() && actionKey.isEmpty())) { + if (message.isEmpty() || actionKey.isEmpty()) { finish(); return; } @@ -109,15 +105,9 @@ public class AcceptActivity extends ViewSelectorActivity { view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.action_send_item, container, false); final ImageView confirmButton = view.findViewById(R.id.confirmbutton); confirmButton.setOnClickListener((View v) -> { - if (!actionstring.isEmpty()) - DataLayerListenerService.Companion.confirmAction(AcceptActivity.this, actionstring); - else { - ActionData actionData = ActionData.Companion.deserialize(actionKey); - if (actionData instanceof ActionData.ConfirmAction) - rxBus.send(new EventWearToMobileConfirm(actionData)); - if (actionData instanceof ActionData.ChangeAction) - rxBus.send(new EventWearToMobileChange(actionData)); - } + EventData returnCommand = EventData.Companion.deserialize(actionKey); + rxBus.send(new EventWearToMobile(returnCommand)); + rxBus.send(new EventData.CancelNotification(System.currentTimeMillis())); finishAffinity(); }); container.addView(view); diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/BackgroundActionActivity.kt b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/BackgroundActionActivity.kt index 4d0ecbc0a9..15afd34b46 100644 --- a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/BackgroundActionActivity.kt +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/BackgroundActionActivity.kt @@ -3,21 +3,25 @@ package info.nightscout.androidaps.interaction.actions import android.os.Bundle import android.widget.Toast import dagger.android.DaggerActivity -import info.nightscout.androidaps.data.DataLayerListenerService +import info.nightscout.androidaps.comm.DataLayerListenerServiceWear +import info.nightscout.androidaps.events.EventWearToMobile +import info.nightscout.androidaps.plugins.bus.RxBus import info.nightscout.shared.logging.AAPSLogger import info.nightscout.shared.logging.LTag +import info.nightscout.shared.weardata.EventData import javax.inject.Inject class BackgroundActionActivity : DaggerActivity() { @Inject lateinit var aapsLogger: AAPSLogger + @Inject lateinit var rxBus: RxBus override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - intent.extras?.getString("actionString")?.let { actionString -> - aapsLogger.info(LTag.WEAR, "QuickWizardActivity.onCreate: actionString=$actionString") - DataLayerListenerService.initiateAction(this, actionString) - intent.extras?.getString("message")?.let { message -> + intent.extras?.getString(DataLayerListenerServiceWear.KEY_ACTION)?.let { action -> + aapsLogger.info(LTag.WEAR, "QuickWizardActivity.onCreate: action=$action") + rxBus.send(EventWearToMobile(EventData.deserialize(action))) + intent.extras?.getString(DataLayerListenerServiceWear.KEY_MESSAGE)?.let { message -> Toast.makeText(this, message, Toast.LENGTH_LONG).show() } } ?: aapsLogger.error(LTag.WEAR, "BackgroundActionActivity.onCreate extras 'actionString' required") diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/BolusActivity.java b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/BolusActivity.java index 55109a352f..372330ca7f 100644 --- a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/BolusActivity.java +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/BolusActivity.java @@ -10,10 +10,10 @@ import android.widget.ImageView; import java.text.DecimalFormat; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.events.EventWearToMobileAction; +import info.nightscout.androidaps.events.EventWearToMobile; import info.nightscout.androidaps.interaction.utils.PlusMinusEditText; import info.nightscout.shared.SafeParse; -import info.nightscout.shared.weardata.ActionData; +import info.nightscout.shared.weardata.EventData; public class BolusActivity extends ViewSelectorActivity { @@ -63,8 +63,7 @@ public class BolusActivity extends ViewSelectorActivity { view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.action_send_item, container, false); final ImageView confirmButton = view.findViewById(R.id.confirmbutton); confirmButton.setOnClickListener((View v) -> { - ActionData.Bolus bolus = new ActionData.Bolus(SafeParse.stringToDouble(editInsulin.editText.getText().toString()), 0); - rxBus.send(new EventWearToMobileAction(bolus)); + rxBus.send(new EventWearToMobile(new EventData.ActionBolusPreCheck(SafeParse.stringToDouble(editInsulin.editText.getText().toString()), 0))); showToast(BolusActivity.this, R.string.action_bolus_confirmation); finishAffinity(); }); diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/CPPActivity.java b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/CPPActivity.java index dde240c015..c0b6cedb33 100644 --- a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/CPPActivity.java +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/CPPActivity.java @@ -10,11 +10,10 @@ import android.widget.ImageView; import java.text.DecimalFormat; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.DataLayerListenerService; -import info.nightscout.androidaps.events.EventWearToMobileAction; +import info.nightscout.androidaps.events.EventWearToMobile; import info.nightscout.androidaps.interaction.utils.PlusMinusEditText; import info.nightscout.shared.SafeParse; -import info.nightscout.shared.weardata.ActionData; +import info.nightscout.shared.weardata.EventData; /** * Created by adrian on 09/02/17. @@ -52,6 +51,7 @@ public class CPPActivity extends ViewSelectorActivity { finish(); } + @SuppressWarnings("deprecation") private class MyGridViewPagerAdapter extends GridPagerAdapter { @Override public int getColumnCount(int arg0) { @@ -94,9 +94,9 @@ public class CPPActivity extends ViewSelectorActivity { confirmButton.setOnClickListener((View v) -> { //check if it can happen that the fragment is never created that hold data? // (you have to swipe past them anyways - but still) - ActionData.ProfileSwitch ps = - new ActionData.ProfileSwitch(SafeParse.stringToInt(editTimeshift.editText.getText().toString()), SafeParse.stringToInt(editPercentage.editText.getText().toString())); - rxBus.send(new EventWearToMobileAction(ps)); + EventData.ActionProfileSwitchPreCheck ps = + new EventData.ActionProfileSwitchPreCheck(SafeParse.stringToInt(editTimeshift.editText.getText().toString()), SafeParse.stringToInt(editPercentage.editText.getText().toString())); + rxBus.send(new EventWearToMobile(ps)); showToast(CPPActivity.this, R.string.action_cpp_confirmation); finishAffinity(); }); @@ -108,7 +108,7 @@ public class CPPActivity extends ViewSelectorActivity { @Override public void destroyItem(ViewGroup container, int row, int col, Object view) { // Handle this to get the data before the view is destroyed? - // Object should still be kept by this, just setup for reinit? + // Object should still be kept by this, just setup for re-init? container.removeView((View) view); } diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/CarbActivity.java b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/CarbActivity.java index d9c9298236..fe6f9c6767 100644 --- a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/CarbActivity.java +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/CarbActivity.java @@ -10,9 +10,10 @@ import android.widget.ImageView; import java.text.DecimalFormat; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.DataLayerListenerService; +import info.nightscout.androidaps.events.EventWearToMobile; import info.nightscout.androidaps.interaction.utils.PlusMinusEditText; import info.nightscout.shared.SafeParse; +import info.nightscout.shared.weardata.EventData; public class CarbActivity extends ViewSelectorActivity { @@ -32,6 +33,7 @@ public class CarbActivity extends ViewSelectorActivity { finish(); } + @SuppressWarnings("deprecation") private class MyGridViewPagerAdapter extends GridPagerAdapter { @Override public int getColumnCount(int arg0) { @@ -46,37 +48,42 @@ public class CarbActivity extends ViewSelectorActivity { @Override public Object instantiateItem(ViewGroup container, int row, int col) { + final View view; if (col == 0) { - final View view = getInflatedPlusMinusView(container); + view = getInflatedPlusMinusView(container); double def = 0; if (editCarbs != null) { def = SafeParse.stringToDouble(editCarbs.editText.getText().toString()); } - editCarbs = new PlusMinusEditText(view, R.id.amountfield, R.id.plusbutton, R.id.minusbutton, def, 0d, (double)maxCarbs, 1d, new DecimalFormat("0"), true); + editCarbs = new PlusMinusEditText(view, R.id.amountfield, R.id.plusbutton, R.id.minusbutton, def, 0d, (double) maxCarbs, 1d, new DecimalFormat("0"), true); setLabelToPlusMinusView(view, getString(R.string.action_carbs)); container.addView(view); view.requestFocus(); - return view; } else { - final View view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.action_send_item, container, false); - final ImageView confirmbutton = view.findViewById(R.id.confirmbutton); - confirmbutton.setOnClickListener((View v) -> { + view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.action_send_item, container, false); + final ImageView confirmButton = view.findViewById(R.id.confirmbutton); + confirmButton.setOnClickListener((View v) -> { // With start time 0 and duration 0 - String actionstring = "ecarbs " + SafeParse.stringToInt(editCarbs.editText.getText().toString()) + " 0 0"; - DataLayerListenerService.Companion.initiateAction(CarbActivity.this, actionstring); + EventData.ActionECarbsPreCheck bolus = + new EventData.ActionECarbsPreCheck( + SafeParse.stringToInt(editCarbs.editText.getText().toString()), + 0, + 0 + ); + rxBus.send(new EventWearToMobile(bolus)); showToast(CarbActivity.this, R.string.action_ecarb_confirmation); finishAffinity(); }); container.addView(view); - return view; } + return view; } @Override public void destroyItem(ViewGroup container, int row, int col, Object view) { // Handle this to get the data before the view is destroyed? - // Object should still be kept by this, just setup for reinit? + // Object should still be kept by this, just setup for re-init? container.removeView((View) view); } diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/ECarbActivity.java b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/ECarbActivity.java index cc36635961..3b8f548c59 100644 --- a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/ECarbActivity.java +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/ECarbActivity.java @@ -10,9 +10,10 @@ import android.widget.ImageView; import java.text.DecimalFormat; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.DataLayerListenerService; +import info.nightscout.androidaps.events.EventWearToMobile; import info.nightscout.androidaps.interaction.utils.PlusMinusEditText; import info.nightscout.shared.SafeParse; +import info.nightscout.shared.weardata.EventData; /** * Created by adrian on 04/08/18. @@ -38,6 +39,7 @@ public class ECarbActivity extends ViewSelectorActivity { finish(); } + @SuppressWarnings("deprecation") private class MyGridViewPagerAdapter extends GridPagerAdapter { @Override public int getColumnCount(int arg0) { @@ -86,16 +88,19 @@ public class ECarbActivity extends ViewSelectorActivity { } else { final View view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.action_send_item, container, false); - final ImageView confirmbutton = view.findViewById(R.id.confirmbutton); - confirmbutton.setOnClickListener((View v) -> { + final ImageView confirmButton = view.findViewById(R.id.confirmbutton); + confirmButton.setOnClickListener((View v) -> { //check if it can happen that the fragment is never created that hold data? // (you have to swipe past them anyways - but still) - String actionstring = "ecarbs " + SafeParse.stringToInt(editCarbs.editText.getText().toString()) - + " " + SafeParse.stringToInt(editStartTime.editText.getText().toString()) - + " " + SafeParse.stringToInt(editDuration.editText.getText().toString()); - DataLayerListenerService.Companion.initiateAction(ECarbActivity.this, actionstring); + EventData.ActionECarbsPreCheck bolus = + new EventData.ActionECarbsPreCheck( + SafeParse.stringToInt(editCarbs.editText.getText().toString()), + SafeParse.stringToInt(editStartTime.editText.getText().toString()), + SafeParse.stringToInt(editDuration.editText.getText().toString()) + ); + rxBus.send(new EventWearToMobile(bolus)); showToast(ECarbActivity.this, R.string.action_ecarb_confirmation); finishAffinity(); @@ -108,7 +113,7 @@ public class ECarbActivity extends ViewSelectorActivity { @Override public void destroyItem(ViewGroup container, int row, int col, Object view) { // Handle this to get the data before the view is destroyed? - // Object should still be kept by this, just setup for reinit? + // Object should still be kept by this, just setup for re-init? container.removeView((View) view); } diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/FillActivity.java b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/FillActivity.java index 28493146ca..c7cbb892ce 100644 --- a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/FillActivity.java +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/FillActivity.java @@ -10,9 +10,10 @@ import android.widget.ImageView; import java.text.DecimalFormat; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.DataLayerListenerService; +import info.nightscout.androidaps.events.EventWearToMobile; import info.nightscout.androidaps.interaction.utils.PlusMinusEditText; import info.nightscout.shared.SafeParse; +import info.nightscout.shared.weardata.EventData; /** * Created by adrian on 09/02/17. @@ -34,6 +35,7 @@ public class FillActivity extends ViewSelectorActivity { finish(); } + @SuppressWarnings("deprecation") private class MyGridViewPagerAdapter extends GridPagerAdapter { @Override public int getColumnCount(int arg0) { @@ -67,8 +69,7 @@ public class FillActivity extends ViewSelectorActivity { //check if it can happen that the fagment is never created that hold data? // (you have to swipe past them anyways - but still) - String actionstring = "fill " + SafeParse.stringToDouble(editInsulin.editText.getText().toString()); - DataLayerListenerService.Companion.initiateAction(FillActivity.this, actionstring); + rxBus.send(new EventWearToMobile(new EventData.ActionFillPreCheck(SafeParse.stringToDouble(editInsulin.editText.getText().toString())))); showToast(FillActivity.this, R.string.action_fill_confirmation); finishAffinity(); }); diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/TempTargetActivity.java b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/TempTargetActivity.java index 4bab152cb5..22cb47811f 100644 --- a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/TempTargetActivity.java +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/TempTargetActivity.java @@ -10,9 +10,10 @@ import android.widget.ImageView; import java.text.DecimalFormat; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.DataLayerListenerService; +import info.nightscout.androidaps.events.EventWearToMobile; import info.nightscout.androidaps.interaction.utils.PlusMinusEditText; import info.nightscout.shared.SafeParse; +import info.nightscout.shared.weardata.EventData; /** * Created by adrian on 09/02/17. @@ -42,6 +43,7 @@ public class TempTargetActivity extends ViewSelectorActivity { finish(); } + @SuppressWarnings("deprecation") private class MyGridViewPagerAdapter extends GridPagerAdapter { @Override public int getColumnCount(int arg0) { @@ -112,18 +114,20 @@ public class TempTargetActivity extends ViewSelectorActivity { } else { final View view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.action_send_item, container, false); - final ImageView confirmbutton = view.findViewById(R.id.confirmbutton); - confirmbutton.setOnClickListener((View v) -> { - //check if it can happen that the fagment is never created that hold data? + final ImageView confirmButton = view.findViewById(R.id.confirmbutton); + confirmButton.setOnClickListener((View v) -> { + //check if it can happen that the fragment is never created that hold data? // (you have to swipe past them anyways - but still) - String actionstring = "temptarget" - + " " + isMGDL - + " " + SafeParse.stringToInt(time.editText.getText().toString()) - + " " + SafeParse.stringToDouble(lowRange.editText.getText().toString()) - + " " + (isSingleTarget ? SafeParse.stringToDouble(lowRange.editText.getText().toString()) : SafeParse.stringToDouble(highRange.editText.getText().toString())); - - DataLayerListenerService.Companion.initiateAction(TempTargetActivity.this, actionstring); + EventData.ActionTempTargetPreCheck action = new EventData.ActionTempTargetPreCheck( + EventData.ActionTempTargetPreCheck.TempTargetCommand.MANUAL, + isMGDL, + SafeParse.stringToInt(time.editText.getText().toString()), + SafeParse.stringToDouble(lowRange.editText.getText().toString()), + (isSingleTarget ? + SafeParse.stringToDouble(lowRange.editText.getText().toString()) : SafeParse.stringToDouble(highRange.editText.getText().toString())) + ); + rxBus.send(new EventWearToMobile(action)); showToast(TempTargetActivity.this, R.string.action_tempt_confirmation); finishAffinity(); }); @@ -135,7 +139,7 @@ public class TempTargetActivity extends ViewSelectorActivity { @Override public void destroyItem(ViewGroup container, int row, int col, Object view) { // Handle this to get the data before the view is destroyed? - // Object should still be kept by this, just setup for reinit? + // Object should still be kept by this, just setup for re-init? container.removeView((View) view); } diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/TreatmentActivity.java b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/TreatmentActivity.java index 2b0f0dd4a6..fa353128ba 100644 --- a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/TreatmentActivity.java +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/TreatmentActivity.java @@ -10,11 +10,10 @@ import android.widget.ImageView; import java.text.DecimalFormat; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.DataLayerListenerService; -import info.nightscout.androidaps.events.EventWearToMobileAction; +import info.nightscout.androidaps.events.EventWearToMobile; import info.nightscout.androidaps.interaction.utils.PlusMinusEditText; import info.nightscout.shared.SafeParse; -import info.nightscout.shared.weardata.ActionData; +import info.nightscout.shared.weardata.EventData; /** * Created by adrian on 09/02/17. @@ -42,6 +41,7 @@ public class TreatmentActivity extends ViewSelectorActivity { } + @SuppressWarnings("deprecation") private class MyGridViewPagerAdapter extends GridPagerAdapter { @Override public int getColumnCount(int arg0) { @@ -80,12 +80,12 @@ public class TreatmentActivity extends ViewSelectorActivity { } else { final View view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.action_send_item, container, false); - final ImageView confirmbutton = view.findViewById(R.id.confirmbutton); - confirmbutton.setOnClickListener((View v) -> { + final ImageView confirmButton = view.findViewById(R.id.confirmbutton); + confirmButton.setOnClickListener((View v) -> { //check if it can happen that the fragment is never created that hold data? // (you have to swipe past them anyways - but still) - ActionData.Bolus bolus = new ActionData.Bolus(SafeParse.stringToDouble(editInsulin.editText.getText().toString()), SafeParse.stringToInt(editCarbs.editText.getText().toString())); - rxBus.send(new EventWearToMobileAction(bolus)); + EventData.ActionBolusPreCheck bolus = new EventData.ActionBolusPreCheck(SafeParse.stringToDouble(editInsulin.editText.getText().toString()), SafeParse.stringToInt(editCarbs.editText.getText().toString())); + rxBus.send(new EventWearToMobile(bolus)); showToast(TreatmentActivity.this, R.string.action_treatment_confirmation); finishAffinity(); }); @@ -97,7 +97,7 @@ public class TreatmentActivity extends ViewSelectorActivity { @Override public void destroyItem(ViewGroup container, int row, int col, Object view) { // Handle this to get the data before the view is destroyed? - // Object should still be kept by this, just setup for reinit? + // Object should still be kept by this, just setup for re-init? container.removeView((View) view); } diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/WizardActivity.java b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/WizardActivity.java index ac2ecb30fe..698c3fc59d 100644 --- a/wear/src/main/java/info/nightscout/androidaps/interaction/actions/WizardActivity.java +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/actions/WizardActivity.java @@ -10,9 +10,10 @@ import android.widget.ImageView; import java.text.DecimalFormat; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.DataLayerListenerService; +import info.nightscout.androidaps.events.EventWearToMobile; import info.nightscout.androidaps.interaction.utils.PlusMinusEditText; import info.nightscout.shared.SafeParse; +import info.nightscout.shared.weardata.EventData; /** * Created by adrian on 09/02/17. @@ -42,6 +43,7 @@ public class WizardActivity extends ViewSelectorActivity { finish(); } + @SuppressWarnings("deprecation") private class MyGridViewPagerAdapter extends GridPagerAdapter { @Override public int getColumnCount(int arg0) { @@ -82,15 +84,17 @@ public class WizardActivity extends ViewSelectorActivity { } else { final View view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.action_send_item, container, false); - final ImageView confirmbutton = view.findViewById(R.id.confirmbutton); - confirmbutton.setOnClickListener((View v) -> { + final ImageView confirmButton = view.findViewById(R.id.confirmbutton); + confirmButton.setOnClickListener((View v) -> { if (editPercentage != null) { percentage = SafeParse.stringToInt(editPercentage.editText.getText().toString()); } - String actionstring = "wizard2 " + SafeParse.stringToInt(editCarbs.editText.getText().toString()) - + " " + percentage; - DataLayerListenerService.Companion.initiateAction(WizardActivity.this, actionstring); + EventData.ActionWizardPreCheck action = new EventData.ActionWizardPreCheck( + SafeParse.stringToInt(editCarbs.editText.getText().toString()), + percentage + ); + rxBus.send(new EventWearToMobile(action)); showToast(WizardActivity.this, R.string.action_wizard_confirmation); finishAffinity(); }); @@ -102,7 +106,7 @@ public class WizardActivity extends ViewSelectorActivity { @Override public void destroyItem(ViewGroup container, int row, int col, Object view) { // Handle this to get the data before the view is destroyed? - // Object should still be kept by this, just setup for reinit? + // Object should still be kept by this, just setup for re-init? container.removeView((View) view); } diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/menus/FillMenuActivity.java b/wear/src/main/java/info/nightscout/androidaps/interaction/menus/FillMenuActivity.java deleted file mode 100644 index f11c887130..0000000000 --- a/wear/src/main/java/info/nightscout/androidaps/interaction/menus/FillMenuActivity.java +++ /dev/null @@ -1,51 +0,0 @@ -package info.nightscout.androidaps.interaction.menus; - -import android.content.Intent; -import android.os.Bundle; - -import java.util.ArrayList; -import java.util.List; - -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.DataLayerListenerService; -import info.nightscout.androidaps.interaction.actions.FillActivity; -import info.nightscout.androidaps.interaction.utils.MenuListActivity; - -/** - * Created by adrian on 09/02/17. - */ - -public class FillMenuActivity extends MenuListActivity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - setTitle(R.string.menu_prime_fill); - super.onCreate(savedInstanceState); - } - - @Override - protected List getElements() { - List menuItems = new ArrayList<>(); - menuItems.add(new MenuItem(R.drawable.ic_canula, getString(R.string.action_preset_1))); - menuItems.add(new MenuItem(R.drawable.ic_canula, getString(R.string.action_preset_2))); - menuItems.add(new MenuItem(R.drawable.ic_canula, getString(R.string.action_preset_3))); - menuItems.add(new MenuItem(R.drawable.ic_canula, getString(R.string.action_free_amount))); - - return menuItems; - } - - @Override - protected void doAction(String action) { - if (getString(R.string.action_preset_1).equals(action)) { - DataLayerListenerService.Companion.initiateAction(this, "fillpreset 1"); - } else if (getString(R.string.action_preset_2).equals(action)) { - DataLayerListenerService.Companion.initiateAction(this, "fillpreset 2"); - } else if (getString(R.string.action_preset_3).equals(action)) { - 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); - this.startActivity(intent); - } - } -} diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/menus/FillMenuActivity.kt b/wear/src/main/java/info/nightscout/androidaps/interaction/menus/FillMenuActivity.kt new file mode 100644 index 0000000000..56889fca22 --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/menus/FillMenuActivity.kt @@ -0,0 +1,34 @@ +package info.nightscout.androidaps.interaction.menus + +import android.content.Intent +import android.os.Bundle +import info.nightscout.androidaps.R +import info.nightscout.androidaps.events.EventWearToMobile +import info.nightscout.androidaps.interaction.actions.FillActivity +import info.nightscout.androidaps.interaction.utils.MenuListActivity +import info.nightscout.shared.weardata.EventData + +class FillMenuActivity : MenuListActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + setTitle(R.string.menu_prime_fill) + super.onCreate(savedInstanceState) + } + + override fun getElements(): List = + ArrayList().apply { + add(MenuItem(R.drawable.ic_canula, getString(R.string.action_preset_1))) + add(MenuItem(R.drawable.ic_canula, getString(R.string.action_preset_2))) + add(MenuItem(R.drawable.ic_canula, getString(R.string.action_preset_3))) + add(MenuItem(R.drawable.ic_canula, getString(R.string.action_free_amount))) + } + + override fun doAction(action: String) { + when (action) { + getString(R.string.action_preset_1) -> rxBus.send(EventWearToMobile(EventData.ActionFillPresetPreCheck(1))) + getString(R.string.action_preset_2) -> rxBus.send(EventWearToMobile(EventData.ActionFillPresetPreCheck(2))) + getString(R.string.action_preset_3) -> rxBus.send(EventWearToMobile(EventData.ActionFillPresetPreCheck(3))) + getString(R.string.action_free_amount) -> startActivity(Intent(this, FillActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) }) + } + } +} \ No newline at end of file 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 deleted file mode 100644 index 92f82f5920..0000000000 --- a/wear/src/main/java/info/nightscout/androidaps/interaction/menus/MainMenuActivity.java +++ /dev/null @@ -1,98 +0,0 @@ -package info.nightscout.androidaps.interaction.menus; - -import android.content.Intent; -import android.content.SharedPreferences; -import android.os.Bundle; -import android.preference.PreferenceManager; - -import java.util.ArrayList; -import java.util.List; - -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.DataLayerListenerService; -import info.nightscout.androidaps.interaction.AAPSPreferences; -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; - -/** - * Created by adrian on 09/02/17. - */ - -public class MainMenuActivity extends MenuListActivity { - - SharedPreferences sp; - - @Override - protected void onCreate(Bundle savedInstanceState) { - sp = PreferenceManager.getDefaultSharedPreferences(this); - setTitle(R.string.label_actions_activity); - super.onCreate(savedInstanceState); - DataLayerListenerService.Companion.requestData(this); - } - - @Override - protected List getElements() { - - List menuItems = new ArrayList<>(); - if (!sp.getBoolean("wearcontrol", false)) { - menuItems.add(new MenuItem(R.drawable.ic_settings, getString(R.string.menu_settings))); - menuItems.add(new MenuItem(R.drawable.ic_sync, getString(R.string.menu_resync))); - - return menuItems; - } - - boolean showPrimeFill = sp.getBoolean("primefill", false); - boolean showWizard = sp.getBoolean("showWizard", true); - - if (showWizard) menuItems.add(new MenuItem(R.drawable.ic_calculator, getString(R.string.menu_wizard))); - menuItems.add(new MenuItem(R.drawable.ic_e_carbs, getString(R.string.menu_ecarb))); - menuItems.add(new MenuItem(R.drawable.ic_treatment, getString(R.string.menu_treatment))); - menuItems.add(new MenuItem(R.drawable.ic_temptarget, getString(R.string.menu_tempt))); - menuItems.add(new MenuItem(R.drawable.ic_settings, getString(R.string.menu_settings))); - menuItems.add(new MenuItem(R.drawable.ic_status, getString(R.string.menu_status))); - if (showPrimeFill) menuItems.add(new MenuItem(R.drawable.ic_canula, getString(R.string.menu_prime_fill))); - - return menuItems; - } - - @Override - protected void doAction(String action) { - - Intent intent; - - if (getString(R.string.menu_settings).equals(action)) { - intent = new Intent(this, AAPSPreferences.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - this.startActivity(intent); - } else if (getString(R.string.menu_resync).equals(action)) { - 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); - this.startActivity(intent); - } else if (getString(R.string.menu_treatment).equals(action)) { - intent = new Intent(this, TreatmentActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - this.startActivity(intent); - } else if (getString(R.string.menu_wizard).equals(action)) { - intent = new Intent(this, WizardActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - this.startActivity(intent); - } else if (getString(R.string.menu_status).equals(action)) { - intent = new Intent(this, StatusMenuActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - this.startActivity(intent); - } else if (getString(R.string.menu_prime_fill).equals(action)) { - intent = new Intent(this, FillMenuActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - this.startActivity(intent); - } else if (getString(R.string.menu_ecarb).equals(action)) { - intent = new Intent(this, ECarbActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - this.startActivity(intent); - } - } -} diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/menus/MainMenuActivity.kt b/wear/src/main/java/info/nightscout/androidaps/interaction/menus/MainMenuActivity.kt new file mode 100644 index 0000000000..4f37ea33dd --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/menus/MainMenuActivity.kt @@ -0,0 +1,56 @@ +package info.nightscout.androidaps.interaction.menus + +import android.content.Intent +import android.os.Bundle +import info.nightscout.androidaps.R +import info.nightscout.androidaps.events.EventWearToMobile +import info.nightscout.androidaps.interaction.AAPSPreferences +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 +import info.nightscout.shared.weardata.EventData +import info.nightscout.shared.weardata.EventData.ActionResendData + +class MainMenuActivity : MenuListActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + setTitle(R.string.label_actions_activity) + super.onCreate(savedInstanceState) + rxBus.send(EventWearToMobile(ActionResendData("MainMenuListActivity"))) + } + + override fun getElements(): List = + ArrayList().apply { + if (!sp.getBoolean(R.string.key_wear_control, false)) { + add(MenuItem(R.drawable.ic_settings, getString(R.string.menu_settings))) + add(MenuItem(R.drawable.ic_sync, getString(R.string.menu_resync))) + } else { + if (sp.getBoolean(R.string.key_show_wizard, true)) + add(MenuItem(R.drawable.ic_calculator, getString(R.string.menu_wizard))) + add(MenuItem(R.drawable.ic_e_carbs, getString(R.string.menu_ecarb))) + add(MenuItem(R.drawable.ic_treatment, getString(R.string.menu_treatment))) + add(MenuItem(R.drawable.ic_temptarget, getString(R.string.menu_tempt))) + add(MenuItem(R.drawable.ic_status, getString(R.string.status_cpp))) + add(MenuItem(R.drawable.ic_settings, getString(R.string.menu_settings))) + add(MenuItem(R.drawable.ic_status, getString(R.string.menu_status))) + if (sp.getBoolean(R.string.key_prime_fill, false)) + add(MenuItem(R.drawable.ic_canula, getString(R.string.menu_prime_fill))) + } + } + + override fun doAction(action: String) { + when (action) { + getString(R.string.menu_settings) -> startActivity(Intent(this, AAPSPreferences::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) }) + getString(R.string.menu_resync) -> rxBus.send(EventWearToMobile(ActionResendData("Re-Sync"))) + getString(R.string.status_cpp) -> rxBus.send(EventWearToMobile(EventData.ActionProfileSwitchSendInitialData(System.currentTimeMillis()))) + getString(R.string.menu_tempt) -> startActivity(Intent(this, TempTargetActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) }) + getString(R.string.menu_treatment) -> startActivity(Intent(this, TreatmentActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) }) + getString(R.string.menu_wizard) -> startActivity(Intent(this, WizardActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) }) + getString(R.string.menu_status) -> startActivity(Intent(this, StatusMenuActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) }) + getString(R.string.menu_prime_fill) -> startActivity(Intent(this, FillMenuActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) }) + getString(R.string.menu_ecarb) -> startActivity(Intent(this, ECarbActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) }) + } + } +} \ No newline at end of file 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 deleted file mode 100644 index 6f9c5586c8..0000000000 --- a/wear/src/main/java/info/nightscout/androidaps/interaction/menus/StatusMenuActivity.java +++ /dev/null @@ -1,47 +0,0 @@ -package info.nightscout.androidaps.interaction.menus; - -import android.os.Bundle; - -import java.util.ArrayList; -import java.util.List; - -import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.DataLayerListenerService; -import info.nightscout.androidaps.interaction.utils.MenuListActivity; - -/** - * Created by adrian on 09/02/17. - */ - -public class StatusMenuActivity extends MenuListActivity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - setTitle(R.string.menu_status); - super.onCreate(savedInstanceState); - } - - @Override - protected List getElements() { - List menuitems = new ArrayList<>(); - menuitems.add(new MenuItem(R.drawable.ic_status, getString(R.string.status_pump))); - menuitems.add(new MenuItem(R.drawable.ic_loop_closed, getString(R.string.status_loop))); - menuitems.add(new MenuItem(R.drawable.ic_status, getString(R.string.status_cpp))); - menuitems.add(new MenuItem(R.drawable.ic_tdd, getString(R.string.status_tdd))); - - return menuitems; - } - - @Override - protected void doAction(String action) { - if (getString(R.string.status_pump).equals(action)) { - DataLayerListenerService.Companion.initiateAction(this, "status pump"); - } else if (getString(R.string.status_loop).equals(action)) { - DataLayerListenerService.Companion.initiateAction(this, "status loop"); - } else if (getString(R.string.status_cpp).equals(action)) { - DataLayerListenerService.Companion.initiateAction(this, "opencpp"); - } else if (getString(R.string.status_tdd).equals(action)) { - DataLayerListenerService.Companion.initiateAction(this, "tddstats"); - } - } -} diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/menus/StatusMenuActivity.kt b/wear/src/main/java/info/nightscout/androidaps/interaction/menus/StatusMenuActivity.kt new file mode 100644 index 0000000000..38ffeef840 --- /dev/null +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/menus/StatusMenuActivity.kt @@ -0,0 +1,32 @@ +package info.nightscout.androidaps.interaction.menus + +import android.os.Bundle +import info.nightscout.androidaps.R +import info.nightscout.androidaps.events.EventWearToMobile +import info.nightscout.androidaps.interaction.utils.MenuListActivity +import info.nightscout.shared.weardata.EventData.ActionLoopStatus +import info.nightscout.shared.weardata.EventData.ActionPumpStatus +import info.nightscout.shared.weardata.EventData.ActionTddStatus + +class StatusMenuActivity : MenuListActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + setTitle(R.string.menu_status) + super.onCreate(savedInstanceState) + } + + override fun getElements(): List = + ArrayList().apply { + add(MenuItem(R.drawable.ic_status, getString(R.string.status_pump))) + add(MenuItem(R.drawable.ic_loop_closed, getString(R.string.status_loop))) + add(MenuItem(R.drawable.ic_tdd, getString(R.string.status_tdd))) + } + + override fun doAction(action: String) { + when (action) { + getString(R.string.status_pump) -> rxBus.send(EventWearToMobile(ActionPumpStatus(System.currentTimeMillis()))) + getString(R.string.status_loop) -> rxBus.send(EventWearToMobile(ActionLoopStatus(System.currentTimeMillis()))) + getString(R.string.status_tdd) -> rxBus.send(EventWearToMobile(ActionTddStatus(System.currentTimeMillis()))) + } + } +} \ No newline at end of file diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/utils/DisplayFormat.java b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/DisplayFormat.java index bbdfa0aa04..966b921db5 100644 --- a/wear/src/main/java/info/nightscout/androidaps/interaction/utils/DisplayFormat.java +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/DisplayFormat.java @@ -65,27 +65,27 @@ public class DisplayFormat { public String shortTrend(final RawDisplayData raw) { String minutes = "--"; - if (raw.datetime > 0) { - minutes = shortTimeSince(raw.datetime); + if (raw.getSingleBg().getTimeStamp() > 0) { + minutes = shortTimeSince(raw.getSingleBg().getTimeStamp()); } - if (minutes.length() + raw.sDelta.length() + deltaSymbol().length() + 1 <= MAX_FIELD_LEN_SHORT) { - return minutes + " " + deltaSymbol() + raw.sDelta; + if (minutes.length() + raw.getSingleBg().getDelta().length() + deltaSymbol().length() + 1 <= MAX_FIELD_LEN_SHORT) { + return minutes + " " + deltaSymbol() + raw.getSingleBg().getDelta(); } // that only optimizes obvious things like 0 before . or at end, + at beginning - String delta = (new SmallestDoubleString(raw.sDelta)).minimise(MAX_FIELD_LEN_SHORT -1); + String delta = (new SmallestDoubleString(raw.getSingleBg().getDelta())).minimise(MAX_FIELD_LEN_SHORT -1); if (minutes.length() + delta.length() + deltaSymbol().length() + 1 <= MAX_FIELD_LEN_SHORT) { return minutes + " " + deltaSymbol() + delta; } - String shortDelta = (new SmallestDoubleString(raw.sDelta)).minimise(MAX_FIELD_LEN_SHORT -(1+minutes.length())); + String shortDelta = (new SmallestDoubleString(raw.getSingleBg().getDelta())).minimise(MAX_FIELD_LEN_SHORT -(1+minutes.length())); return minutes + " " + shortDelta; } public String longGlucoseLine(final RawDisplayData raw) { - return raw.sSgv + raw.sDirection + " " + deltaSymbol() + (new SmallestDoubleString(raw.sDelta)).minimise(8) + " (" + shortTimeSince(raw.datetime) + ")"; + return raw.getSingleBg().getSgvString() + raw.getSingleBg().getSlopeArrow() + " " + deltaSymbol() + (new SmallestDoubleString(raw.getSingleBg().getDelta())).minimise(8) + " (" + shortTimeSince(raw.getSingleBg().getTimeStamp()) + ")"; } public String longDetailsLine(final RawDisplayData raw) { @@ -95,40 +95,41 @@ public class DisplayFormat { final int SEP_SHORT_LEN = SEP_SHORT.length(); final String SEP_MIN = " "; - String line = raw.sCOB2 + SEP_LONG + raw.sIOB1 + SEP_LONG + basalRateSymbol()+raw.sBasalRate; + String line = + raw.getStatus().getCob() + SEP_LONG + raw.getStatus().getIobSum() + SEP_LONG + basalRateSymbol()+raw.getStatus().getCurrentBasal(); if (line.length() <= MAX_FIELD_LEN_LONG) { return line; } - line = raw.sCOB2 + SEP_SHORT + raw.sIOB1 + SEP_SHORT + raw.sBasalRate; + line = raw.getStatus().getCob() + SEP_SHORT + raw.getStatus().getIobSum() + SEP_SHORT + raw.getStatus().getCurrentBasal(); if (line.length() <= MAX_FIELD_LEN_LONG) { return line; } - int remainingMax = MAX_FIELD_LEN_LONG - (raw.sCOB2.length() + raw.sBasalRate.length() + SEP_SHORT_LEN*2); - final String smallestIoB = new SmallestDoubleString(raw.sIOB1, SmallestDoubleString.Units.USE).minimise(Math.max(MIN_FIELD_LEN_IOB, remainingMax)); - line = raw.sCOB2 + SEP_SHORT + smallestIoB + SEP_SHORT + raw.sBasalRate; + int remainingMax = MAX_FIELD_LEN_LONG - (raw.getStatus().getCob().length() + raw.getStatus().getCurrentBasal().length() + SEP_SHORT_LEN*2); + final String smallestIoB = new SmallestDoubleString(raw.getStatus().getIobSum(), SmallestDoubleString.Units.USE).minimise(Math.max(MIN_FIELD_LEN_IOB, remainingMax)); + line = raw.getStatus().getCob() + SEP_SHORT + smallestIoB + SEP_SHORT + raw.getStatus().getCurrentBasal(); if (line.length() <= MAX_FIELD_LEN_LONG) { return line; } - remainingMax = MAX_FIELD_LEN_LONG - (smallestIoB.length() + raw.sBasalRate.length() + SEP_SHORT_LEN*2); - final String simplifiedCob = new SmallestDoubleString(raw.sCOB2, SmallestDoubleString.Units.USE).minimise(Math.max(MIN_FIELD_LEN_COB, remainingMax)); + remainingMax = MAX_FIELD_LEN_LONG - (smallestIoB.length() + raw.getStatus().getCurrentBasal().length() + SEP_SHORT_LEN*2); + final String simplifiedCob = new SmallestDoubleString(raw.getStatus().getCob(), SmallestDoubleString.Units.USE).minimise(Math.max(MIN_FIELD_LEN_COB, remainingMax)); - line = simplifiedCob + SEP_SHORT + smallestIoB + SEP_SHORT + raw.sBasalRate; + line = simplifiedCob + SEP_SHORT + smallestIoB + SEP_SHORT + raw.getStatus().getCurrentBasal(); if (line.length() <= MAX_FIELD_LEN_LONG) { return line; } - line = simplifiedCob + SEP_MIN + smallestIoB + SEP_MIN + raw.sBasalRate; + line = simplifiedCob + SEP_MIN + smallestIoB + SEP_MIN + raw.getStatus().getCurrentBasal(); return line; } public Pair detailedIob(RawDisplayData raw) { - final String iob1 = new SmallestDoubleString(raw.sIOB1, SmallestDoubleString.Units.USE).minimise(MAX_FIELD_LEN_SHORT); + final String iob1 = new SmallestDoubleString(raw.getStatus().getIobSum(), SmallestDoubleString.Units.USE).minimise(MAX_FIELD_LEN_SHORT); String iob2 = ""; - if (raw.sIOB2.contains("|")) { - String[] iobs = raw.sIOB2.replace("(", "").replace(")", "").split("\\|"); + if (raw.getStatus().getIobDetail().contains("|")) { + String[] iobs = raw.getStatus().getIobDetail().replace("(", "").replace(")", "").split("\\|"); String iobBolus = new SmallestDoubleString(iobs[0]).minimise(MIN_FIELD_LEN_IOB); if (iobBolus.trim().length() == 0) { @@ -144,7 +145,7 @@ public class DisplayFormat { } public Pair detailedCob(final RawDisplayData raw) { - SmallestDoubleString cobMini = new SmallestDoubleString(raw.sCOB2, SmallestDoubleString.Units.USE); + SmallestDoubleString cobMini = new SmallestDoubleString(raw.getStatus().getCob(), SmallestDoubleString.Units.USE); String cob2 = ""; if (cobMini.getExtra().length() > 0) { diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/utils/MenuListActivity.java b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/MenuListActivity.java index fb53413d1e..3f55afebd2 100644 --- a/wear/src/main/java/info/nightscout/androidaps/interaction/utils/MenuListActivity.java +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/MenuListActivity.java @@ -1,6 +1,5 @@ package info.nightscout.androidaps.interaction.utils; -import android.app.Activity; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -9,6 +8,7 @@ import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; +import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import androidx.wear.widget.CurvedTextView; import androidx.wear.widget.WearableLinearLayoutManager; @@ -16,13 +16,22 @@ import androidx.wear.widget.WearableRecyclerView; import java.util.List; +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 08/02/17. */ -public abstract class MenuListActivity extends Activity { +public abstract class MenuListActivity extends DaggerActivity { + + @Inject public RxBus rxBus; + @Inject public SP sp; + List elements; protected abstract List getElements(); @@ -96,7 +105,7 @@ public abstract class MenuListActivity extends Activity { } } - @Override + @NonNull @Override public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false); diff --git a/wear/src/main/java/info/nightscout/androidaps/interaction/utils/Persistence.java b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/Persistence.java index afb2b42bd2..a0856395d9 100644 --- a/wear/src/main/java/info/nightscout/androidaps/interaction/utils/Persistence.java +++ b/wear/src/main/java/info/nightscout/androidaps/interaction/utils/Persistence.java @@ -3,22 +3,20 @@ package info.nightscout.androidaps.interaction.utils; import android.content.Context; import android.content.SharedPreferences; import android.util.Base64; -import android.util.Log; import androidx.annotation.Nullable; -import com.google.android.gms.wearable.DataMap; - import java.util.HashSet; import java.util.Set; import javax.inject.Inject; import javax.inject.Singleton; -import info.nightscout.androidaps.Aaps; import info.nightscout.androidaps.complications.BaseComplicationProviderService; +import info.nightscout.androidaps.data.RawDisplayData; import info.nightscout.shared.logging.AAPSLogger; import info.nightscout.shared.logging.LTag; +import info.nightscout.shared.weardata.EventData; /** * Created by dlvoy on 2019-11-12 @@ -26,7 +24,6 @@ import info.nightscout.shared.logging.LTag; @Singleton public class Persistence { - private final Context context; private final AAPSLogger aapsLogger; private final WearUtil wearUtil; private final SharedPreferences preferences; @@ -35,7 +32,6 @@ public class Persistence { @Inject public Persistence(Context context, AAPSLogger aapsLogger, WearUtil wearUtil) { - this.context = context; this.aapsLogger = aapsLogger; this.wearUtil = wearUtil; preferences = context.getSharedPreferences(COMPLICATION_PROVIDER_PREFERENCES_FILE_KEY, 0); @@ -51,24 +47,6 @@ public class Persistence { return Base64.encodeToString(input, flags); } - @Nullable - public DataMap getDataMap(String key) { - if (preferences.contains(key)) { - final String rawB64Data = preferences.getString(key, null); - byte[] rawData = base64decode(rawB64Data, Base64.DEFAULT); - try { - return DataMap.fromByteArray(rawData); - } catch (IllegalArgumentException ex) { - // Should never happen, and if it happen - we ignore and fallback to null - } - } - return null; - } - - public void putDataMap(String key, DataMap dataMap) { - preferences.edit().putString(key, base64encodeToString(dataMap.toByteArray(), Base64.DEFAULT)).apply(); - } - public String getString(String key, String defaultValue) { return preferences.getString(key, defaultValue); } @@ -109,11 +87,67 @@ public class Persistence { putString(key, joinSet(set, "|")); } - public void storeDataMap(String key, DataMap dataMap) { - putDataMap(key, dataMap); + public void store(EventData.SingleBg singleBg) { + putString(RawDisplayData.BG_DATA_PERSISTENCE_KEY, singleBg.serialize()); markDataUpdated(); } + @Nullable + public EventData.SingleBg readSingleBg() { + try { + String s = getString(RawDisplayData.BG_DATA_PERSISTENCE_KEY, null); + if (s != null) { + return (EventData.SingleBg) EventData.Companion.deserialize(s); + } + } catch (Exception ignored) {} + return null; + } + + @Nullable + public EventData.Status readStatus() { + try { + String s = getString(RawDisplayData.STATUS_PERSISTENCE_KEY, null); + if (s != null) { + return (EventData.Status) EventData.Companion.deserialize(s); + } + } catch (Exception ignored) {} + return null; + } + + @Nullable + public EventData.TreatmentData readTreatments() { + try { + String s = getString(RawDisplayData.BASALS_PERSISTENCE_KEY, null); + if (s != null) { + return (EventData.TreatmentData) EventData.Companion.deserialize(s); + } + } catch (Exception ignored) {} + return null; + } + + @Nullable + public EventData.GraphData readGraphData() { + try { + String s = getString(RawDisplayData.BG_DATA_PERSISTENCE_KEY, null); + if (s != null) { + return (EventData.GraphData) EventData.Companion.deserialize(s); + } + } catch (Exception ignored) {} + return null; + } + + public void store(EventData.GraphData graphData) { + putString(RawDisplayData.GRAPH_DATA_PERSISTENCE_KEY, graphData.serialize()); + } + + public void store(EventData.TreatmentData treatmentData) { + putString(RawDisplayData.BASALS_PERSISTENCE_KEY, treatmentData.serialize()); + } + + public void store(EventData.Status status) { + putString(RawDisplayData.STATUS_PERSISTENCE_KEY, status.serialize()); + } + public String joinSet(Set set, String separator) { StringBuilder sb = new StringBuilder(); int i = 0; diff --git a/wear/src/main/java/info/nightscout/androidaps/tile/QuickWizardSource.kt b/wear/src/main/java/info/nightscout/androidaps/tile/QuickWizardSource.kt index 36e2f224fb..b03123076f 100644 --- a/wear/src/main/java/info/nightscout/androidaps/tile/QuickWizardSource.kt +++ b/wear/src/main/java/info/nightscout/androidaps/tile/QuickWizardSource.kt @@ -2,93 +2,64 @@ package info.nightscout.androidaps.tile import android.content.Context import android.content.res.Resources -import android.util.Base64 -import android.util.Log -import androidx.preference.PreferenceManager -import com.google.android.gms.wearable.DataMap import info.nightscout.androidaps.R import info.nightscout.androidaps.interaction.actions.BackgroundActionActivity +import info.nightscout.shared.logging.AAPSLogger +import info.nightscout.shared.logging.LTag +import info.nightscout.shared.sharedPreferences.SP +import info.nightscout.shared.weardata.EventData import java.util.* object QuickWizardSource : TileSource { - override fun getSelectedActions(context: Context): List { + override fun getSelectedActions(context: Context, sp: SP, aapsLogger: AAPSLogger): List { val quickList = mutableListOf() - val quickMap = getDataMap(context) + val quickMap = getQuickWizardData(sp) val sfm = secondsFromMidnight() - for (quick in quickMap) { - val validFrom = quick.getInt("from", 0) - val validTo = quick.getInt("to", 0) - val isActive = sfm in validFrom..validTo - val guid = quick.getString("guid", "") - if (isActive && guid != "") { + for (quick in quickMap.entries) { + val isActive = sfm in quick.validFrom..quick.validTo + if (isActive && quick.guid.isNotEmpty()) { quickList.add( Action( - buttonText = quick.getString("button_text", "?"), - buttonTextSub = "${quick.getInt("carbs", 0)} g", + buttonText = quick.buttonText, + buttonTextSub = "${quick.carbs} g", iconRes = R.drawable.ic_quick_wizard, activityClass = BackgroundActionActivity::class.java.name, - actionString = "quick_wizard $guid", - message = context.resources.getString(R.string.action_quick_wizard_confirmation), + action = EventData.ActionQuickWizardPreCheck(quick.guid), + message = context.resources.getString(R.string.action_quick_wizard_confirmation) ) ) - Log.i(TAG, "getSelectedActions: active " + quick.getString("button_text", "?") + " guid=" + guid) + aapsLogger.info(LTag.WEAR, """getSelectedActions: active ${quick.buttonText} guid=${quick.guid}""") } else { - Log.i(TAG, "getSelectedActions: not active " + quick.getString("button_text", "?") + " guid=" + guid) + aapsLogger.info(LTag.WEAR, """getSelectedActions: not active ${quick.buttonText} guid=${quick.guid}""") } - } - return quickList } - override fun getValidFor(context: Context): Long? { - val quickMap = getDataMap(context) - if (quickMap.size == 0) { - return null - } + override fun getValidFor(sp: SP): Long? { + val quickMap = getQuickWizardData(sp) + if (quickMap.entries.size == 0) return null + val sfm = secondsFromMidnight() var validTill = 24 * 60 * 60 - for (quick in quickMap) { - val validFrom = quick.getInt("from", 0) - val validTo = quick.getInt("to", 0) - val isActive = sfm in validFrom..validTo - val guid = quick.getString("guid", "") - Log.i(TAG, "valid: " + validFrom + "-" + validTo) - if (guid != "") { - if (isActive && validTill > validTo) { - validTill = validTo - } - if (validFrom > sfm && validTill > validFrom) { - validTill = validFrom - } + for (quick in quickMap.entries) { + val isActive = sfm in quick.validFrom..quick.validTo + if (quick.guid.isNotEmpty()) { + if (isActive && validTill > quick.validTo) validTill = quick.validTo + if (quick.validFrom in (sfm + 1) until validTill) validTill = quick.validFrom } } val validWithin = 60 - val delta = (validTill - sfm + validWithin) * 1000L - Log.i(TAG, "getValidTill: sfm" + sfm + " till" + validTill + " d=" + delta) - return delta + //aapsLogger.info(LTag.WEAR, "getValidTill: sfm$sfm till$validTill d=$delta") + return (validTill - sfm + validWithin) * 1000L } - private fun getDataMap(context: Context): ArrayList { - val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context) - val key = context.resources.getString(R.string.key_quick_wizard_data_map) - if (sharedPrefs.contains(key)) { - val rawB64Data: String? = sharedPrefs.getString(key, null) - val rawData: ByteArray = Base64.decode(rawB64Data, Base64.DEFAULT) - try { - val map = DataMap.fromByteArray(rawData) - return map.getDataMapArrayList("quick_wizard") - - } catch (ex: IllegalArgumentException) { - Log.e(TAG, "getSelectedActions: IllegalArgumentException ", ex) - } - } - return arrayListOf() - } + private fun getQuickWizardData(sp: SP): EventData.QuickWizard = + EventData.deserialize(sp.getString(R.string.key_quick_wizard_data, EventData.QuickWizard(arrayListOf()).serialize())) as EventData.QuickWizard private fun secondsFromMidnight(): Int { val c = Calendar.getInstance() @@ -101,8 +72,5 @@ object QuickWizardSource : TileSource { return (passed / 1000).toInt() } - override fun getResourceReferences(resources: Resources): List { - return listOf(R.drawable.ic_quick_wizard) - } - + override fun getResourceReferences(resources: Resources): List = listOf(R.drawable.ic_quick_wizard) } diff --git a/wear/src/main/java/info/nightscout/androidaps/tile/StaticTileSource.kt b/wear/src/main/java/info/nightscout/androidaps/tile/StaticTileSource.kt index c1f369275c..5f27d2e367 100644 --- a/wear/src/main/java/info/nightscout/androidaps/tile/StaticTileSource.kt +++ b/wear/src/main/java/info/nightscout/androidaps/tile/StaticTileSource.kt @@ -5,6 +5,9 @@ import android.content.SharedPreferences import android.content.res.Resources import androidx.annotation.DrawableRes import androidx.preference.PreferenceManager +import info.nightscout.shared.logging.AAPSLogger +import info.nightscout.shared.sharedPreferences.SP +import info.nightscout.shared.weardata.EventData class StaticAction( val settingName: String, @@ -12,9 +15,9 @@ class StaticAction( buttonTextSub: String? = null, activityClass: String, @DrawableRes iconRes: Int, - actionString: String? = null, + action: EventData? = null, message: String? = null, -) : Action(buttonText, buttonTextSub, activityClass, iconRes, actionString, message) +) : Action(buttonText, buttonTextSub, activityClass, iconRes, action, message) abstract class StaticTileSource : TileSource { @@ -23,7 +26,7 @@ abstract class StaticTileSource : TileSource { abstract val preferencePrefix: String abstract fun getDefaultConfig(): Map - override fun getSelectedActions(context: Context): List { + override fun getSelectedActions(context: Context, sp: SP, aapsLogger: AAPSLogger): List { val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context) setDefaultSettings(sharedPrefs) @@ -40,7 +43,7 @@ abstract class StaticTileSource : TileSource { return actionList } - override fun getValidFor(context: Context): Long? = null + override fun getValidFor(sp: SP): Long? = null private fun getActionFromPreference(resources: Resources, sharedPrefs: SharedPreferences, index: Int): Action? { val actionPref = sharedPrefs.getString(preferencePrefix + index, "none") diff --git a/wear/src/main/java/info/nightscout/androidaps/tile/TempTargetSource.kt b/wear/src/main/java/info/nightscout/androidaps/tile/TempTargetSource.kt index 75cdbca36c..48c39de5de 100644 --- a/wear/src/main/java/info/nightscout/androidaps/tile/TempTargetSource.kt +++ b/wear/src/main/java/info/nightscout/androidaps/tile/TempTargetSource.kt @@ -4,6 +4,7 @@ import android.content.res.Resources import info.nightscout.androidaps.R import info.nightscout.androidaps.interaction.actions.BackgroundActionActivity import info.nightscout.androidaps.interaction.actions.TempTargetActivity +import info.nightscout.shared.weardata.EventData object TempTargetSource : StaticTileSource() { @@ -19,7 +20,8 @@ object TempTargetSource : StaticTileSource() { activityClass = BackgroundActionActivity::class.java.name, message = message, // actionString = "temptarget false 90 8.0 8.0", - actionString = "temptarget preset activity", + // actionString = "temptarget preset activity", + action = EventData.ActionTempTargetPreCheck(EventData.ActionTempTargetPreCheck.TempTargetCommand.PRESET_ACTIVITY) ), StaticAction( settingName = "eating_soon", @@ -28,7 +30,8 @@ object TempTargetSource : StaticTileSource() { activityClass = BackgroundActionActivity::class.java.name, message = message, // actionString = "temptarget false 45 4.5 4.5", - actionString = "temptarget preset eating", + // actionString = "temptarget preset eating", + action = EventData.ActionTempTargetPreCheck(EventData.ActionTempTargetPreCheck.TempTargetCommand.PRESET_EATING) ), StaticAction( settingName = "hypo", @@ -37,13 +40,15 @@ object TempTargetSource : StaticTileSource() { activityClass = BackgroundActionActivity::class.java.name, message = message, // actionString = "temptarget false 45 7.0 7.0", - actionString = "temptarget preset hypo", + // actionString = "temptarget preset hypo", + action = EventData.ActionTempTargetPreCheck(EventData.ActionTempTargetPreCheck.TempTargetCommand.PRESET_HYPO) ), StaticAction( settingName = "manual", buttonText = resources.getString(R.string.temp_target_manual), iconRes = R.drawable.ic_target_manual, activityClass = TempTargetActivity::class.java.name, + action = null ), StaticAction( settingName = "cancel", @@ -51,7 +56,8 @@ object TempTargetSource : StaticTileSource() { iconRes = R.drawable.ic_target_cancel, activityClass = BackgroundActionActivity::class.java.name, message = message, - actionString = "temptarget cancel", + //actionString = "temptarget cancel", + action = EventData.ActionTempTargetPreCheck(EventData.ActionTempTargetPreCheck.TempTargetCommand.CANCEL) ) ) } diff --git a/wear/src/main/java/info/nightscout/androidaps/tile/TileBase.kt b/wear/src/main/java/info/nightscout/androidaps/tile/TileBase.kt index 031dbf223e..7fd3cbf5c9 100644 --- a/wear/src/main/java/info/nightscout/androidaps/tile/TileBase.kt +++ b/wear/src/main/java/info/nightscout/androidaps/tile/TileBase.kt @@ -5,11 +5,10 @@ import android.os.Build import androidx.annotation.DrawableRes import androidx.annotation.RequiresApi import androidx.core.content.ContextCompat -import androidx.preference.PreferenceManager import androidx.wear.tiles.ActionBuilders import androidx.wear.tiles.ColorBuilders.argb -import androidx.wear.tiles.DeviceParametersBuilders.SCREEN_SHAPE_ROUND import androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters +import androidx.wear.tiles.DeviceParametersBuilders.SCREEN_SHAPE_ROUND import androidx.wear.tiles.DimensionBuilders.SpProp import androidx.wear.tiles.DimensionBuilders.dp import androidx.wear.tiles.DimensionBuilders.sp @@ -29,11 +28,17 @@ import androidx.wear.tiles.TileService import androidx.wear.tiles.TimelineBuilders.Timeline import androidx.wear.tiles.TimelineBuilders.TimelineEntry import com.google.common.util.concurrent.ListenableFuture +import dagger.android.AndroidInjection import info.nightscout.androidaps.R +import info.nightscout.androidaps.comm.DataLayerListenerServiceWear +import info.nightscout.shared.logging.AAPSLogger +import info.nightscout.shared.sharedPreferences.SP +import info.nightscout.shared.weardata.EventData import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.guava.future +import javax.inject.Inject import kotlin.math.sqrt private const val SPACING_ACTIONS = 3f @@ -44,8 +49,8 @@ private const val LARGE_SCREEN_WIDTH_DP = 210 interface TileSource { fun getResourceReferences(resources: android.content.res.Resources): List - fun getSelectedActions(context: Context): List - fun getValidFor(context: Context): Long? + fun getSelectedActions(context: Context, sp: SP, aapsLogger: AAPSLogger): List + fun getValidFor(sp: SP): Long? } open class Action( @@ -53,7 +58,7 @@ open class Action( val buttonTextSub: String? = null, val activityClass: String, @DrawableRes val iconRes: Int, - val actionString: String? = null, + val action: EventData? = null, val message: String? = null, ) @@ -63,12 +68,21 @@ enum class WearControl { abstract class TileBase : TileService() { + @Inject lateinit var sp: SP + @Inject lateinit var aapsLogger: AAPSLogger + abstract val resourceVersion: String abstract val source: TileSource private val serviceJob = Job() private val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob) + // Not derived from DaggerService, do injection here + override fun onCreate() { + AndroidInjection.inject(this) + super.onCreate() + } + override fun onTileRequest( requestParams: RequestBuilders.TileRequest ): ListenableFuture = serviceScope.future { @@ -93,11 +107,11 @@ abstract class TileBase : TileService() { private fun getSelectedActions(): List { // TODO check why thi scan not be don in scope of the coroutine - return source.getSelectedActions(this) + return source.getSelectedActions(this, sp, aapsLogger) } private fun validFor(): Long? { - return source.getValidFor(this) + return source.getValidFor(sp) } @RequiresApi(Build.VERSION_CODES.N) @@ -172,13 +186,13 @@ abstract class TileBase : TileService() { val builder = ActionBuilders.AndroidActivity.Builder() .setClassName(action.activityClass) .setPackageName(this.packageName) - if (action.actionString != null) { - val actionString = ActionBuilders.AndroidStringExtra.Builder().setValue(action.actionString).build() - builder.addKeyToExtraMapping("actionString", actionString) + if (action.action != null) { + val actionString = ActionBuilders.AndroidStringExtra.Builder().setValue(action.action.serialize()).build() + builder.addKeyToExtraMapping(DataLayerListenerServiceWear.KEY_ACTION, actionString) } if (action.message != null) { val message = ActionBuilders.AndroidStringExtra.Builder().setValue(action.message).build() - builder.addKeyToExtraMapping("message", message) + builder.addKeyToExtraMapping(DataLayerListenerServiceWear.KEY_MESSAGE, message) } return ActionBuilders.LaunchAction.Builder() @@ -197,12 +211,8 @@ abstract class TileBase : TileService() { Modifiers.Builder() .setBackground( Background.Builder() - .setColor( - argb(ContextCompat.getColor(baseContext, BUTTON_COLOR)) - ) - .setCorner( - Corner.Builder().setRadius(dp(circleDiameter / 2)).build() - ) + .setColor(argb(ContextCompat.getColor(baseContext, BUTTON_COLOR))) + .setCorner(Corner.Builder().setRadius(dp(circleDiameter / 2)).build()) .build() ) .setSemantics( @@ -239,9 +249,7 @@ abstract class TileBase : TileService() { .setFontStyle( FontStyle.Builder() .setWeight(FONT_WEIGHT_BOLD) - .setColor( - argb(ContextCompat.getColor(baseContext, R.color.white)) - ) + .setColor(argb(ContextCompat.getColor(baseContext, R.color.white))) .setSize(buttonTextSize(deviceParameters, text)) .build() ) @@ -253,9 +261,7 @@ abstract class TileBase : TileService() { .setText(textSub) .setFontStyle( FontStyle.Builder() - .setColor( - argb(ContextCompat.getColor(baseContext, R.color.white)) - ) + .setColor(argb(ContextCompat.getColor(baseContext, R.color.white))) .setSize(buttonTextSize(deviceParameters, textSub)) .build() ) @@ -283,11 +289,10 @@ abstract class TileBase : TileService() { } private fun getWearControl(): WearControl { - val sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this) - if (!sharedPrefs.contains("wearcontrol")) { + if (!sp.contains("wearcontrol")) { return WearControl.NO_DATA } - val wearControlPref = sharedPrefs.getBoolean("wearcontrol", false) + val wearControlPref = sp.getBoolean("wearcontrol", false) if (wearControlPref) { return WearControl.ENABLED } 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 618dc68998..cd56d3fd0a 100644 --- a/wear/src/main/java/info/nightscout/androidaps/watchfaces/BIGChart.java +++ b/wear/src/main/java/info/nightscout/androidaps/watchfaces/BIGChart.java @@ -1,29 +1,18 @@ package info.nightscout.androidaps.watchfaces; -import android.content.BroadcastReceiver; +import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; -import android.content.SharedPreferences; import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.LinearGradient; -import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Point; import android.graphics.Rect; -import android.graphics.Shader; -import android.os.Bundle; import android.os.PowerManager; -import android.os.SystemClock; -import android.preference.PreferenceManager; -import androidx.core.content.ContextCompat; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; import android.support.wearable.view.WatchViewStub; import android.support.wearable.watchface.WatchFaceStyle; import android.text.format.DateFormat; import android.util.DisplayMetrics; -import android.util.Log; import android.view.Display; import android.view.LayoutInflater; import android.view.View; @@ -32,7 +21,8 @@ import android.view.WindowManager; import android.widget.RelativeLayout; import android.widget.TextView; -import com.google.android.gms.wearable.DataMap; +import androidx.core.content.ContextCompat; + import com.ustwo.clockwise.common.WatchFaceTime; import com.ustwo.clockwise.common.WatchMode; import com.ustwo.clockwise.common.WatchShape; @@ -40,25 +30,43 @@ import com.ustwo.clockwise.wearable.WatchFace; import java.util.ArrayList; +import javax.inject.Inject; + +import dagger.android.AndroidInjection; 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.DataLayerListenerService; -import info.nightscout.androidaps.data.TempWatchData; +import info.nightscout.androidaps.events.EventWearToMobile; import info.nightscout.androidaps.interaction.menus.MainMenuActivity; +import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.utils.rx.AapsSchedulers; +import info.nightscout.shared.logging.AAPSLogger; +import info.nightscout.shared.logging.LTag; +import info.nightscout.shared.sharedPreferences.SP; +import info.nightscout.shared.weardata.EventData; +import io.reactivex.rxjava3.disposables.CompositeDisposable; import lecho.lib.hellocharts.view.LineChartView; /** * Created by adrianLxM. */ -public class BIGChart extends WatchFace implements SharedPreferences.OnSharedPreferenceChangeListener { - public final static IntentFilter INTENT_FILTER; - public static final int SCREENSIZE_SMALL = 280; - public TextView mTime, mSgv, mTimestamp, mDelta, mAvgDelta; - public RelativeLayout mRelativeLayout; - public long sgvLevel = 0; - public int batteryLevel = 1; +@SuppressWarnings("deprecation") +public class BIGChart extends WatchFace { + + @Inject RxBus rxBus; + @Inject AapsSchedulers aapsSchedulers; + @Inject AAPSLogger aapsLogger; + @Inject SP sp; + + CompositeDisposable disposable = new CompositeDisposable(); + + private EventData.SingleBg singleBg; + private EventData.Status status; + private EventData.TreatmentData treatmentData; + private EventData.GraphData graphData; + + private static final int SCREEN_SIZE_SMALL = 280; + private TextView mTime, mSgv, mTimestamp, mDelta, mAvgDelta; + private RelativeLayout mRelativeLayout; + public int ageLevel = 1; public int highColor = Color.YELLOW; public int lowColor = Color.RED; @@ -73,36 +81,21 @@ public class BIGChart extends WatchFace implements SharedPreferences.OnSharedPre public boolean layoutSet = false; public BgGraphBuilder bgGraphBuilder; public LineChartView chart; - public long datetime; - public ArrayList bgDataList = new ArrayList<>(); - public ArrayList tempWatchDataList = new ArrayList<>(); - public ArrayList basalWatchDataList = new ArrayList<>(); - public ArrayList bolusWatchDataList = new ArrayList<>(); - public ArrayList predictionList = new ArrayList<>(); + public ArrayList bgDataList = new ArrayList<>(); public PowerManager.WakeLock wakeLock; public View layoutView; private final Point displaySize = new Point(); private int specW, specH; - private int animationAngle = 0; - private boolean isAnimated = false; - - private LocalBroadcastManager localBroadcastManager; - private MessageReceiver messageReceiver; - - protected SharedPreferences sharedPrefs; - private String rawString = "000 | 000 | 000"; - private String batteryString = "--"; - private String sgvString = "--"; - private String externalStatusString = "no status"; - private String cobString = ""; private TextView statusView; private long chartTapTime = 0L; private long sgvTapTime = 0L; + @SuppressLint("InflateParams") @Override public void onCreate() { + AndroidInjection.inject(this); super.onCreate(); Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)) .getDefaultDisplay(); @@ -113,17 +106,71 @@ public class BIGChart extends WatchFace implements SharedPreferences.OnSharedPre View.MeasureSpec.EXACTLY); specH = View.MeasureSpec.makeMeasureSpec(displaySize.y, View.MeasureSpec.EXACTLY); - sharedPrefs = PreferenceManager - .getDefaultSharedPreferences(this); - sharedPrefs.registerOnSharedPreferenceChangeListener(this); LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); DisplayMetrics metrics = getResources().getDisplayMetrics(); - if(metrics.widthPixels < SCREENSIZE_SMALL || metrics.heightPixels < SCREENSIZE_SMALL){ + if (metrics.widthPixels < SCREEN_SIZE_SMALL || metrics.heightPixels < SCREEN_SIZE_SMALL) { layoutView = inflater.inflate(R.layout.activity_bigchart_small, null); } else { layoutView = inflater.inflate(R.layout.activity_bigchart, null); } performViewSetup(); + disposable.add(rxBus + .toObservable(EventData.SingleBg.class) + .observeOn(aapsSchedulers.getMain()) + .subscribe(event -> { + aapsLogger.debug(LTag.WEAR, "SingleBg received"); + singleBg = event; + + mSgv.setText(singleBg.getSgvString()); + if (ageLevel() <= 0) + mSgv.setPaintFlags(mSgv.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); + else mSgv.setPaintFlags(mSgv.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG); + final java.text.DateFormat timeFormat = DateFormat.getTimeFormat(BIGChart.this); + mTime.setText(timeFormat.format(System.currentTimeMillis())); + mDelta.setText(singleBg.getDelta()); + mAvgDelta.setText(singleBg.getAvgDelta()); + }) + ); + disposable.add(rxBus + .toObservable(EventData.TreatmentData.class) + .observeOn(aapsSchedulers.getMain()) + .subscribe(event -> treatmentData = event) + ); + disposable.add(rxBus + .toObservable(EventData.GraphData.class) + .observeOn(aapsSchedulers.getMain()) + .subscribe(event -> graphData = event) + ); + disposable.add(rxBus + .toObservable(EventData.Status.class) + .observeOn(aapsSchedulers.getMain()) + .subscribe(event -> { + // this event is received as last batch of data + aapsLogger.debug(LTag.WEAR, "Status received"); + status = event; + showAgeAndStatus(); + addToWatchSet(); + mRelativeLayout.measure(specW, specH); + mRelativeLayout.layout(0, 0, mRelativeLayout.getMeasuredWidth(), + mRelativeLayout.getMeasuredHeight()); + invalidate(); + setColor(); + }) + ); + disposable.add(rxBus + .toObservable(EventData.Preferences.class) + .observeOn(aapsSchedulers.getMain()) + .subscribe(event -> { + setColor(); + if (layoutSet) { + showAgeAndStatus(); + mRelativeLayout.measure(specW, specH); + mRelativeLayout.layout(0, 0, mRelativeLayout.getMeasuredWidth(), + mRelativeLayout.getMeasuredHeight()); + } + invalidate(); + }) + ); } @Override @@ -134,54 +181,46 @@ public class BIGChart extends WatchFace implements SharedPreferences.OnSharedPre public void performViewSetup() { final WatchViewStub stub = layoutView.findViewById(R.id.watch_view_stub); - IntentFilter messageFilter = new IntentFilter(Intent.ACTION_SEND); - messageReceiver = new MessageReceiver(); - localBroadcastManager = LocalBroadcastManager.getInstance(this); - localBroadcastManager.registerReceiver(messageReceiver, messageFilter); - - stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() { - @Override - public void onLayoutInflated(WatchViewStub stub) { - mTime = stub.findViewById(R.id.watch_time); - mSgv = stub.findViewById(R.id.sgv); - mTimestamp = stub.findViewById(R.id.timestamp); - mDelta = stub.findViewById(R.id.delta); - mAvgDelta = stub.findViewById(R.id.avgdelta); - mRelativeLayout = stub.findViewById(R.id.main_layout); - chart = stub.findViewById(R.id.chart); - statusView = stub.findViewById(R.id.aps_status); - layoutSet = true; - showAgeAndStatus(); - mRelativeLayout.measure(specW, specH); - mRelativeLayout.layout(0, 0, mRelativeLayout.getMeasuredWidth(), - mRelativeLayout.getMeasuredHeight()); - } + stub.setOnLayoutInflatedListener(stub1 -> { + mTime = stub1.findViewById(R.id.watch_time); + mSgv = stub1.findViewById(R.id.sgv); + mTimestamp = stub1.findViewById(R.id.timestamp); + mDelta = stub1.findViewById(R.id.delta); + mAvgDelta = stub1.findViewById(R.id.avgdelta); + mRelativeLayout = stub1.findViewById(R.id.main_layout); + chart = stub1.findViewById(R.id.chart); + statusView = stub1.findViewById(R.id.aps_status); + layoutSet = true; + showAgeAndStatus(); + mRelativeLayout.measure(specW, specH); + mRelativeLayout.layout(0, 0, mRelativeLayout.getMeasuredWidth(), + mRelativeLayout.getMeasuredHeight()); }); - DataLayerListenerService.Companion.requestData(this); + rxBus.send(new EventWearToMobile(new EventData.ActionResendData("BIGChart:performViewSetup"))); wakeLock.acquire(50); } @Override protected void onTapCommand(int tapType, int x, int y, long eventTime) { - int extra = mSgv!=null?(mSgv.getRight() - mSgv.getLeft())/2:0; + int extra = mSgv != null ? (mSgv.getRight() - mSgv.getLeft()) / 2 : 0; - if (tapType == TAP_TYPE_TAP&& - x >=chart.getLeft() && - x <= chart.getRight()&& + if (tapType == TAP_TYPE_TAP && + x >= chart.getLeft() && + x <= chart.getRight() && y >= chart.getTop() && - y <= chart.getBottom()){ - if (eventTime - chartTapTime < 800){ + y <= chart.getBottom()) { + if (eventTime - chartTapTime < 800) { changeChartTimeframe(); } chartTapTime = eventTime; - } else if (tapType == TAP_TYPE_TAP&& - x + extra >=mSgv.getLeft() && - x - extra <= mSgv.getRight()&& + } else if (tapType == TAP_TYPE_TAP && + x + extra >= mSgv.getLeft() && + x - extra <= mSgv.getRight() && y >= mSgv.getTop() && - y <= mSgv.getBottom()){ - if (eventTime - sgvTapTime < 800){ + y <= mSgv.getBottom()) { + if (eventTime - sgvTapTime < 800) { Intent intent = new Intent(this, MainMenuActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); @@ -191,17 +230,17 @@ public class BIGChart extends WatchFace implements SharedPreferences.OnSharedPre } private void changeChartTimeframe() { - int timeframe = Integer.parseInt(sharedPrefs.getString("chart_timeframe", "3")); - timeframe = (timeframe%5) + 1; - sharedPrefs.edit().putString("chart_timeframe", "" + timeframe).apply(); + int timeframe = sp.getInt("chart_timeframe", 3); + timeframe = (timeframe % 5) + 1; + sp.putInt("chart_timeframe", timeframe); } protected void onWatchModeChanged(WatchMode watchMode) { - if(lowResMode ^ isLowRes(watchMode)){ //if there was a change in lowResMode + if (lowResMode ^ isLowRes(watchMode)) { //if there was a change in lowResMode lowResMode = isLowRes(watchMode); setColor(); - } else if (! sharedPrefs.getBoolean("dark", true)){ + } else if (!sp.getBoolean("dark", true)) { //in bright mode: different colours if active: setColor(); } @@ -213,14 +252,13 @@ public class BIGChart extends WatchFace implements SharedPreferences.OnSharedPre @Override - protected WatchFaceStyle getWatchFaceStyle(){ + protected WatchFaceStyle getWatchFaceStyle() { return new WatchFaceStyle.Builder(this).setAcceptsTapEvents(true).build(); } - public int ageLevel() { - if(timeSince() <= (1000 * 60 * 12)) { + if (timeSince() <= (1000 * 60 * 12)) { return 1; } else { return 0; @@ -228,40 +266,30 @@ public class BIGChart extends WatchFace implements SharedPreferences.OnSharedPre } public double timeSince() { - return System.currentTimeMillis() - datetime; + return System.currentTimeMillis() - singleBg.getTimeStamp(); } public String readingAge(boolean shortString) { - if (datetime == 0) { return shortString?"--'":"-- Minute ago"; } - int minutesAgo = (int) Math.floor(timeSince()/(1000*60)); - if (minutesAgo == 1) { - return minutesAgo + (shortString?"'":" Minute ago"); + if (singleBg == null || singleBg.getTimeStamp() == 0) { + return shortString ? "--'" : "-- Minute ago"; } - return minutesAgo + (shortString?"'":" Minutes ago"); + int minutesAgo = (int) Math.floor(timeSince() / (1000 * 60)); + if (minutesAgo == 1) { + return minutesAgo + (shortString ? "'" : " Minute ago"); + } + return minutesAgo + (shortString ? "'" : " Minutes ago"); } @Override public void onDestroy() { - if(localBroadcastManager != null && messageReceiver != null){ - localBroadcastManager.unregisterReceiver(messageReceiver);} - if (sharedPrefs != null){ - sharedPrefs.unregisterOnSharedPreferenceChangeListener(this); - } + disposable.clear(); super.onDestroy(); } - static { - INTENT_FILTER = new IntentFilter(); - INTENT_FILTER.addAction(Intent.ACTION_TIME_TICK); - INTENT_FILTER.addAction(Intent.ACTION_TIMEZONE_CHANGED); - INTENT_FILTER.addAction(Intent.ACTION_TIME_CHANGED); - } - @Override protected void onDraw(Canvas canvas) { - if(layoutSet) { + if (layoutSet) { this.mRelativeLayout.draw(canvas); - Log.d("onDraw", "draw"); } } @@ -273,7 +301,7 @@ public class BIGChart extends WatchFace implements SharedPreferences.OnSharedPre mTime.setText(timeFormat.format(System.currentTimeMillis())); showAgeAndStatus(); - if(ageLevel()<=0) { + if (ageLevel() <= 0) { mSgv.setPaintFlags(mSgv.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); } else { mSgv.setPaintFlags(mSgv.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG); @@ -286,173 +314,25 @@ public class BIGChart extends WatchFace implements SharedPreferences.OnSharedPre } } - public class MessageReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - Bundle bundle = intent.getBundleExtra("data"); - if (layoutSet && bundle !=null) { - DataMap dataMap = DataMap.fromBundle(bundle); - wakeLock.acquire(50); - sgvLevel = dataMap.getLong("sgvLevel"); - batteryLevel = dataMap.getInt("batteryLevel"); - datetime = dataMap.getLong("timestamp"); - rawString = dataMap.getString("rawString"); - sgvString = dataMap.getString("sgvString"); - batteryString = dataMap.getString("battery"); - mSgv.setText(dataMap.getString("sgvString")); - - if(ageLevel()<=0) { - mSgv.setPaintFlags(mSgv.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); - } else { - mSgv.setPaintFlags(mSgv.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG); - } - - final java.text.DateFormat timeFormat = DateFormat.getTimeFormat(BIGChart.this); - mTime.setText(timeFormat.format(System.currentTimeMillis())); - - showAgeAndStatus(); - - String delta = dataMap.getString("delta"); - - if (delta.endsWith(" mg/dl")) { - mDelta.setText(delta.substring(0, delta.length() - 6)); - } else if (delta.endsWith(" mmol/l")||delta.endsWith(" mmol")) { - mDelta.setText(delta.substring(0, delta.length() - 5)); - } else { - mDelta.setText(delta); - } - - - String avgDelta = dataMap.getString("avgDelta"); - - if (delta.endsWith(" mg/dl")) { - mAvgDelta.setText(avgDelta.substring(0, avgDelta.length() - 6)); - } else if (avgDelta.endsWith(" mmol/l")||avgDelta.endsWith(" mmol")) { - mAvgDelta.setText(avgDelta.substring(0, avgDelta.length() - 5)); - } else { - mAvgDelta.setText(avgDelta); - } - - if (chart != null) { - addToWatchSet(dataMap); - setupCharts(); - } - mRelativeLayout.measure(specW, specH); - mRelativeLayout.layout(0, 0, mRelativeLayout.getMeasuredWidth(), - mRelativeLayout.getMeasuredHeight()); - invalidate(); - setColor(); - - //start animation? - // dataMap.getDataMapArrayList("entries") == null -> not on "resend data". - if (!lowResMode && (sharedPrefs.getBoolean("animation", false) && dataMap.getDataMapArrayList("entries") == null && (sgvString.equals("100") || sgvString.equals("5.5") || sgvString.equals("5,5")))) { - startAnimation(); - } - } - //status - bundle = intent.getBundleExtra("status"); - if (layoutSet && bundle != null) { - DataMap dataMap = DataMap.fromBundle(bundle); - wakeLock.acquire(50); - externalStatusString = dataMap.getString("externalStatusString"); - cobString = dataMap.getString("cob"); - - - showAgeAndStatus(); - - mRelativeLayout.measure(specW, specH); - mRelativeLayout.layout(0, 0, mRelativeLayout.getMeasuredWidth(), - mRelativeLayout.getMeasuredHeight()); - invalidate(); - setColor(); - } - //basals and temps - bundle = intent.getBundleExtra("basals"); - if (layoutSet && bundle != null) { - DataMap dataMap = DataMap.fromBundle(bundle); - wakeLock.acquire(500); - - loadBasalsAndTemps(dataMap); - - mRelativeLayout.measure(specW, specH); - mRelativeLayout.layout(0, 0, mRelativeLayout.getMeasuredWidth(), - mRelativeLayout.getMeasuredHeight()); - invalidate(); - setColor(); - } - } - } - - private void loadBasalsAndTemps(DataMap dataMap) { - ArrayList temps = dataMap.getDataMapArrayList("temps"); - if (temps != null) { - tempWatchDataList = new ArrayList<>(); - for (DataMap temp : temps) { - TempWatchData twd = new TempWatchData(); - twd.startTime = temp.getLong("starttime"); - twd.startBasal = temp.getDouble("startBasal"); - twd.endTime = temp.getLong("endtime"); - twd.endBasal = temp.getDouble("endbasal"); - twd.amount = temp.getDouble("amount"); - tempWatchDataList.add(twd); - } - } - ArrayList basals = dataMap.getDataMapArrayList("basals"); - if (basals != null) { - basalWatchDataList = new ArrayList<>(); - for (DataMap basal : basals) { - BasalWatchData bwd = new BasalWatchData(); - bwd.startTime = basal.getLong("starttime"); - bwd.endTime = basal.getLong("endtime"); - bwd.amount = basal.getDouble("amount"); - basalWatchDataList.add(bwd); - } - } - ArrayList boluses = dataMap.getDataMapArrayList("boluses"); - if (boluses != null) { - bolusWatchDataList = new ArrayList<>(); - for (DataMap bolus : boluses) { - BolusWatchData bwd = new BolusWatchData(); - bwd.date = bolus.getLong("date"); - bwd.bolus = bolus.getDouble("bolus"); - bwd.carbs = bolus.getDouble("carbs"); - bwd.isSMB = bolus.getBoolean("isSMB"); - bwd.isValid = bolus.getBoolean("isValid"); - bolusWatchDataList.add(bwd); - } - } - ArrayList predictions = dataMap.getDataMapArrayList("predictions"); - if (boluses != null) { - predictionList = new ArrayList<>(); - for (DataMap prediction : predictions) { - BgWatchData bwd = new BgWatchData(); - bwd.timestamp = prediction.getLong("timestamp"); - bwd.sgv = prediction.getDouble("sgv"); - bwd.color = prediction.getInt("color"); - predictionList.add(bwd); - } - } - } - private void showAgeAndStatus() { - if( mTimestamp != null){ + if (mTimestamp != null) { mTimestamp.setText(readingAge(true)); } - boolean showStatus = sharedPrefs.getBoolean("showExternalStatus", true); - boolean showAvgDelta = sharedPrefs.getBoolean("showAvgDelta", true); + boolean showStatus = sp.getBoolean("showExternalStatus", true); + boolean showAvgDelta = sp.getBoolean("showAvgDelta", true); - if(showAvgDelta){ + if (showAvgDelta) { mAvgDelta.setVisibility(View.VISIBLE); } else { mAvgDelta.setVisibility(View.GONE); } - if(showStatus){ - String status = externalStatusString; - if (sharedPrefs.getBoolean("show_cob", true)) { - status = externalStatusString + " " + cobString; + if (showStatus && status != null) { + String status = this.status.getExternalStatus(); + if (sp.getBoolean("show_cob", true)) { + status += " " + this.status.getCob(); } statusView.setText(status); @@ -463,9 +343,9 @@ public class BIGChart extends WatchFace implements SharedPreferences.OnSharedPre } public void setColor() { - if(lowResMode){ + if (lowResMode) { setColorLowRes(); - } else if (sharedPrefs.getBoolean("dark", true)) { + } else if (sp.getBoolean("dark", true)) { setColorDark(); } else { setColorBright(); @@ -473,66 +353,6 @@ public class BIGChart extends WatchFace implements SharedPreferences.OnSharedPre } - - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key){ - setColor(); - if(layoutSet){ - showAgeAndStatus(); - mRelativeLayout.measure(specW, specH); - mRelativeLayout.layout(0, 0, mRelativeLayout.getMeasuredWidth(), - mRelativeLayout.getMeasuredHeight()); - } - invalidate(); - } - - protected void updateRainbow() { - animationAngle = (animationAngle + 1) % 360; - //Animation matrix: - int[] rainbow = {Color.RED, Color.YELLOW, Color.GREEN, Color.BLUE - , Color.CYAN}; - Shader shader = new LinearGradient(0, 0, 0, 20, rainbow, - null, Shader.TileMode.MIRROR); - Matrix matrix = new Matrix(); - matrix.setRotate(animationAngle); - shader.setLocalMatrix(matrix); - mSgv.getPaint().setShader(shader); - invalidate(); - } - - private synchronized boolean isAnimated() { - return isAnimated; - } - - private synchronized void setIsAnimated(boolean isAnimated) { - this.isAnimated = isAnimated; - } - - void startAnimation() { - Log.d("CircleWatchface", "start startAnimation"); - - Thread animator = new Thread() { - - - public void run() { - setIsAnimated(true); - for (int i = 0; i <= 8 * 1000 / 40; i++) { - updateRainbow(); - SystemClock.sleep(40); - } - mSgv.getPaint().setShader(null); - setIsAnimated(false); - invalidate(); - setColor(); - - System.gc(); - } - }; - - animator.start(); - } - protected void setColorLowRes() { mTime.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_mTime)); statusView.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_statusView)); @@ -555,40 +375,41 @@ public class BIGChart extends WatchFace implements SharedPreferences.OnSharedPre } protected void setColorDark() { - mTime.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_mTime)); - statusView.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_statusView)); - mRelativeLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_background)); - if (sgvLevel == 1) { - mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor)); - mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor)); - mAvgDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor)); - } else if (sgvLevel == 0) { - mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor)); - mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor)); - mAvgDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor)); - } else if (sgvLevel == -1) { - mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor)); - mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor)); - mAvgDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor)); - } + if (singleBg != null) { + mTime.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_mTime)); + statusView.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_statusView)); + mRelativeLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_background)); + if (singleBg.getSgvLevel() == 1) { + mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor)); + mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor)); + mAvgDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor)); + } else if (singleBg.getSgvLevel() == 0) { + mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor)); + mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor)); + mAvgDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor)); + } else if (singleBg.getSgvLevel() == -1) { + mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor)); + mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor)); + mAvgDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor)); + } - if (ageLevel == 1) { - mTimestamp.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_Timestamp)); - } else { - mTimestamp.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_TimestampOld)); - } + if (ageLevel == 1) { + mTimestamp.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_Timestamp)); + } else { + mTimestamp.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_TimestampOld)); + } - if (chart != null) { - highColor = ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor); - lowColor = ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor); - midColor = ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor); - gridColour = ContextCompat.getColor(getApplicationContext(), R.color.dark_gridColor); - basalBackgroundColor = ContextCompat.getColor(getApplicationContext(), R.color.basal_dark); - basalCenterColor = ContextCompat.getColor(getApplicationContext(), R.color.basal_light); - pointSize = 2; - setupCharts(); + if (chart != null) { + highColor = ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor); + lowColor = ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor); + midColor = ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor); + gridColour = ContextCompat.getColor(getApplicationContext(), R.color.dark_gridColor); + basalBackgroundColor = ContextCompat.getColor(getApplicationContext(), R.color.basal_dark); + basalCenterColor = ContextCompat.getColor(getApplicationContext(), R.color.basal_light); + pointSize = 2; + setupCharts(); + } } - } @@ -598,15 +419,15 @@ public class BIGChart extends WatchFace implements SharedPreferences.OnSharedPre mTime.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_bigchart_time)); statusView.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_bigchart_status)); mRelativeLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.light_background)); - if (sgvLevel == 1) { + if (singleBg.getSgvLevel() == 1) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_highColor)); mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_highColor)); mAvgDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_highColor)); - } else if (sgvLevel == 0) { + } else if (singleBg.getSgvLevel() == 0) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_midColor)); mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_midColor)); mAvgDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_midColor)); - } else if (sgvLevel == -1) { + } else if (singleBg.getSgvLevel() == -1) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_lowColor)); mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_lowColor)); mAvgDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_lowColor)); @@ -636,61 +457,36 @@ 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) { - DataLayerListenerService.Companion.requestData(this); // attempt endTime recover missing data + // attempt endTime recover missing data + rxBus.send(new EventWearToMobile(new EventData.ActionResendData("BIGChart:missedReadingAlert"))); } } - public void addToWatchSet(DataMap dataMap) { - - ArrayList entries = dataMap.getDataMapArrayList("entries"); - if (entries != null) { - bgDataList = new ArrayList(); - for (DataMap entry : entries) { - double sgv = entry.getDouble("sgvDouble"); - double high = entry.getDouble("high"); - double low = entry.getDouble("low"); - long timestamp = entry.getLong("timestamp"); - int color = entry.getInt("color", 0); - bgDataList.add(new BgWatchData(sgv, high, low, timestamp, color)); - } + public void addToWatchSet() { + if (graphData != null) { + bgDataList = graphData.getEntries(); } else { - double sgv = dataMap.getDouble("sgvDouble"); - double high = dataMap.getDouble("high"); - double low = dataMap.getDouble("low"); - long timestamp = dataMap.getLong("timestamp"); - int color = dataMap.getInt("color", 0); - final int size = bgDataList.size(); - if (size > 0) { - if (bgDataList.get(size - 1).timestamp == timestamp) - return; // Ignore duplicates. - } - - bgDataList.add(new BgWatchData(sgv, high, low, timestamp, color)); - } - - for (int i = 0; i < bgDataList.size(); i++) { - if (bgDataList.get(i).timestamp < (System.currentTimeMillis() - (1000 * 60 * 60 * 5))) { - bgDataList.remove(i); //Get rid of anything more than 5 hours old - break; - } + if (size > 0 && bgDataList.get(size - 1).getTimeStamp() == singleBg.getTimeStamp()) + return; // Ignore duplicates. + bgDataList.add(singleBg); } } public void setupCharts() { - if(bgDataList.size() > 0) { //Dont crash things just because we dont have values, people dont like crashy things - int timeframe = Integer.parseInt(sharedPrefs.getString("chart_timeframe", "3")); + if (bgDataList.size() > 0) { + int timeframe = sp.getInt("chart_timeframe", 3); if (lowResMode) { - bgGraphBuilder = new BgGraphBuilder(getApplicationContext(), bgDataList, predictionList, tempWatchDataList, basalWatchDataList, bolusWatchDataList, pointSize, midColor, gridColour, basalBackgroundColor, basalCenterColor, bolusColor, carbsColor, timeframe); + bgGraphBuilder = new BgGraphBuilder(getApplicationContext(), bgDataList, treatmentData.getPredictions(), treatmentData.getTemps(), treatmentData.getBasals(), treatmentData.getBoluses(), pointSize, midColor, gridColour, basalBackgroundColor, basalCenterColor, bolusColor, carbsColor, timeframe); } else { - bgGraphBuilder = new BgGraphBuilder(getApplicationContext(), bgDataList, predictionList, tempWatchDataList, basalWatchDataList, bolusWatchDataList, pointSize, highColor, lowColor, midColor, gridColour, basalBackgroundColor, basalCenterColor, bolusColor, carbsColor, timeframe); + bgGraphBuilder = new BgGraphBuilder(getApplicationContext(), bgDataList, treatmentData.getPredictions(), treatmentData.getTemps(), treatmentData.getBasals(), treatmentData.getBoluses(), pointSize, highColor, lowColor, midColor, gridColour, basalBackgroundColor, basalCenterColor, bolusColor, carbsColor, timeframe); } chart.setLineChartData(bgGraphBuilder.lineData()); chart.setViewportCalculationEnabled(true); chart.setMaximumViewport(chart.getMaximumViewport()); } else { - DataLayerListenerService.Companion.requestData(this); + rxBus.send(new EventWearToMobile(new EventData.ActionResendData("BIGChart:setupCharts"))); } } } 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 6765a751bb..8c44241e6b 100644 --- a/wear/src/main/java/info/nightscout/androidaps/watchfaces/BaseWatchFace.java +++ b/wear/src/main/java/info/nightscout/androidaps/watchfaces/BaseWatchFace.java @@ -4,7 +4,6 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.SharedPreferences; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Color; @@ -15,7 +14,6 @@ import android.graphics.Typeface; import android.os.BatteryManager; import android.os.PowerManager; import android.os.Vibrator; -import android.preference.PreferenceManager; import android.support.wearable.view.WatchViewStub; import android.text.format.DateFormat; import android.view.Display; @@ -28,15 +26,14 @@ import android.widget.RelativeLayout; import android.widget.TextView; import androidx.core.content.ContextCompat; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; -import com.google.android.gms.wearable.DataMap; import com.ustwo.clockwise.common.WatchFaceTime; import com.ustwo.clockwise.common.WatchMode; import com.ustwo.clockwise.common.WatchShape; import com.ustwo.clockwise.wearable.WatchFace; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; import java.util.Locale; @@ -44,12 +41,17 @@ import javax.inject.Inject; import dagger.android.AndroidInjection; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.DataLayerListenerService; -import info.nightscout.androidaps.data.RawDisplayData; +import info.nightscout.androidaps.events.EventWearPreferenceChange; +import info.nightscout.androidaps.events.EventWearToMobile; import info.nightscout.androidaps.interaction.utils.Persistence; import info.nightscout.androidaps.interaction.utils.WearUtil; +import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.utils.rx.AapsSchedulers; import info.nightscout.shared.logging.AAPSLogger; import info.nightscout.shared.logging.LTag; +import info.nightscout.shared.sharedPreferences.SP; +import info.nightscout.shared.weardata.EventData; +import io.reactivex.rxjava3.disposables.CompositeDisposable; import lecho.lib.hellocharts.view.LineChartView; /** @@ -58,20 +60,27 @@ import lecho.lib.hellocharts.view.LineChartView; * Refactored by dlvoy on 2019-11-2019 */ -public abstract class BaseWatchFace extends WatchFace implements SharedPreferences.OnSharedPreferenceChangeListener { +public abstract class BaseWatchFace extends WatchFace { @Inject WearUtil wearUtil; @Inject Persistence persistence; @Inject AAPSLogger aapsLogger; + @Inject RxBus rxBus; + @Inject AapsSchedulers aapsSchedulers; + @Inject SP sp; - public final static IntentFilter INTENT_FILTER; + CompositeDisposable disposable = new CompositeDisposable(); - static { - INTENT_FILTER = new IntentFilter(); - INTENT_FILTER.addAction(Intent.ACTION_TIME_TICK); - INTENT_FILTER.addAction(Intent.ACTION_TIMEZONE_CHANGED); - INTENT_FILTER.addAction(Intent.ACTION_TIME_CHANGED); - } + protected EventData.SingleBg singleBg = new EventData.SingleBg(0, "---", "-", "--", "--", "--" + , 0, 0.0, 0.0, 0.0, 0); + protected EventData.Status status = new EventData.Status("no status", "IOB", "-.--", false, "--g", "-.--U/h", "--", "--", -1, "--", false, 1); + protected EventData.TreatmentData treatmentData = new EventData.TreatmentData( + new ArrayList<>(), + new ArrayList<>(), + new ArrayList<>(), + new ArrayList<>() + ); + protected EventData.GraphData graphData = new EventData.GraphData(new ArrayList<>()); static IntentFilter iFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); @@ -96,7 +105,6 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc public int pointSize = 2; public BgGraphBuilder bgGraphBuilder; public LineChartView chart; - public RawDisplayData rawData; public PowerManager.WakeLock wakeLock; // related endTime manual layout public View layoutView; @@ -104,9 +112,6 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc public boolean forceSquareCanvas = false; // Set to true by the Steampunk watch face. public String sMinute = "0"; public String sHour = "0"; - protected SharedPreferences sharedPrefs; - private LocalBroadcastManager localBroadcastManager; - private MessageReceiver messageReceiver; private BroadcastReceiver batteryReceiver; private int colorDarkHigh, colorDarkMid, colorDarkLow; private java.text.DateFormat timeFormat; @@ -126,7 +131,6 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc colorDarkMid = ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor); colorDarkLow = ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor); - rawData = new RawDisplayData(wearUtil); Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); display.getSize(displaySize); wakeLock = ((PowerManager) getSystemService(Context.POWER_SERVICE)).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AndroidAPS:BaseWatchFace"); @@ -137,8 +141,49 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc } else { specH = View.MeasureSpec.makeMeasureSpec(displaySize.y, View.MeasureSpec.EXACTLY); } - sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); - sharedPrefs.registerOnSharedPreferenceChangeListener(this); + + disposable.add(rxBus + .toObservable(EventWearPreferenceChange.class) + .observeOn(aapsSchedulers.getMain()) + .subscribe(event -> { + setupBatteryReceiver(); + if (event.getChangedKey() != null && event.getChangedKey().equals("delta_granularity")) + rxBus.send(new EventWearToMobile(new EventData.ActionResendData("BaseWatchFace:onSharedPreferenceChanged"))); + if (layoutSet) setDataFields(); + invalidate(); + }) + ); + disposable.add(rxBus + .toObservable(EventData.SingleBg.class) + .observeOn(aapsSchedulers.getMain()) + .subscribe(event -> singleBg = event) + ); + disposable.add(rxBus + .toObservable(EventData.TreatmentData.class) + .observeOn(aapsSchedulers.getMain()) + .subscribe(event -> treatmentData = event) + ); + disposable.add(rxBus + .toObservable(EventData.GraphData.class) + .observeOn(aapsSchedulers.getMain()) + .subscribe(event -> graphData = event) + ); + disposable.add(rxBus + .toObservable(EventData.Status.class) + .observeOn(aapsSchedulers.getMain()) + .subscribe(event -> { + // this event is received as last batch of data + if (isSimpleUi()) { + if (needUpdate()) { + invalidate(); + } + } else { + setupCharts(); + setDataFields(); + invalidate(); + } + }) + ); persistence.turnOff(); @@ -148,7 +193,7 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc } private void setupBatteryReceiver() { - String setting = sharedPrefs.getString("simplify_ui", "off"); + String setting = sp.getString("simplify_ui", "off"); if ((setting.equals("charging") || setting.equals("ambient_charging")) && batteryReceiver == null) { IntentFilter intentBatteryFilter = new IntentFilter(); intentBatteryFilter.addAction(BatteryManager.ACTION_CHARGING); @@ -215,11 +260,6 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc public void performViewSetup() { final WatchViewStub layoutStub = layoutView.findViewById(R.id.watch_view_stub); - IntentFilter messageFilter = new IntentFilter(Intent.ACTION_SEND); - - messageReceiver = new MessageReceiver(); - localBroadcastManager = LocalBroadcastManager.getInstance(this); - localBroadcastManager.registerReceiver(messageReceiver, messageFilter); layoutStub.setOnLayoutInflatedListener((WatchViewStub stub) -> { mTime = stub.findViewById(R.id.watch_time); @@ -274,11 +314,11 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc } public double timeSince() { - return System.currentTimeMillis() - rawData.datetime; + return System.currentTimeMillis() - singleBg.getTimeStamp(); } public String readingAge(boolean shortString) { - if (rawData.datetime == 0) { + if (singleBg.getTimeStamp() == 0) { return shortString ? "--" : "-- Minute ago"; } int minutesAgo = (int) Math.floor(timeSince() / (1000 * 60)); @@ -290,12 +330,7 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc @Override public void onDestroy() { - if (localBroadcastManager != null && messageReceiver != null) { - localBroadcastManager.unregisterReceiver(messageReceiver); - } - if (sharedPrefs != null) { - sharedPrefs.unregisterOnSharedPreferenceChangeListener(this); - } + disposable.clear(); if (batteryReceiver != null) { unregisterReceiver(batteryReceiver); } @@ -326,16 +361,16 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc float xHalf = displaySize.x / 2f; float yThird = displaySize.y / 3f; - boolean isOutdated = rawData.datetime > 0 && ageLevel() <= 0; + boolean isOutdated = singleBg.getTimeStamp() > 0 && ageLevel() <= 0; mSvgPaint.setStrikeThruText(isOutdated); - mSvgPaint.setColor(getBgColour(rawData.sgvLevel)); - mDirectionPaint.setColor(getBgColour(rawData.sgvLevel)); + mSvgPaint.setColor(getBgColour(singleBg.getSgvLevel())); + mDirectionPaint.setColor(getBgColour(singleBg.getSgvLevel())); - String sSvg = rawData.sSgv; + String sSvg = singleBg.getSgvString(); float svgWidth = mSvgPaint.measureText(sSvg); - String sDirection = " " + rawData.sDirection + "\uFE0E"; + String sDirection = " " + singleBg.getSgvString() + "\uFE0E"; float directionWidth = mDirectionPaint.measureText(sDirection); float xSvg = xHalf - (svgWidth + directionWidth) / 2; @@ -382,7 +417,7 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc } private void checkVibrateHourly(WatchFaceTime oldTime, WatchFaceTime newTime) { - boolean hourlyVibratePref = sharedPrefs.getBoolean("vibrate_Hourly", false); + boolean hourlyVibratePref = sp.getBoolean("vibrate_Hourly", false); if (hourlyVibratePref && layoutSet && newTime.hasHourChanged(oldTime)) { aapsLogger.info(LTag.WEAR, "hourlyVibratePref", "true --> " + newTime); Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE); @@ -394,8 +429,8 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc public void setDataFields() { setDateAndTime(); if (mSgv != null) { - if (sharedPrefs.getBoolean("showBG", true)) { - mSgv.setText(rawData.sSgv); + if (sp.getBoolean("showBG", true)) { + mSgv.setText(singleBg.getSgvString()); mSgv.setVisibility(View.VISIBLE); } else { // Leave the textview there but invisible, as a height holder for the empty space above the white line @@ -407,8 +442,8 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc strikeThroughSgvIfNeeded(); if (mDirection != null) { - if (sharedPrefs.getBoolean("show_direction", true)) { - mDirection.setText(rawData.sDirection + "\uFE0E"); + if (sp.getBoolean("show_direction", true)) { + mDirection.setText(singleBg.getSgvString() + "\uFE0E"); mDirection.setVisibility(View.VISIBLE); } else { mDirection.setVisibility(View.GONE); @@ -416,8 +451,8 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc } if (mDelta != null) { - if (sharedPrefs.getBoolean("showDelta", true)) { - mDelta.setText(rawData.sDelta); + if (sp.getBoolean("showDelta", true)) { + mDelta.setText(singleBg.getDelta()); mDelta.setVisibility(View.VISIBLE); } else { mDelta.setVisibility(View.GONE); @@ -425,8 +460,8 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc } if (mAvgDelta != null) { - if (sharedPrefs.getBoolean("showAvgDelta", true)) { - mAvgDelta.setText(rawData.sAvgDelta); + if (sp.getBoolean("showAvgDelta", true)) { + mAvgDelta.setText(singleBg.getAvgDelta()); mAvgDelta.setVisibility(View.VISIBLE); } else { mAvgDelta.setVisibility(View.GONE); @@ -434,8 +469,8 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc } if (mCOB1 != null && mCOB2 != null) { - mCOB2.setText(rawData.sCOB2); - if (sharedPrefs.getBoolean("show_cob", true)) { + mCOB2.setText(status.getCob()); + if (sp.getBoolean("show_cob", true)) { mCOB1.setVisibility(View.VISIBLE); mCOB2.setVisibility(View.VISIBLE); } else { @@ -444,8 +479,8 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc } // Deal with cases where there is only the value shown for COB, and not the label } else if (mCOB2 != null) { - mCOB2.setText(rawData.sCOB2); - if (sharedPrefs.getBoolean("show_cob", true)) { + mCOB2.setText(status.getCob()); + if (sp.getBoolean("show_cob", true)) { mCOB2.setVisibility(View.VISIBLE); } else { mCOB2.setVisibility(View.GONE); @@ -453,15 +488,15 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc } if (mIOB1 != null && mIOB2 != null) { - if (sharedPrefs.getBoolean("show_iob", true)) { + if (sp.getBoolean("show_iob", true)) { mIOB1.setVisibility(View.VISIBLE); mIOB2.setVisibility(View.VISIBLE); - if (rawData.detailedIOB) { - mIOB1.setText(rawData.sIOB1); - mIOB2.setText(rawData.sIOB2); + if (status.getDetailedIob()) { + mIOB1.setText(status.getIobSum()); + mIOB2.setText(status.getIobDetail()); } else { mIOB1.setText(getString(R.string.activity_IOB)); - mIOB2.setText(rawData.sIOB1); + mIOB2.setText(status.getIobSum()); } } else { mIOB1.setVisibility(View.GONE); @@ -469,12 +504,12 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc } // Deal with cases where there is only the value shown for IOB, and not the label } else if (mIOB2 != null) { - if (sharedPrefs.getBoolean("show_iob", true)) { + if (sp.getBoolean("show_iob", true)) { mIOB2.setVisibility(View.VISIBLE); - if (rawData.detailedIOB) { - mIOB2.setText(rawData.sIOB2); + if (status.getDetailedIob()) { + mIOB2.setText(status.getIobDetail()); } else { - mIOB2.setText(rawData.sIOB1); + mIOB2.setText(status.getIobSum()); } } else { mIOB2.setText(""); @@ -482,11 +517,11 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc } if (mTimestamp != null) { - if (sharedPrefs.getBoolean("showAgo", true)) { + if (sp.getBoolean("showAgo", true)) { if (isAAPSv2 != null) { mTimestamp.setText(readingAge(true)); } else { - boolean shortString = sharedPrefs.getBoolean("showExternalStatus", true); + boolean shortString = sp.getBoolean("showExternalStatus", true); mTimestamp.setText(readingAge(shortString)); } mTimestamp.setVisibility(View.VISIBLE); @@ -496,15 +531,15 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc } if (mUploaderBattery != null) { - if (sharedPrefs.getBoolean("show_uploader_battery", true)) { + if (sp.getBoolean("show_uploader_battery", true)) { if (isAAPSv2 != null) { - mUploaderBattery.setText(rawData.sUploaderBattery + "%"); + mUploaderBattery.setText(status.getBattery() + "%"); mUploaderBattery.setVisibility(View.VISIBLE); } else { - if (sharedPrefs.getBoolean("showExternalStatus", true)) { - mUploaderBattery.setText("U: " + rawData.sUploaderBattery + "%"); + if (sp.getBoolean("showExternalStatus", true)) { + mUploaderBattery.setText("U: " + status.getBattery() + "%"); } else { - mUploaderBattery.setText("Uploader: " + rawData.sUploaderBattery + "%"); + mUploaderBattery.setText("Uploader: " + status.getBattery() + "%"); } } } else { @@ -513,8 +548,8 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc } if (mRigBattery != null) { - if (sharedPrefs.getBoolean("show_rig_battery", false)) { - mRigBattery.setText(rawData.sRigBattery); + if (sp.getBoolean("show_rig_battery", false)) { + mRigBattery.setText(status.getRigBattery()); mRigBattery.setVisibility(View.VISIBLE); } else { mRigBattery.setVisibility(View.GONE); @@ -522,8 +557,8 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc } if (mBasalRate != null) { - if (sharedPrefs.getBoolean("show_temp_basal", true)) { - mBasalRate.setText(rawData.sBasalRate); + if (sp.getBoolean("show_temp_basal", true)) { + mBasalRate.setText(status.getCurrentBasal()); mBasalRate.setVisibility(View.VISIBLE); } else { mBasalRate.setVisibility(View.GONE); @@ -531,8 +566,8 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc } if (mBgi != null) { - if (rawData.showBGI) { - mBgi.setText(rawData.sBgi); + if (status.getShowBgi()) { + mBgi.setText(status.getBgi()); mBgi.setVisibility(View.VISIBLE); } else { mBgi.setVisibility(View.GONE); @@ -540,8 +575,8 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc } if (mStatus != null) { - if (sharedPrefs.getBoolean("showExternalStatus", true)) { - mStatus.setText(rawData.externalStatusString); + if (sp.getBoolean("showExternalStatus", true)) { + mStatus.setText(status.getExternalStatus()); mStatus.setVisibility(View.VISIBLE); } else { mStatus.setVisibility(View.GONE); @@ -549,10 +584,10 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc } if (mLoop != null) { - if (sharedPrefs.getBoolean("showExternalStatus", true)) { + if (sp.getBoolean("showExternalStatus", true)) { mLoop.setVisibility(View.VISIBLE); - if (rawData.openApsStatus != -1) { - int mins = (int) ((System.currentTimeMillis() - rawData.openApsStatus) / 1000 / 60); + if (status.getOpenApsStatus() != -1) { + int mins = (int) ((System.currentTimeMillis() - status.getOpenApsStatus()) / 1000 / 60); mLoop.setText(mins + "'"); if (mins > 14) { loopLevel = 0; @@ -605,7 +640,7 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc } if (mDate != null && mDay != null && mMonth != null) { - if (sharedPrefs.getBoolean("show_date", false)) { + if (sp.getBoolean("show_date", false)) { if (mDayName != null) { mDayName.setText(sdfDayName.format(mDateTime)); } @@ -620,10 +655,10 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc } public void setColor() { - dividerMatchesBg = sharedPrefs.getBoolean("match_divider", false); + dividerMatchesBg = sp.getBoolean("match_divider", false); if (lowResMode) { setColorLowRes(); - } else if (sharedPrefs.getBoolean("dark", true)) { + } else if (sp.getBoolean("dark", true)) { setColorDark(); } else { setColorBright(); @@ -632,8 +667,8 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc public void strikeThroughSgvIfNeeded() { if (mSgv != null) { - if (sharedPrefs.getBoolean("showBG", true)) { - if (ageLevel() <= 0 && rawData.datetime > 0) { + if (sp.getBoolean("showBG", true)) { + if (ageLevel() <= 0 && singleBg.getTimeStamp() > 0) { mSgv.setPaintFlags(mSgv.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); } else { mSgv.setPaintFlags(mSgv.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG); @@ -664,7 +699,7 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc } private boolean isSimpleUi() { - String simplify = sharedPrefs.getString("simplify_ui", "off"); + String simplify = sp.getString("simplify_ui", "off"); if (simplify.equals("off")) { return false; } @@ -674,18 +709,6 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc return (simplify.equals("charging") || simplify.equals("ambient_charging")) && isCharging(); } - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - setupBatteryReceiver(); - if ("delta_granularity".equals(key)) { - DataLayerListenerService.Companion.requestData(this); - } - if (layoutSet) { - setDataFields(); - } - invalidate(); - } - protected abstract void setColorDark(); protected abstract void setColorBright(); @@ -694,8 +717,9 @@ 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) { - DataLayerListenerService.Companion.requestData(this); // Attempt endTime recover missing data + if (singleBg.getTimeStamp() == 0 || minutes_since >= 16 && ((minutes_since - 16) % 5) == 0) { + // Attempt endTime recover missing data + rxBus.send(new EventWearToMobile(new EventData.ActionResendData("BaseWatchFace:missedReadingAlert"))); } } @@ -703,12 +727,12 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc if (isSimpleUi()) { return; } - if (rawData.bgDataList.size() > 0) { // Dont crash things just because we dont have values, people dont like crashy things - int timeframe = Integer.parseInt(sharedPrefs.getString("chart_timeframe", "3")); + if (chart != null && graphData.getEntries().size() > 0) { + int timeframe = sp.getInt("chart_timeframe", 3); if (lowResMode) { - bgGraphBuilder = new BgGraphBuilder(getApplicationContext(), rawData, pointSize, midColor, gridColor, basalBackgroundColor, basalCenterColor, bolusColor, Color.GREEN, timeframe); + bgGraphBuilder = new BgGraphBuilder(getApplicationContext(), graphData.getEntries(), treatmentData.getPredictions(), treatmentData.getTemps(), treatmentData.getBasals(), treatmentData.getBoluses(), pointSize, midColor, gridColor, basalBackgroundColor, basalCenterColor, bolusColor, Color.GREEN, timeframe); } else { - bgGraphBuilder = new BgGraphBuilder(getApplicationContext(), rawData, pointSize, highColor, lowColor, midColor, gridColor, basalBackgroundColor, basalCenterColor, bolusColor, Color.GREEN, timeframe); + bgGraphBuilder = new BgGraphBuilder(getApplicationContext(), graphData.getEntries(), treatmentData.getPredictions(), treatmentData.getTemps(), treatmentData.getBasals(), treatmentData.getBoluses(), pointSize, highColor, lowColor, midColor, gridColor, basalBackgroundColor, basalCenterColor, bolusColor, Color.GREEN, timeframe); } chart.setLineChartData(bgGraphBuilder.lineData()); @@ -717,39 +741,12 @@ public abstract class BaseWatchFace extends WatchFace implements SharedPreferenc } } - public class MessageReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - PowerManager.WakeLock wl = wearUtil.getWakeLock("readingPrefs", 50); - - final DataMap dataMap = rawData.updateDataFromMessage(intent, wakeLock); - if (chart != null && dataMap != null) { - rawData.addToWatchSet(dataMap); - setupCharts(); - } - rawData.updateStatusFromMessage(intent, wakeLock); - rawData.updateBasalsFromMessage(intent); - - if (isSimpleUi()) { - if (needUpdate()) { - invalidate(); - } - } else { - setupCharts(); - setDataFields(); - invalidate(); - } - wearUtil.releaseWakeLock(wl); - } - } - private boolean needUpdate() { - if (mLastSvg.equals(rawData.sSgv) && mLastDirection.equals(rawData.sDirection)) { + if (mLastSvg.equals(singleBg.getSgvString()) && mLastDirection.equals(singleBg.getSgvString())) { return false; } - mLastSvg = rawData.sSgv; - mLastDirection = rawData.sDirection; + mLastSvg = singleBg.getSgvString(); + mLastDirection = singleBg.getSgvString(); return true; } - } diff --git a/wear/src/main/java/info/nightscout/androidaps/watchfaces/BgGraphBuilder.java b/wear/src/main/java/info/nightscout/androidaps/watchfaces/BgGraphBuilder.java index 3cae85f962..2dd39c850f 100644 --- a/wear/src/main/java/info/nightscout/androidaps/watchfaces/BgGraphBuilder.java +++ b/wear/src/main/java/info/nightscout/androidaps/watchfaces/BgGraphBuilder.java @@ -14,17 +14,12 @@ import java.util.List; import java.util.Map; import java.util.TimeZone; -import info.nightscout.androidaps.data.BasalWatchData; -import info.nightscout.androidaps.data.BgWatchData; -import info.nightscout.androidaps.data.BolusWatchData; -import info.nightscout.androidaps.data.RawDisplayData; -import info.nightscout.androidaps.data.TempWatchData; +import info.nightscout.shared.weardata.EventData; import lecho.lib.hellocharts.model.Axis; import lecho.lib.hellocharts.model.AxisValue; import lecho.lib.hellocharts.model.Line; import lecho.lib.hellocharts.model.LineChartData; import lecho.lib.hellocharts.model.PointValue; -import lecho.lib.hellocharts.model.Viewport; /** * Created by emmablack on 11/15/14. @@ -33,10 +28,10 @@ public class BgGraphBuilder { public static final double MAX_PREDICTION__TIME_RATIO = (3d / 5); public static final double UPPER_CUTOFF_SGV = 400; private final long predictionEndTime; - private final List predictionsList; - private final ArrayList bolusWatchDataList; - private final ArrayList basalWatchDataList; - public List tempWatchDataList; + private final List predictionsList; + private final ArrayList bolusWatchDataList; + private final ArrayList basalWatchDataList; + public List tempWatchDataList; private final int timespan; public long end_time; public long start_time; @@ -44,7 +39,7 @@ public class BgGraphBuilder { public Context context; public double highMark; public double lowMark; - public List bgDataList = new ArrayList(); + public List bgDataList; public int pointSize; public int highColor; @@ -58,20 +53,24 @@ public class BgGraphBuilder { public boolean singleLine = false; - private final List inRangeValues = new ArrayList(); - private final List highValues = new ArrayList(); - private final List lowValues = new ArrayList(); - public Viewport viewport; - + private final List inRangeValues = new ArrayList<>(); + private final List highValues = new ArrayList<>(); + private final List lowValues = new ArrayList<>(); //used for low resolution screen. - public BgGraphBuilder(Context context, List aBgList, List predictionsList, List tempWatchDataList, ArrayList basalWatchDataList, ArrayList bolusWatchDataList, int aPointSize, int aMidColor, int gridColour, int basalBackgroundColor, int basalCenterColor, int bolusInvalidColor, int carbsColor, int timespan) { - this.start_time = System.currentTimeMillis() - (1000 * 60 * 60 * timespan); //timespan hours ago + public BgGraphBuilder(Context context, List aBgList, + List predictionsList, + List tempWatchDataList, + ArrayList basalWatchDataList, + ArrayList bolusWatchDataList, + int aPointSize, int aMidColor, int gridColour, int basalBackgroundColor, int basalCenterColor, int bolusInvalidColor, int carbsColor, int timespan) { + this.start_time = System.currentTimeMillis() - (1000L * 60 * 60 * timespan); //timespan + // hours ago this.bgDataList = aBgList; this.predictionsList = predictionsList; this.context = context; - this.highMark = aBgList.get(aBgList.size() - 1).high; - this.lowMark = aBgList.get(aBgList.size() - 1).low; + this.highMark = aBgList.get(aBgList.size() - 1).getHigh(); + this.lowMark = aBgList.get(aBgList.size() - 1).getLow(); this.pointSize = aPointSize; this.singleLine = false; this.midColor = aMidColor; @@ -80,24 +79,31 @@ public class BgGraphBuilder { this.timespan = timespan; this.tempWatchDataList = tempWatchDataList; this.basalWatchDataList = basalWatchDataList; - this.bolusWatchDataList = (bolusWatchDataList!=null)?bolusWatchDataList:new ArrayList(); + this.bolusWatchDataList = (bolusWatchDataList != null) ? bolusWatchDataList : new ArrayList<>(); this.gridColour = gridColour; this.basalCenterColor = basalCenterColor; this.basalBackgroundColor = basalBackgroundColor; this.bolusInvalidColor = bolusInvalidColor; this.carbsColor = carbsColor; - this.end_time = System.currentTimeMillis() + (1000 * 60 * 6 * timespan); //Now plus 30 minutes padding (for 5 hours. Less if less.) + this.end_time = System.currentTimeMillis() + (1000L * 60 * 6 * timespan); //Now plus 30 + // minutes padding (for 5 hours. Less if less.) this.predictionEndTime = getPredictionEndTime(); - this.end_time = (predictionEndTime>end_time)?predictionEndTime:end_time; + this.end_time = Math.max(predictionEndTime, end_time); } - public BgGraphBuilder(Context context, List aBgList, List predictionsList, List tempWatchDataList, ArrayList basalWatchDataList, ArrayList bolusWatchDataList, int aPointSize, int aHighColor, int aLowColor, int aMidColor, int gridColour, int basalBackgroundColor, int basalCenterColor, int bolusInvalidColor, int carbsColor, int timespan) { - this.start_time = System.currentTimeMillis() - (1000 * 60 * 60 * timespan); //timespan hours ago + public BgGraphBuilder(Context context, List aBgList, + List predictionsList, + List tempWatchDataList, + ArrayList basalWatchDataList, + ArrayList bolusWatchDataList, + int aPointSize, int aHighColor, int aLowColor, int aMidColor, int gridColour, int basalBackgroundColor, int basalCenterColor, int bolusInvalidColor, int carbsColor, int timespan) { + this.start_time = System.currentTimeMillis() - (1000L * 60 * 60 * timespan); //timespan + // hours ago this.bgDataList = aBgList; this.predictionsList = predictionsList; this.context = context; - this.highMark = aBgList.get(aBgList.size() - 1).high; - this.lowMark = aBgList.get(aBgList.size() - 1).low; + this.highMark = aBgList.get(aBgList.size() - 1).getHigh(); + this.lowMark = aBgList.get(aBgList.size() - 1).getLow(); this.pointSize = aPointSize; this.highColor = aHighColor; this.lowColor = aLowColor; @@ -105,51 +111,16 @@ public class BgGraphBuilder { this.timespan = timespan; this.tempWatchDataList = tempWatchDataList; this.basalWatchDataList = basalWatchDataList; - this.bolusWatchDataList = (bolusWatchDataList!=null)?bolusWatchDataList:new ArrayList(); + this.bolusWatchDataList = (bolusWatchDataList != null) ? bolusWatchDataList : new ArrayList<>(); this.gridColour = gridColour; this.basalCenterColor = basalCenterColor; this.basalBackgroundColor = basalBackgroundColor; this.bolusInvalidColor = bolusInvalidColor; this.carbsColor = carbsColor; - this.end_time = System.currentTimeMillis() + (1000 * 60 * 6 * timespan); //Now plus 30 minutes padding (for 5 hours. Less if less.) + this.end_time = System.currentTimeMillis() + (1000L * 60 * 6 * timespan); //Now plus 30 + // minutes padding (for 5 hours. Less if less.) this.predictionEndTime = getPredictionEndTime(); - this.end_time = (predictionEndTime>end_time)?predictionEndTime:end_time; - } - - public BgGraphBuilder(Context context, RawDisplayData raw, int aPointSize, int aHighColor, int aLowColor, int aMidColor, int gridColour, int basalBackgroundColor, int basalCenterColor, int bolusInvalidColor, int carbsColor, int timespan) { - this(context, - raw.bgDataList, - raw.predictionList, - raw.tempWatchDataList, - raw.basalWatchDataList, - raw.bolusWatchDataList, - aPointSize, - aHighColor, - aLowColor, - aMidColor, - gridColour, - basalBackgroundColor, - basalCenterColor, - bolusInvalidColor, - carbsColor, - timespan); - } - - public BgGraphBuilder(Context context, RawDisplayData raw, int aPointSize, int aMidColor, int gridColour, int basalBackgroundColor, int basalCenterColor, int bolusInvalidColor, int carbsColor, int timespan) { - this(context, - raw.bgDataList, - raw.predictionList, - raw.tempWatchDataList, - raw.basalWatchDataList, - raw.bolusWatchDataList, - aPointSize, - aMidColor, - gridColour, - basalBackgroundColor, - basalCenterColor, - bolusInvalidColor, - carbsColor, - timespan); + this.end_time = Math.max(predictionEndTime, end_time); } public LineChartData lineData() { @@ -162,7 +133,7 @@ public class BgGraphBuilder { public List defaultLines() { addBgReadingValues(); - List lines = new ArrayList(); + List lines = new ArrayList<>(); lines.add(highLine()); lines.add(lowLine()); lines.add(inRangeValuesLine()); @@ -172,41 +143,41 @@ public class BgGraphBuilder { double minChart = lowMark; double maxChart = highMark; - for ( BgWatchData bgd:bgDataList) { - if(bgd.sgv > maxChart){ - maxChart = bgd.sgv; + for (EventData.SingleBg bgd : bgDataList) { + if (bgd.getSgv() > maxChart) { + maxChart = bgd.getSgv(); } - if(bgd.sgv < minChart){ - minChart = bgd.sgv; + if (bgd.getSgv() < minChart) { + minChart = bgd.getSgv(); } } double maxBasal = 0.1; - for (BasalWatchData bwd: basalWatchDataList) { - if(bwd.amount > maxBasal){ - maxBasal = bwd.amount; + for (EventData.TreatmentData.Basal bwd : basalWatchDataList) { + if (bwd.getAmount() > maxBasal) { + maxBasal = bwd.getAmount(); } } double maxTemp = maxBasal; - for (TempWatchData twd: tempWatchDataList) { - if(twd.amount > maxTemp){ - maxTemp = twd.amount; + for (EventData.TreatmentData.TempBasal twd : tempWatchDataList) { + if (twd.getAmount() > maxTemp) { + maxTemp = twd.getAmount(); } } - double factor = (maxChart-minChart)/maxTemp; + double factor = (maxChart - minChart) / maxTemp; // in case basal is the highest, don't paint it totally at the top. - factor = Math.min(factor, ((maxChart-minChart)/maxBasal)*(2/3d)); + factor = Math.min(factor, ((maxChart - minChart) / maxBasal) * (2 / 3d)); boolean highlight = PreferenceManager .getDefaultSharedPreferences(context) .getBoolean("highlight_basals", false); - for (TempWatchData twd: tempWatchDataList) { - if(twd.endTime > start_time) { - lines.add(tempValuesLine(twd, (float) minChart, factor, false, highlight?(pointSize+1):pointSize)); - if(highlight){ + for (EventData.TreatmentData.TempBasal twd : tempWatchDataList) { + if (twd.getEndTime() > start_time) { + lines.add(tempValuesLine(twd, (float) minChart, factor, false, highlight ? (pointSize + 1) : pointSize)); + if (highlight) { lines.add(tempValuesLine(twd, (float) minChart, factor, true, 1)); } } @@ -224,13 +195,14 @@ public class BgGraphBuilder { private Line basalLine(float offset, double factor, boolean highlight) { - List pointValues = new ArrayList(); + List pointValues = new ArrayList<>(); - for (BasalWatchData bwd: basalWatchDataList) { - if(bwd.endTime > start_time) { - long begin = Math.max(start_time, bwd.startTime); - pointValues.add(new PointValue(fuzz(begin), offset + (float) (factor * bwd.amount))); - pointValues.add(new PointValue(fuzz(bwd.endTime), offset + (float) (factor * bwd.amount))); + for (EventData.TreatmentData.Basal bwd : basalWatchDataList) { + if (bwd.getEndTime() > start_time) { + long begin = Math.max(start_time, bwd.getStartTime()); + pointValues.add(new PointValue(fuzz(begin), offset + (float) (factor * bwd.getAmount()))); + pointValues.add(new PointValue(fuzz(bwd.getEndTime()), + offset + (float) (factor * bwd.getAmount()))); } } @@ -238,7 +210,7 @@ public class BgGraphBuilder { basalLine.setHasPoints(false); basalLine.setColor(basalCenterColor); basalLine.setPathEffect(new DashPathEffect(new float[]{4f, 3f}, 4f)); - basalLine.setStrokeWidth(highlight?2:1); + basalLine.setStrokeWidth(highlight ? 2 : 1); return basalLine; @@ -246,28 +218,28 @@ public class BgGraphBuilder { private Line bolusLine(float offset) { - List pointValues = new ArrayList(); + List pointValues = new ArrayList<>(); - for (BolusWatchData bwd: bolusWatchDataList) { - if(bwd.date > start_time && bwd.date <= end_time && !bwd.isSMB && bwd.isValid && bwd.bolus > 0) { - pointValues.add(new PointValue(fuzz(bwd.date), offset -2)); + for (EventData.TreatmentData.Treatment bwd : bolusWatchDataList) { + if (bwd.getDate() > start_time && bwd.getDate() <= end_time && !bwd.isSMB() && bwd.isValid() && bwd.getBolus() > 0) { + pointValues.add(new PointValue(fuzz(bwd.getDate()), offset - 2)); } } Line line = new Line(pointValues); line.setColor(basalCenterColor); line.setHasLines(false); - line.setPointRadius(pointSize*2); + line.setPointRadius(pointSize * 2); line.setHasPoints(true); return line; } private Line smbLine(float offset) { - List pointValues = new ArrayList(); + List pointValues = new ArrayList<>(); - for (BolusWatchData bwd: bolusWatchDataList) { - if(bwd.date > start_time && bwd.date <= end_time && bwd.isSMB && bwd.isValid && bwd.bolus > 0) { - pointValues.add(new PointValue(fuzz(bwd.date), offset -2)); + for (EventData.TreatmentData.Treatment bwd : bolusWatchDataList) { + if (bwd.getDate() > start_time && bwd.getDate() <= end_time && bwd.isSMB() && bwd.isValid() && bwd.getBolus() > 0) { + pointValues.add(new PointValue(fuzz(bwd.getDate()), offset - 2)); } } Line line = new Line(pointValues); @@ -280,11 +252,11 @@ public class BgGraphBuilder { private Line bolusInvalidLine(float offset) { - List pointValues = new ArrayList(); + List pointValues = new ArrayList<>(); - for (BolusWatchData bwd: bolusWatchDataList) { - if(bwd.date > start_time && bwd.date <= end_time && !(bwd.isValid && (bwd.bolus > 0 || bwd.carbs > 0))) { - pointValues.add(new PointValue(fuzz(bwd.date), offset -2)); + for (EventData.TreatmentData.Treatment bwd : bolusWatchDataList) { + if (bwd.getDate() > start_time && bwd.getDate() <= end_time && !(bwd.isValid() && (bwd.getBolus() > 0 || bwd.getCarbs() > 0))) { + pointValues.add(new PointValue(fuzz(bwd.getDate()), offset - 2)); } } Line line = new Line(pointValues); @@ -297,17 +269,17 @@ public class BgGraphBuilder { private Line carbsLine(float offset) { - List pointValues = new ArrayList(); + List pointValues = new ArrayList<>(); - for (BolusWatchData bwd: bolusWatchDataList) { - if(bwd.date > start_time && bwd.date <= end_time && !bwd.isSMB && bwd.isValid && bwd.carbs > 0) { - pointValues.add(new PointValue(fuzz(bwd.date), offset +2)); + for (EventData.TreatmentData.Treatment bwd : bolusWatchDataList) { + if (bwd.getDate() > start_time && bwd.getDate() <= end_time && !bwd.isSMB() && bwd.isValid() && bwd.getCarbs() > 0) { + pointValues.add(new PointValue(fuzz(bwd.getDate()), offset + 2)); } } Line line = new Line(pointValues); line.setColor(carbsColor); line.setHasLines(false); - line.setPointRadius(pointSize*2); + line.setPointRadius(pointSize * 2); line.setHasPoints(true); return line; } @@ -316,13 +288,13 @@ public class BgGraphBuilder { private void addPredictionLines(List lines) { Map> values = new HashMap<>(); long endTime = getPredictionEndTime(); - for (BgWatchData bwd : predictionsList) { - if (bwd.timestamp <= endTime) { - double value = Math.min(bwd.sgv, UPPER_CUTOFF_SGV); - if (!values.containsKey(bwd.color)) { - values.put(bwd.color, new ArrayList<>()); + for (EventData.SingleBg bwd : predictionsList) { + if (bwd.getTimeStamp() <= endTime) { + double value = Math.min(bwd.getSgv(), UPPER_CUTOFF_SGV); + if (!values.containsKey(bwd.getColor())) { + values.put(bwd.getColor(), new ArrayList<>()); } - values.get(bwd.color).add(new PointValue(fuzz(bwd.timestamp), (float) value)); + values.get(bwd.getColor()).add(new PointValue(fuzz(bwd.getTimeStamp()), (float) value)); } } for (Map.Entry> entry : values.entrySet()) { @@ -358,7 +330,7 @@ public class BgGraphBuilder { public Line inRangeValuesLine() { Line inRangeValuesLine = new Line(inRangeValues); inRangeValuesLine.setColor(midColor); - if(singleLine) { + if (singleLine) { inRangeValuesLine.setHasLines(true); inRangeValuesLine.setHasPoints(false); inRangeValuesLine.setStrokeWidth(pointSize); @@ -371,19 +343,19 @@ public class BgGraphBuilder { } - public Line tempValuesLine(TempWatchData twd, float offset, double factor, boolean isHighlightLine, int strokeWidth) { - List lineValues = new ArrayList(); - long begin = Math.max(start_time, twd.startTime); - lineValues.add(new PointValue(fuzz(begin), offset + (float) (factor * twd.startBasal))); - lineValues.add(new PointValue(fuzz(begin), offset + (float) (factor * twd.amount))); - lineValues.add(new PointValue(fuzz(twd.endTime), offset + (float) (factor * twd.amount))); - lineValues.add(new PointValue(fuzz(twd.endTime), offset + (float) (factor * twd.endBasal))); + public Line tempValuesLine(EventData.TreatmentData.TempBasal twd, float offset, double factor, boolean isHighlightLine, int strokeWidth) { + List lineValues = new ArrayList<>(); + long begin = Math.max(start_time, twd.getStartTime()); + lineValues.add(new PointValue(fuzz(begin), offset + (float) (factor * twd.getStartBasal()))); + lineValues.add(new PointValue(fuzz(begin), offset + (float) (factor * twd.getAmount()))); + lineValues.add(new PointValue(fuzz(twd.getEndTime()), offset + (float) (factor * twd.getAmount()))); + lineValues.add(new PointValue(fuzz(twd.getEndTime()), offset + (float) (factor * twd.getEndBasal()))); Line valueLine = new Line(lineValues); valueLine.setHasPoints(false); - if (isHighlightLine){ + if (isHighlightLine) { valueLine.setColor(basalCenterColor); valueLine.setStrokeWidth(1); - }else { + } else { valueLine.setColor(basalBackgroundColor); valueLine.setStrokeWidth(strokeWidth); } @@ -391,38 +363,36 @@ public class BgGraphBuilder { } - - private void addBgReadingValues() { - if(singleLine) { - for (BgWatchData bgReading : bgDataList) { - if(bgReading.timestamp > start_time) { - if (bgReading.sgv >= 450) { - inRangeValues.add(new PointValue(fuzz(bgReading.timestamp), (float) 450)); - } else if (bgReading.sgv >= highMark) { - inRangeValues.add(new PointValue(fuzz(bgReading.timestamp), (float) bgReading.sgv)); - } else if (bgReading.sgv >= lowMark) { - inRangeValues.add(new PointValue(fuzz(bgReading.timestamp), (float) bgReading.sgv)); - } else if (bgReading.sgv >= 40) { - inRangeValues.add(new PointValue(fuzz(bgReading.timestamp), (float) bgReading.sgv)); - } else if (bgReading.sgv >= 11) { - inRangeValues.add(new PointValue(fuzz(bgReading.timestamp), (float) 40)); + if (singleLine) { + for (EventData.SingleBg bgReading : bgDataList) { + if (bgReading.getTimeStamp() > start_time) { + if (bgReading.getSgv() >= 450) { + inRangeValues.add(new PointValue(fuzz(bgReading.getTimeStamp()), (float) 450)); + } else if (bgReading.getSgv() >= highMark) { + inRangeValues.add(new PointValue(fuzz(bgReading.getTimeStamp()), (float) bgReading.getSgv())); + } else if (bgReading.getSgv() >= lowMark) { + inRangeValues.add(new PointValue(fuzz(bgReading.getTimeStamp()), (float) bgReading.getSgv())); + } else if (bgReading.getSgv() >= 40) { + inRangeValues.add(new PointValue(fuzz(bgReading.getTimeStamp()), (float) bgReading.getSgv())); + } else if (bgReading.getSgv() >= 11) { + inRangeValues.add(new PointValue(fuzz(bgReading.getTimeStamp()), (float) 40)); } } } } else { - for (BgWatchData bgReading : bgDataList) { - if (bgReading.timestamp > start_time) { - if (bgReading.sgv >= 450) { - highValues.add(new PointValue(fuzz(bgReading.timestamp), (float) 450)); - } else if (bgReading.sgv >= highMark) { - highValues.add(new PointValue(fuzz(bgReading.timestamp), (float) bgReading.sgv)); - } else if (bgReading.sgv >= lowMark) { - inRangeValues.add(new PointValue(fuzz(bgReading.timestamp), (float) bgReading.sgv)); - } else if (bgReading.sgv >= 40) { - lowValues.add(new PointValue(fuzz(bgReading.timestamp), (float) bgReading.sgv)); - } else if (bgReading.sgv >= 11) { - lowValues.add(new PointValue(fuzz(bgReading.timestamp), (float) 40)); + for (EventData.SingleBg bgReading : bgDataList) { + if (bgReading.getTimeStamp() > start_time) { + if (bgReading.getSgv() >= 450) { + highValues.add(new PointValue(fuzz(bgReading.getTimeStamp()), (float) 450)); + } else if (bgReading.getSgv() >= highMark) { + highValues.add(new PointValue(fuzz(bgReading.getTimeStamp()), (float) bgReading.getSgv())); + } else if (bgReading.getSgv() >= lowMark) { + inRangeValues.add(new PointValue(fuzz(bgReading.getTimeStamp()), (float) bgReading.getSgv())); + } else if (bgReading.getSgv() >= 40) { + lowValues.add(new PointValue(fuzz(bgReading.getTimeStamp()), (float) bgReading.getSgv())); + } else if (bgReading.getSgv() >= 11) { + lowValues.add(new PointValue(fuzz(bgReading.getTimeStamp()), (float) 40)); } } } @@ -430,7 +400,7 @@ public class BgGraphBuilder { } public Line highLine() { - List highLineValues = new ArrayList(); + List highLineValues = new ArrayList<>(); highLineValues.add(new PointValue(fuzz(start_time), (float) highMark)); highLineValues.add(new PointValue(fuzz(end_time), (float) highMark)); Line highLine = new Line(highLineValues); @@ -441,7 +411,7 @@ public class BgGraphBuilder { } public Line lowLine() { - List lowLineValues = new ArrayList(); + List lowLineValues = new ArrayList<>(); lowLineValues.add(new PointValue(fuzz(start_time), (float) lowMark)); lowLineValues.add(new PointValue(fuzz(end_time), (float) lowMark)); Line lowLine = new Line(lowLineValues); @@ -457,7 +427,7 @@ public class BgGraphBuilder { public Axis yAxis() { Axis yAxis = new Axis(); yAxis.setAutoGenerated(true); - List axisValues = new ArrayList(); + List axisValues = new ArrayList<>(); yAxis.setValues(axisValues); yAxis.setHasLines(false); yAxis.setLineColor(gridColour); @@ -466,13 +436,13 @@ public class BgGraphBuilder { public Axis xAxis() { final boolean is24 = DateFormat.is24HourFormat(context); - SimpleDateFormat timeFormat = new SimpleDateFormat(is24? "HH" : "h a"); + SimpleDateFormat timeFormat = new SimpleDateFormat(is24 ? "HH" : "h a"); timeFormat.setTimeZone(TimeZone.getDefault()); long timeNow = System.currentTimeMillis(); Axis xAxis = new Axis(); xAxis.setAutoGenerated(false); - List xAxisValues = new ArrayList(); + List xAxisValues = new ArrayList<>(); //get the time-tick at the full hour after start_time GregorianCalendar startGC = new GregorianCalendar(); @@ -484,14 +454,14 @@ public class BgGraphBuilder { long start_hour = startGC.getTimeInMillis(); //Display current time on the graph - SimpleDateFormat longTimeFormat = new SimpleDateFormat(is24? "HH:mm" : "h:mm a"); + SimpleDateFormat longTimeFormat = new SimpleDateFormat(is24 ? "HH:mm" : "h:mm a"); xAxisValues.add(new AxisValue(fuzz(timeNow)).setLabel((longTimeFormat.format(timeNow)))); long hourTick = start_hour; // add all full hours within the timeframe - while (hourTick < end_time){ - if(Math.abs(hourTick - timeNow) > (8 * (end_time-start_time)/60)){ + while (hourTick < end_time) { + if (Math.abs(hourTick - timeNow) > (8 * (end_time - start_time) / 60)) { xAxisValues.add(new AxisValue(fuzz(hourTick)).setLabel(timeFormat.format(hourTick))); } else { //don't print hour label if too close to now to avoid overlaps @@ -499,7 +469,7 @@ public class BgGraphBuilder { } //increment by one hour - hourTick += 60*60*1000; + hourTick += 60 * 60 * 1000; } xAxis.setValues(xAxisValues); @@ -513,16 +483,16 @@ public class BgGraphBuilder { public long getPredictionEndTime() { long maxPredictionDate = System.currentTimeMillis(); - for (BgWatchData prediction : + for (EventData.SingleBg prediction : predictionsList) { - if (maxPredictionDate < prediction.timestamp) { - maxPredictionDate = prediction.timestamp; + if (maxPredictionDate < prediction.getTimeStamp()) { + maxPredictionDate = prediction.getTimeStamp(); } } - return (long) Math.min(maxPredictionDate, System.currentTimeMillis() + MAX_PREDICTION__TIME_RATIO *timespan*1000*60*60); + return (long) Math.min(maxPredictionDate, System.currentTimeMillis() + MAX_PREDICTION__TIME_RATIO * timespan * 1000 * 60 * 60); } public float fuzz(long value) { - return (float) Math.round(value / fuzzyTimeDenom); + return (float) Math.round(value / fuzzyTimeDenom); } } diff --git a/wear/src/main/java/info/nightscout/androidaps/watchfaces/CircleWatchface.java b/wear/src/main/java/info/nightscout/androidaps/watchfaces/CircleWatchface.java index f4c7d18a90..d80b3e43d5 100644 --- a/wear/src/main/java/info/nightscout/androidaps/watchfaces/CircleWatchface.java +++ b/wear/src/main/java/info/nightscout/androidaps/watchfaces/CircleWatchface.java @@ -1,25 +1,15 @@ package info.nightscout.androidaps.watchfaces; -import android.content.BroadcastReceiver; +import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; -import android.content.SharedPreferences; import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.LinearGradient; -import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Point; import android.graphics.RectF; -import android.graphics.Shader; -import android.os.Bundle; import android.os.PowerManager; -import android.os.SystemClock; -import android.preference.PreferenceManager; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; import android.support.wearable.watchface.WatchFaceStyle; -import android.util.Log; import android.util.TypedValue; import android.view.Display; import android.view.LayoutInflater; @@ -27,21 +17,40 @@ import android.view.View; import android.view.WindowManager; import android.widget.TextView; -import com.google.android.gms.wearable.DataMap; import com.ustwo.clockwise.common.WatchFaceTime; import com.ustwo.clockwise.wearable.WatchFace; import java.util.ArrayList; import java.util.Calendar; -import java.util.HashSet; -import java.util.TreeSet; +import javax.inject.Inject; + +import dagger.android.AndroidInjection; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.BgWatchData; +import info.nightscout.androidaps.events.EventWearToMobile; import info.nightscout.androidaps.interaction.menus.MainMenuActivity; +import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.utils.rx.AapsSchedulers; +import info.nightscout.shared.logging.AAPSLogger; +import info.nightscout.shared.logging.LTag; +import info.nightscout.shared.sharedPreferences.SP; +import info.nightscout.shared.weardata.EventData; +import io.reactivex.rxjava3.disposables.CompositeDisposable; -public class CircleWatchface extends WatchFace implements SharedPreferences.OnSharedPreferenceChangeListener { +public class CircleWatchface extends WatchFace { + + @Inject RxBus rxBus; + @Inject AapsSchedulers aapsSchedulers; + @Inject AAPSLogger aapsLogger; + @Inject SP sp; + + CompositeDisposable disposable = new CompositeDisposable(); + + private EventData.SingleBg singleBg = new EventData.SingleBg(0, "---", "-", "--", "--", "--", 0, 0.0, 0.0, 0.0, 0); + private EventData.GraphData graphData; + private EventData.Status status = new EventData.Status("no status", "IOB", "-.--", false, "--g", "-.--U/h", "--", "--", -1, "--", false, 1); + public final float PADDING = 20f; public final float CIRCLE_WIDTH = 10f; public final int BIG_HAND_WIDTH = 16; @@ -52,43 +61,27 @@ public class CircleWatchface extends WatchFace implements SharedPreferences.OnSh //variables for time private float angleBig = 0f; private float angleSMALL = 0f; - private int hour, minute; private int color; private final Paint circlePaint = new Paint(); private final Paint removePaint = new Paint(); private RectF rect, rectDelete; private boolean overlapping; - private int animationAngle = 0; - private boolean isAnimated = false; - - public Point displaySize = new Point(); - private final MessageReceiver messageReceiver = new MessageReceiver(); - private int sgvLevel = 0; - private String sgvString = "999"; - private String statusString = "no status"; - - - private int batteryLevel = 0; - private long datetime = 0; - private String direction = ""; - private String delta = ""; - private String avgDelta = ""; - public TreeSet bgDataList = new TreeSet<>(); + public ArrayList bgDataList = new ArrayList<>(); private int specW; private int specH; private View myLayout; - protected SharedPreferences sharedPrefs; private TextView mSgv; private long sgvTapTime = 0; - @Override + @SuppressLint("InflateParams") @Override public void onCreate() { + AndroidInjection.inject(this); super.onCreate(); PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE); @@ -104,19 +97,46 @@ public class CircleWatchface extends WatchFace implements SharedPreferences.OnSh specH = View.MeasureSpec.makeMeasureSpec(displaySize.y, View.MeasureSpec.EXACTLY); - sharedPrefs = PreferenceManager - .getDefaultSharedPreferences(this); - sharedPrefs.registerOnSharedPreferenceChangeListener(this); - //register Message Receiver - LocalBroadcastManager.getInstance(this).registerReceiver(messageReceiver, new IntentFilter(Intent.ACTION_SEND)); LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); myLayout = inflater.inflate(R.layout.modern_layout, null); prepareLayout(); prepareDrawTime(); - //ListenerService.requestData(this); //usually connection is not set up yet + disposable.add(rxBus + .toObservable(EventData.SingleBg.class) + .observeOn(aapsSchedulers.getMain()) + .subscribe(event -> singleBg = event) + ); + disposable.add(rxBus + .toObservable(EventData.GraphData.class) + .observeOn(aapsSchedulers.getMain()) + .subscribe(event -> graphData = event) + ); + disposable.add(rxBus + .toObservable(EventData.Status.class) + .observeOn(aapsSchedulers.getMain()) + .subscribe(event -> { + // this event is received as last batch of data + aapsLogger.debug(LTag.WEAR, "Status received"); + status = event; + addToWatchSet(); + prepareLayout(); + prepareDrawTime(); + invalidate(); + }) + ); + disposable.add(rxBus + .toObservable(EventData.Preferences.class) + .observeOn(aapsSchedulers.getMain()) + .subscribe(event -> { + prepareDrawTime(); + prepareLayout(); + invalidate(); + }) + ); + rxBus.send(new EventWearToMobile(new EventData.ActionResendData("CircleWatchFace::onCreate"))); wakeLock.release(); } @@ -124,18 +144,13 @@ public class CircleWatchface extends WatchFace implements SharedPreferences.OnSh @Override public void onDestroy() { - if (messageReceiver != null) { - LocalBroadcastManager.getInstance(this).unregisterReceiver(messageReceiver); - } - if (sharedPrefs != null) { - sharedPrefs.unregisterOnSharedPreferenceChangeListener(this); - } + disposable.clear(); super.onDestroy(); } @Override protected synchronized void onDraw(Canvas canvas) { - Log.d("CircleWatchface", "start onDraw"); + aapsLogger.debug(LTag.WEAR, "start onDraw"); canvas.drawColor(getBackgroundColor()); drawTime(canvas); drawOtherStuff(canvas); @@ -145,27 +160,25 @@ public class CircleWatchface extends WatchFace implements SharedPreferences.OnSh private synchronized void prepareLayout() { - Log.d("CircleWatchface", "start startPrepareLayout"); + aapsLogger.debug(LTag.WEAR, "start startPrepareLayout"); // prepare fields - TextView textView; mSgv = myLayout.findViewById(R.id.sgvString); - textView = myLayout.findViewById(R.id.sgvString); - if (sharedPrefs.getBoolean("showBG", true)) { - textView.setVisibility(View.VISIBLE); - textView.setText(getSgvString()); - textView.setTextColor(getTextColor()); + if (sp.getBoolean("showBG", true)) { + mSgv.setVisibility(View.VISIBLE); + mSgv.setText(singleBg.getSgvString()); + mSgv.setTextColor(getTextColor()); } else { //Also possible: View.INVISIBLE instead of View.GONE (no layout change) - textView.setVisibility(View.INVISIBLE); + mSgv.setVisibility(View.INVISIBLE); } - textView = myLayout.findViewById(R.id.statusString); - if (sharedPrefs.getBoolean("showExternalStatus", true)) { + TextView textView = myLayout.findViewById(R.id.statusString); + if (sp.getBoolean("showExternalStatus", true)) { textView.setVisibility(View.VISIBLE); - textView.setText(getStatusString()); + textView.setText(status.getExternalStatus()); textView.setTextColor(getTextColor()); } else { @@ -174,10 +187,10 @@ public class CircleWatchface extends WatchFace implements SharedPreferences.OnSh } textView = myLayout.findViewById(R.id.agoString); - if (sharedPrefs.getBoolean("showAgo", true)) { + if (sp.getBoolean("showAgo", true)) { textView.setVisibility(View.VISIBLE); - if (sharedPrefs.getBoolean("showBigNumbers", false)) { + if (sp.getBoolean("showBigNumbers", false)) { textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 26); } else { ((TextView) myLayout.findViewById(R.id.agoString)).setTextSize(TypedValue.COMPLEX_UNIT_SP, 18); @@ -190,17 +203,17 @@ public class CircleWatchface extends WatchFace implements SharedPreferences.OnSh } textView = myLayout.findViewById(R.id.deltaString); - if (sharedPrefs.getBoolean("showDelta", true)) { + if (sp.getBoolean("showDelta", true)) { textView.setVisibility(View.VISIBLE); - textView.setText(getDelta()); + textView.setText(singleBg.getDelta()); textView.setTextColor(getTextColor()); - if (sharedPrefs.getBoolean("showBigNumbers", false)) { + if (sp.getBoolean("showBigNumbers", false)) { textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 25); } else { textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18); } - if (sharedPrefs.getBoolean("showAvgDelta", true)) { - textView.append(" " + getAvgDelta()); + if (sp.getBoolean("showAvgDelta", true)) { + textView.append(" " + singleBg.getAvgDelta()); } } else { @@ -215,8 +228,8 @@ public class CircleWatchface extends WatchFace implements SharedPreferences.OnSh public String getMinutes() { String minutes = "--'"; - if (getDatetime() != 0) { - minutes = ((int) Math.floor((System.currentTimeMillis() - getDatetime()) / 60000.0)) + "'"; + if (singleBg.getTimeStamp() != 0) { + minutes = ((int) Math.floor((System.currentTimeMillis() - singleBg.getTimeStamp()) / 60000.0)) + "'"; } return minutes; } @@ -249,16 +262,16 @@ public class CircleWatchface extends WatchFace implements SharedPreferences.OnSh } private synchronized void prepareDrawTime() { - Log.d("CircleWatchface", "start prepareDrawTime"); + aapsLogger.debug(LTag.WEAR, "start prepareDrawTime"); - hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY) % 12; - minute = Calendar.getInstance().get(Calendar.MINUTE); + int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY) % 12; + int minute = Calendar.getInstance().get(Calendar.MINUTE); angleBig = (((hour + minute / 60f) / 12f * 360) - 90 - BIG_HAND_WIDTH / 2f + 360) % 360; angleSMALL = ((minute / 60f * 360) - 90 - SMALL_HAND_WIDTH / 2f + 360) % 360; color = 0; - switch (getSgvLevel()) { + switch ((int) singleBg.getSgvLevel()) { case -1: color = getLowColor(); break; @@ -271,20 +284,7 @@ public class CircleWatchface extends WatchFace implements SharedPreferences.OnSh } - if (isAnimated()) { - //Animation matrix: - int[] rainbow = {Color.RED, Color.YELLOW, Color.GREEN, Color.BLUE - , Color.CYAN}; - Shader shader = new LinearGradient(0, 0, 0, 20, rainbow, - null, Shader.TileMode.MIRROR); - Matrix matrix = new Matrix(); - matrix.setRotate(animationAngle); - shader.setLocalMatrix(matrix); - circlePaint.setShader(shader); - } else { - circlePaint.setShader(null); - } - + circlePaint.setShader(null); circlePaint.setStyle(Paint.Style.STROKE); circlePaint.setStrokeWidth(CIRCLE_WIDTH); @@ -299,17 +299,10 @@ public class CircleWatchface extends WatchFace implements SharedPreferences.OnSh rect = new RectF(PADDING, PADDING, displaySize.x - PADDING, displaySize.y - PADDING); rectDelete = new RectF(PADDING - CIRCLE_WIDTH / 2, PADDING - CIRCLE_WIDTH / 2, displaySize.x - PADDING + CIRCLE_WIDTH / 2, displaySize.y - PADDING + CIRCLE_WIDTH / 2); overlapping = ALWAYS_HIGHLIGT_SMALL || areOverlapping(angleSMALL, angleSMALL + SMALL_HAND_WIDTH + NEAR, angleBig, angleBig + BIG_HAND_WIDTH + NEAR); - Log.d("CircleWatchface", "end prepareDrawTime"); + aapsLogger.debug(LTag.WEAR, "end prepareDrawTime"); } - synchronized void animationStep() { - animationAngle = (animationAngle + 1) % 360; - prepareDrawTime(); - invalidate(); - } - - private boolean areOverlapping(float aBegin, float aEnd, float bBegin, float bEnd) { return aBegin <= bBegin && aEnd >= bBegin || @@ -338,7 +331,7 @@ public class CircleWatchface extends WatchFace implements SharedPreferences.OnSh // defining color for dark and bright public int getLowColor() { - if (sharedPrefs.getBoolean("dark", true)) { + if (sp.getBoolean("dark", true)) { return Color.argb(255, 255, 120, 120); } else { return Color.argb(255, 255, 80, 80); @@ -346,7 +339,7 @@ public class CircleWatchface extends WatchFace implements SharedPreferences.OnSh } public int getInRangeColor() { - if (sharedPrefs.getBoolean("dark", true)) { + if (sp.getBoolean("dark", true)) { return Color.argb(255, 120, 255, 120); } else { return Color.argb(255, 0, 240, 0); @@ -355,7 +348,7 @@ public class CircleWatchface extends WatchFace implements SharedPreferences.OnSh } public int getHighColor() { - if (sharedPrefs.getBoolean("dark", true)) { + if (sp.getBoolean("dark", true)) { return Color.argb(255, 255, 255, 120); } else { return Color.argb(255, 255, 200, 0); @@ -364,7 +357,7 @@ public class CircleWatchface extends WatchFace implements SharedPreferences.OnSh } public int getBackgroundColor() { - if (sharedPrefs.getBoolean("dark", true)) { + if (sp.getBoolean("dark", true)) { return Color.BLACK; } else { return Color.WHITE; @@ -373,7 +366,7 @@ public class CircleWatchface extends WatchFace implements SharedPreferences.OnSh } public int getTextColor() { - if (sharedPrefs.getBoolean("dark", true)) { + if (sp.getBoolean("dark", true)) { return Color.WHITE; } else { return Color.BLACK; @@ -382,23 +375,22 @@ public class CircleWatchface extends WatchFace implements SharedPreferences.OnSh } public void drawOtherStuff(Canvas canvas) { - Log.d("CircleWatchface", "start onDrawOtherStuff. bgDataList.size(): " + bgDataList.size()); + aapsLogger.debug(LTag.WEAR, "start onDrawOtherStuff. bgDataList.size(): " + bgDataList.size()); - if (isAnimated()) return; // too many repaints when animated - if (sharedPrefs.getBoolean("showRingHistory", false)) { + if (sp.getBoolean("showRingHistory", false)) { //Perfect low and High indicators if (bgDataList.size() > 0) { addIndicator(canvas, 100, Color.LTGRAY); - addIndicator(canvas, (float) bgDataList.iterator().next().low, getLowColor()); - addIndicator(canvas, (float) bgDataList.iterator().next().high, getHighColor()); + addIndicator(canvas, (float) bgDataList.iterator().next().getLow(), getLowColor()); + addIndicator(canvas, (float) bgDataList.iterator().next().getHigh(), getHighColor()); - if (sharedPrefs.getBoolean("softRingHistory", true)) { - for (BgWatchData data : bgDataList) { + if (sp.getBoolean("softRingHistory", true)) { + for (EventData.SingleBg data : bgDataList) { addReadingSoft(canvas, data); } } else { - for (BgWatchData data : bgDataList) { + for (EventData.SingleBg data : bgDataList) { addReading(canvas, data); } } @@ -406,202 +398,15 @@ public class CircleWatchface extends WatchFace implements SharedPreferences.OnSh } } - public int holdInMemory() { - return 6; - } + public synchronized void addToWatchSet() { - //getters & setters + bgDataList.clear(); + if (!sp.getBoolean("showRingHistory", false)) return; - private synchronized int getSgvLevel() { - return sgvLevel; - } - - private synchronized void setSgvLevel(int sgvLevel) { - this.sgvLevel = sgvLevel; - } - - private synchronized int getBatteryLevel() { - return batteryLevel; - } - - private synchronized void setBatteryLevel(int batteryLevel) { - this.batteryLevel = batteryLevel; - } - - - private synchronized long getDatetime() { - return datetime; - } - - private synchronized void setDatetime(long datetime) { - this.datetime = datetime; - } - - private synchronized String getDirection() { - return direction; - } - - private void setDirection(String direction) { - this.direction = direction; - } - - String getSgvString() { - return sgvString; - } - - void setSgvString(String sgvString) { - this.sgvString = sgvString; - } - - String getStatusString() { - return statusString; - } - - void setStatusString(String statusString) { - this.statusString = statusString; - } - - public String getDelta() { - return delta; - } - - private void setDelta(String delta) { - this.delta = delta; - } - - private String getAvgDelta() { - return avgDelta; - } - - private void setAvgDelta(String avgDelta) { - this.avgDelta = avgDelta; - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - prepareDrawTime(); - prepareLayout(); - invalidate(); - } - - private synchronized boolean isAnimated() { - return isAnimated; - } - - private synchronized void setIsAnimated(boolean isAnimated) { - this.isAnimated = isAnimated; - } - - void startAnimation() { - Log.d("CircleWatchface", "start startAnimation"); - - Thread animator = new Thread() { - - - public void run() { - setIsAnimated(true); - for (int i = 0; i <= 8 * 1000 / 40; i++) { - animationStep(); - SystemClock.sleep(40); - } - setIsAnimated(false); - prepareDrawTime(); - invalidate(); - System.gc(); - } - }; - - animator.start(); - } - - - public class MessageReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE); - PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AndroidAPS:MessageReceiver"); - wakeLock.acquire(30000); - Bundle bundle = intent.getBundleExtra("data"); - if (bundle != null) { - DataMap dataMap = DataMap.fromBundle(bundle); - setSgvLevel((int) dataMap.getLong("sgvLevel")); - Log.d("CircleWatchface", "sgv level : " + getSgvLevel()); - setSgvString(dataMap.getString("sgvString")); - Log.d("CircleWatchface", "sgv string : " + getSgvString()); - setDelta(dataMap.getString("delta")); - setAvgDelta(dataMap.getString("avgDelta")); - setDatetime(dataMap.getLong("timestamp")); - addToWatchSet(dataMap); - - - //start animation? - // dataMap.getDataMapArrayList("entries") == null -> not on "resend data". - if (sharedPrefs.getBoolean("animation", false) && dataMap.getDataMapArrayList("entries") == null && (getSgvString().equals("100") || getSgvString().equals("5.5") || getSgvString().equals("5,5"))) { - startAnimation(); - } - - prepareLayout(); - prepareDrawTime(); - invalidate(); - } - //status - bundle = intent.getBundleExtra("status"); - if (bundle != null) { - DataMap dataMap = DataMap.fromBundle(bundle); - wakeLock.acquire(50); - setStatusString(dataMap.getString("externalStatusString")); - - prepareLayout(); - prepareDrawTime(); - invalidate(); - } - wakeLock.release(); - } - } - - public synchronized void addToWatchSet(DataMap dataMap) { - - if (!sharedPrefs.getBoolean("showRingHistory", false)) { - bgDataList.clear(); - return; - } - - Log.d("CircleWatchface", "start addToWatchSet"); - ArrayList entries = dataMap.getDataMapArrayList("entries"); - if (entries == null) { - double sgv = dataMap.getDouble("sgvDouble"); - double high = dataMap.getDouble("high"); - double low = dataMap.getDouble("low"); - long timestamp = dataMap.getLong("timestamp"); - int color = dataMap.getInt("color", 0); - bgDataList.add(new BgWatchData(sgv, high, low, timestamp, color)); - } else if (!sharedPrefs.getBoolean("animation", false)) { - // don't load history at once if animations are set (less resource consumption) - Log.d("addToWatchSet", "entries.size(): " + entries.size()); - - for (DataMap entry : entries) { - double sgv = entry.getDouble("sgvDouble"); - double high = entry.getDouble("high"); - double low = entry.getDouble("low"); - long timestamp = entry.getLong("timestamp"); - int color = entry.getInt("color", 0); - bgDataList.add(new BgWatchData(sgv, high, low, timestamp, color)); - } - } else - - Log.d("addToWatchSet", "start removing bgDataList.size(): " + bgDataList.size()); - HashSet removeSet = new HashSet<>(); - double threshold = (System.currentTimeMillis() - (1000 * 60 * 5 * holdInMemory())); - for (BgWatchData data : bgDataList) { - if (data.timestamp < threshold) { - removeSet.add(data); - - } - } - bgDataList.removeAll(removeSet); - Log.d("addToWatchSet", "after bgDataList.size(): " + bgDataList.size()); - removeSet = null; - System.gc(); + double threshold = (System.currentTimeMillis() - (1000L * 60 * 30)); // 30 min + for (EventData.SingleBg entry : graphData.getEntries()) + if (entry.getTimeStamp() >= threshold) bgDataList.add(entry); + aapsLogger.debug(LTag.WEAR, "addToWatchSet size=" + bgDataList.size()); } public int darken(int color, double fraction) { @@ -618,10 +423,7 @@ public class CircleWatchface extends WatchFace implements SharedPreferences.OnSh private int darkenColor(int color, double fraction) { - //if (sharedPrefs.getBoolean("dark", false)) { return (int) Math.max(color - (color * fraction), 0); - //} - // return (int)Math.min(color + (color * fraction), 255); } @@ -659,44 +461,46 @@ public class CircleWatchface extends WatchFace implements SharedPreferences.OnSh } - public void addReadingSoft(Canvas canvas, BgWatchData entry) { + public void addReadingSoft(Canvas canvas, EventData.SingleBg entry) { - Log.d("CircleWatchface", "addReadingSoft"); + aapsLogger.debug(LTag.WEAR, "addReadingSoft"); double size; int color = Color.LTGRAY; - if (sharedPrefs.getBoolean("dark", true)) { + if (sp.getBoolean("dark", true)) { color = Color.DKGRAY; } float offsetMultiplier = (((displaySize.x / 2f) - PADDING) / 12f); - float offset = (float) Math.max(1, Math.ceil((System.currentTimeMillis() - entry.timestamp) / (1000 * 60 * 5.0))); - size = bgToAngle((float) entry.sgv); + float offset = (float) Math.max(1, + Math.ceil((System.currentTimeMillis() - entry.getTimeStamp()) / (1000 * 60 * 5.0))); + size = bgToAngle((float) entry.getSgv()); addArch(canvas, offset * offsetMultiplier + 10, color, (float) size); addArch(canvas, (float) size, offset * offsetMultiplier + 10, getBackgroundColor(), (float) (360 - size)); addArch(canvas, (offset + .8f) * offsetMultiplier + 10, getBackgroundColor(), 360); } - public void addReading(Canvas canvas, BgWatchData entry) { - Log.d("CircleWatchface", "addReading"); + public void addReading(Canvas canvas, EventData.SingleBg entry) { + aapsLogger.debug(LTag.WEAR, "addReading"); double size; int color = Color.LTGRAY; int indicatorColor = Color.DKGRAY; - if (sharedPrefs.getBoolean("dark", true)) { + if (sp.getBoolean("dark", true)) { color = Color.DKGRAY; indicatorColor = Color.LTGRAY; } int barColor = Color.GRAY; - if (entry.sgv >= entry.high) { + if (entry.getSgv() >= entry.getHigh()) { indicatorColor = getHighColor(); barColor = darken(getHighColor(), .5); - } else if (entry.sgv <= entry.low) { + } else if (entry.getSgv() <= entry.getLow()) { indicatorColor = getLowColor(); barColor = darken(getLowColor(), .5); } float offsetMultiplier = (((displaySize.x / 2f) - PADDING) / 12f); - float offset = (float) Math.max(1, Math.ceil((System.currentTimeMillis() - entry.timestamp) / (1000 * 60 * 5.0))); - size = bgToAngle((float) entry.sgv); + float offset = (float) Math.max(1, + Math.ceil((System.currentTimeMillis() - entry.getTimeStamp()) / (1000 * 60 * 5.0))); + size = bgToAngle((float) entry.getSgv()); addArch(canvas, offset * offsetMultiplier + 11, barColor, (float) size - 2); // Dark Color Bar addArch(canvas, (float) size - 2, offset * offsetMultiplier + 11, indicatorColor, 2f); // Indicator at end of bar addArch(canvas, (float) size, offset * offsetMultiplier + 11, color, (float) (360f - size)); // Dark fill @@ -705,8 +509,8 @@ public class CircleWatchface extends WatchFace implements SharedPreferences.OnSh @Override protected void onTapCommand(int tapType, int x, int y, long eventTime) { - - int extra = mSgv != null ? (mSgv.getRight() - mSgv.getLeft()) / 2 : 0; + if (mSgv == null) return; + int extra = (mSgv.getRight() - mSgv.getLeft()) / 2; if (tapType == TAP_TYPE_TAP && x + extra >= mSgv.getLeft() && @@ -726,6 +530,4 @@ public class CircleWatchface extends WatchFace implements SharedPreferences.OnSh protected WatchFaceStyle getWatchFaceStyle() { return new WatchFaceStyle.Builder(this).setAcceptsTapEvents(true).build(); } - - } \ No newline at end of file diff --git a/wear/src/main/java/info/nightscout/androidaps/watchfaces/Cockpit.java b/wear/src/main/java/info/nightscout/androidaps/watchfaces/Cockpit.java index 3a68158109..9ac98fe0ce 100644 --- a/wear/src/main/java/info/nightscout/androidaps/watchfaces/Cockpit.java +++ b/wear/src/main/java/info/nightscout/androidaps/watchfaces/Cockpit.java @@ -1,5 +1,6 @@ package info.nightscout.androidaps.watchfaces; +import android.annotation.SuppressLint; import android.content.Intent; import android.support.wearable.watchface.WatchFaceStyle; import android.view.LayoutInflater; @@ -16,7 +17,7 @@ public class Cockpit extends BaseWatchFace { private long sgvTapTime = 0; - @Override + @SuppressLint("InflateParams") @Override public void onCreate() { super.onCreate(); LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); @@ -48,13 +49,13 @@ public class Cockpit extends BaseWatchFace { setTextSizes(); if (mHighLight != null && mLowLight != null) { - if (rawData.sgvLevel == 1) { + if (singleBg.getSgvLevel() == 1) { mHighLight.setBackgroundResource(R.drawable.airplane_led_yellow_lit); mLowLight.setBackgroundResource(R.drawable.airplane_led_grey_unlit); - } else if (rawData.sgvLevel == 0) { + } else if (singleBg.getSgvLevel() == 0) { mHighLight.setBackgroundResource(R.drawable.airplane_led_grey_unlit); mLowLight.setBackgroundResource(R.drawable.airplane_led_grey_unlit); - } else if (rawData.sgvLevel == -1) { + } else if (singleBg.getSgvLevel() == -1) { mHighLight.setBackgroundResource(R.drawable.airplane_led_grey_unlit); mLowLight.setBackgroundResource(R.drawable.airplane_led_red_lit); } @@ -84,7 +85,7 @@ public class Cockpit extends BaseWatchFace { protected void setTextSizes() { if (mIOB2 != null) { - if (rawData.detailedIOB) { + if (status.getDetailedIob()) { if (bIsRound) { mIOB2.setTextSize(10); } else { diff --git a/wear/src/main/java/info/nightscout/androidaps/watchfaces/DigitalStyle.java b/wear/src/main/java/info/nightscout/androidaps/watchfaces/DigitalStyle.java index 2b3975e949..b44f3c95dd 100644 --- a/wear/src/main/java/info/nightscout/androidaps/watchfaces/DigitalStyle.java +++ b/wear/src/main/java/info/nightscout/androidaps/watchfaces/DigitalStyle.java @@ -1,5 +1,6 @@ package info.nightscout.androidaps.watchfaces; +import android.annotation.SuppressLint; import android.content.Intent; import android.content.res.ColorStateList; import android.support.wearable.watchface.WatchFaceStyle; @@ -19,10 +20,9 @@ import info.nightscout.androidaps.interaction.menus.MainMenuActivity; public class DigitalStyle extends BaseWatchFace { private static final long TIME_TAP_THRESHOLD = 800; - private final long chartTapTime = 0; private long sgvTapTime = 0; - @Override + @SuppressLint("InflateParams") @Override public void onCreate() { super.onCreate(); LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); @@ -57,13 +57,13 @@ public class DigitalStyle extends BaseWatchFace { } protected void setColorDark() { - if (rawData.sgvLevel == 1) { + if (singleBg.getSgvLevel() == 1) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor)); mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor)); - } else if (rawData.sgvLevel == 0) { + } else if (singleBg.getSgvLevel() == 0) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor)); mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor)); - } else if (rawData.sgvLevel == -1) { + } else if (singleBg.getSgvLevel() == -1) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor)); mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor)); } @@ -74,7 +74,7 @@ public class DigitalStyle extends BaseWatchFace { mTimestamp.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_TimestampOld)); } - if (rawData.batteryLevel == 1) { + if (status.getBatteryLevel() == 1) { mUploaderBattery.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor)); } else { mUploaderBattery.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_uploaderBatteryEmpty)); @@ -99,10 +99,11 @@ public class DigitalStyle extends BaseWatchFace { LinearLayout mShapesElements = layoutView.findViewById(R.id.shapes_elements); if (mShapesElements != null) { String displayFormatType = (mShapesElements.getContentDescription().toString().startsWith("round") ? "round" : "rect"); - String displayStyle=sharedPrefs.getString("digitalstyle_frameStyle", "full"); - String displayFrameColor=sharedPrefs.getString("digitalstyle_frameColor", "red"); - String displayFrameColorSaturation=sharedPrefs.getString("digitalstyle_frameColorSaturation", "500"); - String displayFrameColorOpacity=sharedPrefs.getString("digitalstyle_frameColorOpacity", "1"); + String displayStyle=sp.getString("digitalstyle_frameStyle", "full"); + String displayFrameColor=sp.getString("digitalstyle_frameColor", "red"); + String displayFrameColorSaturation=sp.getString("digitalstyle_frameColorSaturation", + "500"); + String displayFrameColorOpacity=sp.getString("digitalstyle_frameColorOpacity", "1"); // Load image with shapes String styleDrawableName = "digitalstyle_bg_" + displayStyle + "_" + displayFormatType; @@ -133,7 +134,7 @@ public class DigitalStyle extends BaseWatchFace { } /* optimize font-size --> when date is off then increase font-size of time */ - Boolean isShowDate = sharedPrefs.getBoolean("show_date", false); + Boolean isShowDate = sp.getBoolean("show_date", false); if (!isShowDate) { layoutView.findViewById(R.id.date_time).setVisibility(View.GONE); mHour.setTextSize(62); @@ -148,7 +149,7 @@ public class DigitalStyle extends BaseWatchFace { mMinute.setLetterSpacing((float) 0); /* display week number */ - Boolean isShowWeekNumber = sharedPrefs.getBoolean("show_weeknumber", false); + Boolean isShowWeekNumber = sp.getBoolean("show_weeknumber", false); Log.i("---------------------------------","weeknumber refresh "); TextView mWeekNumber= layoutView.findViewById(R.id.weeknumber); if (isShowWeekNumber) { diff --git a/wear/src/main/java/info/nightscout/androidaps/watchfaces/Home.java b/wear/src/main/java/info/nightscout/androidaps/watchfaces/Home.java index ce84f97e46..fb15053f51 100644 --- a/wear/src/main/java/info/nightscout/androidaps/watchfaces/Home.java +++ b/wear/src/main/java/info/nightscout/androidaps/watchfaces/Home.java @@ -1,5 +1,6 @@ package info.nightscout.androidaps.watchfaces; +import android.annotation.SuppressLint; import android.content.Intent; import android.graphics.Color; import androidx.core.content.ContextCompat; @@ -16,7 +17,7 @@ public class Home extends BaseWatchFace { private long chartTapTime = 0; private long sgvTapTime = 0; - @Override + @SuppressLint("InflateParams") @Override public void onCreate() { super.onCreate(); LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); @@ -53,9 +54,9 @@ public class Home extends BaseWatchFace { } private void changeChartTimeframe() { - int timeframe = Integer.parseInt(sharedPrefs.getString("chart_timeframe", "3")); + int timeframe = sp.getInt("chart_timeframe", 3); timeframe = (timeframe%5) + 1; - sharedPrefs.edit().putString("chart_timeframe", "" + timeframe).apply(); + sp.putString("chart_timeframe", "" + timeframe); } @Override @@ -69,15 +70,15 @@ public class Home extends BaseWatchFace { R.color.dark_background : R.color.dark_statusView)); mTime.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_mTime)); mRelativeLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_background)); - if (rawData.sgvLevel == 1) { + if (singleBg.getSgvLevel() == 1) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor)); mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor)); mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor)); - } else if (rawData.sgvLevel == 0) { + } else if (singleBg.getSgvLevel() == 0) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor)); mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor)); mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor)); - } else if (rawData.sgvLevel == -1) { + } else if (singleBg.getSgvLevel() == -1) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor)); mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor)); mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor)); @@ -90,7 +91,7 @@ public class Home extends BaseWatchFace { mTimestamp.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_TimestampOld)); } - if (rawData.batteryLevel == 1) { + if (status.getBatteryLevel() == 1) { mUploaderBattery.setTextColor(ContextCompat.getColor(getApplicationContext(), dividerMatchesBg ? R.color.dark_midColor : R.color.dark_uploaderBattery)); } else { @@ -138,15 +139,15 @@ public class Home extends BaseWatchFace { mLinearLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), dividerMatchesBg ? R.color.light_background : R.color.light_stripe_background)); mRelativeLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.light_background)); - if (rawData.sgvLevel == 1) { + if (singleBg.getSgvLevel() == 1) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_highColor)); mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_highColor)); mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_highColor)); - } else if (rawData.sgvLevel == 0) { + } else if (singleBg.getSgvLevel() == 0) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_midColor)); mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_midColor)); mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_midColor)); - } else if (rawData.sgvLevel == -1) { + } else if (singleBg.getSgvLevel() == -1) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_lowColor)); mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_lowColor)); mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_lowColor)); @@ -158,7 +159,7 @@ public class Home extends BaseWatchFace { mTimestamp.setTextColor(Color.RED); } - if (rawData.batteryLevel == 1) { + if (status.getBatteryLevel() == 1) { mUploaderBattery.setTextColor(dividerMatchesBg ? Color.BLACK : Color.WHITE); } else { mUploaderBattery.setTextColor(Color.RED); diff --git a/wear/src/main/java/info/nightscout/androidaps/watchfaces/Home2.java b/wear/src/main/java/info/nightscout/androidaps/watchfaces/Home2.java index 9f1cb75ddd..21a600ce5f 100644 --- a/wear/src/main/java/info/nightscout/androidaps/watchfaces/Home2.java +++ b/wear/src/main/java/info/nightscout/androidaps/watchfaces/Home2.java @@ -1,5 +1,6 @@ package info.nightscout.androidaps.watchfaces; +import android.annotation.SuppressLint; import android.content.Intent; import android.graphics.Color; @@ -19,7 +20,7 @@ public class Home2 extends BaseWatchFace { private long chartTapTime = 0; private long sgvTapTime = 0; - @Override + @SuppressLint("InflateParams") @Override public void onCreate() { super.onCreate(); LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); @@ -57,9 +58,9 @@ public class Home2 extends BaseWatchFace { } private void changeChartTimeframe() { - int timeframe = Integer.parseInt(sharedPrefs.getString("chart_timeframe", "3")); + int timeframe = sp.getInt("chart_timeframe", 3); timeframe = (timeframe % 5) + 1; - sharedPrefs.edit().putString("chart_timeframe", "" + timeframe).apply(); + sp.putString("chart_timeframe", "" + timeframe); } @Override @@ -89,13 +90,13 @@ public class Home2 extends BaseWatchFace { setTextSizes(); - if (rawData.sgvLevel == 1) { + if (singleBg.getSgvLevel() == 1) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor)); mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor)); - } else if (rawData.sgvLevel == 0) { + } else if (singleBg.getSgvLevel() == 0) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor)); mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor)); - } else if (rawData.sgvLevel == -1) { + } else if (singleBg.getSgvLevel() == -1) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor)); mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor)); } @@ -106,7 +107,7 @@ public class Home2 extends BaseWatchFace { mTimestamp.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_TimestampOld)); } - if (rawData.batteryLevel == 1) { + if (status.getBatteryLevel() == 1) { mUploaderBattery.setTextColor(dividerBatteryOkColor); } else { mUploaderBattery.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_uploaderBatteryEmpty)); @@ -200,13 +201,13 @@ public class Home2 extends BaseWatchFace { setTextSizes(); - if (rawData.sgvLevel == 1) { + if (singleBg.getSgvLevel() == 1) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_highColor)); mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_highColor)); - } else if (rawData.sgvLevel == 0) { + } else if (singleBg.getSgvLevel() == 0) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_midColor)); mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_midColor)); - } else if (rawData.sgvLevel == -1) { + } else if (singleBg.getSgvLevel() == -1) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_lowColor)); mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_lowColor)); } @@ -217,7 +218,7 @@ public class Home2 extends BaseWatchFace { mTimestamp.setTextColor(Color.RED); } - if (rawData.batteryLevel == 1) { + if (status.getBatteryLevel() == 1) { mUploaderBattery.setTextColor(dividerTxtColor); } else { mUploaderBattery.setTextColor(Color.RED); @@ -255,7 +256,7 @@ public class Home2 extends BaseWatchFace { if (mIOB1 != null && mIOB2 != null) { - if (rawData.detailedIOB) { + if (status.getDetailedIob()) { mIOB1.setTextSize(14); mIOB2.setTextSize(10); } else { diff --git a/wear/src/main/java/info/nightscout/androidaps/watchfaces/LargeHome.java b/wear/src/main/java/info/nightscout/androidaps/watchfaces/LargeHome.java index efce6baab7..29774131ef 100644 --- a/wear/src/main/java/info/nightscout/androidaps/watchfaces/LargeHome.java +++ b/wear/src/main/java/info/nightscout/androidaps/watchfaces/LargeHome.java @@ -1,5 +1,6 @@ package info.nightscout.androidaps.watchfaces; +import android.annotation.SuppressLint; import android.content.Intent; import android.graphics.Color; import androidx.core.content.ContextCompat; @@ -15,7 +16,7 @@ public class LargeHome extends BaseWatchFace { private long sgvTapTime = 0; - @Override + @SuppressLint("InflateParams") @Override public void onCreate() { super.onCreate(); LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); @@ -25,8 +26,8 @@ public class LargeHome extends BaseWatchFace { @Override protected void onTapCommand(int tapType, int x, int y, long eventTime) { - - int extra = mSgv!=null?(mSgv.getRight() - mSgv.getLeft())/2:0; + if (mSgv == null) return; + int extra = (mSgv.getRight() - mSgv.getLeft())/2; if (tapType == TAP_TYPE_TAP&& x + extra >=mSgv.getLeft() && @@ -53,15 +54,15 @@ public class LargeHome extends BaseWatchFace { R.color.dark_background : R.color.dark_mLinearLayout)); mTime.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_mTime)); mRelativeLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_background)); - if (rawData.sgvLevel == 1) { + if (singleBg.getSgvLevel() == 1) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor)); mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor)); mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_highColor)); - } else if (rawData.sgvLevel == 0) { + } else if (singleBg.getSgvLevel() == 0) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor)); mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor)); mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_midColor)); - } else if (rawData.sgvLevel == -1) { + } else if (singleBg.getSgvLevel() == -1) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor)); mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor)); mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_lowColor)); @@ -74,7 +75,7 @@ public class LargeHome extends BaseWatchFace { mTimestamp.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_TimestampOld)); } - if (rawData.batteryLevel == 1) { + if (status.getBatteryLevel() == 1) { mUploaderBattery.setTextColor(ContextCompat.getColor(getApplicationContext(), dividerMatchesBg ? R.color.dark_midColor : R.color.dark_uploaderBattery)); } else { @@ -90,15 +91,15 @@ public class LargeHome extends BaseWatchFace { mLinearLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), dividerMatchesBg ? R.color.light_background : R.color.light_stripe_background)); mRelativeLayout.setBackgroundColor(ContextCompat.getColor(getApplicationContext(), R.color.light_background)); - if (rawData.sgvLevel == 1) { + if (singleBg.getSgvLevel() == 1) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_highColor)); mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_highColor)); mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_highColor)); - } else if (rawData.sgvLevel == 0) { + } else if (singleBg.getSgvLevel() == 0) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_midColor)); mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_midColor)); mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_midColor)); - } else if (rawData.sgvLevel == -1) { + } else if (singleBg.getSgvLevel() == -1) { mSgv.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_lowColor)); mDelta.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_lowColor)); mDirection.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.light_lowColor)); @@ -110,7 +111,7 @@ public class LargeHome extends BaseWatchFace { mTimestamp.setTextColor(Color.RED); } - if (rawData.batteryLevel == 1) { + if (status.getBatteryLevel() == 1) { mUploaderBattery.setTextColor(dividerMatchesBg ? Color.BLACK : Color.WHITE); } else { mUploaderBattery.setTextColor(Color.RED); @@ -120,15 +121,15 @@ public class LargeHome extends BaseWatchFace { } else { mRelativeLayout.setBackgroundColor(Color.BLACK); mLinearLayout.setBackgroundColor(dividerMatchesBg ? Color.BLACK : Color.LTGRAY); - if (rawData.sgvLevel == 1) { + if (singleBg.getSgvLevel() == 1) { mSgv.setTextColor(Color.YELLOW); mDirection.setTextColor(Color.YELLOW); mDelta.setTextColor(Color.YELLOW); - } else if (rawData.sgvLevel == 0) { + } else if (singleBg.getSgvLevel() == 0) { mSgv.setTextColor(Color.WHITE); mDirection.setTextColor(Color.WHITE); mDelta.setTextColor(Color.WHITE); - } else if (rawData.sgvLevel == -1) { + } else if (singleBg.getSgvLevel() == -1) { mSgv.setTextColor(Color.RED); mDirection.setTextColor(Color.RED); mDelta.setTextColor(Color.RED); 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 ed773c68d9..4091e9a8db 100644 --- a/wear/src/main/java/info/nightscout/androidaps/watchfaces/NOChart.java +++ b/wear/src/main/java/info/nightscout/androidaps/watchfaces/NOChart.java @@ -1,29 +1,17 @@ package info.nightscout.androidaps.watchfaces; -import android.content.BroadcastReceiver; +import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; -import android.content.SharedPreferences; import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.LinearGradient; -import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Point; import android.graphics.Rect; -import android.graphics.Shader; -import android.os.Bundle; import android.os.PowerManager; -import android.os.SystemClock; -import android.preference.PreferenceManager; -import androidx.core.content.ContextCompat; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; import android.support.wearable.view.WatchViewStub; import android.support.wearable.watchface.WatchFaceStyle; import android.text.format.DateFormat; import android.util.DisplayMetrics; -import android.util.Log; import android.view.Display; import android.view.LayoutInflater; import android.view.View; @@ -32,56 +20,60 @@ import android.view.WindowManager; import android.widget.RelativeLayout; import android.widget.TextView; -import com.google.android.gms.wearable.DataMap; +import androidx.core.content.ContextCompat; + import com.ustwo.clockwise.common.WatchFaceTime; import com.ustwo.clockwise.common.WatchMode; import com.ustwo.clockwise.common.WatchShape; import com.ustwo.clockwise.wearable.WatchFace; -import java.util.ArrayList; +import javax.inject.Inject; +import dagger.android.AndroidInjection; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.data.BasalWatchData; -import info.nightscout.androidaps.data.BgWatchData; -import info.nightscout.androidaps.data.DataLayerListenerService; -import info.nightscout.androidaps.data.TempWatchData; +import info.nightscout.androidaps.events.EventWearToMobile; import info.nightscout.androidaps.interaction.menus.MainMenuActivity; +import info.nightscout.androidaps.plugins.bus.RxBus; +import info.nightscout.androidaps.utils.rx.AapsSchedulers; +import info.nightscout.shared.logging.AAPSLogger; +import info.nightscout.shared.logging.LTag; +import info.nightscout.shared.sharedPreferences.SP; +import info.nightscout.shared.weardata.EventData; +import io.reactivex.rxjava3.disposables.CompositeDisposable; /** * Created by adrianLxM. */ -public class NOChart extends WatchFace implements SharedPreferences.OnSharedPreferenceChangeListener { - public final static IntentFilter INTENT_FILTER; +public class NOChart extends WatchFace { + + @Inject RxBus rxBus; + @Inject AapsSchedulers aapsSchedulers; + @Inject AAPSLogger aapsLogger; + @Inject SP sp; + + CompositeDisposable disposable = new CompositeDisposable(); + + private EventData.SingleBg singleBg; + private EventData.Status status; + public static final int SCREENSIZE_SMALL = 280; public TextView mTime, mSgv, mTimestamp, mDelta, mAvgDelta; public RelativeLayout mRelativeLayout; public long sgvLevel = 0; - public int batteryLevel = 1; public int ageLevel = 1; public boolean lowResMode = false; public boolean layoutSet = false; - public long datetime; - public ArrayList bgDataList = new ArrayList<>(); - public ArrayList tempWatchDataList = new ArrayList<>(); - public ArrayList basalWatchDataList = new ArrayList<>(); public PowerManager.WakeLock wakeLock; public View layoutView; private final Point displaySize = new Point(); private int specW, specH; - private int animationAngle = 0; - private boolean isAnimated = false; - private LocalBroadcastManager localBroadcastManager; - private MessageReceiver messageReceiver; - - protected SharedPreferences sharedPrefs; - private String sgvString = "--"; - private String externalStatusString = "no status"; private TextView statusView; private long sgvTapTime = 0L; - @Override + @SuppressLint("InflateParams") @Override public void onCreate() { + AndroidInjection.inject(this); super.onCreate(); Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)) .getDefaultDisplay(); @@ -92,17 +84,60 @@ public class NOChart extends WatchFace implements SharedPreferences.OnSharedPref View.MeasureSpec.EXACTLY); specH = View.MeasureSpec.makeMeasureSpec(displaySize.y, View.MeasureSpec.EXACTLY); - sharedPrefs = PreferenceManager - .getDefaultSharedPreferences(this); - sharedPrefs.registerOnSharedPreferenceChangeListener(this); LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); - layoutView = inflater.inflate(R.layout.activity_nochart, null); + DisplayMetrics metrics = getResources().getDisplayMetrics(); - if(metrics.widthPixels < SCREENSIZE_SMALL || metrics.heightPixels < SCREENSIZE_SMALL){ + if (metrics.widthPixels < SCREENSIZE_SMALL || metrics.heightPixels < SCREENSIZE_SMALL) { layoutView = inflater.inflate(R.layout.activity_nochart_small, null); } else { layoutView = inflater.inflate(R.layout.activity_nochart, null); } + disposable.add(rxBus + .toObservable(EventData.SingleBg.class) + .observeOn(aapsSchedulers.getMain()) + .subscribe(event -> { + aapsLogger.debug(LTag.WEAR, "SingleBg received"); + singleBg = event; + + mSgv.setText(singleBg.getSgvString()); + if (ageLevel() <= 0) + mSgv.setPaintFlags(mSgv.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); + else mSgv.setPaintFlags(mSgv.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG); + final java.text.DateFormat timeFormat = DateFormat.getTimeFormat(this); + mTime.setText(timeFormat.format(System.currentTimeMillis())); + mDelta.setText(singleBg.getDelta()); + mAvgDelta.setText(singleBg.getAvgDelta()); + }) + ); + disposable.add(rxBus + .toObservable(EventData.Status.class) + .observeOn(aapsSchedulers.getMain()) + .subscribe(event -> { + // this event is received as last batch of data + aapsLogger.debug(LTag.WEAR, "Status received"); + status = event; + showAgeAndStatus(); + mRelativeLayout.measure(specW, specH); + mRelativeLayout.layout(0, 0, mRelativeLayout.getMeasuredWidth(), + mRelativeLayout.getMeasuredHeight()); + invalidate(); + setColor(); + }) + ); + disposable.add(rxBus + .toObservable(EventData.Preferences.class) + .observeOn(aapsSchedulers.getMain()) + .subscribe(event -> { + setColor(); + if (layoutSet) { + showAgeAndStatus(); + mRelativeLayout.measure(specW, specH); + mRelativeLayout.layout(0, 0, mRelativeLayout.getMeasuredWidth(), + mRelativeLayout.getMeasuredHeight()); + } + invalidate(); + }) + ); performViewSetup(); } @@ -114,44 +149,37 @@ public class NOChart extends WatchFace implements SharedPreferences.OnSharedPref public void performViewSetup() { final WatchViewStub stub = layoutView.findViewById(R.id.watch_view_stub); - IntentFilter messageFilter = new IntentFilter(Intent.ACTION_SEND); - messageReceiver = new MessageReceiver(); - localBroadcastManager = LocalBroadcastManager.getInstance(this); - localBroadcastManager.registerReceiver(messageReceiver, messageFilter); - - stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() { - @Override - public void onLayoutInflated(WatchViewStub stub) { - mTime = stub.findViewById(R.id.watch_time); - mSgv = stub.findViewById(R.id.sgv); - mTimestamp = stub.findViewById(R.id.timestamp); - mDelta = stub.findViewById(R.id.delta); - mAvgDelta = stub.findViewById(R.id.avgdelta); - mRelativeLayout = stub.findViewById(R.id.main_layout); - statusView = stub.findViewById(R.id.aps_status); - layoutSet = true; - showAgeAndStatus(); - mRelativeLayout.measure(specW, specH); - mRelativeLayout.layout(0, 0, mRelativeLayout.getMeasuredWidth(), - mRelativeLayout.getMeasuredHeight()); - } + stub.setOnLayoutInflatedListener(stub1 -> { + mTime = stub1.findViewById(R.id.watch_time); + mSgv = stub1.findViewById(R.id.sgv); + mTimestamp = stub1.findViewById(R.id.timestamp); + mDelta = stub1.findViewById(R.id.delta); + mAvgDelta = stub1.findViewById(R.id.avgdelta); + mRelativeLayout = stub1.findViewById(R.id.main_layout); + statusView = stub1.findViewById(R.id.aps_status); + layoutSet = true; + showAgeAndStatus(); + mRelativeLayout.measure(specW, specH); + mRelativeLayout.layout(0, 0, mRelativeLayout.getMeasuredWidth(), + mRelativeLayout.getMeasuredHeight()); }); - DataLayerListenerService.Companion.requestData(this); + rxBus.send(new EventWearToMobile(new EventData.ActionResendData("NOChart" + + ":performViewSetup"))); wakeLock.acquire(50); } @Override protected void onTapCommand(int tapType, int x, int y, long eventTime) { + if (mSgv == null) return; + int extra = (mSgv.getRight() - mSgv.getLeft()) / 2; - int extra = mSgv!=null?(mSgv.getRight() - mSgv.getLeft())/2:0; - - if (tapType == TAP_TYPE_TAP&& - x + extra >=mSgv.getLeft() && - x - extra <= mSgv.getRight()&& + if (tapType == TAP_TYPE_TAP && + x + extra >= mSgv.getLeft() && + x - extra <= mSgv.getRight() && y >= mSgv.getTop() && - y <= mSgv.getBottom()){ - if (eventTime - sgvTapTime < 800){ + y <= mSgv.getBottom()) { + if (eventTime - sgvTapTime < 800) { Intent intent = new Intent(this, MainMenuActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); @@ -162,10 +190,10 @@ public class NOChart extends WatchFace implements SharedPreferences.OnSharedPref protected void onWatchModeChanged(WatchMode watchMode) { - if(lowResMode ^ isLowRes(watchMode)){ //if there was a change in lowResMode + if (lowResMode ^ isLowRes(watchMode)) { //if there was a change in lowResMode lowResMode = isLowRes(watchMode); setColor(); - } else if (! sharedPrefs.getBoolean("dark", true)){ + } else if (!sp.getBoolean("dark", true)) { //in bright mode: different colours if active: setColor(); } @@ -177,14 +205,13 @@ public class NOChart extends WatchFace implements SharedPreferences.OnSharedPref @Override - protected WatchFaceStyle getWatchFaceStyle(){ + protected WatchFaceStyle getWatchFaceStyle() { return new WatchFaceStyle.Builder(this).setAcceptsTapEvents(true).build(); } - public int ageLevel() { - if(timeSince() <= (1000 * 60 * 12)) { + if (timeSince() <= (1000 * 60 * 12)) { return 1; } else { return 0; @@ -192,40 +219,30 @@ public class NOChart extends WatchFace implements SharedPreferences.OnSharedPref } public double timeSince() { - return System.currentTimeMillis() - datetime; + return System.currentTimeMillis() - singleBg.getTimeStamp(); } public String readingAge(boolean shortString) { - if (datetime == 0) { return shortString?"--'":"-- Minute ago"; } - int minutesAgo = (int) Math.floor(timeSince()/(1000*60)); - if (minutesAgo == 1) { - return minutesAgo + (shortString?"'":" Minute ago"); + if (singleBg == null || singleBg.getTimeStamp() == 0) { + return shortString ? "--'" : "-- Minute ago"; } - return minutesAgo + (shortString?"'":" Minutes ago"); + int minutesAgo = (int) Math.floor(timeSince() / (1000 * 60)); + if (minutesAgo == 1) { + return minutesAgo + (shortString ? "'" : " Minute ago"); + } + return minutesAgo + (shortString ? "'" : " Minutes ago"); } @Override public void onDestroy() { - if(localBroadcastManager != null && messageReceiver != null){ - localBroadcastManager.unregisterReceiver(messageReceiver);} - if (sharedPrefs != null){ - sharedPrefs.unregisterOnSharedPreferenceChangeListener(this); - } + disposable.clear(); super.onDestroy(); } - static { - INTENT_FILTER = new IntentFilter(); - INTENT_FILTER.addAction(Intent.ACTION_TIME_TICK); - INTENT_FILTER.addAction(Intent.ACTION_TIMEZONE_CHANGED); - INTENT_FILTER.addAction(Intent.ACTION_TIME_CHANGED); - } - @Override protected void onDraw(Canvas canvas) { - if(layoutSet) { + if (layoutSet) { this.mRelativeLayout.draw(canvas); - Log.d("onDraw", "draw"); } } @@ -237,7 +254,7 @@ public class NOChart extends WatchFace implements SharedPreferences.OnSharedPref mTime.setText(timeFormat.format(System.currentTimeMillis())); showAgeAndStatus(); - if(ageLevel()<=0) { + if (ageLevel() <= 0) { mSgv.setPaintFlags(mSgv.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); } else { mSgv.setPaintFlags(mSgv.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG); @@ -250,142 +267,28 @@ public class NOChart extends WatchFace implements SharedPreferences.OnSharedPref } } - public class MessageReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - Bundle bundle = intent.getBundleExtra("data"); - if (layoutSet && bundle !=null) { - DataMap dataMap = DataMap.fromBundle(bundle); - wakeLock.acquire(50); - sgvLevel = dataMap.getLong("sgvLevel"); - batteryLevel = dataMap.getInt("batteryLevel"); - datetime = dataMap.getLong("timestamp"); - sgvString = dataMap.getString("sgvString"); - mSgv.setText(dataMap.getString("sgvString")); - - if(ageLevel()<=0) { - mSgv.setPaintFlags(mSgv.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); - } else { - mSgv.setPaintFlags(mSgv.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG); - } - - final java.text.DateFormat timeFormat = DateFormat.getTimeFormat(NOChart.this); - mTime.setText(timeFormat.format(System.currentTimeMillis())); - - showAgeAndStatus(); - - String delta = dataMap.getString("delta"); - - if (delta.endsWith(" mg/dl")) { - mDelta.setText(delta.substring(0, delta.length() - 6)); - } else if (delta.endsWith(" mmol/l")||delta.endsWith(" mmol")) { - mDelta.setText(delta.substring(0, delta.length() - 5)); - } else { - mDelta.setText(delta); - } - - - String avgDelta = dataMap.getString("avgDelta"); - - if (delta.endsWith(" mg/dl")) { - mAvgDelta.setText(avgDelta.substring(0, avgDelta.length() - 6)); - } else if (avgDelta.endsWith(" mmol/l")||avgDelta.endsWith(" mmol")) { - mAvgDelta.setText(avgDelta.substring(0, avgDelta.length() - 5)); - } else { - mAvgDelta.setText(avgDelta); - } - - mRelativeLayout.measure(specW, specH); - mRelativeLayout.layout(0, 0, mRelativeLayout.getMeasuredWidth(), - mRelativeLayout.getMeasuredHeight()); - invalidate(); - setColor(); - - //start animation? - // dataMap.getDataMapArrayList("entries") == null -> not on "resend data". - if (!lowResMode && (sharedPrefs.getBoolean("animation", false) && dataMap.getDataMapArrayList("entries") == null && (sgvString.equals("100") || sgvString.equals("5.5") || sgvString.equals("5,5")))) { - startAnimation(); - } - } - //status - bundle = intent.getBundleExtra("status"); - if (layoutSet && bundle != null) { - DataMap dataMap = DataMap.fromBundle(bundle); - wakeLock.acquire(50); - externalStatusString = dataMap.getString("externalStatusString"); - - showAgeAndStatus(); - - mRelativeLayout.measure(specW, specH); - mRelativeLayout.layout(0, 0, mRelativeLayout.getMeasuredWidth(), - mRelativeLayout.getMeasuredHeight()); - invalidate(); - setColor(); - } - //basals and temps - bundle = intent.getBundleExtra("basals"); - if (layoutSet && bundle != null) { - DataMap dataMap = DataMap.fromBundle(bundle); - wakeLock.acquire(500); - - loadBasalsAndTemps(dataMap); - - mRelativeLayout.measure(specW, specH); - mRelativeLayout.layout(0, 0, mRelativeLayout.getMeasuredWidth(), - mRelativeLayout.getMeasuredHeight()); - invalidate(); - setColor(); - } - } - } - - private void loadBasalsAndTemps(DataMap dataMap) { - ArrayList temps = dataMap.getDataMapArrayList("temps"); - if (temps != null) { - tempWatchDataList = new ArrayList<>(); - for (DataMap temp : temps) { - TempWatchData twd = new TempWatchData(); - twd.startTime = temp.getLong("starttime"); - twd.startBasal = temp.getDouble("startBasal"); - twd.endTime = temp.getLong("endtime"); - twd.endBasal = temp.getDouble("endbasal"); - twd.amount = temp.getDouble("amount"); - tempWatchDataList.add(twd); - } - } - ArrayList basals = dataMap.getDataMapArrayList("basals"); - if (basals != null) { - basalWatchDataList = new ArrayList<>(); - for (DataMap basal : basals) { - BasalWatchData bwd = new BasalWatchData(); - bwd.startTime = basal.getLong("starttime"); - bwd.endTime = basal.getLong("endtime"); - bwd.amount = basal.getDouble("amount"); - basalWatchDataList.add(bwd); - } - } - } - private void showAgeAndStatus() { - if( mTimestamp != null){ + if (mTimestamp != null) { mTimestamp.setText(readingAge(true)); } - boolean showAvgDelta = sharedPrefs.getBoolean("showAvgDelta", true); + boolean showAvgDelta = sp.getBoolean("showAvgDelta", true); - if(showAvgDelta){ + if (showAvgDelta) { mAvgDelta.setVisibility(View.VISIBLE); } else { mAvgDelta.setVisibility(View.GONE); } - statusView.setText(externalStatusString); + if (status != null) { + statusView.setText(status.getExternalStatus()); statusView.setVisibility(View.VISIBLE); + } } public void setColor() { - if(lowResMode){ + if (lowResMode) { setColorLowRes(); - } else if (sharedPrefs.getBoolean("dark", true)) { + } else if (sp.getBoolean("dark", true)) { setColorDark(); } else { setColorBright(); @@ -393,62 +296,6 @@ public class NOChart extends WatchFace implements SharedPreferences.OnSharedPref } - - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key){ - setColor(); - if(layoutSet){ - showAgeAndStatus(); - mRelativeLayout.measure(specW, specH); - mRelativeLayout.layout(0, 0, mRelativeLayout.getMeasuredWidth(), - mRelativeLayout.getMeasuredHeight()); - } - invalidate(); - } - - protected void updateRainbow() { - animationAngle = (animationAngle + 1) % 360; - //Animation matrix: - int[] rainbow = {Color.RED, Color.YELLOW, Color.GREEN, Color.BLUE - , Color.CYAN}; - Shader shader = new LinearGradient(0, 0, 0, 20, rainbow, - null, Shader.TileMode.MIRROR); - Matrix matrix = new Matrix(); - matrix.setRotate(animationAngle); - shader.setLocalMatrix(matrix); - mSgv.getPaint().setShader(shader); - invalidate(); - } - - private synchronized void setIsAnimated(boolean isAnimated) { - this.isAnimated = isAnimated; - } - - void startAnimation() { - Log.d("CircleWatchface", "start startAnimation"); - - Thread animator = new Thread() { - - - public void run() { - setIsAnimated(true); - for (int i = 0; i <= 8 * 1000 / 40; i++) { - updateRainbow(); - SystemClock.sleep(40); - } - mSgv.getPaint().setShader(null); - setIsAnimated(false); - invalidate(); - setColor(); - - System.gc(); - } - }; - - animator.start(); - } - protected void setColorLowRes() { mTime.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_mTime)); statusView.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.dark_statusView)); @@ -516,9 +363,11 @@ 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) { - DataLayerListenerService.Companion.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) { + // attempt endTime recover missing data + rxBus.send(new EventWearToMobile(new EventData.ActionResendData("NOChart" + + ":missedReadingAlert"))); } } } diff --git a/wear/src/main/java/info/nightscout/androidaps/watchfaces/Steampunk.java b/wear/src/main/java/info/nightscout/androidaps/watchfaces/Steampunk.java index 644c17668d..13791efd06 100644 --- a/wear/src/main/java/info/nightscout/androidaps/watchfaces/Steampunk.java +++ b/wear/src/main/java/info/nightscout/androidaps/watchfaces/Steampunk.java @@ -1,13 +1,15 @@ package info.nightscout.androidaps.watchfaces; +import android.annotation.SuppressLint; import android.content.Intent; -import androidx.core.content.ContextCompat; import android.support.wearable.watchface.WatchFaceStyle; import android.view.LayoutInflater; import android.view.animation.Animation; import android.view.animation.LinearInterpolator; import android.view.animation.RotateAnimation; +import androidx.core.content.ContextCompat; + import info.nightscout.androidaps.R; import info.nightscout.androidaps.interaction.menus.MainMenuActivity; import info.nightscout.shared.SafeParse; @@ -23,7 +25,7 @@ public class Steampunk extends BaseWatchFace { private float lastEndDegrees = 0f; private float deltaRotationAngle = 0f; - @Override + @SuppressLint("InflateParams") @Override public void onCreate() { forceSquareCanvas = true; super.onCreate(); @@ -35,22 +37,22 @@ public class Steampunk extends BaseWatchFace { @Override protected void onTapCommand(int tapType, int x, int y, long eventTime) { - if (tapType == TAP_TYPE_TAP&& + if (tapType == TAP_TYPE_TAP && x >= mChartTap.getLeft() && - x <= mChartTap.getRight()&& + x <= mChartTap.getRight() && y >= mChartTap.getTop() && - y <= mChartTap.getBottom()){ - if (eventTime - chartTapTime < 800){ + y <= mChartTap.getBottom()) { + if (eventTime - chartTapTime < 800) { changeChartTimeframe(); } chartTapTime = eventTime; - } else if (tapType == TAP_TYPE_TAP&& + } else if (tapType == TAP_TYPE_TAP && x >= mMainMenuTap.getLeft() && - x <= mMainMenuTap.getRight()&& + x <= mMainMenuTap.getRight() && y >= mMainMenuTap.getTop() && - y <= mMainMenuTap.getBottom()){ - if (eventTime - mainMenuTapTime < 800){ + y <= mMainMenuTap.getBottom()) { + if (eventTime - mainMenuTapTime < 800) { Intent intent = new Intent(this, MainMenuActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); @@ -67,7 +69,7 @@ public class Steampunk extends BaseWatchFace { protected void setColorDark() { if (mLinearLayout2 != null) { - if (ageLevel() <= 0 && rawData.datetime != 0) { + if (ageLevel() <= 0 && singleBg.getTimeStamp() != 0) { mLinearLayout2.setBackgroundResource(R.drawable.redline); mTimestamp.setTextColor(getResources().getColor(R.color.red_600)); } else { @@ -84,30 +86,32 @@ public class Steampunk extends BaseWatchFace { } } - if (!rawData.sSgv.equals("---")) { + if (!singleBg.getSgvString().equals("---")) { float rotationAngle = 0f; //by default, show ? on the dial (? is at 0 degrees on the dial) - if (!rawData.sUnits.equals("-")) { + if (!singleBg.getGlucoseUnits().equals("-")) { //ensure the glucose dial is the correct units - if (rawData.sUnits.equals("mmol")) { + if (singleBg.getGlucoseUnits().equals("mmol")) { mGlucoseDial.setImageResource(R.drawable.steampunk_dial_mmol); } else { mGlucoseDial.setImageResource(R.drawable.steampunk_dial_mgdl); } //convert the Sgv to degrees of rotation - if (rawData.sUnits.equals("mmol")) { - rotationAngle = Float.valueOf(rawData.sSgv) * 18f; //convert to mg/dL, which is equivalent to degrees + if (singleBg.getGlucoseUnits().equals("mmol")) { + rotationAngle = SafeParse.stringToFloat(singleBg.getSgvString()) * 18f; //convert to + // mg/dL, which is equivalent to degrees } else { - rotationAngle = Float.valueOf(rawData.sSgv); //if glucose a value is received, use it to determine the amount of rotation of the dial. + rotationAngle = SafeParse.stringToFloat(singleBg.getSgvString()); //if glucose a value is received, use it to determine the amount of rotation of the dial. } - } - if (rotationAngle > 330) rotationAngle = 330; //if the glucose value is higher than 330 then show "HIGH" on the dial. ("HIGH" is at 330 degrees on the dial) - if (rotationAngle != 0 && rotationAngle < 30) rotationAngle = 30; //if the glucose value is lower than 30 show "LOW" on the dial. ("LOW" is at 30 degrees on the dial) + if (rotationAngle > 330) + rotationAngle = 330; //if the glucose value is higher than 330 then show "HIGH" on the dial. ("HIGH" is at 330 degrees on the dial) + if (rotationAngle != 0 && rotationAngle < 30) + rotationAngle = 30; //if the glucose value is lower than 30 show "LOW" on the dial. ("LOW" is at 30 degrees on the dial) if (lastEndDegrees == 0) lastEndDegrees = rotationAngle; //rotate glucose dial @@ -124,51 +128,54 @@ public class Steampunk extends BaseWatchFace { //set the delta gauge and rotate the delta pointer float deltaIsNegative = 1f; //by default go clockwise - if (!rawData.sAvgDelta.equals("--")) { //if a legitimate delta value is received, then... - if (rawData.sAvgDelta.charAt(0) == '-') deltaIsNegative = -1f; //if the delta is negative, go counter-clockwise - Float AbssAvgDelta = SafeParse.stringToFloat(rawData.sAvgDelta.substring(1)) ; //get rid of the sign so it can be converted to float. - String autogranularity = "0" ; //autogranularity off + if (!singleBg.getAvgDelta().equals("--")) { //if a legitimate delta value is + // received, + // then... + if (singleBg.getAvgDelta().charAt(0) == '-') + deltaIsNegative = -1f; //if the delta is negative, go counter-clockwise + Float AbssAvgDelta = SafeParse.stringToFloat(singleBg.getAvgDelta().substring(1)); //get rid of the sign so it can be converted to float. + String autogranularity = "0"; //autogranularity off //ensure the delta gauge is the right units and granularity - if (!rawData.sUnits.equals("-")) { - if (rawData.sUnits.equals("mmol")) { - if (sharedPrefs.getString("delta_granularity", "2").equals("4")) { //Auto granularity + if (!singleBg.getGlucoseUnits().equals("-")) { + if (singleBg.getGlucoseUnits().equals("mmol")) { + if (sp.getString("delta_granularity", "2").equals("4")) { //Auto granularity autogranularity = "1"; // low (init) - if (AbssAvgDelta < 0.3 ) { - autogranularity = "3" ; // high if below 0.3 mmol/l + if (AbssAvgDelta < 0.3) { + autogranularity = "3"; // high if below 0.3 mmol/l } else if (AbssAvgDelta < 0.5) { - autogranularity = "2" ; // medium if below 0.5 mmol/l + autogranularity = "2"; // medium if below 0.5 mmol/l } } - if (sharedPrefs.getString("delta_granularity", "2").equals("1") || autogranularity.equals("1")) { //low + if (sp.getString("delta_granularity", "2").equals("1") || autogranularity.equals("1")) { //low mLinearLayout.setBackgroundResource(R.drawable.steampunk_gauge_mmol_10); deltaRotationAngle = (AbssAvgDelta * 30f); } - if (sharedPrefs.getString("delta_granularity", "2").equals("2") || autogranularity.equals("2")) { //medium + if (sp.getString("delta_granularity", "2").equals("2") || autogranularity.equals("2")) { //medium mLinearLayout.setBackgroundResource(R.drawable.steampunk_gauge_mmol_05); deltaRotationAngle = (AbssAvgDelta * 60f); } - if (sharedPrefs.getString("delta_granularity", "2").equals("3") || autogranularity.equals("3")) { //high + if (sp.getString("delta_granularity", "2").equals("3") || autogranularity.equals("3")) { //high mLinearLayout.setBackgroundResource(R.drawable.steampunk_gauge_mmol_03); deltaRotationAngle = (AbssAvgDelta * 100f); } } else { - if (sharedPrefs.getString("delta_granularity", "2").equals("4")) { //Auto granularity + if (sp.getString("delta_granularity", "2").equals("4")) { //Auto granularity autogranularity = "1"; // low (init) - if (AbssAvgDelta < 5 ) { - autogranularity = "3" ; // high if below 5 mg/dl + if (AbssAvgDelta < 5) { + autogranularity = "3"; // high if below 5 mg/dl } else if (AbssAvgDelta < 10) { - autogranularity = "2" ; // medium if below 10 mg/dl + autogranularity = "2"; // medium if below 10 mg/dl } } - if (sharedPrefs.getString("delta_granularity", "2").equals("1") || autogranularity.equals("1")) { //low + if (sp.getString("delta_granularity", "2").equals("1") || autogranularity.equals("1")) { //low mLinearLayout.setBackgroundResource(R.drawable.steampunk_gauge_mgdl_20); deltaRotationAngle = (AbssAvgDelta * 1.5f); } - if (sharedPrefs.getString("delta_granularity", "2").equals("2") || autogranularity.equals("2")) { //medium + if (sp.getString("delta_granularity", "2").equals("2") || autogranularity.equals("2")) { //medium mLinearLayout.setBackgroundResource(R.drawable.steampunk_gauge_mgdl_10); deltaRotationAngle = (AbssAvgDelta * 3f); } - if (sharedPrefs.getString("delta_granularity", "2").equals("3") || autogranularity.equals("3")) { //high + if (sp.getString("delta_granularity", "2").equals("3") || autogranularity.equals("3")) { //high mLinearLayout.setBackgroundResource(R.drawable.steampunk_gauge_mgdl_5); deltaRotationAngle = (AbssAvgDelta * 6f); } @@ -179,10 +186,10 @@ public class Steampunk extends BaseWatchFace { } //rotate the minute hand. - mMinuteHand.setRotation(Float.valueOf(sMinute) * 6f); + mMinuteHand.setRotation(Float.parseFloat(sMinute) * 6f); //rotate the hour hand. - mHourHand.setRotation((Float.valueOf(sHour) * 30f) + (Float.valueOf(sMinute) * 0.5f)); + mHourHand.setRotation((Float.parseFloat(sHour) * 30f) + (Float.parseFloat(sMinute) * 0.5f)); setTextSizes(); @@ -197,7 +204,7 @@ public class Steampunk extends BaseWatchFace { gridColor = ContextCompat.getColor(getApplicationContext(), R.color.grey_steampunk); basalBackgroundColor = ContextCompat.getColor(getApplicationContext(), R.color.basal_dark); basalCenterColor = ContextCompat.getColor(getApplicationContext(), R.color.basal_dark); - if (Integer.parseInt(sharedPrefs.getString("chart_timeframe", "3")) < 3) { + if (sp.getInt("chart_timeframe", 3) < 3) { pointSize = 2; } else { pointSize = 1; @@ -232,7 +239,7 @@ public class Steampunk extends BaseWatchFace { //top row. large font unless text too big (i.e. detailedIOB) mCOB2.setTextSize(fontLarge); mBasalRate.setTextSize(fontLarge); - if (rawData.sIOB2.length() < 7) { + if (status.getIobDetail().length() < 7) { mIOB2.setTextSize(fontLarge); } else { mIOB2.setTextSize(fontSmall); @@ -248,7 +255,8 @@ public class Steampunk extends BaseWatchFace { } //if both batteries are shown, make them smaller. - if (sharedPrefs.getBoolean("show_uploader_battery", true) && sharedPrefs.getBoolean("show_rig_battery", false)) { + if (sp.getBoolean("show_uploader_battery", true) && sp.getBoolean( + "show_rig_battery", false)) { mUploaderBattery.setTextSize(fontSmall); mRigBattery.setTextSize(fontSmall); } else { @@ -258,14 +266,14 @@ public class Steampunk extends BaseWatchFace { } private void changeChartTimeframe() { - int timeframe = Integer.parseInt(sharedPrefs.getString("chart_timeframe", "3")); - timeframe = (timeframe%5) + 1; + int timeframe = sp.getInt("chart_timeframe", 3); + timeframe = (timeframe % 5) + 1; if (timeframe < 3) { pointSize = 2; } else { pointSize = 1; } setupCharts(); - sharedPrefs.edit().putString("chart_timeframe", "" + timeframe).apply(); + sp.putString("chart_timeframe", "" + timeframe); } } diff --git a/wear/src/main/res/values/strings.xml b/wear/src/main/res/values/strings.xml index 56709347c7..b4f91135a5 100644 --- a/wear/src/main/res/values/strings.xml +++ b/wear/src/main/res/values/strings.xml @@ -170,12 +170,6 @@ AAPS Bolus Progress Silent Bolus progress and cancel Bolus progress and cancel with less vibrations - QuickWizard - wearcontrol - units_mgdl - boluswizard_percentage - treatmentssafety_maxcarbs - treatmentssafety_maxbolus Off During Charging Always On Mode @@ -189,6 +183,15 @@ No config available Wear controls disabled No data available - quick_wizard_data_map + + QuickWizard + wearcontrol + units_mgdl + boluswizard_percentage + treatmentssafety_maxcarbs + treatmentssafety_maxbolus + quick_wizard_data + primefill + showWizard diff --git a/wear/src/test/java/info/nightscout/androidaps/data/BgWatchDataTest.java b/wear/src/test/java/info/nightscout/androidaps/data/BgWatchDataTest.java deleted file mode 100644 index 9149c16675..0000000000 --- a/wear/src/test/java/info/nightscout/androidaps/data/BgWatchDataTest.java +++ /dev/null @@ -1,97 +0,0 @@ -package info.nightscout.androidaps.data; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -import java.util.HashSet; -import java.util.Set; - -import info.nightscout.androidaps.TestBase; -import info.nightscout.androidaps.interaction.utils.Constants; -import info.nightscout.androidaps.testing.mockers.WearUtilMocker; - -public class BgWatchDataTest extends TestBase { - - @Test - public void bgWatchDataHashTest() { - // GIVEN - BgWatchData inserted = new BgWatchData( - 88.0, 160.0, 90.0, - WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 4, 1 - ); - Set set = new HashSet<>(); - - // THEN - //noinspection ConstantConditions - assertFalse(set.contains(inserted)); - set.add(inserted); - assertTrue(set.contains(inserted)); - } - - /** - * BgWatchData has BIZARRE equals - only timestamp and color are checked! - */ - @Test - public void bgWatchDataEqualsTest() { - // GIVEN - BgWatchData item1 = new BgWatchData( - 88.0, 160.0, 90.0, - WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS, 1 - ); - - BgWatchData item2sameTimeSameColor = new BgWatchData( - 123.0, 190, 90.0, - WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS, 1 - ); - - BgWatchData item3sameTimeSameDiffColor = new BgWatchData( - 96.0, 190, 90.0, - WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS, 0 - ); - BgWatchData item4differentTime = new BgWatchData( - 88.0, 160.0, 90.0, - WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 2, 1 - ); - - // THEN - assertEquals(item1, item2sameTimeSameColor); - assertNotEquals(item1, item3sameTimeSameDiffColor); - assertNotEquals(item1, item4differentTime); - } - - /** - * BgWatchData is ordered by timestamp, reverse order - */ - @Test - public void bgWatchDataCompareTest() { - // GIVEN - BgWatchData item1 = new BgWatchData( - 85, 160.0, 90.0, - WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 2, 1 - ); - - BgWatchData item2 = new BgWatchData( - 80, 190, 90.0, - WearUtilMocker.REF_NOW, 1 - ); - - BgWatchData item3 = new BgWatchData( - 80, 190, 50.0, - WearUtilMocker.REF_NOW + Constants.MINUTE_IN_MS * 5, 0 - ); - - BgWatchData item4 = new BgWatchData( - 160, 140, 70.0, - WearUtilMocker.REF_NOW, 0 - ); - - // THEN - assertTrue(item2.compareTo(item1) < 0); - assertTrue(item2.compareTo(item3) > 0); - assertEquals(0, item2.compareTo(item4)); - } -} diff --git a/wear/src/test/java/info/nightscout/androidaps/data/RawDataSgvDisplayDataTest.java b/wear/src/test/java/info/nightscout/androidaps/data/RawDataSgvDisplayDataTest.java deleted file mode 100644 index 942d634e32..0000000000 --- a/wear/src/test/java/info/nightscout/androidaps/data/RawDataSgvDisplayDataTest.java +++ /dev/null @@ -1,123 +0,0 @@ -package info.nightscout.androidaps.data; - -import static org.junit.Assert.assertEquals; - -import android.content.Intent; -import android.os.Bundle; - -import com.google.android.gms.wearable.DataMap; - -import org.junit.Test; - -import info.nightscout.androidaps.TestBase; -import info.nightscout.androidaps.interaction.utils.Constants; -import info.nightscout.androidaps.testing.mockers.WearUtilMocker; -import info.nightscout.androidaps.testing.mocks.BundleMock; -import info.nightscout.androidaps.testing.mocks.IntentMock; - -public class RawDataSgvDisplayDataTest extends TestBase { - - //============================================================================================== - // SGV DATA - //============================================================================================== - - private DataMap dataMapForData() { - DataMap dataMap = new DataMap(); - dataMap.putLong("sgvLevel", 1L); - dataMap.putLong("timestamp", WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS); - dataMap.putString("sgvString", "106"); - dataMap.putString("slopeArrow", "↗"); - dataMap.putString("delta", "5.4"); - dataMap.putString("avgDelta", "3.7"); - dataMap.putString("glucoseUnits", "mg/dl"); - return dataMap; - } - - private void assertDataEmpty(RawDisplayData newRaw) { - assertEquals(newRaw.sgvLevel, 0L); - assertEquals(newRaw.datetime, 0L); - assertEquals(newRaw.sSgv, "---"); - assertEquals(newRaw.sDirection, "--"); - assertEquals(newRaw.sDelta, "--"); - assertEquals(newRaw.sAvgDelta, "--"); - assertEquals(newRaw.sUnits, "-"); - } - - private void assertDataOk(RawDisplayData newRaw) { - assertEquals(newRaw.sgvLevel, 1L); - assertEquals(newRaw.datetime, WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS); - assertEquals(newRaw.sSgv, "106"); - assertEquals(newRaw.sDirection, "↗"); - assertEquals(newRaw.sDelta, "5.4"); - assertEquals(newRaw.sAvgDelta, "3.7"); - assertEquals(newRaw.sUnits, "mg/dl"); - } - - @Test - public void updateDataFromEmptyPersistenceTest() { - // GIVEN - RawDisplayData newRaw = new RawDisplayData(getWearUtil()); - - // WHEN - newRaw.updateFromPersistence(persistence); - - // THEN - assertDataEmpty(newRaw); - } - - @Test - public void updateDataFromPersistenceTest() { - // GIVEN - RawDisplayData newRaw = new RawDisplayData(getWearUtil()); - - // WHEN - persistence.storeDataMap(RawDisplayData.DATA_PERSISTENCE_KEY, dataMapForData()); - newRaw.updateFromPersistence(persistence); - - // THEN - assertDataOk(newRaw); - } - - @Test - public void partialUpdateDataFromPersistenceTest() { - // GIVEN - RawDisplayData newRaw = new RawDisplayData(getWearUtil()); - - // WHEN - persistence.storeDataMap(RawDisplayData.DATA_PERSISTENCE_KEY, dataMapForData()); - newRaw.updateForComplicationsFromPersistence(persistence); - - // THEN - assertDataOk(newRaw); - } - - @Test - public void updateDataFromMessageTest() { - // GIVEN - Intent intent = IntentMock.mock(); - Bundle bundle = BundleMock.mock(dataMapForData()); - - intent.putExtra("data", bundle); - RawDisplayData newRaw = new RawDisplayData(getWearUtil()); - - // WHEN - newRaw.updateDataFromMessage(intent, null); - - // THEN - assertDataOk(newRaw); - } - - @Test - public void updateDataFromEmptyMessageTest() { - // GIVEN - Intent intent = IntentMock.mock(); - RawDisplayData newRaw = new RawDisplayData(getWearUtil()); - - // WHEN - newRaw.updateDataFromMessage(intent, null); - - // THEN - assertDataEmpty(newRaw); - } - -} diff --git a/wear/src/test/java/info/nightscout/androidaps/data/RawDisplayDataBasalsTest.java b/wear/src/test/java/info/nightscout/androidaps/data/RawDisplayDataBasalsTest.java deleted file mode 100644 index f6fefeefd6..0000000000 --- a/wear/src/test/java/info/nightscout/androidaps/data/RawDisplayDataBasalsTest.java +++ /dev/null @@ -1,242 +0,0 @@ -package info.nightscout.androidaps.data; - -import static org.junit.Assert.assertEquals; - -import android.content.Intent; -import android.os.Bundle; - -import com.google.android.gms.wearable.DataMap; - -import org.junit.Test; - -import java.util.ArrayList; - -import info.nightscout.androidaps.TestBase; -import info.nightscout.androidaps.interaction.utils.Constants; -import info.nightscout.androidaps.testing.mockers.WearUtilMocker; -import info.nightscout.androidaps.testing.mocks.BundleMock; -import info.nightscout.androidaps.testing.mocks.IntentMock; -import info.nightscout.androidaps.testing.utils.BasalWatchDataExt; -import info.nightscout.androidaps.testing.utils.BgWatchDataExt; -import info.nightscout.androidaps.testing.utils.BolusWatchDataExt; -import info.nightscout.androidaps.testing.utils.TempWatchDataExt; - -@SuppressWarnings("SpellCheckingInspection") -public class RawDisplayDataBasalsTest extends TestBase { - - - //============================================================================================== - // BASALS for chart - //============================================================================================== - - private DataMap dataMapForBasals() { - - DataMap dataMap = new DataMap(); - - ArrayList temps = new ArrayList<>(); - DataMap temp = new DataMap(); - temp.putLong("starttime", WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 20); - temp.putDouble("startBasal", 1.5); - temp.putLong("endtime", WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 10); - temp.putDouble("endbasal", 1.5); - temp.putDouble("amount", 1.8); - temps.add(temp); - - DataMap temp2 = new DataMap(); - temp2.putLong("starttime", WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 10); - temp2.putDouble("startBasal", 1.3); - temp2.putLong("endtime", WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 2); - temp2.putDouble("endbasal", 1.3); - temp2.putDouble("amount", 2.3); - temps.add(temp2); - dataMap.putDataMapArrayList("temps", temps); - - ArrayList basals = new ArrayList<>(); - DataMap basal = new DataMap(); - basal.putLong("starttime", WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 20); - basal.putLong("endtime", WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 2); - basal.putDouble("amount", 1.2); - basals.add(basal); - dataMap.putDataMapArrayList("basals", basals); - - ArrayList boluses = new ArrayList<>(); - DataMap bolus = new DataMap(); - bolus.putLong("date", WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 17); - bolus.putDouble("bolus", 5.5); - bolus.putDouble("carbs", 20.0); - bolus.putBoolean("isSMB", false); - bolus.putBoolean("isValid", true); - boluses.add(bolus); - - DataMap bolus2 = new DataMap(); - bolus2.putLong("date", WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 11); - bolus2.putDouble("bolus", 3.0); - bolus2.putDouble("carbs", 0.0); - bolus2.putBoolean("isSMB", false); - bolus2.putBoolean("isValid", true); - boluses.add(bolus2); - - DataMap bolus3 = new DataMap(); - bolus3.putLong("date", WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 3); - bolus3.putDouble("bolus", 0.0); - bolus3.putDouble("carbs", 15.0); - bolus3.putBoolean("isSMB", true); - bolus3.putBoolean("isValid", false); - boluses.add(bolus3); - - dataMap.putDataMapArrayList("boluses", boluses); - - ArrayList predictions = new ArrayList<>(); - for (int i = 0; i < 10; i++) { - DataMap prediction = new DataMap(); - prediction.putLong("timestamp", WearUtilMocker.REF_NOW + Constants.MINUTE_IN_MS * i); - prediction.putDouble("sgv", 160 - 4 * i); - prediction.putInt("color", 0); - predictions.add(prediction); - } - dataMap.putDataMapArrayList("predictions", predictions); - - return dataMap; - } - - private void assertBasalsEmpty(RawDisplayData newRaw) { - assertEquals(newRaw.tempWatchDataList.size(), 0); - assertEquals(newRaw.basalWatchDataList.size(), 0); - assertEquals(newRaw.bolusWatchDataList.size(), 0); - assertEquals(newRaw.predictionList.size(), 0); - } - - private void assertBasalsOk(RawDisplayData newRaw) { - assertEquals(newRaw.tempWatchDataList.size(), 2); - assertEquals(newRaw.basalWatchDataList.size(), 1); - assertEquals(newRaw.bolusWatchDataList.size(), 3); - assertEquals(newRaw.predictionList.size(), 10); - - assertEquals(new TempWatchDataExt(newRaw.tempWatchDataList.get(0)), TempWatchDataExt.build( - WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 20, - 1.5, - WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 10, - 1.5, - 1.8 - )); - - assertEquals(new TempWatchDataExt(newRaw.tempWatchDataList.get(1)), TempWatchDataExt.build( - WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 10, - 1.3, - WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 2, - 1.3, - 2.3 - )); - - assertEquals(new BasalWatchDataExt(newRaw.basalWatchDataList.get(0)), BasalWatchDataExt.build( - WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 20, - WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 2, - 1.2 - )); - - assertEquals(new BolusWatchDataExt(newRaw.bolusWatchDataList.get(0)), BolusWatchDataExt.build( - WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 17, - 5.5, - 20, - false, - true - )); - - assertEquals(new BolusWatchDataExt(newRaw.bolusWatchDataList.get(1)), BolusWatchDataExt.build( - WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 11, - 3, - 0, - false, - true - )); - - assertEquals(new BolusWatchDataExt(newRaw.bolusWatchDataList.get(2)), BolusWatchDataExt.build( - WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 3, - 0, - 15, - true, - false - )); - - - assertEquals(new BgWatchDataExt(newRaw.predictionList.get(3)), BgWatchDataExt.build( - 160 - 4 * 3, - WearUtilMocker.REF_NOW + Constants.MINUTE_IN_MS * 3, - 0 - )); - - assertEquals(new BgWatchDataExt(newRaw.predictionList.get(7)), BgWatchDataExt.build( - 160 - 4 * 7, - WearUtilMocker.REF_NOW + Constants.MINUTE_IN_MS * 7, - 0 - )); - } - - @Test - public void updateBasalsFromEmptyPersistenceTest() { - // GIVEN - RawDisplayData newRaw = new RawDisplayData(getWearUtil()); - - // WHEN - newRaw.updateFromPersistence(persistence); - - // THEN - assertBasalsEmpty(newRaw); - } - - @Test - public void updateBasalsFromPersistenceTest() { - // GIVEN - RawDisplayData newRaw = new RawDisplayData(getWearUtil()); - - // WHEN - persistence.storeDataMap(RawDisplayData.BASALS_PERSISTENCE_KEY, dataMapForBasals()); - newRaw.updateFromPersistence(persistence); - - // THEN - assertBasalsOk(newRaw); - } - - @Test - public void partialUpdateBasalsFromPersistenceTest() { - // GIVEN - RawDisplayData newRaw = new RawDisplayData(getWearUtil()); - - // WHEN - persistence.storeDataMap(RawDisplayData.BASALS_PERSISTENCE_KEY, dataMapForBasals()); - newRaw.updateForComplicationsFromPersistence(persistence); - - // THEN - assertBasalsEmpty(newRaw); - } - - @Test - public void updateBasalsFromMessageTest() { - // GIVEN - Intent intent = IntentMock.mock(); - Bundle bundle = BundleMock.mock(dataMapForBasals()); - - intent.putExtra("basals", bundle); - RawDisplayData newRaw = new RawDisplayData(getWearUtil()); - - // WHEN - newRaw.updateBasalsFromMessage(intent); - - // THEN - assertBasalsOk(newRaw); - } - - @Test - public void updateBasalsFromEmptyMessageTest() { - // GIVEN - Intent intent = IntentMock.mock(); - RawDisplayData newRaw = new RawDisplayData(getWearUtil()); - - // WHEN - newRaw.updateBasalsFromMessage(intent); - - // THEN - assertBasalsEmpty(newRaw); - } - -} diff --git a/wear/src/test/java/info/nightscout/androidaps/data/RawDisplayDataBgEntriesTest.java b/wear/src/test/java/info/nightscout/androidaps/data/RawDisplayDataBgEntriesTest.java deleted file mode 100644 index 61b5d119f1..0000000000 --- a/wear/src/test/java/info/nightscout/androidaps/data/RawDisplayDataBgEntriesTest.java +++ /dev/null @@ -1,135 +0,0 @@ -package info.nightscout.androidaps.data; - -import static org.junit.Assert.assertEquals; - -import com.google.android.gms.wearable.DataMap; - -import org.junit.Test; - -import java.util.ArrayList; - -import info.nightscout.androidaps.TestBase; -import info.nightscout.androidaps.interaction.utils.Constants; -import info.nightscout.androidaps.testing.mockers.WearUtilMocker; -import info.nightscout.androidaps.testing.utils.BgWatchDataExt; - -@SuppressWarnings("PointlessArithmeticExpression") -public class RawDisplayDataBgEntriesTest extends TestBase { - - //============================================================================================== - // ENTRIES for chart - //============================================================================================== - - private DataMap dataMapForEntries() { - - DataMap dataMap = new DataMap(); - ArrayList entries = new ArrayList<>(); - for (int i = 0; i < 12; i++) { - DataMap entry = new DataMap(); - entry.putLong("timestamp", WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 4 * (16 - i)); - entry.putDouble("sgvDouble", 145.0 - 5 * i); - entry.putDouble("high", 170.0); - entry.putDouble("low", 80.0); - entry.putInt("color", 0); - entries.add(entry); - } - dataMap.putDataMapArrayList("entries", entries); - - return dataMap; - } - - private DataMap dataMapForEntries(long timestamp, double sgv) { - DataMap entry = new DataMap(); - entry.putLong("timestamp", timestamp); - entry.putDouble("sgvDouble", sgv); - entry.putDouble("high", 160.0); - entry.putDouble("low", 90.0); - entry.putInt("color", 1); - return entry; - } - - @Test - public void addToWatchSetTest() { - // GIVEN - RawDisplayData newRaw = new RawDisplayData(getWearUtil()); - DataMap multipleEntries = dataMapForEntries(); - DataMap singleEntry1 = dataMapForEntries(WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 4 * 2, 92); - DataMap singleEntry2 = dataMapForEntries(WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 4 * 1, 88); - - // WHEN, THEN - // add list - newRaw.addToWatchSet(multipleEntries); - assertEquals(newRaw.bgDataList.size(), 12); - - assertEquals(new BgWatchDataExt(newRaw.bgDataList.get(5)), - new BgWatchDataExt(new BgWatchData( - 120.0, 170.0, 80.0, - WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 4 * (16 - 5), 0 - ))); - - assertEquals(new BgWatchDataExt(newRaw.bgDataList.get(11)), - new BgWatchDataExt(new BgWatchData( - 90.0, 170.0, 80.0, - WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 4 * (16 - 11), 0 - ))); - - // add single entries - newRaw.addToWatchSet(singleEntry1); - newRaw.addToWatchSet(singleEntry2); - assertEquals(newRaw.bgDataList.size(), 14); - - assertEquals(new BgWatchDataExt(newRaw.bgDataList.get(12)), - new BgWatchDataExt(new BgWatchData( - 92.0, 160.0, 90.0, - WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 4 * 2, 1 - ))); - assertEquals(new BgWatchDataExt(newRaw.bgDataList.get(13)), - new BgWatchDataExt(new BgWatchData( - 88.0, 160.0, 90.0, - WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 4 * 1, 1 - ))); - - // ignore duplicates - newRaw.addToWatchSet(singleEntry2); - assertEquals(newRaw.bgDataList.size(), 14); - } - - @Test - public void addToWatchSetCleanupOldTest() { - RawDisplayData newRaw = new RawDisplayData(getWearUtil()); - - newRaw.addToWatchSet(dataMapForEntries(getWearUtil().timestamp(), 125)); - assertEquals(newRaw.bgDataList.size(), 1); - - getWearUtilMocker().progressClock(Constants.HOUR_IN_MS * 2); - newRaw.addToWatchSet(dataMapForEntries(getWearUtil().timestamp(), 140)); - assertEquals(newRaw.bgDataList.size(), 2); - - getWearUtilMocker().progressClock(Constants.HOUR_IN_MS * 1); - newRaw.addToWatchSet(dataMapForEntries(getWearUtil().timestamp(), 150)); - getWearUtilMocker().progressClock(Constants.HOUR_IN_MS * 1 + Constants.MINUTE_IN_MS * 30); - newRaw.addToWatchSet(dataMapForEntries(getWearUtil().timestamp(), 101)); - assertEquals(newRaw.bgDataList.size(), 4); - - getWearUtilMocker().progressClock(Constants.MINUTE_IN_MS * 30); - newRaw.addToWatchSet(dataMapForEntries(getWearUtil().timestamp(), 90)); - assertEquals(newRaw.bgDataList.size(), 5); - - getWearUtilMocker().progressClock(Constants.HOUR_IN_MS * 1 + Constants.MINUTE_IN_MS * 30); - newRaw.addToWatchSet(dataMapForEntries(getWearUtil().timestamp(), 80)); - assertEquals(newRaw.bgDataList.size(), 5); - - getWearUtilMocker().progressClock(Constants.HOUR_IN_MS * 4); - newRaw.addToWatchSet(dataMapForEntries(getWearUtil().timestamp(), 92)); - assertEquals(newRaw.bgDataList.size(), 2); - - getWearUtilMocker().progressClock(Constants.HOUR_IN_MS * 5 + Constants.MINUTE_IN_MS * 30); - newRaw.addToWatchSet(dataMapForEntries(getWearUtil().timestamp(), 107)); - assertEquals(newRaw.bgDataList.size(), 1); - - getWearUtilMocker().progressClock(Constants.HOUR_IN_MS * 6 + Constants.MINUTE_IN_MS * 30); - newRaw.addToWatchSet(dataMapForEntries(getWearUtil().timestamp() - Constants.HOUR_IN_MS * 6, 138)); - assertEquals(newRaw.bgDataList.size(), 0); - } - -} diff --git a/wear/src/test/java/info/nightscout/androidaps/data/RawDisplayDataStatusTest.java b/wear/src/test/java/info/nightscout/androidaps/data/RawDisplayDataStatusTest.java deleted file mode 100644 index 834e801b70..0000000000 --- a/wear/src/test/java/info/nightscout/androidaps/data/RawDisplayDataStatusTest.java +++ /dev/null @@ -1,160 +0,0 @@ -package info.nightscout.androidaps.data; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import android.content.Intent; -import android.os.Bundle; - -import com.google.android.gms.wearable.DataMap; - -import org.junit.Before; -import org.junit.Test; - -import info.nightscout.androidaps.TestBase; -import info.nightscout.androidaps.interaction.utils.Constants; -import info.nightscout.androidaps.testing.mockers.RawDataMocker; -import info.nightscout.androidaps.testing.mockers.WearUtilMocker; -import info.nightscout.androidaps.testing.mocks.BundleMock; -import info.nightscout.androidaps.testing.mocks.IntentMock; - -@SuppressWarnings("SimplifiableAssertion") -public class RawDisplayDataStatusTest extends TestBase { - - private RawDataMocker rawDataMocker; - - @Before - public void mock() { - rawDataMocker = new RawDataMocker(getWearUtil()); - } - - @SuppressWarnings("AssertBetweenInconvertibleTypes") @Test - public void toDebugStringTest() { - RawDisplayData raw = rawDataMocker.rawDelta(5, "1.5"); - raw.externalStatusString = "placeholder-here"; - - assertEquals(raw.datetime, WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 5); - assertTrue(raw.toDebugString().contains("placeholder-here")); - } - - //============================================================================================== - // STATUS - //============================================================================================== - - private DataMap dataMapForStatus() { - DataMap dataMap = new DataMap(); - dataMap.putString("currentBasal", "120%"); - dataMap.putString("battery", "76"); - dataMap.putString("rigBattery", "40%"); - dataMap.putBoolean("detailedIob", true); - dataMap.putString("iobSum", "12.5"); - dataMap.putString("iobDetail", "(11,2|1,3)"); - dataMap.putString("cob", "5(10)g"); - dataMap.putString("bgi", "13"); - dataMap.putBoolean("showBgi", false); - dataMap.putString("externalStatusString", ""); - dataMap.putInt("batteryLevel", 1); - dataMap.putLong("openApsStatus", WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 2); - return dataMap; - } - - private void assertStatusEmpty(RawDisplayData newRaw) { - assertEquals(newRaw.sBasalRate, "-.--U/h"); - assertEquals(newRaw.sUploaderBattery, "--"); - assertEquals(newRaw.sRigBattery, "--"); - assertEquals(newRaw.detailedIOB, false); - assertEquals(newRaw.sIOB1, "IOB"); - assertEquals(newRaw.sIOB2, "-.--"); - assertEquals(newRaw.sCOB1, "Carb"); - assertEquals(newRaw.sCOB2, "--g"); - assertEquals(newRaw.sBgi, "--"); - assertEquals(newRaw.showBGI, false); - assertEquals(newRaw.externalStatusString, "no status"); - assertEquals(newRaw.batteryLevel, 1); - assertEquals(newRaw.openApsStatus, -1L); - } - - private void assertStatusOk(RawDisplayData newRaw) { - assertEquals(newRaw.sBasalRate, "120%"); - assertEquals(newRaw.sUploaderBattery, "76"); - assertEquals(newRaw.sRigBattery, "40%"); - assertEquals(newRaw.detailedIOB, true); - assertEquals(newRaw.sIOB1, "12.5U"); - assertEquals(newRaw.sIOB2, "(11,2|1,3)"); - assertEquals(newRaw.sCOB1, "Carb"); - assertEquals(newRaw.sCOB2, "5(10)g"); - assertEquals(newRaw.sBgi, "13"); - assertEquals(newRaw.showBGI, false); - assertEquals(newRaw.externalStatusString, ""); - assertEquals(newRaw.batteryLevel, 1); - assertEquals(newRaw.openApsStatus, WearUtilMocker.REF_NOW - Constants.MINUTE_IN_MS * 2); - } - - @Test - public void updateStatusFromEmptyPersistenceTest() { - // GIVEN - RawDisplayData newRaw = new RawDisplayData(getWearUtil()); - - // WHEN - newRaw.updateFromPersistence(persistence); - - // THEN - assertStatusEmpty(newRaw); - } - - @Test - public void updateStatusFromPersistenceTest() { - // GIVEN - RawDisplayData newRaw = new RawDisplayData(getWearUtil()); - - // WHEN - persistence.storeDataMap(RawDisplayData.STATUS_PERSISTENCE_KEY, dataMapForStatus()); - newRaw.updateFromPersistence(persistence); - - // THEN - assertStatusOk(newRaw); - } - - @Test - public void partialUpdateStatusFromPersistenceTest() { - // GIVEN - RawDisplayData newRaw = new RawDisplayData(getWearUtil()); - - // WHEN - persistence.storeDataMap(RawDisplayData.STATUS_PERSISTENCE_KEY, dataMapForStatus()); - newRaw.updateForComplicationsFromPersistence(persistence); - - // THEN - assertStatusOk(newRaw); - } - - @Test - public void updateStatusFromMessageTest() { - // GIVEN - Intent intent = IntentMock.mock(); - Bundle bundle = BundleMock.mock(dataMapForStatus()); - - intent.putExtra("status", bundle); - RawDisplayData newRaw = new RawDisplayData(getWearUtil()); - - // WHEN - newRaw.updateStatusFromMessage(intent, null); - - // THEN - assertStatusOk(newRaw); - } - - @Test - public void updateStatusFromEmptyMessageTest() { - // GIVEN - Intent intent = IntentMock.mock(); - RawDisplayData newRaw = new RawDisplayData(getWearUtil()); - - // WHEN - newRaw.updateStatusFromMessage(intent, null); - - // THEN - assertStatusEmpty(newRaw); - } - -} diff --git a/wear/src/test/java/info/nightscout/androidaps/interaction/utils/DisplayFormatTest.java b/wear/src/test/java/info/nightscout/androidaps/interaction/utils/DisplayFormatTest.java index e6469b0110..d563cdcddb 100644 --- a/wear/src/test/java/info/nightscout/androidaps/interaction/utils/DisplayFormatTest.java +++ b/wear/src/test/java/info/nightscout/androidaps/interaction/utils/DisplayFormatTest.java @@ -106,10 +106,10 @@ public class DisplayFormatTest extends TestBase { @Test public void shortTrendTest() { - RawDisplayData raw = new RawDisplayData(wearUtil); + RawDisplayData raw = new RawDisplayData(); assertEquals(displayFormat.shortTrend(raw), "-- Δ--"); - raw.datetime = wearUtilMocker.backInTime(0, 0, 2, 0); + raw.getSingleBg().setTimeStamp(wearUtilMocker.backInTime(0, 0, 2, 0)); assertEquals(displayFormat.shortTrend(raw), "2' Δ--"); when(sp.getBoolean("complication_unicode", true)).thenReturn(true); diff --git a/wear/src/test/java/info/nightscout/androidaps/interaction/utils/PersistenceTest.java b/wear/src/test/java/info/nightscout/androidaps/interaction/utils/PersistenceTest.java index b879ff440e..dcfbf5235e 100644 --- a/wear/src/test/java/info/nightscout/androidaps/interaction/utils/PersistenceTest.java +++ b/wear/src/test/java/info/nightscout/androidaps/interaction/utils/PersistenceTest.java @@ -2,12 +2,7 @@ package info.nightscout.androidaps.interaction.utils; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static info.nightscout.androidaps.testing.mockers.WearUtilMocker.REF_NOW; - -import com.google.android.gms.wearable.DataMap; import org.junit.Test; @@ -42,62 +37,6 @@ public class PersistenceTest extends TestBase { assertTrue(updatedGot); } - @Test - public void whenDataUpdatedTest() { - // GIVEN - DataMap map = new DataMap(); - - // WHEN - final long whenNotUpdated = persistence.whenDataUpdated(); - - persistence.storeDataMap("data-map", map); - final long whenUpdatedFirst = persistence.whenDataUpdated(); - - getWearUtilMocker().progressClock(60000); - persistence.storeDataMap("data-map", map); - final long whenUpdatedNext = persistence.whenDataUpdated(); - - // THEN - assertEquals(whenNotUpdated, 0L); - assertEquals(whenUpdatedFirst, REF_NOW); - assertEquals(whenUpdatedNext, REF_NOW + 60000); - } - - @Test - public void getDataMapTest() { - // GIVEN - DataMap map = new DataMap(); - map.putByteArray("test-key", new byte[]{9, 42, 127, -5}); - - // WHEN - DataMap notExisting = persistence.getDataMap("not-there"); - persistence.storeDataMap("data-map", map); - DataMap restoredMap = persistence.getDataMap("data-map"); - assert restoredMap != null; - byte[] restoredMapContents = restoredMap.getByteArray("test-key"); - - // THEN - assertNull(notExisting); - assertNotNull(restoredMap); - assertTrue(restoredMap.containsKey("test-key")); - - assertEquals(restoredMapContents.length, 4); - assertEquals(restoredMapContents[0], (byte) 9); - assertEquals(restoredMapContents[1], (byte) 42); - assertEquals(restoredMapContents[2], (byte) 127); - assertEquals(restoredMapContents[3], (byte) -5); - } - - @Test - public void brokenDataMapTest() { - // WHEN - persistence.putString("data-map", "ZmFrZSBkYXRh"); - DataMap restoredMap = persistence.getDataMap("data-map"); - - // THEN - assertNull(restoredMap); - } - @Test public void setsTest() { // WHEN diff --git a/wear/src/test/java/info/nightscout/androidaps/interaction/utils/WearUtilTest.kt b/wear/src/test/java/info/nightscout/androidaps/interaction/utils/WearUtilTest.kt index 3f853db233..3c4fbe283a 100644 --- a/wear/src/test/java/info/nightscout/androidaps/interaction/utils/WearUtilTest.kt +++ b/wear/src/test/java/info/nightscout/androidaps/interaction/utils/WearUtilTest.kt @@ -4,15 +4,14 @@ import com.google.android.gms.wearable.DataMap import info.nightscout.androidaps.TestBase import info.nightscout.androidaps.testing.mockers.WearUtilMocker import info.nightscout.androidaps.testing.mocks.BundleMock -import org.hamcrest.CoreMatchers import org.junit.Assert import org.junit.Test -import java.util.* /** * Created by dlvoy on 22.11.2019. */ -@Suppress("SpellCheckingInspection") class WearUtilTest : TestBase() { +@Suppress("SpellCheckingInspection") +class WearUtilTest : TestBase() { @Test fun timestampAndTimeDiffsTest() { diff --git a/wear/src/test/java/info/nightscout/androidaps/testing/mockers/RawDataMocker.java b/wear/src/test/java/info/nightscout/androidaps/testing/mockers/RawDataMocker.java index a6bdf446f9..b6aa2318e9 100644 --- a/wear/src/test/java/info/nightscout/androidaps/testing/mockers/RawDataMocker.java +++ b/wear/src/test/java/info/nightscout/androidaps/testing/mockers/RawDataMocker.java @@ -3,70 +3,138 @@ package info.nightscout.androidaps.testing.mockers; import info.nightscout.androidaps.data.RawDisplayData; import info.nightscout.androidaps.interaction.utils.WearUtil; import info.nightscout.shared.SafeParse; +import info.nightscout.shared.weardata.EventData; @SuppressWarnings("PointlessArithmeticExpression") public class RawDataMocker { - private final WearUtil wearUtil; private final WearUtilMocker wearUtilMocker; public RawDataMocker(WearUtil wearUtil) { - this.wearUtil = wearUtil; wearUtilMocker = new WearUtilMocker(wearUtil); } public RawDisplayData rawSgv(String sgv, int m, String deltaString) { - RawDisplayData raw = new RawDisplayData(wearUtil); - raw.datetime = wearUtilMocker.backInTime(0, 0, m, 0); - raw.sDelta = deltaString; - raw.sSgv = sgv; - + RawDisplayData raw = new RawDisplayData(); double delta = SafeParse.stringToDouble(deltaString); + String d; if (delta <= (-3.5 * 5)) { - raw.sDirection = "\u21ca"; + d = "\u21ca"; } else if (delta <= (-2 * 5)) { - raw.sDirection = "\u2193"; + d = "\u2193"; } else if (delta <= (-1 * 5)) { - raw.sDirection = "\u2198"; + d = "\u2198"; } else if (delta <= (1 * 5)) { - raw.sDirection = "\u2192"; + d = "\u2192"; } else if (delta <= (2 * 5)) { - raw.sDirection = "\u2197"; + d = "\u2197"; } else if (delta <= (3.5 * 5)) { - raw.sDirection = "\u2191"; + d = "\u2191"; } else { - raw.sDirection = "\u21c8"; + d = "\u21c8"; } - + raw.setSingleBg( + new EventData.SingleBg( + wearUtilMocker.backInTime(0, 0, m, 0), + sgv, + "", + d, + deltaString, + "", + 0, + 0.0, + 0.0, + 0.0, + 0 + ) + ); return raw; } public RawDisplayData rawDelta(int m, String delta) { - RawDisplayData raw = new RawDisplayData(wearUtil); - raw.datetime = wearUtilMocker.backInTime(0, 0, m, 0); - raw.sDelta = delta; + RawDisplayData raw = new RawDisplayData(); + raw.setSingleBg( + new EventData.SingleBg( + wearUtilMocker.backInTime(0, 0, m, 0), + "", + "", + "", + delta, + "", + 0, + 0.0, + 0.0, + 0.0, + 0 + ) + ); return raw; } public RawDisplayData rawCobIobBr(String cob, String iob, String br) { - RawDisplayData raw = new RawDisplayData(wearUtil); - raw.sCOB2 = cob; - raw.sIOB1 = iob; - raw.sBasalRate = br; + RawDisplayData raw = new RawDisplayData(); + raw.setStatus( + new EventData.Status( + "", + iob, + "", + true, + cob, + br, + "", + "", + 0L, + "", + true, + 0 + + ) + ); return raw; } public RawDisplayData rawIob(String iob, String iob2) { - RawDisplayData raw = new RawDisplayData(wearUtil); - raw.sIOB1 = iob; - raw.sIOB2 = iob2; + RawDisplayData raw = new RawDisplayData(); + raw.setStatus( + new EventData.Status( + "", + iob, + iob2, + true, + "", + "", + "", + "", + 0L, + "", + true, + 0 + + ) + ); return raw; } public RawDisplayData rawCob(String cob) { - RawDisplayData raw = new RawDisplayData(wearUtil); - raw.sCOB2 = cob; + RawDisplayData raw = new RawDisplayData(); + raw.setStatus( + new EventData.Status( + "", + "", + "", + true, + cob, + "", + "", + "", + 0L, + "", + true, + 0 + + ) + ); return raw; } diff --git a/wear/src/test/java/info/nightscout/androidaps/testing/utils/BasalWatchDataExt.java b/wear/src/test/java/info/nightscout/androidaps/testing/utils/BasalWatchDataExt.java deleted file mode 100644 index c0fffbff71..0000000000 --- a/wear/src/test/java/info/nightscout/androidaps/testing/utils/BasalWatchDataExt.java +++ /dev/null @@ -1,59 +0,0 @@ -package info.nightscout.androidaps.testing.utils; - -import static info.nightscout.androidaps.testing.utils.ExtUtil.assertClassHaveSameFields; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import java.util.Objects; - -import info.nightscout.androidaps.data.BasalWatchData; - -public class BasalWatchDataExt extends BasalWatchData { - - private BasalWatchDataExt() { - super(); - } - - public BasalWatchDataExt(BasalWatchData ref) { - super(); - - // since we do not want modify BasalWatchData - we use this wrapper class - // but we make sure it has same fields - assertClassHaveSameFields(BasalWatchData.class, "startTime,endTime,amount"); - - this.startTime = ref.startTime; - this.endTime = ref.endTime; - this.amount = ref.amount; - } - - public static BasalWatchDataExt build(long startTime, long endTime, double amount) { - BasalWatchDataExt bwd = new BasalWatchDataExt(); - bwd.startTime = startTime; - bwd.endTime = endTime; - bwd.amount = amount; - return bwd; - } - - @Override - public boolean equals(@Nullable Object obj) { - if (obj instanceof BasalWatchData) { - return (this.startTime == ((BasalWatchData) obj).startTime) - && (this.endTime == ((BasalWatchData) obj).endTime) - && (this.amount == ((BasalWatchData) obj).amount); - } else { - return false; - } - } - - @NonNull @Override - public String toString() { - return startTime + ", " + endTime + ", " + amount; - } - - @Override - public int hashCode() { - return Objects.hash(startTime, endTime, amount); - } - -} diff --git a/wear/src/test/java/info/nightscout/androidaps/testing/utils/BgWatchDataExt.java b/wear/src/test/java/info/nightscout/androidaps/testing/utils/BgWatchDataExt.java deleted file mode 100644 index 5b70ef1ace..0000000000 --- a/wear/src/test/java/info/nightscout/androidaps/testing/utils/BgWatchDataExt.java +++ /dev/null @@ -1,68 +0,0 @@ -package info.nightscout.androidaps.testing.utils; - -import static info.nightscout.androidaps.testing.utils.ExtUtil.assertClassHaveSameFields; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import java.util.Objects; - -import info.nightscout.androidaps.data.BgWatchData; - -@SuppressWarnings("unused") -public class BgWatchDataExt extends BgWatchData { - - private BgWatchDataExt() { - super(); - } - - public BgWatchDataExt(double aSgv, double aHigh, double aLow, long aTimestamp, int aColor) { - super(aSgv, aHigh, aLow, aTimestamp, aColor); - } - - public BgWatchDataExt(BgWatchData ref) { - super(); - - // since we do not want modify BgWatchDataExt - we use this wrapper class - // but we make sure it has same fields - assertClassHaveSameFields(BgWatchData.class, "sgv,high,low,timestamp,color"); - - this.sgv = ref.sgv; - this.high = ref.high; - this.low = ref.low; - this.timestamp = ref.timestamp; - this.color = ref.color; - } - - public static BgWatchDataExt build(double sgv, long timestamp, int color) { - BgWatchDataExt twd = new BgWatchDataExt(); - twd.sgv = sgv; - twd.timestamp = timestamp; - twd.color = color; - return twd; - } - - @Override - public boolean equals(@Nullable Object obj) { - if (obj instanceof BgWatchData) { - return (this.sgv == ((BgWatchData) obj).sgv) - && (this.high == ((BgWatchData) obj).high) - && (this.low == ((BgWatchData) obj).low) - && (this.timestamp == ((BgWatchData) obj).timestamp) - && (this.color == ((BgWatchData) obj).color); - } else { - return false; - } - } - - @Override @NonNull - public String toString() { - return sgv + ", " + high + ", " + low + ", " + timestamp + ", " + color; - } - - @Override - public int hashCode() { - return Objects.hash(sgv, high, low, timestamp, color); - } - -} diff --git a/wear/src/test/java/info/nightscout/androidaps/testing/utils/BolusWatchDataExt.java b/wear/src/test/java/info/nightscout/androidaps/testing/utils/BolusWatchDataExt.java deleted file mode 100644 index 73f4c367c6..0000000000 --- a/wear/src/test/java/info/nightscout/androidaps/testing/utils/BolusWatchDataExt.java +++ /dev/null @@ -1,65 +0,0 @@ -package info.nightscout.androidaps.testing.utils; - -import static info.nightscout.androidaps.testing.utils.ExtUtil.assertClassHaveSameFields; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import java.util.Objects; - -import info.nightscout.androidaps.data.BolusWatchData; - -public class BolusWatchDataExt extends BolusWatchData { - - private BolusWatchDataExt() { - super(); - } - - public BolusWatchDataExt(BolusWatchData ref) { - super(); - - // since we do not want modify BolusWatchData - we use this wrapper class - // but we make sure it has same fields - assertClassHaveSameFields(BolusWatchData.class, "date,bolus,carbs,isSMB,isValid"); - - this.date = ref.date; - this.bolus = ref.bolus; - this.carbs = ref.carbs; - this.isSMB = ref.isSMB; - this.isValid = ref.isValid; - } - - public static BolusWatchDataExt build(long date, double bolus, double carbs, boolean isSMB, boolean isValid) { - BolusWatchDataExt bwd = new BolusWatchDataExt(); - bwd.date = date; - bwd.bolus = bolus; - bwd.carbs = carbs; - bwd.isSMB = isSMB; - bwd.isValid = isValid; - return bwd; - } - - @Override - public boolean equals(@Nullable Object obj) { - if (obj instanceof BolusWatchData) { - return (this.date == ((BolusWatchData) obj).date) - && (this.bolus == ((BolusWatchData) obj).bolus) - && (this.carbs == ((BolusWatchData) obj).carbs) - && (this.isSMB == ((BolusWatchData) obj).isSMB) - && (this.isValid == ((BolusWatchData) obj).isValid); - } else { - return false; - } - } - - @NonNull @Override - public String toString() { - return date + ", " + bolus + ", " + carbs + ", " + isSMB + ", " + isValid; - } - - @Override - public int hashCode() { - return Objects.hash(date, bolus, carbs, isSMB, isValid); - } - -} diff --git a/wear/src/test/java/info/nightscout/androidaps/testing/utils/ExtUtil.java b/wear/src/test/java/info/nightscout/androidaps/testing/utils/ExtUtil.java deleted file mode 100644 index 9a7014754e..0000000000 --- a/wear/src/test/java/info/nightscout/androidaps/testing/utils/ExtUtil.java +++ /dev/null @@ -1,27 +0,0 @@ -package info.nightscout.androidaps.testing.utils; - -import static org.junit.Assert.assertEquals; - -import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -class ExtUtil { - - static void assertClassHaveSameFields(Class checkedClass, String commaSeparatedFieldList) { - Set parentFields = new HashSet<>(); - for (Field f : checkedClass.getDeclaredFields()) { - final String fieldName = f.getName(); - // skip runtime-injected fields like $jacocoData - if (fieldName.startsWith("$")) { - continue; - } - parentFields.add(fieldName); - } - - Set knownFields = new HashSet<>(Arrays.asList(commaSeparatedFieldList.split(","))); - assertEquals(parentFields, knownFields); - } - -} diff --git a/wear/src/test/java/info/nightscout/androidaps/testing/utils/TempWatchDataExt.java b/wear/src/test/java/info/nightscout/androidaps/testing/utils/TempWatchDataExt.java deleted file mode 100644 index 6a13266365..0000000000 --- a/wear/src/test/java/info/nightscout/androidaps/testing/utils/TempWatchDataExt.java +++ /dev/null @@ -1,67 +0,0 @@ -package info.nightscout.androidaps.testing.utils; - -import static info.nightscout.androidaps.testing.utils.ExtUtil.assertClassHaveSameFields; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import java.util.Objects; - -import info.nightscout.androidaps.data.TempWatchData; - - -public class TempWatchDataExt extends TempWatchData { - - private TempWatchDataExt() { - super(); - } - - public TempWatchDataExt(TempWatchData ref) { - super(); - - // since we do not want modify BolusWatchData - we use this wrapper class - // but we make sure it has same fields - assertClassHaveSameFields(TempWatchData.class, "startTime,startBasal,endTime,endBasal,amount"); - - this.startTime = ref.startTime; - this.startBasal = ref.startBasal; - this.endTime = ref.endTime; - this.endBasal = ref.endBasal; - this.amount = ref.amount; - } - - public static TempWatchDataExt build(long startTime, double startBasal, long endTime, - double endBasal, double amount) { - TempWatchDataExt twd = new TempWatchDataExt(); - twd.startTime = startTime; - twd.startBasal = startBasal; - twd.endTime = endTime; - twd.endBasal = endBasal; - twd.amount = amount; - return twd; - } - - @Override - public boolean equals(@Nullable Object obj) { - if (obj instanceof TempWatchData) { - return (this.startTime == ((TempWatchData) obj).startTime) - && (this.startBasal == ((TempWatchData) obj).startBasal) - && (this.endTime == ((TempWatchData) obj).endTime) - && (this.endBasal == ((TempWatchData) obj).endBasal) - && (this.amount == ((TempWatchData) obj).amount); - } else { - return false; - } - } - - @NonNull @Override - public String toString() { - return startTime + ", " + startBasal + ", " + endTime + ", " + endBasal + ", " + amount; - } - - @Override - public int hashCode() { - return Objects.hash(startTime, startBasal, endTime, endBasal, amount); - } - -}